diff options
Diffstat (limited to 'chromium/chrome/browser/extensions')
192 files changed, 5638 insertions, 3106 deletions
diff --git a/chromium/chrome/browser/extensions/BUILD.gn b/chromium/chrome/browser/extensions/BUILD.gn index 358b0a3b16f..a1f5e2a7e0e 100644 --- a/chromium/chrome/browser/extensions/BUILD.gn +++ b/chromium/chrome/browser/extensions/BUILD.gn @@ -190,6 +190,8 @@ static_library("extensions") { "api/identity/identity_constants.h", "api/identity/identity_get_accounts_function.cc", "api/identity/identity_get_accounts_function.h", + "api/identity/identity_get_auth_token_error.cc", + "api/identity/identity_get_auth_token_error.h", "api/identity/identity_get_auth_token_function.cc", "api/identity/identity_get_auth_token_function.h", "api/identity/identity_get_profile_user_info_function.cc", @@ -610,14 +612,14 @@ static_library("extensions") { "external_provider_impl.h", "external_registry_loader_win.cc", "external_registry_loader_win.h", - "forced_extensions/installation_metrics.cc", - "forced_extensions/installation_metrics.h", - "forced_extensions/installation_reporter.cc", - "forced_extensions/installation_reporter.h", - "forced_extensions/installation_reporter_factory.cc", - "forced_extensions/installation_reporter_factory.h", - "forced_extensions/installation_tracker.cc", - "forced_extensions/installation_tracker.h", + "forced_extensions/force_installed_metrics.cc", + "forced_extensions/force_installed_metrics.h", + "forced_extensions/force_installed_tracker.cc", + "forced_extensions/force_installed_tracker.h", + "forced_extensions/install_stage_tracker.cc", + "forced_extensions/install_stage_tracker.h", + "forced_extensions/install_stage_tracker_factory.cc", + "forced_extensions/install_stage_tracker_factory.h", "global_shortcut_listener.cc", "global_shortcut_listener.h", "global_shortcut_listener_mac.h", @@ -645,6 +647,8 @@ static_library("extensions") { "launch_util.h", "load_error_reporter.cc", "load_error_reporter.h", + "media_router_extension_access_logger_impl.cc", + "media_router_extension_access_logger_impl.h", "menu_manager.cc", "menu_manager.h", "menu_manager_factory.cc", @@ -751,7 +755,7 @@ static_library("extensions") { # Lots of APIs use headers from the list above. "//chrome/browser/extensions/api:api_registration", - # TODO(loyso): Remove this circular dependency. http://crbug.com/876576. + # TODO(crbug.com/1065748): Remove this circular dependency. "//chrome/browser/web_applications/extensions", # TODO(crbug/925153): Remove this circular dependency. @@ -796,11 +800,13 @@ static_library("extensions") { "//chrome/browser/resource_coordinator:intervention_policy_database_proto", "//chrome/browser/resource_coordinator:mojo_bindings", "//chrome/browser/safe_browsing", + "//chrome/browser/web_applications", "//chrome/browser/web_applications/components", + + # TODO(crbug.com/1065748): Remove this dependency: "//chrome/browser/web_applications/extensions", "//chrome/common/extensions/api:extensions_features", "//chrome/common/safe_browsing:proto", - "//chrome/services/app_service/public/mojom", "//chrome/services/removable_storage_writer/public/mojom", "//components/autofill/content/browser", "//components/bookmarks/browser", @@ -853,6 +859,7 @@ static_library("extensions") { "//components/safe_browsing/core/common:safe_browsing_prefs", "//components/safe_browsing/core/db:database_manager", "//components/search_engines", + "//components/services/app_service/public/mojom", "//components/services/patch/content", "//components/services/unzip/content", "//components/services/unzip/public/cpp", @@ -897,6 +904,7 @@ static_library("extensions") { "//services/audio/public/cpp", "//services/data_decoder/public/cpp", "//services/device/public/mojom", + "//services/metrics/public/cpp:ukm_builders", "//services/network/public/mojom", "//services/service_manager/public/cpp", "//services/service_manager/public/mojom", @@ -947,6 +955,8 @@ static_library("extensions") { "api/certificate_provider/certificate_provider_api.h", "api/enterprise_device_attributes/enterprise_device_attributes_api.cc", "api/enterprise_device_attributes/enterprise_device_attributes_api.h", + "api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc", + "api/enterprise_networking_attributes/enterprise_networking_attributes_api.h", "api/enterprise_platform_keys/enterprise_platform_keys_api.cc", "api/enterprise_platform_keys/enterprise_platform_keys_api.h", "api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc", @@ -1004,10 +1014,12 @@ static_library("extensions") { "//ash/keyboard/ui:resources_grit_grit", "//ash/public/cpp", "//chrome/browser/chromeos/crostini:crostini_installer_types_mojom", - "//chrome/browser/resources/chromeos/camera:chrome_camera_app", "//chrome/browser/ui/webui/settings/chromeos/constants:mojom", "//chromeos", "//chromeos/attestation", + "//chromeos/components/camera_app_ui", + "//chromeos/components/camera_app_ui:mojo_bindings", + "//chromeos/components/camera_app_ui/resources:chrome_camera_app", "//chromeos/components/proximity_auth", "//chromeos/constants", "//chromeos/cryptohome", @@ -1023,11 +1035,11 @@ static_library("extensions") { "//chromeos/services/machine_learning/public/cpp", "//chromeos/services/machine_learning/public/mojom", "//chromeos/services/media_perception/public/mojom", + "//chromeos/services/tts/public/mojom", "//chromeos/settings", "//chromeos/system", "//chromeos/tpm", "//components/arc", - "//components/chromeos_camera:camera_app_helper", "//components/constrained_window", "//components/drive", "//components/user_manager", @@ -1160,17 +1172,6 @@ static_library("extensions") { ] } - if (is_win || (is_linux && !is_chromeos)) { - sources += [ - "api/input_ime/input_ime_api.cc", - "api/input_ime/input_ime_api.h", - "api/input_ime/input_ime_api_nonchromeos.cc", - "api/input_ime/input_ime_api_nonchromeos.h", - "api/input_ime/input_ime_event_router_base.cc", - "api/input_ime/input_ime_event_router_base.h", - ] - } - if (enable_plugins) { sources += [ "plugin_manager.cc", @@ -1220,7 +1221,7 @@ static_library("extensions") { "api/braille_display_private/brlapi_keycode_map.cc", "api/braille_display_private/brlapi_keycode_map.h", ] - deps += [ "//build/linux/libbrlapi" ] + deps += [ "//third_party/libbrlapi" ] } else { sources += [ "api/braille_display_private/braille_controller_stub.cc" ] } diff --git a/chromium/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc b/chromium/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc index 5d7be8f6b8c..f26c3ec2c48 100644 --- a/chromium/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/activity_log_private/activity_log_private_apitest.cc @@ -58,13 +58,13 @@ class ActivityLogApiTest : public ExtensionApiTest { base::CommandLine saved_cmdline_; }; -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) -// TODO(crbug.com/299393): Flaky on Mac, Windows and Linux. +#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER) +// TODO(crbug.com/299393): This test is very long and can time out in debug or +// ASAN builds. #define MAYBE_TriggerEvent DISABLED_TriggerEvent #else #define MAYBE_TriggerEvent TriggerEvent #endif - // The test extension sends a message to its 'friend'. The test completes // if it successfully sees the 'friend' receive the message. IN_PROC_BROWSER_TEST_F(ActivityLogApiTest, MAYBE_TriggerEvent) { @@ -82,4 +82,3 @@ IN_PROC_BROWSER_TEST_F(ActivityLogApiTest, MAYBE_TriggerEvent) { } } // namespace extensions - diff --git a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc index eacee156995..1de49aa22be 100644 --- a/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc +++ b/chromium/chrome/browser/extensions/api/alarms/alarms_apitest.cc @@ -6,6 +6,7 @@ #include "content/public/test/browser_test.h" #include "extensions/browser/event_router.h" #include "extensions/common/api/test.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" #include "net/dns/mock_host_resolver.h" @@ -15,12 +16,20 @@ using extensions::ResultCatcher; namespace extensions { -class AlarmsApiTest : public ExtensionApiTest { +using ContextType = ExtensionApiTest::ContextType; + +class AlarmsApiTest : public ExtensionApiTest, + public testing::WithParamInterface<ContextType> { public: void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); host_resolver()->AddRule("*", "127.0.0.1"); ASSERT_TRUE(StartEmbeddedTestServer()); + + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); } static std::unique_ptr<base::ListValue> BuildEventArguments( @@ -30,11 +39,30 @@ class AlarmsApiTest : public ExtensionApiTest { info.last_message = last_message; return api::test::OnMessage::Create(info); } + + const Extension* LoadAlarmsExtensionIncognito(const char* path) { + int flags = kFlagEnableFileAccess | kFlagEnableIncognito; + if (GetParam() == ContextType::kServiceWorker) + flags |= kFlagRunAsServiceWorkerBasedExtension; + + return LoadExtensionWithFlags( + test_data_dir_.AppendASCII("alarms").AppendASCII(path), flags); + } + + private: + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; }; +INSTANTIATE_TEST_SUITE_P(EventPage, + AlarmsApiTest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + AlarmsApiTest, + ::testing::Values(ContextType::kServiceWorker)); + // Tests that an alarm created by an extension with incognito split mode is // only triggered in the browser context it was created in. -IN_PROC_BROWSER_TEST_F(AlarmsApiTest, IncognitoSplit) { +IN_PROC_BROWSER_TEST_P(AlarmsApiTest, IncognitoSplit) { // We need 2 ResultCatchers because we'll be running the same test in both // regular and incognito mode. Profile* incognito_profile = browser()->profile()->GetOffTheRecordProfile(); @@ -48,8 +76,7 @@ IN_PROC_BROWSER_TEST_F(AlarmsApiTest, IncognitoSplit) { ExtensionTestMessageListener listener_incognito("ready: true", false); - ASSERT_TRUE(LoadExtensionIncognito( - test_data_dir_.AppendASCII("alarms").AppendASCII("split"))); + ASSERT_TRUE(LoadAlarmsExtensionIncognito("split")); EXPECT_TRUE(listener.WaitUntilSatisfied()); EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); @@ -67,12 +94,11 @@ IN_PROC_BROWSER_TEST_F(AlarmsApiTest, IncognitoSplit) { // Tests that the behavior for an alarm created in incognito context should be // the same if incognito is in spanning mode. -IN_PROC_BROWSER_TEST_F(AlarmsApiTest, IncognitoSpanning) { +IN_PROC_BROWSER_TEST_P(AlarmsApiTest, IncognitoSpanning) { ResultCatcher catcher; catcher.RestrictToBrowserContext(browser()->profile()); - ASSERT_TRUE(LoadExtensionIncognito( - test_data_dir_.AppendASCII("alarms").AppendASCII("spanning"))); + ASSERT_TRUE(LoadAlarmsExtensionIncognito("spanning")); // Open incognito window. OpenURLOffTheRecord(browser()->profile(), GURL("about:blank")); diff --git a/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc b/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc index a96f3c9d9d7..cb6fe1e90f2 100644 --- a/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc +++ b/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.cc @@ -189,9 +189,19 @@ void AutofillAssistantPrivateEventRouter::OnInfoBoxChanged( void AutofillAssistantPrivateEventRouter::OnProgressChanged(int progress) {} +void AutofillAssistantPrivateEventRouter::OnProgressActiveStepChanged( + int active_step) {} + void AutofillAssistantPrivateEventRouter::OnProgressVisibilityChanged( bool visible) {} +void AutofillAssistantPrivateEventRouter::OnStepProgressBarConfigurationChanged( + const autofill_assistant::ShowProgressBarProto:: + StepProgressBarConfiguration& configuration) {} + +void AutofillAssistantPrivateEventRouter::OnProgressBarErrorStateChanged( + bool error) {} + void AutofillAssistantPrivateEventRouter::OnTouchableAreaChanged( const autofill_assistant::RectF& visual_viewport, const std::vector<autofill_assistant::RectF>& touchable_areas, diff --git a/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h b/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h index d9b1b8fdea7..034c4e7decc 100644 --- a/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h +++ b/chromium/chrome/browser/extensions/api/autofill_assistant_private/autofill_assistant_private_api.h @@ -139,7 +139,12 @@ class AutofillAssistantPrivateEventRouter void OnDetailsChanged(const autofill_assistant::Details* details) override; void OnInfoBoxChanged(const autofill_assistant::InfoBox* info_box) override; void OnProgressChanged(int progress) override; + void OnProgressActiveStepChanged(int active_step) override; void OnProgressVisibilityChanged(bool visible) override; + void OnStepProgressBarConfigurationChanged( + const autofill_assistant::ShowProgressBarProto:: + StepProgressBarConfiguration& configuration) override; + void OnProgressBarErrorStateChanged(bool error) override; void OnTouchableAreaChanged( const autofill_assistant::RectF& visual_viewport, const std::vector<autofill_assistant::RectF>& touchable_areas, 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 450445fb776..9e6591918d7 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 @@ -347,11 +347,31 @@ ExtensionFunction::ResponseAction AutofillPrivateSaveCreditCardFunction::Run() { base::UTF8ToUTF16(*card->expiration_year)); } + if (card->nickname) { + credit_card.SetNickname(base::UTF8ToUTF16(*card->nickname)); + } + if (use_existing_card) { + // Only updates when the card info changes. + if (existing_card && existing_card->Compare(credit_card) == 0) + return RespondNow(NoArguments()); + + // Record when nickname is updated. + if (credit_card.HasValidNickname() && + existing_card->nickname() != credit_card.nickname()) { + base::RecordAction( + base::UserMetricsAction("AutofillCreditCardsEditedWithNickname")); + } + personal_data->UpdateCreditCard(credit_card); + base::RecordAction(base::UserMetricsAction("AutofillCreditCardsEdited")); } else { personal_data->AddCreditCard(credit_card); base::RecordAction(base::UserMetricsAction("AutofillCreditCardsAdded")); + if (credit_card.HasValidNickname()) { + base::RecordAction( + base::UserMetricsAction("AutofillCreditCardsAddedWithNickname")); + } } return RespondNow(NoArguments()); 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 00a1b8607db..48da5f15a0a 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 @@ -44,8 +44,7 @@ class AutofillPrivateApiTest : public ExtensionApiTest { // TODO(hcarmona): Investigate converting these tests to unittests. -// TODO(crbug.com/643097) Disabled for flakiness. -IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, DISABLED_GetCountryList) { +IN_PROC_BROWSER_TEST_F(AutofillPrivateApiTest, GetCountryList) { EXPECT_TRUE(RunAutofillSubtest("getCountryList")) << message_; } 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 cbe58c41776..10fe596182d 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc @@ -146,6 +146,10 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry( credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_MONTH)))); card.expiration_year.reset(new std::string(base::UTF16ToUTF8( credit_card.GetRawInfo(autofill::CREDIT_CARD_EXP_4_DIGIT_YEAR)))); + if (!credit_card.nickname().empty()) { + card.nickname.reset( + new std::string(base::UTF16ToUTF8(credit_card.nickname()))); + } // Create address metadata and add it to |address|. std::unique_ptr<autofill_private::AutofillMetadata> metadata( diff --git a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc index 660aa4ed198..806848fbcb0 100644 --- a/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc +++ b/chromium/chrome/browser/extensions/api/bluetooth_low_energy/bluetooth_low_energy_apitest.cc @@ -757,7 +757,7 @@ IN_PROC_BROWSER_TEST_F(BluetoothLowEnergyApiTest, WriteCharacteristicValue) { .WillRepeatedly(Return(chrc0_.get())); std::vector<uint8_t> write_value; - EXPECT_CALL(*chrc0_, WriteRemoteCharacteristic_(_, _, _)) + EXPECT_CALL(*chrc0_, DeprecatedWriteRemoteCharacteristic_(_, _, _)) .Times(2) .WillOnce(InvokeCallbackArgument<2>( BluetoothRemoteGattService::GATT_ERROR_FAILED)) diff --git a/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc b/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc index 7b0cdb28d3d..cde5b9d368a 100644 --- a/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc +++ b/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.cc @@ -68,12 +68,12 @@ namespace StartDrag = api::bookmark_manager_private::StartDrag; namespace { // Returns a single bookmark node from the argument ID. -// This returns NULL in case of failure. +// This returns nullptr in case of failure. const BookmarkNode* GetNodeFromString(BookmarkModel* model, const std::string& id_string) { int64_t id; if (!base::StringToInt64(id_string, &id)) - return NULL; + return nullptr; return bookmarks::GetBookmarkNodeByID(model, id); } @@ -197,7 +197,7 @@ void BookmarkManagerPrivateEventRouter::BookmarkModelChanged() {} void BookmarkManagerPrivateEventRouter::BookmarkModelBeingDeleted( BookmarkModel* model) { - bookmark_model_ = NULL; + bookmark_model_ = nullptr; } BookmarkManagerPrivateAPI::BookmarkManagerPrivateAPI( @@ -304,7 +304,7 @@ const BookmarkNodeData* BookmarkManagerPrivateDragEventRouter::GetBookmarkNodeData() { if (bookmark_drag_data_.is_valid()) return &bookmark_drag_data_; - return NULL; + return nullptr; } void BookmarkManagerPrivateDragEventRouter::ClearBookmarkNodeData() { @@ -509,7 +509,7 @@ BookmarkManagerPrivateGetSubtreeFunction::RunOnReady() { if (!params) return BadMessage(); - const BookmarkNode* node = NULL; + const BookmarkNode* node = nullptr; if (params->id.empty()) { BookmarkModel* model = diff --git a/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h b/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h index b2617d86624..27e4655bd64 100644 --- a/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h +++ b/chromium/chrome/browser/extensions/api/bookmark_manager_private/bookmark_manager_private_api.h @@ -104,7 +104,7 @@ class BookmarkManagerPrivateDragEventRouter void OnDrop(const bookmarks::BookmarkNodeData& data) override; // The bookmark drag and drop data. This gets set after a drop was done on - // the page. This returns NULL if no data is available. + // the page. This returns nullptr if no data is available. const bookmarks::BookmarkNodeData* GetBookmarkNodeData(); // Clears the drag and drop data. diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc index 5f90d3df246..8671eb11e31 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_api_helpers_unittest.cc @@ -35,11 +35,11 @@ namespace bookmark_api_helpers { class ExtensionBookmarksTest : public testing::Test { public: ExtensionBookmarksTest() - : managed_(NULL), - model_(NULL), - node_(NULL), - node2_(NULL), - folder_(NULL) {} + : managed_(nullptr), + model_(nullptr), + node_(nullptr), + node2_(nullptr), + folder_(nullptr) {} void SetUp() override { profile_.CreateBookmarkModel(false); diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc index 0664a2e51ba..78136b17ddb 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmark_apitest.cc @@ -19,12 +19,37 @@ #include "components/bookmarks/test/bookmark_test_helpers.h" #include "components/prefs/pref_service.h" #include "content/public/test/browser_test.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" using bookmarks::BookmarkModel; namespace extensions { -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Bookmarks) { +using ContextType = ExtensionApiTest::ContextType; + +class BookmarksApiTest : public ExtensionApiTest, + public testing::WithParamInterface<ContextType> { + public: + void SetUp() override { + ExtensionApiTest::SetUp(); + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); + } + + private: + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; +}; + +INSTANTIATE_TEST_SUITE_P(EventPage, + BookmarksApiTest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + BookmarksApiTest, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P(BookmarksApiTest, Bookmarks) { // Add test managed bookmarks to verify that the bookmarks API can read them // and can't modify them. Profile* profile = browser()->profile(); @@ -45,7 +70,13 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Bookmarks) { profile->GetPrefs()->Set(bookmarks::prefs::kManagedBookmarks, list); ASSERT_EQ(2u, managed->managed_node()->children().size()); - ASSERT_TRUE(RunExtensionTest("bookmarks")) << message_; + if (GetParam() == ContextType::kEventPage) { + ASSERT_TRUE(RunExtensionTest("bookmarks")) << message_; + } else { + ASSERT_TRUE(RunExtensionTestWithFlags( + "bookmarks", kFlagRunAsServiceWorkerBasedExtension, kFlagNone)) + << message_; + } } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc index f7fa8079e88..2e7f7b8235d 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc @@ -272,7 +272,7 @@ void BookmarkEventRouter::BookmarkModelLoaded(BookmarkModel* model, } void BookmarkEventRouter::BookmarkModelBeingDeleted(BookmarkModel* model) { - model_ = NULL; + model_ = nullptr; } void BookmarkEventRouter::BookmarkNodeMoved(BookmarkModel* model, @@ -744,17 +744,12 @@ void BookmarksIOFunction::ShowSelectFileDialog( gfx::NativeWindow owning_window = web_contents ? platform_util::GetTopLevel(web_contents->GetNativeView()) : gfx::kNullNativeWindow; - // |web_contents| can be NULL (for background pages), which is fine. In such - // a case if file-selection dialogs are forbidden by policy, we will not + // |web_contents| can be nullptr (for background pages), which is fine. In + // such a case if file-selection dialogs are forbidden by policy, we will not // show an InfoBar, which is better than letting one appear out of the blue. - select_file_dialog_->SelectFile(type, - base::string16(), - default_path, - &file_type_info, - 0, - base::FilePath::StringType(), - owning_window, - NULL); + select_file_dialog_->SelectFile( + type, base::string16(), default_path, &file_type_info, 0, + base::FilePath::StringType(), owning_window, nullptr); } void BookmarksIOFunction::FileSelectionCanceled(void* params) { diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h index e604b22d732..73703408cde 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.h @@ -165,7 +165,7 @@ class BookmarksFunction : public ExtensionFunction, bool EditBookmarksEnabled(); // Helper that checks if |node| can be modified. Returns false if |node| - // is NULL, or a managed node, or the root node. In these cases the node + // is nullptr, or a managed node, or the root node. In these cases the node // can't be edited, can't have new child nodes appended, and its direct // children can't be moved or reordered. bool CanBeModified(const bookmarks::BookmarkNode* node, std::string* error); 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 ab44ae9d90a..c1a99212564 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 @@ -15,7 +15,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/stl_util.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/threading/scoped_blocking_call.h" #include "base/time/time.h" @@ -74,18 +73,17 @@ BrailleControllerImpl::~BrailleControllerImpl() { void BrailleControllerImpl::TryLoadLibBrlApi() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - if (libbrlapi_loader_.loaded()) + if (skip_libbrlapi_so_load_ || libbrlapi_loader_.loaded()) return; - // These versions of libbrlapi work the same for the functions we - // are using. (0.6.0 adds brlapi_writeWText). - static const char* const kSupportedVersions[] = { - "libbrlapi.so.0.5", "libbrlapi.so.0.6", "libbrlapi.so.0.7", - "libbrlapi.so.0.8"}; - for (size_t i = 0; i < base::size(kSupportedVersions); ++i) { - if (libbrlapi_loader_.Load(kSupportedVersions[i])) - return; + + // This api version needs to match the one contained in + // third_party/libbrlapi/brlapi.h. + static const char* const kSupportedVersion = "libbrlapi.so.0.8"; + + if (!libbrlapi_loader_.Load(kSupportedVersion)) { + LOG(WARNING) << "Couldn't load libbrlapi(" << kSupportedVersion << ": " + << strerror(errno); } - LOG(WARNING) << "Couldn't load libbrlapi: " << strerror(errno); } std::unique_ptr<DisplayState> BrailleControllerImpl::GetDisplayState() { @@ -102,6 +100,10 @@ std::unique_ptr<DisplayState> BrailleControllerImpl::GetDisplayState() { display_state->available = true; display_state->text_column_count.reset(new int(columns)); display_state->text_row_count.reset(new int(rows)); + + unsigned int cell_size = 0; + connection_->GetCellSize(&cell_size); + display_state->cell_size.reset(new int(cell_size)); } } return display_state; @@ -134,9 +136,9 @@ void BrailleControllerImpl::WriteDots(const std::vector<uint8_t>& cells, void BrailleControllerImpl::AddObserver(BrailleObserver* observer) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!base::PostTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&BrailleControllerImpl::StartConnecting, - base::Unretained(this)))) { + if (!content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrailleControllerImpl::StartConnecting, + base::Unretained(this)))) { NOTREACHED(); } observers_.AddObserver(observer); @@ -168,7 +170,7 @@ void BrailleControllerImpl::StartConnecting() { return; started_connecting_ = true; TryLoadLibBrlApi(); - if (!libbrlapi_loader_.loaded()) { + if (!libbrlapi_loader_.loaded() && !skip_libbrlapi_so_load_) { return; } @@ -212,8 +214,8 @@ void BrailleControllerImpl::OnSocketDirChangedOnTaskThread( LOG(ERROR) << "Error watching brlapi directory: " << path.value(); return; } - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrailleControllerImpl::OnSocketDirChangedOnIOThread, base::Unretained(this))); } @@ -230,7 +232,7 @@ void BrailleControllerImpl::OnSocketDirChangedOnIOThread() { void BrailleControllerImpl::TryToConnect() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - DCHECK(libbrlapi_loader_.loaded()); + DCHECK(skip_libbrlapi_so_load_ || libbrlapi_loader_.loaded()); connect_scheduled_ = false; if (!connection_.get()) connection_ = create_brlapi_connection_function_.Run(); @@ -271,10 +273,11 @@ void BrailleControllerImpl::ScheduleTryToConnect() { } VLOG(1) << "Scheduling connection retry to brlapi"; connect_scheduled_ = true; - base::PostDelayedTask(FROM_HERE, {BrowserThread::IO}, - base::BindOnce(&BrailleControllerImpl::TryToConnect, - base::Unretained(this)), - kConnectionDelay); + content::GetIOThreadTaskRunner({})->PostDelayedTask( + FROM_HERE, + base::BindOnce(&BrailleControllerImpl::TryToConnect, + base::Unretained(this)), + kConnectionDelay); } void BrailleControllerImpl::Disconnect() { @@ -288,7 +291,7 @@ void BrailleControllerImpl::Disconnect() { std::unique_ptr<BrlapiConnection> BrailleControllerImpl::CreateBrlapiConnection() { - DCHECK(libbrlapi_loader_.loaded()); + DCHECK(skip_libbrlapi_so_load_ || libbrlapi_loader_.loaded()); return BrlapiConnection::Create(&libbrlapi_loader_); } @@ -316,8 +319,8 @@ void BrailleControllerImpl::DispatchKeys() { void BrailleControllerImpl::DispatchKeyEvent(std::unique_ptr<KeyEvent> event) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&BrailleControllerImpl::DispatchKeyEvent, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrailleControllerImpl::DispatchKeyEvent, base::Unretained(this), std::move(event))); return; } @@ -329,8 +332,8 @@ void BrailleControllerImpl::DispatchKeyEvent(std::unique_ptr<KeyEvent> event) { void BrailleControllerImpl::DispatchOnDisplayStateChanged( std::unique_ptr<DisplayState> new_state) { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - if (!base::PostTask( - FROM_HERE, {BrowserThread::UI}, + if (!content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &BrailleControllerImpl::DispatchOnDisplayStateChanged, base::Unretained(this), std::move(new_state)))) { diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h index fc98d3d832d..db1054e140c 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h +++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller_brlapi.h @@ -83,6 +83,9 @@ class BrailleControllerImpl : public BrailleController { // Manipulated by the SequencedTaskRunner. base::FilePathWatcher file_path_watcher_; + // Set by tests to skip libbrlapi.so loading. + bool skip_libbrlapi_so_load_ = false; + friend struct base::DefaultSingletonTraits<BrailleControllerImpl>; DISALLOW_COPY_AND_ASSIGN(BrailleControllerImpl); 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 fb509eb68ac..f77120a3600 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 @@ -89,7 +89,7 @@ bool BrailleDisplayPrivateAPI::IsProfileActive() { // Since we are creating one instance per profile / user, we should be fine // comparing against the active user. That said - if we ever change that, // this code will need to be changed. - return profile_->IsSameProfile(ProfileManager::GetActiveUserProfile()); + return profile_->IsSameOrParent(ProfileManager::GetActiveUserProfile()); #else // !defined(OS_CHROMEOS) return true; #endif 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 12ff95b9219..573132b3aa6 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 @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/containers/circular_deque.h" -#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" @@ -50,6 +49,7 @@ struct MockBrlapiConnectionData { bool connected; size_t display_columns; size_t display_rows; + size_t cell_size; brlapi_error_t error; std::vector<std::string> written_content; // List of brlapi key codes. A negative number makes the connection mock @@ -69,8 +69,8 @@ class MockBrlapiConnection : public BrlapiConnection { data_->connected = true; on_data_ready_ = on_data_ready; if (!data_->pending_keys.empty()) { - base::PostTask(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&MockBrlapiConnection::NotifyDataReady, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&MockBrlapiConnection::NotifyDataReady, base::Unretained(this))); } return CONNECT_SUCCESS; @@ -80,8 +80,8 @@ class MockBrlapiConnection : public BrlapiConnection { data_->connected = false; if (data_->reappear_on_disconnect) { data_->display_columns *= 2; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &BrailleControllerImpl::PokeSocketDirForTesting, base::Unretained(BrailleControllerImpl::GetInstance()))); @@ -125,12 +125,17 @@ class MockBrlapiConnection : public BrlapiConnection { } } + bool GetCellSize(unsigned int* cell_size) override { + *cell_size = data_->cell_size; + return true; + } + private: void NotifyDataReady() { on_data_ready_.Run(); if (!data_->pending_keys.empty()) { - base::PostTask(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&MockBrlapiConnection::NotifyDataReady, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&MockBrlapiConnection::NotifyDataReady, base::Unretained(this))); } } @@ -152,6 +157,7 @@ class BrailleDisplayPrivateApiTest : public ExtensionApiTest { base::Bind( &BrailleDisplayPrivateApiTest::CreateBrlapiConnection, base::Unretained(this))); + BrailleControllerImpl::GetInstance()->skip_libbrlapi_so_load_ = true; DisableAccessibilityManagerBraille(); } @@ -178,6 +184,7 @@ class BrailleDisplayPrivateApiTest : public ExtensionApiTest { IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) { connection_data_.display_columns = 11; connection_data_.display_rows = 1; + connection_data_.cell_size = 6; ASSERT_TRUE(RunComponentExtensionTest("braille_display_private/write_dots")) << message_; ASSERT_EQ(3U, connection_data_.written_content.size()); @@ -195,6 +202,7 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, WriteDots) { IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) { connection_data_.display_columns = 11; connection_data_.display_rows = 1; + connection_data_.cell_size = 6; // Braille navigation commands. connection_data_.pending_keys.push_back(BRLAPI_KEY_TYPE_CMD | @@ -259,6 +267,7 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, KeyEvents) { IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateApiTest, DisplayStateChanges) { connection_data_.display_columns = 11; connection_data_.display_rows = 1; + connection_data_.cell_size = 6; connection_data_.pending_keys.push_back(kErrorKeyCode); connection_data_.reappear_on_disconnect = true; ASSERT_TRUE(RunComponentExtensionTest( @@ -321,7 +330,7 @@ IN_PROC_BROWSER_TEST_F(BrailleDisplayPrivateAPIUserTest, KeyEventOnLockScreen) { // Make sure the signin profile and active profile are different. Profile* signin_profile = chromeos::ProfileHelper::GetSigninProfile(); Profile* user_profile = ProfileManager::GetActiveUserProfile(); - ASSERT_FALSE(signin_profile->IsSameProfile(user_profile)) + ASSERT_FALSE(signin_profile->IsSameOrParent(user_profile)) << signin_profile->GetDebugName() << " vs " << user_profile->GetDebugName(); diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.cc b/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.cc index 4995fa32b19..3d04c0cdc44 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.cc @@ -7,6 +7,7 @@ #include <errno.h> #include "base/files/file_descriptor_watcher_posix.h" +#include "base/logging.h" #include "base/memory/free_deleter.h" #include "base/stl_util.h" #include "base/system/sys_info.h" @@ -44,6 +45,7 @@ class BrlapiConnectionImpl : public BrlapiConnection { bool GetDisplaySize(unsigned int* rows, unsigned int* columns) override; bool WriteDots(const std::vector<unsigned char>& cells) override; int ReadKey(brlapi_keyCode_t* keyCode) override; + bool GetCellSize(unsigned int* cell_size) override; private: bool CheckConnected(); @@ -180,6 +182,23 @@ int BrlapiConnectionImpl::ReadKey(brlapi_keyCode_t* key_code) { handle_.get(), 0 /*wait*/, key_code); } +bool BrlapiConnectionImpl::GetCellSize(unsigned int* cell_size) { + if (!CheckConnected()) { + return false; + } + + brlapi_param_deviceCellSize_t device_cell_size; + ssize_t result = libbrlapi_loader_->brlapi__getParameter( + handle_.get(), BRLAPI_PARAM_DEVICE_CELL_SIZE, 0, BRLAPI_PARAMF_GLOBAL, + &device_cell_size, sizeof(device_cell_size)); + + if (result == -1 || result != sizeof(device_cell_size)) + return false; + + *cell_size = device_cell_size; + return true; +} + bool BrlapiConnectionImpl::CheckConnected() { if (!handle_) { BrlapiError()->brlerrno = BRLAPI_ERROR_ILLEGAL_INSTRUCTION; diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.h b/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.h index 0d1fddeb00a..3a7d028e6a8 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.h +++ b/chromium/chrome/browser/extensions/api/braille_display_private/brlapi_connection.h @@ -67,6 +67,9 @@ class BrlapiConnection { // value. virtual int ReadKey(brlapi_keyCode_t* keyCode) = 0; + // Gets the number of dots in a braille cell. + virtual bool GetCellSize(unsigned int* cell_size) = 0; + protected: BrlapiConnection(); DISALLOW_COPY_AND_ASSIGN(BrlapiConnection); diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/mock_braille_controller.cc b/chromium/chrome/browser/extensions/api/braille_display_private/mock_braille_controller.cc index 0e07e7aa2e6..2b2b867331b 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/mock_braille_controller.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/mock_braille_controller.cc @@ -9,7 +9,7 @@ namespace api { namespace braille_display_private { MockBrailleController::MockBrailleController() - : available_(false), observer_(NULL) {} + : available_(false), observer_(nullptr) {} std::unique_ptr<DisplayState> MockBrailleController::GetDisplayState() { std::unique_ptr<DisplayState> state(new DisplayState()); @@ -22,13 +22,13 @@ std::unique_ptr<DisplayState> MockBrailleController::GetDisplayState() { } void MockBrailleController::AddObserver(BrailleObserver* observer) { - CHECK(observer_ == NULL); + CHECK(!observer_); observer_ = observer; } void MockBrailleController::RemoveObserver(BrailleObserver* observer) { CHECK(observer == observer_); - observer_ = NULL; + observer_ = nullptr; } void MockBrailleController::SetAvailable(bool available) { 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 3ec6c150da5..a769041140d 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 @@ -12,7 +12,6 @@ #include <utility> #include "base/bind.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/values.h" #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h" @@ -102,7 +101,7 @@ static_assert((kFilterableDataTypes & "kFilterableDataTypes must be a subset of " "ChromeBrowsingDataRemoverDelegate::FILTERABLE_DATA_TYPES"); -int MaskForKey(const char* key) { +uint64_t MaskForKey(const char* key) { if (strcmp(key, extension_browsing_data_api_constants::kAppCacheKey) == 0) return content::BrowsingDataRemover::DATA_TYPE_APP_CACHE; if (strcmp(key, extension_browsing_data_api_constants::kCacheKey) == 0) @@ -133,12 +132,12 @@ int MaskForKey(const char* key) { if (strcmp(key, extension_browsing_data_api_constants::kWebSQLKey) == 0) return content::BrowsingDataRemover::DATA_TYPE_WEB_SQL; - return 0; + return 0ULL; } // Returns false if any of the selected data types are not allowed to be // deleted. -bool IsRemovalPermitted(int removal_mask, PrefService* prefs) { +bool IsRemovalPermitted(uint64_t removal_mask, PrefService* prefs) { // Enterprise policy or user preference might prohibit deleting browser or // download history. if ((removal_mask & ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY) || @@ -391,8 +390,8 @@ void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported( if (!PluginDataRemoverHelper::IsSupported(plugin_prefs.get())) removal_mask_ &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA; - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&BrowsingDataRemoverFunction::StartRemoving, this)); } #endif @@ -456,7 +455,7 @@ void BrowsingDataRemoverFunction::StartRemoving() { bool BrowsingDataRemoverFunction::ParseOriginTypeMask( const base::DictionaryValue& options, - int* origin_type_mask) { + uint64_t* origin_type_mask) { // Parse the |options| dictionary to generate the origin set mask. Default to // UNPROTECTED_WEB if the developer doesn't specify anything. *origin_type_mask = content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB; @@ -533,7 +532,7 @@ bool BrowsingDataRemoverFunction::ParseOrigins(const base::Value& list_value, // Parses the |dataToRemove| argument to generate the removal mask. // Returns false if parse was not successful, i.e. if 'dataToRemove' is not // present or any data-type keys don't have supported (boolean) values. -bool BrowsingDataRemoveFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveFunction::GetRemovalMask(uint64_t* removal_mask) { base::DictionaryValue* data_to_remove; if (!args_->GetDictionary(1, &data_to_remove)) return false; @@ -556,73 +555,82 @@ bool BrowsingDataRemoveFunction::IsPauseSyncAllowed() { return false; } -bool BrowsingDataRemoveAppcacheFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveAppcacheFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_APP_CACHE; return true; } -bool BrowsingDataRemoveCacheFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveCacheFunction::GetRemovalMask(uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE; return true; } -bool BrowsingDataRemoveCookiesFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveCookiesFunction::GetRemovalMask(uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_COOKIES; return true; } -bool BrowsingDataRemoveDownloadsFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveDownloadsFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS; return true; } -bool BrowsingDataRemoveFileSystemsFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveFileSystemsFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_FILE_SYSTEMS; return true; } -bool BrowsingDataRemoveFormDataFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveFormDataFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_FORM_DATA; return true; } -bool BrowsingDataRemoveHistoryFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveHistoryFunction::GetRemovalMask(uint64_t* removal_mask) { *removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY; return true; } -bool BrowsingDataRemoveIndexedDBFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveIndexedDBFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_INDEXED_DB; return true; } -bool BrowsingDataRemoveLocalStorageFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveLocalStorageFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_LOCAL_STORAGE; return true; } -bool BrowsingDataRemovePluginDataFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemovePluginDataFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA; return true; } -bool BrowsingDataRemovePasswordsFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemovePasswordsFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PASSWORDS; return true; } bool BrowsingDataRemoveServiceWorkersFunction::GetRemovalMask( - int* removal_mask) { + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_SERVICE_WORKERS; return true; } -bool BrowsingDataRemoveCacheStorageFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveCacheStorageFunction::GetRemovalMask( + uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE_STORAGE; return true; } -bool BrowsingDataRemoveWebSQLFunction::GetRemovalMask(int* removal_mask) { +bool BrowsingDataRemoveWebSQLFunction::GetRemovalMask(uint64_t* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_WEB_SQL; return true; } diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.h b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.h index 6d39a5dfc38..e181262ab99 100644 --- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.h +++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_api.h @@ -116,7 +116,7 @@ class BrowsingDataRemoverFunction // based on the API call they represent. // Returns whether or not removal mask retrieval was successful. // |removal_mask| is populated with the result, if successful. - virtual bool GetRemovalMask(int* removal_mask) = 0; + virtual bool GetRemovalMask(uint64_t* removal_mask) = 0; // Returns true if the data removal is allowed to pause Sync. Returns true by // default. Subclasses can override it to return false and prevent Sync from @@ -135,7 +135,7 @@ class BrowsingDataRemoverFunction // that can be used with the BrowsingDataRemover. // Returns true if parsing was successful. bool ParseOriginTypeMask(const base::DictionaryValue& options, - int* origin_type_mask); + uint64_t* origin_type_mask); // Parses the developer-provided list of origins into |result|. // Returns whether or not parsing was successful. In case of parse failure, @@ -152,8 +152,8 @@ class BrowsingDataRemoverFunction void OnTaskFinished(); base::Time remove_since_; - int removal_mask_ = 0; - int origin_type_mask_ = 0; + uint64_t removal_mask_ = 0; + uint64_t origin_type_mask_ = 0; std::vector<url::Origin> origins_; content::BrowsingDataFilterBuilder::Mode mode_ = content::BrowsingDataFilterBuilder::Mode::BLACKLIST; @@ -174,7 +174,7 @@ class BrowsingDataRemoveAppcacheFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveAppcacheFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveFunction : public BrowsingDataRemoverFunction { @@ -185,7 +185,7 @@ class BrowsingDataRemoveFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; bool IsPauseSyncAllowed() override; }; @@ -198,7 +198,7 @@ class BrowsingDataRemoveCacheFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveCacheFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveCookiesFunction : public BrowsingDataRemoverFunction { @@ -210,7 +210,7 @@ class BrowsingDataRemoveCookiesFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveCookiesFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveDownloadsFunction : public BrowsingDataRemoverFunction { @@ -222,7 +222,7 @@ class BrowsingDataRemoveDownloadsFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveDownloadsFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveFileSystemsFunction @@ -235,7 +235,7 @@ class BrowsingDataRemoveFileSystemsFunction ~BrowsingDataRemoveFileSystemsFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveFormDataFunction : public BrowsingDataRemoverFunction { @@ -247,7 +247,7 @@ class BrowsingDataRemoveFormDataFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveFormDataFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveHistoryFunction : public BrowsingDataRemoverFunction { @@ -259,7 +259,7 @@ class BrowsingDataRemoveHistoryFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveHistoryFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveIndexedDBFunction : public BrowsingDataRemoverFunction { @@ -271,7 +271,7 @@ class BrowsingDataRemoveIndexedDBFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveIndexedDBFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveLocalStorageFunction @@ -284,7 +284,7 @@ class BrowsingDataRemoveLocalStorageFunction ~BrowsingDataRemoveLocalStorageFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemovePluginDataFunction @@ -297,7 +297,7 @@ class BrowsingDataRemovePluginDataFunction ~BrowsingDataRemovePluginDataFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemovePasswordsFunction : public BrowsingDataRemoverFunction { @@ -309,7 +309,7 @@ class BrowsingDataRemovePasswordsFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemovePasswordsFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveServiceWorkersFunction @@ -322,7 +322,7 @@ class BrowsingDataRemoveServiceWorkersFunction ~BrowsingDataRemoveServiceWorkersFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveCacheStorageFunction @@ -335,7 +335,7 @@ class BrowsingDataRemoveCacheStorageFunction ~BrowsingDataRemoveCacheStorageFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; class BrowsingDataRemoveWebSQLFunction : public BrowsingDataRemoverFunction { @@ -347,7 +347,7 @@ class BrowsingDataRemoveWebSQLFunction : public BrowsingDataRemoverFunction { ~BrowsingDataRemoveWebSQLFunction() override {} // BrowsingDataRemoverFunction: - bool GetRemovalMask(int* removal_mask) override; + bool GetRemovalMask(uint64_t* removal_mask) override; }; #endif // CHROME_BROWSER_EXTENSIONS_API_BROWSING_DATA_BROWSING_DATA_API_H_ diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc index 9713ed1a3d0..8dc6ced4f5b 100644 --- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc +++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_test.cc @@ -29,6 +29,7 @@ #include "google_apis/gaia/google_service_auth_error.h" #include "mojo/public/cpp/bindings/callback_helpers.h" #include "net/cookies/canonical_cookie.h" +#include "net/cookies/cookie_inclusion_status.h" #include "url/gurl.h" using extension_function_test_utils::RunFunctionAndReturnSingleResult; @@ -61,10 +62,9 @@ bool SetGaiaCookieForProfile(Profile* profile) { bool success = false; base::RunLoop loop; base::OnceClosure loop_quit = loop.QuitClosure(); - base::OnceCallback<void(net::CanonicalCookie::CookieInclusionStatus)> - callback = base::BindLambdaForTesting( - [&success, - &loop_quit](net::CanonicalCookie::CookieInclusionStatus s) { + base::OnceCallback<void(net::CookieInclusionStatus)> callback = + base::BindLambdaForTesting( + [&success, &loop_quit](net::CookieInclusionStatus s) { success = s.IsInclude(); std::move(loop_quit).Run(); }); @@ -74,9 +74,9 @@ bool SetGaiaCookieForProfile(Profile* profile) { cookie_manager->SetCanonicalCookie( cookie, google_url, net::CookieOptions::MakeAllInclusive(), mojo::WrapCallbackWithDefaultInvokeIfNotRun( - std::move(callback), net::CanonicalCookie::CookieInclusionStatus( - net::CanonicalCookie::CookieInclusionStatus:: - EXCLUDE_UNKNOWN_ERROR))); + std::move(callback), + net::CookieInclusionStatus( + net::CookieInclusionStatus::EXCLUDE_UNKNOWN_ERROR))); loop.Run(); return success; } diff --git a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc index eb659cae14f..953451e762f 100644 --- a/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc +++ b/chromium/chrome/browser/extensions/api/browsing_data/browsing_data_unittest.cc @@ -72,15 +72,17 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { return remover_->GetLastUsedBeginTimeForTesting(); } - int GetRemovalMask() { return remover_->GetLastUsedRemovalMaskForTesting(); } + uint64_t GetRemovalMask() { + return remover_->GetLastUsedRemovalMaskForTesting(); + } - int GetOriginTypeMask() { + uint64_t GetOriginTypeMask() { return remover_->GetLastUsedOriginTypeMaskForTesting(); } - int GetAsMask(const base::DictionaryValue* dict, - std::string path, - int mask_value) { + uint64_t GetAsMask(const base::DictionaryValue* dict, + std::string path, + uint64_t mask_value) { bool result; EXPECT_TRUE(dict->GetBoolean(path, &result)) << "for " << path; return result ? mask_value : 0; @@ -88,7 +90,7 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { void RunBrowsingDataRemoveFunctionAndCompareRemovalMask( const std::string& data_types, - int expected_mask) { + uint64_t expected_mask) { auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>(); SCOPED_TRACE(data_types); EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( @@ -99,15 +101,16 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { EXPECT_EQ(UNPROTECTED_WEB, GetOriginTypeMask()); } - void RunBrowsingDataRemoveWithKeyAndCompareRemovalMask(const std::string& key, - int expected_mask) { + void RunBrowsingDataRemoveWithKeyAndCompareRemovalMask( + const std::string& key, + uint64_t expected_mask) { RunBrowsingDataRemoveFunctionAndCompareRemovalMask( std::string("{\"") + key + "\": true}", expected_mask); } void RunBrowsingDataRemoveFunctionAndCompareOriginTypeMask( const std::string& protectedStr, - int expected_mask) { + uint64_t expected_mask) { auto function = base::MakeRefCounted<BrowsingDataRemoveFunction>(); SCOPED_TRACE(protectedStr); EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( @@ -119,7 +122,7 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { } template <class ShortcutFunction> - void RunAndCompareRemovalMask(int expected_mask) { + void RunAndCompareRemovalMask(uint64_t expected_mask) { scoped_refptr<ShortcutFunction> function = new ShortcutFunction(); SCOPED_TRACE(ShortcutFunction::function_name()); EXPECT_EQ(NULL, @@ -161,8 +164,8 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { } void SetPrefsAndVerifySettings(int data_type_flags, - int expected_origin_type_mask, - int expected_removal_mask) { + uint64_t expected_origin_type_mask, + uint64_t expected_removal_mask) { PrefService* prefs = browser()->profile()->GetPrefs(); prefs->SetInteger( browsing_data::prefs::kLastClearBrowsingDataTab, @@ -200,8 +203,8 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { } void SetBasicPrefsAndVerifySettings(int data_type_flags, - int expected_origin_type_mask, - int expected_removal_mask) { + uint64_t expected_origin_type_mask, + uint64_t expected_removal_mask) { PrefService* prefs = browser()->profile()->GetPrefs(); prefs->SetInteger( browsing_data::prefs::kLastClearBrowsingDataTab, @@ -223,8 +226,8 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { VerifyRemovalMask(expected_origin_type_mask, expected_removal_mask); } - void VerifyRemovalMask(int expected_origin_type_mask, - int expected_removal_mask) { + void VerifyRemovalMask(uint64_t expected_origin_type_mask, + uint64_t expected_removal_mask) { scoped_refptr<BrowsingDataSettingsFunction> function = new BrowsingDataSettingsFunction(); SCOPED_TRACE("settings"); @@ -238,7 +241,7 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { EXPECT_TRUE(result->GetDictionary("options", &options)); base::DictionaryValue* origin_types; EXPECT_TRUE(options->GetDictionary("originTypes", &origin_types)); - int origin_type_mask = + uint64_t origin_type_mask = GetAsMask(origin_types, "unprotectedWeb", UNPROTECTED_WEB) | GetAsMask(origin_types, "protectedWeb", PROTECTED_WEB) | GetAsMask(origin_types, "extension", EXTENSION); @@ -246,7 +249,7 @@ class BrowsingDataApiTest : public ExtensionServiceTestBase { base::DictionaryValue* data_to_remove; EXPECT_TRUE(result->GetDictionary("dataToRemove", &data_to_remove)); - int removal_mask = + uint64_t removal_mask = GetAsMask(data_to_remove, "appcache", content::BrowsingDataRemover::DATA_TYPE_APP_CACHE) | GetAsMask(data_to_remove, "cache", @@ -467,9 +470,9 @@ TEST_F(BrowsingDataApiTest, BrowsingDataRemovalInputFromSettings) { prefs->SetBoolean(browsing_data::prefs::kDeleteHostedAppsData, false); prefs->SetBoolean(browsing_data::prefs::kDeletePasswords, false); prefs->SetBoolean(prefs::kClearPluginLSODataEnabled, false); - int expected_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE | - content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS | - ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY; + uint64_t expected_mask = content::BrowsingDataRemover::DATA_TYPE_CACHE | + content::BrowsingDataRemover::DATA_TYPE_DOWNLOADS | + ChromeBrowsingDataRemoverDelegate::DATA_TYPE_HISTORY; std::string json; // Scoping for the traces. { diff --git a/chromium/chrome/browser/extensions/api/cast_streaming/cast_streaming_apitest.cc b/chromium/chrome/browser/extensions/api/cast_streaming/cast_streaming_apitest.cc deleted file mode 100644 index 6ebf8e5713e..00000000000 --- a/chromium/chrome/browser/extensions/api/cast_streaming/cast_streaming_apitest.cc +++ /dev/null @@ -1,420 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> -#include <cmath> -#include <memory> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/macros.h" -#include "base/run_loop.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/stringprintf.h" -#include "base/threading/thread_restrictions.h" -#include "build/build_config.h" -#include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/common/chrome_switches.h" -#include "content/public/common/content_features.h" -#include "content/public/common/content_switches.h" -#include "content/public/test/browser_test.h" -#include "extensions/common/switches.h" -#include "media/base/bind_to_current_loop.h" -#include "media/base/video_frame.h" -#include "media/cast/cast_config.h" -#include "media/cast/cast_environment.h" -#include "media/cast/test/utility/audio_utility.h" -#include "media/cast/test/utility/default_config.h" -#include "media/cast/test/utility/in_process_receiver.h" -#include "media/cast/test/utility/net_utility.h" -#include "media/cast/test/utility/standalone_cast_environment.h" -#include "net/base/net_errors.h" -#include "net/base/rand_callback.h" -#include "net/log/net_log_source.h" -#include "net/socket/udp_server_socket.h" -#include "testing/gtest/include/gtest/gtest.h" - -using media::cast::test::GetFreeLocalPort; - -namespace extensions { - -class CastStreamingApiTest : public ExtensionApiTest { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - ExtensionApiTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII( - extensions::switches::kWhitelistedExtensionID, - "ddchlicdkolnonkihahngkmmmjnjlkkf"); - command_line->AppendSwitchASCII(::switches::kWindowSize, "300,300"); - } -}; - -// Test running the test extension for Cast Mirroring API. -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, Basics) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "basics.html")) << message_; -} - -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, Stats) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "stats.html")) << message_; -} - -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, BadLogging) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "bad_logging.html")) - << message_; -} - -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, DestinationNotSet) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "destination_not_set.html")) - << message_; -} - -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, StopNoStart) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "stop_no_start.html")) - << message_; -} - -IN_PROC_BROWSER_TEST_F(CastStreamingApiTest, NullStream) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "null_stream.html")) - << message_; -} - -namespace { - -struct YUVColor { - int y; - int u; - int v; - - YUVColor() : y(0), u(0), v(0) {} - YUVColor(int y_val, int u_val, int v_val) : y(y_val), u(u_val), v(v_val) {} -}; - -media::cast::FrameReceiverConfig WithFakeAesKeyAndIv( - media::cast::FrameReceiverConfig config) { - config.aes_key = "0123456789abcdef"; - config.aes_iv_mask = "fedcba9876543210"; - return config; -} - -// An in-process Cast receiver that examines the audio/video frames being -// received for expected colors and tones. Used in -// CastStreamingApiTest.EndToEnd, below. -class TestPatternReceiver : public media::cast::InProcessReceiver { - public: - explicit TestPatternReceiver( - const scoped_refptr<media::cast::CastEnvironment>& cast_environment, - const net::IPEndPoint& local_end_point) - : InProcessReceiver( - cast_environment, - local_end_point, - net::IPEndPoint(), - WithFakeAesKeyAndIv(media::cast::GetDefaultAudioReceiverConfig()), - WithFakeAesKeyAndIv(media::cast::GetDefaultVideoReceiverConfig())) { - } - - ~TestPatternReceiver() override {} - - void AddExpectedTone(int tone_frequency) { - expected_tones_.push_back(tone_frequency); - } - - void AddExpectedColor(const YUVColor& yuv_color) { - expected_yuv_colors_.push_back(yuv_color); - } - - // Blocks the caller until all expected tones and colors have been observed. - void WaitForExpectedTonesAndColors() { - base::RunLoop run_loop; - cast_env()->PostTask( - media::cast::CastEnvironment::MAIN, FROM_HERE, - base::BindOnce( - &TestPatternReceiver::NotifyOnceObservedAllTonesAndColors, - base::Unretained(this), - media::BindToCurrentLoop(run_loop.QuitClosure()))); - run_loop.Run(); - } - - private: - void NotifyOnceObservedAllTonesAndColors(const base::Closure& done_callback) { - DCHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN)); - done_callback_ = done_callback; - MaybeRunDoneCallback(); - } - - void MaybeRunDoneCallback() { - DCHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN)); - if (done_callback_.is_null()) - return; - if (expected_tones_.empty() && expected_yuv_colors_.empty()) { - std::move(done_callback_).Run(); - } else { - LOG(INFO) << "Waiting to encounter " << expected_tones_.size() - << " more tone(s) and " << expected_yuv_colors_.size() - << " more color(s)."; - } - } - - // Invoked by InProcessReceiver for each received audio frame. - void OnAudioFrame(std::unique_ptr<media::AudioBus> audio_frame, - base::TimeTicks playout_time, - bool is_continuous) override { - DCHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN)); - - if (audio_frame->frames() <= 0) { - NOTREACHED() << "OnAudioFrame called with no samples?!?"; - return; - } - - if (done_callback_.is_null() || expected_tones_.empty()) - return; // No need to waste CPU doing analysis on the signal. - - // Assume the audio signal is a single sine wave (it can have some - // low-amplitude noise). Count zero crossings, and extrapolate the - // frequency of the sine wave in |audio_frame|. - int crossings = 0; - for (int ch = 0; ch < audio_frame->channels(); ++ch) { - crossings += media::cast::CountZeroCrossings(audio_frame->channel(ch), - audio_frame->frames()); - } - crossings /= audio_frame->channels(); // Take the average. - const float seconds_per_frame = - audio_frame->frames() / static_cast<float>(audio_config().rtp_timebase); - const float frequency = crossings / seconds_per_frame / 2.0f; - VLOG(1) << "Current audio tone frequency: " << frequency; - - const int kTargetWindowHz = 20; - for (auto it = expected_tones_.begin(); it != expected_tones_.end(); ++it) { - if (abs(static_cast<int>(frequency) - *it) < kTargetWindowHz) { - LOG(INFO) << "Heard tone at frequency " << *it << " Hz."; - expected_tones_.erase(it); - MaybeRunDoneCallback(); - break; - } - } - } - - void OnVideoFrame(scoped_refptr<media::VideoFrame> video_frame, - base::TimeTicks playout_time, - bool is_continuous) override { - DCHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN)); - - CHECK(video_frame->format() == media::PIXEL_FORMAT_YV12 || - video_frame->format() == media::PIXEL_FORMAT_I420 || - video_frame->format() == media::PIXEL_FORMAT_I420A); - - if (done_callback_.is_null() || expected_yuv_colors_.empty()) - return; // No need to waste CPU doing analysis on the frame. - - // Take the median value of each plane because the test image will contain a - // letterboxed content region of mostly a solid color plus a small piece of - // "something" that's animating to keep the tab capture pipeline generating - // new frames. - const gfx::Rect region = FindLetterboxedContentRegion(*video_frame); - YUVColor current_color; - current_color.y = ComputeMedianIntensityInRegionInPlane( - region, - video_frame->stride(media::VideoFrame::kYPlane), - video_frame->data(media::VideoFrame::kYPlane)); - current_color.u = ComputeMedianIntensityInRegionInPlane( - gfx::ScaleToEnclosedRect(region, 0.5f), - video_frame->stride(media::VideoFrame::kUPlane), - video_frame->data(media::VideoFrame::kUPlane)); - current_color.v = ComputeMedianIntensityInRegionInPlane( - gfx::ScaleToEnclosedRect(region, 0.5f), - video_frame->stride(media::VideoFrame::kVPlane), - video_frame->data(media::VideoFrame::kVPlane)); - VLOG(1) << "Current video color: yuv(" << current_color.y << ", " - << current_color.u << ", " << current_color.v << ')'; - - // Note: The range of acceptable colors is quite large because there's no - // way to know whether software compositing is being used for screen - // capture; and, if software compositing is being used, there is no color - // space management and color values can be off by a lot. That said, color - // accuracy is being tested by a suite of content_browsertests. - const int kTargetWindow = 50; - for (auto it = expected_yuv_colors_.begin(); - it != expected_yuv_colors_.end(); ++it) { - if (abs(current_color.y - it->y) < kTargetWindow && - abs(current_color.u - it->u) < kTargetWindow && - abs(current_color.v - it->v) < kTargetWindow) { - LOG(INFO) << "Saw expected color yuv(" << it->y << ", " << it->u << ", " - << it->v << ") as yuv(" << current_color.y << ", " - << current_color.u << ", " << current_color.v << ")."; - expected_yuv_colors_.erase(it); - MaybeRunDoneCallback(); - break; - } - } - } - - // Return the region that excludes the black letterboxing borders surrounding - // the content within |frame|, if any. - static gfx::Rect FindLetterboxedContentRegion( - const media::VideoFrame& frame) { - const int kNonBlackIntensityThreshold = 20; // 16 plus some fuzz. - const int width = frame.row_bytes(media::VideoFrame::kYPlane); - const int height = frame.rows(media::VideoFrame::kYPlane); - const int stride = frame.stride(media::VideoFrame::kYPlane); - - gfx::Rect result; - - // Scan from the bottom-right until the first non-black pixel is - // encountered. - for (int y = height - 1; y >= 0; --y) { - const uint8_t* const start = - frame.data(media::VideoFrame::kYPlane) + y * stride; - const uint8_t* const end = start + width; - for (const uint8_t* p = end - 1; p >= start; --p) { - if (*p > kNonBlackIntensityThreshold) { - result.set_width(p - start + 1); - result.set_height(y + 1); - y = 0; // Discontinue outer loop. - break; - } - } - } - - // Scan from the upper-left until the first non-black pixel is encountered. - for (int y = 0; y < result.height(); ++y) { - const uint8_t* const start = - frame.data(media::VideoFrame::kYPlane) + y * stride; - const uint8_t* const end = start + result.width(); - for (const uint8_t* p = start; p < end; ++p) { - if (*p > kNonBlackIntensityThreshold) { - result.set_x(p - start); - result.set_width(result.width() - result.x()); - result.set_y(y); - result.set_height(result.height() - result.y()); - y = result.height(); // Discontinue outer loop. - break; - } - } - } - - return result; - } - - static uint8_t ComputeMedianIntensityInRegionInPlane(const gfx::Rect& region, - int stride, - const uint8_t* data) { - if (region.IsEmpty()) - return 0; - const size_t num_values = region.size().GetArea(); - std::unique_ptr<uint8_t[]> values(new uint8_t[num_values]); - for (int y = 0; y < region.height(); ++y) { - memcpy(values.get() + y * region.width(), - data + (region.y() + y) * stride + region.x(), - region.width()); - } - const size_t middle_idx = num_values / 2; - std::nth_element(values.get(), - values.get() + middle_idx, - values.get() + num_values); - return values[middle_idx]; - } - - std::vector<int> expected_tones_; - std::vector<YUVColor> expected_yuv_colors_; - base::Closure done_callback_; - - DISALLOW_COPY_AND_ASSIGN(TestPatternReceiver); -}; - -} // namespace - -class CastStreamingApiTestWithPixelOutput - : public CastStreamingApiTest, - public testing::WithParamInterface<bool> { - public: - - void SetUp() override { - EnablePixelOutput(); - CastStreamingApiTest::SetUp(); - } - - void SetUpCommandLine(base::CommandLine* command_line) override { - command_line->AppendSwitchASCII(::switches::kWindowSize, "128,128"); - CastStreamingApiTest::SetUpCommandLine(command_line); - } - - private: - base::test::ScopedFeatureList audio_service_features_; -}; - -// Tests the Cast streaming API and its basic functionality end-to-end. An -// extension subtest is run to generate test content, capture that content, and -// use the API to send it out. At the same time, this test launches an -// in-process Cast receiver, listening on a localhost UDP socket, to receive the -// content and check whether it matches expectations. -#if defined(NDEBUG) && !defined(OS_MACOSX) -#define MAYBE_EndToEnd EndToEnd -#else -// Flaky on Mac: https://crbug.com/841387 -#define MAYBE_EndToEnd DISABLED_EndToEnd // crbug.com/396413 -#endif -IN_PROC_BROWSER_TEST_F(CastStreamingApiTestWithPixelOutput, MAYBE_EndToEnd) { - std::unique_ptr<net::UDPServerSocket> receive_socket( - new net::UDPServerSocket(NULL, net::NetLogSource())); - receive_socket->AllowAddressReuse(); - ASSERT_EQ(net::OK, receive_socket->Listen(GetFreeLocalPort())); - net::IPEndPoint receiver_end_point; - ASSERT_EQ(net::OK, receive_socket->GetLocalAddress(&receiver_end_point)); - receive_socket.reset(); - - // Start the in-process receiver that examines audio/video for the expected - // test patterns. - const scoped_refptr<media::cast::StandaloneCastEnvironment> cast_environment( - new media::cast::StandaloneCastEnvironment()); - TestPatternReceiver* const receiver = - new TestPatternReceiver(cast_environment, receiver_end_point); - - // Launch the page that: 1) renders the source content; 2) uses the - // chrome.tabCapture and chrome.cast.streaming APIs to capture its content and - // stream using Cast; and 3) calls chrome.test.succeed() once it is - // operational. - const std::string page_url = base::StringPrintf( - "end_to_end_sender.html?port=%d&aesKey=%s&aesIvMask=%s", - receiver_end_point.port(), - base::HexEncode(receiver->audio_config().aes_key.data(), - receiver->audio_config().aes_key.size()).c_str(), - base::HexEncode(receiver->audio_config().aes_iv_mask.data(), - receiver->audio_config().aes_iv_mask.size()).c_str()); - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", page_url)) << message_; - - // Examine the Cast receiver for expected audio/video test patterns. The - // colors and tones specified here must match those in end_to_end_sender.js. - // Note that we do not check that the color and tone are received - // simultaneously since A/V sync should be measured in perf tests. - receiver->AddExpectedTone(200 /* Hz */); - receiver->AddExpectedTone(500 /* Hz */); - receiver->AddExpectedTone(1800 /* Hz */); - receiver->AddExpectedColor(YUVColor(63, 102, 239)); // rgb(255, 0, 0) - receiver->AddExpectedColor(YUVColor(173, 41, 26)); // rgb(0, 255, 0) - receiver->AddExpectedColor(YUVColor(32, 239, 117)); // rgb(0, 0, 255) - receiver->Start(); - receiver->WaitForExpectedTonesAndColors(); - receiver->Stop(); - - delete receiver; - base::ScopedAllowBlockingForTesting allow_blocking; - cast_environment->Shutdown(); -} - -#if !defined(OS_MACOSX) -#define MAYBE_RtpStreamError RtpStreamError -#else -// Flaky on Mac https://crbug.com/841986 -#define MAYBE_RtpStreamError DISABLED_RtpStreamError -#endif -IN_PROC_BROWSER_TEST_F(CastStreamingApiTestWithPixelOutput, - MAYBE_RtpStreamError) { - ASSERT_TRUE(RunExtensionSubtest("cast_streaming", "rtp_stream_error.html")); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc b/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc index 69a5d995237..5be83c91173 100644 --- a/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc +++ b/chromium/chrome/browser/extensions/api/cast_streaming/performance_test.cc @@ -742,9 +742,14 @@ IN_PROC_BROWSER_TEST_P(CastV2PerformanceTest, DISABLED_Performance) { // Load the extension and test page, and tell the extension to start tab // capture + Cast Streaming. - LoadExtension(GetApiTestDataDir() - .AppendASCII("cast_streaming") - .AppendASCII("perftest_extension")); + + // TODO(https://crbug.com/974427): Update test to no longer require + // extension APIs. + + // LoadExtension(GetApiTestDataDir() + // .AppendASCII("cast_streaming") + // .AppendASCII("perftest_extension")); + NavigateToTestPage(test_page_html_); const base::Value response = SendMessageToExtension(base::StringPrintf( "{start:true, enableAutoThrottling:%s, maxFrameRate:%d, recvPort:%d," diff --git a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc index 80918c5ca00..472210d290f 100644 --- a/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc +++ b/chromium/chrome/browser/extensions/api/certificate_provider/certificate_provider_api.cc @@ -249,8 +249,8 @@ CertificateProviderStopPinRequestFunction:: ExtensionFunction::ResponseAction CertificateProviderStopPinRequestFunction::Run() { - std::unique_ptr<api_cp::RequestPin::Params> params( - api_cp::RequestPin::Params::Create(*args_)); + std::unique_ptr<api_cp::StopPinRequest::Params> params( + api_cp::StopPinRequest::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); // TODO(crbug.com/1046860): Remove logging after stabilizing the feature. 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 57049879e4c..f9fe19fc02a 100644 --- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc +++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.cc @@ -46,15 +46,12 @@ #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" #include "components/pdf/browser/pdf_web_contents_helper.h" -#include "components/performance_manager/embedder/performance_manager_registry.h" -#include "components/performance_manager/public/performance_manager.h" #include "components/signin/core/browser/signin_header_helper.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" -#include "extensions/browser/api/management/supervised_user_service_delegate.h" #include "extensions/browser/api/system_display/display_info_provider.h" #include "extensions/browser/api/virtual_keyboard_private/virtual_keyboard_delegate.h" #include "extensions/browser/api/web_request/web_request_info.h" @@ -62,6 +59,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/guest_view/web_view/web_view_guest.h" #include "extensions/browser/guest_view/web_view/web_view_permission_helper.h" +#include "extensions/browser/supervised_user_extensions_delegate.h" #include "extensions/browser/value_store/value_store_factory.h" #include "google_apis/gaia/gaia_urls.h" #include "printing/buildflags/buildflags.h" @@ -81,7 +79,7 @@ #if BUILDFLAG(ENABLE_SUPERVISED_USERS) // TODO(https://crbug.com/1060801): Here and elsewhere, possibly switch build // flag to #if defined(OS_CHROMEOS) -#include "chrome/browser/supervised_user/supervised_user_service_management_api_delegate.h" +#include "chrome/browser/supervised_user/supervised_user_extensions_delegate_impl.h" #endif namespace extensions { @@ -116,10 +114,6 @@ void ChromeExtensionsAPIClient::AttachWebContentsHelpers( extensions::ChromeExtensionWebContentsObserver::CreateForWebContents( web_contents); - if (auto* performance_manager_registry = - performance_manager::PerformanceManagerRegistry::GetInstance()) { - performance_manager_registry->CreatePageNodeForWebContents(web_contents); - } } bool ChromeExtensionsAPIClient::ShouldHideResponseHeader( @@ -340,10 +334,10 @@ ManagementAPIDelegate* ChromeExtensionsAPIClient::CreateManagementAPIDelegate() return new ChromeManagementAPIDelegate; } -std::unique_ptr<SupervisedUserServiceDelegate> -ChromeExtensionsAPIClient::CreateSupervisedUserServiceDelegate() const { +std::unique_ptr<SupervisedUserExtensionsDelegate> +ChromeExtensionsAPIClient::CreateSupervisedUserExtensionsDelegate() const { #if BUILDFLAG(ENABLE_SUPERVISED_USERS) - return std::make_unique<SupervisedUserServiceManagementAPIDelegate>(); + return std::make_unique<SupervisedUserExtensionsDelegateImpl>(); #else return nullptr; #endif 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 a9498af9053..98d45c99383 100644 --- a/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h +++ b/chromium/chrome/browser/extensions/api/chrome_extensions_api_client.h @@ -67,8 +67,8 @@ class ChromeExtensionsAPIClient : public ExtensionsAPIClient { std::unique_ptr<VirtualKeyboardDelegate> CreateVirtualKeyboardDelegate( content::BrowserContext* browser_context) const override; ManagementAPIDelegate* CreateManagementAPIDelegate() const override; - std::unique_ptr<SupervisedUserServiceDelegate> - CreateSupervisedUserServiceDelegate() const override; + std::unique_ptr<SupervisedUserExtensionsDelegate> + CreateSupervisedUserExtensionsDelegate() const override; std::unique_ptr<DisplayInfoProvider> CreateDisplayInfoProvider() const override; 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 cd71b0bd21c..f21fdbb45dd 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,6 +33,7 @@ #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" +#include "components/content_settings/core/common/features.h" #include "components/permissions/features.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" @@ -311,6 +312,16 @@ ContentSettingsContentSettingSetFunction::Run() { store->SetExtensionContentSetting(extension_id(), primary_pattern, secondary_pattern, content_type, resource_identifier, setting, scope); + + if (base::FeatureList::IsEnabled( + content_settings::kDisallowWildcardsInPluginContentSettings) && + content_type == ContentSettingsType::PLUGINS && + primary_pattern.HasHostWildcards()) { + WriteToConsole( + blink::mojom::ConsoleMessageLevel::kError, + content_settings_api_constants::kWildcardPatternsForPluginsDisallowed); + } + return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc index efd89b9c283..27aa53d3720 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.cc @@ -23,6 +23,8 @@ const char kIncognitoSessionOnlyError[] = "You cannot read incognito content settings when no incognito window " "is open."; const char kInvalidUrlError[] = "The URL \"*\" is invalid."; - +const char kWildcardPatternsForPluginsDisallowed[] = + "Host wildcards ('*') and \"<all_urls>\" are no longer " + "supported in `primaryPattern` for `plugins`."; } // namespace content_settings_api_constants } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h index 492fb00260e..65cef1f6b24 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_api_constants.h @@ -23,7 +23,7 @@ extern const char kSecondaryPatternKey[]; extern const char kIncognitoContextError[]; extern const char kIncognitoSessionOnlyError[]; extern const char kInvalidUrlError[]; - +extern const char kWildcardPatternsForPluginsDisallowed[]; } } // namespace extensions 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 b34033a96b1..60626f7d26f 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 @@ -18,15 +18,19 @@ #include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/extensions/api/content_settings/content_settings_api.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/permissions/permission_manager_factory.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_switches.h" #include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" +#include "components/content_settings/core/common/features.h" #include "components/keep_alive_registry/keep_alive_types.h" #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/browser/plugin_service.h" @@ -36,12 +40,15 @@ #include "content/public/test/test_utils.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/test_extension_registry_observer.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" namespace extensions { +using ContextType = ExtensionApiTest::ContextType; + class ExtensionContentSettingsApiTest : public ExtensionApiTest { public: - ExtensionContentSettingsApiTest() : profile_(NULL) {} + ExtensionContentSettingsApiTest() : profile_(nullptr) {} void SetUpOnMainThread() override { ExtensionApiTest::SetUpOnMainThread(); @@ -89,7 +96,7 @@ class ExtensionContentSettingsApiTest : public ExtensionApiTest { map->GetContentSetting(example_url, example_url, ContentSettingsType::JAVASCRIPT, std::string())); EXPECT_EQ( - CONTENT_SETTING_ALLOW, + CONTENT_SETTING_BLOCK, map->GetContentSetting(example_url, example_url, ContentSettingsType::PLUGINS, std::string())); EXPECT_EQ( @@ -134,7 +141,7 @@ class ExtensionContentSettingsApiTest : public ExtensionApiTest { EXPECT_EQ(CONTENT_SETTING_BLOCK, map->GetContentSetting(url, url, ContentSettingsType::JAVASCRIPT, std::string())); - EXPECT_EQ(CONTENT_SETTING_DETECT_IMPORTANT_CONTENT, + EXPECT_EQ(CONTENT_SETTING_BLOCK, map->GetContentSetting(url, url, ContentSettingsType::PLUGINS, std::string())); EXPECT_EQ(CONTENT_SETTING_ALLOW, @@ -256,6 +263,42 @@ class ExtensionContentSettingsApiTest : public ExtensionApiTest { std::unique_ptr<ScopedKeepAlive> keep_alive_; }; +class ExtensionContentSettingsApiLazyTest + : public ExtensionContentSettingsApiTest, + public testing::WithParamInterface<ContextType> { + public: + void SetUp() override { + ExtensionContentSettingsApiTest::SetUp(); + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); + } + + protected: + bool RunLazyTest(const std::string& extension_name) { + return RunLazyTestWithArg(extension_name, nullptr); + } + + bool RunLazyTestWithArg(const std::string& extension_name, const char* arg) { + int browser_test_flags = kFlagNone; + if (GetParam() == ContextType::kServiceWorker) + browser_test_flags |= kFlagRunAsServiceWorkerBasedExtension; + + return RunExtensionTestWithFlagsAndArg(extension_name, arg, + browser_test_flags, kFlagNone); + } + + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; +}; + +INSTANTIATE_TEST_SUITE_P(EventPage, + ExtensionContentSettingsApiLazyTest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + ExtensionContentSettingsApiLazyTest, + ::testing::Values(ContextType::kServiceWorker)); + IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, Standard) { CheckContentSettingsDefault(); @@ -285,7 +328,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, Standard) { // brittle or just have the JS side look for the additional plugins. // // Flaky on the trybots. See http://crbug.com/96725. -IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, +IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, DISABLED_GetResourceIdentifiers) { base::FilePath::CharType kFooPath[] = FILE_PATH_LITERAL("/plugins/foo.plugin"); @@ -307,21 +350,22 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, base::ASCIIToUTF16("bar")), false); - EXPECT_TRUE(RunExtensionTest("content_settings/getresourceidentifiers")) + EXPECT_TRUE(RunLazyTest("content_settings/getresourceidentifiers")) << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, +IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, UnsupportedDefaultSettings) { const char kExtensionPath[] = "content_settings/unsupporteddefaultsettings"; - EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + EXPECT_TRUE(RunExtensionTest(kExtensionPath)) << message_; } // Tests if an extension clearing content settings for one content type leaves // the others unchanged. -IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, ClearProperlyGranular) { +IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, + ClearProperlyGranular) { const char kExtensionPath[] = "content_settings/clearproperlygranular"; - EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + EXPECT_TRUE(RunLazyTest(kExtensionPath)) << message_; } // Tests if changing permissions in incognito mode keeps the previous state of @@ -333,9 +377,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, IncognitoIsolation) { std::vector<int> content_settings_before = GetContentSettingsSnapshot(url); // Run extension, set all permissions to allow, and check if they are changed. - EXPECT_TRUE(RunExtensionSubtest("content_settings/incognitoisolation", - "test.html?allow", kFlagEnableIncognito, - kFlagUseIncognito)) + EXPECT_TRUE(RunExtensionSubtestWithArgAndFlags( + "content_settings/incognitoisolation", "test.html", "allow", + kFlagEnableIncognito, kFlagUseIncognito)) << message_; // Get content settings after running extension to ensure nothing is changed. @@ -343,9 +387,9 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, IncognitoIsolation) { EXPECT_EQ(content_settings_before, content_settings_after); // Run extension, set all permissions to block, and check if they are changed. - EXPECT_TRUE(RunExtensionSubtest("content_settings/incognitoisolation", - "test.html?block", kFlagEnableIncognito, - kFlagUseIncognito)) + EXPECT_TRUE(RunExtensionSubtestWithArgAndFlags( + "content_settings/incognitoisolation", "test.html", "block", + kFlagEnableIncognito, kFlagUseIncognito)) << message_; // Get content settings after running extension to ensure nothing is changed. @@ -356,16 +400,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, IncognitoIsolation) { // Tests if changing incognito mode permissions in regular profile are rejected. IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, IncognitoNotAllowedInRegular) { - EXPECT_FALSE(RunExtensionSubtest("content_settings/incognitoisolation", - "test.html?allow")) + EXPECT_FALSE(RunExtensionSubtestWithArg("content_settings/incognitoisolation", + "test.html", "allow")) << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, +IN_PROC_BROWSER_TEST_P(ExtensionContentSettingsApiLazyTest, EmbeddedSettingsMetric) { base::HistogramTester histogram_tester; const char kExtensionPath[] = "content_settings/embeddedsettingsmetric"; - EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + EXPECT_TRUE(RunLazyTest(kExtensionPath)) << message_; size_t num_values = 0; int images_type = ContentSettingTypeToHistogramValue( @@ -391,7 +435,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, } class ExtensionContentSettingsApiTestWithPermissionDelegationDisabled - : public ExtensionContentSettingsApiTest { + : public ExtensionContentSettingsApiLazyTest { public: ExtensionContentSettingsApiTestWithPermissionDelegationDisabled() { feature_list_.InitAndDisableFeature( @@ -402,8 +446,18 @@ class ExtensionContentSettingsApiTestWithPermissionDelegationDisabled base::test::ScopedFeatureList feature_list_; }; +INSTANTIATE_TEST_SUITE_P( + EventPage, + ExtensionContentSettingsApiTestWithPermissionDelegationDisabled, + ::testing::Values(ContextType::kEventPage)); + +INSTANTIATE_TEST_SUITE_P( + ServiceWorker, + ExtensionContentSettingsApiTestWithPermissionDelegationDisabled, + ::testing::Values(ContextType::kServiceWorker)); + class ExtensionContentSettingsApiTestWithPermissionDelegationEnabled - : public ExtensionContentSettingsApiTest { + : public ExtensionContentSettingsApiLazyTest { public: ExtensionContentSettingsApiTestWithPermissionDelegationEnabled() { feature_list_.InitAndEnableFeature( @@ -414,20 +468,76 @@ class ExtensionContentSettingsApiTestWithPermissionDelegationEnabled base::test::ScopedFeatureList feature_list_; }; -IN_PROC_BROWSER_TEST_F( +INSTANTIATE_TEST_SUITE_P( + EventPage, + ExtensionContentSettingsApiTestWithPermissionDelegationEnabled, + ::testing::Values(ContextType::kEventPage)); + +INSTANTIATE_TEST_SUITE_P( + ServiceWorker, + ExtensionContentSettingsApiTestWithPermissionDelegationEnabled, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P( ExtensionContentSettingsApiTestWithPermissionDelegationDisabled, EmbeddedSettings) { const char kExtensionPath[] = "content_settings/embeddedsettings"; - EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + EXPECT_TRUE(RunLazyTestWithArg(kExtensionPath, nullptr)) << message_; } -IN_PROC_BROWSER_TEST_F( +IN_PROC_BROWSER_TEST_P( ExtensionContentSettingsApiTestWithPermissionDelegationEnabled, EmbeddedSettings) { const char kExtensionPath[] = "content_settings/embeddedsettings"; - EXPECT_TRUE( - RunExtensionSubtest(kExtensionPath, "test.html?permission_delegation")) - << message_; + EXPECT_TRUE(RunLazyTestWithArg(kExtensionPath, "permission")) << message_; +} + +class ExtensionContentSettingsApiTestWithWildcardMatchingDisabled + : public ExtensionContentSettingsApiLazyTest { + public: + ExtensionContentSettingsApiTestWithWildcardMatchingDisabled() { + scoped_feature_list_.InitAndEnableFeature( + content_settings::kDisallowWildcardsInPluginContentSettings); + } + + private: + base::test::ScopedFeatureList scoped_feature_list_; +}; + +INSTANTIATE_TEST_SUITE_P( + EventPage, + ExtensionContentSettingsApiTestWithWildcardMatchingDisabled, + ::testing::Values(ContextType::kEventPage)); + +INSTANTIATE_TEST_SUITE_P( + ServiceWorker, + ExtensionContentSettingsApiTestWithWildcardMatchingDisabled, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P( + ExtensionContentSettingsApiTestWithWildcardMatchingDisabled, + PluginTest) { + constexpr char kExtensionPath[] = "content_settings/pluginswildcardmatching"; + EXPECT_TRUE(RunLazyTest(kExtensionPath)) << message_; + + constexpr char kGoogleMailUrl[] = "http://mail.google.com:443"; + constexpr char kGoogleDriveUrl[] = "http://drive.google.com:443"; + + permissions::PermissionManager* permission_manager = + PermissionManagerFactory::GetForProfile(browser()->profile()); + EXPECT_EQ( + permission_manager + ->GetPermissionStatus(ContentSettingsType::PLUGINS, + GURL(kGoogleMailUrl), GURL(kGoogleMailUrl)) + .content_setting, + ContentSetting::CONTENT_SETTING_BLOCK); + + EXPECT_EQ( + permission_manager + ->GetPermissionStatus(ContentSettingsType::PLUGINS, + GURL(kGoogleDriveUrl), GURL(kGoogleDriveUrl)) + .content_setting, + ContentSetting::CONTENT_SETTING_ALLOW); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.cc index a8042a43b0e..40b9e99f0de 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.cc @@ -32,6 +32,14 @@ std::unique_ptr<RuleIterator> CustomExtensionProvider::GetRuleIterator( incognito); } +std::unique_ptr<RuleIterator> CustomExtensionProvider::GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const { + return extensions_settings_->GetDiscardedRuleIterator( + content_type, resource_identifier, incognito); +} + bool CustomExtensionProvider::SetWebsiteSetting( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h b/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h index 3532f1f10c9..45e74998fd9 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_custom_extension_provider.h @@ -30,6 +30,11 @@ class CustomExtensionProvider : public ObservableProvider, const ResourceIdentifier& resource_identifier, bool incognito) const override; + std::unique_ptr<RuleIterator> GetDiscardedRuleIterator( + ContentSettingsType content_type, + const ResourceIdentifier& resource_identifier, + bool incognito) const override; + bool SetWebsiteSetting( const ContentSettingsPattern& primary_pattern, const ContentSettingsPattern& secondary_pattern, diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.cc index ca3eab1fb10..bc148ab971c 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.cc @@ -25,6 +25,7 @@ #include "components/content_settings/core/browser/content_settings_utils.h" #include "components/content_settings/core/browser/website_settings_info.h" #include "components/content_settings/core/common/content_settings_utils.h" +#include "components/content_settings/core/common/features.h" #include "components/permissions/features.h" #include "content/public/browser/browser_thread.h" @@ -37,6 +38,51 @@ using content_settings::ResourceIdentifier; namespace extensions { +namespace { + +enum class FilterType { + WANT_DISCARDED_PATTERNS, + WANT_VALID_PATTERNS, +}; + +class FilterRuleIterator : public RuleIterator { + public: + FilterRuleIterator(std::unique_ptr<RuleIterator> iterator, + const FilterType filter_type) + : iterator_(std::move(iterator)), filter_type_(filter_type) {} + + ~FilterRuleIterator() override = default; + + bool HasNext() const override { + if (!iterator_) + return false; + if (current_rule_) + return true; + while (iterator_->HasNext()) { + current_rule_ = iterator_->Next(); + if (!((filter_type_ == FilterType::WANT_DISCARDED_PATTERNS) ^ + current_rule_->primary_pattern.HasHostWildcards())) { + return true; + } + } + return false; + } + + Rule Next() override { + DCHECK(current_rule_.has_value()); + Rule rule = std::move(*current_rule_); + current_rule_.reset(); + return rule; + } + + private: + std::unique_ptr<RuleIterator> iterator_; + const FilterType filter_type_; + mutable base::Optional<Rule> current_rule_; +}; + +} // namespace + struct ContentSettingsStore::ExtensionEntry { // Extension id. std::string id; @@ -63,6 +109,36 @@ std::unique_ptr<RuleIterator> ContentSettingsStore::GetRuleIterator( ContentSettingsType type, const content_settings::ResourceIdentifier& identifier, bool incognito) const { + if (base::FeatureList::IsEnabled( + content_settings::kDisallowWildcardsInPluginContentSettings) && + type == ContentSettingsType::PLUGINS) { + return std::make_unique<FilterRuleIterator>( + GetAllRulesIterator(type, identifier, incognito), + FilterType::WANT_VALID_PATTERNS); + } else { + return GetAllRulesIterator(type, identifier, incognito); + } +} + +std::unique_ptr<RuleIterator> ContentSettingsStore::GetDiscardedRuleIterator( + ContentSettingsType type, + const content_settings::ResourceIdentifier& identifier, + bool incognito) const { + if (base::FeatureList::IsEnabled( + content_settings::kDisallowWildcardsInPluginContentSettings) && + type == ContentSettingsType::PLUGINS) { + return std::make_unique<FilterRuleIterator>( + GetAllRulesIterator(type, identifier, incognito), + FilterType::WANT_DISCARDED_PATTERNS); + } else { + return std::make_unique<content_settings::EmptyRuleIterator>(); + } +} + +std::unique_ptr<RuleIterator> ContentSettingsStore::GetAllRulesIterator( + ContentSettingsType type, + const content_settings::ResourceIdentifier& identifier, + bool incognito) const { std::vector<std::unique_ptr<RuleIterator>> iterators; // The individual |RuleIterators| shouldn't lock; pass |lock_| to the diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.h b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.h index 286201f338a..291975c5aa2 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.h +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store.h @@ -59,6 +59,16 @@ class ContentSettingsStore const content_settings::ResourceIdentifier& identifier, bool incognito) const; + std::unique_ptr<content_settings::RuleIterator> GetDiscardedRuleIterator( + ContentSettingsType type, + const content_settings::ResourceIdentifier& identifier, + bool incognito) const; + + std::unique_ptr<content_settings::RuleIterator> GetAllRulesIterator( + ContentSettingsType type, + const content_settings::ResourceIdentifier& identifier, + bool incognito) const; + // Sets the content |setting| for |pattern| of extension |ext_id|. The // |incognito| flag allow to set whether the provided setting is for // incognito mode only. diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store_unittest.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store_unittest.cc index ec02e12ba9a..51f9f783604 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_store_unittest.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_store_unittest.cc @@ -15,6 +15,7 @@ #include "components/content_settings/core/browser/content_settings_rule.h" #include "components/content_settings/core/browser/content_settings_utils.h" #include "components/content_settings/core/common/content_settings_utils.h" +#include "components/content_settings/core/common/features.h" #include "components/content_settings/core/test/content_settings_test_utils.h" #include "components/permissions/features.h" #include "testing/gmock/include/gmock/gmock.h" @@ -374,4 +375,45 @@ TEST_F(ContentSettingsStoreTest, RemoveEmbedded) { store()->RemoveObserver(&observer); } +TEST_F(ContentSettingsStoreTest, DisallowWildcardsInFlash) { + // Enabling the feature which disallows wildcard matching for Plugin content + // settings. + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + content_settings::kDisallowWildcardsInPluginContentSettings); + + // Register extension. + std::string ext_id("my_extension"); + RegisterExtension(ext_id); + ContentSettingsPattern primary_pattern = + ContentSettingsPattern::FromString("https://[*.]google.com"); + ContentSettingsPattern secondary_pattern = ContentSettingsPattern::Wildcard(); + store()->SetExtensionContentSetting( + ext_id, primary_pattern, secondary_pattern, ContentSettingsType::PLUGINS, + std::string(), CONTENT_SETTING_ALLOW, kExtensionPrefsScopeRegular); + store()->SetExtensionContentSetting( + ext_id, primary_pattern, secondary_pattern, ContentSettingsType::COOKIES, + std::string(), CONTENT_SETTING_ALLOW, kExtensionPrefsScopeRegular); + + std::vector<content_settings::Rule> rules; + rules = GetSettingsForOneTypeFromStore(store(), ContentSettingsType::PLUGINS, + std::string(), false); + // Number of rules will be zero because we tried to add a pattern with + // wildcards. + ASSERT_EQ(rules.size(), 0u); + + rules = GetSettingsForOneTypeFromStore(store(), ContentSettingsType::COOKIES, + std::string(), false); + // Here we will have one rule because wildcard patterns are allowed for + // ContentSettingsType::COOKIES. + ASSERT_EQ(rules.size(), 1u); + + std::unique_ptr<content_settings::RuleIterator> discarded_rules_iterator = + store()->GetDiscardedRuleIterator(ContentSettingsType::PLUGINS, + std::string(), false); + ASSERT_TRUE(discarded_rules_iterator->HasNext()); + ASSERT_EQ(discarded_rules_iterator->Next().primary_pattern, primary_pattern); + ASSERT_FALSE(discarded_rules_iterator->HasNext()); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc b/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc index a4f8124fa44..8710a97caf1 100644 --- a/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc +++ b/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.cc @@ -99,6 +99,10 @@ MenuItem::ContextList GetContexts(const std::vector< // Not available for <webview>. contexts.Add(extensions::MenuItem::PAGE_ACTION); break; + case extensions::api::context_menus::CONTEXT_TYPE_ACTION: + // Not available for <webview>. + contexts.Add(extensions::MenuItem::ACTION); + break; case extensions::api::context_menus::CONTEXT_TYPE_NONE: NOTREACHED(); } diff --git a/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h b/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h index 28335e39113..b45a66a8a0a 100644 --- a/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h +++ b/chromium/chrome/browser/extensions/api/context_menus/context_menus_api_helpers.h @@ -101,7 +101,8 @@ bool CreateMenuItem(const PropertyWithEnumT& create_properties, } if (contexts.Contains(MenuItem::BROWSER_ACTION) || - contexts.Contains(MenuItem::PAGE_ACTION)) { + contexts.Contains(MenuItem::PAGE_ACTION) || + contexts.Contains(MenuItem::ACTION)) { // Action items are not allowed for <webview>. if (!extension->is_extension() || is_webview) { *error = kActionNotAllowedError; diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc b/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc index 8fa3cd840d0..5dbb6f601d9 100644 --- a/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc +++ b/chromium/chrome/browser/extensions/api/cookies/cookies_api.cc @@ -256,16 +256,17 @@ ExtensionFunction::ResponseAction CookiesGetFunction::Run() { } void CookiesGetFunction::GetCookieListCallback( - const net::CookieStatusList& cookie_status_list, - const net::CookieStatusList& excluded_cookies) { + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - for (const net::CookieWithStatus& cookie_with_status : cookie_status_list) { + for (const net::CookieWithAccessResult& cookie_with_access_result : + cookie_list) { // Return the first matching cookie. Relies on the fact that the // CookieManager interface returns them in canonical order (longest path, // then earliest creation time). - if (cookie_with_status.cookie.Name() == parsed_args_->details.name) { + if (cookie_with_access_result.cookie.Name() == parsed_args_->details.name) { api::cookies::Cookie api_cookie = cookies_helpers::CreateCookie( - cookie_with_status.cookie, *parsed_args_->details.store_id); + cookie_with_access_result.cookie, *parsed_args_->details.store_id); Respond(ArgumentList(api::cookies::Get::Results::Create(api_cookie))); return; } @@ -336,14 +337,14 @@ void CookiesGetAllFunction::GetAllCookiesCallback( } void CookiesGetAllFunction::GetCookieListCallback( - const net::CookieStatusList& cookie_status_list, - const net::CookieStatusList& excluded_cookies) { + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies) { DCHECK_CURRENTLY_ON(BrowserThread::UI); ResponseValue response; if (extension()) { std::vector<api::cookies::Cookie> match_vector; - cookies_helpers::AppendMatchingCookiesFromCookieStatusListToVector( - cookie_status_list, &parsed_args_->details, extension(), &match_vector); + cookies_helpers::AppendMatchingCookiesFromCookieAccessResultListToVector( + cookie_list, &parsed_args_->details, extension(), &match_vector); response = ArgumentList(api::cookies::GetAll::Results::Create(match_vector)); @@ -437,7 +438,8 @@ ExtensionFunction::ResponseAction CookiesSetFunction::Run() { // is generated. success_ = false; state_ = SET_COMPLETED; - GetCookieListCallback(net::CookieStatusList(), net::CookieStatusList()); + GetCookieListCallback(net::CookieAccessResultList(), + net::CookieAccessResultList()); return AlreadyResponded(); } @@ -461,7 +463,7 @@ ExtensionFunction::ResponseAction CookiesSetFunction::Run() { } void CookiesSetFunction::SetCanonicalCookieCallback( - net::CanonicalCookie::CookieInclusionStatus set_cookie_result) { + net::CookieInclusionStatus set_cookie_result) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(NO_RESPONSE, state_); state_ = SET_COMPLETED; @@ -469,8 +471,8 @@ void CookiesSetFunction::SetCanonicalCookieCallback( } void CookiesSetFunction::GetCookieListCallback( - const net::CookieStatusList& cookie_list, - const net::CookieStatusList& excluded_cookies) { + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies) { DCHECK_CURRENTLY_ON(BrowserThread::UI); DCHECK_EQ(SET_COMPLETED, state_); state_ = GET_COMPLETED; @@ -485,16 +487,17 @@ void CookiesSetFunction::GetCookieListCallback( } ResponseValue value; - for (const net::CookieWithStatus& cookie_with_status : cookie_list) { + for (const net::CookieWithAccessResult& cookie_with_access_result : + cookie_list) { // Return the first matching cookie. Relies on the fact that the // CookieMonster returns them in canonical order (longest path, then // earliest creation time). std::string name = parsed_args_->details.name.get() ? *parsed_args_->details.name : std::string(); - if (cookie_with_status.cookie.Name() == name) { + if (cookie_with_access_result.cookie.Name() == name) { api::cookies::Cookie api_cookie = cookies_helpers::CreateCookie( - cookie_with_status.cookie, *parsed_args_->details.store_id); + cookie_with_access_result.cookie, *parsed_args_->details.store_id); value = ArgumentList(api::cookies::Set::Results::Create(api_cookie)); break; } diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_api.h b/chromium/chrome/browser/extensions/api/cookies/cookies_api.h index d6b171b5f04..a506809822a 100644 --- a/chromium/chrome/browser/extensions/api/cookies/cookies_api.h +++ b/chromium/chrome/browser/extensions/api/cookies/cookies_api.h @@ -23,6 +23,7 @@ #include "mojo/public/cpp/bindings/remote.h" #include "net/cookies/canonical_cookie.h" #include "net/cookies/cookie_change_dispatcher.h" +#include "net/cookies/cookie_inclusion_status.h" #include "services/network/public/mojom/cookie_manager.mojom.h" #include "url/gurl.h" @@ -105,8 +106,9 @@ class CookiesGetFunction : public ExtensionFunction { ResponseAction Run() override; private: - void GetCookieListCallback(const net::CookieStatusList& cookie_status_list, - const net::CookieStatusList& excluded_cookies); + void GetCookieListCallback( + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies); GURL url_; mojo::Remote<network::mojom::CookieManager> store_browser_cookie_manager_; @@ -130,8 +132,9 @@ class CookiesGetAllFunction : public ExtensionFunction { // For the two different callback signatures for getting cookies for a URL vs // getting all cookies. They do the same thing. void GetAllCookiesCallback(const net::CookieList& cookie_list); - void GetCookieListCallback(const net::CookieStatusList& cookie_status_list, - const net::CookieStatusList& excluded_cookies); + void GetCookieListCallback( + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies); GURL url_; mojo::Remote<network::mojom::CookieManager> store_browser_cookie_manager_; @@ -150,10 +153,10 @@ class CookiesSetFunction : public ExtensionFunction { ResponseAction Run() override; private: - void SetCanonicalCookieCallback( - net::CanonicalCookie::CookieInclusionStatus set_cookie_result); - void GetCookieListCallback(const net::CookieStatusList& cookie_list, - const net::CookieStatusList& excluded_cookies); + void SetCanonicalCookieCallback(net::CookieInclusionStatus set_cookie_result); + void GetCookieListCallback( + const net::CookieAccessResultList& cookie_list, + const net::CookieAccessResultList& excluded_cookies); enum { NO_RESPONSE, SET_COMPLETED, GET_COMPLETED } state_; GURL url_; diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_apitest.cc b/chromium/chrome/browser/extensions/api/cookies/cookies_apitest.cc index bf114b6f2be..e5f22089cac 100644 --- a/chromium/chrome/browser/extensions/api/cookies/cookies_apitest.cc +++ b/chromium/chrome/browser/extensions/api/cookies/cookies_apitest.cc @@ -6,10 +6,16 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/test/browser_test.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" #include "net/cookies/cookie_util.h" namespace extensions { +using ContextType = ExtensionApiTest::ContextType; + +// TODO(crbug.com/1093066): This test uses the DOM to set and +// check cookies for one test. Figure out how to isolate that +// test and adapt the rest of it for a SW-based extension. IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Cookies) { ASSERT_TRUE(RunExtensionTestWithArg( "cookies/api", @@ -19,21 +25,60 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Cookies) { << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CookiesEvents) { - ASSERT_TRUE(RunExtensionTest("cookies/events")) << message_; +class CookiesApiTest : public ExtensionApiTest, + public testing::WithParamInterface<ContextType> { + public: + void SetUp() override { + ExtensionApiTest::SetUp(); + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); + } + + protected: + bool RunTest(const std::string& extension_name) { + return RunTestWithFlags(extension_name, kFlagNone); + } + + bool RunTestIncognito(const std::string& extension_name) { + return RunTestWithFlags(extension_name, kFlagEnableIncognito); + } + + bool RunTestWithFlags(const std::string& extension_name, + int browser_test_flags) { + if (GetParam() == ContextType::kServiceWorker) + browser_test_flags |= kFlagRunAsServiceWorkerBasedExtension; + + return RunExtensionTestWithFlags(extension_name, browser_test_flags, + kFlagNone); + } + + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; +}; + +INSTANTIATE_TEST_SUITE_P(EventPage, + CookiesApiTest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + CookiesApiTest, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEvents) { + ASSERT_TRUE(RunTest("cookies/events")) << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CookiesEventsSpanning) { +IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesEventsSpanning) { // We need to initialize an incognito mode window in order have an initialized // incognito cookie store. Otherwise, the chrome.cookies.set operation is just // ignored and we won't be notified about a newly set cookie for which we want // to test whether the storeId is set correctly. OpenURLOffTheRecord(browser()->profile(), GURL("chrome://newtab/")); - ASSERT_TRUE(RunExtensionTestIncognito("cookies/events_spanning")) << message_; + ASSERT_TRUE(RunTestIncognito("cookies/events_spanning")) << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, CookiesNoPermission) { - ASSERT_TRUE(RunExtensionTest("cookies/no_permission")) << message_; +IN_PROC_BROWSER_TEST_P(CookiesApiTest, CookiesNoPermission) { + ASSERT_TRUE(RunTest("cookies/no_permission")) << message_; } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.cc b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.cc index 43bb1619bfb..9aee1f6143b 100644 --- a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.cc +++ b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.cc @@ -182,14 +182,14 @@ void AppendMatchingCookiesFromCookieListToVector( } } -void AppendMatchingCookiesFromCookieStatusListToVector( - const net::CookieStatusList& all_cookies_with_statuses, +void AppendMatchingCookiesFromCookieAccessResultListToVector( + const net::CookieAccessResultList& all_cookies_with_access_result, const GetAll::Params::Details* details, const Extension* extension, std::vector<Cookie>* match_vector) { - for (const net::CookieWithStatus& cookie_with_status : - all_cookies_with_statuses) { - const net::CanonicalCookie& cookie = cookie_with_status.cookie; + for (const net::CookieWithAccessResult& cookie_with_access_result : + all_cookies_with_access_result) { + const net::CanonicalCookie& cookie = cookie_with_access_result.cookie; AppendCookieToVectorIfMatchAndHasHostPermission(cookie, details, extension, match_vector); } diff --git a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h index 298b16e49a7..9d5acf0fbc7 100644 --- a/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h +++ b/chromium/chrome/browser/extensions/api/cookies/cookies_helpers.h @@ -85,9 +85,10 @@ void AppendMatchingCookiesFromCookieListToVector( const Extension* extension, std::vector<api::cookies::Cookie>* match_vector); -// Same as above except takes a CookieStatusList (and ignores the statuses). -void AppendMatchingCookiesFromCookieStatusListToVector( - const net::CookieStatusList& all_cookies_with_statuses, +// Same as above except takes a CookieAccessResultList (and ignores the access +// results). +void AppendMatchingCookiesFromCookieAccessResultListToVector( + const net::CookieAccessResultList& all_cookies_with_access_result, const api::cookies::GetAll::Params::Details* details, const Extension* extension, std::vector<api::cookies::Cookie>* match_vector); diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc index 253ce751bd9..3aaf2863859 100644 --- a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc @@ -389,9 +389,7 @@ bool ExtensionDevToolsClientHost::MayWriteLocalFiles() { // DebuggerFunction ----------------------------------------------------------- -DebuggerFunction::DebuggerFunction() - : client_host_(NULL) { -} +DebuggerFunction::DebuggerFunction() : client_host_(nullptr) {} DebuggerFunction::~DebuggerFunction() = default; diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc index 704f95eed0c..71ce5a3399d 100644 --- a/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc +++ b/chromium/chrome/browser/extensions/api/debugger/debugger_apitest.cc @@ -381,4 +381,24 @@ IN_PROC_BROWSER_TEST_F(SitePerProcessDebuggerExtensionApiTest, Debugger) { << message_; } +IN_PROC_BROWSER_TEST_F(SitePerProcessDebuggerExtensionApiTest, + AutoAttachPermissions) { + GURL url(embedded_test_server()->GetURL( + "a.com", + "/extensions/api_test/debugger_auto_attach_permissions/page.html")); + ASSERT_TRUE(RunExtensionTestWithArg("debugger_auto_attach_permissions", + url.spec().c_str())) + << message_; +} + +IN_PROC_BROWSER_TEST_F(SitePerProcessDebuggerExtensionApiTest, + NavigateSubframe) { + GURL url(embedded_test_server()->GetURL( + "a.com", + "/extensions/api_test/debugger_navigate_subframe/inspected_page.html")); + ASSERT_TRUE( + RunExtensionTestWithArg("debugger_navigate_subframe", url.spec().c_str())) + << message_; +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc index 13265b25237..06daba2fe34 100644 --- a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc @@ -199,18 +199,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, PersistRules) { EXPECT_EQ(kTestTitle, GetTitle()); } -// Disabled for flakiness: http://crbug.com/851854 -#if defined(OS_MACOSX) && defined(ADDRESS_SANITIZER) -#define MAYBE_ExtensionLifetimeRulesHandling \ - DISABLED_ExtensionLifetimeRulesHandling -#else -#define MAYBE_ExtensionLifetimeRulesHandling ExtensionLifetimeRulesHandling -#endif - // Test that the rules are correctly persisted and (de)activated during // changing the "installed" and "enabled" status of an extension. -IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, - MAYBE_ExtensionLifetimeRulesHandling) { +IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, ExtensionLifetimeRulesHandling) { TestExtensionDir ext_dir; // 1. Install the extension. Rules should become active. @@ -276,17 +267,10 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, EXPECT_EQ(0u, NumberOfRegisteredRules(extension_id)); } -// Disabled for flakiness: http://crbug.com/851854 -#if defined(OS_MACOSX) && defined(ADDRESS_SANITIZER) -#define MAYBE_NoTracesAfterUninstalling DISABLED_NoTracesAfterUninstalling -#else -#define MAYBE_NoTracesAfterUninstalling NoTracesAfterUninstalling -#endif - // When an extension is uninstalled, the state store deletes all preferences // stored for that extension. We need to make sure we don't store anything after // that deletion occurs. -IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, MAYBE_NoTracesAfterUninstalling) { +IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, NoTracesAfterUninstalling) { TestExtensionDir ext_dir; // 1. Install the extension. Verify that rules become active and some prefs diff --git a/chromium/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc b/chromium/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc index 8b25d8e15ff..97ef56d188a 100644 --- a/chromium/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc +++ b/chromium/chrome/browser/extensions/api/declarative/rules_registry_service_unittest.cc @@ -10,11 +10,11 @@ #include "base/bind.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" #include "chrome/test/base/testing_profile.h" #include "components/version_info/channel.h" #include "components/version_info/version_info.h" #include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/api/declarative/test_rules_registry.h" #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" @@ -84,23 +84,23 @@ TEST_F(RulesRegistryServiceTest, TestConstructionAndMultiThreading) { EXPECT_TRUE(registry_service.GetRulesRegistry(key, "io").get()); EXPECT_FALSE(registry_service.GetRulesRegistry(key, "foo").get()); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&InsertRule, registry_service.GetRulesRegistry(key, "ui"), "ui_task")); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&InsertRule, registry_service.GetRulesRegistry(key, "io"), "io_task")); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&VerifyNumberOfRules, registry_service.GetRulesRegistry(key, "ui"), 1)); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&VerifyNumberOfRules, registry_service.GetRulesRegistry(key, "io"), 1)); @@ -120,13 +120,13 @@ TEST_F(RulesRegistryServiceTest, TestConstructionAndMultiThreading) { .Build(); registry_service.SimulateExtensionUninstalled(extension.get()); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&VerifyNumberOfRules, registry_service.GetRulesRegistry(key, "ui"), 0)); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&VerifyNumberOfRules, registry_service.GetRulesRegistry(key, "io"), 0)); diff --git a/chromium/chrome/browser/extensions/api/declarative_content/content_action.cc b/chromium/chrome/browser/extensions/api/declarative_content/content_action.cc index 9951f0a2666..b06e0ae94c4 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/content_action.cc +++ b/chromium/chrome/browser/extensions/api/declarative_content/content_action.cc @@ -235,7 +235,7 @@ std::unique_ptr<ContentAction> RequestContentScript::Create( // static std::unique_ptr<ContentAction> RequestContentScript::CreateForTest( - DeclarativeUserScriptMaster* master, + DeclarativeUserScriptSet* script_set, const Extension* extension, const base::Value& json_action, std::string* error) { @@ -256,10 +256,10 @@ std::unique_ptr<ContentAction> RequestContentScript::CreateForTest( if (!InitScriptData(action_dict, error, &script_data)) return std::unique_ptr<ContentAction>(); - // Inject provided DeclarativeUserScriptMaster, rather than looking it up + // Inject provided DeclarativeUserScriptSet, rather than looking it up // using a BrowserContext. return base::WrapUnique( - new RequestContentScript(master, extension, script_data)); + new RequestContentScript(script_set, extension, script_data)); } // static @@ -309,25 +309,24 @@ RequestContentScript::RequestContentScript( HostID host_id(HostID::EXTENSIONS, extension->id()); InitScript(host_id, extension, script_data); - master_ = DeclarativeUserScriptManager::Get(browser_context) - ->GetDeclarativeUserScriptMasterByID(host_id); + script_set_ = DeclarativeUserScriptManager::Get(browser_context) + ->GetDeclarativeUserScriptSetByID(host_id); AddScript(); } -RequestContentScript::RequestContentScript( - DeclarativeUserScriptMaster* master, - const Extension* extension, - const ScriptData& script_data) { +RequestContentScript::RequestContentScript(DeclarativeUserScriptSet* script_set, + const Extension* extension, + const ScriptData& script_data) { HostID host_id(HostID::EXTENSIONS, extension->id()); InitScript(host_id, extension, script_data); - master_ = master; + script_set_ = script_set; AddScript(); } RequestContentScript::~RequestContentScript() { - DCHECK(master_); - master_->RemoveScript(UserScriptIDPair(script_.id(), script_.host_id())); + DCHECK(script_set_); + script_set_->RemoveScript(UserScriptIDPair(script_.id(), script_.host_id())); } void RequestContentScript::InitScript(const HostID& host_id, @@ -355,8 +354,8 @@ void RequestContentScript::InitScript(const HostID& host_id, } void RequestContentScript::AddScript() { - DCHECK(master_); - master_->AddScript(UserScript::CopyMetadataFrom(script_)); + DCHECK(script_set_); + script_set_->AddScript(UserScript::CopyMetadataFrom(script_)); } void RequestContentScript::Apply(const ApplyInfo& apply_info) const { @@ -394,7 +393,8 @@ std::unique_ptr<ContentAction> SetIcon::Create( gfx::ImageSkia icon; const base::DictionaryValue* canvas_set = NULL; if (dict->GetDictionary("imageData", &canvas_set) && - !ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon)) { + ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon) != + ExtensionAction::IconParseResult::kSuccess) { *error = kInvalidIconDictionary; return nullptr; } diff --git a/chromium/chrome/browser/extensions/api/declarative_content/content_action.h b/chromium/chrome/browser/extensions/api/declarative_content/content_action.h index c16e87cca97..ea28a5a863a 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/content_action.h +++ b/chromium/chrome/browser/extensions/api/declarative_content/content_action.h @@ -9,7 +9,7 @@ #include <string> #include "base/macros.h" -#include "extensions/browser/declarative_user_script_master.h" +#include "extensions/browser/declarative_user_script_set.h" #include "extensions/common/user_script.h" namespace base { @@ -74,7 +74,7 @@ class RequestContentScript : public ContentAction { RequestContentScript(content::BrowserContext* browser_context, const Extension* extension, const ScriptData& script_data); - RequestContentScript(DeclarativeUserScriptMaster* master, + RequestContentScript(DeclarativeUserScriptSet* script_set, const Extension* extension, const ScriptData& script_data); @@ -87,7 +87,7 @@ class RequestContentScript : public ContentAction { std::string* error); static std::unique_ptr<ContentAction> CreateForTest( - DeclarativeUserScriptMaster* master, + DeclarativeUserScriptSet* master, const Extension* extension, const base::Value& json_action, std::string* error); @@ -112,7 +112,7 @@ class RequestContentScript : public ContentAction { const Extension* extension) const; UserScript script_; - DeclarativeUserScriptMaster* master_; + DeclarativeUserScriptSet* script_set_; DISALLOW_COPY_AND_ASSIGN(RequestContentScript); }; diff --git a/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc index 73650f930ac..dc7055aea74 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_content/declarative_content_apitest.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_model_factory.h" +#include "chrome/browser/extensions/chrome_extension_test_notification_observer.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_action_test_util.h" @@ -257,8 +258,7 @@ void DeclarativeContentApiTest::CheckBookmarkEvents(bool match_is_bookmarked) { EXPECT_EQ(!match_is_bookmarked, action->GetIsVisible(tab_id)); } -// Disabled due to flake. https://crbug.com/606574. -IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_Overview) { +IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, Overview) { ext_dir_.WriteManifest(kDeclarativeContentManifest); ext_dir_.WriteFile( FILE_PATH_LITERAL("background.js"), @@ -295,14 +295,20 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_Overview) { browser()->tab_strip_model()->GetWebContentsAt(0); const int tab_id = ExtensionTabUtil::GetTabId(tab); - NavigateInRenderer(tab, GURL("http://test1/")); + // Observer to track page action visibility. This helps avoid + // flakes by waiting to check visibility until there is an + // actual update to the page action. + ChromeExtensionTestNotificationObserver test_observer(browser()); + NavigateInRenderer(tab, GURL("http://test1/")); // The declarative API should show the page action instantly, rather // than waiting for the extension to run. + test_observer.WaitForPageActionVisibilityChangeTo(1); EXPECT_TRUE(action->GetIsVisible(tab_id)); // Make sure leaving a matching page unshows the page action. NavigateInRenderer(tab, GURL("http://not_checked/")); + test_observer.WaitForPageActionVisibilityChangeTo(0); EXPECT_FALSE(action->GetIsVisible(tab_id)); // Insert a password field to make sure that's noticed. @@ -311,10 +317,7 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_Overview) { tab, "document.body.innerHTML = '<input type=\"password\">';" "document.body.offsetTop;")); - // Give the style match a chance to run and send back the matching-selector - // update. - ASSERT_TRUE(content::ExecuteScript(tab, std::string())); - + test_observer.WaitForPageActionVisibilityChangeTo(1); EXPECT_TRUE(action->GetIsVisible(tab_id)) << "Adding a matching element should show the page action."; @@ -324,10 +327,7 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_Overview) { tab, "document.body.innerHTML = 'Hello world';" "document.body.offsetTop;")); - // Give the style match a chance to run and send back the matching-selector - // update. - ASSERT_TRUE(content::ExecuteScript(tab, std::string())); - + test_observer.WaitForPageActionVisibilityChangeTo(0); EXPECT_FALSE(action->GetIsVisible(tab_id)) << "Removing the matching element should hide the page action again."; } @@ -643,11 +643,17 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EXPECT_TRUE(incognito_action->GetIsVisible(incognito_tab_id)); } +constexpr char kRulesExtensionName[] = + "Declarative content persistence apitest"; + +// TODO(crbug.com/512431): Flaky on Windows release builds. +#if defined(OS_WIN) && defined(NDEBUG) +#define MAYBE_PRE_RulesPersistence DISABLED_PRE_RulesPersistence +#else +#define MAYBE_PRE_RulesPersistence PRE_RulesPersistence +#endif // Sets up rules matching http://test1/ in a normal and incognito browser. -// Frequently times out on ChromiumOS, Linux ASan, and Windows: -// https://crbug.com/512431. -IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, - DISABLED_PRE_RulesPersistence) { +IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, MAYBE_PRE_RulesPersistence) { ExtensionTestMessageListener ready("ready", false); ExtensionTestMessageListener ready_split("ready (split)", false); // An on-disk extension is required so that it can be reloaded later in the @@ -656,30 +662,36 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, LoadExtensionIncognito(test_data_dir_.AppendASCII("declarative_content") .AppendASCII("persistence")); ASSERT_TRUE(extension); + ASSERT_EQ(kRulesExtensionName, extension->name()); ASSERT_TRUE(ready.WaitUntilSatisfied()); CreateIncognitoBrowser(); ASSERT_TRUE(ready_split.WaitUntilSatisfied()); } +// TODO(crbug.com/512431): Flaky on Windows release builds. +#if defined(OS_WIN) && defined(NDEBUG) +#define MAYBE_RulesPersistence DISABLED_RulesPersistence +#else +#define MAYBE_RulesPersistence RulesPersistence +#endif // Reloads the extension from PRE_RulesPersistence and checks that the rules // continue to work as expected after being persisted and reloaded. -IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_RulesPersistence) { - ExtensionTestMessageListener ready("second run ready", false); - ExtensionTestMessageListener ready_split("second run ready (split)", false); - ASSERT_TRUE(ready.WaitUntilSatisfied()); - - ExtensionRegistry* registry = ExtensionRegistry::Get(profile()); - const Extension* extension = - GetExtensionByPath(registry->enabled_extensions(), - test_data_dir_.AppendASCII("declarative_content") - .AppendASCII("persistence")); +IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, MAYBE_RulesPersistence) { + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + ASSERT_EQ(kRulesExtensionName, extension->name()); // Check non-incognito browser. content::WebContents* const tab = browser()->tab_strip_model()->GetWebContentsAt(0); const int tab_id = ExtensionTabUtil::GetTabId(tab); + // Observer to track page action visibility. This helps avoid + // flakes by waiting to check visibility until there is an + // actual update to the page action. + ChromeExtensionTestNotificationObserver test_observer(browser()); + const ExtensionAction* action = ExtensionActionManager::Get(browser()->profile()) ->GetExtensionAction(*extension); @@ -687,11 +699,15 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_RulesPersistence) { EXPECT_FALSE(action->GetIsVisible(tab_id)); NavigateInRenderer(tab, GURL("http://test_normal/")); + test_observer.WaitForPageActionVisibilityChangeTo(1); EXPECT_TRUE(action->GetIsVisible(tab_id)); NavigateInRenderer(tab, GURL("http://test_split/")); + test_observer.WaitForPageActionVisibilityChangeTo(0); EXPECT_FALSE(action->GetIsVisible(tab_id)); + ExtensionTestMessageListener ready_split("second run ready (split)", false); + // Check incognito browser. Browser* incognito_browser = CreateIncognitoBrowser(); ASSERT_TRUE(ready_split.WaitUntilSatisfied()); @@ -699,35 +715,35 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, DISABLED_RulesPersistence) { incognito_browser->tab_strip_model()->GetWebContentsAt(0); const int incognito_tab_id = ExtensionTabUtil::GetTabId(incognito_tab); + ChromeExtensionTestNotificationObserver incognito_test_observer( + incognito_browser); + const ExtensionAction* incognito_action = ExtensionActionManager::Get(incognito_browser->profile()) ->GetExtensionAction(*extension); ASSERT_TRUE(incognito_action); NavigateInRenderer(incognito_tab, GURL("http://test_split/")); + incognito_test_observer.WaitForPageActionVisibilityChangeTo(1); EXPECT_TRUE(incognito_action->GetIsVisible(incognito_tab_id)); NavigateInRenderer(incognito_tab, GURL("http://test_normal/")); + incognito_test_observer.WaitForPageActionVisibilityChangeTo(0); EXPECT_FALSE(incognito_action->GetIsVisible(incognito_tab_id)); } // http://crbug.com/304373 -#if defined(OS_WIN) -// Fails on XP: http://crbug.com/515717 -#define MAYBE_UninstallWhileActivePageAction \ - DISABLED_UninstallWhileActivePageAction -#else -#define MAYBE_UninstallWhileActivePageAction UninstallWhileActivePageAction -#endif IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, - MAYBE_UninstallWhileActivePageAction) { + UninstallWhileActivePageAction) { ext_dir_.WriteManifest(kDeclarativeContentManifest); - ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers); + std::string script = + kBackgroundHelpers + std::string("\nchrome.test.sendMessage('ready');"); + ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), script); + ExtensionTestMessageListener ready_listener("ready", false); const Extension* extension = LoadExtension(ext_dir_.UnpackedPath()); ASSERT_TRUE(extension); - // Wait for declarative rules to be set up. - content::BrowserContext::GetDefaultStoragePartition(profile()) - ->FlushNetworkInterfaceForTesting(); + ASSERT_TRUE(ready_listener.WaitUntilSatisfied()); + const std::string extension_id = extension->id(); const ExtensionAction* action = ExtensionActionManager::Get(browser()->profile()) @@ -754,10 +770,10 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EXPECT_EQ(1u, extension_action_test_util::GetVisiblePageActionCount(tab)); EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab)); + ExtensionTestMessageListener reload_ready_listener("ready", false); ReloadExtension(extension_id); // Invalidates action and extension. - // Wait for declarative rules to be removed. - content::BrowserContext::GetDefaultStoragePartition(profile()) - ->FlushNetworkInterfaceForTesting(); + ASSERT_TRUE(reload_ready_listener.WaitUntilSatisfied()); + EXPECT_EQ("test_rule", ExecuteScriptInBackgroundPage(extension_id, kTestRule)); // TODO(jyasskin): Apply new rules to existing tabs, without waiting for a @@ -779,7 +795,7 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, // This tests against a renderer crash that was present during development. IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, - DISABLED_AddExtensionMatchingExistingTabWithDeadFrames) { + AddExtensionMatchingExistingTabWithDeadFrames) { ext_dir_.WriteManifest(kDeclarativeContentManifest); ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers); content::WebContents* const tab = @@ -792,6 +808,10 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, ASSERT_TRUE(content::ExecuteScript( tab, "document.body.innerHTML = '<span class=\"foo\">';")); + // Observer to track page action visibility. This helps avoid flakes by + // waiting to check visibility until there is an update to the page action. + ChromeExtensionTestNotificationObserver test_observer(browser()); + const Extension* extension = LoadExtension(ext_dir_.UnpackedPath()); ASSERT_TRUE(extension); const ExtensionAction* action = @@ -807,13 +827,8 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, " css: [\"span[class=foo]\"]})],\n" " actions: [new ShowAction()]\n" "}], 'rule0');\n")); - // Give the renderer a chance to apply the rules change and notify the - // browser. This takes one time through the Blink message loop to receive - // the rule change and apply the new stylesheet, and a second to dedupe the - // update. - ASSERT_TRUE(content::ExecuteScript(tab, std::string())); - ASSERT_TRUE(content::ExecuteScript(tab, std::string())); + test_observer.WaitForPageActionVisibilityChangeTo(1); EXPECT_FALSE(tab->IsCrashed()); EXPECT_TRUE(action->GetIsVisible(tab_id)) << "Loading an extension when an open page matches its rules " @@ -825,6 +840,7 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, "onPageChanged.removeRules(undefined, function() {\n" " window.domAutomationController.send('removed');\n" "});\n")); + test_observer.WaitForPageActionVisibilityChangeTo(0); EXPECT_FALSE(action->GetIsVisible(tab_id)); } diff --git a/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc index 65865c4235a..4a99b452a50 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_content/request_content_script_apitest.cc @@ -113,7 +113,7 @@ class RequestContentScriptAPITest : public ExtensionBrowserTest { }; RequestContentScriptAPITest::RequestContentScriptAPITest() - : extension_(NULL) {} + : extension_(nullptr) {} testing::AssertionResult RequestContentScriptAPITest::RunTest( PermissionOrMatcherType manifest_permission, diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc index 827ac893936..76c4987a474 100644 --- a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_apitest.cc @@ -10,13 +10,19 @@ #include "base/macros.h" #include "base/threading/thread_restrictions.h" #include "content/public/test/browser_test.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" #include "net/dns/mock_host_resolver.h" namespace { -class DeclarativeNetRequestAPItest : public extensions::ExtensionApiTest { +using ContextType = extensions::ExtensionApiTest::ContextType; +using extensions::ScopedWorkerBasedExtensionsChannel; + +class DeclarativeNetRequestAPItest + : public extensions::ExtensionApiTest, + public testing::WithParamInterface<ContextType> { public: - DeclarativeNetRequestAPItest() {} + DeclarativeNetRequestAPItest() = default; protected: // ExtensionApiTest override. @@ -40,28 +46,55 @@ class DeclarativeNetRequestAPItest : public extensions::ExtensionApiTest { // Override the path used for loading the extension. test_data_dir_ = temp_dir_.GetPath().AppendASCII("declarative_net_request"); + + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); + } + + bool RunTest(const std::string& extension_path) { + if (GetParam() != ContextType::kServiceWorker) { + return RunExtensionTest(extension_path); + } + return RunExtensionTestWithFlags( + extension_path, kFlagRunAsServiceWorkerBasedExtension, kFlagNone); } private: base::ScopedTempDir temp_dir_; - DISALLOW_COPY_AND_ASSIGN(DeclarativeNetRequestAPItest); + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; }; -IN_PROC_BROWSER_TEST_F(DeclarativeNetRequestAPItest, DynamicRules) { - ASSERT_TRUE(RunExtensionTest("dynamic_rules")) << message_; +using DeclarativeNetRequestLazyAPItest = DeclarativeNetRequestAPItest; + +INSTANTIATE_TEST_SUITE_P(PersistentBackground, + DeclarativeNetRequestAPItest, + ::testing::Values(ContextType::kPersistentBackground)); +INSTANTIATE_TEST_SUITE_P(EventPage, + DeclarativeNetRequestLazyAPItest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + DeclarativeNetRequestLazyAPItest, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, DynamicRules) { + ASSERT_TRUE(RunTest("dynamic_rules")) << message_; +} + +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, OnRulesMatchedDebug) { + ASSERT_TRUE(RunTest("on_rules_matched_debug")) << message_; } -// TODO(crbug.com/1029233) Restore this test. This is disabled due to -// flakiness. -IN_PROC_BROWSER_TEST_F(DeclarativeNetRequestAPItest, - DISABLED_OnRulesMatchedDebug) { - ASSERT_TRUE(RunExtensionTest("on_rules_matched_debug")) << message_; +// This test uses webRequest/webRequestBlocking, so it's not currently +// supported for service workers. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestAPItest, ModifyHeaders) { + ASSERT_TRUE(RunTest("modify_headers")) << message_; } -// TODO(crbug.com/1070344): Disabled due to flakiness. -IN_PROC_BROWSER_TEST_F(DeclarativeNetRequestAPItest, DISABLED_GetMatchedRules) { - ASSERT_TRUE(RunExtensionTest("get_matched_rules")) << message_; +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestLazyAPItest, GetMatchedRules) { + ASSERT_TRUE(RunTest("get_matched_rules")) << message_; } } // namespace 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 a5d9df75278..26f4b57d2bc 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 @@ -546,6 +546,24 @@ class DeclarativeNetRequestBrowserTest return results; } + TestRule CreateModifyHeadersRule( + int id, + int priority, + const std::string& url_filter, + base::Optional<std::vector<TestHeaderInfo>> request_headers, + base::Optional<std::vector<TestHeaderInfo>> response_headers) { + TestRule rule = CreateGenericRule(); + rule.id = id; + rule.priority = priority; + rule.condition->url_filter = url_filter; + rule.condition->resource_types = std::vector<std::string>({"sub_frame"}); + rule.action->type = "modifyHeaders"; + rule.action->request_headers = std::move(request_headers); + rule.action->response_headers = std::move(response_headers); + + return rule; + } + private: // Handler to monitor the requests which reach the EmbeddedTestServer. This // will be run on the EmbeddedTestServer's IO thread. @@ -650,10 +668,9 @@ class DeclarativeNetRequestBrowserTest tester.ExpectTotalCount( "Extensions.DeclarativeNetRequest.CreateVerifiedMatcherTime", expected_enabled_rulesets_count); - tester.ExpectUniqueSample( - "Extensions.DeclarativeNetRequest.LoadRulesetResult", - RulesetMatcher::kLoadSuccess /*sample*/, - expected_enabled_rulesets_count); + tester.ExpectUniqueSample(kLoadRulesetResultHistogram, + LoadRulesetResult::kSuccess /*sample*/, + expected_enabled_rulesets_count); EXPECT_TRUE(AreAllIndexedStaticRulesetsValid(*extension, profile())); @@ -692,8 +709,9 @@ using DeclarativeNetRequestBrowserTest_Packed = using DeclarativeNetRequestBrowserTest_Unpacked = DeclarativeNetRequestBrowserTest; -#if defined(OS_WIN) && !defined(NDEBUG) +#if (defined(OS_WIN) || defined(OS_MACOSX)) && !defined(NDEBUG) // TODO: test times out on win7-debug. http://crbug.com/900447. +// Also times out on mac-debug: https://crbug.com/900447 #define MAYBE_BlockRequests_UrlFilter DISABLED_BlockRequests_UrlFilter #else #define MAYBE_BlockRequests_UrlFilter BlockRequests_UrlFilter @@ -2091,8 +2109,6 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, EXPECT_FALSE(IsNavigationBlocked(unblocked_url)); }; - const char* kLoadRulesetResultHistogram = - "Extensions.DeclarativeNetRequest.LoadRulesetResult"; const char* kReindexHistogram = "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful"; @@ -2104,19 +2120,13 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, base::HistogramTester tester; test_extension_works_after_reload(); - // Loading the ruleset would have failed initially due to checksum mismatch - // and later succeeded. - tester.ExpectBucketCount(kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult:: - kLoadErrorChecksumMismatch /* sample */, - 1 /* count */); - // Count of 2 because we load both static and dynamic rulesets. - tester.ExpectBucketCount( - kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult::kLoadSuccess /* sample */, - 2 /* count */); - // Verify that reindexing of the static ruleset succeeded. + // Loading the static ruleset would fail initially due to checksum mismatch + // but will succeed on re-indexing. tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 1 /*count*/); + // Count of 2 because we load both static and dynamic rulesets. + tester.ExpectBucketCount(kLoadRulesetResultHistogram, + LoadRulesetResult::kSuccess /* sample */, + 2 /* count */); } // Test dynamic ruleset re-indexing. @@ -2127,19 +2137,14 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, base::HistogramTester tester; test_extension_works_after_reload(); - // Loading the ruleset would have failed initially due to checksum mismatch - // and later succeeded. - tester.ExpectBucketCount(kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult:: - kLoadErrorChecksumMismatch /* sample */, - 1 /* count */); - // Count of 2 because we load both static and dynamic rulesets. - tester.ExpectBucketCount( - kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult::kLoadSuccess /* sample */, - 2 /* count */); - // Verify that reindexing of the dynamic ruleset succeeded. + // Loading the dynamic ruleset would have failed initially due to checksum + // mismatch and later succeeded on re-indexing. tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 1 /*count*/); + + // Count of 2 because we load both static and dynamic rulesets. + tester.ExpectBucketCount(kLoadRulesetResultHistogram, + LoadRulesetResult::kSuccess /* sample */, + 2 /* count */); } // Go crazy and corrupt both static and dynamic rulesets. @@ -2153,17 +2158,11 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, // Loading the ruleset would have failed initially due to checksum mismatch // and later succeeded. + tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 2 /*count*/); + // Count of 2 because we load both static and dynamic rulesets. tester.ExpectBucketCount(kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult:: - kLoadErrorChecksumMismatch /* sample */, + LoadRulesetResult::kSuccess /* sample */, 2 /* count */); - // Count of 2 because we load both static and dynamic rulesets. - tester.ExpectBucketCount( - kLoadRulesetResultHistogram, - RulesetMatcher::LoadRulesetResult::kLoadSuccess /* sample */, - 2 /* count */); - // Verify that reindexing of both the rulesets succeeded. - tester.ExpectBucketCount(kReindexHistogram, true /*sample*/, 2 /*count*/); } } @@ -2228,17 +2227,15 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // Verify that loading the corrupted rulesets failed due to checksum mismatch. // The non-corrupted rulesets should load fine. - tester.ExpectTotalCount("Extensions.DeclarativeNetRequest.LoadRulesetResult", - rulesets.size()); + tester.ExpectTotalCount(kLoadRulesetResultHistogram, rulesets.size()); EXPECT_EQ(corrupted_ruleset_indices.size(), static_cast<size_t>(tester.GetBucketCount( - "Extensions.DeclarativeNetRequest.LoadRulesetResult", - RulesetMatcher::LoadRulesetResult:: - kLoadErrorChecksumMismatch /*sample*/))); + kLoadRulesetResultHistogram, + LoadRulesetResult::kErrorChecksumMismatch /*sample*/))); EXPECT_EQ(non_corrupted_ruleset_indices.size(), - static_cast<size_t>(tester.GetBucketCount( - "Extensions.DeclarativeNetRequest.LoadRulesetResult", - RulesetMatcher::LoadRulesetResult::kLoadSuccess /*sample*/))); + static_cast<size_t>( + tester.GetBucketCount(kLoadRulesetResultHistogram, + LoadRulesetResult::kSuccess /*sample*/))); // Verify that re-indexing the corrupted rulesets failed. tester.ExpectUniqueSample( @@ -2305,22 +2302,14 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, // We add 1 to include the dynamic ruleset. const int kNumRulesets = kNumStaticRulesets + 1; - // Verify that loading the static and dynamic rulesets would have failed - // initially due to version header mismatch and later succeeded. - EXPECT_EQ(kNumRulesets, - tester.GetBucketCount( - "Extensions.DeclarativeNetRequest.LoadRulesetResult", - RulesetMatcher::LoadRulesetResult:: - kLoadErrorVersionMismatch /*sample*/)); - EXPECT_EQ(kNumRulesets, - tester.GetBucketCount( - "Extensions.DeclarativeNetRequest.LoadRulesetResult", - RulesetMatcher::LoadRulesetResult::kLoadSuccess /*sample*/)); - - // Verify that reindexing succeeded. + // Verify that loading the static and dynamic rulesets would cause reindexing + // due to version header mismatch and later succeeded. tester.ExpectUniqueSample( "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful", true /*sample*/, kNumRulesets /*count*/); + EXPECT_EQ(kNumRulesets, + tester.GetBucketCount(kLoadRulesetResultHistogram, + LoadRulesetResult::kSuccess /*sample*/)); // Ensure that the new checksum was correctly persisted in prefs. const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); @@ -2762,11 +2751,15 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, int priority; std::string action_type; base::Optional<std::string> redirect_url; + base::Optional<std::vector<TestHeaderInfo>> request_headers; } rules_data[] = { - {"abc.com", 1, 1, "block", base::nullopt}, - {"def.com", 2, 1, "redirect", "http://zzz.com"}, - {"abcd.com", 4, 1, "block", base::nullopt}, - {"abcd", 5, 1, "allow", base::nullopt}, + {"abc.com", 1, 1, "block", base::nullopt, base::nullopt}, + {"def.com", 2, 1, "redirect", "http://zzz.com", base::nullopt}, + {"jkl.com", 3, 1, "modifyHeaders", base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("referer", "remove", base::nullopt)})}, + {"abcd.com", 4, 1, "block", base::nullopt, base::nullopt}, + {"abcd", 5, 1, "allow", base::nullopt, base::nullopt}, }; // Load the extension. @@ -2781,6 +2774,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, rule.action->type = rule_data.action_type; rule.action->redirect.emplace(); rule.action->redirect->url = rule_data.redirect_url; + rule.action->request_headers = rule_data.request_headers; rules.push_back(rule); } @@ -2810,10 +2804,17 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // def.com is redirected by a matching rule and should increment the badge // text. {"def.com", "2", false}, + // jkl.com matches with a modifyHeaders rule which removes the referer + // header, but has no headers. Therefore no action is taken and the badge + // text stays the same. + {"jkl.com", "2", false}, + // jkl.com matches with a modifyHeaders rule and has a referer header. + // Therefore the badge text should be incremented. + {"jkl.com", "3", true}, // abcd.com matches both a block rule and an allow rule. Since the allow // rule overrides the block rule, no action is taken and the badge text // stays the same, - {"abcd.com", "2", false}, + {"abcd.com", "3", false}, }; ui_test_utils::NavigateToURL(browser(), page_url); @@ -2879,9 +2880,11 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // Four rules should be matched on the tab with |first_tab_id|: // - the block rule for abc.com (ruleId = 1) // - the redirect rule for def.com (ruleId = 2) + // - the modifyHeaders rule for jkl.com (ruleId = 3) // - the allow rule for abcd.com (ruleId = 5) - EXPECT_EQ(base::StringPrintf("1,%s|2,%s|5,%s", kDefaultRulesetID, - kDefaultRulesetID, kDefaultRulesetID), + EXPECT_EQ(base::StringPrintf("1,%s|2,%s|3,%s|5,%s", kDefaultRulesetID, + kDefaultRulesetID, kDefaultRulesetID, + kDefaultRulesetID), get_matched_rules(first_tab_id)); // No rule should be matched on the tab with |second_tab_id|. @@ -3180,6 +3183,10 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, "/pages_with_script/index.html"); }; + auto get_set_cookie_url = [this](std::string hostname) { + return embedded_test_server()->GetURL(hostname, "/set-cookie?a=b"); + }; + struct { std::string url_filter; int id; @@ -3187,16 +3194,24 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, std::string action_type; std::vector<std::string> resource_types; base::Optional<std::string> redirect_url; + base::Optional<std::vector<TestHeaderInfo>> response_headers; } rules_data[] = { {"abc.com", 1, 1, "block", std::vector<std::string>({"script"}), - base::nullopt}, + base::nullopt, base::nullopt}, {"||def.com", 2, 1, "redirect", std::vector<std::string>({"main_frame"}), - get_url_for_host("abc.com").spec()}, + get_url_for_host("abc.com").spec(), base::nullopt}, {"gotodef.com", 3, 1, "redirect", std::vector<std::string>({"main_frame"}), - get_url_for_host("def.com").spec()}, + get_url_for_host("def.com").spec(), base::nullopt}, {"ghi.com", 4, 1, "block", std::vector<std::string>({"main_frame"}), - base::nullopt}, + base::nullopt, base::nullopt}, + {"gotosetcookie.com", 5, 1, "redirect", + std::vector<std::string>({"main_frame"}), + get_set_cookie_url("setcookie.com").spec(), base::nullopt}, + {"setcookie.com", 6, 1, "modifyHeaders", + std::vector<std::string>({"main_frame"}), base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("set-cookie", "remove", base::nullopt)})}, }; // Load the extension. @@ -3210,6 +3225,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, rule.action->type = rule_data.action_type; rule.action->redirect.emplace(); rule.action->redirect->url = rule_data.redirect_url; + rule.action->response_headers = rule_data.response_headers; rules.push_back(rule); } @@ -3245,6 +3261,10 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // gotodef.com to def.com caused by a rule match. Therefore the badge text // should be 3. {"gotodef.com", "3"}, + // The request to gotosetcookie.com will match with a rule and redirect to + // setcookie.com. The Set-Cookie header on setcookie.com will also match + // with a rule and get removed. Therefore the badge text should be 2. + {"gotosetcookie.com", "2"}, }; int first_tab_id = ExtensionTabUtil::GetTabId(web_contents()); @@ -3258,6 +3278,160 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, } } +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, + ModifyHeadersBadgeText) { + auto get_referer_url = [this](const std::string& host) { + return embedded_test_server()->GetURL(host, "/set-header?referer: none"); + }; + auto get_set_cookie_url = [this](const std::string& host) { + return embedded_test_server()->GetURL(host, "/set-cookie?a=b"); + }; + auto get_no_headers_url = [this](const std::string& host) { + return embedded_test_server()->GetURL(host, + "/pages_with_script/index.html"); + }; + + const std::string kFrameName1 = "frame1"; + const GURL page_url = embedded_test_server()->GetURL( + "nomatch.com", "/page_with_two_frames.html"); + + // Create an extension with rules and get the ExtensionAction for it. + TestRule example_set_cookie_rule = CreateModifyHeadersRule( + kMinValidID, kMinValidPriority, "example.com", base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("set-cookie", "remove", base::nullopt)})); + + TestRule both_headers_rule = CreateModifyHeadersRule( + kMinValidID + 1, kMinValidPriority, "google.com", + std::vector<TestHeaderInfo>( + {TestHeaderInfo("referer", "remove", base::nullopt)}), + std::vector<TestHeaderInfo>( + {TestHeaderInfo("set-cookie", "remove", base::nullopt)})); + + TestRule abc_set_cookie_rule = CreateModifyHeadersRule( + kMinValidID + 2, kMinValidPriority, "abc.com", base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("set-cookie", "remove", base::nullopt)})); + + TestRule abc_referer_rule = CreateModifyHeadersRule( + kMinValidID + 3, kMinValidPriority, "abc.com", + std::vector<TestHeaderInfo>( + {TestHeaderInfo("referer", "remove", base::nullopt)}), + base::nullopt); + + TestRule ext1_set_custom_request_header_rule = CreateModifyHeadersRule( + kMinValidID + 4, kMinValidPriority, "def.com", + std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "set", "ext_1")}), + base::nullopt); + + TestRule ext1_add_custom_response_header_rule = CreateModifyHeadersRule( + kMinValidID + 5, kMinValidPriority, "ghi.com", base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("header2", "append", "ext_1")})); + + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( + {example_set_cookie_rule, both_headers_rule, abc_set_cookie_rule, + abc_referer_rule, ext1_set_custom_request_header_rule, + ext1_add_custom_response_header_rule}, + "extension_1", {URLPattern::kAllUrlsPattern})); + + const ExtensionId extension_1_id = last_loaded_extension_id(); + ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText( + extension_1_id, true); + + ExtensionAction* extension_1_action = + ExtensionActionManager::Get(web_contents()->GetBrowserContext()) + ->GetExtensionAction(*extension_registry()->GetExtensionById( + extension_1_id, extensions::ExtensionRegistry::ENABLED)); + + // Create another extension which removes the referer header from example.com + // and get the ExtensionAction for it. + TestRule example_referer_rule = CreateModifyHeadersRule( + kMinValidID, kMinValidPriority, "example.com", + std::vector<TestHeaderInfo>( + {TestHeaderInfo("referer", "remove", base::nullopt)}), + base::nullopt); + + TestRule ext2_set_custom_request_header_rule = CreateModifyHeadersRule( + kMinValidID + 4, kMinValidPriority, "def.com", + std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "set", "ext_2")}), + base::nullopt); + + TestRule ext2_add_custom_response_header_rule = CreateModifyHeadersRule( + kMinValidID + 5, kMinValidPriority, "ghi.com", base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("header2", "append", "ext_2")})); + + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules( + {example_referer_rule, ext2_set_custom_request_header_rule, + ext2_add_custom_response_header_rule}, + "extension_2", {URLPattern::kAllUrlsPattern})); + + const ExtensionId extension_2_id = last_loaded_extension_id(); + ExtensionPrefs::Get(profile())->SetDNRUseActionCountAsBadgeText( + extension_2_id, true); + + ExtensionAction* extension_2_action = + ExtensionActionManager::Get(web_contents()->GetBrowserContext()) + ->GetExtensionAction(*extension_registry()->GetExtensionById( + extension_2_id, extensions::ExtensionRegistry::ENABLED)); + + struct { + GURL url; + bool use_referrer; + std::string expected_ext_1_badge_text; + std::string expected_ext_2_badge_text; + } test_cases[] = { + // This request only has a Set-Cookie header. Only the badge text for the + // extension with a remove Set-Cookie header rule should be incremented. + {get_set_cookie_url("example.com"), false, "1", ""}, + // This request only has a Referer header. Only the badge text for the + // extension with a remove Referer header rule should be incremented. + {get_referer_url("example.com"), true, "1", "1"}, + // This request has both a Referer and a Set-Cookie header. The badge text + // for both extensions should be incremented. + {get_set_cookie_url("example.com"), true, "2", "2"}, + // This request with a Referer and Set-Cookie header matches with one rule + // from |extension_1| and so the action count for |extension_1| should + // only increment by one, + {get_set_cookie_url("google.com"), true, "3", "2"}, + // This request with a Referer and Set-Cookie header matches with two + // separate rules from |extension_1| and so the action count for + // |extension_1| should increment by two. + {get_set_cookie_url("abc.com"), true, "5", "2"}, + // This request without headers matches rules to set the header1 request + // header from both extensions. Since |extension_2| was installed later + // than |extension_1|, only the rule from |extension_2| should take effect + // and so the action count for |extension_2| should increment by one. + {get_no_headers_url("def.com"), false, "5", "3"}, + // This request without headers matches rules to append the header2 + // response header from both extensions. Since each extension has a rule + // which has taken effect, the action count for both extensions should + // increment by one. + {get_no_headers_url("ghi.com"), false, "6", "4"}, + }; + + ui_test_utils::NavigateToURL(browser(), page_url); + ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame())); + + int first_tab_id = ExtensionTabUtil::GetTabId(web_contents()); + EXPECT_EQ("", extension_1_action->GetDisplayBadgeText(first_tab_id)); + EXPECT_EQ("", extension_2_action->GetDisplayBadgeText(first_tab_id)); + + for (const auto& test_case : test_cases) { + SCOPED_TRACE(base::StringPrintf("Testing URL: %s, using referrer: %s", + test_case.url.spec().c_str(), + test_case.use_referrer ? "true" : "false")); + + NavigateFrame(kFrameName1, test_case.url, test_case.use_referrer); + EXPECT_EQ(test_case.expected_ext_1_badge_text, + extension_1_action->GetDisplayBadgeText(first_tab_id)); + + EXPECT_EQ(test_case.expected_ext_2_badge_text, + extension_2_action->GetDisplayBadgeText(first_tab_id)); + } +} + // Test that the onRuleMatchedDebug event is only available for unpacked // extensions. IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, @@ -3286,6 +3460,81 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, ASSERT_EQ(expected_event_availability, actual_event_availability); } +// Test that the onRuleMatchedDebug event returns the correct number of matched +// rules for a request which is matched with multiple rules. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Unpacked, + OnRuleMatchedDebugMultipleRules) { + // This is only tested for unpacked extensions since the onRuleMatchedDebug + // event is only available for unpacked extensions. + ASSERT_EQ(ExtensionLoadType::UNPACKED, GetParam()); + + // Load the extension with a background script so scripts can be run from its + // generated background page. Also grant the feedback permission for the + // extension so it has access to the onRuleMatchedDebug event. + set_config_flags(ConfigFlag::kConfig_HasBackgroundScript | + ConfigFlag::kConfig_HasFeedbackPermission); + + const std::string kFrameName1 = "frame1"; + const std::string sub_frame_host = "abc.com"; + const GURL page_url = embedded_test_server()->GetURL( + "nomatch.com", "/page_with_two_frames.html"); + + TestRule abc_referer_rule = CreateModifyHeadersRule( + kMinValidID, kMinValidPriority, sub_frame_host, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("referer", "remove", base::nullopt)}), + base::nullopt); + + TestRule abc_set_cookie_rule = CreateModifyHeadersRule( + kMinValidID + 1, kMinValidPriority, sub_frame_host, base::nullopt, + std::vector<TestHeaderInfo>( + {TestHeaderInfo("set-cookie", "remove", base::nullopt)})); + + // Load an extension with removeHeaders rules for the Referer and Set-Cookie + // headers. + ASSERT_NO_FATAL_FAILURE( + LoadExtensionWithRules({abc_set_cookie_rule, abc_referer_rule}, + "extension_1", {URLPattern::kAllUrlsPattern})); + + ui_test_utils::NavigateToURL(browser(), page_url); + ASSERT_TRUE(WasFrameWithScriptLoaded(GetMainFrame())); + + // Start the onRuleMatchedDebug observer. + const char kOnRuleMatchedDebugScript[] = R"( + var matchedRules = []; + var onRuleMatchedDebugCallback = (rule) => { + matchedRules.push(rule); + }; + + chrome.declarativeNetRequest.onRuleMatchedDebug.addListener( + onRuleMatchedDebugCallback); + window.domAutomationController.send('ready'); + )"; + + ASSERT_EQ("ready", ExecuteScriptInBackgroundPage(last_loaded_extension_id(), + kOnRuleMatchedDebugScript)); + + auto set_cookie_and_referer_url = + embedded_test_server()->GetURL(sub_frame_host, "/set-cookie?a=b"); + + NavigateFrame(kFrameName1, set_cookie_and_referer_url); + + // Now query the onRuleMatchedDebug results. + const char kQueryMatchedRulesScript[] = R"( + chrome.declarativeNetRequest.onRuleMatchedDebug.removeListener( + onRuleMatchedDebugCallback); + var ruleIds = matchedRules.map(matchedRule => matchedRule.rule.ruleId); + window.domAutomationController.send(ruleIds.sort().join()); + )"; + + std::string matched_rule_ids = ExecuteScriptInBackgroundPage( + last_loaded_extension_id(), kQueryMatchedRulesScript); + + // The request to |set_cookie_and_referer_url| should be matched with the + // Referer rule (ruleId 1) and the Set-Cookie rule (ruleId 2). + EXPECT_EQ("1,2", matched_rule_ids); +} + // Test that getMatchedRules returns the correct rules when called by different // extensions with rules matched by requests initiated from different tabs. IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc index e4b0f34e4ee..4e96958b66a 100644 --- a/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_net_request/declarative_net_request_unittest.cc @@ -38,6 +38,7 @@ #include "extensions/browser/api/declarative_net_request/ruleset_manager.h" #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h" #include "extensions/browser/api/declarative_net_request/test_utils.h" +#include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/api_test_utils.h" #include "extensions/browser/disable_reason.h" #include "extensions/browser/test_extension_registry_observer.h" @@ -335,9 +336,9 @@ class SingleRulesetTest : public DeclarativeNetRequestUnittest { else rules_count = rules_list_.size(); - // We only index up to dnr_api::MAX_NUMBER_OF_RULES rules per ruleset. - rules_count = std::min(rules_count, - static_cast<size_t>(dnr_api::MAX_NUMBER_OF_RULES)); + // We only index up to GetStaticRuleLimit() rules per ruleset. + rules_count = + std::min(rules_count, static_cast<size_t>(GetStaticRuleLimit())); DeclarativeNetRequestUnittest::LoadAndExpectSuccess(rules_count, rules_count, true); @@ -652,10 +653,14 @@ TEST_P(SingleRulesetTest, InvalidJSONRules_Parsed) { } } -// Ensure that we can add up to MAX_NUMBER_OF_RULES. +// Ensure that we can add up to GetStaticRuleLimit() rules. TEST_P(SingleRulesetTest, RuleCountLimitMatched) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedStaticRuleLimitOverrideForTesting(100); + TestRule rule = CreateGenericRule(); - for (int i = 0; i < dnr_api::MAX_NUMBER_OF_RULES; ++i) { + for (int i = 0; i < GetStaticRuleLimit(); ++i) { rule.id = kMinValidID + i; rule.condition->url_filter = std::to_string(i); AddRule(rule); @@ -665,8 +670,12 @@ TEST_P(SingleRulesetTest, RuleCountLimitMatched) { // Ensure that we get an install warning on exceeding the rule count limit. TEST_P(SingleRulesetTest, RuleCountLimitExceeded) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedStaticRuleLimitOverrideForTesting(100); + TestRule rule = CreateGenericRule(); - for (int i = 1; i <= dnr_api::MAX_NUMBER_OF_RULES + 1; ++i) { + for (int i = 1; i <= GetStaticRuleLimit() + 1; ++i) { rule.id = kMinValidID + i; rule.condition->url_filter = std::to_string(i); AddRule(rule); @@ -749,10 +758,14 @@ TEST_P(SingleRulesetTest, WarningAndError) { // Ensure that we get an install warning on exceeding the regex rule count // limit. TEST_P(SingleRulesetTest, RegexRuleCountExceeded) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedRegexRuleLimitOverrideForTesting(100); + TestRule regex_rule = CreateGenericRule(); regex_rule.condition->url_filter.reset(); int rule_id = kMinValidID; - for (int i = 1; i <= dnr_api::MAX_NUMBER_OF_REGEX_RULES + 5; ++i, ++rule_id) { + for (int i = 1; i <= GetRegexRuleLimit() + 5; ++i, ++rule_id) { regex_rule.id = rule_id; regex_rule.condition->regex_filter = std::to_string(i); AddRule(regex_rule); @@ -767,8 +780,7 @@ TEST_P(SingleRulesetTest, RegexRuleCountExceeded) { } extension_loader()->set_ignore_manifest_warnings(true); - LoadAndExpectSuccess(dnr_api::MAX_NUMBER_OF_REGEX_RULES + - kCountNonRegexRules); + LoadAndExpectSuccess(GetRegexRuleLimit() + kCountNonRegexRules); // TODO(crbug.com/879355): CrxInstaller reloads the extension after moving it, // which causes it to lose the install warning. This should be fixed. if (GetParam() != ExtensionLoadType::PACKED) { @@ -952,10 +964,9 @@ class MultipleRulesetsTest : public DeclarativeNetRequestUnittest { for (const TestRulesetInfo& info : rulesets_) { size_t count = info.rules_value.GetList().size(); - // We only index up to dnr_api::MAX_NUMBER_OF_RULES per ruleset, but may + // We only index up to GetStaticRuleLimit() rules per ruleset, but may // index more rules than this limit across rulesets. - count = - std::min(count, static_cast<size_t>(dnr_api::MAX_NUMBER_OF_RULES)); + count = std::min(count, static_cast<size_t>(GetStaticRuleLimit())); rules_count += count; if (info.enabled) @@ -1017,6 +1028,12 @@ TEST_P(MultipleRulesetsTest, ListNotPassed) { // Tests an extension with multiple static rulesets with each ruleset generating // some install warnings. TEST_P(MultipleRulesetsTest, InstallWarnings) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedStaticRuleLimitOverrideForTesting(100); + base::AutoReset<int> regex_rule_limit_override = + CreateScopedRegexRuleLimitOverrideForTesting(60); + size_t expected_rule_count = 0; size_t enabled_rule_count = 0; std::vector<std::string> expected_warnings; @@ -1045,29 +1062,27 @@ TEST_P(MultipleRulesetsTest, InstallWarnings) { { // Persist a ruleset with an install warning for exceeding the rule count. TestRulesetInfo info = - CreateRuleset(kId2, dnr_api::MAX_NUMBER_OF_RULES + 1, 0, false); + CreateRuleset(kId2, GetStaticRuleLimit() + 1, 0, false); AddRuleset(info); expected_warnings.push_back( GetErrorWithFilename(kRuleCountExceeded, info.relative_file_path)); - expected_rule_count += dnr_api::MAX_NUMBER_OF_RULES; + expected_rule_count += GetStaticRuleLimit(); } { // Persist a ruleset with an install warning for exceeding the regex rule // count. size_t kCountNonRegexRules = 5; - TestRulesetInfo info = - CreateRuleset(kId3, kCountNonRegexRules, - dnr_api::MAX_NUMBER_OF_REGEX_RULES + 1, false); + TestRulesetInfo info = CreateRuleset(kId3, kCountNonRegexRules, + GetRegexRuleLimit() + 1, false); AddRuleset(info); expected_warnings.push_back( GetErrorWithFilename(kRegexRuleCountExceeded, info.relative_file_path)); - expected_rule_count += - kCountNonRegexRules + dnr_api::MAX_NUMBER_OF_REGEX_RULES; + expected_rule_count += kCountNonRegexRules + GetRegexRuleLimit(); } extension_loader()->set_ignore_manifest_warnings(true); @@ -1111,12 +1126,16 @@ TEST_P(MultipleRulesetsTest, EnabledRulesCount) { // Ensure that exceeding the rules count limit across rulesets raises an install // warning. TEST_P(MultipleRulesetsTest, StaticRuleCountExceeded) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedStaticRuleLimitOverrideForTesting(50); + // Enabled on load. AddRuleset(CreateRuleset(kId1, 10, 0, true)); // Disabled by default. AddRuleset(CreateRuleset(kId2, 20, 0, false)); // Not enabled on load since including it exceeds the static rules count. - AddRuleset(CreateRuleset(kId3, dnr_api::MAX_NUMBER_OF_RULES + 10, 0, true)); + AddRuleset(CreateRuleset(kId3, GetStaticRuleLimit() + 10, 0, true)); // Enabled on load. AddRuleset(CreateRuleset(kId4, 30, 0, true)); @@ -1126,8 +1145,6 @@ TEST_P(MultipleRulesetsTest, StaticRuleCountExceeded) { { // To prevent timeouts in debug builds, increase the wait timeout to the // test launcher's timeout. See crbug.com/1071403. - // TODO(karandeepb): Provide a way to fake dnr_api::MAX_NUMBER_OF_RULES in - // tests to decrease test runtime. base::test::ScopedRunLoopTimeout specific_timeout( FROM_HERE, TestTimeouts::test_launcher_timeout()); LoadAndExpectSuccess(); @@ -1167,7 +1184,7 @@ TEST_P(MultipleRulesetsTest, RegexRuleCountExceeded) { AddRuleset(CreateRuleset(kId1, 10000, 100, true)); // Won't be enabled on load since including it will exceed the regex rule // count. - AddRuleset(CreateRuleset(kId2, 1, dnr_api::MAX_NUMBER_OF_REGEX_RULES, true)); + AddRuleset(CreateRuleset(kId2, 1, GetRegexRuleLimit(), true)); // Won't be enabled on load since it is disabled by default. AddRuleset(CreateRuleset(kId3, 10, 10, false)); // Enabled on load. @@ -1225,8 +1242,12 @@ TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_InvalidRulesetID) { } TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_RuleCountExceeded) { + // Override the API rule limit to prevent a timeout on loading the extension. + base::AutoReset<int> rule_limit_override = + CreateScopedStaticRuleLimitOverrideForTesting(100); + AddRuleset(CreateRuleset(kId1, 10, 10, true)); - AddRuleset(CreateRuleset(kId2, dnr_api::MAX_NUMBER_OF_RULES, 0, false)); + AddRuleset(CreateRuleset(kId2, GetStaticRuleLimit(), 0, false)); RulesetManagerObserver ruleset_waiter(manager()); LoadAndExpectSuccess(); @@ -1245,7 +1266,7 @@ TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_RuleCountExceeded) { TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_RegexRuleCountExceeded) { AddRuleset(CreateRuleset(kId1, 0, 10, false)); - AddRuleset(CreateRuleset(kId2, 0, dnr_api::MAX_NUMBER_OF_REGEX_RULES, true)); + AddRuleset(CreateRuleset(kId2, 0, GetRegexRuleLimit(), true)); RulesetManagerObserver ruleset_waiter(manager()); LoadAndExpectSuccess(); @@ -1274,8 +1295,7 @@ TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_InternalError) { // First delete the indexed ruleset file for the second ruleset. Enabling it // should cause re-indexing and succeed in enabling the ruleset. base::HistogramTester tester; - ASSERT_TRUE(base::DeleteFile(static_sources[1].indexed_path(), - false /* recursive */)); + ASSERT_TRUE(base::DeleteFile(static_sources[1].indexed_path())); RunUpdateEnabledRulesetsFunction(*extension(), {kId1}, {kId2}, base::nullopt); @@ -1290,10 +1310,8 @@ TEST_P(MultipleRulesetsTest, UpdateEnabledRulesets_InternalError) { // Now delete both the indexed and json ruleset file for the first ruleset. // This will prevent enabling the first ruleset since re-indexing will fail. base::HistogramTester tester; - ASSERT_TRUE(base::DeleteFile(static_sources[0].indexed_path(), - false /* recursive */)); - ASSERT_TRUE( - base::DeleteFile(static_sources[0].json_path(), false /* recursive */)); + ASSERT_TRUE(base::DeleteFile(static_sources[0].indexed_path())); + ASSERT_TRUE(base::DeleteFile(static_sources[0].json_path())); RunUpdateEnabledRulesetsFunction(*extension(), {}, {kId1}, kInternalErrorUpdatingEnabledRulesets); 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 2c18c239b23..b1c2a521b2d 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 @@ -14,7 +14,6 @@ #include "chrome/browser/extensions/api/declarative_net_request/dnr_test_base.h" #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/extension_util.h" -#include "components/version_info/channel.h" #include "extensions/browser/api/declarative_net_request/composite_matcher.h" #include "extensions/browser/api/declarative_net_request/request_action.h" #include "extensions/browser/api/declarative_net_request/ruleset_matcher.h" @@ -28,7 +27,6 @@ #include "extensions/common/api/declarative_net_request.h" #include "extensions/common/api/declarative_net_request/constants.h" #include "extensions/common/api/declarative_net_request/test_utils.h" -#include "extensions/common/features/feature_channel.h" #include "extensions/common/file_util.h" #include "extensions/common/manifest_handlers/background_info.h" #include "extensions/common/url_pattern.h" @@ -96,7 +94,7 @@ class RulesetManagerTest : public DNRTestBase { &expected_checksum)); std::vector<std::unique_ptr<RulesetMatcher>> matchers(1); - EXPECT_EQ(RulesetMatcher::kLoadSuccess, + EXPECT_EQ(LoadRulesetResult::kSuccess, RulesetMatcher::CreateVerifiedMatcher( std::move(sources[0]), expected_checksum, &matchers[0])); @@ -458,22 +456,18 @@ TEST_P(RulesetManagerTest, ExtensionScheme) { // Test that the correct modifyHeaders actions are returned for each extension. TEST_P(RulesetManagerTest, ModifyHeaders) { - // TODO(crbug.com/947591): Remove the channel override once implementation of - // modifyHeaders action is complete. - ScopedCurrentChannel channel(::version_info::Channel::UNKNOWN); - const Extension* extension_1 = nullptr; const Extension* extension_2 = nullptr; - // Add an extension which removes the "header1" and "header2" headers. + // Add an extension which removes "header1" and sets "header2". { std::unique_ptr<CompositeMatcher> matcher; TestRule rule = CreateGenericRule(); rule.condition->url_filter = std::string("*"); rule.action->type = std::string("modifyHeaders"); - rule.action->request_headers = - std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "remove"), - TestHeaderInfo("header2", "remove")}); + rule.action->request_headers = std::vector<TestHeaderInfo>( + {TestHeaderInfo("header1", "remove", base::nullopt), + TestHeaderInfo("header2", "set", "value2")}); ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules( {rule}, "test extension", &matcher, {URLPattern::kAllUrlsPattern})); @@ -481,16 +475,16 @@ TEST_P(RulesetManagerTest, ModifyHeaders) { manager()->AddRuleset(extension_1->id(), std::move(matcher)); } - // Add another extension which removes the "header1" and "header3" headers. + // Add another extension which removes "header1" and appends "header3". { std::unique_ptr<CompositeMatcher> matcher; TestRule rule = CreateGenericRule(); rule.condition->url_filter = std::string("*"); rule.action->type = std::string("modifyHeaders"); - rule.action->request_headers = - std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "remove")}); - rule.action->response_headers = - std::vector<TestHeaderInfo>({TestHeaderInfo("header3", "remove")}); + rule.action->request_headers = std::vector<TestHeaderInfo>( + {TestHeaderInfo("header1", "remove", base::nullopt)}); + rule.action->response_headers = std::vector<TestHeaderInfo>( + {TestHeaderInfo("header3", "append", "value3")}); ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules( {rule}, "test extension 2", &matcher, {URLPattern::kAllUrlsPattern})); @@ -509,18 +503,20 @@ TEST_P(RulesetManagerTest, ModifyHeaders) { RequestAction expected_action_1 = CreateRequestActionForTesting( RequestActionType::MODIFY_HEADERS, kMinValidID, kDefaultPriority, kMinValidStaticRulesetID, extension_2->id()); - expected_action_1.request_headers_to_modify = { - RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE)}; - expected_action_1.response_headers_to_modify = { - RequestAction::HeaderInfo("header3", dnr_api::HEADER_OPERATION_REMOVE)}; + expected_action_1.request_headers_to_modify = {RequestAction::HeaderInfo( + "header1", dnr_api::HEADER_OPERATION_REMOVE, base::nullopt)}; + expected_action_1.response_headers_to_modify = {RequestAction::HeaderInfo( + "header3", dnr_api::HEADER_OPERATION_APPEND, "value3")}; // Create the expected RequestAction for |extension_1|. RequestAction expected_action_2 = CreateRequestActionForTesting( RequestActionType::MODIFY_HEADERS, kMinValidID, kDefaultPriority, kMinValidStaticRulesetID, extension_1->id()); expected_action_2.request_headers_to_modify = { - RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE), - RequestAction::HeaderInfo("header2", dnr_api::HEADER_OPERATION_REMOVE)}; + RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE, + base::nullopt), + RequestAction::HeaderInfo("header2", dnr_api::HEADER_OPERATION_SET, + "value2")}; // Verify that the list of actions is sorted in descending order of extension // priority. @@ -533,18 +529,14 @@ TEST_P(RulesetManagerTest, ModifyHeaders) { // Test that an extension's modify header rules are applied on a request only if // it has host permissions for the request. TEST_P(RulesetManagerTest, ModifyHeaders_HostPermissions) { - // TODO(crbug.com/947591): Remove the channel override once implementation of - // modifyHeaders action is complete. - ScopedCurrentChannel channel(::version_info::Channel::UNKNOWN); - // Add an extension which removes the "header1" header with host permissions // for example.com. std::unique_ptr<CompositeMatcher> matcher; TestRule rule = CreateGenericRule(); rule.condition->url_filter = std::string("*"); rule.action->type = std::string("modifyHeaders"); - rule.action->request_headers = - std::vector<TestHeaderInfo>({TestHeaderInfo("header1", "remove")}); + rule.action->request_headers = std::vector<TestHeaderInfo>( + {TestHeaderInfo("header1", "remove", base::nullopt)}); ASSERT_NO_FATAL_FAILURE(CreateMatcherForRules( {rule}, "test extension", &matcher, {"*://example.com/*"})); @@ -561,8 +553,8 @@ TEST_P(RulesetManagerTest, ModifyHeaders_HostPermissions) { RequestAction expected_action = CreateRequestActionForTesting( RequestActionType::MODIFY_HEADERS, kMinValidID, kDefaultPriority, kMinValidStaticRulesetID, extension->id()); - expected_action.request_headers_to_modify = { - RequestAction::HeaderInfo("header1", dnr_api::HEADER_OPERATION_REMOVE)}; + expected_action.request_headers_to_modify = {RequestAction::HeaderInfo( + "header1", dnr_api::HEADER_OPERATION_REMOVE, base::nullopt)}; EXPECT_THAT(actual_actions, ::testing::ElementsAre(::testing::Eq( ::testing::ByRef(expected_action)))); diff --git a/chromium/chrome/browser/extensions/api/developer_private/DEPS b/chromium/chrome/browser/extensions/api/developer_private/DEPS index 5b9436cd654..96ba367d2e6 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/DEPS +++ b/chromium/chrome/browser/extensions/api/developer_private/DEPS @@ -7,4 +7,10 @@ specific_include_rules = { # Allow the unittest to create a data_decoder service. "+services/data_decoder" ], + + # This DEPS violation snuck in while there was a bug in the checkdeps tool. + # https://crbug.com/1084826 + "developer_private_api\.cc": [ + "+chrome/browser/apps/app_service/app_launch_params.h", + ], } 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 c496bd428c4..56c6abbd526 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 @@ -19,7 +19,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "chrome/browser/apps/app_service/app_launch_params.h" #include "chrome/browser/devtools/devtools_window.h" @@ -394,7 +393,8 @@ void DeveloperPrivateEventRouter::RemoveExtensionId( void DeveloperPrivateEventRouter::OnExtensionLoaded( content::BrowserContext* browser_context, const Extension* extension) { - DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context))); + DCHECK( + profile_->IsSameOrParent(Profile::FromBrowserContext(browser_context))); BroadcastItemStateChanged(developer::EVENT_TYPE_LOADED, extension->id()); } @@ -402,7 +402,8 @@ void DeveloperPrivateEventRouter::OnExtensionUnloaded( content::BrowserContext* browser_context, const Extension* extension, UnloadedExtensionReason reason) { - DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context))); + DCHECK( + profile_->IsSameOrParent(Profile::FromBrowserContext(browser_context))); BroadcastItemStateChanged(developer::EVENT_TYPE_UNLOADED, extension->id()); } @@ -410,7 +411,8 @@ void DeveloperPrivateEventRouter::OnExtensionInstalled( content::BrowserContext* browser_context, const Extension* extension, bool is_update) { - DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context))); + DCHECK( + profile_->IsSameOrParent(Profile::FromBrowserContext(browser_context))); BroadcastItemStateChanged(developer::EVENT_TYPE_INSTALLED, extension->id()); } @@ -418,7 +420,8 @@ void DeveloperPrivateEventRouter::OnExtensionUninstalled( content::BrowserContext* browser_context, const Extension* extension, extensions::UninstallReason reason) { - DCHECK(profile_->IsSameProfile(Profile::FromBrowserContext(browser_context))); + DCHECK( + profile_->IsSameOrParent(Profile::FromBrowserContext(browser_context))); BroadcastItemStateChanged(developer::EVENT_TYPE_UNINSTALLED, extension->id()); } @@ -1486,8 +1489,8 @@ void DeveloperPrivateLoadDirectoryFunction::ClearExistingDirectoryContent( pending_copy_operations_count_ = 1; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPI, this, project_path, project_path.BaseName())); @@ -1555,8 +1558,8 @@ void DeveloperPrivateLoadDirectoryFunction::ReadDirectoryByFileSystemAPICb( response = NoArguments(); else response = Error(error_); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&DeveloperPrivateLoadDirectoryFunction::Respond, this, std::move(response))); } @@ -1597,8 +1600,8 @@ void DeveloperPrivateLoadDirectoryFunction::CopyFile( pending_copy_operations_count_--; if (!pending_copy_operations_count_) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&DeveloperPrivateLoadDirectoryFunction::Load, this)); } } 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 42ae22ba000..79ba1f83ece 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 @@ -466,7 +466,7 @@ TEST_F(DeveloperPrivateApiUnitTest, DeveloperPrivatePackFunction) { // Try to pack a final time when omitting (an existing) pem file. We should // get an error. - base::DeleteFile(crx_path, false); + base::DeleteFile(crx_path); EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the pem key argument. EXPECT_TRUE(pack_args.Remove(1u, nullptr)); // Remove the flags argument. EXPECT_TRUE(TestPackExtensionFunction( diff --git a/chromium/chrome/browser/extensions/api/developer_private/entry_picker.cc b/chromium/chrome/browser/extensions/api/developer_private/entry_picker.cc index afb404e5637..ad7d48b8c67 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/entry_picker.cc +++ b/chromium/chrome/browser/extensions/api/developer_private/entry_picker.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/files/file_path.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/developer_private/developer_private_api.h" #include "chrome/browser/platform_util.h" #include "chrome/browser/ui/chrome_select_file_policy.h" @@ -37,14 +36,14 @@ EntryPicker::EntryPicker(EntryPickerClient* client, : client_(client) { if (g_skip_picker_for_test) { if (g_path_to_be_picked_for_test) { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&EntryPicker::FileSelected, base::Unretained(this), *g_path_to_be_picked_for_test, 1, static_cast<void*>(nullptr))); } else { - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&EntryPicker::FileSelectionCanceled, base::Unretained(this), static_cast<void*>(nullptr))); } 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 6c4f2b9c43f..af6a8022a03 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 @@ -522,17 +522,10 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( // ControlledInfo. bool is_policy_location = Manifest::IsPolicyLocation(extension.location()); if (is_policy_location) { - info->controlled_info.reset(new developer::ControlledInfo()); - if (is_policy_location) { - info->controlled_info->type = developer::CONTROLLER_TYPE_POLICY; - info->controlled_info->text = - l10n_util::GetStringUTF8(IDS_EXTENSIONS_INSTALL_LOCATION_ENTERPRISE); - } else { - info->controlled_info->type = - developer::CONTROLLER_TYPE_SUPERVISED_USER_CUSTODIAN; - info->controlled_info->text = l10n_util::GetStringUTF8( - IDS_EXTENSIONS_INSTALLED_BY_SUPERVISED_USER_CUSTODIAN); - } + info->controlled_info = std::make_unique<developer::ControlledInfo>(); + info->controlled_info->type = developer::CONTROLLER_TYPE_POLICY; + info->controlled_info->text = + l10n_util::GetStringUTF8(IDS_EXTENSIONS_INSTALL_LOCATION_ENTERPRISE); } bool is_enabled = state == developer::EXTENSION_STATE_ENABLED; @@ -578,6 +571,7 @@ void ExtensionInfoGenerator::CreateExtensionInfoHelper( bool permissions_increase = (disable_reasons & disable_reason::DISABLE_PERMISSIONS_INCREASE) != 0; info->disable_reasons.parent_disabled_permissions = + supervised_user_service_->IsChild() && !supervised_user_service_ ->GetSupervisedUserExtensionsMayRequestPermissionsPref() && (custodian_approval_required || permissions_increase); 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 c80a16f7e47..47d6e81852b 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 @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback_helpers.h" +#include "base/files/file_path.h" #include "base/json/json_file_value_serializer.h" #include "base/json/json_writer.h" #include "base/macros.h" @@ -22,12 +23,13 @@ #include "chrome/browser/extensions/chrome_test_extension_loader.h" #include "chrome/browser/extensions/error_console/error_console.h" #include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/browser/extensions/extension_service_test_with_install.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/permissions_test_util.h" #include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/buildflags.h" #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/pref_names.h" #include "components/crx_file/id_util.h" @@ -44,6 +46,12 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#if BUILDFLAG(ENABLE_SUPERVISED_USERS) +#include "chrome/browser/supervised_user/supervised_user_service.h" +#include "chrome/browser/supervised_user/supervised_user_service_factory.h" +#include "chrome/browser/supervised_user/supervised_user_test_util.h" +#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) + namespace extensions { namespace developer = api::developer_private; @@ -88,15 +96,20 @@ std::string SiteControlsToString( } // namespace -class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestBase { +class ExtensionInfoGeneratorUnitTest : public ExtensionServiceTestWithInstall { public: ExtensionInfoGeneratorUnitTest() {} ~ExtensionInfoGeneratorUnitTest() override {} protected: void SetUp() override { - ExtensionServiceTestBase::SetUp(); - InitializeEmptyExtensionService(); + ExtensionServiceTestWithInstall::SetUp(); + InitializeExtensionService(GetExtensionServiceInitParams()); + } + + // Returns the initialization parameters for the extension service. + virtual ExtensionServiceInitParams GetExtensionServiceInitParams() { + return CreateDefaultInitParams(); } void OnInfoGenerated(std::unique_ptr<developer::ExtensionInfo>* info_out, @@ -810,4 +823,128 @@ TEST_F(ExtensionInfoGeneratorUnitTest, Blacklisted) { EXPECT_EQ(developer::EXTENSION_STATE_ENABLED, info2->state); } +// Tests that the parent_disabled_permissions disable reason is never set for +// regular users. Prevents a regression to crbug/1100395. +TEST_F(ExtensionInfoGeneratorUnitTest, + NoParentDisabledPermissionsForRegularUsers) { + // Preconditions. + ASSERT_FALSE(profile()->IsChild()); + + base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); + base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); + base::FilePath path = base_path.AppendASCII("v1"); + const Extension* extension = PackAndInstallCRX(path, pem_path, INSTALL_NEW); + // The extension must now be installed and enabled. + ASSERT_TRUE(extension); + ASSERT_TRUE(registry()->enabled_extensions().Contains(extension->id())); + + // Save the id, as |extension| will be destroyed during updating. + std::string extension_id = extension->id(); + + // Update to a new version with increased permissions. + path = base_path.AppendASCII("v2"); + PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); + + // The extension should be disabled pending approval for permission increases. + EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); + + // Due to a permissions increase, prefs will contain escalation information. + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); + EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); + + std::unique_ptr<api::developer_private::ExtensionInfo> info = + GenerateExtensionInfo(extension_id); + + // Verify that the kite icon error tooltip doesn't appear for regular users. + EXPECT_FALSE(info->disable_reasons.parent_disabled_permissions); +} + +#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. +class ExtensionInfoGeneratorUnitTestSupervised + : public ExtensionInfoGeneratorUnitTest { + public: + ExtensionInfoGeneratorUnitTestSupervised() = default; + ~ExtensionInfoGeneratorUnitTestSupervised() override = default; + + SupervisedUserService* GetSupervisedUserService() { + return SupervisedUserServiceFactory::GetForProfile(profile()); + } + + // ExtensionInfoGeneratorUnitTest: + ExtensionServiceInitParams GetExtensionServiceInitParams() override { + ExtensionServiceInitParams params = + ExtensionInfoGeneratorUnitTest::GetExtensionServiceInitParams(); + // Force a TestingPrefServiceSyncable to be created. + params.pref_file.clear(); + params.profile_is_supervised = true; + return params; + } + + void SetUp() override { + ExtensionInfoGeneratorUnitTest::SetUp(); + + // Set up custodians (parents) for the child. + supervised_user_test_util::AddCustodians(profile()); + + GetSupervisedUserService()->Init(); + // Set the pref to allow the child to request extension install. + GetSupervisedUserService() + ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(true); + } +}; + +// Tests that when an extension is disabled pending permission updates, and the +// parent has turned off the "Permissions for sites, apps and extensions" +// toggle, then supervised users will see a kite error icon with a tooltip. +TEST_F(ExtensionInfoGeneratorUnitTestSupervised, + ParentDisabledPermissionsForSupervisedUsers) { + // Preconditions. + ASSERT_TRUE(profile()->IsChild()); + + base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); + base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); + base::FilePath path = base_path.AppendASCII("v1"); + const Extension* extension = + PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); + // The extension should be installed but disabled pending custodian approval. + ASSERT_TRUE(extension); + EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); + + // Save the id, as |extension| will be destroyed during updating. + std::string extension_id = extension->id(); + + ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); + EXPECT_TRUE(prefs->HasDisableReason( + extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); + + // Simulate parent approval for the extension installation. + GetSupervisedUserService()->AddExtensionApproval(*extension); + // The extension should be enabled now. + EXPECT_TRUE(registry()->enabled_extensions().Contains(extension_id)); + + // Update to a new version with increased permissions. + path = base_path.AppendASCII("v2"); + PackCRXAndUpdateExtension(extension_id, path, pem_path, DISABLED); + + // The extension should be disabled pending approval for permission increases. + EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); + + // Due to a permission increase, prefs will contain escalation information. + EXPECT_TRUE(prefs->DidExtensionEscalatePermissions(extension_id)); + + // Simulate the parent disallowing the child from approving permission + // updates. + GetSupervisedUserService() + ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); + + std::unique_ptr<api::developer_private::ExtensionInfo> info = + GenerateExtensionInfo(extension_id); + + // Verify that the kite icon error tooltip appears for supervised users. + EXPECT_TRUE(info->disable_reasons.parent_disabled_permissions); +} + +#endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc index 2ae57ab4ddd..cd347b06c7b 100644 --- a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc +++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc @@ -1563,7 +1563,7 @@ ExtensionFunction::ResponseAction DownloadsSetShelfEnabledFunction::Run() { (current_service == incognito_service)) && browser->window()->IsDownloadShelfVisible() && !current_service->IsShelfEnabled()) - browser->window()->GetDownloadShelf()->Close(DownloadShelf::AUTOMATIC); + browser->window()->GetDownloadShelf()->Close(); } } 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 fc10f9ec110..fbf95144f6e 100644 --- a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc @@ -18,7 +18,6 @@ #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h" #include "base/threading/thread_restrictions.h" @@ -68,12 +67,6 @@ #include "net/dns/mock_host_resolver.h" #include "net/test/embedded_test_server/controllable_http_response.h" #include "net/test/embedded_test_server/embedded_test_server.h" -#include "net/test/url_request/url_request_slow_download_job.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_context.h" -#include "net/url_request/url_request_job.h" -#include "net/url_request/url_request_job_factory.h" -#include "net/url_request/url_request_job_factory_impl.h" #include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_operation_runner.h" #include "storage/browser/file_system/file_system_url.h" @@ -114,8 +107,8 @@ void OnOpenPromptCreated(download::DownloadItem* item, DownloadOpenPrompt* prompt) { EXPECT_FALSE(item->GetOpened()); // Posts a task to accept the DownloadOpenPrompt. - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&DownloadOpenPrompt::AcceptConfirmationDialogForTesting, base::Unretained(prompt))); } @@ -321,10 +314,9 @@ class DownloadOpenObserver : public download::DownloadItem::Observer { class DownloadExtensionTest : public ExtensionApiTest { public: DownloadExtensionTest() - : extension_(NULL), - incognito_browser_(NULL), - current_browser_(NULL) { - } + : extension_(nullptr), + incognito_browser_(nullptr), + current_browser_(nullptr) {} protected: // Used with CreateHistoryDownloads @@ -709,8 +701,8 @@ class MockIconExtractorImpl : public DownloadFileIconExtractor { if (expected_path_ == path && expected_icon_size_ == icon_size) { callback_ = callback; - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&MockIconExtractorImpl::RunCallback, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&MockIconExtractorImpl::RunCallback, base::Unretained(this))); return true; } else { @@ -793,14 +785,14 @@ class HTML5FileWriter { // Invoke the fileapi to copy it into the sandboxed filesystem. bool result = false; base::RunLoop run_loop; - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&CreateFileForTestingOnIOThread, base::Unretained(context), path, temp_file, base::Unretained(&result), run_loop.QuitClosure())); // Wait for that to finish. run_loop.Run(); - base::DeleteFile(temp_file, false); + base::DeleteFile(temp_file); return result; } @@ -810,7 +802,7 @@ class HTML5FileWriter { base::File::Error error) { DCHECK_CURRENTLY_ON(BrowserThread::IO); *result = error == base::File::FILE_OK; - base::PostTask(FROM_HERE, {BrowserThread::UI}, quit_closure); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, quit_closure); } static void CreateFileForTestingOnIOThread( @@ -882,14 +874,7 @@ downloads::InterruptReason InterruptReasonContentToExtension( } // namespace -#if defined(OS_CHROMEOS) -// http://crbug.com/396510 -#define MAYBE_DownloadExtensionTest_Open DISABLED_DownloadExtensionTest_Open -#else -#define MAYBE_DownloadExtensionTest_Open DownloadExtensionTest_Open -#endif -IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_Open) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_Open) { LoadExtension("downloads_split"); DownloadsOpenFunction* open_function = new DownloadsOpenFunction(); open_function->set_user_gesture(true); @@ -1056,16 +1041,10 @@ scoped_refptr<ExtensionFunction> MockedGetFileIconFunction( return function; } -// https://crbug.com/678967 -#if defined(OS_WIN) || defined(OS_LINUX) -#define MAYBE_DownloadExtensionTest_FileIcon_Active DISABLED_DownloadExtensionTest_FileIcon_Active -#else -#define MAYBE_DownloadExtensionTest_FileIcon_Active DownloadExtensionTest_FileIcon_Active -#endif // Test downloads.getFileIcon() on in-progress, finished, cancelled and deleted // download items. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_FileIcon_Active) { + DownloadExtensionTest_FileIcon_Active) { DownloadItem* download_item = CreateFirstSlowTestDownload(); ASSERT_TRUE(download_item); ASSERT_FALSE(download_item->GetTargetFilePath().empty()); @@ -1202,7 +1181,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, FileExistenceCheckAfterSearch) { // Finish the download and try again. FinishFirstSlowDownloads(); - base::DeleteFile(download_item->GetTargetFilePath(), false); + base::DeleteFile(download_item->GetTargetFilePath()); ASSERT_FALSE(download_item->GetFileExternallyRemoved()); std::unique_ptr<base::Value> result( @@ -1266,14 +1245,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, // Test the |id| parameter for search(). // -// http://crbug.com/508949 -#if defined(MEMORY_SANITIZER) -#define MAYBE_DownloadExtensionTest_SearchId DISABLED_DownloadExtensionTest_SearchId -#else -#define MAYBE_DownloadExtensionTest_SearchId DownloadExtensionTest_SearchId -#endif -IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_SearchId) { +IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, DownloadExtensionTest_SearchId) { DownloadManager::DownloadVector items; CreateTwoDownloads(&items); ScopedItemVectorCanceller delete_items(&items); @@ -1294,14 +1266,8 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, // Test specifying both the |id| and |filename| parameters for search(). // -// http://crbug.com/508949 -#if defined(MEMORY_SANITIZER) -#define MAYBE_DownloadExtensionTest_SearchIdAndFilename DISABLED_DownloadExtensionTest_SearchIdAndFilename -#else -#define MAYBE_DownloadExtensionTest_SearchIdAndFilename DownloadExtensionTest_SearchIdAndFilename -#endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_SearchIdAndFilename) { + DownloadExtensionTest_SearchIdAndFilename) { DownloadManager::DownloadVector items; CreateTwoDownloads(&items); ScopedItemVectorCanceller delete_items(&items); @@ -1400,15 +1366,8 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, } // Test the |state| option for search(). -// -// http://crbug.com/508949 -#if defined(MEMORY_SANITIZER) -#define MAYBE_DownloadExtensionTest_SearchState DISABLED_DownloadExtensionTest_SearchState -#else -#define MAYBE_DownloadExtensionTest_SearchState DownloadExtensionTest_SearchState -#endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_SearchState) { + DownloadExtensionTest_SearchState) { DownloadManager::DownloadVector items; CreateTwoDownloads(&items); ScopedItemVectorCanceller delete_items(&items); @@ -1424,15 +1383,8 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, } // Test the |limit| option for search(). -// -// http://crbug.com/508949 -#if defined(MEMORY_SANITIZER) -#define MAYBE_DownloadExtensionTest_SearchLimit DISABLED_DownloadExtensionTest_SearchLimit -#else -#define MAYBE_DownloadExtensionTest_SearchLimit DownloadExtensionTest_SearchLimit -#endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_SearchLimit) { + DownloadExtensionTest_SearchLimit) { DownloadManager::DownloadVector items; CreateTwoDownloads(&items); ScopedItemVectorCanceller delete_items(&items); @@ -1495,21 +1447,13 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, ASSERT_EQ(items[2]->GetTargetFilePath().value(), item_name); } -// https://crbug.com/874946, flaky on Win. -#if defined(OS_WIN) -#define MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito \ - DISABLED_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito -#else -#define MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito \ - DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito -#endif // Test that incognito downloads are only visible in incognito contexts, and // test that on-record downloads are visible in both incognito and on-record // contexts, for DownloadsSearchFunction, DownloadsPauseFunction, // DownloadsResumeFunction, and DownloadsCancelFunction. IN_PROC_BROWSER_TEST_F( DownloadExtensionTest, - MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) { + DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) { std::unique_ptr<base::Value> result_value; base::ListValue* result_list = NULL; base::DictionaryValue* result_dict = NULL; @@ -1887,18 +1831,9 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, result_id))); } -#if defined(OS_WIN) -// This test is very flaky on Win. http://crbug.com/248438 -#define MAYBE_DownloadExtensionTest_Download_UnsafeHeaders \ - DISABLED_DownloadExtensionTest_Download_UnsafeHeaders -#else -#define MAYBE_DownloadExtensionTest_Download_UnsafeHeaders \ - DownloadExtensionTest_Download_UnsafeHeaders -#endif - // Test that we disallow certain headers case-insensitively. IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, - MAYBE_DownloadExtensionTest_Download_UnsafeHeaders) { + DownloadExtensionTest_Download_UnsafeHeaders) { LoadExtension("downloads_split"); ASSERT_TRUE(StartEmbeddedTestServer()); GoOnTheRecord(); @@ -1975,6 +1910,7 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, download_url.c_str())).c_str()); } +// This test is very flaky on Win. http://crbug.com/248438 #if defined(OS_WIN) #define MAYBE_DownloadExtensionTest_Download_Subdirectory\ DISABLED_DownloadExtensionTest_Download_Subdirectory @@ -2043,19 +1979,10 @@ IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, download_url.c_str())).c_str()); } -// flaky on mac: crbug.com/392288 -#if defined(OS_MACOSX) -#define MAYBE_DownloadExtensionTest_Download_InvalidURLs \ - DISABLED_DownloadExtensionTest_Download_InvalidURLs -#else -#define MAYBE_DownloadExtensionTest_Download_InvalidURLs \ - DownloadExtensionTest_Download_InvalidURLs -#endif - class DownloadExtensionTestWithFtp : public DownloadExtensionTest { public: DownloadExtensionTestWithFtp() { - // DownloadExtensionTest_Download_InvalidURLs requires FTP support. + // DownloadExtensionTest_Download_InvalidURLs2 requires FTP support. // TODO(https://crbug.com/333943): Remove FTP tests and FTP feature flags. scoped_feature_list_.InitAndEnableFeature(features::kFtpProtocol); } @@ -2066,27 +1993,27 @@ class DownloadExtensionTestWithFtp : public DownloadExtensionTest { // Test that downloading invalid URLs immediately returns kInvalidURLError. IN_PROC_BROWSER_TEST_F(DownloadExtensionTestWithFtp, - MAYBE_DownloadExtensionTest_Download_InvalidURLs) { - LoadExtension("downloads_split"); - GoOnTheRecord(); - - static const char* const kInvalidURLs[] = { - "foo bar", - "../hello", - "/hello", - "http://", - "#frag", - "foo/bar.html#frag", - "google.com/", + DownloadExtensionTest_Download_InvalidURLs1) { + static constexpr const char* kInvalidURLs[] = { + "foo bar", "../hello", "/hello", "http://", + "#frag", "foo/bar.html#frag", "google.com/", }; - for (size_t index = 0; index < base::size(kInvalidURLs); ++index) { + for (const char* url : kInvalidURLs) { EXPECT_STREQ(errors::kInvalidURL, - RunFunctionAndReturnError(new DownloadsDownloadFunction(), - base::StringPrintf( - "[{\"url\": \"%s\"}]", kInvalidURLs[index])).c_str()) - << kInvalidURLs[index]; + RunFunctionAndReturnError( + new DownloadsDownloadFunction(), + base::StringPrintf("[{\"url\": \"%s\"}]", url)) + .c_str()) + << url; } +} + +// Test various failure modes for downloading invalid URLs. +IN_PROC_BROWSER_TEST_F(DownloadExtensionTestWithFtp, + DownloadExtensionTest_Download_InvalidURLs2) { + LoadExtension("downloads_split"); + GoOnTheRecord(); int result_id = -1; std::unique_ptr<base::Value> result(RunFunctionAndReturnResult( @@ -4255,8 +4182,11 @@ IN_PROC_BROWSER_TEST_F( // resumed. http://crbug.com/225901 ui_test_utils::NavigateToURLWithDisposition( current_browser(), - GURL(net::URLRequestSlowDownloadJob::kUnknownSizeUrl), - WindowOpenDisposition::CURRENT_TAB, ui_test_utils::BROWSER_TEST_NONE); + // This code used to use a mock class that no longer works, due to the + // NetworkService shipping. + // TODO(https://crbug.com/700382): Fix or delete this test. + GURL(), WindowOpenDisposition::CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); observer->WaitForFinished(); EXPECT_EQ(1u, observer->NumDownloadsSeenInState(DownloadItem::IN_PROGRESS)); DownloadManager::DownloadVector items; @@ -4293,8 +4223,10 @@ IN_PROC_BROWSER_TEST_F( ClearEvents(); ui_test_utils::NavigateToURLWithDisposition( current_browser(), - GURL(net::URLRequestSlowDownloadJob::kErrorDownloadUrl), - WindowOpenDisposition::NEW_BACKGROUND_TAB, + // This code used to use a mock class that no longer works, due to the + // NetworkService shipping. + // TODO(https://crbug.com/700382): Fix or delete this test. + GURL(), WindowOpenDisposition::NEW_BACKGROUND_TAB, ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP); // Errors caught before filename determination are delayed until after @@ -4393,12 +4325,14 @@ void OnDangerPromptCreated(DownloadDangerPrompt* prompt) { prompt->InvokeActionForTesting(DownloadDangerPrompt::ACCEPT); } -#if defined(OS_MACOSX) -// Flakily triggers and assert on Mac. +#if defined(OS_MACOSX) && !defined(NDEBUG) +// Flaky on Mac debug, failing with a timeout. // http://crbug.com/180759 -#define MAYBE_DownloadExtensionTest_AcceptDanger DISABLED_DownloadExtensionTest_AcceptDanger +#define MAYBE_DownloadExtensionTest_AcceptDanger \ + DISABLED_DownloadExtensionTest_AcceptDanger #else -#define MAYBE_DownloadExtensionTest_AcceptDanger DownloadExtensionTest_AcceptDanger +#define MAYBE_DownloadExtensionTest_AcceptDanger \ + DownloadExtensionTest_AcceptDanger #endif IN_PROC_BROWSER_TEST_F(DownloadExtensionTest, MAYBE_DownloadExtensionTest_AcceptDanger) { diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc index d94b303a10b..1e25f6a94be 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.cc @@ -26,14 +26,17 @@ namespace { // Checks for the current browser context if the user is affiliated or belongs // to the sign-in profile. -bool IsPermittedToGetDeviceAttributes(content::BrowserContext* context) { - if (chromeos::ProfileHelper::IsSigninProfile( - Profile::FromBrowserContext(context))) { +bool CanGetDeviceAttributesForBrowserContext(content::BrowserContext* context) { + const Profile* profile = Profile::FromBrowserContext(context); + + if (chromeos::ProfileHelper::IsSigninProfile(profile)) return true; - } + + if (!profile->IsRegularProfile()) + return false; + const user_manager::User* user = - chromeos::ProfileHelper::Get()->GetUserByProfile( - Profile::FromBrowserContext(context)); + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); return user->IsAffiliated(); } @@ -48,7 +51,7 @@ EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction:: ExtensionFunction::ResponseAction EnterpriseDeviceAttributesGetDirectoryDeviceIdFunction::Run() { std::string device_id; - if (IsPermittedToGetDeviceAttributes(browser_context())) { + if (CanGetDeviceAttributesForBrowserContext(browser_context())) { device_id = g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetDirectoryApiID(); @@ -67,7 +70,7 @@ EnterpriseDeviceAttributesGetDeviceSerialNumberFunction:: ExtensionFunction::ResponseAction EnterpriseDeviceAttributesGetDeviceSerialNumberFunction::Run() { std::string serial_number; - if (IsPermittedToGetDeviceAttributes(browser_context())) { + if (CanGetDeviceAttributesForBrowserContext(browser_context())) { serial_number = chromeos::system::StatisticsProvider::GetInstance() ->GetEnterpriseMachineID(); } @@ -85,7 +88,7 @@ EnterpriseDeviceAttributesGetDeviceAssetIdFunction:: ExtensionFunction::ResponseAction EnterpriseDeviceAttributesGetDeviceAssetIdFunction::Run() { std::string asset_id; - if (IsPermittedToGetDeviceAttributes(browser_context())) { + if (CanGetDeviceAttributesForBrowserContext(browser_context())) { asset_id = g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetDeviceAssetID(); @@ -104,7 +107,7 @@ EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction:: ExtensionFunction::ResponseAction EnterpriseDeviceAttributesGetDeviceAnnotatedLocationFunction::Run() { std::string annotated_location; - if (IsPermittedToGetDeviceAttributes(browser_context())) { + if (CanGetDeviceAttributesForBrowserContext(browser_context())) { annotated_location = g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetDeviceAnnotatedLocation(); @@ -123,7 +126,7 @@ EnterpriseDeviceAttributesGetDeviceHostnameFunction:: ExtensionFunction::ResponseAction EnterpriseDeviceAttributesGetDeviceHostnameFunction::Run() { std::string hostname; - if (IsPermittedToGetDeviceAttributes(browser_context())) { + if (CanGetDeviceAttributesForBrowserContext(browser_context())) { hostname = g_browser_process->platform_part() ->browser_policy_connector_chromeos() ->GetHostnameHandler() diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h index 0974be24255..33d41d1169b 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h +++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_api.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_DEVICE_ATTRIBUTES_ENTERPRISE_DEVICE_ATTRIBUTES_API_H_ #include "extensions/browser/extension_function.h" +#include "extensions/browser/extension_function_histogram_value.h" namespace extensions { diff --git a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc index 0309f441857..cfbdae447b1 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_device_attributes/enterprise_device_attributes_apitest.cc @@ -2,38 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include <memory> - -#include "base/bind.h" -#include "base/json/json_writer.h" -#include "base/path_service.h" -#include "base/task/post_task.h" +#include "base/files/file_path.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/policy/affiliation_test_helper.h" -#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" -#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" +#include "chrome/browser/chromeos/policy/device_policy_builder.h" +#include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h" #include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/extensions/policy_test_utils.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/test/base/ui_test_utils.h" #include "chromeos/dbus/session_manager/fake_session_manager_client.h" #include "chromeos/system/fake_statistics_provider.h" #include "chromeos/system/statistics_provider.h" -#include "chromeos/tpm/stub_install_attributes.h" -#include "components/account_id/account_id.h" -#include "components/policy/core/common/cloud/device_management_service.h" -#include "components/policy/core/common/mock_configuration_policy_provider.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 "content/public/browser/notification_service.h" #include "content/public/test/browser_test.h" #include "content/public/test/test_utils.h" -#include "extensions/browser/api_test_utils.h" #include "extensions/browser/extension_registry.h" -#include "extensions/test/result_catcher.h" +#include "url/gurl.h" namespace { @@ -42,30 +25,10 @@ constexpr char kSerialNumber[] = "serial_number"; constexpr char kAssetId[] = "asset_id"; constexpr char kAnnotatedLocation[] = "annotated_location"; constexpr char kHostname[] = "hostname"; -constexpr char kUpdateManifestPath[] = - "/extensions/api_test/enterprise_device_attributes/update_manifest.xml"; -constexpr char kAffiliatedUserEmail[] = "user@example.com"; -constexpr char kAffiliatedUserGaiaId[] = "1029384756"; -constexpr char kAffiliationID[] = "some-affiliation-id"; -constexpr char kAnotherAffiliationID[] = "another-affiliation-id"; - -// The managed_storage extension has a key defined in its manifest, so that -// its extension ID is well-known and the policy system can push policies for -// the extension. constexpr char kTestExtensionID[] = "nbiliclbejdndfpchgkbmfoppjplbdok"; - -struct Params { - explicit Params(bool affiliated) : affiliated(affiliated) {} - bool affiliated; -}; - -// Must be a valid test name (no spaces etc.). Makes the test show up as e.g. -// AffiliationCheck/U.A.B.T.Affiliated/NotAffiliated_NotActiveDirectory -std::string PrintParam(testing::TestParamInfo<Params> param_info) { - return base::StringPrintf("%sAffiliated", - param_info.param.affiliated ? "" : "Not"); -} +constexpr char kUpdateManifestPath[] = + "/extensions/api_test/enterprise_device_attributes/update_manifest.xml"; base::Value BuildCustomArg(const std::string& expected_directory_device_id, const std::string& expected_serial_number, @@ -89,49 +52,21 @@ base::Value BuildCustomArg(const std::string& expected_directory_device_id, namespace extensions { class EnterpriseDeviceAttributesTest - : public ExtensionApiTest, - public ::testing::WithParamInterface<Params> { + : public ForceInstalledAffiliatedExtensionApiTest, + public ::testing::WithParamInterface<bool> { public: - EnterpriseDeviceAttributesTest() { + EnterpriseDeviceAttributesTest() + : ForceInstalledAffiliatedExtensionApiTest(GetParam()) { fake_statistics_provider_.SetMachineStatistic( chromeos::system::kSerialNumberKeyForTest, kSerialNumber); - set_exit_when_last_browser_closes(false); - set_chromeos_user_ = false; } protected: - // ExtensionApiTest - void SetUpCommandLine(base::CommandLine* command_line) override { - ExtensionApiTest::SetUpCommandLine(command_line); - policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager( - command_line); - } - + // ForceInstalledAffiliatedExtensionApiTest void SetUpInProcessBrowserTestFixture() override { - ExtensionApiTest::SetUpInProcessBrowserTestFixture(); - - chromeos::SessionManagerClient::InitializeFakeInMemory(); - policy::AffiliationTestHelper affiliation_helper = - policy::AffiliationTestHelper::CreateForCloud( - chromeos::FakeSessionManagerClient::Get()); + ForceInstalledAffiliatedExtensionApiTest:: + SetUpInProcessBrowserTestFixture(); - std::set<std::string> device_affiliation_ids; - device_affiliation_ids.insert(kAffiliationID); - ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs( - &test_helper_, device_affiliation_ids)); - test_helper_.InstallOwnerKey(); - - std::set<std::string> user_affiliation_ids; - if (GetParam().affiliated) { - user_affiliation_ids.insert(kAffiliationID); - } else { - user_affiliation_ids.insert(kAnotherAffiliationID); - } - policy::UserPolicyBuilder user_policy; - ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs( - &user_policy, affiliated_account_id_, user_affiliation_ids)); - - test_helper_.InstallOwnerKey(); // Init the device policy. policy::DevicePolicyBuilder* device_policy = test_helper_.device_policy(); device_policy->SetDefaultSigningKey(); @@ -146,67 +81,9 @@ class EnterpriseDeviceAttributesTest chromeos::FakeSessionManagerClient::Get()->set_device_policy( device_policy->GetBlob()); chromeos::FakeSessionManagerClient::Get()->OnPropertyChangeComplete(true); - - // Init the user policy provider. - EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_)) - .WillRepeatedly(testing::Return(true)); - policy_provider_.SetAutoRefresh(); - policy::BrowserPolicyConnector::SetPolicyProviderForTesting( - &policy_provider_); - - // Set retry delay to prevent timeouts. - policy::DeviceManagementService::SetRetryDelayForTesting(0); } - void SetUpOnMainThread() override { - const base::ListValue* users = - g_browser_process->local_state()->GetList("LoggedInUsers"); - if (!users->empty()) - policy::AffiliationTestHelper::LoginUser(affiliated_account_id_); - - ExtensionApiTest::SetUpOnMainThread(); - } - - // Load |page_url| in |browser| and wait for PASSED or FAILED notification. - // The functionality of this function is reduced functionality of - // RunExtensionSubtest(), but we don't use it here because it requires - // function InProcessBrowserTest::browser() to return non-NULL pointer. - // Unfortunately it returns the value which is set in constructor and can't be - // modified. Because on login flow there is no browser, the function - // InProcessBrowserTest::browser() always returns NULL. Besides this we need - // only very little functionality from RunExtensionSubtest(). Thus so that - // don't make RunExtensionSubtest() to complex we just introduce a new - // function. - bool TestExtension(Browser* browser, - const std::string& page_url, - const base::Value& custom_arg_value) { - DCHECK(!page_url.empty()) << "page_url cannot be empty"; - - std::string custom_arg; - base::JSONWriter::Write(custom_arg_value, &custom_arg); - SetCustomArg(custom_arg); - - extensions::ResultCatcher catcher; - ui_test_utils::NavigateToURL(browser, GURL(page_url)); - - if (!catcher.GetNextResult()) { - message_ = catcher.message(); - return false; - } - return true; - } - - const AccountId affiliated_account_id_ = - AccountId::FromUserEmailGaiaId(kAffiliatedUserEmail, - kAffiliatedUserGaiaId); - - policy::MockConfigurationPolicyProvider policy_provider_; - private: - chromeos::ScopedStubInstallAttributes test_install_attributes_{ - chromeos::StubInstallAttributes::CreateCloudManaged("fake-domain", - "fake-id")}; - policy::DevicePolicyCrosTestHelper test_helper_; chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_; }; @@ -215,36 +92,33 @@ IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, PRE_Success) { } IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, Success) { - policy_test_utils::SetUpEmbeddedTestServer(embedded_test_server()); - ASSERT_TRUE(embedded_test_server()->Start()); - policy_test_utils::SetExtensionInstallForcelistPolicy( - kTestExtensionID, embedded_test_server()->GetURL(kUpdateManifestPath), - profile(), &policy_provider_); + const bool is_affiliated = GetParam(); + EXPECT_EQ(is_affiliated, user_manager::UserManager::Get() + ->FindUser(affiliated_account_id_) + ->IsAffiliated()); - EXPECT_EQ(GetParam().affiliated, user_manager::UserManager::Get() - ->FindUser(affiliated_account_id_) - ->IsAffiliated()); + const Extension* extension = + ForceInstallExtension(kTestExtensionID, kUpdateManifestPath); + const GURL test_url = extension->GetResourceURL("basic.html"); // Device attributes are available only for affiliated user. - std::string expected_directory_device_id = - GetParam().affiliated ? kDeviceId : ""; - std::string expected_serial_number = - GetParam().affiliated ? kSerialNumber : ""; - std::string expected_asset_id = GetParam().affiliated ? kAssetId : ""; + std::string expected_directory_device_id = is_affiliated ? kDeviceId : ""; + std::string expected_serial_number = is_affiliated ? kSerialNumber : ""; + std::string expected_asset_id = is_affiliated ? kAssetId : ""; std::string expected_annotated_location = - GetParam().affiliated ? kAnnotatedLocation : ""; - std::string expected_hostname = GetParam().affiliated ? kHostname : ""; - - // Pass the expected value (device_id) to test. - ASSERT_TRUE(TestExtension( - CreateBrowser(profile()), - base::StringPrintf("chrome-extension://%s/basic.html", kTestExtensionID), - BuildCustomArg(expected_directory_device_id, expected_serial_number, - expected_asset_id, expected_annotated_location, - expected_hostname))) - << message_; + is_affiliated ? kAnnotatedLocation : ""; + std::string expected_hostname = is_affiliated ? kHostname : ""; + TestExtension(CreateBrowser(profile()), test_url, + BuildCustomArg(expected_directory_device_id, + expected_serial_number, expected_asset_id, + expected_annotated_location, expected_hostname)); } +// Both cases of affiliated and non-affiliated users are tested. +INSTANTIATE_TEST_SUITE_P(AffiliationCheck, + EnterpriseDeviceAttributesTest, + ::testing::Bool()); + // Ensure that extensions that are not pre-installed by policy throw an install // warning if they request the enterprise.deviceAttributes permission in the // manifest and that such extensions don't see the @@ -258,10 +132,10 @@ IN_PROC_BROWSER_TEST_F( base::FilePath extension_path = test_data_dir_.AppendASCII("enterprise_device_attributes"); - extensions::ExtensionRegistry* registry = - extensions::ExtensionRegistry::Get(profile()); const extensions::Extension* extension = - GetExtensionByPath(registry->enabled_extensions(), extension_path); + extensions::ExtensionRegistry::Get(profile()) + ->enabled_extensions() + .GetByID(kTestExtensionID); ASSERT_FALSE(extension->install_warnings().empty()); EXPECT_EQ( "'enterprise.deviceAttributes' is not allowed for specified install " @@ -269,10 +143,4 @@ IN_PROC_BROWSER_TEST_F( extension->install_warnings()[0].message); } -// Both cases of affiliated and non-affiliated on the device user are tested. -INSTANTIATE_TEST_SUITE_P(AffiliationCheck, - EnterpriseDeviceAttributesTest, - ::testing::Values(Params(true /* affiliated */), - Params(false /* affiliated */)), - PrintParam); } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/OWNERS b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/OWNERS new file mode 100644 index 00000000000..d11193dcf55 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/OWNERS @@ -0,0 +1,3 @@ +hendrich@chromium.org + +# COMPONENT: Enterprise diff --git a/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc new file mode 100644 index 00000000000..981d40da62b --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.cc @@ -0,0 +1,90 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.h" + +#include "base/values.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/enterprise_networking_attributes.h" +#include "chromeos/network/device_state.h" +#include "chromeos/network/network_handler.h" +#include "chromeos/network/network_state.h" +#include "chromeos/network/network_state_handler.h" +#include "chromeos/network/network_util.h" +#include "components/user_manager/user.h" +#include "third_party/cros_system_api/dbus/shill/dbus-constants.h" + +namespace extensions { + +namespace { + +const char kErrorUserNotAffiliated[] = + "Network attributes can only be read by an affiliated user."; +const char kErrorNetworkNotConnected[] = + "Device is not connected to a network."; + +// Checks for the current browser context if the user is affiliated or belongs +// to the sign-in profile. +bool CanGetNetworkAttributesForBrowserContext( + content::BrowserContext* context) { + const Profile* profile = Profile::FromBrowserContext(context); + + if (chromeos::ProfileHelper::IsSigninProfile(profile)) + return true; + + if (!profile->IsRegularProfile()) + return false; + + const user_manager::User* user = + chromeos::ProfileHelper::Get()->GetUserByProfile(profile); + return user->IsAffiliated(); +} + +} // namespace + +EnterpriseNetworkingAttributesGetNetworkDetailsFunction:: + EnterpriseNetworkingAttributesGetNetworkDetailsFunction() = default; + +EnterpriseNetworkingAttributesGetNetworkDetailsFunction:: + ~EnterpriseNetworkingAttributesGetNetworkDetailsFunction() = default; + +ExtensionFunction::ResponseAction +EnterpriseNetworkingAttributesGetNetworkDetailsFunction::Run() { + if (!CanGetNetworkAttributesForBrowserContext(browser_context())) { + return RespondNow(Error(kErrorUserNotAffiliated)); + } + + chromeos::NetworkStateHandler* network_state_handler = + chromeos::NetworkHandler::Get()->network_state_handler(); + const chromeos::NetworkState* network = + network_state_handler->DefaultNetwork(); + if (!network) { + // Not connected to a network. + return RespondNow(Error(kErrorNetworkNotConnected)); + } + const chromeos::DeviceState* device = + network_state_handler->GetDeviceState(network->device_path()); + if (!device) { + return RespondNow(Error(kErrorNetworkNotConnected)); + } + + const std::string mac_address = + chromeos::network_util::FormattedMacAddress(device->mac_address()); + const std::string ipv4_address = device->GetIpAddressByType(shill::kTypeIPv4); + const std::string ipv6_address = device->GetIpAddressByType(shill::kTypeIPv6); + + api::enterprise_networking_attributes::NetworkDetails details; + details.mac_address = mac_address; + if (!ipv4_address.empty()) { + details.ipv4 = std::make_unique<std::string>(ipv4_address); + } + if (!ipv6_address.empty()) { + details.ipv6 = std::make_unique<std::string>(ipv6_address); + } + + return RespondNow(OneArgument(details.ToValue())); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.h b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.h new file mode 100644 index 00000000000..85f23e82822 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_api.h @@ -0,0 +1,30 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_NETWORKING_ATTRIBUTES_ENTERPRISE_NETWORKING_ATTRIBUTES_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_NETWORKING_ATTRIBUTES_ENTERPRISE_NETWORKING_ATTRIBUTES_API_H_ + +#include "extensions/browser/extension_function.h" +#include "extensions/browser/extension_function_histogram_value.h" + +namespace extensions { + +class EnterpriseNetworkingAttributesGetNetworkDetailsFunction + : public ExtensionFunction { + public: + EnterpriseNetworkingAttributesGetNetworkDetailsFunction(); + + protected: + ~EnterpriseNetworkingAttributesGetNetworkDetailsFunction() override; + + ResponseAction Run() override; + + private: + DECLARE_EXTENSION_FUNCTION( + "enterprise.networkingAttributes.getNetworkDetails", + ENTERPRISE_NETWORKINGATTRIBUTES_GETNETWORKDETAILS) +}; + +} // namespace extensions +#endif // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_NETWORKING_ATTRIBUTES_ENTERPRISE_NETWORKING_ATTRIBUTES_API_H_ diff --git a/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_apitest.cc b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_apitest.cc new file mode 100644 index 00000000000..a3c7706efdc --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_networking_attributes/enterprise_networking_attributes_apitest.cc @@ -0,0 +1,210 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <memory> +#include <string> + +#include "base/values.h" +#include "chrome/browser/chromeos/policy/affiliation_test_helper.h" +#include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chromeos/dbus/shill/shill_device_client.h" +#include "chromeos/dbus/shill/shill_ipconfig_client.h" +#include "chromeos/dbus/shill/shill_profile_client.h" +#include "chromeos/dbus/shill/shill_service_client.h" +#include "content/public/test/browser_test.h" +#include "url/gurl.h" + +namespace { + +const char kErrorUserNotAffiliated[] = + "Network attributes can only be read by an affiliated user."; +const char kErrorNetworkNotConnected[] = + "Device is not connected to a network."; + +constexpr char kTestExtensionID[] = "pkhdjpcjgonhlomdjmnddhbfpgkdhgle"; +constexpr char kUpdateManifestPath[] = + "/extensions/api_test/enterprise_networking_attributes/update_manifest.xml"; + +constexpr char kMacAddress[] = "0123456789AB"; +constexpr char kFormattedMacAddress[] = "01:23:45:67:89:AB"; +constexpr char kIpv4Address[] = "192.168.0.42"; +constexpr char kIpv6Address[] = "fe80::1262:d0ff:fef5:e8a9"; + +constexpr char kWifiDevicePath[] = "/device/stub_wifi"; +constexpr char kWifiServicePath[] = "/service/stub_wifi"; +constexpr char kWifiIPConfigV4Path[] = "/ipconfig/stub_wifi-ipv4"; +constexpr char kWifiIPConfigV6Path[] = "/ipconfig/stub_wifi-ipv6"; + +base::Value BuildCustomArgForSuccess(const std::string& expected_mac_address, + const std::string& expected_ipv4_address, + const std::string& expected_ipv6_address) { + base::Value network_details(base::Value::Type::DICTIONARY); + network_details.SetKey("macAddress", base::Value(expected_mac_address)); + network_details.SetKey("ipv4", base::Value(expected_ipv4_address)); + network_details.SetKey("ipv6", base::Value(expected_ipv6_address)); + + base::Value custom_arg(base::Value::Type::DICTIONARY); + custom_arg.SetKey("testName", base::Value("success")); + custom_arg.SetKey("expectedResult", std::move(network_details)); + return custom_arg; +} + +base::Value BuildCustomArgForFailure( + const std::string& expected_error_message) { + base::Value custom_arg(base::Value::Type::DICTIONARY); + custom_arg.SetKey("testName", base::Value("failure")); + custom_arg.SetKey("expectedErrorMessage", + base::Value(expected_error_message)); + return custom_arg; +} + +} // namespace + +namespace extensions { + +class EnterpriseNetworkingAttributesTest + : public ForceInstalledAffiliatedExtensionApiTest, + public ::testing::WithParamInterface<bool> { + public: + EnterpriseNetworkingAttributesTest() + : ForceInstalledAffiliatedExtensionApiTest(GetParam()) {} + + void SetupDisconnectedNetwork() { + chromeos::ShillDeviceClient::TestInterface* shill_device_client = + chromeos::DBusThreadManager::Get() + ->GetShillDeviceClient() + ->GetTestInterface(); + chromeos::ShillIPConfigClient::TestInterface* shill_ipconfig_client = + chromeos::DBusThreadManager::Get() + ->GetShillIPConfigClient() + ->GetTestInterface(); + chromeos::ShillServiceClient::TestInterface* shill_service_client = + chromeos::DBusThreadManager::Get() + ->GetShillServiceClient() + ->GetTestInterface(); + chromeos::ShillProfileClient::TestInterface* shill_profile_client = + chromeos::DBusThreadManager::Get() + ->GetShillProfileClient() + ->GetTestInterface(); + + shill_service_client->ClearServices(); + shill_device_client->ClearDevices(); + + shill_device_client->AddDevice(kWifiDevicePath, shill::kTypeWifi, + "stub_wifi_device"); + shill_device_client->SetDeviceProperty( + kWifiDevicePath, shill::kAddressProperty, base::Value(kMacAddress), + /* notify_changed= */ false); + + base::DictionaryValue ipconfig_v4_dictionary; + ipconfig_v4_dictionary.SetKey(shill::kAddressProperty, + base::Value(kIpv4Address)); + ipconfig_v4_dictionary.SetKey(shill::kMethodProperty, + base::Value(shill::kTypeIPv4)); + shill_ipconfig_client->AddIPConfig(kWifiIPConfigV4Path, + ipconfig_v4_dictionary); + + base::DictionaryValue ipconfig_v6_dictionary; + ipconfig_v6_dictionary.SetKey(shill::kAddressProperty, + base::Value(kIpv6Address)); + ipconfig_v6_dictionary.SetKey(shill::kMethodProperty, + base::Value(shill::kTypeIPv6)); + shill_ipconfig_client->AddIPConfig(kWifiIPConfigV6Path, + ipconfig_v6_dictionary); + + base::ListValue ip_configs; + ip_configs.AppendString(kWifiIPConfigV4Path); + ip_configs.AppendString(kWifiIPConfigV6Path); + shill_device_client->SetDeviceProperty( + kWifiDevicePath, shill::kIPConfigsProperty, ip_configs, + /*notify_changed=*/false); + + shill_service_client->AddService(kWifiServicePath, "wifi_guid", + "wifi_network_name", shill::kTypeWifi, + shill::kStateIdle, /* visible= */ true); + shill_service_client->SetServiceProperty( + kWifiServicePath, shill::kConnectableProperty, base::Value(true)); + + shill_profile_client->AddService( + chromeos::ShillProfileClient::GetSharedProfilePath(), kWifiServicePath); + + base::RunLoop().RunUntilIdle(); + } + + void ConnectNetwork() { + chromeos::ShillServiceClient::TestInterface* shill_service_client = + chromeos::DBusThreadManager::Get() + ->GetShillServiceClient() + ->GetTestInterface(); + shill_service_client->SetServiceProperty(kWifiServicePath, + shill::kStateProperty, + base::Value(shill::kStateOnline)); + base::RunLoop().RunUntilIdle(); + } +}; + +IN_PROC_BROWSER_TEST_P(EnterpriseNetworkingAttributesTest, + PRE_GetNetworkDetails) { + policy::AffiliationTestHelper::PreLoginUser(affiliated_account_id_); +} + +IN_PROC_BROWSER_TEST_P(EnterpriseNetworkingAttributesTest, GetNetworkDetails) { + const bool is_affiliated = GetParam(); + EXPECT_EQ(is_affiliated, user_manager::UserManager::Get() + ->FindUser(affiliated_account_id_) + ->IsAffiliated()); + + const Extension* extension = + ForceInstallExtension(kTestExtensionID, kUpdateManifestPath); + SetupDisconnectedNetwork(); + const GURL test_url = extension->GetResourceURL("test.html"); + + // Run test without connected network. + base::Value custom_arg_disconnected = + is_affiliated ? BuildCustomArgForFailure(kErrorNetworkNotConnected) + : BuildCustomArgForFailure(kErrorUserNotAffiliated); + TestExtension(CreateBrowser(profile()), test_url, + std::move(custom_arg_disconnected)); + + // Run test with connected network. + ConnectNetwork(); + base::Value custom_arg_connected = + is_affiliated ? BuildCustomArgForSuccess(kFormattedMacAddress, + kIpv4Address, kIpv6Address) + : BuildCustomArgForFailure(kErrorUserNotAffiliated); + TestExtension(CreateBrowser(profile()), test_url, + std::move(custom_arg_connected)); +} + +// Both cases of affiliated and non-affiliated users are tested. +INSTANTIATE_TEST_SUITE_P(AffiliationCheck, + EnterpriseNetworkingAttributesTest, + ::testing::Bool()); + +// Ensure that extensions that are not pre-installed by policy throw an install +// warning if they request the enterprise.networkingAttributes permission in the +// manifest and that such extensions don't see the +// chrome.enterprise.networkingAttributes namespace. +IN_PROC_BROWSER_TEST_F( + ExtensionApiTest, + EnterpriseNetworkingAttributesIsRestrictedToPolicyExtension) { + ASSERT_TRUE(RunExtensionSubtest("enterprise_networking_attributes", + "api_not_available.html", + kFlagIgnoreManifestWarnings, kFlagNone)); + + base::FilePath extension_path = + test_data_dir_.AppendASCII("enterprise_networking_attributes"); + const extensions::Extension* extension = + extensions::ExtensionRegistry::Get(profile()) + ->enabled_extensions() + .GetByID(kTestExtensionID); + ASSERT_FALSE(extension->install_warnings().empty()); + EXPECT_EQ( + "'enterprise.networkingAttributes' is not allowed for specified install " + "location.", + extension->install_warnings()[0].message); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc index f3a5cfa7d60..26dbef23d59 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc @@ -7,7 +7,6 @@ #include <utility> #include "base/bind.h" -#include "base/task/post_task.h" #include "base/values.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h" @@ -300,7 +299,7 @@ EnterprisePlatformKeysChallengeMachineKeyFunction::Run() { chromeos::attestation::KEY_DEVICE, scoped_refptr<ExtensionFunction>(this), std::move(callback), StringFromVector(params->challenge), params->register_key ? *params->register_key : false); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task)); return RespondLater(); } @@ -334,7 +333,7 @@ EnterprisePlatformKeysChallengeUserKeyFunction::Run() { chromeos::attestation::KEY_USER, scoped_refptr<ExtensionFunction>(this), std::move(callback), StringFromVector(params->challenge), params->register_key); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task)); return RespondLater(); } 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 3f946e23d57..4cdb396d069 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 @@ -25,6 +25,7 @@ #include "content/public/common/content_switches.h" #include "content/public/test/browser_test.h" #include "crypto/nss_util_internal.h" +#include "crypto/scoped_nss_types.h" #include "crypto/scoped_test_system_nss_key_slot.h" #include "extensions/browser/extension_registry.h" #include "net/cert/nss_cert_database.h" @@ -117,17 +118,17 @@ void ImportPrivateKeyPKCS8ToSlot(const unsigned char* pkcs8_der, const_cast<unsigned char*>(pkcs8_der), pkcs8_der_size}; - SECKEYPrivateKey* seckey = NULL; - ASSERT_EQ(SECSuccess, - PK11_ImportDERPrivateKeyInfoAndReturnKey(slot, - &pki_der_user, - NULL, // nickname - NULL, // publicValue - true, // isPerm - true, // isPrivate - KU_ALL, // usage - &seckey, - NULL)); + SECKEYPrivateKey* seckey_raw = nullptr; + ASSERT_EQ(SECSuccess, PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot, &pki_der_user, + /*nickname=*/nullptr, + /*publicValue=*/nullptr, + /*isPerm=*/true, + /*isPrivate=*/true, + /*usage=*/KU_ALL, &seckey_raw, /*wincx=*/nullptr)); + + // Make sure that the memory allocated for the key gets freed. + crypto::ScopedSECKEYPrivateKey seckey(seckey_raw); } // The managed_storage extension has a key defined in its manifest, so that diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc index 275d148f325..6051e3a45ea 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc @@ -6,7 +6,6 @@ #include "base/base64.h" #include "base/bind.h" -#include "base/task/post_task.h" #include "base/values.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" @@ -116,7 +115,7 @@ EnterprisePlatformKeysPrivateChallengeMachineKeyFunction::Run() { chromeos::attestation::KEY_DEVICE, scoped_refptr<ExtensionFunction>(this), std::move(callback), challenge, /*register_key=*/false); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task)); return RespondLater(); } @@ -161,7 +160,7 @@ EnterprisePlatformKeysPrivateChallengeUserKeyFunction::Run() { &EPKPChallengeKey::Run, base::Unretained(&impl_), chromeos::attestation::KEY_USER, scoped_refptr<ExtensionFunction>(this), std::move(callback), challenge, params->register_key); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, std::move(task)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, std::move(task)); return RespondLater(); } 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 36d3139b284..caa30811789 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 @@ -242,87 +242,91 @@ const wchar_t kDefaultRegistryPath[] = L"SOFTWARE\\Google\\Endpoint Verification"; const wchar_t kValueName[] = L"Safe Storage"; -std::string ReadEncryptedSecret() { +LONG ReadEncryptedSecret(std::string* encrypted_secret) { base::win::RegKey key; DWORD kMaxRawSize = 1024; char raw_data[kMaxRawSize]; DWORD raw_data_size = kMaxRawSize; DWORD raw_type; - if (ERROR_SUCCESS != - key.Open(HKEY_CURRENT_USER, kDefaultRegistryPath, KEY_READ) || - ERROR_SUCCESS != - key.ReadValue(kValueName, raw_data, &raw_data_size, &raw_type) || - raw_type != REG_BINARY) { - return std::string(); + encrypted_secret->clear(); + LONG result = key.Open(HKEY_CURRENT_USER, kDefaultRegistryPath, KEY_READ); + if (result != ERROR_SUCCESS) + return result; + result = key.ReadValue(kValueName, raw_data, &raw_data_size, &raw_type); + if (result != ERROR_SUCCESS) + return result; + if (raw_type != REG_BINARY) { + key.DeleteValue(kValueName); + return ERROR_INVALID_DATATYPE; } - std::string encrypted_secret; - encrypted_secret.insert(0, raw_data, raw_data_size); - return encrypted_secret; + encrypted_secret->insert(0, raw_data, raw_data_size); + return ERROR_SUCCESS; } // Encrypts the |plaintext| and write the result in |cyphertext|. This // function was taken from os_crypt/os_crypt_win.cc (Chromium). -bool EncryptString(const std::string& plaintext, std::string* ciphertext) { +LONG EncryptString(const std::string& plaintext, std::string* ciphertext) { DATA_BLOB input; input.pbData = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(plaintext.data())); input.cbData = static_cast<DWORD>(plaintext.length()); + ciphertext->clear(); DATA_BLOB output; BOOL result = ::CryptProtectData(&input, nullptr, nullptr, nullptr, nullptr, 0, &output); if (!result) - return false; + return ::GetLastError(); // this does a copy ciphertext->assign(reinterpret_cast<std::string::value_type*>(output.pbData), output.cbData); LocalFree(output.pbData); - return true; + return ERROR_SUCCESS; } // Decrypts the |cyphertext| and write the result in |plaintext|. This // function was taken from os_crypt/os_crypt_win.cc (Chromium). -bool DecryptString(const std::string& ciphertext, std::string* plaintext) { +LONG DecryptString(const std::string& ciphertext, std::string* plaintext) { DATA_BLOB input; input.pbData = const_cast<BYTE*>(reinterpret_cast<const BYTE*>(ciphertext.data())); input.cbData = static_cast<DWORD>(ciphertext.length()); + plaintext->clear(); DATA_BLOB output; BOOL result = ::CryptUnprotectData(&input, nullptr, nullptr, nullptr, nullptr, 0, &output); if (!result) - return false; + return ::GetLastError(); plaintext->assign(reinterpret_cast<char*>(output.pbData), output.cbData); LocalFree(output.pbData); - return true; + return ERROR_SUCCESS; } -std::string CreateRandomSecret() { +LONG CreateRandomSecret(std::string* secret) { // Generate a password with 128 bits of randomness. const int kBytes = 128 / 8; - std::string secret; - base::Base64Encode(base::RandBytesAsString(kBytes), &secret); + std::string generated_secret; + base::Base64Encode(base::RandBytesAsString(kBytes), &generated_secret); std::string encrypted_secret; - if (!EncryptString(secret, &encrypted_secret)) { - return std::string(); - } + LONG result = EncryptString(generated_secret, &encrypted_secret); + if (result != ERROR_SUCCESS) + return result; base::win::RegKey key; - if (ERROR_SUCCESS != - key.Create(HKEY_CURRENT_USER, kDefaultRegistryPath, KEY_WRITE)) { - return std::string(); - } - if (ERROR_SUCCESS != key.WriteValue(kValueName, encrypted_secret.data(), - encrypted_secret.size(), REG_BINARY)) { - return std::string(); - } - return secret; + result = key.Create(HKEY_CURRENT_USER, kDefaultRegistryPath, KEY_WRITE); + if (result != ERROR_SUCCESS) + return result; + result = key.WriteValue(kValueName, encrypted_secret.data(), + encrypted_secret.size(), REG_BINARY); + if (result == ERROR_SUCCESS) + *secret = generated_secret; + return result; } #elif defined(OS_MACOSX) // defined(OS_WIN) @@ -330,7 +334,8 @@ std::string CreateRandomSecret() { constexpr char kServiceName[] = "Endpoint Verification Safe Storage"; constexpr char kAccountName[] = "Endpoint Verification"; -std::string AddRandomPasswordToKeychain(const crypto::AppleKeychain& keychain) { +OSStatus AddRandomPasswordToKeychain(const crypto::AppleKeychain& keychain, + std::string* secret) { // Generate a password with 128 bits of randomness. const int kBytes = 128 / 8; std::string password; @@ -342,32 +347,29 @@ std::string AddRandomPasswordToKeychain(const crypto::AppleKeychain& keychain) { strlen(kServiceName), kServiceName, strlen(kAccountName), kAccountName, password.size(), password_data, nullptr); - if (error != noErr) - return std::string(); - return password; + if (error == noErr) + *secret = password; + else + secret->clear(); + return error; } -std::string ReadEncryptedSecret() { +OSStatus ReadEncryptedSecret(std::string* secret) { UInt32 password_length = 0; void* password_data = nullptr; crypto::AppleKeychain keychain; + secret->clear(); OSStatus error = keychain.FindGenericPassword( strlen(kServiceName), kServiceName, strlen(kAccountName), kAccountName, &password_length, &password_data, nullptr); if (error == noErr) { - std::string password = - std::string(static_cast<char*>(password_data), password_length); + *secret = std::string(static_cast<char*>(password_data), password_length); keychain.ItemFreeContent(password_data); - return password; + } else if (error == errSecItemNotFound) { + error = AddRandomPasswordToKeychain(keychain, secret); } - - if (error == errSecItemNotFound) { - std::string password = AddRandomPasswordToKeychain(keychain); - return password; - } - - return std::string(); + return error; } #endif // defined(OS_MACOSX) @@ -483,9 +485,9 @@ void StoreDeviceData(const std::string& id, success = base::Move(tmp_path, data_file); } else { // Not passing a second parameter means clear the data sored under |id|. - success = base::DeleteFile(data_file, false); + success = base::DeleteFile(data_file); if (base::IsDirectoryEmpty(data_file.DirName())) - base::DeleteFile(data_file.DirName(), false); + base::DeleteFile(data_file.DirName()); } std::move(callback).Run(success); @@ -493,17 +495,19 @@ void StoreDeviceData(const std::string& id, void RetrieveDeviceData( const std::string& id, - base::OnceCallback<void(const std::string&, bool)> callback) { + base::OnceCallback<void(const std::string&, RetrieveDeviceDataStatus)> + callback) { base::FilePath data_file = GetEndpointVerificationDir(); if (data_file.empty()) { - std::move(callback).Run("", false); + std::move(callback).Run("", + RetrieveDeviceDataStatus::kDataDirectoryUnknown); return; } data_file = data_file.AppendASCII(id); // If the file does not exist don't treat this as an error rather return an // empty string. if (!base::PathExists(data_file)) { - std::move(callback).Run("", true); + std::move(callback).Run("", RetrieveDeviceDataStatus::kDataRecordNotFound); return; } std::string data; @@ -512,32 +516,27 @@ void RetrieveDeviceData( // Chrome. bool result = base::ReadFileToString(data_file, &data); - std::move(callback).Run(data, result); + std::move(callback).Run( + data, result ? RetrieveDeviceDataStatus::kSuccess + : RetrieveDeviceDataStatus::kDataRecordRetrievalError); } void RetrieveDeviceSecret( - base::OnceCallback<void(const std::string&, bool)> callback) { + base::OnceCallback<void(const std::string&, long int)> callback) { std::string secret; #if defined(OS_WIN) - std::string encrypted_secret = ReadEncryptedSecret(); - if (encrypted_secret.empty()) { - secret = CreateRandomSecret(); - std::move(callback).Run(secret, !secret.empty()); - return; - } - if (!DecryptString(encrypted_secret, &secret)) { - std::move(callback).Run("", false); - return; - } + std::string encrypted_secret; + LONG result = ReadEncryptedSecret(&encrypted_secret); + if (result == ERROR_FILE_NOT_FOUND) + result = CreateRandomSecret(&secret); + else if (result == ERROR_SUCCESS) + result = DecryptString(encrypted_secret, &secret); #elif defined(OS_MACOSX) - secret = ReadEncryptedSecret(); - if (secret.empty()) { - std::move(callback).Run(secret, false); - return; - } - + OSStatus result = ReadEncryptedSecret(&secret); +#else + long int result = -1; // Anything but 0 is a failure. #endif - std::move(callback).Run(secret, true); + std::move(callback).Run(secret, static_cast<long int>(result)); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h index fd6d229ea24..764db67ed36 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h @@ -21,6 +21,18 @@ class DictionaryValue; namespace extensions { +// Result of the retrieve operation +enum class RetrieveDeviceDataStatus { + // The operation finished successfully. + kSuccess, + // The path for device data can not be identified. + kDataDirectoryUnknown, + // The requested device data record does not exist. + kDataRecordNotFound, + // The requested device data record can not be read. + kDataRecordRetrievalError, +}; + // Transfer the input from Json file to protobuf. Return nullptr if the input // is not valid. std::unique_ptr<enterprise_management::ChromeDesktopReportRequest> @@ -40,7 +52,8 @@ void StoreDeviceData(const std::string& id, // completion with the data retrieved if the second parameter is true. void RetrieveDeviceData( const std::string& id, - base::OnceCallback<void(const std::string&, bool)> callback); + base::OnceCallback<void(const std::string&, RetrieveDeviceDataStatus)> + callback); // Get the Endpoint Verification secret (symmetric key) for this system. If no // password exists in the Registry then one is generated, stored in the @@ -48,7 +61,7 @@ void RetrieveDeviceData( // If one exists then it is fetched from the Registry and returned. // If an error occurs then the second parameter is false. void RetrieveDeviceSecret( - base::OnceCallback<void(const std::string&, bool)> callback); + base::OnceCallback<void(const std::string&, long int)> callback); } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc index 5ec66328e92..4594ebe731a 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.cc @@ -16,7 +16,6 @@ #include "base/values.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h" #include "chrome/browser/extensions/api/enterprise_reporting_private/device_info_fetcher.h" #include "chrome/browser/net/system_network_context_manager.h" #include "chrome/browser/policy/browser_dm_token_storage.h" @@ -35,6 +34,10 @@ namespace { void LogReportError(const std::string& reason) { VLOG(1) << "Enterprise report is not uploaded: " << reason; } +void LogReportErrorCode(const std::string& reason, long int code) { + VLOG(1) << "Enterprise report is not uploaded: " << reason + << " code: " << code; +} } // namespace @@ -48,8 +51,7 @@ const char kEndpointVerificationRetrievalFailed[] = "Failed to retrieve the endpoint verification data."; const char kEndpointVerificationStoreFailed[] = "Failed to store the endpoint verification data."; -// const char kEndpointVerificationSecretRetrievalFailed[] = "Failed to retrieve -// the endpoint verification secret."; +const char kEndpointVerificationSecretRetrievalFailed[] = "%ld"; } // namespace enterprise_reporting @@ -193,15 +195,17 @@ EnterpriseReportingPrivateGetPersistentSecretFunction::Run() { void EnterpriseReportingPrivateGetPersistentSecretFunction::OnDataRetrieved( const std::string& data, - bool status) { - if (status) { + long int status) { + if (status == 0) { // Success. VLOG(1) << "The Endpoint Verification secret was retrieved."; Respond(OneArgument(std::make_unique<base::Value>(base::Value::BlobStorage( reinterpret_cast<const uint8_t*>(data.data()), reinterpret_cast<const uint8_t*>(data.data() + data.size()))))); } else { - LogReportError("Endpoint Verification secret retrieval error."); - Respond(Error(enterprise_reporting::kEndpointVerificationRetrievalFailed)); + LogReportErrorCode("Endpoint Verification secret retrieval error.", status); + Respond(Error(base::StringPrintf( + enterprise_reporting::kEndpointVerificationSecretRetrievalFailed, + static_cast<long int>(status)))); } } @@ -231,15 +235,24 @@ EnterpriseReportingPrivateGetDeviceDataFunction::Run() { void EnterpriseReportingPrivateGetDeviceDataFunction::OnDataRetrieved( const std::string& data, - bool status) { - if (status) { - VLOG(1) << "The Endpoint Verification data was retrieved."; - Respond(OneArgument(std::make_unique<base::Value>(base::Value::BlobStorage( - reinterpret_cast<const uint8_t*>(data.data()), - reinterpret_cast<const uint8_t*>(data.data() + data.size()))))); - } else { - LogReportError("Endpoint Verification data retrieval error."); - Respond(Error(enterprise_reporting::kEndpointVerificationRetrievalFailed)); + RetrieveDeviceDataStatus status) { + switch (status) { + case RetrieveDeviceDataStatus::kSuccess: + VLOG(1) << "The Endpoint Verification data was retrieved."; + Respond( + OneArgument(std::make_unique<base::Value>(base::Value::BlobStorage( + reinterpret_cast<const uint8_t*>(data.data()), + reinterpret_cast<const uint8_t*>(data.data() + data.size()))))); + return; + case RetrieveDeviceDataStatus::kDataRecordNotFound: + VLOG(1) << "The Endpoint Verification data is not present."; + Respond(NoArguments()); + return; + default: + LogReportErrorCode("Endpoint Verification data retrieval error.", + static_cast<long int>(status)); + Respond( + Error(enterprise_reporting::kEndpointVerificationRetrievalFailed)); } } diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h index c33bfe43276..1778b6c6868 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_api.h @@ -9,6 +9,7 @@ #include <string> #include "base/memory/ref_counted.h" +#include "chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper.h" #include "chrome/common/extensions/api/enterprise_reporting_private.h" #include "components/policy/core/common/cloud/dm_token.h" #include "extensions/browser/extension_function.h" @@ -104,7 +105,7 @@ class EnterpriseReportingPrivateGetPersistentSecretFunction ExtensionFunction::ResponseAction Run() override; // Callback once the data was retrieved from the file. - void OnDataRetrieved(const std::string& data, bool status); + void OnDataRetrieved(const std::string& data, long int status); }; class EnterpriseReportingPrivateGetDeviceDataFunction @@ -126,7 +127,8 @@ class EnterpriseReportingPrivateGetDeviceDataFunction ExtensionFunction::ResponseAction Run() override; // Callback once the data was retrieved from the file. - void OnDataRetrieved(const std::string& data, bool status); + void OnDataRetrieved(const std::string& data, + RetrieveDeviceDataStatus status); }; class EnterpriseReportingPrivateSetDeviceDataFunction diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc index 325ef19e167..76dd8a810e4 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_private_unittest.cc @@ -81,6 +81,7 @@ class EnterpriseReportingPrivateUploadChromeDesktopReportTest private: network::TestURLLoaderFactory test_url_loader_factory_; + policy::FakeBrowserDMTokenStorage storage_; DISALLOW_COPY_AND_ASSIGN( EnterpriseReportingPrivateUploadChromeDesktopReportTest); @@ -204,7 +205,7 @@ TEST_F(EnterpriseReportingPrivateDeviceDataFunctionsTest, DeviceDataMissing) { browser(), extensions::api_test_utils::NONE); ASSERT_TRUE(function->GetResultList()); - EXPECT_EQ(1u, function->GetResultList()->GetSize()); + EXPECT_EQ(0u, function->GetResultList()->GetSize()); EXPECT_TRUE(function->GetError().empty()); } @@ -282,11 +283,8 @@ TEST_F(EnterpriseReportingPrivateDeviceDataFunctionsTest, RetrieveDeviceData) { std::move(values2), browser(), extensions::api_test_utils::NONE); ASSERT_TRUE(get_function2->GetResultList()); - EXPECT_TRUE(get_function2->GetResultList()->Get(0, &single_result)); + EXPECT_EQ(0u, get_function2->GetResultList()->GetSize()); EXPECT_TRUE(get_function2->GetError().empty()); - ASSERT_TRUE(single_result); - ASSERT_TRUE(single_result->is_blob()); - EXPECT_EQ(base::Value::BlobStorage(), single_result->GetBlob()); } // TODO(pastarmovj): Remove once implementation for the other platform exists. diff --git a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc index 5b236b80e49..69618e22c88 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/browser_action_apitest.cc @@ -806,13 +806,9 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, CloseBackgroundPage) { const ExtensionHostDestructionObserver& other) = delete; ~ExtensionHostDestructionObserver() override = default; - void OnExtensionHostDestroyed(const ExtensionHost* host) override { - // TODO(devlin): It would be nice to - // ASSERT_TRUE(host_observer_.IsObserving(host)); - // host_observer_.Remove(host); - // But we can't, because |host| is const. Work around it by just - // RemoveAll()ing. - host_observer_.RemoveAll(); + void OnExtensionHostDestroyed(ExtensionHost* host) override { + ASSERT_TRUE(host_observer_.IsObserving(host)); + host_observer_.Remove(host); run_loop_.QuitWhenIdle(); } diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc index b2fa34c8b44..573ad4f32f0 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/lazy_instance.h" #include "base/location.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" @@ -240,11 +241,10 @@ void ExtensionActionAPI::Shutdown() { // ExtensionActionFunction::ExtensionActionFunction() - : details_(NULL), + : details_(nullptr), tab_id_(ExtensionAction::kDefaultTabId), - contents_(NULL), - extension_action_(NULL) { -} + contents_(nullptr), + extension_action_(nullptr) {} ExtensionActionFunction::~ExtensionActionFunction() { } @@ -356,7 +356,26 @@ void ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting( ExtensionFunction::ResponseAction ExtensionActionSetIconFunction::RunExtensionAction() { - EXTENSION_FUNCTION_VALIDATE(details_); + // TODO(devlin): Temporary logging to track down https://crbug.com/1087948. + // Remove this (and the redundant `if (!x) { VALIDATE(x); }`) checks after + // the bug is fixed. + // Don't reorder or remove values. + enum class FailureType { + kFailedToParseDetails = 0, + kFailedToDecodeCanvas = 1, + kFailedToUnpickleCanvas = 2, + kNoImageDataOrIconIndex = 3, + kMaxValue = kNoImageDataOrIconIndex, + }; + + auto log_set_icon_failure = [](FailureType type) { + base::UmaHistogramEnumeration("Extensions.ActionSetIconFailureType", type); + }; + + if (!details_) { + log_set_icon_failure(FailureType::kFailedToParseDetails); + EXTENSION_FUNCTION_VALIDATE(details_); + } // setIcon can take a variant argument: either a dictionary of canvas // ImageData, or an icon index. @@ -365,8 +384,23 @@ ExtensionActionSetIconFunction::RunExtensionAction() { if (details_->GetDictionary("imageData", &canvas_set)) { gfx::ImageSkia icon; - EXTENSION_FUNCTION_VALIDATE( - ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon)); + ExtensionAction::IconParseResult parse_result = + ExtensionAction::ParseIconFromCanvasDictionary(*canvas_set, &icon); + + if (parse_result != ExtensionAction::IconParseResult::kSuccess) { + switch (parse_result) { + case ExtensionAction::IconParseResult::kDecodeFailure: + log_set_icon_failure(FailureType::kFailedToDecodeCanvas); + break; + case ExtensionAction::IconParseResult::kUnpickleFailure: + log_set_icon_failure(FailureType::kFailedToUnpickleCanvas); + break; + case ExtensionAction::IconParseResult::kSuccess: + NOTREACHED(); + break; + } + EXTENSION_FUNCTION_VALIDATE(false); + } if (icon.isNull()) return RespondNow(Error("Icon invalid.")); @@ -391,6 +425,7 @@ ExtensionActionSetIconFunction::RunExtensionAction() { // Obsolete argument: ignore it. return RespondNow(NoArguments()); } else { + log_set_icon_failure(FailureType::kNoImageDataOrIconIndex); EXTENSION_FUNCTION_VALIDATE(false); } NotifyChange(); 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 1bddc30de58..1549e17b027 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 @@ -549,24 +549,16 @@ IN_PROC_BROWSER_TEST_P(MultiActionAPICanvasTest, DynamicSetIcon) { "background": { "scripts": ["background.js"] } })"; - std::string blue_icon; - std::string red_icon; - { - base::ScopedAllowBlockingForTesting allow_blocking; - ASSERT_TRUE(base::ReadFileToString( - test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), &blue_icon)); - ASSERT_TRUE(base::ReadFileToString( - test_data_dir_.AppendASCII("icon_rgb_255_0_0.png"), &red_icon)); - } - TestExtensionDir test_dir; test_dir.WriteManifest(base::StringPrintf( kManifestTemplate, GetManifestKeyForActionType(GetParam()))); test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), base::StringPrintf(kSetIconBackgroundJsTemplate, GetAPINameForActionType(GetParam()))); - test_dir.WriteFile(FILE_PATH_LITERAL("blue_icon.png"), blue_icon); - test_dir.WriteFile(FILE_PATH_LITERAL("red_icon.png"), red_icon); + test_dir.CopyFileTo(test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), + FILE_PATH_LITERAL("blue_icon.png")); + test_dir.CopyFileTo(test_data_dir_.AppendASCII("icon_rgb_255_0_0.png"), + FILE_PATH_LITERAL("red_icon.png")); const Extension* extension = LoadExtension(test_dir.UnpackedPath()); ASSERT_TRUE(extension); @@ -672,20 +664,14 @@ IN_PROC_BROWSER_TEST_P(MultiActionAPITest, SetIconWithJavascriptHooks) { "background": { "scripts": ["background.js"] } })"; - std::string blue_icon; - { - base::ScopedAllowBlockingForTesting allow_blocking; - ASSERT_TRUE(base::ReadFileToString( - test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), &blue_icon)); - } - TestExtensionDir test_dir; test_dir.WriteManifest(base::StringPrintf( kManifestTemplate, GetManifestKeyForActionType(GetParam()))); test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), base::StringPrintf(kSetIconBackgroundJsTemplate, GetAPINameForActionType(GetParam()))); - test_dir.WriteFile(FILE_PATH_LITERAL("blue_icon.png"), blue_icon); + test_dir.CopyFileTo(test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), + FILE_PATH_LITERAL("blue_icon.png")); const Extension* extension = LoadExtension(test_dir.UnpackedPath()); ASSERT_TRUE(extension); @@ -738,13 +724,6 @@ IN_PROC_BROWSER_TEST_P(MultiActionAPITest, SetIconWithSelfDefined) { "background": { "scripts": ["background.js"] } })"; - std::string blue_icon; - { - base::ScopedAllowBlockingForTesting allow_blocking; - ASSERT_TRUE(base::ReadFileToString( - test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), &blue_icon)); - } - TestExtensionDir test_dir; test_dir.WriteManifest(base::StringPrintf( kManifestTemplate, GetManifestKeyForActionType(GetParam()))); @@ -752,7 +731,8 @@ IN_PROC_BROWSER_TEST_P(MultiActionAPITest, SetIconWithSelfDefined) { test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), base::StringPrintf(kSetIconBackgroundJsTemplate, GetAPINameForActionType(GetParam()))); - test_dir.WriteFile(FILE_PATH_LITERAL("blue_icon.png"), blue_icon); + test_dir.CopyFileTo(test_data_dir_.AppendASCII("icon_rgb_0_0_255.png"), + FILE_PATH_LITERAL("blue_icon.png")); const Extension* extension = LoadExtension(test_dir.UnpackedPath()); ASSERT_TRUE(extension); diff --git a/chromium/chrome/browser/extensions/api/extension_action/page_action_apitest.cc b/chromium/chrome/browser/extensions/api/extension_action/page_action_apitest.cc index 7c2a722b840..25dbaadb950 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/page_action_apitest.cc +++ b/chromium/chrome/browser/extensions/api/extension_action/page_action_apitest.cc @@ -171,22 +171,6 @@ IN_PROC_BROWSER_TEST_F(PageActionApiTest, RemovePopup) { << "Page action popup should have been removed."; } -// Tests popups in page actions. -// Flaky on the trybots. See http://crbug.com/96725. -IN_PROC_BROWSER_TEST_F(PageActionApiTest, DISABLED_ShowPageActionPopup) { - ASSERT_TRUE(RunExtensionTest("page_action/popup")) << message_; - const Extension* extension = GetSingleLoadedExtension(); - ASSERT_TRUE(extension) << message_; - - ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); - - { - ResultCatcher catcher; - ExtensionActionAPI::Get(browser()->profile()) - ->ShowExtensionActionPopupForAPICall(extension, browser()); - ASSERT_TRUE(catcher.GetNextResult()); - } -} // Test http://crbug.com/57333: that two page action extensions using the same // icon for the page action icon and the extension icon do not crash. diff --git a/chromium/chrome/browser/extensions/api/extension_action/page_action_interactive_test.cc b/chromium/chrome/browser/extensions/api/extension_action/page_action_interactive_test.cc new file mode 100644 index 00000000000..09c8a9b7e61 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/extension_action/page_action_interactive_test.cc @@ -0,0 +1,36 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/browser_window.h" +#include "chrome/test/base/interactive_test_utils.h" +#include "content/public/test/browser_test.h" +#include "extensions/test/result_catcher.h" + +namespace extensions { +namespace { + +using PageActionInteractiveTest = ExtensionApiTest; + +// Tests popups in page actions. +IN_PROC_BROWSER_TEST_F(PageActionInteractiveTest, ShowPageActionPopup) { + ASSERT_TRUE(RunExtensionTest("page_action/popup")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); + + ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser())); + ASSERT_TRUE(browser()->window()->IsActive()); + + ResultCatcher catcher; + ASSERT_TRUE(ExtensionActionAPI::Get(browser()->profile()) + ->ShowExtensionActionPopupForAPICall(extension, browser())); + ASSERT_TRUE(catcher.GetNextResult()); +} + +} // namespace +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/feedback_private/OWNERS b/chromium/chrome/browser/extensions/api/feedback_private/OWNERS index bd30e9ff740..5c0b2c9fb1e 100644 --- a/chromium/chrome/browser/extensions/api/feedback_private/OWNERS +++ b/chromium/chrome/browser/extensions/api/feedback_private/OWNERS @@ -1,4 +1,5 @@ afakhry@chromium.org jkardatzke@chromium.org +iby@chromium.org # COMPONENT: Platform>Apps>Feedback diff --git a/chromium/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc b/chromium/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc index cb3b85fb7b6..312a3838cda 100644 --- a/chromium/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc +++ b/chromium/chrome/browser/extensions/api/feedback_private/chrome_feedback_private_delegate.cc @@ -70,9 +70,10 @@ ChromeFeedbackPrivateDelegate::GetStrings( std::make_unique<base::DictionaryValue>(); #define SET_STRING(id, idr) dict->SetString(id, l10n_util::GetStringUTF16(idr)) - SET_STRING("page-title", from_crash - ? IDS_FEEDBACK_REPORT_PAGE_TITLE_SAD_TAB_FLOW - : IDS_FEEDBACK_REPORT_PAGE_TITLE); + SET_STRING("pageTitle", from_crash + ? IDS_FEEDBACK_REPORT_PAGE_TITLE_SAD_TAB_FLOW + : IDS_FEEDBACK_REPORT_PAGE_TITLE); + SET_STRING("appTitle", IDS_FEEDBACK_REPORT_APP_TITLE); SET_STRING("additionalInfo", IDS_FEEDBACK_ADDITIONAL_INFO_LABEL); SET_STRING("minimizeBtnLabel", IDS_FEEDBACK_MINIMIZE_BUTTON_LABEL); SET_STRING("closeBtnLabel", IDS_FEEDBACK_CLOSE_BUTTON_LABEL); @@ -117,7 +118,7 @@ ChromeFeedbackPrivateDelegate::GetStrings( system_logs::SystemLogsFetcher* ChromeFeedbackPrivateDelegate::CreateSystemLogsFetcher( content::BrowserContext* context) const { - return system_logs::BuildChromeSystemLogsFetcher(); + return system_logs::BuildChromeSystemLogsFetcher(/*scrub_data=*/true); } #if defined(OS_CHROMEOS) 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 408f09d5342..f02f4eb82cd 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 @@ -165,7 +165,7 @@ class FileSystemApiTestForDrive : public PlatformAppBrowserTest { SetUpTestFileHierarchy(); integration_service_ = new drive::DriveIntegrationService( - profile, nullptr, "", test_cache_root_.GetPath(), + profile, "", test_cache_root_.GetPath(), fake_drivefs_helper_->CreateFakeDriveFsListenerFactory()); return integration_service_; } @@ -312,7 +312,7 @@ class FileSystemApiTestForRequestFileSystem : public PlatformAppBrowserTest { profile, drivefs_root_.GetPath().Append("drive-user")); return new drive::DriveIntegrationService( - profile, nullptr, "", {}, + profile, "", {}, fake_drivefs_helper_->CreateFakeDriveFsListenerFactory()); } diff --git a/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc b/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc new file mode 100644 index 00000000000..e1cfefca8a1 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.cc @@ -0,0 +1,143 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h" + +#include "base/json/json_writer.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/chromeos/policy/affiliation_test_helper.h" +#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" +#include "chrome/browser/extensions/policy_test_utils.h" +#include "chrome/test/base/ui_test_utils.h" +#include "chromeos/dbus/session_manager/fake_session_manager_client.h" +#include "components/prefs/pref_service.h" +#include "extensions/browser/extension_registry.h" +#include "extensions/test/result_catcher.h" + +namespace { + +// If running with |is_affiliated|==true, the test will use the same +// |kAffiliationID| as user and device affiliation ID, which makes the user +// affiliated (affiliation IDs overlap). +// If running with |is_affiliated|==false, the test will use |kAffiliationID| as +// device and |kAnotherAffiliationID| as user affiliation ID, which makes the +// user non-affiliated (affiliation IDs don't overlap). +constexpr char kAffiliationID[] = "some-affiliation-id"; +constexpr char kAnotherAffiliationID[] = "another-affiliation-id"; + +constexpr char kAffiliatedUserEmail[] = "user@example.com"; +constexpr char kAffiliatedUserGaiaId[] = "1029384756"; + +} // namespace + +namespace extensions { + +ForceInstalledAffiliatedExtensionApiTest:: + ForceInstalledAffiliatedExtensionApiTest(bool is_affiliated) + : is_affiliated_(is_affiliated), + affiliated_account_id_( + AccountId::FromUserEmailGaiaId(kAffiliatedUserEmail, + kAffiliatedUserGaiaId)), + test_install_attributes_( + chromeos::StubInstallAttributes::CreateCloudManaged("fake-domain", + "fake-id")) { + set_exit_when_last_browser_closes(false); + set_chromeos_user_ = false; +} + +ForceInstalledAffiliatedExtensionApiTest:: + ~ForceInstalledAffiliatedExtensionApiTest() = default; + +void ForceInstalledAffiliatedExtensionApiTest::SetUpCommandLine( + base::CommandLine* command_line) { + ExtensionApiTest::SetUpCommandLine(command_line); + policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager( + command_line); +} + +void ForceInstalledAffiliatedExtensionApiTest:: + SetUpInProcessBrowserTestFixture() { + ExtensionApiTest::SetUpInProcessBrowserTestFixture(); + + // Initialize clients here so they are available during setup. They will be + // shutdown in ChromeBrowserMain. + chromeos::SessionManagerClient::InitializeFakeInMemory(); + policy::AffiliationTestHelper affiliation_helper = + policy::AffiliationTestHelper::CreateForCloud( + chromeos::FakeSessionManagerClient::Get()); + + std::set<std::string> device_affiliation_ids; + device_affiliation_ids.insert(kAffiliationID); + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs( + &test_helper_, device_affiliation_ids)); + test_helper_.InstallOwnerKey(); + + std::set<std::string> user_affiliation_ids; + if (is_affiliated_) { + user_affiliation_ids.insert(kAffiliationID); + } else { + user_affiliation_ids.insert(kAnotherAffiliationID); + } + policy::UserPolicyBuilder user_policy; + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs( + &user_policy, affiliated_account_id_, user_affiliation_ids)); + test_helper_.InstallOwnerKey(); + + // Init the user policy provider. + EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_)) + .WillRepeatedly(testing::Return(true)); + policy_provider_.SetAutoRefresh(); + policy::BrowserPolicyConnector::SetPolicyProviderForTesting( + &policy_provider_); + + // Set retry delay to prevent timeouts. + policy::DeviceManagementService::SetRetryDelayForTesting(0); +} + +void ForceInstalledAffiliatedExtensionApiTest::SetUpOnMainThread() { + // Log in user that was created with + // policy::AffiliationTestHelper::PreLoginUser() in the PRE_ test. + const base::ListValue* users = + g_browser_process->local_state()->GetList("LoggedInUsers"); + if (!users->empty()) { + policy::AffiliationTestHelper::LoginUser(affiliated_account_id_); + } + + policy_test_utils::SetUpEmbeddedTestServer(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + + ExtensionApiTest::SetUpOnMainThread(); +} + +const extensions::Extension* +ForceInstalledAffiliatedExtensionApiTest::ForceInstallExtension( + const extensions::ExtensionId& extension_id, + const std::string& update_manifest_path) { + policy_test_utils::SetExtensionInstallForcelistPolicy( + extension_id, embedded_test_server()->GetURL(update_manifest_path), + profile(), &policy_provider_); + const extensions::Extension* extension = + ExtensionRegistry::Get(profile())->enabled_extensions().GetByID( + extension_id); + DCHECK(extension); + return extension; +} + +void ForceInstalledAffiliatedExtensionApiTest::TestExtension( + Browser* browser, + const GURL& page_url, + const base::Value& custom_arg_value) { + DCHECK(page_url.is_valid()) << "page_url must be valid"; + + std::string custom_arg; + base::JSONWriter::Write(custom_arg_value, &custom_arg); + SetCustomArg(custom_arg); + + extensions::ResultCatcher catcher; + ui_test_utils::NavigateToURL(browser, GURL(page_url)); + + ASSERT_TRUE(catcher.GetNextResult()) << catcher.message(); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h b/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h new file mode 100644 index 00000000000..7707876e67b --- /dev/null +++ b/chromium/chrome/browser/extensions/api/force_installed_affiliated_extension_apitest.h @@ -0,0 +1,67 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_FORCE_INSTALLED_AFFILIATED_EXTENSION_APITEST_H_ +#define CHROME_BROWSER_EXTENSIONS_API_FORCE_INSTALLED_AFFILIATED_EXTENSION_APITEST_H_ + +#include <string> + +#include "base/values.h" +#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chromeos/tpm/stub_install_attributes.h" +#include "components/account_id/account_id.h" +#include "components/policy/core/common/mock_configuration_policy_provider.h" +#include "extensions/common/extension_id.h" +#include "url/gurl.h" + +class Browser; + +namespace base { +class CommandLine; +} // namespace base + +namespace extensions { + +class Extension; + +// TODO(https://crbug.com/1082195) Create force-installed extension and user +// affiliation test mixins to replace this class. + +// Helper class to test force-installed extensions in a +// affiliated/non-affiliated user profile. +class ForceInstalledAffiliatedExtensionApiTest : public ExtensionApiTest { + public: + explicit ForceInstalledAffiliatedExtensionApiTest(bool is_affiliated); + ~ForceInstalledAffiliatedExtensionApiTest() override; + + protected: + // ExtensionApiTest + void SetUpCommandLine(base::CommandLine* command_line) override; + void SetUpInProcessBrowserTestFixture() override; + void SetUpOnMainThread() override; + + const extensions::Extension* ForceInstallExtension( + const extensions::ExtensionId& extension_id, + const std::string& update_manifest_path); + + // Sets |custom_arg_value|, loads |page_url| and waits for an extension API + // test pass/fail notification. + void TestExtension(Browser* browser, + const GURL& page_url, + const base::Value& custom_arg_value); + + // Whether the user should be affiliated (= user and device affiliation IDs + // overlap). + const bool is_affiliated_; + + const AccountId affiliated_account_id_; + policy::MockConfigurationPolicyProvider policy_provider_; + chromeos::ScopedStubInstallAttributes test_install_attributes_; + policy::DevicePolicyCrosTestHelper test_helper_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_FORCE_INSTALLED_AFFILIATED_EXTENSION_APITEST_H_ diff --git a/chromium/chrome/browser/extensions/api/gcm/gcm_apitest.cc b/chromium/chrome/browser/extensions/api/gcm/gcm_apitest.cc index d9b189cf106..623b7a0697c 100644 --- a/chromium/chrome/browser/extensions/api/gcm/gcm_apitest.cc +++ b/chromium/chrome/browser/extensions/api/gcm/gcm_apitest.cc @@ -60,7 +60,7 @@ namespace extensions { class GcmApiTest : public ExtensionApiTest { public: - GcmApiTest() : fake_gcm_profile_service_(NULL) {} + GcmApiTest() : fake_gcm_profile_service_(nullptr) {} protected: void SetUpCommandLine(base::CommandLine* command_line) override; 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 e650e3f1ebb..bd091a6f0e2 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 @@ -5,6 +5,7 @@ #include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h" #include "base/bind.h" +#include "base/metrics/histogram_functions.h" #include "base/strings/stringprintf.h" #include "chrome/browser/extensions/api/identity/identity_api.h" #include "chrome/browser/profiles/profile.h" @@ -24,6 +25,15 @@ namespace extensions { +namespace { + +void RecordResultHistogram(GaiaRemoteConsentFlow::Failure failure) { + base::UmaHistogramEnumeration("Signin.Extensions.GaiaRemoteConsentFlowResult", + failure); +} + +} // namespace + GaiaRemoteConsentFlow::Delegate::~Delegate() = default; GaiaRemoteConsentFlow::GaiaRemoteConsentFlow( @@ -61,7 +71,7 @@ void GaiaRemoteConsentFlow::OnSetAccountsComplete( } if (result != signin::SetAccountsInCookieResult::kSuccess) { - delegate_->OnGaiaRemoteConsentFlowFailed( + GaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED); return; } @@ -100,16 +110,16 @@ void GaiaRemoteConsentFlow::OnConsentResultSet( std::string gaia_id; if (!gaia::ParseOAuth2MintTokenConsentResult(consent_result, &consent_approved, &gaia_id)) { - delegate_->OnGaiaRemoteConsentFlowFailed( - GaiaRemoteConsentFlow::INVALID_CONSENT_RESULT); + GaiaRemoteConsentFlowFailed(GaiaRemoteConsentFlow::INVALID_CONSENT_RESULT); return; } if (!consent_approved) { - delegate_->OnGaiaRemoteConsentFlowFailed(GaiaRemoteConsentFlow::NO_GRANT); + GaiaRemoteConsentFlowFailed(GaiaRemoteConsentFlow::NO_GRANT); return; } + RecordResultHistogram(GaiaRemoteConsentFlow::NONE); delegate_->OnGaiaRemoteConsentFlowApproved(consent_result, gaia_id); } @@ -129,7 +139,7 @@ void GaiaRemoteConsentFlow::OnAuthFlowFailure(WebAuthFlow::Failure failure) { break; } - delegate_->OnGaiaRemoteConsentFlowFailed(gaia_failure); + GaiaRemoteConsentFlowFailed(gaia_failure); } std::unique_ptr<GaiaAuthFetcher> @@ -146,7 +156,15 @@ GaiaRemoteConsentFlow::GetCookieManagerForPartition() { } void GaiaRemoteConsentFlow::OnEndBatchOfRefreshTokenStateChanges() { +// 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 defined(OS_CHROMEOS) SetAccountsInCookie(); +#endif } void GaiaRemoteConsentFlow::SetWebAuthFlowForTesting( @@ -196,4 +214,9 @@ void GaiaRemoteConsentFlow::SetAccountsInCookie() { base::Unretained(this))); } +void GaiaRemoteConsentFlow::GaiaRemoteConsentFlowFailed(Failure failure) { + RecordResultHistogram(failure); + delegate_->OnGaiaRemoteConsentFlowFailed(failure); +} + } // namespace extensions 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 433777bc7c0..0fd5cc8de97 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 @@ -25,12 +25,16 @@ class GaiaRemoteConsentFlow public signin::AccountsCookieMutator::PartitionDelegate, public signin::IdentityManager::Observer { public: + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. enum Failure { - WINDOW_CLOSED, - LOAD_FAILED, - SET_ACCOUNTS_IN_COOKIE_FAILED, - INVALID_CONSENT_RESULT, - NO_GRANT + NONE = 0, + WINDOW_CLOSED = 1, + LOAD_FAILED = 2, + SET_ACCOUNTS_IN_COOKIE_FAILED = 3, + INVALID_CONSENT_RESULT = 4, + NO_GRANT = 5, + kMaxValue = NO_GRANT }; class Delegate { @@ -80,6 +84,8 @@ class GaiaRemoteConsentFlow private: void SetAccountsInCookie(); + void GaiaRemoteConsentFlowFailed(Failure failure); + Delegate* delegate_; Profile* profile_; CoreAccountId account_id_; 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 fa1a7c19ed7..bf65e8160b6 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,6 +7,7 @@ #include <vector> #include "base/run_loop.h" +#include "base/test/metrics/histogram_tester.h" #include "chrome/browser/signin/identity_test_environment_profile_adaptor.h" #include "chrome/test/base/testing_profile.h" #include "components/signin/public/identity_manager/identity_test_environment.h" @@ -17,6 +18,9 @@ namespace extensions { +const char kResultHistogramName[] = + "Signin.Extensions.GaiaRemoteConsentFlowResult"; + const char kWindowKey[] = "window_key"; const char kGaiaId[] = "fake_gaia_id"; const char kConsentResult[] = "CAESCUVOQ1JZUFRFRBoMZmFrZV9nYWlhX2lk"; @@ -98,8 +102,11 @@ class IdentityGaiaRemoteConsentFlowTest : public testing::Test { window_key)); } + base::HistogramTester* histogram_tester() { return &histogram_tester_; } + protected: base::test::TaskEnvironment task_env_; + base::HistogramTester histogram_tester_; testing::StrictMock<MockGaiaRemoteConsentFlowDelegate> delegate_; }; @@ -108,6 +115,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult) { EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowApproved(kConsentResult, kGaiaId)); flow->OnConsentResultSet(kConsentResult, kWindowKey); + histogram_tester()->ExpectUniqueSample(kResultHistogramName, + GaiaRemoteConsentFlow::NONE, 1); } TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_WrongWindowIgnored) { @@ -130,6 +139,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, ConsentResult_TwoWindows) { EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowApproved(kConsentResult, kGaiaId)); flow->OnConsentResultSet(kConsentResult, kWindowKey); + histogram_tester()->ExpectUniqueSample(kResultHistogramName, + GaiaRemoteConsentFlow::NONE, 2); } TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) { @@ -139,6 +150,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, InvalidConsentResult) { OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::INVALID_CONSENT_RESULT)); flow->OnConsentResultSet(kInvalidConsentResult, kWindowKey); + histogram_tester()->ExpectUniqueSample( + kResultHistogramName, GaiaRemoteConsentFlow::INVALID_CONSENT_RESULT, 1); } TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) { @@ -147,6 +160,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, NoGrant) { EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::NO_GRANT)); flow->OnConsentResultSet(kNoGrantConsentResult, kWindowKey); + histogram_tester()->ExpectUniqueSample(kResultHistogramName, + GaiaRemoteConsentFlow::NO_GRANT, 1); } TEST_F(IdentityGaiaRemoteConsentFlowTest, SetAccountsFailure) { @@ -157,6 +172,9 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, SetAccountsFailure) { GaiaRemoteConsentFlow::Failure::SET_ACCOUNTS_IN_COOKIE_FAILED)); flow->OnSetAccountsComplete( signin::SetAccountsInCookieResult::kPersistentError); + histogram_tester()->ExpectUniqueSample( + kResultHistogramName, + GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED, 1); } TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) { @@ -164,6 +182,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_WindowClosed) { EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::WINDOW_CLOSED)); flow->OnAuthFlowFailure(WebAuthFlow::Failure::WINDOW_CLOSED); + histogram_tester()->ExpectUniqueSample( + kResultHistogramName, GaiaRemoteConsentFlow::WINDOW_CLOSED, 1); } TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) { @@ -171,6 +191,8 @@ TEST_F(IdentityGaiaRemoteConsentFlowTest, WebAuthFlowFailure_LoadFailed) { EXPECT_CALL(delegate_, OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure::LOAD_FAILED)); flow->OnAuthFlowFailure(WebAuthFlow::Failure::LOAD_FAILED); + histogram_tester()->ExpectUniqueSample(kResultHistogramName, + GaiaRemoteConsentFlow::LOAD_FAILED, 1); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc index 68327bb525c..71f36451b23 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc @@ -14,6 +14,7 @@ #include "base/run_loop.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" +#include "base/test/metrics/histogram_tester.h" #include "base/values.h" #include "build/build_config.h" #include "build/buildflag.h" @@ -23,6 +24,7 @@ #include "chrome/browser/extensions/api/identity/identity_api.h" #include "chrome/browser/extensions/api/identity/identity_constants.h" #include "chrome/browser/extensions/api/identity/identity_get_accounts_function.h" +#include "chrome/browser/extensions/api/identity/identity_get_auth_token_error.h" #include "chrome/browser/extensions/api/identity/identity_get_auth_token_function.h" #include "chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h" #include "chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h" @@ -91,8 +93,13 @@ namespace { namespace errors = identity_constants; namespace utils = extension_function_test_utils; -static const char kAccessToken[] = "auth_token"; -static const char kExtensionId[] = "ext_id"; +const char kAccessToken[] = "auth_token"; +const char kExtensionId[] = "ext_id"; + +const char kGetAuthTokenResultHistogramName[] = + "Signin.Extensions.GetAuthTokenResult"; +const char kGetAuthTokenResultAfterConsentApprovedHistogramName[] = + "Signin.Extensions.GetAuthTokenResult.RemoteConsentApproved"; #if defined(OS_CHROMEOS) void InitNetwork() { @@ -681,9 +688,9 @@ IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoPrimaryAccount) { } IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, - PrimaryAccountHasNoRefreshToken) { + PrimaryAccountHasInvalidRefreshToken) { CoreAccountId primary_account_id = SignIn("primary@example.com"); - identity_test_env()->RemoveRefreshTokenForAccount(primary_account_id); + identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount(); EXPECT_TRUE(ExpectGetAccounts({})); } @@ -938,6 +945,8 @@ class GetAuthTokenFunctionTest id_api()->mint_queue()->RequestComplete(type, key, request); } + base::HistogramTester* histogram_tester() { return &histogram_tester_; } + base::OnceClosure on_access_token_requested_; private: @@ -950,6 +959,7 @@ class GetAuthTokenFunctionTest std::move(on_access_token_requested_).Run(); } + base::HistogramTester histogram_tester_; std::string extension_id_; std::set<std::string> oauth_scopes_; }; @@ -962,6 +972,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoClientId) { EXPECT_EQ(std::string(errors::kInvalidClientId), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kInvalidClientId, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoScopes) { @@ -972,6 +985,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoScopes) { EXPECT_EQ(std::string(errors::kInvalidScopes), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kEmptyScopes, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveNotSignedIn) { @@ -982,6 +998,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveNotSignedIn) { EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNotSignedIn, 1); } // The signin flow is simply not used on ChromeOS. @@ -996,6 +1015,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kSignInFailed, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1018,6 +1040,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kBrowserSigninNotAllowed), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kBrowserSigninNotAllowed, 1); } #endif @@ -1032,6 +1057,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveMintFailure) { base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1044,6 +1072,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); EXPECT_TRUE(base::StartsWith(error, errors::kAuthFailure, base::CompareCase::INSENSITIVE_ASCII)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGetAccessTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1061,6 +1092,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_ADVICE, GetCachedToken(CoreAccountId()).status()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaConsentInteractionRequired, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1076,6 +1110,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1091,6 +1128,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1109,6 +1149,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, // in a valid state. EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } // The signin flow is simply not used on ChromeOS. @@ -1129,6 +1172,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } #endif @@ -1147,6 +1193,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoOptionsSuccess) { EXPECT_FALSE(func->scope_ui_shown()); EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, GetCachedToken(CoreAccountId()).status()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveSuccess) { @@ -1164,6 +1213,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveSuccess) { EXPECT_FALSE(func->scope_ui_shown()); EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, GetCachedToken(CoreAccountId()).status()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveLoginCanceled) { @@ -1179,6 +1231,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveLoginCanceled) { EXPECT_TRUE(func->login_ui_shown()); #endif EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kSignInFailed, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1195,6 +1250,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } // The interactive login flow is always short-circuited out with failure on @@ -1213,6 +1271,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1228,6 +1289,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1242,6 +1306,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGetAccessTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1259,6 +1326,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1273,6 +1343,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserRejected), error); EXPECT_TRUE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowRejected, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1290,6 +1363,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_TRUE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } #endif @@ -1304,6 +1380,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalAborted) { EXPECT_EQ(std::string(errors::kUserRejected), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowRejected, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1318,6 +1397,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kPageLoadFailure), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kPageLoadFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1332,6 +1414,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kInvalidRedirect), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kInvalidRedirect, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1348,6 +1433,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1366,6 +1454,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, // The login UI should not be shown as the account is in a valid state. EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowAuthFailure, 1); } // The signin flow is simply not used on ChromeOS. @@ -1388,6 +1479,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_TRUE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowAuthFailure, 1); } #endif @@ -1396,15 +1490,24 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, SignIn("primary@example.com"); scoped_refptr<const Extension> extension(CreateExtension(CLIENT_ID | SCOPES)); - std::map<std::string, std::string> error_map; - error_map.insert(std::make_pair("access_denied", errors::kUserRejected)); - error_map.insert(std::make_pair("invalid_scope", errors::kInvalidScopes)); - error_map.insert(std::make_pair( - "unmapped_error", std::string(errors::kAuthFailure) + "unmapped_error")); + struct TestCase { + std::string oauth_error; + std::string error_message; + IdentityGetAuthTokenError::State error_state; + }; - for (std::map<std::string, std::string>::const_iterator it = - error_map.begin(); - it != error_map.end(); ++it) { + std::vector<TestCase> test_cases; + test_cases.push_back({"access_denied", errors::kUserRejected, + IdentityGetAuthTokenError::State::kOAuth2AccessDenied}); + test_cases.push_back( + {"invalid_scope", errors::kInvalidScopes, + IdentityGetAuthTokenError::State::kOAuth2InvalidScopes}); + test_cases.push_back({"unmapped_error", + std::string(errors::kAuthFailure) + "unmapped_error", + IdentityGetAuthTokenError::State::kOAuth2Failure}); + + for (const auto& test_case : test_cases) { + base::HistogramTester histogram_tester; scoped_refptr<FakeGetAuthTokenFunction> func( new FakeGetAuthTokenFunction()); func->set_extension(extension.get()); @@ -1412,12 +1515,14 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, // flow to be leaked. id_api()->EraseAllCachedTokens(); func->push_mint_token_result(TestOAuth2MintTokenFlow::ISSUE_ADVICE_SUCCESS); - func->set_scope_ui_oauth_error(it->first); + func->set_scope_ui_oauth_error(test_case.oauth_error); std::string error = utils::RunFunctionAndReturnError( func.get(), "[{\"interactive\": true}]", browser()); - EXPECT_EQ(it->second, error); + EXPECT_EQ(test_case.error_message, error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester.ExpectUniqueSample(kGetAuthTokenResultHistogramName, + test_case.error_state, 1); } } @@ -1438,6 +1543,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalSuccess) { EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, GetCachedToken(CoreAccountId()).status()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } #if !defined(OS_MACOSX) @@ -1472,6 +1580,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, // The login screen should not be shown when the browser process is shutting // down. EXPECT_FALSE(func->login_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowAuthFailure, 1); } #endif // !defined(OS_MACOSX) @@ -1506,6 +1617,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) { EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) { @@ -1540,6 +1654,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) { EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueueShutdown) { @@ -1571,6 +1688,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueueShutdown) { EXPECT_FALSE(func->scope_ui_shown()); QueueRequestComplete(type, &queued_request); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kCanceled, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) { @@ -1585,6 +1705,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) { // After the request is canceled, the function will complete. func->OnIdentityAPIShutdown(); EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get())); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kCanceled, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1611,6 +1734,10 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_FALSE(func->scope_ui_shown()); QueueRequestComplete(type, &queued_request); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaConsentInteractionAlreadyRunning, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveCacheHit) { @@ -1632,6 +1759,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveCacheHit) { EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } // Checks that the first account in Gaia cookie can be used when extensions are @@ -1665,6 +1795,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, std::string error = utils::RunFunctionAndReturnError(func.get(), "[{}]", browser()); EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNotSignedIn, 1); } else { // Use the account from Gaia cookies. std::unique_ptr<base::Value> value( @@ -1672,6 +1805,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, std::string access_token; EXPECT_TRUE(value->GetAsString(&access_token)); EXPECT_EQ(std::string(kAccessToken), access_token); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kNone, 1); } EXPECT_FALSE(func->login_ui_shown()); @@ -1697,6 +1833,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kNoGrant), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaConsentInteractionRequired, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveCacheHit) { @@ -1734,6 +1873,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveCacheHit) { EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } // The interactive login UI is never shown on ChromeOS, so tests of the @@ -1763,6 +1905,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, LoginInvalidatesTokenCache) { EXPECT_TRUE(func->scope_ui_shown()); EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_NOTFOUND, GetCachedToken(CoreAccountId()).status()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } #endif @@ -1789,6 +1934,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(kAccessToken, value->GetString()); EXPECT_TRUE(func->scope_ui_shown()); EXPECT_FALSE(GetCachedGaiaId().has_value()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1814,6 +1962,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserRejected), error); EXPECT_TRUE(func->scope_ui_shown()); EXPECT_FALSE(GetCachedGaiaId().has_value()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowRejected, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) { @@ -1852,6 +2003,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, IdentityAPIShutdown) { id_api()->Shutdown(); EXPECT_EQ(std::string(errors::kCanceled), WaitForError(func.get())); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kCanceled, 1); } // Ensure that when there are multiple active function calls, IdentityAPI @@ -1920,6 +2074,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ManuallyIssueToken) { GetCachedToken(CoreAccountId()).status()); EXPECT_THAT(func->login_access_tokens(), testing::ElementsAre(primary_account_access_token)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ManuallyIssueTokenFailure) { @@ -1947,6 +2104,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ManuallyIssueTokenFailure) { GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE) .ToString(), WaitForError(func.get())); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGetAccessTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1976,6 +2136,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, GetCachedToken(CoreAccountId()).status()); EXPECT_THAT(func->login_access_tokens(), testing::ElementsAre(primary_account_access_token)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2007,6 +2170,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, GetCachedToken(CoreAccountId()).status()); EXPECT_THAT(func->login_access_tokens(), testing::ElementsAre(primary_account_access_token)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2033,6 +2199,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserNonPrimary), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNonPrimary, 1); return; } @@ -2052,6 +2221,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, GetCachedToken(secondary_account_id).status()); EXPECT_THAT(func->login_access_tokens(), testing::ElementsAre(secondary_account_access_token)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2068,10 +2240,17 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, func.get(), "[{\"account\": { \"id\": \"unknown@example.com\" } }]", browser()); std::string expected_error; - if (id_api()->AreExtensionsRestrictedToPrimaryAccount()) + if (id_api()->AreExtensionsRestrictedToPrimaryAccount()) { EXPECT_EQ(errors::kUserNonPrimary, error); - else + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNonPrimary, 1); + } else { EXPECT_EQ(errors::kUserNotSignedIn, error); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNotSignedIn, 1); + } } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2094,6 +2273,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, base::CompareCase::INSENSITIVE_ASCII)); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kMintTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2114,6 +2296,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, browser()); EXPECT_TRUE(base::StartsWith(error, errors::kAuthFailure, base::CompareCase::INSENSITIVE_ASCII)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGetAccessTokenAuthFailure, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -2137,6 +2322,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserRejected), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kGaiaFlowRejected, 1); } // Tests that Chrome remembers user's choice of an account at the end of the @@ -2172,6 +2360,12 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, if (id_api()->AreExtensionsRestrictedToPrimaryAccount()) { EXPECT_EQ(std::string(errors::kUserNonPrimary), WaitForError(func.get())); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kRemoteConsentUserNonPrimary, 1); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultAfterConsentApprovedHistogramName, + IdentityGetAuthTokenError::State::kRemoteConsentUserNonPrimary, 1); return; } @@ -2189,6 +2383,12 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_THAT(func->login_access_tokens(), testing::ElementsAre(primary_account_access_token, secondary_account_access_token)); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kNone, 1); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultAfterConsentApprovedHistogramName, + IdentityGetAuthTokenError::State::kNone, 1); } { @@ -2204,6 +2404,12 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kNone, 2); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultAfterConsentApprovedHistogramName, + IdentityGetAuthTokenError::State::kNone, 1); } } @@ -2261,6 +2467,10 @@ IN_PROC_BROWSER_TEST_F( EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, GetCachedToken(account.account_id).status()); + + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 2); } // Tests two concurrent remote consent flows. Both of them should succeed. @@ -2325,6 +2535,10 @@ IN_PROC_BROWSER_TEST_F( EXPECT_EQ(IdentityTokenCacheValue::CACHE_STATUS_TOKEN, GetCachedToken(account.account_id).status()); + + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 2); } // The signin flow is simply not used on ChromeOS. @@ -2356,6 +2570,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(errors::kUserNonPrimary), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kUserNonPrimary, 1); } else { // Extensions can show the login UI for secondary accounts, and get the auth // token. @@ -2366,6 +2583,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_EQ(std::string(kAccessToken), access_token); EXPECT_TRUE(func->login_ui_shown()); EXPECT_TRUE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kNone, 1); } } #endif @@ -2386,6 +2606,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesDefault) { EXPECT_EQ(2ul, token_key->scopes.size()); EXPECT_TRUE(base::Contains(token_key->scopes, "scope1")); EXPECT_TRUE(base::Contains(token_key->scopes, "scope2")); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmpty) { @@ -2397,6 +2620,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmpty) { func.get(), "[{\"scopes\": []}]", browser())); EXPECT_EQ(errors::kInvalidScopes, error); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kEmptyScopes, 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmail) { @@ -2414,6 +2640,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmail) { const ExtensionTokenKey* token_key = func->GetExtensionTokenKeyForTest(); EXPECT_EQ(1ul, token_key->scopes.size()); EXPECT_TRUE(base::Contains(token_key->scopes, "email")); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmailFooBar) { @@ -2433,6 +2662,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmailFooBar) { EXPECT_TRUE(base::Contains(token_key->scopes, "email")); EXPECT_TRUE(base::Contains(token_key->scopes, "foo")); EXPECT_TRUE(base::Contains(token_key->scopes, "bar")); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } #if defined(OS_CHROMEOS) @@ -2488,11 +2720,12 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionPublicSessionTest, NonWhitelisted) { EXPECT_EQ(std::string(errors::kUserNotSignedIn), error); EXPECT_FALSE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, + IdentityGetAuthTokenError::State::kNotWhitelistedInPublicSession, 1); } -// TODO(crbug.com/830052): This test is flaky. -IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionPublicSessionTest, - DISABLED_Whitelisted) { +IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionPublicSessionTest, Whitelisted) { // GetAuthToken() should return a token for whitelisted extensions. user_manager::ScopedUserManager user_manager_enabler( base::WrapUnique(user_manager_)); @@ -2504,6 +2737,9 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionPublicSessionTest, std::string access_token; EXPECT_TRUE(value->GetAsString(&access_token)); EXPECT_EQ(std::string(kAccessToken), access_token); + histogram_tester()->ExpectUniqueSample( + kGetAuthTokenResultHistogramName, IdentityGetAuthTokenError::State::kNone, + 1); } #endif @@ -2829,19 +3065,19 @@ IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountSignOut) { #endif // !defined(OS_CHROMEOS) // Test that an event is fired when the primary account has a refresh token -// revoked. +// invalidated. IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, - FireOnPrimaryAccountRefreshTokenRevoked) { + FireOnPrimaryAccountRefreshTokenInvalidated) { api::identity::AccountInfo account_info; account_info.id = "gaia_id_for_primary_example.com"; AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); CoreAccountId primary_account_id = SignIn("primary@example.com"); - AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, false)); + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); // Revoke the refresh token and verify that the callback fires. - identity_test_env()->RemoveRefreshTokenForAccount(primary_account_id); + identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount(); EXPECT_FALSE(HasExpectedEvent()); } @@ -2856,14 +3092,14 @@ IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, CoreAccountId primary_account_id = SignIn("primary@example.com"); - AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, false)); - identity_test_env()->RemoveRefreshTokenForAccount(primary_account_id); + AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); + identity_test_env()->SetInvalidRefreshTokenForPrimaryAccount(); account_info.id = "gaia_id_for_primary_example.com"; AddExpectedEvent(api::identity::OnSignInChanged::Create(account_info, true)); // Make the primary account available again and check that the callback fires. - identity_test_env()->MakeAccountAvailable("primary@example.com"); + identity_test_env()->SetRefreshTokenForPrimaryAccount(); EXPECT_FALSE(HasExpectedEvent()); } 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 new file mode 100644 index 00000000000..7f02651c90d --- /dev/null +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.cc @@ -0,0 +1,112 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/identity/identity_get_auth_token_error.h" + +#include "base/strings/string_piece.h" +#include "chrome/browser/extensions/api/identity/identity_constants.h" + +namespace extensions { + +// static +IdentityGetAuthTokenError IdentityGetAuthTokenError::FromGaiaFlowAuthError( + base::StringPiece error_message) { + return IdentityGetAuthTokenError(State::kGaiaFlowAuthFailure, error_message); +} + +// static +IdentityGetAuthTokenError IdentityGetAuthTokenError::FromMintTokenAuthError( + base::StringPiece error_message) { + return IdentityGetAuthTokenError(State::kMintTokenAuthFailure, error_message); +} + +// static +IdentityGetAuthTokenError +IdentityGetAuthTokenError::FromGetAccessTokenAuthError( + base::StringPiece error_message) { + return IdentityGetAuthTokenError(State::kGetAccessTokenAuthFailure, + error_message); +} + +// static +IdentityGetAuthTokenError IdentityGetAuthTokenError::FromOAuth2Error( + base::StringPiece oauth2_error) { + const char kOAuth2ErrorAccessDenied[] = "access_denied"; + const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; + + if (oauth2_error == kOAuth2ErrorAccessDenied) { + return IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kOAuth2AccessDenied); + } else if (oauth2_error == kOAuth2ErrorInvalidScope) { + return IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kOAuth2InvalidScopes); + } else { + return IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kOAuth2Failure, oauth2_error); + } +} + +IdentityGetAuthTokenError::IdentityGetAuthTokenError() + : IdentityGetAuthTokenError(State::kNone) {} + +IdentityGetAuthTokenError::IdentityGetAuthTokenError(State state) + : IdentityGetAuthTokenError(state, base::StringPiece()) {} + +IdentityGetAuthTokenError::State IdentityGetAuthTokenError::state() const { + return state_; +} + +std::string IdentityGetAuthTokenError::ToString() const { + switch (state_) { + case State::kNone: + return std::string(); + case State::kInvalidClientId: + return identity_constants::kInvalidClientId; + case State::kEmptyScopes: + case State::kOAuth2InvalidScopes: + return identity_constants::kInvalidScopes; + case State::kGaiaFlowAuthFailure: + case State::kMintTokenAuthFailure: + case State::kGetAccessTokenAuthFailure: + case State::kOAuth2Failure: + return identity_constants::kAuthFailure + error_message_; + case State::kNoGrant: + case State::kGaiaConsentInteractionRequired: + case State::kGaiaConsentInteractionAlreadyRunning: + return identity_constants::kNoGrant; + case State::kOAuth2AccessDenied: + case State::kGaiaFlowRejected: + case State::kRemoteConsentFlowRejected: + return identity_constants::kUserRejected; + case State::kUserNotSignedIn: + case State::kNotWhitelistedInPublicSession: + case State::kSignInFailed: + case State::kRemoteConsentUserNotSignedIn: + return identity_constants::kUserNotSignedIn; + case State::kUserNonPrimary: + case State::kRemoteConsentUserNonPrimary: + return identity_constants::kUserNonPrimary; + case State::kBrowserSigninNotAllowed: + return identity_constants::kBrowserSigninNotAllowed; + case State::kInvalidRedirect: + return identity_constants::kInvalidRedirect; + case State::kOffTheRecord: + return identity_constants::kOffTheRecord; + case State::kPageLoadFailure: + case State::kRemoteConsentPageLoadFailure: + return identity_constants::kPageLoadFailure; + case State::kSetAccountsInCookieFailure: + return identity_constants::kSetAccountsInCookieFailure; + case State::kInvalidConsentResult: + return identity_constants::kInvalidConsentResult; + case State::kCanceled: + return identity_constants::kCanceled; + } +} + +IdentityGetAuthTokenError::IdentityGetAuthTokenError(State state, + base::StringPiece error) + : state_(state), error_message_(error) {} + +} // namespace extensions 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 new file mode 100644 index 00000000000..e03a89cea0b --- /dev/null +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_error.h @@ -0,0 +1,90 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_GET_AUTH_TOKEN_ERROR_H_ +#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_GET_AUTH_TOKEN_ERROR_H_ + +#include <string> + +#include "base/strings/string_piece_forward.h" + +namespace extensions { + +class IdentityGetAuthTokenError { + public: + // These values are persisted to logs. Entries should not be renumbered and + // numeric values should never be reused. + enum class State { + kNone = 0, + kInvalidClientId = 1, + kEmptyScopes = 2, + kOAuth2InvalidScopes = 3, + kGaiaFlowAuthFailure = 4, + kMintTokenAuthFailure = 5, + kGetAccessTokenAuthFailure = 6, + kOAuth2Failure = 7, + kNoGrant = 8, + kGaiaConsentInteractionRequired = 9, + kGaiaConsentInteractionAlreadyRunning = 10, + kOAuth2AccessDenied = 11, + kGaiaFlowRejected = 12, + kRemoteConsentFlowRejected = 13, + kUserNotSignedIn = 14, + kNotWhitelistedInPublicSession = 15, + kSignInFailed = 16, + kRemoteConsentUserNotSignedIn = 17, + kUserNonPrimary = 18, + kRemoteConsentUserNonPrimary = 19, + kBrowserSigninNotAllowed = 20, + kInvalidRedirect = 21, + kOffTheRecord = 22, + kPageLoadFailure = 23, + kRemoteConsentPageLoadFailure = 24, + kSetAccountsInCookieFailure = 25, + kInvalidConsentResult = 26, + kCanceled = 27, + kMaxValue = kCanceled, + }; + + // Constructs a |State::kGaiaFlowAuthFailure| error with an |error_message|. + static IdentityGetAuthTokenError FromGaiaFlowAuthError( + base::StringPiece error_message); + + // Constructs a |State::kMintTokenAuthFailure| error with an + // |error_message|. + static IdentityGetAuthTokenError FromMintTokenAuthError( + base::StringPiece error_message); + + // Constructs a |State::kGetAccessTokenAuthFailure| error with an + // |error_message|. + static IdentityGetAuthTokenError FromGetAccessTokenAuthError( + base::StringPiece error_message); + + // Constructs an IdentityGetAuthTokenError from |oauth2_error|. + static IdentityGetAuthTokenError FromOAuth2Error( + base::StringPiece oauth2_error); + + // Constructs a |State::kNone| error. + IdentityGetAuthTokenError(); + + // Constructs an IdentityGetAuthTokenError from |state| with no additional + // data. + explicit IdentityGetAuthTokenError(State state); + + State state() const; + + // Returns an error message that can be returned to the developer in + // chrome.runtime.lastError. + std::string ToString() const; + + private: + IdentityGetAuthTokenError(State state, base::StringPiece error); + + State state_; + std::string error_message_; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_GET_AUTH_TOKEN_ERROR_H_ 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 39a89fd15be..4e52c3887e1 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 @@ -10,14 +10,16 @@ #include "base/bind.h" #include "base/feature_list.h" #include "base/location.h" +#include "base/metrics/histogram_functions.h" +#include "base/notreached.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" -#include "base/task/post_task.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.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/identity_get_auth_token_error.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/chrome_device_id_helper.h" #include "chrome/browser/signin/identity_manager_factory.h" @@ -77,6 +79,17 @@ std::string GetOAuth2MintTokenFlowChannel() { return version_info::GetChannelString(chrome::GetChannel()); } +void RecordFunctionResult(const IdentityGetAuthTokenError& error, + bool remote_consent_approved) { + base::UmaHistogramEnumeration("Signin.Extensions.GetAuthTokenResult", + error.state()); + if (remote_consent_approved) { + base::UmaHistogramEnumeration( + "Signin.Extensions.GetAuthTokenResult.RemoteConsentApproved", + error.state()); + } +} + } // namespace IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() @@ -104,7 +117,10 @@ ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() { this, "extension", extension()->id()); if (GetProfile()->IsOffTheRecord()) { - return RespondNow(Error(identity_constants::kOffTheRecord)); + IdentityGetAuthTokenError error( + IdentityGetAuthTokenError::State::kOffTheRecord); + RecordFunctionResult(error, remote_consent_approved_); + return RespondNow(Error(error.ToString())); } std::unique_ptr<api::identity::GetAuthToken::Params> params( @@ -122,7 +138,10 @@ ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() { // Check that the necessary information is present in the manifest. oauth2_client_id_ = GetOAuth2ClientId(); if (oauth2_client_id_.empty()) { - return RespondNow(Error(identity_constants::kInvalidClientId)); + IdentityGetAuthTokenError error( + IdentityGetAuthTokenError::State::kInvalidClientId); + RecordFunctionResult(error, remote_consent_approved_); + return RespondNow(Error(error.ToString())); } std::set<std::string> scopes(oauth2_info.scopes.begin(), @@ -140,7 +159,10 @@ ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() { } if (scopes.empty()) { - return RespondNow(Error(identity_constants::kInvalidScopes)); + IdentityGetAuthTokenError error( + IdentityGetAuthTokenError::State::kEmptyScopes); + RecordFunctionResult(error, remote_consent_approved_); + return RespondNow(Error(error.ToString())); } token_key_.scopes = scopes; @@ -159,15 +181,15 @@ ExtensionFunction::ResponseAction IdentityGetAuthTokenFunction::Run() { if (gaia_id.empty() || IsPrimaryAccountOnly()) { // Try the primary account. // TODO(https://crbug.com/932400): collapse the asynchronicity - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &IdentityGetAuthTokenFunction::GetAuthTokenForPrimaryAccount, weak_ptr_factory_.GetWeakPtr(), gaia_id)); } else { // Get the AccountInfo for the account that the extension wishes to use. - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&IdentityGetAuthTokenFunction::FetchExtensionAccountInfo, weak_ptr_factory_.GetWeakPtr(), gaia_id)); } @@ -186,7 +208,8 @@ void IdentityGetAuthTokenFunction::GetAuthTokenForPrimaryAccount( // than the primary account. if (primary_account_only && !extension_gaia_id.empty() && extension_gaia_id != primary_account_info.gaia) { - CompleteFunctionWithError(identity_constants::kUserNonPrimary); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kUserNonPrimary)); return; } @@ -230,7 +253,8 @@ void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo( if (connector->IsEnterpriseManaged() && (is_kiosk || is_public_session)) { if (is_public_session && !IsOriginWhitelistedInPublicSession()) { - CompleteFunctionWithError(identity_constants::kUserNotSignedIn); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kNotWhitelistedInPublicSession)); return; } @@ -243,10 +267,11 @@ void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo( !IdentityManagerFactory::GetForProfile(GetProfile()) ->HasAccountWithRefreshToken(account_info->account_id)) { if (!ShouldStartSigninFlow()) { - CompleteFunctionWithError( + IdentityGetAuthTokenError error( IsBrowserSigninAllowed(GetProfile()) - ? identity_constants::kUserNotSignedIn - : identity_constants::kBrowserSigninNotAllowed); + ? IdentityGetAuthTokenError::State::kUserNotSignedIn + : IdentityGetAuthTokenError::State::kBrowserSigninNotAllowed); + CompleteFunctionWithError(error); return; } // Display a login prompt. @@ -303,14 +328,16 @@ void IdentityGetAuthTokenFunction::CompleteAsyncRun(ResponseValue response) { void IdentityGetAuthTokenFunction::CompleteFunctionWithResult( const std::string& access_token) { + RecordFunctionResult(IdentityGetAuthTokenError(), remote_consent_approved_); CompleteAsyncRun(OneArgument(std::make_unique<base::Value>(access_token))); } void IdentityGetAuthTokenFunction::CompleteFunctionWithError( - const std::string& error) { + const IdentityGetAuthTokenError& error) { TRACE_EVENT_NESTABLE_ASYNC_INSTANT1("identity", "CompleteFunctionWithError", - this, "error", error); - CompleteAsyncRun(Error(error)); + this, "error", error.ToString()); + RecordFunctionResult(error, remote_consent_approved_); + CompleteAsyncRun(Error(error.ToString())); } bool IdentityGetAuthTokenFunction::ShouldStartSigninFlow() { @@ -394,14 +421,17 @@ void IdentityGetAuthTokenFunction::StartMintTokenFlow( if (type == IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE) { // GAIA told us to do a consent UI. - CompleteFunctionWithError(identity_constants::kNoGrant); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kGaiaConsentInteractionRequired)); return; } if (!id_api->mint_queue()->empty( IdentityMintRequestQueue::MINT_TYPE_INTERACTIVE, token_key_)) { // Another call is going through a consent UI. - CompleteFunctionWithError(identity_constants::kNoGrant); + CompleteFunctionWithError( + IdentityGetAuthTokenError(IdentityGetAuthTokenError::State:: + kGaiaConsentInteractionAlreadyRunning)); return; } } @@ -437,7 +467,9 @@ void IdentityGetAuthTokenFunction::StartMintToken( // Always force minting token for ChromeOS kiosk app and public session. if (user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() && !IsOriginWhitelistedInPublicSession()) { - CompleteFunctionWithError(identity_constants::kUserNotSignedIn); + CompleteFunctionWithError( + IdentityGetAuthTokenError(IdentityGetAuthTokenError::State:: + kNotWhitelistedInPublicSession)); return; } @@ -556,8 +588,8 @@ void IdentityGetAuthTokenFunction::OnMintTokenFailure( break; } - CompleteFunctionWithError(std::string(identity_constants::kAuthFailure) + - error.ToString()); + CompleteFunctionWithError( + IdentityGetAuthTokenError::FromMintTokenAuthError(error.ToString())); } void IdentityGetAuthTokenFunction::OnIssueAdviceSuccess( @@ -614,6 +646,20 @@ void IdentityGetAuthTokenFunction::OnRefreshTokenUpdatedForAccount( } } +bool IdentityGetAuthTokenFunction::TryRecoverFromServiceAuthError( + const GoogleServiceAuthError& error) { + // If this is really an authentication error and not just a transient + // network error, then we show signin UI if appropriate. + if (error.state() != GoogleServiceAuthError::CONNECTION_FAILED && + error.state() != GoogleServiceAuthError::SERVICE_UNAVAILABLE) { + if (ShouldStartSigninFlow()) { + StartSigninFlow(); + return true; + } + } + return false; +} + void IdentityGetAuthTokenFunction::OnPrimaryAccountSet( const CoreAccountInfo& primary_account_info) { if (account_listening_mode_ != AccountListeningMode::kListeningPrimaryAccount) @@ -635,7 +681,8 @@ void IdentityGetAuthTokenFunction::OnPrimaryAccountSet( void IdentityGetAuthTokenFunction::SigninFailed() { TRACE_EVENT_NESTABLE_ASYNC_INSTANT0("identity", "SigninFailed", this); - CompleteFunctionWithError(identity_constants::kUserNotSignedIn); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kSignInFailed)); } void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( @@ -643,43 +690,40 @@ void IdentityGetAuthTokenFunction::OnGaiaFlowFailure( GoogleServiceAuthError service_error, const std::string& oauth_error) { CompleteMintTokenFlow(); - std::string error; + IdentityGetAuthTokenError error; switch (failure) { case GaiaWebAuthFlow::WINDOW_CLOSED: - error = identity_constants::kUserRejected; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kGaiaFlowRejected); break; case GaiaWebAuthFlow::INVALID_REDIRECT: - error = identity_constants::kInvalidRedirect; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kInvalidRedirect); break; case GaiaWebAuthFlow::SERVICE_AUTH_ERROR: - // If this is really an authentication error and not just a transient - // network error, then we show signin UI if appropriate. - if (service_error.state() != GoogleServiceAuthError::CONNECTION_FAILED && - service_error.state() != - GoogleServiceAuthError::SERVICE_UNAVAILABLE) { - if (ShouldStartSigninFlow()) { - StartSigninFlow(); - return; - } + if (TryRecoverFromServiceAuthError(service_error)) { + return; } - error = std::string(identity_constants::kAuthFailure) + - service_error.ToString(); + error = IdentityGetAuthTokenError::FromGaiaFlowAuthError( + service_error.ToString()); break; case GaiaWebAuthFlow::OAUTH_ERROR: - error = MapOAuth2ErrorToDescription(oauth_error); + error = IdentityGetAuthTokenError::FromOAuth2Error(oauth_error); break; case GaiaWebAuthFlow::LOAD_FAILED: - error = identity_constants::kPageLoadFailure; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kPageLoadFailure); break; default: NOTREACHED() << "Unexpected error from gaia web auth flow: " << failure; - error = identity_constants::kInvalidRedirect; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kInvalidRedirect); break; } @@ -706,27 +750,36 @@ void IdentityGetAuthTokenFunction::OnGaiaFlowCompleted( void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowFailed( GaiaRemoteConsentFlow::Failure failure) { CompleteMintTokenFlow(); - std::string error; + IdentityGetAuthTokenError error; switch (failure) { case GaiaRemoteConsentFlow::WINDOW_CLOSED: - error = identity_constants::kUserRejected; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kRemoteConsentFlowRejected); break; case GaiaRemoteConsentFlow::SET_ACCOUNTS_IN_COOKIE_FAILED: - error = identity_constants::kSetAccountsInCookieFailure; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kSetAccountsInCookieFailure); break; case GaiaRemoteConsentFlow::LOAD_FAILED: - error = identity_constants::kPageLoadFailure; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kRemoteConsentPageLoadFailure); break; case GaiaRemoteConsentFlow::INVALID_CONSENT_RESULT: - error = identity_constants::kInvalidConsentResult; + error = IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kInvalidConsentResult); break; case GaiaRemoteConsentFlow::NO_GRANT: - error = identity_constants::kNoGrant; + error = + IdentityGetAuthTokenError(IdentityGetAuthTokenError::State::kNoGrant); + break; + + case GaiaRemoteConsentFlow::NONE: + NOTREACHED(); break; } @@ -739,13 +792,15 @@ void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowApproved( TRACE_EVENT_NESTABLE_ASYNC_INSTANT1( "identity", "OnGaiaRemoteConsentFlowApproved", this, "gaia_id", gaia_id); DCHECK(!consent_result.empty()); + remote_consent_approved_ = true; base::Optional<AccountInfo> account = IdentityManagerFactory::GetForProfile(GetProfile()) ->FindExtendedAccountInfoForAccountWithRefreshTokenByGaiaId(gaia_id); if (!account) { CompleteMintTokenFlow(); - CompleteFunctionWithError(identity_constants::kUserNotSignedIn); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kRemoteConsentUserNotSignedIn)); return; } @@ -755,7 +810,8 @@ void IdentityGetAuthTokenFunction::OnGaiaRemoteConsentFlowApproved( ->GetPrimaryAccountId(); if (primary_account_id != account->account_id) { CompleteMintTokenFlow(); - CompleteFunctionWithError(identity_constants::kUserNonPrimary); + CompleteFunctionWithError(IdentityGetAuthTokenError( + IdentityGetAuthTokenError::State::kRemoteConsentUserNonPrimary)); return; } } @@ -795,8 +851,13 @@ void IdentityGetAuthTokenFunction::OnGetAccessTokenComplete( TRACE_EVENT_NESTABLE_ASYNC_END1("identity", "GetAccessToken", this, "error", error.ToString()); - OnGaiaFlowFailure(GaiaWebAuthFlow::SERVICE_AUTH_ERROR, error, - std::string()); + CompleteMintTokenFlow(); + if (TryRecoverFromServiceAuthError(error)) { + return; + } + CompleteFunctionWithError( + IdentityGetAuthTokenError::FromGetAccessTokenAuthError( + error.ToString())); } } @@ -841,7 +902,8 @@ void IdentityGetAuthTokenFunction::OnIdentityAPIShutdown() { ->mint_queue() ->RequestCancel(token_key_, this); - CompleteFunctionWithError(identity_constants::kCanceled); + CompleteFunctionWithError( + IdentityGetAuthTokenError(IdentityGetAuthTokenError::State::kCanceled)); } #if defined(OS_CHROMEOS) @@ -964,19 +1026,6 @@ bool IdentityGetAuthTokenFunction::HasRefreshTokenForTokenKeyAccount() const { return identity_manager->HasAccountWithRefreshToken(token_key_.account_id); } -std::string IdentityGetAuthTokenFunction::MapOAuth2ErrorToDescription( - const std::string& error) { - const char kOAuth2ErrorAccessDenied[] = "access_denied"; - const char kOAuth2ErrorInvalidScope[] = "invalid_scope"; - - if (error == kOAuth2ErrorAccessDenied) - return std::string(identity_constants::kUserRejected); - else if (error == kOAuth2ErrorInvalidScope) - return std::string(identity_constants::kInvalidScopes); - else - return std::string(identity_constants::kAuthFailure) + error; -} - std::string IdentityGetAuthTokenFunction::GetOAuth2ClientId() const { const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension()); std::string client_id = oauth2_info.client_id; diff --git a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h index 77dae78c32d..48b802ef28c 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_get_auth_token_function.h @@ -28,6 +28,7 @@ struct AccessTokenInfo; } // namespace signin namespace extensions { +class IdentityGetAuthTokenError; // identity.getAuthToken fetches an OAuth 2 function for the // caller. The request has three sub-flows: non-interactive, @@ -155,6 +156,11 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction, void OnPrimaryAccountSet( const CoreAccountInfo& primary_account_info) override; + // Attempts to show the signin UI after the service auth error if this error + // isn't transient. + // Returns true iff the signin flow was triggered. + bool TryRecoverFromServiceAuthError(const GoogleServiceAuthError& error); + // ExtensionFunction: ResponseAction Run() override; @@ -162,7 +168,7 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction, void StartAsyncRun(); void CompleteAsyncRun(ResponseValue response); void CompleteFunctionWithResult(const std::string& access_token); - void CompleteFunctionWithError(const std::string& error); + void CompleteFunctionWithError(const IdentityGetAuthTokenError& error); // Whether a signin flow should be initiated in the user's current state. bool ShouldStartSigninFlow(); @@ -202,10 +208,6 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction, // Checks if there is a master login token to mint tokens for the extension. bool HasRefreshTokenForTokenKeyAccount() const; - // Maps OAuth2 protocol errors to an error message returned to the - // developer in chrome.runtime.lastError. - std::string MapOAuth2ErrorToDescription(const std::string& error); - std::string GetOAuth2ClientId() const; // Returns true if extensions are restricted to the primary account. @@ -231,6 +233,8 @@ class IdentityGetAuthTokenFunction : public ExtensionFunction, RemoteConsentResolutionData resolution_data_; std::unique_ptr<GaiaRemoteConsentFlow> gaia_remote_consent_flow_; std::string consent_result_; + // Added for debugging https://crbug.com/1091423. + bool remote_consent_approved_ = false; // Invoked when IdentityAPI is shut down. std::unique_ptr<base::CallbackList<void()>::Subscription> 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 de1e3443c03..c3c748e6d98 100644 --- a/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc +++ b/chromium/chrome/browser/extensions/api/identity/web_auth_flow.cc @@ -80,7 +80,7 @@ WebAuthFlow::WebAuthFlow(Delegate* delegate, } WebAuthFlow::~WebAuthFlow() { - DCHECK(delegate_ == NULL); + DCHECK(!delegate_); // Stop listening to notifications first since some of the code // below may generate notifications. @@ -134,7 +134,7 @@ void WebAuthFlow::Start() { } void WebAuthFlow::DetachDelegateAndDelete() { - delegate_ = NULL; + delegate_ = nullptr; base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this); } @@ -170,7 +170,7 @@ void WebAuthFlow::OnAppWindowAdded(AppWindow* app_window) { void WebAuthFlow::OnAppWindowRemoved(AppWindow* app_window) { if (app_window->window_key() == app_window_key_ && app_window->extension_id() == extension_misc::kIdentityApiUiAppId) { - app_window_ = NULL; + app_window_ = nullptr; registrar_.RemoveAll(); WebContentsObserver::Observe(nullptr); diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc index 1d58c0f6f7e..60b1c97a814 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/image_writer_utility_client_browsertest.cc @@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/sequenced_task_runner.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/operation.h" @@ -201,7 +200,7 @@ class ImageWriterUtilityClientTest : public InProcessBrowserTest { success_ = cancel_; quit_called_ = true; - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, quit_closure_); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, quit_closure_); } void Shutdown() { @@ -210,7 +209,7 @@ class ImageWriterUtilityClientTest : public InProcessBrowserTest { image_writer_utility_client_->Shutdown(); quit_called_ = true; - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, quit_closure_); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, quit_closure_); } static void FillFile(const base::FilePath& path, char pattern) { diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc index 608842bfe5d..582b050d487 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation.cc @@ -8,7 +8,6 @@ #include "base/bind.h" #include "base/files/file_util.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" @@ -20,8 +19,6 @@ namespace extensions { namespace image_writer { -using content::BrowserThread; - namespace { const int kMD5BufferSize = 1024; @@ -129,16 +126,16 @@ void Operation::Finish() { CleanUp(); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&OperationManager::OnComplete, manager_, extension_id_)); } void Operation::Error(const std::string& error_message) { DCHECK(IsRunningInCorrectSequence()); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&OperationManager::OnError, manager_, extension_id_, stage_, progress_, error_message)); @@ -158,8 +155,8 @@ void Operation::SetProgress(int progress) { progress_ = progress; - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&OperationManager::OnProgress, manager_, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&OperationManager::OnProgress, manager_, extension_id_, stage_, progress_)); } @@ -172,8 +169,8 @@ void Operation::SetStage(image_writer_api::Stage stage) { stage_ = stage; progress_ = 0; - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&OperationManager::OnProgress, manager_, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&OperationManager::OnProgress, manager_, extension_id_, stage_, progress_)); } diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation_chromeos.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation_chromeos.cc index 5140db323c9..746ca29eacf 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/operation_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation_chromeos.cc @@ -5,7 +5,6 @@ #include <stdint.h> #include "base/bind.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" #include "chrome/browser/extensions/api/image_writer_private/operation.h" #include "chromeos/dbus/dbus_thread_manager.h" @@ -26,8 +25,8 @@ namespace { void ClearImageBurner() { if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&ClearImageBurner)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ClearImageBurner)); return; } @@ -45,8 +44,8 @@ void Operation::Write(const base::Closure& continuation) { // Note this has to be run on the FILE thread to avoid concurrent access. AddCleanUpFunction(base::BindOnce(&ClearImageBurner)); - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&Operation::UnmountVolumes, this, continuation)); } diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc index e069f5f485a..bb46532fd22 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_win.cc @@ -15,6 +15,7 @@ #include <memory> +#include "base/logging.h" #include "base/memory/scoped_refptr.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc index cc3d4473c73..fe3d9928022 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_file_operation_unittest.cc @@ -45,7 +45,7 @@ TEST_F(ImageWriterFromFileTest, InvalidFile) { test_utils_.GetDevicePath().AsUTF8Unsafe(), base::FilePath(FILE_PATH_LITERAL("/var/tmp"))); - base::DeleteFile(test_utils_.GetImagePath(), false); + base::DeleteFile(test_utils_.GetImagePath()); EXPECT_CALL(manager_, OnProgress(kDummyExtensionId, _, _)).Times(0); EXPECT_CALL(manager_, OnComplete(kDummyExtensionId)).Times(0); diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc index 942e3587a50..6b78d3f8de1 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation_unittest.cc @@ -6,7 +6,6 @@ #include "base/bind.h" #include "base/run_loop.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" #include "chrome/browser/extensions/api/image_writer_private/test_utils.h" #include "chrome/test/base/testing_profile.h" @@ -137,15 +136,7 @@ class ImageWriterWriteFromUrlOperationTest : public ImageWriterUnitTestBase { MockOperationManager manager_; }; -// Crashes on Tsan. http://crbug.com/859317 -#if defined(THREAD_SANITIZER) -#define MAYBE_SelectTargetWithoutExtension DISABLED_SelectTargetWithoutExtension -#define MAYBE_SelectTargetWithExtension DISABLED_SelectTargetWithExtension -#else -#define MAYBE_SelectTargetWithoutExtension SelectTargetWithoutExtension -#define MAYBE_SelectTargetWithExtension SelectTargetWithExtension -#endif -TEST_F(ImageWriterWriteFromUrlOperationTest, MAYBE_SelectTargetWithoutExtension) { +TEST_F(ImageWriterWriteFromUrlOperationTest, SelectTargetWithoutExtension) { scoped_refptr<WriteFromUrlOperationForTest> operation = CreateOperation(GURL("http://localhost/foo/bar"), ""); @@ -160,7 +151,7 @@ TEST_F(ImageWriterWriteFromUrlOperationTest, MAYBE_SelectTargetWithoutExtension) content::RunAllTasksUntilIdle(); } -TEST_F(ImageWriterWriteFromUrlOperationTest, MAYBE_SelectTargetWithExtension) { +TEST_F(ImageWriterWriteFromUrlOperationTest, SelectTargetWithExtension) { scoped_refptr<WriteFromUrlOperationForTest> operation = CreateOperation(GURL("http://localhost/foo/bar.zip"), ""); @@ -173,9 +164,6 @@ TEST_F(ImageWriterWriteFromUrlOperationTest, MAYBE_SelectTargetWithExtension) { operation->Cancel(); } -#undef MAYBE_SelectTargetWithoutExtension -#undef MAYBE_SelectTargetWithExtension - TEST_F(ImageWriterWriteFromUrlOperationTest, DownloadFile) { // This test actually triggers the URL fetch code, which will drain the @@ -242,10 +230,8 @@ TEST_F(ImageWriterWriteFromUrlOperationTest, VerifyFile) { // soon. operation->VerifyDownload(base::Bind( [](base::OnceClosure quit_closure) { - base::PostTask( - FROM_HERE, - {content::BrowserThread::UI, base::TaskPriority::USER_VISIBLE}, - std::move(quit_closure)); + content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) + ->PostTask(FROM_HERE, std::move(quit_closure)); }, run_loop.QuitClosure())); diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc index 62d7d390b82..37ea54f92ab 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.cc @@ -10,14 +10,14 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "extensions/browser/extension_registry.h" -#include "ui/base/ime/ime_bridge.h" +#include "ui/base/ime/chromeos/ime_bridge.h" namespace input_ime = extensions::api::input_ime; namespace KeyEventHandled = extensions::api::input_ime::KeyEventHandled; namespace SetComposition = extensions::api::input_ime::SetComposition; namespace CommitText = extensions::api::input_ime::CommitText; namespace SendKeyEvents = extensions::api::input_ime::SendKeyEvents; -using input_method::InputMethodEngineBase; +using chromeos::InputMethodEngineBase; namespace { const char kErrorRouterNotAvailable[] = "The router is not available."; diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h index 0c4eaac6687..934a51efa74 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api.h @@ -14,8 +14,8 @@ #include "base/scoped_observer.h" #include "base/values.h" #include "build/build_config.h" +#include "chrome/browser/chromeos/input_method/input_method_engine_base.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/input_method/input_method_engine_base.h" #include "chrome/common/extensions/api/input_ime.h" #include "components/keyed_service/core/keyed_service.h" #include "extensions/browser/browser_context_keyed_api_factory.h" @@ -31,8 +31,6 @@ #if defined(OS_CHROMEOS) #include "chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.h" -#elif defined(OS_LINUX) || defined(OS_WIN) -#include "chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.h" #endif // defined(OS_CHROMEOS) class Profile; @@ -40,19 +38,21 @@ class Profile; namespace ui { class IMEEngineHandlerInterface; -class ImeObserver : public input_method::InputMethodEngineBase::Observer { +using chromeos::InputMethodEngineBase; + +class ImeObserver : public InputMethodEngineBase::Observer { public: ImeObserver(const std::string& extension_id, Profile* profile); ~ImeObserver() override = default; - // input_method::InputMethodEngineBase::Observer overrides. + // InputMethodEngineBase::Observer overrides. void OnActivate(const std::string& component_id) override; void OnFocus(const IMEEngineHandlerInterface::InputContext& context) override; void OnBlur(int context_id) override; void OnKeyEvent( const std::string& component_id, - const input_method::InputMethodEngineBase::KeyboardEvent& event, + const InputMethodEngineBase::KeyboardEvent& event, IMEEngineHandlerInterface::KeyEventDoneCallback key_data) override; void OnReset(const std::string& component_id) override; void OnDeactivated(const std::string& component_id) override; 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 b9dfada4e67..e58f51d6e4f 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 @@ -13,6 +13,7 @@ #include "base/feature_list.h" #include "base/macros.h" #include "base/strings/stringprintf.h" +#include "chrome/browser/chromeos/input_method/assistive_window_properties.h" #include "chrome/browser/chromeos/input_method/input_method_engine.h" #include "chrome/browser/chromeos/input_method/native_input_method_engine.h" #include "chrome/browser/chromeos/login/lock/screen_locker.h" @@ -43,6 +44,8 @@ namespace SetCursorPosition = extensions::api::input_ime::SetCursorPosition; namespace SetCandidates = extensions::api::input_ime::SetCandidates; namespace SetCandidateWindowProperties = extensions::api::input_ime::SetCandidateWindowProperties; +namespace SetAssistiveWindowProperties = + extensions::api::input_ime::SetAssistiveWindowProperties; namespace ClearComposition = extensions::api::input_ime::ClearComposition; namespace OnCompositionBoundsChanged = extensions::api::input_method_private::OnCompositionBoundsChanged; @@ -54,9 +57,9 @@ namespace SetSelectionRange = extensions::api::input_method_private::SetSelectionRange; namespace FinishComposingText = extensions::api::input_method_private::FinishComposingText; -using ui::IMEEngineHandlerInterface; -using input_method::InputMethodEngineBase; using chromeos::InputMethodEngine; +using chromeos::InputMethodEngineBase; +using ui::IMEEngineHandlerInterface; namespace { const char kErrorEngineNotAvailable[] = "The engine is not available."; @@ -99,6 +102,43 @@ keyboard::KeyboardConfig GetKeyboardConfig() { return ChromeKeyboardControllerClient::Get()->GetKeyboardConfig(); } +ui::ime::AssistiveWindowType ConvertAssistiveWindowType( + input_ime::AssistiveWindowType type) { + switch (type) { + case input_ime::ASSISTIVE_WINDOW_TYPE_NONE: + return ui::ime::AssistiveWindowType::kNone; + case input_ime::ASSISTIVE_WINDOW_TYPE_UNDO: + return ui::ime::AssistiveWindowType::kUndoWindow; + } +} + +input_ime::AssistiveWindowButton ConvertAssistiveWindowButton( + const ui::ime::ButtonId id) { + switch (id) { + case ui::ime::ButtonId::kNone: + case ui::ime::ButtonId::kSmartInputsSettingLink: + case ui::ime::ButtonId::kSuggestion: + case ui::ime::ButtonId::kLearnMore: + return input_ime::ASSISTIVE_WINDOW_BUTTON_NONE; + case ui::ime::ButtonId::kUndo: + return input_ime::ASSISTIVE_WINDOW_BUTTON_UNDO; + case ui::ime::ButtonId::kAddToDictionary: + return input_ime::ASSISTIVE_WINDOW_BUTTON_ADDTODICTIONARY; + } +} + +input_ime::AssistiveWindowType ConvertAssistiveWindowType( + const ui::ime::AssistiveWindowType& type) { + switch (type) { + case ui::ime::AssistiveWindowType::kNone: + case ui::ime::AssistiveWindowType::kEmojiSuggestion: + case ui::ime::AssistiveWindowType::kPersonalInfoSuggestion: + return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_NONE; + case ui::ime::AssistiveWindowType::kUndoWindow: + return input_ime::AssistiveWindowType::ASSISTIVE_WINDOW_TYPE_UNDO; + } +} + class ImeObserverChromeOS : public ui::ImeObserver { public: ImeObserverChromeOS(const std::string& extension_id, Profile* profile) @@ -106,7 +146,7 @@ class ImeObserverChromeOS : public ui::ImeObserver { ~ImeObserverChromeOS() override = default; - // input_method::InputMethodEngineBase::Observer overrides. + // chromeos::InputMethodEngineBase::Observer overrides. void OnInputContextUpdate( const IMEEngineHandlerInterface::InputContext& context) override { if (extension_id_.empty() || @@ -257,6 +297,33 @@ class ImeObserverChromeOS : public ui::ImeObserver { ImeObserver::OnFocus(context); } + void OnAssistiveWindowButtonClicked( + const ui::ime::AssistiveWindowButton& button) override { + if (extension_id_.empty() || + !HasListener(input_ime::OnAssistiveWindowButtonClicked::kEventName)) { + return; + } + input_ime::OnAssistiveWindowButtonClicked::Details details; + details.button_id = ConvertAssistiveWindowButton(button.id); + details.window_type = ConvertAssistiveWindowType(button.window_type); + + std::unique_ptr<base::ListValue> args( + input_ime::OnAssistiveWindowButtonClicked::Create(details)); + DispatchEventToExtension( + extensions::events::INPUT_IME_ON_ASSISTIVE_WINDOW_BUTTON_CLICKED, + input_ime::OnAssistiveWindowButtonClicked::kEventName, std::move(args)); + } + + void OnSuggestionsChanged( + const std::vector<std::string>& suggestions) override { + std::unique_ptr<base::ListValue> args( + input_method_private::OnSuggestionsChanged::Create(suggestions)); + DispatchEventToExtension( + extensions::events::INPUT_IME_ON_SUGGESTIONS_CHANGED, + input_method_private::OnSuggestionsChanged::kEventName, + std::move(args)); + } + private: // ui::ImeObserver overrides. void DispatchEventToExtension( @@ -588,6 +655,33 @@ ExtensionFunction::ResponseAction InputImeHideInputViewFunction::Run() { } ExtensionFunction::ResponseAction +InputImeSetAssistiveWindowPropertiesFunction::Run() { + std::string error; + InputMethodEngine* engine = GetEngineIfActive( + Profile::FromBrowserContext(browser_context()), extension_id(), &error); + if (!engine) { + return RespondNow(Error(InformativeError(error, function_name()))); + } + std::unique_ptr<SetAssistiveWindowProperties::Params> parent_params( + SetAssistiveWindowProperties::Params::Create(*args_)); + const SetAssistiveWindowProperties::Params::Parameters& params = + parent_params->parameters; + const input_ime::AssistiveWindowProperties& window = params.properties; + chromeos::AssistiveWindowProperties assistive_window; + + assistive_window.visible = window.visible; + assistive_window.type = ConvertAssistiveWindowType(window.type); + if (window.announce_string) + assistive_window.announce_string = *window.announce_string; + + engine->SetAssistiveWindowProperties(params.context_id, assistive_window, + &error); + if (!error.empty()) + return RespondNow(Error(InformativeError(error, function_name()))); + return RespondNow(OneArgument(std::make_unique<base::Value>(true))); +} + +ExtensionFunction::ResponseAction InputImeSetCandidateWindowPropertiesFunction::Run() { std::unique_ptr<SetCandidateWindowProperties::Params> parent_params( SetCandidateWindowProperties::Params::Create(*args_)); diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.h b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.h index 36c279aba15..aa03b45186b 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.h +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_chromeos.h @@ -68,6 +68,18 @@ class InputImeSetCursorPositionFunction : public ExtensionFunction { ResponseAction Run() override; }; +class InputImeSetAssistiveWindowPropertiesFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("input.ime.setAssistiveWindowProperties", + INPUT_IME_SETASSISTIVEWINDOWPROPERTIES) + + protected: + ~InputImeSetAssistiveWindowPropertiesFunction() override = default; + + // ExtensionFunction: + ResponseAction Run() override; +}; + class InputImeSetMenuItemsFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("input.ime.setMenuItems", INPUT_IME_SETMENUITEMS) @@ -176,7 +188,7 @@ class InputImeEventRouter : public InputImeEventRouterBase { void UnregisterAllImes(const std::string& extension_id); chromeos::InputMethodEngine* GetEngine(const std::string& extension_id); - input_method::InputMethodEngineBase* GetEngineIfActive( + chromeos::InputMethodEngineBase* GetEngineIfActive( const std::string& extension_id, std::string* error) override; diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc deleted file mode 100644 index 64c0f13ffe2..00000000000 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.cc +++ /dev/null @@ -1,416 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This file is for non-chromeos (win & linux) functions, such as -// chrome.input.ime.activate, chrome.input.ime.createWindow and -// chrome.input.ime.onSelectionChanged. -// TODO(azurewei): May refactor the code structure by using delegate or -// redesign the API to remove this platform-specific file in the future. - -#include "chrome/browser/extensions/api/input_ime/input_ime_api.h" - -#include <memory> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/values.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/input_method/input_method_engine.h" -#include "chrome/common/extensions/api/input_ime.h" -#include "extensions/browser/extension_prefs.h" -#include "ui/base/ime/ime_bridge.h" -#include "ui/gfx/geometry/rect.h" - -namespace input_ime = extensions::api::input_ime; -namespace OnCompositionBoundsChanged = - extensions::api::input_ime::OnCompositionBoundsChanged; -using ui::IMEEngineHandlerInterface; -using input_method::InputMethodEngine; -using input_method::InputMethodEngineBase; - -namespace input_ime = extensions::api::input_ime; - -namespace { - -const char kErrorEngineNotActive[] = "The engine is not active."; -const char kErrorRouterNotAvailable[] = "The router is not available."; -const char kErrorPermissionDenied[] = "User denied permission."; -const char kErrorCouldNotFindActiveBrowser[] = - "Cannot find the active browser."; -const char kErrorNotCalledFromUserAction[] = - "This API is only allowed to be called from a user action."; - -// A preference determining whether to hide the warning bubble next time. -// Not used for now. -const char kPrefWarningBubbleNeverShow[] = "skip_ime_warning_bubble"; - -// A preference to see whether the API has never been called, or it's the first -// time to call since loaded the extension. -// This is used from make an exception for user_gesture checking: no need the -// check when restarting chrome. -const char kPrefNeverActivatedSinceLoaded[] = "never_activated_since_loaded"; - -// A preference to see whether the extension is the last active extension. -const char kPrefLastActiveEngine[] = "last_activated_ime_engine"; - -class ImeBridgeObserver : public ui::IMEBridgeObserver { - public: - void OnRequestSwitchEngine() override { - Browser* browser = chrome::FindLastActive(); - if (!browser) - return; - extensions::InputImeEventRouter* router = - extensions::GetInputImeEventRouter(browser->profile()); - if (!router) - return; - ui::IMEBridge::Get()->SetCurrentEngineHandler(router->active_engine()); - } - void OnInputContextHandlerChanged() override {} -}; - -class ImeObserverNonChromeOS : public ui::ImeObserver { - public: - ImeObserverNonChromeOS(const std::string& extension_id, Profile* profile) - : ImeObserver(extension_id, profile) {} - - ~ImeObserverNonChromeOS() override = default; - - void OnCompositionBoundsChanged( - const std::vector<gfx::Rect>& bounds) override { - if (extension_id_.empty() || bounds.empty() || - !HasListener(OnCompositionBoundsChanged::kEventName)) - return; - - std::vector<input_ime::Bounds> bounds_list; - for (const auto& bound : bounds) { - input_ime::Bounds bounds_value; - bounds_value.left = bound.x(); - bounds_value.top = bound.y(); - bounds_value.width = bound.width(); - bounds_value.height = bound.height(); - bounds_list.push_back(std::move(bounds_value)); - } - - std::unique_ptr<base::ListValue> args( - OnCompositionBoundsChanged::Create(bounds_list)); - - DispatchEventToExtension( - extensions::events::INPUT_IME_ON_COMPOSITION_BOUNDS_CHANGED, - OnCompositionBoundsChanged::kEventName, std::move(args)); - } - - private: - // ImeObserver overrides. - void DispatchEventToExtension( - extensions::events::HistogramValue histogram_value, - const std::string& event_name, - std::unique_ptr<base::ListValue> args) override { - auto event = std::make_unique<extensions::Event>( - histogram_value, event_name, std::move(args), profile_); - extensions::EventRouter::Get(profile_) - ->DispatchEventToExtension(extension_id_, std::move(event)); - } - - std::string GetCurrentScreenType() override { return "normal"; } - - DISALLOW_COPY_AND_ASSIGN(ImeObserverNonChromeOS); -}; - -} // namespace - -namespace extensions { - -InputMethodEngine* GetEngineIfActive(content::BrowserContext* browser_context, - const std::string& extension_id, - std::string* error) { - Profile* profile = Profile::FromBrowserContext(browser_context); - InputImeEventRouter* event_router = GetInputImeEventRouter(profile); - DCHECK(event_router) << kErrorRouterNotAvailable; - InputMethodEngine* engine = static_cast<InputMethodEngine*>( - event_router->GetEngineIfActive(extension_id, error)); - return engine; -} - -void InputImeAPI::OnExtensionLoaded(content::BrowserContext* browser_context, - const Extension* extension) { - // No-op if called multiple times. - ui::IMEBridge::Initialize(); - if (!observer_) { - observer_ = std::make_unique<ImeBridgeObserver>(); - ui::IMEBridge::Get()->AddObserver(observer_.get()); - } - - // Set the preference kPrefNeverActivatedSinceLoaded true to indicate - // input.ime.activate API has been never called since loaded. - Profile* profile = Profile::FromBrowserContext(browser_context); - ExtensionPrefs::Get(profile)->UpdateExtensionPref( - extension->id(), kPrefNeverActivatedSinceLoaded, - std::make_unique<base::Value>(true)); -} - -void InputImeAPI::OnExtensionUnloaded(content::BrowserContext* browser_context, - const Extension* extension, - UnloadedExtensionReason reason) { - InputImeEventRouter* event_router = - GetInputImeEventRouter(Profile::FromBrowserContext(browser_context)); - if (event_router) { - // Records the extension is not the last active IME engine. - ExtensionPrefs::Get(Profile::FromBrowserContext(browser_context)) - ->UpdateExtensionPref(extension->id(), kPrefLastActiveEngine, - std::make_unique<base::Value>(false)); - event_router->DeleteInputMethodEngine(extension->id()); - } -} - -void InputImeAPI::OnListenerAdded(const EventListenerInfo& details) {} - -InputImeEventRouter::InputImeEventRouter(Profile* profile) - : InputImeEventRouterBase(profile), active_engine_(nullptr) {} - -InputImeEventRouter::~InputImeEventRouter() { - if (active_engine_) - DeleteInputMethodEngine(active_engine_->GetExtensionId()); -} - -InputMethodEngineBase* InputImeEventRouter::GetEngineIfActive( - const std::string& extension_id, - std::string* error) { - if (ui::IMEBridge::Get()->GetCurrentEngineHandler() && active_engine_ && - active_engine_->GetExtensionId() == extension_id) { - return active_engine_; - } else { - LOG(WARNING) << kErrorEngineNotActive << " extension id: " << extension_id; - *error = kErrorEngineNotActive; - return nullptr; - } -} - -void InputImeEventRouter::SetActiveEngine(const std::string& extension_id) { - // Records the extension is the last active IME engine. - ExtensionPrefs::Get(GetProfile()) - ->UpdateExtensionPref(extension_id, kPrefLastActiveEngine, - std::make_unique<base::Value>(true)); - if (active_engine_) { - if (active_engine_->GetExtensionId() == extension_id) { - active_engine_->Enable(std::string()); - ui::IMEBridge::Get()->SetCurrentEngineHandler(active_engine_); - return; - } - // Records the extension is not the last active IME engine. - ExtensionPrefs::Get(GetProfile()) - ->UpdateExtensionPref(active_engine_->GetExtensionId(), - kPrefLastActiveEngine, - std::make_unique<base::Value>(false)); - DeleteInputMethodEngine(active_engine_->GetExtensionId()); - } - - std::unique_ptr<input_method::InputMethodEngine> engine( - new input_method::InputMethodEngine()); - std::unique_ptr<InputMethodEngineBase::Observer> observer( - new ImeObserverNonChromeOS(extension_id, GetProfile())); - engine->Initialize(std::move(observer), extension_id.c_str(), GetProfile()); - engine->Enable(std::string()); - active_engine_ = engine.release(); - ui::IMEBridge::Get()->SetCurrentEngineHandler(active_engine_); -} - -void InputImeEventRouter::DeleteInputMethodEngine( - const std::string& extension_id) { - if (active_engine_ && active_engine_->GetExtensionId() == extension_id) { - active_engine_->Disable(); - ui::IMEBridge::Get()->SetCurrentEngineHandler(nullptr); - delete active_engine_; - active_engine_ = nullptr; - } -} - -// static -bool InputImeActivateFunction::disable_bubble_for_testing_ = false; - -ExtensionFunction::ResponseAction InputImeActivateFunction::Run() { - Profile* profile = Profile::FromBrowserContext(browser_context()); - InputImeEventRouter* event_router = GetInputImeEventRouter(profile); - if (!event_router) - return RespondNow( - Error(InformativeError(kErrorEngineNotActive, function_name()))); - - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile); - - bool never_activated_since_loaded = false; - bool last_active_ime_engine = false; - - if (prefs->ReadPrefAsBoolean(extension_id(), kPrefNeverActivatedSinceLoaded, - &never_activated_since_loaded) && - never_activated_since_loaded && - prefs->ReadPrefAsBoolean(extension_id(), kPrefLastActiveEngine, - &last_active_ime_engine) && - last_active_ime_engine) { - // If the extension is the last active IME engine, and the API is called at - // loading the extension, we can tell the API is called from restarting - // chrome. No need for user gesture checking. - event_router->SetActiveEngine(extension_id()); - ExtensionPrefs::Get(profile)->UpdateExtensionPref( - extension_id(), kPrefNeverActivatedSinceLoaded, - std::make_unique<base::Value>(false)); - return RespondNow(NoArguments()); - } - // The API has already been called at least once. - ExtensionPrefs::Get(profile)->UpdateExtensionPref( - extension_id(), kPrefNeverActivatedSinceLoaded, - std::make_unique<base::Value>(false)); - - // Otherwise, this API is only allowed to be called from a user action. - if (!user_gesture()) - return RespondNow(Error( - InformativeError(kErrorNotCalledFromUserAction, function_name()))); - - // Disable using the warning bubble for testing. - if (disable_bubble_for_testing_) { - event_router->SetActiveEngine(extension_id()); - return RespondNow(NoArguments()); - } - - // Disables the warning bubble since we don't need run-time checking anymore. - bool warning_bubble_never_show = true; - if (warning_bubble_never_show) { - // If user allows to activate the extension without showing the warning - // bubble, sets the active engine directly. - // Otherwise, the extension will be activated when the user presses the 'OK' - // button on the warning bubble. - event_router->SetActiveEngine(extension_id()); - return RespondNow(NoArguments()); - } - - // TODO(azurewei): Remove the warning bubble related codes. - Browser* browser = chrome::FindLastActiveWithProfile(profile); - if (!browser) - return RespondNow(Error( - InformativeError(kErrorCouldNotFindActiveBrowser, function_name()))); - - // Creates and shows the warning bubble. The ImeWarningBubble is self-owned, - // it deletes itself when closed. - browser->window()->ShowImeWarningBubble( - extension(), - base::Bind(&InputImeActivateFunction::OnPermissionBubbleFinished, this)); - return RespondLater(); -} - -void InputImeActivateFunction::OnPermissionBubbleFinished( - ImeWarningBubblePermissionStatus status) { - if (status == ImeWarningBubblePermissionStatus::DENIED || - status == ImeWarningBubblePermissionStatus::ABORTED) { - // Fails to activate the extension. - Respond(Error(InformativeError(kErrorPermissionDenied, function_name()))); - return; - } - - DCHECK(status == ImeWarningBubblePermissionStatus::GRANTED || - status == ImeWarningBubblePermissionStatus::GRANTED_AND_NEVER_SHOW); - - // Activates this extension if user presses the 'OK' button. - Profile* profile = Profile::FromBrowserContext(browser_context()); - InputImeEventRouter* event_router = GetInputImeEventRouter(profile); - if (!event_router) { - Respond(Error(InformativeError(kErrorEngineNotActive, function_name()))); - return; - } - event_router->SetActiveEngine(extension_id()); - - if (status == ImeWarningBubblePermissionStatus::GRANTED_AND_NEVER_SHOW) { - // Updates the extension preference if user checks the 'Never show this - // again' check box. So we can activate the extension directly next time. - ExtensionPrefs::Get(profile)->UpdateExtensionPref( - extension_id(), kPrefWarningBubbleNeverShow, - std::make_unique<base::Value>(true)); - } - - Respond(NoArguments()); -} - -ExtensionFunction::ResponseAction InputImeDeactivateFunction::Run() { - std::string error; - InputMethodEngine* engine = - GetEngineIfActive(browser_context(), extension_id(), &error); - if (!engine) { - return RespondNow(Error(InformativeError(error, function_name()))); - } - ui::IMEBridge::Get()->SetCurrentEngineHandler(nullptr); - if (engine) - engine->CloseImeWindows(); - return RespondNow(NoArguments()); -} - -ExtensionFunction::ResponseAction InputImeCreateWindowFunction::Run() { - // Using input_ime::CreateWindow::Params::Create() causes the link errors on - // Windows, only if the method name is 'createWindow'. - // So doing the by-hand parameter unpacking here. - // TODO(shuchen,rdevlin.cronin): investigate the root cause for the link - // errors. - const base::DictionaryValue* params = nullptr; - args_->GetDictionary(0, ¶ms); - EXTENSION_FUNCTION_VALIDATE(params); - input_ime::CreateWindowOptions options; - input_ime::CreateWindowOptions::Populate(*params, &options); - - gfx::Rect bounds(0, 0, 100, 100); // Default bounds. - if (options.bounds.get()) { - bounds.set_x(options.bounds->left); - bounds.set_y(options.bounds->top); - bounds.set_width(options.bounds->width); - bounds.set_height(options.bounds->height); - } - - std::string error; - InputMethodEngine* engine = - GetEngineIfActive(browser_context(), extension_id(), &error); - if (!engine) - return RespondNow(Error(InformativeError(error, function_name()))); - - int frame_id = engine->CreateImeWindow( - extension(), render_frame_host(), - options.url.get() ? *options.url : url::kAboutBlankURL, - options.window_type == input_ime::WINDOW_TYPE_FOLLOWCURSOR - ? ui::ImeWindow::FOLLOW_CURSOR - : ui::ImeWindow::NORMAL, - bounds, &error); - if (!frame_id) - return RespondNow(Error(InformativeError(error, function_name()))); - - std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue()); - result->Set("frameId", std::make_unique<base::Value>(frame_id)); - - return RespondNow(OneArgument(std::move(result))); -} - -ExtensionFunction::ResponseAction InputImeShowWindowFunction::Run() { - std::string error; - InputMethodEngine* engine = - GetEngineIfActive(browser_context(), extension_id(), &error); - if (!engine) - return RespondNow(Error(InformativeError(error, function_name()))); - - std::unique_ptr<api::input_ime::ShowWindow::Params> params( - api::input_ime::ShowWindow::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - engine->ShowImeWindow(params->window_id); - return RespondNow(NoArguments()); -} - -ExtensionFunction::ResponseAction InputImeHideWindowFunction::Run() { - std::string error; - InputMethodEngine* engine = - GetEngineIfActive(browser_context(), extension_id(), &error); - if (!engine) - return RespondNow(Error(InformativeError(error, function_name()))); - - std::unique_ptr<api::input_ime::HideWindow::Params> params( - api::input_ime::HideWindow::Params::Create(*args_)); - EXTENSION_FUNCTION_VALIDATE(params.get()); - engine->HideImeWindow(params->window_id); - return RespondNow(NoArguments()); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.h b/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.h deleted file mode 100644 index ff9a920c06b..00000000000 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_api_nonchromeos.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_EXTENSIONS_API_INPUT_IME_INPUT_IME_API_NONCHROMEOS_H_ -#define CHROME_BROWSER_EXTENSIONS_API_INPUT_IME_INPUT_IME_API_NONCHROMEOS_H_ - -#include "chrome/browser/extensions/api/input_ime/input_ime_event_router_base.h" -#include "chrome/browser/profiles/profile.h" -#include "extensions/browser/extension_function.h" - -class Profile; - -namespace input_method { -class InputMethodEngine; -} // namespace input_method - -// The status indicates whether the permission has been granted or denied when -// the IME warning bubble has been closed. -enum class ImeWarningBubblePermissionStatus { - GRANTED, - GRANTED_AND_NEVER_SHOW, - DENIED, - ABORTED -}; - -namespace extensions { - -class InputImeEventRouterBase; - -class InputImeEventRouter : public InputImeEventRouterBase { - public: - explicit InputImeEventRouter(Profile* profile); - ~InputImeEventRouter() override; - - // Gets the input method engine if the extension is active. - input_method::InputMethodEngineBase* GetEngineIfActive( - const std::string& extension_id, - std::string* error) override; - - // Actives the extension with new input method engine, and deletes the - // previous engine if another extension was active. - void SetActiveEngine(const std::string& extension_id); - - input_method::InputMethodEngine* active_engine() { - return active_engine_; - } - - // Deletes the current input method engine of the specific extension. - void DeleteInputMethodEngine(const std::string& extension_id); - - private: - // The active input method engine. - input_method::InputMethodEngine* active_engine_; - - DISALLOW_COPY_AND_ASSIGN(InputImeEventRouter); -}; - -class InputImeCreateWindowFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("input.ime.createWindow", INPUT_IME_CREATEWINDOW) - - protected: - ~InputImeCreateWindowFunction() override = default; - - // ExtensionFunction: - ExtensionFunction::ResponseAction Run() override; -}; - -class InputImeShowWindowFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("input.ime.showWindow", INPUT_IME_SHOWWINDOW) - - protected: - ~InputImeShowWindowFunction() override = default; - - // ExtensionFunction: - ExtensionFunction::ResponseAction Run() override; -}; - -class InputImeHideWindowFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("input.ime.hideWindow", INPUT_IME_HIDEWINDOW) - - protected: - ~InputImeHideWindowFunction() override = default; - - // ExtensionFunction: - ExtensionFunction::ResponseAction Run() override; -}; - -class InputImeActivateFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("input.ime.activate", INPUT_IME_ACTIVATE) - - // During testing we can disable showing a warning bubble by setting this flag - // to true, so that the extension can be activated directly. - static bool disable_bubble_for_testing_; - - protected: - ~InputImeActivateFunction() override = default; - - // ExtensionFunction: - ResponseAction Run() override; - - private: - // Called when the user finishes interacting with the warning bubble. - // |status| indicates whether the user allows or denies to activate the - // extension. - void OnPermissionBubbleFinished(ImeWarningBubblePermissionStatus status); -}; - -class InputImeDeactivateFunction : public ExtensionFunction { - public: - DECLARE_EXTENSION_FUNCTION("input.ime.deactivate", INPUT_IME_DEACTIVATE) - - protected: - ~InputImeDeactivateFunction() override = default; - - // ExtensionFunction: - ResponseAction Run() override; -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_INPUT_IME_INPUT_IME_API_NONCHROMEOS_H_ diff --git a/chromium/chrome/browser/extensions/api/input_ime/input_ime_event_router_base.h b/chromium/chrome/browser/extensions/api/input_ime/input_ime_event_router_base.h index 3d832e8e3a9..6d3edec518b 100644 --- a/chromium/chrome/browser/extensions/api/input_ime/input_ime_event_router_base.h +++ b/chromium/chrome/browser/extensions/api/input_ime/input_ime_event_router_base.h @@ -10,8 +10,8 @@ #include <utility> #include "base/macros.h" +#include "chrome/browser/chromeos/input_method/input_method_engine_base.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/input_method/input_method_engine_base.h" #include "ui/base/ime/ime_engine_handler_interface.h" namespace extensions { @@ -22,7 +22,7 @@ class InputImeEventRouterBase { virtual ~InputImeEventRouterBase(); // Gets the input method engine if the extension is active. - virtual input_method::InputMethodEngineBase* GetEngineIfActive( + virtual chromeos::InputMethodEngineBase* GetEngineIfActive( const std::string& extension_id, std::string* error) = 0; 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 340c2c0f3c6..5cc290e45f8 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 @@ -188,7 +188,8 @@ std::vector<std::string> GetSortedThirdPartyIMEs( LanguageSettingsPrivateGetLanguageListFunction:: LanguageSettingsPrivateGetLanguageListFunction() - : chrome_details_(this) {} + : chrome_details_(this), + language_list_(std::make_unique<base::ListValue>()) {} LanguageSettingsPrivateGetLanguageListFunction:: ~LanguageSettingsPrivateGetLanguageListFunction() = default; @@ -212,7 +213,7 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() { std::move(spellcheck_languages)); // Build the language list. - std::unique_ptr<base::ListValue> language_list(new base::ListValue); + language_list_->Clear(); #if defined(OS_CHROMEOS) const std::unordered_set<std::string> allowed_ui_locales( GetAllowedLanguages(chrome_details_.GetProfile()->GetPrefs())); @@ -243,7 +244,7 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() { } #endif // defined(OS_CHROMEOS) - language_list->Append(language.ToValue()); + language_list_->Append(language.ToValue()); } #if defined(OS_CHROMEOS) @@ -255,13 +256,54 @@ LanguageSettingsPrivateGetLanguageListFunction::Run() { language.code = chromeos::extension_ime_util::kArcImeLanguage; language.display_name = l10n_util::GetStringUTF8(IDS_SETTINGS_LANGUAGES_KEYBOARD_APPS); - language_list->Append(language.ToValue()); + language_list_->Append(language.ToValue()); } #endif // defined(OS_CHROMEOS) - return RespondNow(OneArgument(std::move(language_list))); +#if defined(OS_WIN) + if (spellcheck::UseBrowserSpellChecker()) { + if (!base::FeatureList::IsEnabled( + spellcheck::kWinDelaySpellcheckServiceInit)) { + // Platform dictionary support already determined at browser startup. + UpdateSupportedPlatformDictionaries(); + } else { + // Asynchronously load the dictionaries to determine platform support. + SpellcheckService* service = + SpellcheckServiceFactory::GetForContext(browser_context()); + AddRef(); // Balanced in OnDictionariesInitialized + service->InitializeDictionaries( + base::BindOnce(&LanguageSettingsPrivateGetLanguageListFunction:: + OnDictionariesInitialized, + base::Unretained(this))); + return RespondLater(); + } + } +#endif // defined(OS_WIN) + + return RespondNow(OneArgument(std::move(language_list_))); } +#if defined(OS_WIN) +void LanguageSettingsPrivateGetLanguageListFunction:: + OnDictionariesInitialized() { + UpdateSupportedPlatformDictionaries(); + Respond(OneArgument(std::move(language_list_))); + // Matches the AddRef in Run(). + Release(); +} + +void LanguageSettingsPrivateGetLanguageListFunction:: + UpdateSupportedPlatformDictionaries() { + SpellcheckService* service = + SpellcheckServiceFactory::GetForContext(browser_context()); + for (auto& language_val : language_list_->GetList()) { + if (service->UsesWindowsDictionary(*language_val.FindStringKey("code"))) { + language_val.SetBoolKey("supportsSpellcheck", new bool(true)); + } + } +} +#endif // defined(OS_WIN) + LanguageSettingsPrivateEnableLanguageFunction:: LanguageSettingsPrivateEnableLanguageFunction() : chrome_details_(this) {} @@ -291,7 +333,6 @@ LanguageSettingsPrivateEnableLanguageFunction::Run() { } translate_prefs->AddToLanguageList(language_code, /*force_blocked=*/false); - translate_prefs->ResetRecentTargetLanguage(); return RespondNow(NoArguments()); } @@ -325,7 +366,9 @@ LanguageSettingsPrivateDisableLanguageFunction::Run() { } translate_prefs->RemoveFromLanguageList(language_code); - translate_prefs->ResetRecentTargetLanguage(); + if (language_code == translate_prefs->GetRecentTargetLanguage()) { + translate_prefs->ResetRecentTargetLanguage(); + } return RespondNow(NoArguments()); } @@ -355,7 +398,6 @@ LanguageSettingsPrivateSetEnableTranslationForLanguageFunction::Run() { } else { translate_prefs->BlockLanguage(language_code); } - translate_prefs->ResetRecentTargetLanguage(); return RespondNow(NoArguments()); } @@ -408,7 +450,6 @@ LanguageSettingsPrivateMoveLanguageFunction::Run() { const int offset = 1; translate_prefs->RearrangeLanguage(language_code, where, offset, supported_language_codes); - translate_prefs->ResetRecentTargetLanguage(); return RespondNow(NoArguments()); } @@ -450,8 +491,8 @@ LanguageSettingsPrivateGetSpellcheckWordsFunction::Run() { return RespondLater(); } -void -LanguageSettingsPrivateGetSpellcheckWordsFunction::OnCustomDictionaryLoaded() { +void LanguageSettingsPrivateGetSpellcheckWordsFunction:: + OnCustomDictionaryLoaded() { SpellcheckService* service = SpellcheckServiceFactory::GetForContext(browser_context()); service->GetCustomDictionary()->RemoveObserver(this); @@ -459,9 +500,9 @@ LanguageSettingsPrivateGetSpellcheckWordsFunction::OnCustomDictionaryLoaded() { Release(); } -void -LanguageSettingsPrivateGetSpellcheckWordsFunction::OnCustomDictionaryChanged( - const SpellcheckCustomDictionary::Change& dictionary_change) { +void LanguageSettingsPrivateGetSpellcheckWordsFunction:: + OnCustomDictionaryChanged( + const SpellcheckCustomDictionary::Change& dictionary_change) { NOTREACHED() << "SpellcheckCustomDictionary::Observer: " "OnCustomDictionaryChanged() called before " "OnCustomDictionaryLoaded()"; @@ -536,8 +577,7 @@ LanguageSettingsPrivateRemoveSpellcheckWordFunction::Run() { LanguageSettingsPrivateGetTranslateTargetLanguageFunction:: LanguageSettingsPrivateGetTranslateTargetLanguageFunction() - : chrome_details_(this) { -} + : chrome_details_(this) {} LanguageSettingsPrivateGetTranslateTargetLanguageFunction:: ~LanguageSettingsPrivateGetTranslateTargetLanguageFunction() = default; @@ -615,8 +655,7 @@ LanguageSettingsPrivateGetInputMethodListsFunction::Run() { InputMethodDescriptors ext_ime_descriptors; ime_state->GetInputMethodExtensions(&ext_ime_descriptors); PopulateInputMethodListFromDescriptors( - ext_ime_descriptors, - &input_method_lists.third_party_extension_imes); + ext_ime_descriptors, &input_method_lists.third_party_extension_imes); } return RespondNow(OneArgument(input_method_lists.ToValue())); diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h index 2e745eb503b..4a8a64b7004 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_EXTENSIONS_API_LANGUAGE_SETTINGS_PRIVATE_LANGUAGE_SETTINGS_PRIVATE_API_H_ #include "base/macros.h" +#include "build/build_config.h" #include "chrome/browser/extensions/chrome_extension_function_details.h" #include "chrome/browser/spellchecker/spellcheck_custom_dictionary.h" #include "extensions/browser/extension_function.h" @@ -26,9 +27,16 @@ class LanguageSettingsPrivateGetLanguageListFunction // ExtensionFunction overrides. ResponseAction Run() override; +#if defined(OS_WIN) + void OnDictionariesInitialized(); + void UpdateSupportedPlatformDictionaries(); +#endif // defined(OS_WIN) + private: ChromeExtensionFunctionDetails chrome_details_; + std::unique_ptr<base::ListValue> language_list_; + DISALLOW_COPY_AND_ASSIGN(LanguageSettingsPrivateGetLanguageListFunction); }; 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 6ba4543a9a5..e073c83c7ba 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 @@ -9,16 +9,21 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_refptr.h" +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_api.h" #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h" #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_factory.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/browser/spellchecker/spellcheck_factory.h" +#include "chrome/browser/spellchecker/spellcheck_service.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/test_browser_window.h" #include "chrome/test/base/testing_profile.h" #include "components/crx_file/id_util.h" #include "components/language/core/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_features.h" #include "extensions/browser/event_router_factory.h" #include "extensions/browser/extension_prefs.h" @@ -82,6 +87,11 @@ std::unique_ptr<KeyedService> BuildLanguageSettingsPrivateDelegate( return std::make_unique<MockLanguageSettingsPrivateDelegate>(profile); } +std::unique_ptr<KeyedService> BuildSpellcheckService( + content::BrowserContext* profile) { + return std::make_unique<SpellcheckService>(static_cast<Profile*>(profile)); +} + } // namespace class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase { @@ -89,6 +99,27 @@ class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase { LanguageSettingsPrivateApiTest() = default; ~LanguageSettingsPrivateApiTest() override = default; + protected: + void RunGetLanguageListTest(); + + virtual void InitFeatures() { +#if defined(OS_WIN) + // Force Windows hybrid spellcheck to be enabled. + feature_list_.InitAndEnableFeature(spellcheck::kWinUseBrowserSpellChecker); +#endif // defined(OS_WIN) + } + +#if defined(OS_WIN) + virtual void AddSpellcheckLanguagesForTesting( + const std::vector<std::string>& spellcheck_languages_for_testing) { + SpellcheckServiceFactory::GetInstance() + ->GetForContext(profile()) + ->InitWindowsDictionaryLanguages(spellcheck_languages_for_testing); + } + + base::test::ScopedFeatureList feature_list_; +#endif // defined(OS_WIN) + private: void SetUp() override { ExtensionServiceTestBase::SetUp(); @@ -96,8 +127,14 @@ class LanguageSettingsPrivateApiTest : public ExtensionServiceTestBase { EventRouterFactory::GetInstance()->SetTestingFactory( profile(), base::BindRepeating(&BuildEventRouter)); + InitFeatures(); + LanguageSettingsPrivateDelegateFactory::GetInstance()->SetTestingFactory( profile(), base::BindRepeating(&BuildLanguageSettingsPrivateDelegate)); + + // Use SetTestingFactoryAndUse to force creation and initialization. + SpellcheckServiceFactory::GetInstance()->SetTestingFactoryAndUse( + profile(), base::BindRepeating(&BuildSpellcheckService)); } std::unique_ptr<TestBrowserWindow> browser_window_; @@ -146,6 +183,121 @@ TEST_F(LanguageSettingsPrivateApiTest, GetSpellcheckDictionaryStatusesTest) { EXPECT_EQ(expected, *actual); } +TEST_F(LanguageSettingsPrivateApiTest, GetLanguageListTest) { + RunGetLanguageListTest(); +} + +void LanguageSettingsPrivateApiTest::RunGetLanguageListTest() { + struct LanguageToTest { + std::string accept_language; + std::string windows_dictionary_name; // Empty string indicates to not use + // fake Windows dictionary + bool is_preferred_language; + bool is_spellcheck_support_expected; + }; + + std::vector<LanguageToTest> languages_to_test = { + // Languages with both Windows and Hunspell spellcheck support. + // GetLanguageList should always report spellchecking to be supported for + // these languages, regardless of whether a language pack is installed or + // if it is a preferred language. + {"fr", "fr-FR", true, true}, + {"de", "de-DE", false, true}, + {"es-MX", "", true, true}, + {"fa", "", false, true}, + {"gl", "", true, false}, + {"zu", "", false, false}, + // Finnish with Filipino language pack (string in string). + {"fi", "fil", true, false}, + // Sesotho with Asturian language pack (string in string). + {"st", "ast", true, false}, + }; + + // A few more test cases for non-Hunspell languages. These languages do have + // Windows spellcheck support depending on the OS version. GetLanguageList + // only reports spellchecking is supported for these languages if the language + // pack is installed. +#if defined(OS_WIN) + if (spellcheck::WindowsVersionSupportsSpellchecker()) { + languages_to_test.push_back({"ar", "ar-SA", true, true}); + languages_to_test.push_back({"bn", "bn-IN", false, true}); + } else { + languages_to_test.push_back({"ar", "ar-SA", true, false}); + languages_to_test.push_back({"bn", "bn-IN", false, false}); + } +#else + languages_to_test.push_back({"ar", "ar-SA", true, false}); + languages_to_test.push_back({"bn", "bn-IN", false, false}); +#endif // defined(OS_WIN) + + // Initialize accept languages prefs. + std::vector<std::string> accept_languages; + for (auto& language_to_test : languages_to_test) { + if (language_to_test.is_preferred_language) { + accept_languages.push_back(language_to_test.accept_language); + } + } + + std::string accept_languages_string = base::JoinString(accept_languages, ","); + DVLOG(2) << "Setting accept languages preferences to: " + << accept_languages_string; + profile()->GetPrefs()->SetString(language::prefs::kAcceptLanguages, + accept_languages_string); + +#if defined(OS_WIN) + // Add fake Windows dictionaries using InitWindowsDictionaryLanguages. + std::vector<std::string> windows_spellcheck_languages_for_testing; + for (auto& language_to_test : languages_to_test) { + if (!language_to_test.windows_dictionary_name.empty()) { + windows_spellcheck_languages_for_testing.push_back( + language_to_test.windows_dictionary_name); + DVLOG(2) << "Will set fake Windows spellcheck dictionary for testing: " + << language_to_test.windows_dictionary_name; + } + } + + AddSpellcheckLanguagesForTesting(windows_spellcheck_languages_for_testing); +#endif // defined(OS_WIN) + + auto function = + base::MakeRefCounted<LanguageSettingsPrivateGetLanguageListFunction>(); + + std::unique_ptr<base::Value> result = + api_test_utils::RunFunctionAndReturnSingleResult(function.get(), "[]", + profile()); + + ASSERT_NE(nullptr, result) << function->GetError(); + EXPECT_TRUE(result->is_list()); + + size_t languages_to_test_found_count = 0; + for (auto& language_val : result->GetList()) { + EXPECT_TRUE(language_val.is_dict()); + std::string* language_code_ptr = language_val.FindStringKey("code"); + ASSERT_NE(nullptr, language_code_ptr); + std::string language_code = *language_code_ptr; + EXPECT_FALSE(language_code.empty()); + + const base::Optional<bool> maybe_supports_spellcheck = + language_val.FindBoolKey("supportsSpellcheck"); + const bool supports_spellcheck = maybe_supports_spellcheck.has_value() + ? maybe_supports_spellcheck.value() + : false; + + for (auto& language_to_test : languages_to_test) { + if (language_to_test.accept_language == language_code) { + DVLOG(2) << "*** Found language code being tested=" << language_code + << ", supportsSpellcheck=" << supports_spellcheck << " ***"; + EXPECT_EQ(language_to_test.is_spellcheck_support_expected, + supports_spellcheck); + languages_to_test_found_count++; + break; + } + } + } + + EXPECT_EQ(languages_to_test.size(), languages_to_test_found_count); +} + #if defined(OS_CHROMEOS) namespace { @@ -337,4 +489,34 @@ TEST_F(LanguageSettingsPrivateApiTest, RemoveInputMethodTest) { #endif // OS_CHROMEOS +#if defined(OS_WIN) +class LanguageSettingsPrivateApiTestDelayInit + : public LanguageSettingsPrivateApiTest { + public: + LanguageSettingsPrivateApiTestDelayInit() = default; + + protected: + void InitFeatures() override { + // Force Windows hybrid spellcheck and delayed initialization of the + // spellcheck service to be enabled. + feature_list_.InitWithFeatures( + /*enabled_features=*/{spellcheck::kWinUseBrowserSpellChecker, + spellcheck::kWinDelaySpellcheckServiceInit}, + /*disabled_features=*/{}); + } + + void AddSpellcheckLanguagesForTesting( + const std::vector<std::string>& spellcheck_languages_for_testing) + override { + SpellcheckServiceFactory::GetInstance() + ->GetForContext(profile()) + ->AddSpellcheckLanguagesForTesting(spellcheck_languages_for_testing); + } +}; + +TEST_F(LanguageSettingsPrivateApiTestDelayInit, GetLanguageListTest) { + RunGetLanguageListTest(); +} +#endif // defined(OS_WIN) + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc index a35e419d0e0..5a581df3758 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate_unittest.cc @@ -5,12 +5,15 @@ #include "chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h" #include "base/bind.h" +#include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_service_test_base.h" #include "chrome/browser/spellchecker/spellcheck_factory.h" #include "chrome/browser/spellchecker/spellcheck_service.h" #include "chrome/test/base/testing_profile.h" #include "components/spellcheck/browser/pref_names.h" +#include "components/spellcheck/common/spellcheck_features.h" #include "components/spellcheck/spellcheck_buildflags.h" #include "content/public/browser/notification_service.h" #include "extensions/browser/event_router_factory.h" @@ -45,6 +48,12 @@ class LanguageSettingsPrivateDelegateTest EventRouterFactory::GetInstance()->SetTestingFactory( profile(), base::BindRepeating(&BuildEventRouter)); +#if defined(OS_WIN) + // Tests were designed assuming Hunspell dictionary used and may fail when + // Windows spellcheck is enabled by default. + feature_list_.InitAndDisableFeature(spellcheck::kWinUseBrowserSpellChecker); +#endif // defined(OS_WIN) + base::ListValue language_codes; language_codes.AppendString("fr"); profile()->GetPrefs()->Set(spellcheck::prefs::kSpellCheckDictionaries, @@ -89,6 +98,9 @@ class LanguageSettingsPrivateDelegateTest run_loop_->Quit(); } +#if defined(OS_WIN) + base::test::ScopedFeatureList feature_list_; +#endif // defined(OS_WIN) std::unique_ptr<LanguageSettingsPrivateDelegate> delegate_; std::unique_ptr<base::RunLoop> run_loop_; }; diff --git a/chromium/chrome/browser/extensions/api/management/DEPS b/chromium/chrome/browser/extensions/api/management/DEPS new file mode 100644 index 00000000000..bd0ce11d35b --- /dev/null +++ b/chromium/chrome/browser/extensions/api/management/DEPS @@ -0,0 +1,10 @@ +specific_include_rules = { + # This DEPS violation snuck in while there was a bug in the checkdeps tool. + # https://crbug.com/1084826 + "chrome_management_api_delegate\.cc": [ + "+chrome/browser/apps/app_service/app_launch_params.h", + "+chrome/browser/apps/app_service/app_service_proxy.h", + "+chrome/browser/apps/app_service/app_service_proxy_factory.h", + "+chrome/browser/apps/app_service/browser_app_launcher.h", + ], +} 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 b5b0ab58441..75ecb26f2e2 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 @@ -282,7 +282,8 @@ class ChromeAppForLinkDelegate : public extensions::AppForLinkDelegate { info.icons->reserve(icon_infos.size()); for (const WebApplicationIconInfo& web_app_icon_info : icon_infos) { extensions::api::management::IconInfo icon_info; - icon_info.size = web_app_icon_info.square_size_px; + if (web_app_icon_info.square_size_px) + icon_info.size = *web_app_icon_info.square_size_px; icon_info.url = web_app_icon_info.url.spec(); info.icons->push_back(std::move(icon_info)); } 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 c3f1fe7ff40..05567b35bde 100644 --- a/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_api_unittest.cc @@ -45,7 +45,6 @@ #include "chrome/browser/supervised_user/supervised_user_service.h" #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #include "chrome/browser/supervised_user/supervised_user_test_util.h" -#include "components/web_modal/web_contents_modal_dialog_manager.h" #include "content/public/browser/gpu_data_manager.h" #endif @@ -843,64 +842,87 @@ class TestManagementAPIDelegate : public ManagementAPIDelegate { // A delegate that allows a child to try to install an extension and tracks // whether the parent permission dialog would have opened. -class TestSupervisedUserServiceDelegate : public SupervisedUserServiceDelegate { +class TestSupervisedUserExtensionsDelegate + : public SupervisedUserExtensionsDelegate { public: - TestSupervisedUserServiceDelegate() = default; - ~TestSupervisedUserServiceDelegate() override = default; + TestSupervisedUserExtensionsDelegate() = default; + ~TestSupervisedUserExtensionsDelegate() override = default; - // SupervisedUserServiceDelegate: + // SupervisedUserExtensionsDelegate: bool IsChild(content::BrowserContext* context) const override { return true; } - bool IsSupervisedChildWhoMayInstallExtensions( - content::BrowserContext* context) const override { - return is_supervised_child_who_may_install_extensions_; - } bool IsExtensionAllowedByParent( const extensions::Extension& extension, content::BrowserContext* context) const override { - return false; + SupervisedUserService* supervised_user_service = + SupervisedUserServiceFactory::GetForBrowserContext(context); + return supervised_user_service->IsExtensionAllowed(extension); + } + + void PromptForParentPermissionOrShowError( + const extensions::Extension& extension, + content::BrowserContext* context, + content::WebContents* contents, + ParentPermissionDialogDoneCallback parent_permission_callback, + base::OnceClosure error_callback) override { + // Preconditions. + DCHECK(IsChild(context)); + DCHECK(!IsExtensionAllowedByParent(extension, context)); + + if (CanInstallExtensions(context)) { + ShowParentPermissionDialogForExtension( + extension, context, contents, std::move(parent_permission_callback)); + } else { + ShowExtensionEnableBlockedByParentDialogForExtension( + extension, contents, std::move(error_callback)); + } + } + + void set_next_parent_permission_dialog_result( + ParentPermissionDialogResult result) { + dialog_result_ = result; } + + int show_dialog_count() const { return show_dialog_count_; } + int show_block_dialog_count() const { return show_block_dialog_count_; } + + private: + // Returns true if |context| represents a supervised child account who may + // install extensions with parent permission. + bool CanInstallExtensions(content::BrowserContext* context) const { + SupervisedUserService* supervised_user_service = + SupervisedUserServiceFactory::GetForBrowserContext(context); + return supervised_user_service->CanInstallExtensions(); + } + + // Shows a parent permission dialog for |extension| and call |done_callback| + // when it completes. void ShowParentPermissionDialogForExtension( const extensions::Extension& extension, content::BrowserContext* context, content::WebContents* contents, - ParentPermissionDialogDoneCallback done_callback) override { + ParentPermissionDialogDoneCallback done_callback) { ++show_dialog_count_; std::move(done_callback).Run(dialog_result_); } + // Shows a dialog indicating that |extension| has been blocked and call + // |done_callback| when it completes. void ShowExtensionEnableBlockedByParentDialogForExtension( - const extensions::Extension* extension, + const extensions::Extension& extension, content::WebContents* contents, - base::OnceClosure done_callback) override { + base::OnceClosure done_callback) { show_block_dialog_count_++; - std::move(done_callback).Run(); - } - - void RecordExtensionEnableBlockedByParentDialogUmaMetric() override { SupervisedUserExtensionsMetricsRecorder::RecordEnablementUmaMetrics( SupervisedUserExtensionsMetricsRecorder::EnablementState:: kFailedToEnable); + std::move(done_callback).Run(); } - void set_next_parent_permission_dialog_result( - ParentPermissionDialogResult result) { - dialog_result_ = result; - } - - void set_is_supervised_child_who_may_install_extensions(bool value) { - is_supervised_child_who_may_install_extensions_ = value; - } - - int show_dialog_count() const { return show_dialog_count_; } - int show_block_dialog_count() const { return show_block_dialog_count_; } - - private: ParentPermissionDialogResult dialog_result_ = ParentPermissionDialogResult::kParentPermissionFailed; int show_dialog_count_ = 0; int show_block_dialog_count_ = 0; - bool is_supervised_child_who_may_install_extensions_ = true; }; // Tests for supervised users (child accounts). Supervised users are not allowed @@ -940,15 +962,15 @@ class ManagementApiSupervisedUserTest : public ManagementApiUnitTest { management_api_ = ManagementAPI::GetFactoryInstance()->Get(profile()); - // Install a SupervisedUserServiceDelegate to sense the dialog state. - supervised_user_delegate_ = new TestSupervisedUserServiceDelegate; - management_api_->set_supervised_user_service_delegate_for_test( + // Install a SupervisedUserExtensionsDelegate to sense the dialog state. + supervised_user_delegate_ = new TestSupervisedUserExtensionsDelegate; + management_api_->set_supervised_user_extensions_delegate_for_test( base::WrapUnique(supervised_user_delegate_)); } std::unique_ptr<content::WebContents> web_contents_; ManagementAPI* management_api_ = nullptr; - TestSupervisedUserServiceDelegate* supervised_user_delegate_ = nullptr; + TestSupervisedUserExtensionsDelegate* supervised_user_delegate_ = nullptr; }; TEST_F(ManagementApiSupervisedUserTest, SetEnabled_BlockedByParent) { @@ -974,11 +996,8 @@ TEST_F(ManagementApiSupervisedUserTest, SetEnabled_BlockedByParent) { // Simulate disabling Permissions for sites, apps and extensions // in the testing supervised user service delegate used by the Management API. - supervised_user_delegate_->set_is_supervised_child_who_may_install_extensions( - false); - // Ensure that the web contents can be used to create a modal dialog. - web_modal::WebContentsModalDialogManager::CreateForWebContents( - web_contents_.get()); + GetSupervisedUserService() + ->SetSupervisedUserExtensionsMayRequestPermissionsPrefForTesting(false); // The supervised user trying to enable while Permissions for sites, apps and // extensions is disabled should fail. @@ -1007,61 +1026,6 @@ TEST_F(ManagementApiSupervisedUserTest, SetEnabled_BlockedByParent) { SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName)); } -TEST_F(ManagementApiSupervisedUserTest, - SetEnabled_BlockedByParentNoDialogWhenNoDialogManagerAvailable) { - // Preconditions. - ASSERT_TRUE(profile()->IsChild()); - - base::HistogramTester histogram_tester; - base::UserActionTester user_action_tester; - - base::FilePath base_path = data_dir().AppendASCII("permissions_increase"); - base::FilePath pem_path = base_path.AppendASCII("permissions.pem"); - - base::FilePath path = base_path.AppendASCII("v1"); - const Extension* extension = - PackAndInstallCRX(path, pem_path, INSTALL_WITHOUT_LOAD); - ASSERT_TRUE(extension); - // The extension should be installed but disabled. - EXPECT_TRUE(registry()->disabled_extensions().Contains(extension->id())); - const std::string extension_id = extension->id(); - ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); - EXPECT_TRUE(prefs->HasDisableReason( - extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)); - - // Simulate disabling Permissions for sites, apps and extensions - // in the testing supervised user service delegate used by the Management API. - supervised_user_delegate_->set_is_supervised_child_who_may_install_extensions( - false); - - // The supervised user trying to enable while Permissions for sites, apps and - // extensions is disabled should fail. - { - std::string error; - - bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, - /*use_user_gesture=*/true, - /*accept_dialog=*/true, &error); - EXPECT_FALSE(success); - EXPECT_FALSE(error.empty()); - EXPECT_TRUE(registry()->disabled_extensions().Contains(extension_id)); - - // The block dialog should not have been shown. - EXPECT_EQ(supervised_user_delegate_->show_block_dialog_count(), 0); - } - - histogram_tester.ExpectUniqueSample( - SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, - SupervisedUserExtensionsMetricsRecorder::EnablementState::kFailedToEnable, - 1); - histogram_tester.ExpectTotalCount( - SupervisedUserExtensionsMetricsRecorder::kEnablementHistogramName, 1); - EXPECT_EQ( - 1, - user_action_tester.GetActionCount( - SupervisedUserExtensionsMetricsRecorder::kFailedToEnableActionName)); -} - // Tests enabling an extension via management API after it was disabled due to // permission increase for supervised users. // Prevents a regression to crbug/1068660. @@ -1295,7 +1259,7 @@ TEST_F(ManagementApiSupervisedUserTest, // Now try again with parent approval, and this should succeed. { supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionReceived); std::string error; bool success = RunSetEnabledFunction(web_contents_.get(), extension_id, @@ -1347,7 +1311,7 @@ TEST_F(ManagementApiSupervisedUserTest, SetEnabled_UnsupportedRequirement) { // Parent approval should fail because of the unsupported requirements. { supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionReceived); std::string error; bool success = RunSetEnabledFunction(web_contents_.get(), extension->id(), @@ -1382,7 +1346,7 @@ TEST_F(ManagementApiSupervisedUserTest, SetEnabledDisabled_UmaMetrics) { // The parent will approve. supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionReceived); RunSetEnabledFunction(web_contents_.get(), extension->id(), @@ -1469,7 +1433,7 @@ TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_ParentApproves) { // The parent will approve. supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionReceived); // Simulate a call to chrome.management.setEnabled(). It should succeed. @@ -1494,7 +1458,7 @@ TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_ParentDenies) { // The parent will deny the next dialog. supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionCanceled); // Simulate a call to chrome.management.setEnabled(). It should not succeed. @@ -1520,7 +1484,7 @@ TEST_F(ManagementApiSupervisedUserTestWithSetup, SetEnabled_DialogFails) { // The next dialog will close due to a failure (e.g. network failure while // looking up parent information). supervised_user_delegate_->set_next_parent_permission_dialog_result( - SupervisedUserServiceDelegate::ParentPermissionDialogResult:: + SupervisedUserExtensionsDelegate::ParentPermissionDialogResult:: kParentPermissionFailed); // Simulate a call to chrome.management.setEnabled(). It should not succeed. diff --git a/chromium/chrome/browser/extensions/api/management/management_apitest.cc b/chromium/chrome/browser/extensions/api/management/management_apitest.cc index aded1d518db..7804dcad939 100644 --- a/chromium/chrome/browser/extensions/api/management/management_apitest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_apitest.cc @@ -18,6 +18,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/components/app_registrar.h" #include "chrome/browser/web_applications/components/app_shortcut_manager.h" +#include "chrome/browser/web_applications/components/install_finalizer.h" #include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/browser/web_applications/components/web_app_provider_base.h" #include "chrome/browser/web_applications/test/test_web_app_ui_manager.h" @@ -36,6 +37,7 @@ #include "extensions/test/result_catcher.h" #include "extensions/test/test_extension_dir.h" #include "net/dns/mock_host_resolver.h" +#include "testing/gtest/include/gtest/gtest.h" using extensions::Extension; using extensions::Manifest; @@ -241,9 +243,12 @@ class InstallReplacementWebAppApiTest : public ExtensionManagementApiTest { chrome::SetAutoAcceptPWAInstallConfirmationForTesting(true); const GURL start_url = https_test_server_.GetURL(web_app_start_url); web_app::AppId web_app_id = web_app::GenerateAppIdFromURL(start_url); - auto* provider = web_app::WebAppProviderBase::GetProviderBase(browser()->profile()); + // Async legacy finalizer install was causing this test to be flaky (see + // crbug.com/1094616). + provider->install_finalizer().RemoveLegacyInstallFinalizerForTesting(); + EXPECT_FALSE(provider->registrar().IsLocallyInstalled(start_url)); EXPECT_EQ(0, static_cast<int>( provider->ui_manager().GetNumWindowsForApp(web_app_id))); diff --git a/chromium/chrome/browser/extensions/api/management/management_browsertest.cc b/chromium/chrome/browser/extensions/api/management/management_browsertest.cc index 68ecb30c5ed..ec72a41d2d6 100644 --- a/chromium/chrome/browser/extensions/api/management/management_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_browsertest.cc @@ -87,8 +87,7 @@ class ExtensionHostDestructionObserver } // ExtensionHostObserver: - void OnExtensionHostDestroyed( - const extensions::ExtensionHost* host) override { + void OnExtensionHostDestroyed(extensions::ExtensionHost* host) override { if (host == host_) { extension_host_observer_.Remove(host_); run_loop_.Quit(); diff --git a/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc b/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc index 914c9eb0467..b7f9bde94a7 100644 --- a/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc +++ b/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc @@ -49,15 +49,8 @@ class MDnsAPITest : public extensions::ExtensionApiTest { } // namespace -// TODO(justinlin): Win Dbg has a workaround that makes RunExtensionSubtest -// always return true without actually running the test. Remove when fixed. -#if defined(OS_WIN) && !defined(NDEBUG) -#define MAYBE_RegisterListener DISABLED_RegisterListener -#else -#define MAYBE_RegisterListener RegisterListener -#endif // Test loading extension, registering an MDNS listener and dispatching events. -IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterListener) { +IN_PROC_BROWSER_TEST_F(MDnsAPITest, RegisterListener) { const std::string& service_type = "_googlecast._tcp.local"; SetUpTestDnsSdRegistry(); @@ -86,15 +79,8 @@ IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterListener) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } -// TODO(justinlin): Win Dbg has a workaround that makes RunExtensionSubtest -// always return true without actually running the test. Remove when fixed. -#if defined(OS_WIN) && !defined(NDEBUG) -#define MAYBE_ForceDiscovery DISABLED_ForceDiscovery -#else -#define MAYBE_ForceDiscovery ForceDiscovery -#endif // Test loading extension, registering an MDNS listener and dispatching events. -IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_ForceDiscovery) { +IN_PROC_BROWSER_TEST_F(MDnsAPITest, ForceDiscovery) { const std::string& service_type = "_googlecast._tcp.local"; SetUpTestDnsSdRegistry(); @@ -120,15 +106,8 @@ IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_ForceDiscovery) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } -// TODO(justinlin): Win Dbg has a workaround that makes RunExtensionSubtest -// always return true without actually running the test. Remove when fixed. -#if defined(OS_WIN) && !defined(NDEBUG) -#define MAYBE_RegisterMultipleListeners DISABLED_RegisterMultipleListeners -#else -#define MAYBE_RegisterMultipleListeners RegisterMultipleListeners -#endif // Test loading extension and registering multiple listeners. -IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterMultipleListeners) { +IN_PROC_BROWSER_TEST_F(MDnsAPITest, RegisterMultipleListeners) { const std::string& service_type = "_googlecast._tcp.local"; const std::string& test_service_type = "_testing._tcp.local"; SetUpTestDnsSdRegistry(); @@ -161,15 +140,8 @@ IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterMultipleListeners) { EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } -// TODO(justinlin): Win Dbg has a workaround that makes RunExtensionSubtest -// always return true without actually running the test. Remove when fixed. -#if defined(OS_WIN) && !defined(NDEBUG) -#define MAYBE_RegisterTooManyListeners DISABLED_RegisterTooManyListeners -#else -#define MAYBE_RegisterTooManyListeners RegisterTooManyListeners -#endif // Test loading extension and registering multiple listeners. -IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterTooManyListeners) { +IN_PROC_BROWSER_TEST_F(MDnsAPITest, RegisterTooManyListeners) { SetUpTestDnsSdRegistry(); EXPECT_CALL(*dns_sd_registry_, RegisterDnsSdListener(_)).Times(10); @@ -182,16 +154,8 @@ IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_RegisterTooManyListeners) { << message_; } -// TODO(justinlin): Win Dbg has a workaround that makes RunExtensionSubtest -// always return true without actually running the test. Remove when fixed. -#if defined(OS_WIN) && !defined(NDEBUG) -#define MAYBE_MaxServiceInstancesPerEventConst \ - DISABLED_MaxServiceInstancesPerEventConst -#else -#define MAYBE_MaxServiceInstancesPerEventConst MaxServiceInstancesPerEventConst -#endif // Test loading extension and registering multiple listeners. -IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_MaxServiceInstancesPerEventConst) { +IN_PROC_BROWSER_TEST_F(MDnsAPITest, MaxServiceInstancesPerEventConst) { EXPECT_TRUE(RunExtensionSubtest("mdns/api", "get_max_service_instances.html")); } diff --git a/chromium/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc b/chromium/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc index ccfb4199428..a3422c55d05 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_message_host_chromeos.cc @@ -15,7 +15,6 @@ #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" -#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "chrome/browser/browser_process.h" @@ -99,8 +98,7 @@ struct BuiltInHost { std::unique_ptr<NativeMessageHost> CreateIt2MeHost( content::BrowserContext* browser_context) { return remoting::CreateIt2MeNativeMessagingHostForChromeOS( - base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}), - base::CreateSingleThreadTaskRunner({content::BrowserThread::UI}), + content::GetIOThreadTaskRunner({}), content::GetUIThreadTaskRunner({}), g_browser_process->policy_service()); } diff --git a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host.cc b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host.cc index 8d1911b6118..d1d51bfa74d 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host.cc @@ -13,7 +13,6 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/process/kill.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" @@ -60,7 +59,7 @@ NativeMessageProcessHost::NativeMessageProcessHost( const std::string& source_extension_id, const std::string& native_host_name, std::unique_ptr<NativeProcessLauncher> launcher) - : client_(NULL), + : client_(nullptr), source_extension_id_(source_extension_id), native_host_name_(native_host_name), launcher_(std::move(launcher)), @@ -72,8 +71,7 @@ NativeMessageProcessHost::NativeMessageProcessHost( write_pending_(false) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - task_runner_ = - base::CreateSingleThreadTaskRunner({content::BrowserThread::IO}); + task_runner_ = content::GetIOThreadTaskRunner({}); } NativeMessageProcessHost::~NativeMessageProcessHost() { diff --git a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc index 428ba272fc3..3acc436a7ea 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_message_process_host_unittest.cc @@ -27,7 +27,6 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "base/test/scoped_feature_list.h" #include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" @@ -59,8 +58,6 @@ #include <unistd.h> #endif -using content::BrowserThread; - namespace { const char kTestMessage[] = "{\"text\": \"Hello.\"}"; @@ -125,8 +122,8 @@ class NativeMessagingTest : public ::testing::Test, void TearDown() override { if (native_message_host_) { - base::DeleteSoon(FROM_HERE, {BrowserThread::IO}, - native_message_host_.release()); + content::GetIOThreadTaskRunner({})->DeleteSoon( + FROM_HERE, native_message_host_.release()); } base::RunLoop().RunUntilIdle(); } 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 f25164371e1..7b83c3a74ff 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_apitest.cc @@ -23,11 +23,14 @@ #include "components/keep_alive_registry/keep_alive_types.h" #include "content/public/test/browser_test.h" #include "extensions/browser/process_manager.h" +#include "extensions/common/scoped_worker_based_extensions_channel.h" #include "extensions/test/result_catcher.h" namespace extensions { namespace { +using ContextType = ExtensionApiTest::ContextType; + class NativeMessagingApiTest : public ExtensionApiTest { protected: extensions::ScopedTestNativeMessagingHost test_host_; @@ -43,6 +46,48 @@ IN_PROC_BROWSER_TEST_F(NativeMessagingApiTest, UserLevelNativeMessaging) { ASSERT_TRUE(RunExtensionTest("native_messaging")) << message_; } +// TODO(crbug.com/1094027): Clean up duplicate test coverage. +class NativeMessagingLazyApiTest + : public NativeMessagingApiTest, + public testing::WithParamInterface<ContextType> { + public: + void SetUp() override { + NativeMessagingApiTest::SetUp(); + // Service Workers are currently only available on certain channels, so set + // the channel for those tests. + if (GetParam() == ContextType::kServiceWorker) + current_channel_ = std::make_unique<ScopedWorkerBasedExtensionsChannel>(); + } + + protected: + bool RunLazyTest(const std::string& extension_name) { + if (GetParam() == ContextType::kEventPage) { + return RunExtensionTest(extension_name); + } + return RunExtensionTestWithFlags( + extension_name, kFlagRunAsServiceWorkerBasedExtension, kFlagNone); + } + + std::unique_ptr<ScopedWorkerBasedExtensionsChannel> current_channel_; +}; + +INSTANTIATE_TEST_SUITE_P(EventPage, + NativeMessagingLazyApiTest, + ::testing::Values(ContextType::kEventPage)); +INSTANTIATE_TEST_SUITE_P(ServiceWorker, + NativeMessagingLazyApiTest, + ::testing::Values(ContextType::kServiceWorker)); + +IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, NativeMessagingBasic) { + ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(false)); + ASSERT_TRUE(RunLazyTest("native_messaging_lazy")) << message_; +} + +IN_PROC_BROWSER_TEST_P(NativeMessagingLazyApiTest, UserLevelNativeMessaging) { + ASSERT_NO_FATAL_FAILURE(test_host_.RegisterTestHost(true)); + ASSERT_TRUE(RunLazyTest("native_messaging_lazy")) << message_; +} + #if !defined(OS_CHROMEOS) class TestProcessManagerObserver : public ProcessManagerObserver { diff --git a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc index 967ebb848e4..f675d66c911 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_messaging_launch_from_native.cc @@ -11,7 +11,6 @@ #include "base/bind_helpers.h" #include "base/strings/strcat.h" #include "base/strings/string_util.h" -#include "base/task/post_task.h" #include "base/threading/sequenced_task_runner_handle.h" #include "base/timer/timer.h" #include "chrome/browser/browser_process.h" @@ -75,8 +74,8 @@ class NativeMessagingHostErrorReporter : public NativeMessageHost::Client { MovableScopedKeepAlive keep_alive( new ScopedKeepAlive(KeepAliveOrigin::NATIVE_MESSAGING_HOST_ERROR_REPORT, KeepAliveRestartOption::DISABLED)); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NativeMessagingHostErrorReporter::ReportOnIoThread, std::move(host), std::move(keep_alive))); } diff --git a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc index 5b5b39a5567..439e74947e0 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc @@ -23,7 +23,6 @@ #include "base/path_service.h" #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "base/values.h" #include "build/build_config.h" @@ -313,8 +312,8 @@ void NativeProcessLauncherImpl::Core::CallCallbackOnIOThread( void NativeProcessLauncherImpl::Core::PostErrorResult( const LaunchedCallback& callback, LaunchResult error) { - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, error, base::Process(), base::File(), base::File())); @@ -325,8 +324,8 @@ void NativeProcessLauncherImpl::Core::PostResult( base::Process process, base::File read_file, base::File write_file) { - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NativeProcessLauncherImpl::Core::CallCallbackOnIOThread, this, callback, RESULT_SUCCESS, std::move(process), std::move(read_file), std::move(write_file))); diff --git a/chromium/chrome/browser/extensions/api/notifications/DEPS b/chromium/chrome/browser/extensions/api/notifications/DEPS new file mode 100644 index 00000000000..bdbc70d13b0 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/notifications/DEPS @@ -0,0 +1,10 @@ +specific_include_rules = { + # This DEPS violation snuck in while there was a bug in the checkdeps tool. + # https://crbug.com/1084826 + "notifications_apitest\.cc": [ + "+chrome/browser/apps/app_service/app_launch_params.h", + "+chrome/browser/apps/app_service/app_service_proxy.h", + "+chrome/browser/apps/app_service/app_service_proxy_factory.h", + "+chrome/browser/apps/app_service/browser_app_launcher.h", + ], +} 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 ba017b4199d..31905b3349a 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 @@ -11,7 +11,6 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/files/file_util.h" -#include "base/task/post_task.h" #include "base/task/thread_pool.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_tab_util.h" @@ -76,8 +75,8 @@ PageCaptureSaveAsMHTMLFunction::PageCaptureSaveAsMHTMLFunction() { PageCaptureSaveAsMHTMLFunction::~PageCaptureSaveAsMHTMLFunction() { if (mhtml_file_.get()) { - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ClearFileReferenceOnIOThread, std::move(mhtml_file_))); } } @@ -200,8 +199,8 @@ void PageCaptureSaveAsMHTMLFunction::ResolvePermissionRequest( void PageCaptureSaveAsMHTMLFunction::CreateTemporaryFile() { bool success = base::CreateTemporaryFile(&mhtml_path_); - base::PostTask( - FROM_HERE, {BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreatedOnIO, this, success)); } @@ -226,8 +225,8 @@ void PageCaptureSaveAsMHTMLFunction::TemporaryFileCreatedOnIO(bool success) { base::TaskShutdownBehavior::BLOCK_SHUTDOWN}) .get()); } - base::PostTask( - FROM_HERE, {BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PageCaptureSaveAsMHTMLFunction::TemporaryFileCreatedOnUI, this, success)); } 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 413965efe9b..54808bfd323 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 @@ -36,7 +36,7 @@ #include "components/password_manager/core/browser/compromised_credentials_table.h" #include "components/password_manager/core/browser/leak_detection/bulk_leak_check.h" #include "components/password_manager/core/browser/leak_detection/encryption_utils.h" -#include "components/password_manager/core/browser/ui/compromised_credentials_provider.h" +#include "components/password_manager/core/browser/ui/compromised_credentials_manager.h" #include "components/password_manager/core/browser/ui/credential_utils.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" #include "components/password_manager/core/common/password_manager_pref_names.h" @@ -54,21 +54,17 @@ namespace { using autofill::PasswordForm; using password_manager::CanonicalizeUsername; -using password_manager::CompromiseType; +using password_manager::CompromiseTypeFlags; using password_manager::CredentialWithPassword; using password_manager::LeakCheckCredential; -using password_manager::PasswordCredentialLess; using ui::TimeFormat; using CompromisedCredentialsView = - password_manager::CompromisedCredentialsProvider::CredentialsView; + password_manager::CompromisedCredentialsManager::CredentialsView; using SavedPasswordsView = password_manager::SavedPasswordsPresenter::SavedPasswordsView; using State = password_manager::BulkLeakCheckService::State; -using CompromisedCredentialSet = - base::flat_set<CredentialWithPassword, PasswordCredentialLess>; - } // namespace // Key used to attach UserData to a LeakCheckCredential. @@ -165,35 +161,6 @@ api::passwords_private::PasswordCheckState ConvertPasswordCheckState( return api::passwords_private::PASSWORD_CHECK_STATE_NONE; } -// Computes a map that matches compromised credentials with corresponding saved -// passwords in the store. This is needed to reflect changes to the underlying -// password store when a compromised credential gets modified or removed through -// the UI. Also it allows to inject affiliation information to be displayed in -// the UI. -PasswordCheckDelegate::CredentialPasswordsMap -MapCompromisedCredentialsToSavedPasswords( - CompromisedCredentialsView compromised_credentials_view, - SavedPasswordsView saved_passwords) { - // Create a set to turn queries to look up a matching credential from O(n) to - // O(log n). - CompromisedCredentialSet compromised_credentials( - compromised_credentials_view.begin(), compromised_credentials_view.end()); - - // Populate the map. The values are vectors, because it is possible that - // multiple saved passwords match to the same compromised credential. In most - // cases there should be a 1-1 relationship, though. - PasswordCheckDelegate::CredentialPasswordsMap passwords_map; - for (const auto& saved_password : saved_passwords) { - auto it = compromised_credentials.find( - password_manager::CredentialView(saved_password)); - if (it != compromised_credentials.end()) { - passwords_map[*it].push_back(saved_password); - } - } - - return passwords_map; -} - std::string FormatElapsedTime(base::Time time) { const base::TimeDelta elapsed_time = base::Time::Now() - time; if (elapsed_time < base::TimeDelta::FromMinutes(1)) @@ -204,9 +171,7 @@ std::string FormatElapsedTime(base::Time time) { } // Helper struct that bundles a CredentialWithPassword with a corresponding -// passwords_private::CompromiseType. This is necessary to support the both -// PHISHED_AND_LEAKED case, which does not exists in password_manager's -// CompromiseType. +// passwords_private::CompromiseType. struct CompromisedCredentialAndType { CredentialWithPassword credential; api::passwords_private::CompromiseType type; @@ -214,65 +179,24 @@ struct CompromisedCredentialAndType { // Orders |compromised_credentials| in such a way that phished credentials // precede leaked credentials, and that credentials of the same compromise type -// are ordered by recency. Furthermore it de-duplicates credentials that are -// both phished and leaked, making sure they only appear once in the final list. +// are ordered by recency. std::vector<CompromisedCredentialAndType> OrderCompromisedCredentials( - CompromisedCredentialsView compromised_credentials) { - // Partition the compromised credentials into phished and leaked. - std::vector<CredentialWithPassword> phished_storage; - std::vector<CredentialWithPassword> leaked_storage; - std::partition_copy( - compromised_credentials.begin(), compromised_credentials.end(), - std::back_inserter(phished_storage), std::back_inserter(leaked_storage), - [](const auto& credential) { - return credential.compromise_type == CompromiseType::kPhished; - }); - - // Perform a set intersection to find credentials that are both phished and - // leaked. Operate on flat_sets, since they provide a more convenient API. - CompromisedCredentialSet phished = std::move(phished_storage); - CompromisedCredentialSet leaked = std::move(leaked_storage); - CompromisedCredentialSet phished_and_leaked; - std::set_intersection( - phished.begin(), phished.end(), leaked.begin(), leaked.end(), - std::inserter(phished_and_leaked, phished_and_leaked.end()), - PasswordCredentialLess()); - - // Iterate through the phished and leaked credentials and update their - // timestamp to be the most recent compromise event. - for (auto& phished_and_leaked : phished_and_leaked) { - phished_and_leaked.create_time = - std::max(phished.find(phished_and_leaked)->create_time, - leaked.find(phished_and_leaked)->create_time); - } - - // Erase phished and leaked credentials from the other two sets. - auto is_phished_and_leaked = [&](const auto& credential) { - return phished_and_leaked.contains(credential); - }; - base::EraseIf(phished, is_phished_and_leaked); - base::EraseIf(leaked, is_phished_and_leaked); - - // Finally move all credentials into a single list, associating with the + std::vector<CredentialWithPassword> compromised_credentials) { + // Move all credentials into a single list, associating with the // corresponding CompromiseType. - const size_t num_phished = phished.size() + phished_and_leaked.size(); std::vector<CompromisedCredentialAndType> results; results.reserve(compromised_credentials.size()); - for (auto& credential : std::move(phished).extract()) { - results.push_back({std::move(credential), - api::passwords_private::COMPROMISE_TYPE_PHISHED}); - } - - for (auto& credential : std::move(phished_and_leaked).extract()) { - results.push_back( - {std::move(credential), - api::passwords_private::COMPROMISE_TYPE_PHISHED_AND_LEAKED}); - } - - for (auto& credential : std::move(leaked).extract()) { - results.push_back({std::move(credential), - api::passwords_private::COMPROMISE_TYPE_LEAKED}); + for (auto& credential : compromised_credentials) { + auto type = static_cast<api::passwords_private::CompromiseType>( + credential.compromise_type); + results.push_back({std::move(credential), type}); } + // Reordering phished credential to the beginning. + auto last_phished = std::partition( + results.begin(), results.end(), [](const auto& credential) { + return credential.type != + api::passwords_private::COMPROMISE_TYPE_LEAKED; + }); // By construction the phished credentials precede the leaked credentials in // |results|. Now sort both groups by their creation date so that most recent @@ -280,8 +204,8 @@ std::vector<CompromisedCredentialAndType> OrderCompromisedCredentials( auto create_time_cmp = [](const auto& lhs, const auto& rhs) { return lhs.credential.create_time > rhs.credential.create_time; }; - std::sort(results.begin(), results.begin() + num_phished, create_time_cmp); - std::sort(results.begin() + num_phished, results.end(), create_time_cmp); + std::sort(results.begin(), last_phished, create_time_cmp); + std::sort(last_phished, results.end(), create_time_cmp); return results; } @@ -293,25 +217,24 @@ PasswordCheckDelegate::PasswordCheckDelegate(Profile* profile) profile, ServiceAccessType::EXPLICIT_ACCESS)), saved_passwords_presenter_(password_store_), - compromised_credentials_provider_(password_store_, - &saved_passwords_presenter_), + compromised_credentials_manager_(password_store_, + &saved_passwords_presenter_), bulk_leak_check_service_adapter_( &saved_passwords_presenter_, BulkLeakCheckServiceFactory::GetForProfile(profile_), profile_->GetPrefs()) { observed_saved_passwords_presenter_.Add(&saved_passwords_presenter_); - observed_compromised_credentials_provider_.Add( - &compromised_credentials_provider_); + observed_compromised_credentials_manager_.Add( + &compromised_credentials_manager_); observed_bulk_leak_check_service_.Add( BulkLeakCheckServiceFactory::GetForProfile(profile_)); // Instructs the presenter and provider to initialize and built their caches. - // This will soon after invoke OnCompromisedCredentialsChanged(), which then - // initializes |credentials_to_forms_| as well. Calls to + // This will soon after invoke OnCompromisedCredentialsChanged(). Calls to // GetCompromisedCredentials() that might happen until then will return an // empty list. saved_passwords_presenter_.Init(); - compromised_credentials_provider_.Init(); + compromised_credentials_manager_.Init(); } PasswordCheckDelegate::~PasswordCheckDelegate() = default; @@ -320,7 +243,7 @@ std::vector<api::passwords_private::CompromisedCredential> PasswordCheckDelegate::GetCompromisedCredentials() { std::vector<CompromisedCredentialAndType> ordered_compromised_credential_and_types = OrderCompromisedCredentials( - compromised_credentials_provider_.GetCompromisedCredentials()); + compromised_credentials_manager_.GetCompromisedCredentials()); std::vector<api::passwords_private::CompromisedCredential> compromised_credentials; @@ -338,7 +261,7 @@ PasswordCheckDelegate::GetCompromisedCredentials() { // special handling for Android. Here we use affiliation information // instead of the signon_realm. const PasswordForm& android_form = - credentials_to_forms_.at(credential).at(0); + compromised_credentials_manager_.GetSavedPasswordsFor(credential)[0]; if (!android_form.app_display_name.empty()) { api_credential.formatted_origin = android_form.app_display_name; api_credential.detailed_origin = android_form.app_display_name; @@ -401,52 +324,26 @@ PasswordCheckDelegate::GetPlaintextCompromisedPassword( bool PasswordCheckDelegate::ChangeCompromisedCredential( const api::passwords_private::CompromisedCredential& credential, base::StringPiece new_password) { - // Try to obtain the original CredentialWithPassword and try to find it in - // |credentials_to_forms_|. Return false if either one fails. + // Try to obtain the original CredentialWithPassword. Return false if fails. const CredentialWithPassword* compromised_credential = FindMatchingCompromisedCredential(credential); if (!compromised_credential) return false; - auto it = credentials_to_forms_.find(*compromised_credential); - if (it == credentials_to_forms_.end()) - return false; - - // Make sure there are matching password forms. Also erase duplicates if there - // are any. - const auto& forms = it->second; - if (forms.empty()) - return false; - - for (size_t i = 1; i < forms.size(); ++i) - password_store_->RemoveLogin(forms[i]); - - // Note: We Invoke EditPassword on the presenter rather than UpdateLogin() on - // the store, so that observers of the presenter get notified of this event. - return saved_passwords_presenter_.EditPassword( - forms[0], base::UTF8ToUTF16(new_password)); + return compromised_credentials_manager_.UpdateCompromisedCredentials( + *compromised_credential, new_password); } bool PasswordCheckDelegate::RemoveCompromisedCredential( const api::passwords_private::CompromisedCredential& credential) { - // Try to obtain the original CredentialWithPassword and try to find it in - // |credentials_to_forms_|. Return false if either one fails. + // Try to obtain the original CredentialWithPassword. Return false if fails. const CredentialWithPassword* compromised_credential = FindMatchingCompromisedCredential(credential); if (!compromised_credential) return false; - auto it = credentials_to_forms_.find(*compromised_credential); - if (it == credentials_to_forms_.end()) - return false; - - // Erase all matching credentials from the store. Return whether any - // credentials were deleted. - SavedPasswordsView saved_passwords = it->second; - for (const PasswordForm& saved_password : saved_passwords) - password_store_->RemoveLogin(saved_password); - - return !saved_passwords.empty(); + return compromised_credentials_manager_.RemoveCompromisedCredential( + *compromised_credential); } void PasswordCheckDelegate::StartPasswordCheck( @@ -547,8 +444,6 @@ void PasswordCheckDelegate::OnSavedPasswordsChanged(SavedPasswordsView) { void PasswordCheckDelegate::OnCompromisedCredentialsChanged( CompromisedCredentialsView credentials) { - credentials_to_forms_ = MapCompromisedCredentialsToSavedPasswords( - credentials, saved_passwords_presenter_.GetSavedPasswords()); if (auto* event_router = PasswordsPrivateEventRouterFactory::GetForProfile(profile_)) { event_router->OnCompromisedCredentialsChanged(GetCompromisedCredentials()); @@ -584,24 +479,7 @@ void PasswordCheckDelegate::OnCredentialDone( const LeakCheckCredential& credential, password_manager::IsLeaked is_leaked) { if (is_leaked) { - // In case the credential is leaked, iterate over all currently saved - // credentials and mark those as compromised that have the same - // canonicalized username and password. - const base::string16 canocalized_username = - CanonicalizeUsername(credential.username()); - for (const PasswordForm& saved_password : - saved_passwords_presenter_.GetSavedPasswords()) { - if (saved_password.password_value == credential.password() && - CanonicalizeUsername(saved_password.username_value) == - canocalized_username) { - password_store_->AddCompromisedCredentials({ - .signon_realm = saved_password.signon_realm, - .username = saved_password.username_value, - .create_time = base::Time::Now(), - .compromise_type = CompromiseType::kLeaked, - }); - } - } + compromised_credentials_manager_.SaveCompromisedCredential(credential); } // Update the progress in case there is one. 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 c2ec8b18e99..a615f879ae8 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 @@ -18,7 +18,7 @@ #include "components/password_manager/core/browser/leak_detection/leak_detection_delegate_interface.h" #include "components/password_manager/core/browser/password_store.h" #include "components/password_manager/core/browser/ui/bulk_leak_check_service_adapter.h" -#include "components/password_manager/core/browser/ui/compromised_credentials_provider.h" +#include "components/password_manager/core/browser/ui/compromised_credentials_manager.h" #include "components/password_manager/core/browser/ui/credential_utils.h" #include "components/password_manager/core/browser/ui/saved_passwords_presenter.h" @@ -38,17 +38,12 @@ class PasswordCheckProgress; // with the bulk password check feature. class PasswordCheckDelegate : public password_manager::SavedPasswordsPresenter::Observer, - public password_manager::CompromisedCredentialsProvider::Observer, - public password_manager::BulkLeakCheckService::Observer { + public password_manager::CompromisedCredentialsManager::Observer, + public password_manager::BulkLeakCheckServiceInterface::Observer { public: using StartPasswordCheckCallback = PasswordsPrivateDelegate::StartPasswordCheckCallback; - using CredentialPasswordsMap = - std::map<password_manager::CredentialWithPassword, - std::vector<autofill::PasswordForm>, - password_manager::PasswordCredentialLess>; - explicit PasswordCheckDelegate(Profile* profile); PasswordCheckDelegate(const PasswordCheckDelegate&) = delete; PasswordCheckDelegate& operator=(const PasswordCheckDelegate&) = delete; @@ -98,7 +93,7 @@ class PasswordCheckDelegate // Invokes PasswordsPrivateEventRouter::OnCompromisedCredentialsChanged if // a valid pointer can be obtained. void OnCompromisedCredentialsChanged( - password_manager::CompromisedCredentialsProvider::CredentialsView + password_manager::CompromisedCredentialsManager::CredentialsView credentials) override; // password_manager::BulkLeakCheckService::Observer: @@ -126,16 +121,16 @@ class PasswordCheckDelegate Profile* profile_ = nullptr; // Handle to the password store, powering both |saved_passwords_presenter_| - // and |compromised_credentials_provider_|. + // and |compromised_credentials_manager_|. scoped_refptr<password_manager::PasswordStore> password_store_; - // Used by |compromised_credentials_provider_| to obtain the list of saved + // Used by |compromised_credentials_manager_| to obtain the list of saved // passwords. password_manager::SavedPasswordsPresenter saved_passwords_presenter_; // Used to obtain the list of compromised credentials. - password_manager::CompromisedCredentialsProvider - compromised_credentials_provider_; + password_manager::CompromisedCredentialsManager + compromised_credentials_manager_; // Adapter used to start, monitor and stop a bulk leak check. password_manager::BulkLeakCheckServiceAdapter @@ -162,22 +157,16 @@ class PasswordCheckDelegate password_manager::SavedPasswordsPresenter::Observer> observed_saved_passwords_presenter_{this}; - // A scoped observer for |compromised_credentials_provider_|. - ScopedObserver<password_manager::CompromisedCredentialsProvider, - password_manager::CompromisedCredentialsProvider::Observer> - observed_compromised_credentials_provider_{this}; + // A scoped observer for |compromised_credentials_manager_|. + ScopedObserver<password_manager::CompromisedCredentialsManager, + password_manager::CompromisedCredentialsManager::Observer> + observed_compromised_credentials_manager_{this}; // A scoped observer for the BulkLeakCheckService. - ScopedObserver<password_manager::BulkLeakCheckService, - password_manager::BulkLeakCheckService::Observer> + ScopedObserver<password_manager::BulkLeakCheckServiceInterface, + password_manager::BulkLeakCheckServiceInterface::Observer> observed_bulk_leak_check_service_{this}; - // A map that matches CredentialWithPasswords to corresponding PasswordForms. - // This is required to inject affiliation information into Android - // credentials, as well as being able to reflect edits and removals of - // compromised credentials in the underlying password store. - CredentialPasswordsMap credentials_to_forms_; - // An id generator for compromised credentials. Required to match // api::passwords_private::CompromisedCredential instances passed to the UI // with the underlying CredentialWithPassword they are based on. 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 067b79f09b7..ef4fb104178 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 @@ -74,6 +74,7 @@ using password_manager::BulkLeakCheckDelegateInterface; using password_manager::BulkLeakCheckService; using password_manager::CompromisedCredentials; using password_manager::CompromiseType; +using password_manager::CompromiseTypeFlags; using password_manager::IsLeaked; using password_manager::LeakCheckCredential; using password_manager::TestPasswordStore; @@ -248,6 +249,27 @@ class PasswordCheckDelegateTest : public ::testing::Test { } // namespace +TEST_F(PasswordCheckDelegateTest, VerifyCastingOfCompromisedCredentialTypes) { + static_assert( + static_cast<int>(api::passwords_private::COMPROMISE_TYPE_NONE) == + static_cast<int>(CompromiseTypeFlags::kNotCompromised), + ""); + static_assert( + static_cast<int>(api::passwords_private::COMPROMISE_TYPE_LEAKED) == + static_cast<int>(CompromiseTypeFlags::kCredentialLeaked), + ""); + static_assert( + static_cast<int>(api::passwords_private::COMPROMISE_TYPE_PHISHED) == + static_cast<int>(CompromiseTypeFlags::kCredentialPhished), + ""); + static_assert( + static_cast<int>( + api::passwords_private::COMPROMISE_TYPE_PHISHED_AND_LEAKED) == + static_cast<int>(CompromiseTypeFlags::kCredentialLeaked | + CompromiseTypeFlags::kCredentialPhished), + ""); +} + // Sets up the password store with a couple of passwords and compromised // credentials. Verifies that the result is ordered in such a way that phished // credentials are before leaked credentials and that within each group 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 15df0b1ca92..c66d3306367 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 @@ -77,7 +77,16 @@ ResponseAction PasswordsPrivateRemoveSavedPasswordFunction::Run() { auto parameters = api::passwords_private::RemoveSavedPassword::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters); - GetDelegate(browser_context())->RemoveSavedPassword(parameters->id); + GetDelegate(browser_context())->RemoveSavedPasswords({parameters->id}); + return RespondNow(NoArguments()); +} + +// PasswordsPrivateRemoveSavedPasswordsFunction +ResponseAction PasswordsPrivateRemoveSavedPasswordsFunction::Run() { + auto parameters = + api::passwords_private::RemoveSavedPasswords::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters); + GetDelegate(browser_context())->RemoveSavedPasswords(parameters->ids); return RespondNow(NoArguments()); } @@ -86,7 +95,16 @@ ResponseAction PasswordsPrivateRemovePasswordExceptionFunction::Run() { auto parameters = api::passwords_private::RemovePasswordException::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters); - GetDelegate(browser_context())->RemovePasswordException(parameters->id); + GetDelegate(browser_context())->RemovePasswordExceptions({parameters->id}); + return RespondNow(NoArguments()); +} + +// PasswordsPrivateRemovePasswordExceptionsFunction +ResponseAction PasswordsPrivateRemovePasswordExceptionsFunction::Run() { + auto parameters = + api::passwords_private::RemovePasswordExceptions::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters); + GetDelegate(browser_context())->RemovePasswordExceptions(parameters->ids); return RespondNow(NoArguments()); } @@ -176,6 +194,16 @@ void PasswordsPrivateGetPasswordExceptionListFunction::GotList( entries))); } +// PasswordsPrivateMovePasswordToAccountFunction +ResponseAction PasswordsPrivateMovePasswordToAccountFunction::Run() { + auto parameters = + api::passwords_private::MovePasswordToAccount::Params::Create(*args_); + EXTENSION_FUNCTION_VALIDATE(parameters); + GetDelegate(browser_context()) + ->MovePasswordToAccount(parameters->id, GetSenderWebContents()); + return RespondNow(NoArguments()); +} + // PasswordsPrivateImportPasswordsFunction ResponseAction PasswordsPrivateImportPasswordsFunction::Run() { GetDelegate(browser_context())->ImportPasswords(GetSenderWebContents()); 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 b374835ffa2..3505216b22a 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 @@ -54,6 +54,18 @@ class PasswordsPrivateRemoveSavedPasswordFunction : public ExtensionFunction { ResponseAction Run() override; }; +class PasswordsPrivateRemoveSavedPasswordsFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.removeSavedPasswords", + PASSWORDSPRIVATE_REMOVESAVEDPASSWORDS) + + protected: + ~PasswordsPrivateRemoveSavedPasswordsFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + class PasswordsPrivateRemovePasswordExceptionFunction : public ExtensionFunction { public: @@ -67,6 +79,19 @@ class PasswordsPrivateRemovePasswordExceptionFunction ResponseAction Run() override; }; +class PasswordsPrivateRemovePasswordExceptionsFunction + : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.removePasswordExceptions", + PASSWORDSPRIVATE_REMOVEPASSWORDEXCEPTIONS) + + protected: + ~PasswordsPrivateRemovePasswordExceptionsFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + class PasswordsPrivateUndoRemoveSavedPasswordOrExceptionFunction : public ExtensionFunction { public: @@ -131,6 +156,18 @@ class PasswordsPrivateGetPasswordExceptionListFunction void GotList(const PasswordsPrivateDelegate::ExceptionEntries& entries); }; +class PasswordsPrivateMovePasswordToAccountFunction : public ExtensionFunction { + public: + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.movePasswordToAccount", + PASSWORDSPRIVATE_MOVEPASSWORDTOACCOUNT) + + protected: + ~PasswordsPrivateMovePasswordToAccountFunction() override = default; + + // ExtensionFunction overrides. + ResponseAction Run() override; +}; + class PasswordsPrivateImportPasswordsFunction : public ExtensionFunction { public: DECLARE_EXTENSION_FUNCTION("passwordsPrivate.importPasswords", 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 eba6dbddfd3..45337a9fd68 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 @@ -108,6 +108,10 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { s_test_delegate_->AddCompromisedCredential(id); } + base::Optional<int> last_moved_password() const { + return s_test_delegate_->last_moved_password(); + } + private: TestPasswordsPrivateDelegate* s_test_delegate_ = nullptr; @@ -127,11 +131,23 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, } IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, + RemoveAndUndoRemoveSavedPasswordsBatch) { + EXPECT_TRUE(RunPasswordsSubtest("removeAndUndoRemoveSavedPasswordsBatch")) + << message_; +} + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RemoveAndUndoRemovePasswordException) { EXPECT_TRUE(RunPasswordsSubtest("removeAndUndoRemovePasswordException")) << message_; } +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, + RemoveAndUndoRemovePasswordExceptionsBatch) { + EXPECT_TRUE(RunPasswordsSubtest("removeAndUndoRemovePasswordExceptionsBatch")) + << message_; +} + IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RequestPlaintextPassword) { EXPECT_TRUE(RunPasswordsSubtest("requestPlaintextPassword")) << message_; } @@ -259,4 +275,10 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordCheckStatus) { EXPECT_TRUE(RunPasswordsSubtest("getPasswordCheckStatus")) << message_; } +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, MovePasswordToAccount) { + EXPECT_FALSE(last_moved_password().has_value()); + EXPECT_TRUE(RunPasswordsSubtest("movePasswordToAccount")) << message_; + EXPECT_EQ(42, last_moved_password()); +} + } // namespace extensions 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 b1c9d8c20f1..edb030dfe12 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 @@ -65,15 +65,13 @@ class PasswordsPrivateDelegate : public KeyedService { base::string16 new_username, base::Optional<base::string16> new_password) = 0; - // Removes the saved password entry corresponding to the |id| generated for - // each entry of the password list. - // |id| the id created when going over the list of saved passwords. - virtual void RemoveSavedPassword(int id) = 0; + // Removes the saved password entries corresponding to the |ids| generated for + // each entry of the password list. Any invalid id will be ignored. + virtual void RemoveSavedPasswords(const std::vector<int>& ids) = 0; - // Removes the saved password exception entry corresponding set in the - // given |id| - // |id| The id for the exception url entry being removed. - virtual void RemovePasswordException(int id) = 0; + // Removes the password exceptions entries corresponding corresponding to + // |ids|. Any invalid id will be ignored. + virtual void RemovePasswordExceptions(const std::vector<int>& ids) = 0; // Undoes the last removal of a saved password or exception. virtual void UndoRemoveSavedPasswordOrException() = 0; @@ -92,6 +90,14 @@ class PasswordsPrivateDelegate : public KeyedService { PlaintextPasswordCallback callback, content::WebContents* web_contents) = 0; + // Moves a password currently stored on the device to being stored in the + // signed-in, non-syncing Google Account. The result is a no-op if any of + // these is true: |id| is invalid; |id| corresponds to a password already + // stored in the account; or the user is not using the account-scoped password + // storage. + virtual void MovePasswordToAccount(int id, + content::WebContents* web_contents) = 0; + // Trigger the password import procedure, allowing the user to select a file // containing passwords to import. virtual void ImportPasswords(content::WebContents* web_contents) = 0; 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 2aaf4ec2863..b23254c66fc 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 @@ -29,6 +29,7 @@ #include "components/password_manager/core/browser/ui/plaintext_reason.h" #include "components/password_manager/core/common/password_manager_features.h" #include "components/prefs/pref_service.h" +#include "components/signin/public/base/signin_metrics.h" #include "content/public/browser/web_contents.h" #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/l10n/l10n_util.h" @@ -122,6 +123,20 @@ password_manager::PlaintextReason ConvertPlaintextReason( return password_manager::PlaintextReason::kView; } +// Gets all the existing keys in |generator| corresponding to |ids|. If no key +// is found for an id, it is simply ignored. +std::vector<std::string> GetSortKeys( + const extensions::IdGenerator<std::string>& generator, + const std::vector<int> ids) { + std::vector<std::string> sort_keys; + sort_keys.reserve(ids.size()); + for (int id : ids) { + if (const std::string* sort_key = generator.TryGetKey(id)) + sort_keys.emplace_back(*sort_key); + } + return sort_keys; +} + } // namespace namespace extensions { @@ -197,28 +212,30 @@ void PasswordsPrivateDelegateImpl::ChangeSavedPassword( *sort_key, std::move(new_username), std::move(new_password)); } -void PasswordsPrivateDelegateImpl::RemoveSavedPassword(int id) { +void PasswordsPrivateDelegateImpl::RemoveSavedPasswords( + const std::vector<int>& ids) { ExecuteFunction( - base::Bind(&PasswordsPrivateDelegateImpl::RemoveSavedPasswordInternal, - base::Unretained(this), id)); + base::Bind(&PasswordsPrivateDelegateImpl::RemoveSavedPasswordsInternal, + base::Unretained(this), ids)); } -void PasswordsPrivateDelegateImpl::RemoveSavedPasswordInternal(int id) { - const std::string* sort_key = password_id_generator_.TryGetKey(id); - if (sort_key) - password_manager_presenter_->RemoveSavedPassword(*sort_key); +void PasswordsPrivateDelegateImpl::RemoveSavedPasswordsInternal( + const std::vector<int>& ids) { + password_manager_presenter_->RemoveSavedPasswords( + GetSortKeys(password_id_generator_, ids)); } -void PasswordsPrivateDelegateImpl::RemovePasswordException(int id) { - ExecuteFunction( - base::Bind(&PasswordsPrivateDelegateImpl::RemovePasswordExceptionInternal, - base::Unretained(this), id)); +void PasswordsPrivateDelegateImpl::RemovePasswordExceptions( + const std::vector<int>& ids) { + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::RemovePasswordExceptionsInternal, + base::Unretained(this), ids)); } -void PasswordsPrivateDelegateImpl::RemovePasswordExceptionInternal(int id) { - const std::string* sort_key = exception_id_generator_.TryGetKey(id); - if (sort_key) - password_manager_presenter_->RemovePasswordException(*sort_key); +void PasswordsPrivateDelegateImpl::RemovePasswordExceptionsInternal( + const std::vector<int>& ids) { + password_manager_presenter_->RemovePasswordExceptions( + GetSortKeys(exception_id_generator_, ids)); } void PasswordsPrivateDelegateImpl::UndoRemoveSavedPasswordOrException() { @@ -267,8 +284,10 @@ void PasswordsPrivateDelegateImpl::RequestPlaintextPassword( std::move(callback).Run(base::nullopt); return; } - ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) - .WriteText(*password); + ui::ScopedClipboardWriter clipboard_writer( + ui::ClipboardBuffer::kCopyPaste); + clipboard_writer.WriteText(*password); + clipboard_writer.MarkAsConfidential(); std::move(callback).Run(base::string16()); }, std::move(callback)); @@ -325,6 +344,9 @@ void PasswordsPrivateDelegateImpl::SetPasswordList( entry.username = base::UTF16ToUTF8(form->username_value); entry.id = password_id_generator_.GenerateId( password_manager::CreateSortKey(*form)); + entry.frontend_id = password_frontend_id_generator_.GenerateId( + password_manager::CreateSortKey(*form, + password_manager::IgnoreStore(true))); if (!form->federation_origin.opaque()) { entry.federation_text.reset(new std::string(l10n_util::GetStringFUTF8( @@ -360,6 +382,10 @@ void PasswordsPrivateDelegateImpl::SetPasswordExceptionList( current_exception_entry.urls = CreateUrlCollectionFromForm(*form); current_exception_entry.id = exception_id_generator_.GenerateId( password_manager::CreateSortKey(*form)); + current_exception_entry.frontend_id = + exception_frontend_id_generator_.GenerateId( + password_manager::CreateSortKey( + *form, password_manager::IgnoreStore(true))); current_exception_entry.from_account_store = form->IsUsingAccountStore(); current_exceptions_.push_back(std::move(current_exception_entry)); @@ -378,6 +404,18 @@ void PasswordsPrivateDelegateImpl::SetPasswordExceptionList( get_password_exception_list_callbacks_.clear(); } +void PasswordsPrivateDelegateImpl::MovePasswordToAccount( + int id, + content::WebContents* web_contents) { + auto* client = ChromePasswordManagerClient::FromWebContents(web_contents); + // TODO(victorvianna): Use a DCHECK instead. + if (!client) + return; + + if (const std::string* sort_key = password_id_generator_.TryGetKey(id)) + password_manager_presenter_->MovePasswordToAccountStore(*sort_key, client); +} + void PasswordsPrivateDelegateImpl::ImportPasswords( content::WebContents* web_contents) { password_manager_porter_->set_web_contents(web_contents); @@ -434,7 +472,8 @@ void PasswordsPrivateDelegateImpl::SetAccountStorageOptIn( return; } // The opt in pref is automatically set upon successful reauth. - client->TriggerReauthForPrimaryAccount(base::DoNothing()); + client->TriggerReauthForPrimaryAccount( + signin_metrics::ReauthAccessPoint::kPasswordSettings, base::DoNothing()); } std::vector<api::passwords_private::CompromisedCredential> 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 5e37007baf2..10de06d47d2 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 @@ -53,13 +53,15 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, int id, base::string16 new_username, base::Optional<base::string16> new_password) override; - void RemoveSavedPassword(int id) override; - void RemovePasswordException(int id) override; + void RemoveSavedPasswords(const std::vector<int>& ids) override; + void RemovePasswordExceptions(const std::vector<int>& ids) override; void UndoRemoveSavedPasswordOrException() override; void RequestPlaintextPassword(int id, api::passwords_private::PlaintextReason reason, PlaintextPasswordCallback callback, content::WebContents* web_contents) override; + void MovePasswordToAccount(int id, + content::WebContents* web_contents) override; void ImportPasswords(content::WebContents* web_contents) override; void ExportPasswords(base::OnceCallback<void(const std::string&)> accepted, content::WebContents* web_contents) override; @@ -123,8 +125,8 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, void SendSavedPasswordsList(); void SendPasswordExceptionsList(); - void RemoveSavedPasswordInternal(int id); - void RemovePasswordExceptionInternal(int id); + void RemoveSavedPasswordsInternal(const std::vector<int>& ids); + void RemovePasswordExceptionsInternal(const std::vector<int>& ids); void UndoRemoveSavedPasswordOrExceptionInternal(); // Callback for when the password list has been written to the destination. @@ -162,7 +164,9 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, // Generators that map between sort keys used by |password_manager_presenter_| // and ids used by the JavaScript front end. IdGenerator<std::string> password_id_generator_; + IdGenerator<std::string> password_frontend_id_generator_; IdGenerator<std::string> exception_id_generator_; + IdGenerator<std::string> exception_frontend_id_generator_; // Whether SetPasswordList and SetPasswordExceptionList have been called, and // whether this class has been initialized, meaning both have been called. 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 015feb23ab7..7871953f2e6 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 @@ -36,6 +36,7 @@ #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/test_password_store.h" +#include "components/signin/public/base/signin_metrics.h" #include "content/public/browser/browser_context.h" #include "content/public/test/browser_task_environment.h" #include "content/public/test/test_renderer_host.h" @@ -49,9 +50,11 @@ using MockReauthCallback = base::MockCallback< using PasswordFormList = std::vector<std::unique_ptr<autofill::PasswordForm>>; using password_manager::ReauthPurpose; using password_manager::TestPasswordStore; +using ::testing::_; using ::testing::Eq; using ::testing::Ne; using ::testing::Return; +using ::testing::SizeIs; using ::testing::StrictMock; namespace extensions { @@ -84,7 +87,8 @@ class MockPasswordManagerClient : public ChromePasswordManagerClient { // ChromePasswordManagerClient overrides. MOCK_METHOD(void, TriggerReauthForPrimaryAccount, - (base::OnceCallback<void(ReauthSucceeded)>), + (signin_metrics::ReauthAccessPoint, + base::OnceCallback<void(ReauthSucceeded)>), (override)); const password_manager::MockPasswordFeatureManager* GetPasswordFeatureManager() const override { @@ -168,7 +172,7 @@ std::unique_ptr<KeyedService> BuildPasswordsPrivateEventRouter( autofill::PasswordForm CreateSampleForm() { autofill::PasswordForm form; form.signon_realm = "http://abc1.com"; - form.origin = GURL("http://abc1.com"); + form.url = GURL("http://abc1.com"); form.username_value = base::ASCIIToUTF16("test@gmail.com"); form.password_value = base::ASCIIToUTF16("test"); return form; @@ -247,6 +251,34 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetSavedPasswordsList) { delegate.GetSavedPasswordsList(callback.Get()); } +TEST_F(PasswordsPrivateDelegateImplTest, + PasswordsDuplicatedInStoresHaveSameFrontendId) { + PasswordsPrivateDelegateImpl delegate(&profile_); + + auto account_password = std::make_unique<autofill::PasswordForm>(); + account_password->in_store = autofill::PasswordForm::Store::kAccountStore; + auto profile_password = std::make_unique<autofill::PasswordForm>(); + profile_password->in_store = autofill::PasswordForm::Store::kProfileStore; + + PasswordFormList list; + list.push_back(std::move(account_password)); + list.push_back(std::move(profile_password)); + + delegate.SetPasswordList(list); + + base::MockCallback<PasswordsPrivateDelegate::UiEntriesCallback> callback; + int first_frontend_id, second_frontend_id; + EXPECT_CALL(callback, Run(SizeIs(2))) + .WillOnce([&](const PasswordsPrivateDelegate::UiEntries& passwords) { + first_frontend_id = passwords[0].frontend_id; + second_frontend_id = passwords[1].frontend_id; + }); + + delegate.GetSavedPasswordsList(callback.Get()); + + EXPECT_EQ(first_frontend_id, second_frontend_id); +} + TEST_F(PasswordsPrivateDelegateImplTest, GetPasswordExceptionsList) { PasswordsPrivateDelegateImpl delegate(&profile_); @@ -265,6 +297,38 @@ TEST_F(PasswordsPrivateDelegateImplTest, GetPasswordExceptionsList) { delegate.GetPasswordExceptionsList(callback.Get()); } +TEST_F(PasswordsPrivateDelegateImplTest, + ExceptionsDuplicatedInStoresHaveSameFrontendId) { + PasswordsPrivateDelegateImpl delegate(&profile_); + + auto account_exception = std::make_unique<autofill::PasswordForm>(); + account_exception->blacklisted_by_user = true; + account_exception->in_store = autofill::PasswordForm::Store::kAccountStore; + auto profile_exception = std::make_unique<autofill::PasswordForm>(); + profile_exception->blacklisted_by_user = true; + profile_exception->in_store = autofill::PasswordForm::Store::kProfileStore; + + PasswordFormList list; + list.push_back(std::move(account_exception)); + list.push_back(std::move(profile_exception)); + + delegate.SetPasswordExceptionList(list); + + base::MockCallback<PasswordsPrivateDelegate::ExceptionEntriesCallback> + callback; + int first_frontend_id, second_frontend_id; + EXPECT_CALL(callback, Run(SizeIs(2))) + .WillOnce( + [&](const PasswordsPrivateDelegate::ExceptionEntries& exceptions) { + first_frontend_id = exceptions[0].frontend_id; + second_frontend_id = exceptions[1].frontend_id; + }); + + delegate.GetPasswordExceptionsList(callback.Get()); + + EXPECT_EQ(first_frontend_id, second_frontend_id); +} + TEST_F(PasswordsPrivateDelegateImplTest, ChangeSavedPassword) { autofill::PasswordForm sample_form = CreateSampleForm(); SetUpPasswordStore({sample_form}); @@ -348,7 +412,9 @@ TEST_F(PasswordsPrivateDelegateImplTest, TestShouldReauthForOptIn) { ON_CALL(*(client->GetPasswordFeatureManager()), IsOptedInForAccountStorage) .WillByDefault(Return(false)); - EXPECT_CALL(*client, TriggerReauthForPrimaryAccount); + EXPECT_CALL(*client, + TriggerReauthForPrimaryAccount( + signin_metrics::ReauthAccessPoint::kPasswordSettings, _)); PasswordsPrivateDelegateImpl delegate(&profile_); delegate.SetAccountStorageOptIn(true, web_contents.get()); @@ -368,7 +434,10 @@ TEST_F(PasswordsPrivateDelegateImplTest, ON_CALL(*feature_manager, IsOptedInForAccountStorage) .WillByDefault(Return(true)); - EXPECT_CALL(*client, TriggerReauthForPrimaryAccount).Times(0); + EXPECT_CALL(*client, + TriggerReauthForPrimaryAccount( + signin_metrics::ReauthAccessPoint::kPasswordSettings, _)) + .Times(0); EXPECT_CALL(*feature_manager, OptOutOfAccountStorageAndClearSettings); PasswordsPrivateDelegateImpl delegate(&profile_); diff --git a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_unittest.cc b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_unittest.cc index 574cac203aa..bca700f8a15 100644 --- a/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_unittest.cc +++ b/chromium/chrome/browser/extensions/api/passwords_private/passwords_private_utils_unittest.cc @@ -13,8 +13,8 @@ namespace extensions { TEST(CreateUrlCollectionFromFormTest, UrlsFromHtmlForm) { autofill::PasswordForm html_form; - html_form.origin = GURL("http://example.com/LoginAuth"); - html_form.signon_realm = html_form.origin.GetOrigin().spec(); + html_form.url = GURL("http://example.com/LoginAuth"); + html_form.signon_realm = html_form.url.GetOrigin().spec(); api::passwords_private::UrlCollection html_urls = CreateUrlCollectionFromForm(html_form); @@ -26,7 +26,7 @@ TEST(CreateUrlCollectionFromFormTest, UrlsFromHtmlForm) { TEST(CreateUrlCollectionFromFormTest, UrlsFromFederatedForm) { autofill::PasswordForm federated_form; federated_form.signon_realm = "federation://example.com/google.com"; - federated_form.origin = GURL("https://example.com/"); + federated_form.url = GURL("https://example.com/"); federated_form.federation_origin = url::Origin::Create(GURL("https://google.com/")); 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 6f0005f6cf9..593c0d9a140 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,6 +26,7 @@ api::passwords_private::PasswordUiEntry CreateEntry(int id) { entry.urls.link = entry.urls.origin; entry.username = "testName" + base::NumberToString(id); entry.id = id; + entry.frontend_id = id; return entry; } @@ -35,6 +36,7 @@ api::passwords_private::ExceptionEntry CreateException(int id) { exception.urls.origin = "http://" + exception.urls.shown + "/login"; exception.urls.link = exception.urls.origin; exception.id = id; + exception.frontend_id = id; return exception; } } // namespace @@ -72,36 +74,55 @@ void TestPasswordsPrivateDelegate::ChangeSavedPassword( SendSavedPasswordsList(); } -void TestPasswordsPrivateDelegate::RemoveSavedPassword(int id) { +void TestPasswordsPrivateDelegate::RemoveSavedPasswords( + const std::vector<int>& ids) { if (current_entries_.empty()) return; - // Since this is just mock data, remove the first entry regardless of - // the data contained. - last_deleted_entry_ = std::move(current_entries_.front()); - current_entries_.erase(current_entries_.begin()); + // Since this is just mock data, remove the first |ids.size()| elements + // regardless of the data contained. + auto first_remaining = (ids.size() <= current_entries_.size()) + ? current_entries_.begin() + ids.size() + : current_entries_.end(); + last_deleted_entries_batch_.assign( + std::make_move_iterator(current_entries_.begin()), + std::make_move_iterator(first_remaining)); + current_entries_.erase(current_entries_.begin(), first_remaining); SendSavedPasswordsList(); } -void TestPasswordsPrivateDelegate::RemovePasswordException(int id) { - // Since this is just mock data, remove the first entry regardless of - // the data contained. - last_deleted_exception_ = std::move(current_exceptions_.front()); - current_exceptions_.erase(current_exceptions_.begin()); +void TestPasswordsPrivateDelegate::RemovePasswordExceptions( + const std::vector<int>& ids) { + if (current_exceptions_.empty()) + return; + + // Since this is just mock data, remove the first |ids.size()| elements + // regardless of the data contained. + auto first_remaining = (ids.size() <= current_exceptions_.size()) + ? current_exceptions_.begin() + ids.size() + : current_exceptions_.end(); + last_deleted_exceptions_batch_.assign( + std::make_move_iterator(current_exceptions_.begin()), + std::make_move_iterator(first_remaining)); + current_exceptions_.erase(current_exceptions_.begin(), first_remaining); SendPasswordExceptionsList(); } // Simplified version of undo logic, only use for testing. void TestPasswordsPrivateDelegate::UndoRemoveSavedPasswordOrException() { - if (last_deleted_entry_) { - current_entries_.insert(current_entries_.begin(), - std::move(*last_deleted_entry_)); - last_deleted_entry_ = base::nullopt; + if (!last_deleted_entries_batch_.empty()) { + current_entries_.insert( + current_entries_.begin(), + std::make_move_iterator(last_deleted_entries_batch_.begin()), + std::make_move_iterator(last_deleted_entries_batch_.end())); + last_deleted_entries_batch_.clear(); SendSavedPasswordsList(); - } else if (last_deleted_exception_) { - current_exceptions_.insert(current_exceptions_.begin(), - std::move(*last_deleted_exception_)); - last_deleted_exception_ = base::nullopt; + } else if (!last_deleted_exceptions_batch_.empty()) { + current_exceptions_.insert( + current_exceptions_.begin(), + std::make_move_iterator(last_deleted_exceptions_batch_.begin()), + std::make_move_iterator(last_deleted_exceptions_batch_.end())); + last_deleted_exceptions_batch_.clear(); SendPasswordExceptionsList(); } } @@ -115,6 +136,12 @@ void TestPasswordsPrivateDelegate::RequestPlaintextPassword( std::move(callback).Run(plaintext_password_); } +void TestPasswordsPrivateDelegate::MovePasswordToAccount( + int id, + content::WebContents* web_contents) { + last_moved_password_ = id; +} + void TestPasswordsPrivateDelegate::ImportPasswords( content::WebContents* web_contents) { // The testing of password importing itself should be handled via 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 639c9092fc4..3b88cedf525 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 @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_TEST_PASSWORDS_PRIVATE_DELEGATE_H_ #define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_TEST_PASSWORDS_PRIVATE_DELEGATE_H_ +#include "base/optional.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" #include "chrome/browser/profiles/profile.h" @@ -25,14 +26,16 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { void ChangeSavedPassword(int id, base::string16 username, base::Optional<base::string16> password) override; - void RemoveSavedPassword(int id) override; - void RemovePasswordException(int id) override; + void RemoveSavedPasswords(const std::vector<int>& id) override; + void RemovePasswordExceptions(const std::vector<int>& ids) override; // Simplified version of undo logic, only use for testing. void UndoRemoveSavedPasswordOrException() override; void RequestPlaintextPassword(int id, api::passwords_private::PlaintextReason reason, PlaintextPasswordCallback callback, content::WebContents* web_contents) override; + void MovePasswordToAccount(int id, + content::WebContents* web_contents) override; void ImportPasswords(content::WebContents* web_contents) override; void ExportPasswords(base::OnceCallback<void(const std::string&)> callback, content::WebContents* web_contents) override; @@ -84,6 +87,10 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { start_password_check_state_ = state; } + base::Optional<int> last_moved_password() const { + return last_moved_password_; + } + private: void SendSavedPasswordsList(); void SendPasswordExceptionsList(); @@ -93,11 +100,15 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { // having to request them from |password_manager_presenter_| again. std::vector<api::passwords_private::PasswordUiEntry> current_entries_; std::vector<api::passwords_private::ExceptionEntry> current_exceptions_; - // Simplified version of a undo manager that only allows undoing and redoing - // the very last deletion. - base::Optional<api::passwords_private::PasswordUiEntry> last_deleted_entry_; - base::Optional<api::passwords_private::ExceptionEntry> - last_deleted_exception_; + + // Simplified version of an undo manager that only allows undoing and redoing + // the very last deletion. When the batches are *empty*, this means there is + // no previous deletion to undo. + std::vector<api::passwords_private::PasswordUiEntry> + last_deleted_entries_batch_; + std::vector<api::passwords_private::ExceptionEntry> + last_deleted_exceptions_batch_; + base::Optional<base::string16> plaintext_password_ = base::ASCIIToUTF16("plaintext"); @@ -118,6 +129,9 @@ class TestPasswordsPrivateDelegate : public PasswordsPrivateDelegate { bool stop_password_check_triggered_ = false; password_manager::BulkLeakCheckService::State start_password_check_state_ = password_manager::BulkLeakCheckService::State::kRunning; + + // Records the id of the last password that was moved. + base::Optional<int> last_moved_password_ = base::nullopt; }; } // namespace extensions 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 be4e536005b..366ae43ac65 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 @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/stl_util.h" +#include "base/strings/string_piece.h" #include "base/values.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h" @@ -37,6 +38,8 @@ const char kErrorInteractiveCallFromBackground[] = "Interactive calls must happen in the context of a browser tab or a " "window."; +const char kWebCryptoEcdsa[] = "ECDSA"; +const char kWebCryptoNamedCurveP256[] = "P-256"; const char kWebCryptoRSASSA_PKCS1_v1_5[] = "RSASSA-PKCS1-v1_5"; struct PublicKeyInfo { @@ -58,7 +61,7 @@ struct PublicKeyInfo { void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info, base::DictionaryValue* algorithm) { CHECK_EQ(net::X509Certificate::kPublicKeyTypeRSA, key_info.key_type); - algorithm->SetKey("name", base::Value(kWebCryptoRSASSA_PKCS1_v1_5)); + algorithm->SetStringKey("name", kWebCryptoRSASSA_PKCS1_v1_5); algorithm->SetKey("modulusLength", base::Value(static_cast<int>(key_info.key_size_bits))); @@ -71,6 +74,15 @@ void BuildWebCryptoRSAAlgorithmDictionary(const PublicKeyInfo& key_info, base::size(defaultPublicExponent))); } +void BuildWebCryptoEcdsaAlgorithmDictionary(const PublicKeyInfo& key_info, + base::DictionaryValue* algorithm) { + CHECK_EQ(net::X509Certificate::kPublicKeyTypeECDSA, key_info.key_type); + algorithm->SetStringKey("name", kWebCryptoEcdsa); + + // Only P-256 named curve is supported. + algorithm->SetStringKey("namedCurve", kWebCryptoNamedCurveP256); +} + const struct NameValuePair { const char* const name; const int value; @@ -84,6 +96,7 @@ const struct NameValuePair { namespace platform_keys { +const char kErrorInvalidSpki[] = "The SubjectPublicKeyInfo is not valid."; const char kErrorInvalidToken[] = "The token is not valid."; const char kErrorInvalidX509Cert[] = "Certificate is not a valid X.509 certificate."; @@ -145,25 +158,85 @@ PlatformKeysInternalGetPublicKeyFunction::Run() { chromeos::platform_keys::GetSubjectPublicKeyInfo(cert_x509); if (!chromeos::platform_keys::GetPublicKey(cert_x509, &key_info.key_type, &key_info.key_size_bits) || + (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA && + key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA)) { + return RespondNow(Error(kErrorAlgorithmNotSupported)); + } + + // Currently, the only supported combinations are: + // 1- A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used + // with the RSASSA-PKCS1-v1.5 algorithm. + // 2- A certificate declaring id-ecPublicKey in the SubjectPublicKeyInfo used + // with the ECDSA algorithm. + if (params->algorithm_name == kWebCryptoRSASSA_PKCS1_v1_5) { + if (key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) { + return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate)); + } + + api_pki::GetPublicKey::Results::Algorithm algorithm; + BuildWebCryptoRSAAlgorithmDictionary(key_info, + &algorithm.additional_properties); + + return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create( + std::vector<uint8_t>(key_info.public_key_spki_der.begin(), + key_info.public_key_spki_der.end()), + algorithm))); + } + + if (params->algorithm_name == kWebCryptoEcdsa) { + if (key_info.key_type != net::X509Certificate::kPublicKeyTypeECDSA) { + return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate)); + } + + api_pki::GetPublicKey::Results::Algorithm algorithm; + BuildWebCryptoEcdsaAlgorithmDictionary(key_info, + &algorithm.additional_properties); + + return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create( + std::vector<uint8_t>(key_info.public_key_spki_der.begin(), + key_info.public_key_spki_der.end()), + algorithm))); + } + + return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate)); +} + +PlatformKeysInternalGetPublicKeyBySpkiFunction:: + ~PlatformKeysInternalGetPublicKeyBySpkiFunction() = default; + +ExtensionFunction::ResponseAction +PlatformKeysInternalGetPublicKeyBySpkiFunction::Run() { + std::unique_ptr<api_pki::GetPublicKeyBySpki::Params> params( + api_pki::GetPublicKeyBySpki::Params::Create(*args_)); + EXTENSION_FUNCTION_VALIDATE(params); + + const auto& public_key_spki_der = params->public_key_spki_der; + if (public_key_spki_der.empty()) + return RespondNow(Error(platform_keys::kErrorInvalidSpki)); + + PublicKeyInfo key_info; + key_info.public_key_spki_der.assign(std::begin(public_key_spki_der), + std::end(public_key_spki_der)); + + if (!chromeos::platform_keys::GetPublicKeyBySpki(key_info.public_key_spki_der, + &key_info.key_type, + &key_info.key_size_bits) || key_info.key_type != net::X509Certificate::kPublicKeyTypeRSA) { return RespondNow(Error(kErrorAlgorithmNotSupported)); } // Currently, the only supported combination is: - // A certificate declaring rsaEncryption in the SubjectPublicKeyInfo used - // with the RSASSA-PKCS1-v1.5 algorithm. + // A SPKI declaring rsaEncryption used with the RSASSA-PKCS1-v1.5 algorithm. if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) { - return RespondNow(Error(kErrorAlgorithmNotPermittedByCertificate)); + return RespondNow(Error(kErrorAlgorithmNotSupported)); } - api_pki::GetPublicKey::Results::Algorithm algorithm; + api_pki::GetPublicKeyBySpki::Results::Algorithm algorithm; BuildWebCryptoRSAAlgorithmDictionary(key_info, &algorithm.additional_properties); - return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create( - std::vector<uint8_t>(key_info.public_key_spki_der.begin(), - key_info.public_key_spki_der.end()), - algorithm))); + return RespondNow(ArgumentList(api_pki::GetPublicKeyBySpki::Results::Create( + public_key_spki_der, algorithm))); } PlatformKeysInternalSelectClientCertificatesFunction:: @@ -305,7 +378,7 @@ ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() { if (params->hash_algorithm_name == "none") { // Signing without digesting is only supported for RSASSA-PKCS1-v1_5. - if (params->algorithm_name != "RSASSA-PKCS1-v1_5") + if (params->algorithm_name != kWebCryptoRSASSA_PKCS1_v1_5) return RespondNow(Error(kErrorAlgorithmNotSupported)); service->SignRSAPKCS1Raw( @@ -329,9 +402,9 @@ ExtensionFunction::ResponseAction PlatformKeysInternalSignFunction::Run() { } chromeos::platform_keys::KeyType key_type; - if (params->algorithm_name == "RSASSA-PKCS1-v1_5") { + if (params->algorithm_name == kWebCryptoRSASSA_PKCS1_v1_5) { key_type = chromeos::platform_keys::KeyType::kRsassaPkcs1V15; - } else if (params->algorithm_name == "ECDSA") { + } else if (params->algorithm_name == kWebCryptoEcdsa) { key_type = chromeos::platform_keys::KeyType::kEcdsa; } else { return RespondNow(Error(kErrorAlgorithmNotSupported)); diff --git a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.h b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.h index 4d1ece03673..fe503135b62 100644 --- a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.h +++ b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_api.h @@ -56,6 +56,16 @@ class PlatformKeysInternalGetPublicKeyFunction : public ExtensionFunction { PLATFORMKEYSINTERNAL_GETPUBLICKEY) }; +class PlatformKeysInternalGetPublicKeyBySpkiFunction + : public ExtensionFunction { + private: + ~PlatformKeysInternalGetPublicKeyBySpkiFunction() override; + ResponseAction Run() override; + + DECLARE_EXTENSION_FUNCTION("platformKeysInternal.getPublicKeyBySpki", + PLATFORMKEYSINTERNAL_GETPUBLICKEYBYSPKI) +}; + class PlatformKeysInternalSignFunction : public ExtensionFunction { private: ~PlatformKeysInternalSignFunction() override; diff --git a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc index 1b2bcb7417a..c5b730978bb 100644 --- a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc +++ b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc @@ -12,7 +12,6 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/strings/stringprintf.h" -#include "base/task/post_task.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h" #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h" #include "chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h" @@ -21,6 +20,7 @@ #include "chrome/browser/profiles/profile.h" #include "components/policy/policy_constants.h" #include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" #include "content/public/test/browser_test.h" #include "crypto/nss_util_internal.h" #include "crypto/scoped_nss_types.h" @@ -53,8 +53,8 @@ class PlatformKeysTest : public PlatformKeysTestBase { // |PlatformKeysTestBase::SetUpOnMainThread| triggers the user sign-in. ASSERT_TRUE(user_private_slot_db_.is_open()); base::RunLoop loop; - base::PostTaskAndReply( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTaskAndReply( + FROM_HERE, base::BindOnce(&PlatformKeysTest::SetPrivateSoftwareSlotOnIO, base::Unretained(this), crypto::ScopedPK11Slot( diff --git a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc index 166d76a626f..b1847d07f98 100644 --- a/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc +++ b/chromium/chrome/browser/extensions/api/platform_keys/platform_keys_test_base.cc @@ -7,7 +7,6 @@ #include "base/bind.h" #include "base/path_service.h" #include "base/run_loop.h" -#include "base/task/post_task.h" #include "chrome/browser/chromeos/policy/affiliation_test_helper.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/ui/browser.h" @@ -155,8 +154,8 @@ void PlatformKeysTestBase::SetUpOnMainThread() { if (system_token_status() == SystemTokenStatus::EXISTS) { base::RunLoop loop; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PlatformKeysTestBase::SetUpTestSystemSlotOnIO, base::Unretained(this), loop.QuitClosure())); loop.Run(); @@ -170,8 +169,8 @@ void PlatformKeysTestBase::TearDownOnMainThread() { if (system_token_status() == SystemTokenStatus::EXISTS) { base::RunLoop loop; - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&PlatformKeysTestBase::TearDownTestSystemSlotOnIO, base::Unretained(this), loop.QuitClosure())); loop.Run(); @@ -211,14 +210,14 @@ void PlatformKeysTestBase::SetUpTestSystemSlotOnIO( PrepareTestSystemSlotOnIO(test_system_slot_.get()); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - std::move(done_callback)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + std::move(done_callback)); } void PlatformKeysTestBase::TearDownTestSystemSlotOnIO( base::OnceClosure done_callback) { test_system_slot_.reset(); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - std::move(done_callback)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + std::move(done_callback)); } diff --git a/chromium/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc b/chromium/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc index 6f382e521ba..1daf379593b 100644 --- a/chromium/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc +++ b/chromium/chrome/browser/extensions/api/platform_keys/verify_trust_api.cc @@ -12,7 +12,6 @@ #include "base/lazy_instance.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/platform_keys/platform_keys_api.h" #include "chrome/common/extensions/api/platform_keys_internal.h" #include "content/public/browser/browser_task_traits.h" @@ -113,8 +112,8 @@ void VerifyTrustAPI::Verify(std::unique_ptr<Params> params, &CallBackOnUI, base::Bind(&VerifyTrustAPI::FinishedVerificationOnUI, weak_factory_.GetWeakPtr(), ui_callback))); - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&IOPart::Verify, base::Unretained(io_part_.get()), std::move(params), extension_id, finish_callback)); } @@ -123,8 +122,8 @@ void VerifyTrustAPI::OnExtensionUnloaded( content::BrowserContext* browser_context, const Extension* extension, UnloadedExtensionReason reason) { - base::PostTask( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&IOPart::OnExtensionUnloaded, base::Unretained(io_part_.get()), extension->id())); } @@ -143,8 +142,8 @@ void VerifyTrustAPI::CallBackOnUI(const VerifyCallback& ui_callback, const std::string& error, int return_value, int cert_status) { - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(ui_callback, error, return_value, cert_status)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(ui_callback, error, return_value, cert_status)); } VerifyTrustAPI::IOPart::~IOPart() { diff --git a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc index 3dfd9b014ce..e11e90a7f90 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc @@ -42,7 +42,7 @@ class ExtensionPreferenceApiTest : public extensions::ExtensionApiTest { protected: - ExtensionPreferenceApiTest() : profile_(NULL) {} + ExtensionPreferenceApiTest() : profile_(nullptr) {} void CheckPreferencesSet() { PrefService* prefs = profile_->GetPrefs(); @@ -168,7 +168,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, Standard) { const char kExtensionPath[] = "preference/standard"; - EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + EXPECT_TRUE(RunExtensionTest(kExtensionPath)) << message_; CheckPreferencesSet(); // The settings should not be reset when the extension is reloaded. diff --git a/chromium/chrome/browser/extensions/api/processes/processes_api.cc b/chromium/chrome/browser/extensions/api/processes/processes_api.cc index bd0417665c3..112a293b669 100644 --- a/chromium/chrome/browser/extensions/api/processes/processes_api.cc +++ b/chromium/chrome/browser/extensions/api/processes/processes_api.cc @@ -17,7 +17,6 @@ #include "base/process/process.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/tabs/tabs_constants.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/profiles/profile.h" @@ -512,8 +511,8 @@ ExtensionFunction::ResponseAction ProcessesTerminateFunction::Run() { // This could be a non-renderer child process like a plugin or a nacl // process. Try to get its handle from the BrowserChildProcessHost on the // IO thread. - base::PostTaskAndReplyWithResult( - FROM_HERE, {content::BrowserThread::IO}, + content::GetIOThreadTaskRunner({})->PostTaskAndReplyWithResult( + FROM_HERE, base::BindOnce(&ProcessesTerminateFunction::GetProcessHandleOnIO, this, child_process_host_id_), base::BindOnce(&ProcessesTerminateFunction::OnProcessHandleOnUI, this)); diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc index 153d46d923d..1a56cf910e8 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc @@ -18,6 +18,7 @@ #include <utility> #include "base/base64.h" +#include "base/notreached.h" #include "base/strings/string_tokenizer.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" diff --git a/chromium/chrome/browser/extensions/api/resources_private/resources_private_api.cc b/chromium/chrome/browser/extensions/api/resources_private/resources_private_api.cc index 24faa7952af..a3597241802 100644 --- a/chromium/chrome/browser/extensions/api/resources_private/resources_private_api.cc +++ b/chromium/chrome/browser/extensions/api/resources_private/resources_private_api.cc @@ -10,21 +10,15 @@ #include "base/values.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/extensions/api/resources_private.h" #include "chrome/grit/generated_resources.h" -#include "components/strings/grit/components_strings.h" -#include "components/zoom/page_zoom_constants.h" #include "pdf/buildflags.h" #include "printing/buildflags/buildflags.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" #if BUILDFLAG(ENABLE_PDF) -#include "pdf/pdf_features.h" -#if defined(OS_CHROMEOS) -#include "chrome/browser/chromeos/login/ui/login_display_host.h" -#endif // defined(OS_CHROMEOS) +#include "chrome/browser/pdf/pdf_extension_util.h" #endif // BUILDFLAG(ENABLE_PDF) // To add a new component to this API, simply: @@ -42,109 +36,6 @@ void AddStringsForIdentity(base::DictionaryValue* dict) { l10n_util::GetStringUTF16(IDS_EXTENSION_CONFIRM_PERMISSIONS)); } -void AddStringsForPdf(base::DictionaryValue* dict) { -#if BUILDFLAG(ENABLE_PDF) - static constexpr webui::LocalizedString kPdfResources[] = { - {"passwordDialogTitle", IDS_PDF_PASSWORD_DIALOG_TITLE}, - {"passwordPrompt", IDS_PDF_NEED_PASSWORD}, - {"passwordSubmit", IDS_PDF_PASSWORD_SUBMIT}, - {"passwordInvalid", IDS_PDF_PASSWORD_INVALID}, - {"pageLoading", IDS_PDF_PAGE_LOADING}, - {"pageLoadFailed", IDS_PDF_PAGE_LOAD_FAILED}, - {"errorDialogTitle", IDS_PDF_ERROR_DIALOG_TITLE}, - {"pageReload", IDS_PDF_PAGE_RELOAD_BUTTON}, - {"bookmarks", IDS_PDF_BOOKMARKS}, - {"labelPageNumber", IDS_PDF_LABEL_PAGE_NUMBER}, - {"tooltipRotateCW", IDS_PDF_TOOLTIP_ROTATE_CW}, - {"tooltipDownload", IDS_PDF_TOOLTIP_DOWNLOAD}, - {"tooltipPrint", IDS_PDF_TOOLTIP_PRINT}, - {"tooltipFitToPage", IDS_PDF_TOOLTIP_FIT_PAGE}, - {"tooltipFitToWidth", IDS_PDF_TOOLTIP_FIT_WIDTH}, - {"tooltipTwoUpViewEnable", IDS_PDF_TOOLTIP_TWO_UP_VIEW_ENABLE}, - {"tooltipTwoUpViewDisable", IDS_PDF_TOOLTIP_TWO_UP_VIEW_DISABLE}, - {"tooltipZoomIn", IDS_PDF_TOOLTIP_ZOOM_IN}, - {"tooltipZoomOut", IDS_PDF_TOOLTIP_ZOOM_OUT}, -#if defined(OS_CHROMEOS) - {"tooltipAnnotate", IDS_PDF_ANNOTATION_ANNOTATE}, - {"annotationDocumentTooLarge", IDS_PDF_ANNOTATION_DOCUMENT_TOO_LARGE}, - {"annotationDocumentProtected", IDS_PDF_ANNOTATION_DOCUMENT_PROTECTED}, - {"annotationDocumentRotated", IDS_PDF_ANNOTATION_DOCUMENT_ROTATED}, - {"annotationPen", IDS_PDF_ANNOTATION_PEN}, - {"annotationHighlighter", IDS_PDF_ANNOTATION_HIGHLIGHTER}, - {"annotationEraser", IDS_PDF_ANNOTATION_ERASER}, - {"annotationUndo", IDS_PDF_ANNOTATION_UNDO}, - {"annotationRedo", IDS_PDF_ANNOTATION_REDO}, - {"annotationExpand", IDS_PDF_ANNOTATION_EXPAND}, - {"annotationColorBlack", IDS_PDF_ANNOTATION_COLOR_BLACK}, - {"annotationColorRed", IDS_PDF_ANNOTATION_COLOR_RED}, - {"annotationColorYellow", IDS_PDF_ANNOTATION_COLOR_YELLOW}, - {"annotationColorGreen", IDS_PDF_ANNOTATION_COLOR_GREEN}, - {"annotationColorCyan", IDS_PDF_ANNOTATION_COLOR_CYAN}, - {"annotationColorPurple", IDS_PDF_ANNOTATION_COLOR_PURPLE}, - {"annotationColorBrown", IDS_PDF_ANNOTATION_COLOR_BROWN}, - {"annotationColorWhite", IDS_PDF_ANNOTATION_COLOR_WHITE}, - {"annotationColorCrimson", IDS_PDF_ANNOTATION_COLOR_CRIMSON}, - {"annotationColorAmber", IDS_PDF_ANNOTATION_COLOR_AMBER}, - {"annotationColorAvocadoGreen", IDS_PDF_ANNOTATION_COLOR_AVOCADO_GREEN}, - {"annotationColorCobaltBlue", IDS_PDF_ANNOTATION_COLOR_COBALT_BLUE}, - {"annotationColorDeepPurple", IDS_PDF_ANNOTATION_COLOR_DEEP_PURPLE}, - {"annotationColorDarkBrown", IDS_PDF_ANNOTATION_COLOR_DARK_BROWN}, - {"annotationColorDarkGrey", IDS_PDF_ANNOTATION_COLOR_DARK_GREY}, - {"annotationColorHotPink", IDS_PDF_ANNOTATION_COLOR_HOT_PINK}, - {"annotationColorOrange", IDS_PDF_ANNOTATION_COLOR_ORANGE}, - {"annotationColorLime", IDS_PDF_ANNOTATION_COLOR_LIME}, - {"annotationColorBlue", IDS_PDF_ANNOTATION_COLOR_BLUE}, - {"annotationColorViolet", IDS_PDF_ANNOTATION_COLOR_VIOLET}, - {"annotationColorTeal", IDS_PDF_ANNOTATION_COLOR_TEAL}, - {"annotationColorLightGrey", IDS_PDF_ANNOTATION_COLOR_LIGHT_GREY}, - {"annotationColorLightPink", IDS_PDF_ANNOTATION_COLOR_LIGHT_PINK}, - {"annotationColorLightOrange", IDS_PDF_ANNOTATION_COLOR_LIGHT_ORANGE}, - {"annotationColorLightGreen", IDS_PDF_ANNOTATION_COLOR_LIGHT_GREEN}, - {"annotationColorLightBlue", IDS_PDF_ANNOTATION_COLOR_LIGHT_BLUE}, - {"annotationColorLavender", IDS_PDF_ANNOTATION_COLOR_LAVENDER}, - {"annotationColorLightTeal", IDS_PDF_ANNOTATION_COLOR_LIGHT_TEAL}, - {"annotationSize1", IDS_PDF_ANNOTATION_SIZE1}, - {"annotationSize2", IDS_PDF_ANNOTATION_SIZE2}, - {"annotationSize3", IDS_PDF_ANNOTATION_SIZE3}, - {"annotationSize4", IDS_PDF_ANNOTATION_SIZE4}, - {"annotationSize8", IDS_PDF_ANNOTATION_SIZE8}, - {"annotationSize12", IDS_PDF_ANNOTATION_SIZE12}, - {"annotationSize16", IDS_PDF_ANNOTATION_SIZE16}, - {"annotationSize20", IDS_PDF_ANNOTATION_SIZE20}, - {"annotationFormWarningTitle", IDS_PDF_DISCARD_FORM_CHANGES}, - {"annotationFormWarningDetail", IDS_PDF_DISCARD_FORM_CHANGES_DETAIL}, - {"annotationFormWarningKeepEditing", IDS_PDF_KEEP_EDITING}, - {"annotationFormWarningDiscard", IDS_PDF_DISCARD}, -#endif // defined(OS_CHROMEOS) - }; - for (const auto& resource : kPdfResources) - dict->SetString(resource.name, l10n_util::GetStringUTF16(resource.id)); - - dict->SetString("presetZoomFactors", zoom::GetPresetZoomFactorsAsJSON()); -#endif // BUILDFLAG(ENABLE_PDF) -} - -void AddAdditionalDataForPdf(base::DictionaryValue* dict) { -#if BUILDFLAG(ENABLE_PDF) - dict->SetKey("pdfFormSaveEnabled", - base::Value(base::FeatureList::IsEnabled( - chrome_pdf::features::kSaveEditedPDFForm))); - dict->SetKey("pdfAnnotationsEnabled", - base::Value(base::FeatureList::IsEnabled( - chrome_pdf::features::kPDFAnnotations))); - dict->SetKey("pdfTwoUpViewEnabled", - base::Value(base::FeatureList::IsEnabled( - chrome_pdf::features::kPDFTwoUpView))); - - bool enable_printing = true; -#if defined(OS_CHROMEOS) - // For Chrome OS, enable printing only if we are not at OOBE. - enable_printing = !chromeos::LoginDisplayHost::default_host(); -#endif // defined(OS_CHROMEOS) - dict->SetKey("printingEnabled", base::Value(enable_printing)); -#endif // BUILDFLAG(ENABLE_PDF) -} - } // namespace namespace get_strings = api::resources_private::GetStrings; @@ -164,10 +55,13 @@ ExtensionFunction::ResponseAction ResourcesPrivateGetStringsFunction::Run() { case api::resources_private::COMPONENT_IDENTITY: AddStringsForIdentity(dict.get()); break; - case api::resources_private::COMPONENT_PDF: - AddStringsForPdf(dict.get()); - AddAdditionalDataForPdf(dict.get()); - break; +#if BUILDFLAG(ENABLE_PDF) + case api::resources_private::COMPONENT_PDF: { + pdf_extension_util::AddStrings(pdf_extension_util::PdfViewerContext::kAll, + dict.get()); + pdf_extension_util::AddAdditionalData(dict.get()); + } break; +#endif // BUILDFLAG(ENABLE_PDF) case api::resources_private::COMPONENT_NONE: NOTREACHED(); } diff --git a/chromium/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc b/chromium/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc index a8d41b78f10..ead182f7275 100644 --- a/chromium/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc +++ b/chromium/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate_unittest.cc @@ -94,7 +94,7 @@ class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate { no_updates_.erase(id); DownloadFinishedArgs args; args.path = path; - args.version = version; + args.version = base::Version(version); updates_[id] = std::move(args); } @@ -107,7 +107,7 @@ class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate { // expecting a synchronous reply (the real code has to go do at least one // network request before getting a response, so this is is a reasonable // expectation by delegates). - for (const std::string& id : fetch_data->extension_ids()) { + for (const std::string& id : fetch_data->GetExtensionIds()) { auto no_update = no_updates_.find(id); if (no_update != no_updates_.end()) { no_updates_.erase(no_update); @@ -147,7 +147,7 @@ class DownloaderTestDelegate : public ExtensionDownloaderTestDelegate { // Simple holder for the data passed in AddUpdateResponse calls. struct DownloadFinishedArgs { base::FilePath path; - std::string version; + base::Version version; }; // These keep track of what response we should give for update checks, keyed @@ -206,7 +206,8 @@ class ChromeRuntimeAPIDelegateTest : public ExtensionServiceTestWithInstall { // Setup the ExtensionService so that extension updates won't complete // installation until the extension is idle. - update_install_gate_ = std::make_unique<UpdateInstallGate>(service()); + update_install_gate_ = + std::make_unique<UpdateInstallGate>(service()->profile()); service()->RegisterInstallGate(ExtensionPrefs::DELAY_REASON_WAIT_FOR_IDLE, update_install_gate_.get()); static_cast<TestExtensionSystem*>(ExtensionSystem::Get(browser_context())) diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc index e0dbf81759b..6c34804f9dc 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.cc @@ -18,17 +18,8 @@ #include "base/time/time.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_content_browser_client.h" -#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_factory.h" -#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h" -#if defined(OS_CHROMEOS) -#include "chrome/browser/browser_process_platform_part_chromeos.h" -#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" -#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h" -#include "chrome/browser/chromeos/profiles/profile_helper.h" -#else -#include "chrome/browser/policy/browser_dm_token_storage.h" -#include "chrome/browser/policy/chrome_browser_cloud_management_controller.h" -#endif +#include "chrome/browser/enterprise/connectors/common.h" +#include "chrome/browser/enterprise/connectors/connectors_manager.h" #include "chrome/browser/policy/chrome_browser_policy_connector.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_attributes_entry.h" @@ -36,6 +27,8 @@ #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/profiles/reporting_util.h" #include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service.h" +#include "chrome/browser/safe_browsing/cloud_content_scanning/binary_upload_service_factory.h" +#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" #include "chrome/browser/safe_browsing/safe_browsing_service.h" #include "chrome/browser/signin/identity_manager_factory.h" #include "chrome/common/extensions/api/safe_browsing_private.h" @@ -45,18 +38,27 @@ #include "components/policy/core/common/cloud/machine_level_user_cloud_policy_manager.h" #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h" #include "components/prefs/pref_service.h" +#include "components/safe_browsing/content/web_ui/safe_browsing_ui.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" #include "components/safe_browsing/core/proto/webprotect.pb.h" #include "components/signin/public/identity_manager/identity_manager.h" -#if defined(OS_CHROMEOS) -#include "components/user_manager/user.h" -#include "components/user_manager/user_manager.h" -#endif #include "content/public/browser/browser_context.h" #include "extensions/browser/event_router.h" #include "services/network/public/cpp/shared_url_loader_factory.h" #include "url/gurl.h" +#if defined(OS_CHROMEOS) +#include "chrome/browser/browser_process_platform_part_chromeos.h" +#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h" +#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "components/user_manager/user.h" +#include "components/user_manager/user_manager.h" +#else +#include "chrome/browser/policy/browser_dm_token_storage.h" +#include "chrome/browser/policy/chrome_browser_cloud_management_controller.h" +#endif + namespace { #if defined(OS_CHROMEOS) @@ -68,46 +70,29 @@ const char kChromeBrowserCloudManagementClientDescription[] = "a machine-level user"; #endif -void AddDlpVerdictToEvent(const safe_browsing::DlpDeepScanningVerdict& verdict, - base::Value* event) { +void AddAnalysisConnectorVerdictToEvent( + const safe_browsing::ContentAnalysisScanResult& result, + base::Value* event) { DCHECK(event); base::ListValue triggered_rule_info; - for (const auto& rule : verdict.triggered_rules()) { + for (const auto& trigger : result.triggers) { base::Value triggered_rule(base::Value::Type::DICTIONARY); - triggered_rule.SetIntKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId, - rule.rule_id()); + int64_t rule_id; + if (base::StringToInt64(trigger.id, &rule_id)) { + triggered_rule.SetIntKey( + extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId, + rule_id); + } else { + triggered_rule.SetIntKey( + extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId, 0); + } + triggered_rule.SetStringKey( extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName, - rule.rule_name()); - triggered_rule.SetStringKey(extensions::SafeBrowsingPrivateEventRouter:: - kKeyTriggeredRuleResourceName, - rule.rule_resource_name()); - triggered_rule.SetStringKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleSeverity, - rule.rule_severity()); + trigger.name); triggered_rule.SetIntKey( extensions::SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleAction, - rule.action()); - - base::ListValue matched_detectors; - for (const auto& detector : rule.matched_detectors()) { - base::Value matched_detector(base::Value::Type::DICTIONARY); - matched_detector.SetStringKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorId, - detector.detector_id()); - matched_detector.SetStringKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorName, - detector.display_name()); - matched_detector.SetStringKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorType, - detector.detector_type()); - - matched_detectors.Append(std::move(matched_detector)); - } - triggered_rule.SetKey( - extensions::SafeBrowsingPrivateEventRouter::kKeyMatchedDetectors, - std::move(matched_detectors)); + trigger.action); triggered_rule_info.Append(std::move(triggered_rule)); } @@ -116,12 +101,22 @@ void AddDlpVerdictToEvent(const safe_browsing::DlpDeepScanningVerdict& verdict, std::move(triggered_rule_info)); } +std::string MalwareRuleToThreatType(const std::string& rule_name) { + if (rule_name == "UWS") { + return "POTENTIALLY_UNWANTED"; + } else if (rule_name == "MALWARE") { + return "DANGEROUS"; + } else { + return "UNKNOWN"; + } +} + } // namespace namespace extensions { const base::Feature SafeBrowsingPrivateEventRouter::kRealtimeReportingFeature{ - "SafeBrowsingRealtimeReporting", base::FEATURE_ENABLED_BY_DEFAULT}; + "SafeBrowsingRealtimeReporting", base::FEATURE_DISABLED_BY_DEFAULT}; // Key names used with when building the dictionary to pass to the real-time // reporting API. @@ -140,19 +135,7 @@ const char SafeBrowsingPrivateEventRouter::kKeyClickedThrough[] = "clickedThrough"; const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleId[] = "ruleId"; const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName[] = "ruleName"; -const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleResourceName[] = - "ruleResourceName"; -const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleSeverity[] = - "severity"; const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleAction[] = "action"; -const char SafeBrowsingPrivateEventRouter::kKeyMatchedDetectors[] = - "matchedDetectors"; -const char SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorId[] = - "detectorId"; -const char SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorName[] = - "displayName"; -const char SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorType[] = - "detectorType"; const char SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleInfo[] = "triggeredRuleInfo"; const char SafeBrowsingPrivateEventRouter::kKeyThreatType[] = "threatType"; @@ -172,6 +155,8 @@ const char SafeBrowsingPrivateEventRouter::kKeySensitiveDataEvent[] = "sensitiveDataEvent"; const char SafeBrowsingPrivateEventRouter::kKeyUnscannedFileEvent[] = "unscannedFileEvent"; +const char SafeBrowsingPrivateEventRouter::kKeyUnscannedReason[] = + "unscannedReason"; const char SafeBrowsingPrivateEventRouter::kTriggerFileDownload[] = "FILE_DOWNLOAD"; @@ -183,18 +168,6 @@ SafeBrowsingPrivateEventRouter::SafeBrowsingPrivateEventRouter( content::BrowserContext* context) : context_(context) { event_router_ = EventRouter::Get(context_); - - // g_browser_process and/or g_browser_process->local_state() may be null - // in tests. - if (g_browser_process && g_browser_process->local_state()) { - RealtimeReportingPrefChanged(std::string()); - registrar_.Init(g_browser_process->local_state()); - registrar_.Add( - prefs::kUnsafeEventsReportingEnabled, - base::BindRepeating( - &SafeBrowsingPrivateEventRouter::RealtimeReportingPrefChanged, - base::Unretained(this))); - } } SafeBrowsingPrivateEventRouter::~SafeBrowsingPrivateEventRouter() {} @@ -418,6 +391,30 @@ void SafeBrowsingPrivateEventRouter::OnSecurityInterstitialProceeded( params.url, params.reason, net_error_code, params.user_name)); } +void SafeBrowsingPrivateEventRouter::OnAnalysisConnectorResult( + const GURL& url, + const std::string& file_name, + const std::string& download_digest_sha256, + const std::string& mime_type, + const std::string& trigger, + safe_browsing::DeepScanAccessPoint /* access_point */, + const safe_browsing::ContentAnalysisScanResult& result, + const int64_t content_size) { + if (!IsRealtimeReportingEnabled()) + return; + + if (result.tag == "malware") { + DCHECK_EQ(1u, result.triggers.size()); + OnDangerousDeepScanningResult( + url, file_name, download_digest_sha256, + MalwareRuleToThreatType(result.triggers[0].name), mime_type, trigger, + content_size); + } else if (result.tag == "dlp") { + OnSensitiveDataEvent(url, file_name, download_digest_sha256, mime_type, + trigger, result, content_size); + } +} + void SafeBrowsingPrivateEventRouter::OnDangerousDeepScanningResult( const GURL& url, const std::string& file_name, @@ -459,12 +456,12 @@ void SafeBrowsingPrivateEventRouter::OnDangerousDeepScanningResult( } void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent( - const safe_browsing::DlpDeepScanningVerdict& verdict, const GURL& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + const safe_browsing::ContentAnalysisScanResult& result, const int64_t content_size) { if (!IsRealtimeReportingEnabled()) return; @@ -472,7 +469,7 @@ void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent( ReportRealtimeEvent( kKeySensitiveDataEvent, base::BindOnce( - [](const safe_browsing::DlpDeepScanningVerdict& verdict, + [](const safe_browsing::ContentAnalysisScanResult& result, const std::string& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& profile_user_name, const std::string& mime_type, @@ -493,33 +490,38 @@ void SafeBrowsingPrivateEventRouter::OnSensitiveDataEvent( event.SetStringKey(kKeyTrigger, trigger); event.SetBoolKey(kKeyClickedThrough, false); - AddDlpVerdictToEvent(verdict, &event); + AddAnalysisConnectorVerdictToEvent(result, &event); return event; }, - verdict, url.spec(), file_name, download_digest_sha256, + result, url.spec(), file_name, download_digest_sha256, GetProfileUserName(), mime_type, trigger, content_size)); } -void SafeBrowsingPrivateEventRouter::OnSensitiveDataWarningBypassed( - const safe_browsing::DlpDeepScanningVerdict& verdict, +void SafeBrowsingPrivateEventRouter::OnAnalysisConnectorWarningBypassed( const GURL& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, + const safe_browsing::ContentAnalysisScanResult& result, const int64_t content_size) { if (!IsRealtimeReportingEnabled()) return; + DCHECK_EQ("dlp", result.tag); + ReportRealtimeEvent( kKeySensitiveDataEvent, base::BindOnce( - [](const safe_browsing::DlpDeepScanningVerdict& verdict, + [](const safe_browsing::ContentAnalysisScanResult& result, const std::string& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& profile_user_name, const std::string& mime_type, - const std::string& trigger, const int64_t content_size) { + const std::string& trigger, + safe_browsing::DeepScanAccessPoint /* access_point */, + const int64_t content_size) { // Create a real-time event dictionary from the arguments and // report it. base::Value event(base::Value::Type::DICTIONARY); @@ -536,12 +538,13 @@ void SafeBrowsingPrivateEventRouter::OnSensitiveDataWarningBypassed( event.SetStringKey(kKeyTrigger, trigger); event.SetBoolKey(kKeyClickedThrough, true); - AddDlpVerdictToEvent(verdict, &event); + AddAnalysisConnectorVerdictToEvent(result, &event); return event; }, - verdict, url.spec(), file_name, download_digest_sha256, - GetProfileUserName(), mime_type, trigger, content_size)); + result, url.spec(), file_name, download_digest_sha256, + GetProfileUserName(), mime_type, trigger, access_point, + content_size)); } void SafeBrowsingPrivateEventRouter::OnUnscannedFileEvent( @@ -550,6 +553,7 @@ void SafeBrowsingPrivateEventRouter::OnUnscannedFileEvent( const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, const std::string& reason, const int64_t content_size) { if (!IsRealtimeReportingEnabled()) @@ -561,8 +565,9 @@ void SafeBrowsingPrivateEventRouter::OnUnscannedFileEvent( [](const std::string& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& profile_user_name, const std::string& mime_type, - const std::string& trigger, const std::string& reason, - const int64_t content_size) { + const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, + const std::string& reason, const int64_t content_size) { // Create a real-time event dictionary from the arguments and // report it. base::Value event(base::Value::Type::DICTIONARY); @@ -572,7 +577,7 @@ void SafeBrowsingPrivateEventRouter::OnUnscannedFileEvent( download_digest_sha256); event.SetStringKey(kKeyProfileUserName, profile_user_name); event.SetStringKey(kKeyContentType, mime_type); - event.SetStringKey(kKeyReason, reason); + event.SetStringKey(kKeyUnscannedReason, reason); // |content_size| can be set to -1 to indicate an unknown size, in // which case the field is not set. if (content_size >= 0) @@ -581,7 +586,7 @@ void SafeBrowsingPrivateEventRouter::OnUnscannedFileEvent( return event; }, url.spec(), file_name, download_digest_sha256, GetProfileUserName(), - mime_type, trigger, reason, content_size)); + mime_type, trigger, access_point, reason, content_size)); } void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarning( @@ -664,7 +669,9 @@ void SafeBrowsingPrivateEventRouter::OnDangerousDownloadWarningBypassed( // static bool SafeBrowsingPrivateEventRouter::ShouldInitRealtimeReportingClient() { - if (!base::FeatureList::IsEnabled(kRealtimeReportingFeature)) { + if (!base::FeatureList::IsEnabled(kRealtimeReportingFeature) && + !base::FeatureList::IsEnabled( + enterprise_connectors::kEnterpriseConnectorsEnabled)) { DVLOG(2) << "Safe browsing real-time reporting is not enabled."; return false; } @@ -687,6 +694,11 @@ void SafeBrowsingPrivateEventRouter::SetBinaryUploadServiceForTesting( binary_upload_service_ = binary_upload_service; } +void SafeBrowsingPrivateEventRouter::SetIdentityManagerForTesting( + signin::IdentityManager* identity_manager) { + identity_manager_ = identity_manager; +} + void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClient() { // If already initialized, do nothing. if (client_) { @@ -719,26 +731,6 @@ void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClient() { return; } - if (g_browser_process) { - binary_upload_service_ = - safe_browsing::BinaryUploadServiceFactory::GetForProfile( - Profile::FromBrowserContext(context_)); - IfAuthorized(base::BindOnce( - &SafeBrowsingPrivateEventRouter::InitRealtimeReportingClientCallback, - weak_ptr_factory_.GetWeakPtr(), device_management_service)); - } -} - -void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClientCallback( - policy::DeviceManagementService* device_management_service, - bool authorized) { - // Don't initialize the client if the browser cannot upload data. - if (!authorized) { - LOG(WARNING) << "The client is not authorized to do safe browsing " - "real-time event reporting."; - return; - } - policy::CloudPolicyClient* client = nullptr; std::string policy_client_desc; @@ -791,6 +783,11 @@ void SafeBrowsingPrivateEventRouter::InitRealtimeReportingClientCallback( policy::CloudPolicyClient::DeviceDMTokenCallback()); client = private_client_.get(); + // TODO(crbug.com/1069049): when we decide to add the extra URL parameters to + // the uploaded reports, do the following: + // client->add_connector_url_params(base::FeatureList::IsEnabled( + // enterprise_connectors::kEnterpriseConnectorsEnabled)); + if (!client->is_registered()) { client->SetupRegistration( dm_token.value(), client_id, @@ -815,25 +812,24 @@ void SafeBrowsingPrivateEventRouter::OnCloudPolicyClientAvailable( } bool SafeBrowsingPrivateEventRouter::IsRealtimeReportingEnabled() { - // g_browser_process and/or g_browser_process->local_state() may be null - // in tests. - return g_browser_process && g_browser_process->local_state() && - g_browser_process->local_state()->GetBoolean( - prefs::kUnsafeEventsReportingEnabled); -} - -void SafeBrowsingPrivateEventRouter::RealtimeReportingPrefChanged( - const std::string& pref) { - // If the reporting policy has been turned on, try to initialized now. - if (IsRealtimeReportingEnabled()) - InitRealtimeReportingClient(); + auto settings = + enterprise_connectors::ConnectorsManager::GetInstance() + ->GetReportingSettings( + enterprise_connectors::ReportingConnector::SECURITY_EVENT); + return settings.has_value(); } void SafeBrowsingPrivateEventRouter::IfAuthorized( base::OnceCallback<void(bool)> cont) { - if (binary_upload_service_) { - binary_upload_service_->IsAuthorized(std::move(cont)); + if (!binary_upload_service_ && g_browser_process) { + binary_upload_service_ = + safe_browsing::BinaryUploadServiceFactory::GetForProfile( + Profile::FromBrowserContext(context_)); } + + // TODO(crbug/1069049): Use reporting URL. + if (binary_upload_service_) + binary_upload_service_->IsAuthorized(GURL(), std::move(cont)); } void SafeBrowsingPrivateEventRouter::ReportRealtimeEvent( @@ -854,8 +850,10 @@ void SafeBrowsingPrivateEventRouter::ReportRealtimeEventCallback( return; } - // |client_| should be set when authorized is true. - DCHECK(client_); + // Make sure real-time reporting is initialized. + InitRealtimeReportingClient(); + if (!client_) + return; // Format the current time (UTC) in RFC3339 format. base::Time::Exploded now_exploded; @@ -884,10 +882,18 @@ void SafeBrowsingPrivateEventRouter::ReportRealtimeEventCallback( } std::string SafeBrowsingPrivateEventRouter::GetProfileUserName() const { - // |identity_manager_| may be null is some tests. - return identity_manager_ && identity_manager_->HasPrimaryAccount() - ? identity_manager_->GetPrimaryAccountInfo().email - : std::string(); + // |identity_manager_| may be null in some tests. + if (!identity_manager_) + return std::string(); + + if (!identity_manager_->HasPrimaryAccount( + signin::ConsentLevel::kNotRequired)) { + return std::string(); + } + + return identity_manager_ + ->GetPrimaryAccountInfo(signin::ConsentLevel::kNotRequired) + .email; } #if defined(OS_CHROMEOS) diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h index dc0ceea0b29..da76717180d 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h @@ -14,7 +14,6 @@ #include "base/memory/weak_ptr.h" #include "base/values.h" #include "components/keyed_service/core/keyed_service.h" -#include "components/prefs/pref_change_registrar.h" namespace content { class BrowserContext; @@ -37,7 +36,8 @@ class DeviceManagementService; namespace safe_browsing { class BinaryUploadService; -class DlpDeepScanningVerdict; +enum class DeepScanAccessPoint; +struct ContentAnalysisScanResult; } #if defined(OS_CHROMEOS) @@ -71,13 +71,7 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { static const char kKeyClickedThrough[]; static const char kKeyTriggeredRuleId[]; static const char kKeyTriggeredRuleName[]; - static const char kKeyTriggeredRuleResourceName[]; - static const char kKeyTriggeredRuleSeverity[]; static const char kKeyTriggeredRuleAction[]; - static const char kKeyMatchedDetectors[]; - static const char kKeyMatchedDetectorId[]; - static const char kKeyMatchedDetectorName[]; - static const char kKeyMatchedDetectorType[]; static const char kKeyTriggeredRuleInfo[]; static const char kKeyThreatType[]; static const char kKeyContentType[]; @@ -90,8 +84,10 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { static const char kKeyInterstitialEvent[]; static const char kKeySensitiveDataEvent[]; static const char kKeyUnscannedFileEvent[]; + static const char kKeyUnscannedReason[]; - // String constants for the "trigger" event field. + // String constants for the "trigger" event field. This corresponds to + // an enterprise connector. static const char kTriggerFileDownload[]; static const char kTriggerFileUpload[]; static const char kTriggerWebContentUpload[]; @@ -126,33 +122,26 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { const std::string& reason, int net_error_code); - // Notifies listeners that deep scanning detected a dangerous download. - void OnDangerousDeepScanningResult(const GURL& url, - const std::string& file_name, - const std::string& download_digest_sha256, - const std::string& threat_type, - const std::string& mime_type, - const std::string& trigger, - const int64_t content_size); - - // Notifies listeners that scanning for sensitive data detected a violation. - void OnSensitiveDataEvent( - const safe_browsing::DlpDeepScanningVerdict& verdict, + // Notifies listeners that the analysis connector detected a violation. + void OnAnalysisConnectorResult( const GURL& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, + const safe_browsing::ContentAnalysisScanResult& result, const int64_t content_size); - // Notifies listeners that scanning for sensitive data detected a violation. - void OnSensitiveDataWarningBypassed( - const safe_browsing::DlpDeepScanningVerdict& verdict, + // Notifies listeners that an analysis connector violation was bypassed. + void OnAnalysisConnectorWarningBypassed( const GURL& url, const std::string& file_name, const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, + const safe_browsing::ContentAnalysisScanResult& result, const int64_t content_size); // Notifies listeners that deep scanning failed, for the given |reason|. @@ -161,6 +150,7 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { const std::string& download_digest_sha256, const std::string& mime_type, const std::string& trigger, + safe_browsing::DeepScanAccessPoint access_point, const std::string& reason, const int64_t content_size); @@ -199,6 +189,8 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { void SetBinaryUploadServiceForTesting( safe_browsing::BinaryUploadService* binary_upload_service); + void SetIdentityManagerForTesting(signin::IdentityManager* identity_manager); + protected: // Callback to report safe browsing event through real-time reporting channel, // if the browser is authorized to do so. Declared as protected to be called @@ -215,12 +207,6 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { // with CBCM and the appropriate policies are enabled. void InitRealtimeReportingClient(); - // Initialize DeviceManagementService and |client_| after validating the - // browser can upload data. - void InitRealtimeReportingClientCallback( - policy::DeviceManagementService* device_management_service, - bool authorized); - // Continues execution if the client is authorized to do so. void IfAuthorized(base::OnceCallback<void(bool)> cont); @@ -261,6 +247,25 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { // an empty string if the profile is not signed in. std::string GetProfileUserName() const; + // Notifies listeners that deep scanning detected a dangerous download. + void OnDangerousDeepScanningResult(const GURL& url, + const std::string& file_name, + const std::string& download_digest_sha256, + const std::string& threat_type, + const std::string& mime_type, + const std::string& trigger, + const int64_t content_size); + + // Notifies listeners that the analysis connector detected a violation. + void OnSensitiveDataEvent( + const GURL& url, + const std::string& file_name, + const std::string& download_digest_sha256, + const std::string& mime_type, + const std::string& trigger, + const safe_browsing::ContentAnalysisScanResult& result, + const int64_t content_size); + content::BrowserContext* context_; signin::IdentityManager* identity_manager_ = nullptr; EventRouter* event_router_ = nullptr; @@ -271,7 +276,6 @@ class SafeBrowsingPrivateEventRouter : public KeyedService { // The |private_client_| is used on platforms where we cannot just get a // client and we create our own (used through |client_|). std::unique_ptr<policy::CloudPolicyClient> private_client_; - PrefChangeRegistrar registrar_; base::WeakPtrFactory<SafeBrowsingPrivateEventRouter> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(SafeBrowsingPrivateEventRouter); diff --git a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc index 3b5e213a364..4685fb42d13 100644 --- a/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc +++ b/chromium/chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_unittest.cc @@ -3,9 +3,11 @@ // found in the LICENSE file. #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router.h" +#include <memory> #include <utility> #include "base/bind.h" +#include "base/json/json_reader.h" #include "base/strings/string16.h" #include "base/strings/utf_string_conversions.h" #include "base/test/mock_callback.h" @@ -13,7 +15,10 @@ #include "base/values.h" #include "build/branding_buildflags.h" #include "build/build_config.h" +#include "chrome/browser/enterprise/connectors/common.h" +#include "chrome/browser/enterprise/connectors/connectors_manager.h" #include "chrome/browser/extensions/api/safe_browsing_private/safe_browsing_private_event_router_factory.h" +#include "chrome/browser/safe_browsing/cloud_content_scanning/deep_scanning_utils.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/api/safe_browsing_private.h" #include "chrome/test/base/testing_browser_process.h" @@ -22,7 +27,9 @@ #include "components/policy/core/common/cloud/mock_cloud_policy_client.h" #include "components/policy/core/common/cloud/realtime_reporting_job_configuration.h" #include "components/safe_browsing/core/common/safe_browsing_prefs.h" +#include "components/safe_browsing/core/features.h" #include "components/safe_browsing/core/proto/webprotect.pb.h" +#include "components/signin/public/identity_manager/identity_test_environment.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/test_event_router.h" #include "testing/gmock/include/gmock/gmock.h" @@ -40,6 +47,7 @@ using ::testing::_; using ::testing::Mock; +using ::testing::Return; using ::testing::SaveArg; namespace extensions { @@ -50,6 +58,12 @@ ACTION_P(CaptureArg, wrapper) { *wrapper = arg0.Clone(); } +constexpr char kConnectorsPrefValue[] = R"([ + { + "service_provider": "google" + } +])"; + } // namespace class FakeAuthorizedSafeBrowsingPrivateEventRouter @@ -175,41 +189,31 @@ class SafeBrowsingPrivateEventRouterTest : public testing::Test { } void TriggerOnSensitiveDataEvent() { - safe_browsing::DlpDeepScanningVerdict verdict; - verdict.set_status(safe_browsing::DlpDeepScanningVerdict::SUCCESS); - safe_browsing::DlpDeepScanningVerdict::TriggeredRule* rule = - verdict.add_triggered_rules(); - rule->set_action( - safe_browsing::DlpDeepScanningVerdict::TriggeredRule::BLOCK); - rule->set_rule_name("fake rule"); - rule->set_rule_id(12345); - rule->set_rule_resource_name("fake resource name"); - rule->set_rule_severity("fake severity"); - - safe_browsing::DlpDeepScanningVerdict::MatchedDetector* detector = - rule->add_matched_detectors(); - detector->set_detector_id("fake id"); - detector->set_display_name("fake name"); - detector->set_detector_type("fake type"); - - detector = rule->add_matched_detectors(); - detector->set_detector_id("fake id2"); - detector->set_display_name("fake name2"); - detector->set_detector_type("fake type2"); + safe_browsing::ContentAnalysisScanResult result; + result.tag = "dlp"; + result.status = 1; + safe_browsing::ContentAnalysisTrigger trigger; + trigger.action = 3; + trigger.name = "fake rule"; + trigger.id = "12345"; + result.triggers.push_back(std::move(trigger)); SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_) - ->OnSensitiveDataEvent(verdict, - GURL("https://evil.com/sensitive_data.txt"), - "sensitive_data.txt", "sha256_of_data", - "text/plain", "FILE_UPLOAD", 12345); + ->OnAnalysisConnectorResult( + GURL("https://evil.com/sensitive_data.txt"), "sensitive_data.txt", + "sha256_of_data", "text/plain", + SafeBrowsingPrivateEventRouter::kTriggerFileUpload, + safe_browsing::DeepScanAccessPoint::UPLOAD, result, 12345); } void TriggerOnUnscannedFileEvent() { SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_) - ->OnUnscannedFileEvent(GURL("https://evil.com/sensitive_data.txt"), - "sensitive_data.txt", "sha256_of_data", - "text/plain", "FILE_DOWNLOAD", - "filePasswordProtected", 12345); + ->OnUnscannedFileEvent( + GURL("https://evil.com/sensitive_data.txt"), "sensitive_data.txt", + "sha256_of_data", "text/plain", + SafeBrowsingPrivateEventRouter::kTriggerFileDownload, + safe_browsing::DeepScanAccessPoint::DOWNLOAD, + "filePasswordProtected", 12345); } void SetReportingPolicy(bool enabled) { @@ -714,7 +718,7 @@ TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnSensitiveDataEvent) { EXPECT_EQ( "sensitive_data.txt", *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName)); - EXPECT_EQ("FILE_UPLOAD", + EXPECT_EQ(SafeBrowsingPrivateEventRouter::kTriggerFileUpload, *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyTrigger)); base::Value* triggered_rule_info = @@ -729,27 +733,6 @@ TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnSensitiveDataEvent) { EXPECT_EQ("fake rule", *triggered_rule.FindStringKey( SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleName)); - EXPECT_EQ("fake resource name", - *triggered_rule.FindStringKey( - SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleResourceName)); - EXPECT_EQ("fake severity", - *triggered_rule.FindStringKey( - SafeBrowsingPrivateEventRouter::kKeyTriggeredRuleSeverity)); - - base::Value* matched_detectors = triggered_rule.FindKey( - SafeBrowsingPrivateEventRouter::kKeyMatchedDetectors); - ASSERT_NE(nullptr, matched_detectors); - ASSERT_EQ(2u, matched_detectors->GetList().size()); - base::Value detector = std::move(matched_detectors->GetList()[0]); - EXPECT_EQ("fake id", - *detector.FindStringKey( - SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorId)); - EXPECT_EQ("fake type", - *detector.FindStringKey( - SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorType)); - EXPECT_EQ("fake name", - *detector.FindStringKey( - SafeBrowsingPrivateEventRouter::kKeyMatchedDetectorName)); } TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnUnscannedFileEvent) { @@ -786,10 +769,48 @@ TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnUnscannedFileEvent) { EXPECT_EQ( "sensitive_data.txt", *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyFileName)); - EXPECT_EQ("FILE_DOWNLOAD", + EXPECT_EQ(SafeBrowsingPrivateEventRouter::kTriggerFileDownload, *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyTrigger)); EXPECT_EQ("filePasswordProtected", - *event->FindStringKey(SafeBrowsingPrivateEventRouter::kKeyReason)); + *event->FindStringKey( + SafeBrowsingPrivateEventRouter::kKeyUnscannedReason)); +} + +TEST_F(SafeBrowsingPrivateEventRouterTest, TestProfileUsername) { + SetUpRouters(); + SafeBrowsingEventObserver event_observer( + api::safe_browsing_private::OnSecurityInterstitialShown::kEventName); + event_router_->AddEventObserver(&event_observer); + + signin::IdentityTestEnvironment identity_test_environment; + SafeBrowsingPrivateEventRouterFactory::GetForProfile(profile_) + ->SetIdentityManagerForTesting( + identity_test_environment.identity_manager()); + + EXPECT_CALL(*client_, UploadRealtimeReport_(_, _)).WillRepeatedly(Return()); + + // With no primary account, we should not set the username. + TriggerOnSecurityInterstitialShownEvent(); + base::RunLoop().RunUntilIdle(); + auto captured_args = event_observer.PassEventArgs().GetList()[0].Clone(); + EXPECT_EQ("", captured_args.FindKey("userName")->GetString()); + + // With an unconsented primary account, we should set the username. + identity_test_environment.MakeUnconsentedPrimaryAccountAvailable( + "profile@example.com"); + TriggerOnSecurityInterstitialShownEvent(); + base::RunLoop().RunUntilIdle(); + captured_args = event_observer.PassEventArgs().GetList()[0].Clone(); + EXPECT_EQ("profile@example.com", + captured_args.FindKey("userName")->GetString()); + + // With a consented primary account, we should set the username. + identity_test_environment.MakePrimaryAccountAvailable("profile@example.com"); + TriggerOnSecurityInterstitialShownEvent(); + base::RunLoop().RunUntilIdle(); + captured_args = event_observer.PassEventArgs().GetList()[0].Clone(); + EXPECT_EQ("profile@example.com", + captured_args.FindKey("userName")->GetString()); } // Tests to make sure the feature flag and policy control real-time reporting @@ -799,22 +820,42 @@ TEST_F(SafeBrowsingPrivateEventRouterTest, TestOnUnscannedFileEvent) { // bool: whether the browser is manageable. // bool: whether the policy is enabled. // bool: whether the server has authorized this browser instance. +// bool: whether to use new connectors or old legacy code. class SafeBrowsingIsRealtimeReportingEnabledTest : public SafeBrowsingPrivateEventRouterTest, public testing::WithParamInterface< - testing::tuple<bool, bool, bool, bool>> { + testing::tuple<bool, bool, bool, bool, bool>> { public: SafeBrowsingIsRealtimeReportingEnabledTest() : is_feature_flag_enabled_(testing::get<0>(GetParam())), is_manageable_(testing::get<1>(GetParam())), is_policy_enabled_(testing::get<2>(GetParam())), - is_authorized_(testing::get<3>(GetParam())) { - if (is_feature_flag_enabled_) { - scoped_feature_list_.InitAndEnableFeature( - SafeBrowsingPrivateEventRouter::kRealtimeReportingFeature); + is_authorized_(testing::get<3>(GetParam())), + use_new_connectors_(testing::get<4>(GetParam())) { + if (use_new_connectors_) { + if (is_feature_flag_enabled_) { + scoped_feature_list_.InitWithFeatures( + {enterprise_connectors::kEnterpriseConnectorsEnabled}, + {extensions::SafeBrowsingPrivateEventRouter:: + kRealtimeReportingFeature}); + } else { + scoped_feature_list_.InitWithFeatures( + {}, {enterprise_connectors::kEnterpriseConnectorsEnabled, + extensions::SafeBrowsingPrivateEventRouter:: + kRealtimeReportingFeature}); + } } else { - scoped_feature_list_.InitAndDisableFeature( - SafeBrowsingPrivateEventRouter::kRealtimeReportingFeature); + if (is_feature_flag_enabled_) { + scoped_feature_list_.InitWithFeatures( + {extensions::SafeBrowsingPrivateEventRouter:: + kRealtimeReportingFeature}, + {enterprise_connectors::kEnterpriseConnectorsEnabled}); + } else { + scoped_feature_list_.InitWithFeatures( + {}, {enterprise_connectors::kEnterpriseConnectorsEnabled, + extensions::SafeBrowsingPrivateEventRouter:: + kRealtimeReportingFeature}); + } } // In chrome branded desktop builds, the browser is always manageable. @@ -825,8 +866,17 @@ class SafeBrowsingIsRealtimeReportingEnabledTest } #endif - TestingBrowserProcess::GetGlobal()->local_state()->SetBoolean( - prefs::kUnsafeEventsReportingEnabled, is_policy_enabled_); + if (use_new_connectors_) { + if (is_policy_enabled_) { + scoped_connector_pref_ = std::make_unique<ScopedConnectorPref>( + ConnectorPref( + enterprise_connectors::ReportingConnector::SECURITY_EVENT), + kConnectorsPrefValue); + } + } else { + TestingBrowserProcess::GetGlobal()->local_state()->SetBoolean( + prefs::kUnsafeEventsReportingEnabled, is_policy_enabled_); + } #if defined(OS_CHROMEOS) auto user_manager = std::make_unique<chromeos::FakeChromeUserManager>(); @@ -847,6 +897,15 @@ class SafeBrowsingIsRealtimeReportingEnabledTest #endif } + void SetUp() override { + enterprise_connectors::ConnectorsManager::GetInstance()->SetUpForTesting(); + } + + void TearDown() override { + enterprise_connectors::ConnectorsManager::GetInstance() + ->TearDownForTesting(); + } + bool should_init() { #if BUILDFLAG(GOOGLE_CHROME_BRANDING) && !defined(OS_CHROMEOS) return is_feature_flag_enabled_; @@ -856,11 +915,32 @@ class SafeBrowsingIsRealtimeReportingEnabledTest } protected: + class ScopedConnectorPref { + public: + ScopedConnectorPref(const char* pref, const char* pref_value) + : pref_(pref) { + auto maybe_pref_value = + base::JSONReader::Read(pref_value, base::JSON_ALLOW_TRAILING_COMMAS); + EXPECT_TRUE(maybe_pref_value.has_value()); + TestingBrowserProcess::GetGlobal()->local_state()->Set( + pref, maybe_pref_value.value()); + } + + ~ScopedConnectorPref() { + TestingBrowserProcess::GetGlobal()->local_state()->ClearPref(pref_); + } + + private: + const char* pref_; + }; + base::test::ScopedFeatureList scoped_feature_list_; + std::unique_ptr<ScopedConnectorPref> scoped_connector_pref_; const bool is_feature_flag_enabled_; const bool is_manageable_; const bool is_policy_enabled_; const bool is_authorized_; + const bool use_new_connectors_; #if defined(OS_CHROMEOS) private: @@ -918,6 +998,7 @@ INSTANTIATE_TEST_SUITE_P(All, testing::Combine(testing::Bool(), testing::Bool(), testing::Bool(), + testing::Bool(), testing::Bool())); } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc b/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc index f130eced5de..fa30a4353f0 100644 --- a/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc +++ b/chromium/chrome/browser/extensions/api/sessions/sessions_api.cc @@ -207,7 +207,7 @@ ExtensionFunction::ResponseAction SessionsGetRecentlyClosedFunction::Run() { TabRestoreServiceFactory::GetForProfile( Profile::FromBrowserContext(browser_context())); - // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in + // TabRestoreServiceFactory::GetForProfile() can return nullptr (i.e., when in // incognito mode) if (!tab_restore_service) { DCHECK(browser_context()->IsOffTheRecord()) @@ -521,7 +521,7 @@ ExtensionFunction::ResponseValue SessionsRestoreFunction::RestoreForeignSession( if (!open_tabs) return Error(kSessionSyncError); - const sessions::SessionTab* tab = NULL; + const sessions::SessionTab* tab = nullptr; if (open_tabs->GetForeignTab(session_id.session_tag(), SessionID::FromSerializedValue(session_id.id()), &tab)) { @@ -583,7 +583,7 @@ ExtensionFunction::ResponseAction SessionsRestoreFunction::Run() { SessionsEventRouter::SessionsEventRouter(Profile* profile) : profile_(profile), tab_restore_service_(TabRestoreServiceFactory::GetForProfile(profile)) { - // TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in + // TabRestoreServiceFactory::GetForProfile() can return nullptr (i.e., when in // incognito mode) if (tab_restore_service_) { tab_restore_service_->LoadTabsFromLastSession(); @@ -606,7 +606,7 @@ void SessionsEventRouter::TabRestoreServiceChanged( void SessionsEventRouter::TabRestoreServiceDestroyed( sessions::TabRestoreService* service) { - tab_restore_service_ = NULL; + tab_restore_service_ = nullptr; } SessionsAPI::SessionsAPI(content::BrowserContext* context) diff --git a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc index 36e74916715..71097a37fc8 100644 --- a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc +++ b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc @@ -297,9 +297,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, GetDevicesListEmpty) { EXPECT_EQ(0u, devices->GetSize()); } -// Flaky timeout: http://crbug.com/278372 -IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, - DISABLED_RestoreForeignSessionWindow) { +IN_PROC_BROWSER_TEST_F(ExtensionSessionsTest, RestoreForeignSessionWindow) { CreateSessionModels(); std::unique_ptr<base::DictionaryValue> restored_window_session( diff --git a/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc b/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc index 18323cfb6a8..dbaadf5b940 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.cc @@ -69,7 +69,7 @@ SetPrefResult GeneratedResolveTimezoneByGeolocationMethodShort::SetPref( // Check if preference is policy or primary-user controlled. if (chromeos::system::TimeZoneResolverManager:: IsTimeZoneResolutionPolicyControlled() || - !profile_->IsSameProfile(ProfileManager::GetPrimaryUserProfile())) { + !profile_->IsSameOrParent(ProfileManager::GetPrimaryUserProfile())) { return SetPrefResult::PREF_NOT_MODIFIABLE; } diff --git a/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc b/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc index 3eec3a7ea37..edd89c470e1 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_on_off.cc @@ -73,7 +73,7 @@ SetPrefResult GeneratedResolveTimezoneByGeolocationOnOff::SetPref( // cannot deactivate automatic timezone. if (chromeos::system::TimeZoneResolverManager:: IsTimeZoneResolutionPolicyControlled() || - !profile_->IsSameProfile(ProfileManager::GetPrimaryUserProfile())) { + !profile_->IsSameOrParent(ProfileManager::GetPrimaryUserProfile())) { return SetPrefResult::PREF_NOT_MODIFIABLE; } diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_pref.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_pref.cc index b0308f67c07..4fd337bf131 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_pref.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_pref.cc @@ -4,6 +4,10 @@ #include "chrome/browser/extensions/api/settings_private/generated_pref.h" +#include "chrome/common/extensions/api/settings_private.h" + +namespace settings_api = extensions::api::settings_private; + namespace extensions { namespace settings_private { @@ -26,5 +30,30 @@ void GeneratedPref::NotifyObservers(const std::string& pref_name) { observer.OnGeneratedPrefChanged(pref_name); } +/* static */ +void GeneratedPref::ApplyControlledByFromPref( + api::settings_private::PrefObject* pref_object, + const PrefService::Preference* pref) { + if (pref->IsManaged()) { + pref_object->controlled_by = + settings_api::ControlledBy::CONTROLLED_BY_DEVICE_POLICY; + return; + } + + if (pref->IsExtensionControlled()) { + pref_object->controlled_by = + settings_api::ControlledBy::CONTROLLED_BY_EXTENSION; + return; + } + + if (pref->IsManagedByCustodian()) { + pref_object->controlled_by = + settings_api::ControlledBy::CONTROLLED_BY_CHILD_RESTRICTION; + return; + } + + NOTREACHED(); +} + } // namespace settings_private } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h b/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h index 3222055de56..00264766f61 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "base/observer_list.h" #include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h" +#include "components/prefs/pref_service.h" namespace base { class Value; @@ -62,6 +63,12 @@ class GeneratedPref { // Call this when the pref value changes. void NotifyObservers(const std::string& pref_name); + // Sets controlled_by for |pref_object| based on provided |pref| for a limited + // subset of controlled_by sources relevant to generated pref use cases. + static void ApplyControlledByFromPref( + api::settings_private::PrefObject* pref_object, + const PrefService::Preference* pref); + private: base::ObserverList<Observer>::Unchecked observers_; diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.cc new file mode 100644 index 00000000000..505519b2c08 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.cc @@ -0,0 +1,16 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/extensions/api/settings_private/generated_pref_test_base.h" + +namespace extensions { +namespace settings_private { + +void TestGeneratedPrefObserver::OnGeneratedPrefChanged( + const std::string& pref_name) { + updated_pref_name_ = pref_name; +} + +} // namespace settings_private +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.h b/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.h new file mode 100644 index 00000000000..7036ad4ea2f --- /dev/null +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_pref_test_base.h @@ -0,0 +1,66 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_GENERATED_PREF_TEST_BASE_H_ +#define CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_GENERATED_PREF_TEST_BASE_H_ + +#include "chrome/browser/extensions/api/settings_private/generated_pref.h" +#include "chrome/common/extensions/api/settings_private.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/test/browser_task_environment.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace settings_api = extensions::api::settings_private; + +namespace extensions { +namespace settings_private { + +// All of the possible managed states for a boolean preference that can be +// both enforced and recommended. +enum class PrefSetting { + kEnforcedOff, + kEnforcedOn, + kRecommendedOff, + kRecommendedOn, + kNotSet, +}; + +// Possible preference sources supported by TestingPrefService. +// TODO(crbug.com/1063281): Extend TestingPrefService to support prefs set for +// supervised users. +enum class PrefSource { + kExtension, + kDevicePolicy, + kRecommended, + kNone, +}; + +class TestGeneratedPrefObserver : public GeneratedPref::Observer { + public: + void OnGeneratedPrefChanged(const std::string& pref_name) override; + + void Reset() { updated_pref_name_ = ""; } + std::string GetUpdatedPrefName() { return updated_pref_name_; } + + protected: + std::string updated_pref_name_; +}; + +class GeneratedPrefTestBase : public testing::Test { + protected: + TestingProfile* profile() { return &profile_; } + + sync_preferences::TestingPrefServiceSyncable* prefs() { + return profile()->GetTestingPrefService(); + } + + private: + content::BrowserTaskEnvironment task_environment_; + TestingProfile profile_; +}; + +} // namespace settings_private +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_SETTINGS_PRIVATE_GENERATED_PREF_TEST_BASE_H_ diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.cc index dff08441a4e..708717077ca 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_prefs.cc @@ -6,9 +6,12 @@ #include "base/callback.h" #include "build/build_config.h" +#include "chrome/browser/content_settings/generated_cookie_prefs.h" #include "chrome/browser/extensions/api/settings_private/generated_pref.h" #include "chrome/browser/extensions/api/settings_private/prefs_util_enums.h" +#include "chrome/browser/safe_browsing/generated_safe_browsing_pref.h" #include "chrome/common/extensions/api/settings_private.h" +#include "components/content_settings/core/common/pref_names.h" #if defined(OS_CHROMEOS) #include "chrome/browser/extensions/api/settings_private/chromeos_resolve_time_zone_by_geolocation_method_short.h" @@ -25,6 +28,14 @@ GeneratedPrefs::GeneratedPrefs(Profile* profile) { prefs_[kResolveTimezoneByGeolocationMethodShort] = CreateGeneratedResolveTimezoneByGeolocationMethodShort(profile); #endif + prefs_[content_settings::kCookiePrimarySetting] = + std::make_unique<content_settings::GeneratedCookiePrimarySettingPref>( + profile); + prefs_[content_settings::kCookieSessionOnly] = + std::make_unique<content_settings::GeneratedCookieSessionOnlyPref>( + profile); + prefs_[safe_browsing::kGeneratedSafeBrowsingPref] = + std::make_unique<safe_browsing::GeneratedSafeBrowsingPref>(profile); } GeneratedPrefs::~GeneratedPrefs() = default; diff --git a/chromium/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc b/chromium/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc index 92dd63b50f5..b6838f04b5c 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_time_zone_pref_base.cc @@ -51,7 +51,7 @@ void GeneratedTimeZonePrefBase::UpdateTimeZonePrefControlledBy( } else { out_pref->enforcement = settings_api::ENFORCEMENT_ENFORCED; } - } else if (!profile_->IsSameProfile( + } else if (!profile_->IsSameOrParent( ProfileManager::GetPrimaryUserProfile())) { out_pref->controlled_by = settings_api::CONTROLLED_BY_PRIMARY_USER; out_pref->controlled_by_name = std::make_unique<std::string>( 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 ea48a66ba88..a6d8479abd9 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc @@ -9,9 +9,11 @@ #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" +#include "chrome/browser/content_settings/generated_cookie_prefs.h" #include "chrome/browser/extensions/api/settings_private/generated_prefs.h" #include "chrome/browser/extensions/api/settings_private/generated_prefs_factory.h" #include "chrome/browser/extensions/settings_api_helpers.h" +#include "chrome/browser/nearby_sharing/nearby_sharing_prefs.h" #include "chrome/browser/prefs/session_startup_pref.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" @@ -286,13 +288,29 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { #if defined(OS_CHROMEOS) (*s_whitelist)[::prefs::kLanguageImeMenuActivated] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[chromeos::prefs::kAssistPersonalInfoEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[chromeos::prefs::kEmojiSuggestionEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[::prefs::kLanguageInputMethodSpecificSettings] = + settings_api::PrefType::PREF_TYPE_DICTIONARY; #endif + // Nearby Share. + (*s_whitelist)[::prefs::kNearbySharingEnabledPrefName] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[::prefs::kNearbySharingActiveProfilePrefName] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + // Search page. (*s_whitelist)[DefaultSearchManager::kDefaultSearchProviderDataPrefName] = settings_api::PrefType::PREF_TYPE_DICTIONARY; // Site Settings prefs. + (*s_whitelist)[::content_settings::kCookiePrimarySetting] = + settings_api::PrefType::PREF_TYPE_NUMBER; + (*s_whitelist)[::content_settings::kCookieSessionOnly] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[::prefs::kBlockThirdPartyCookies] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[::prefs::kCookieControlsMode] = @@ -355,6 +373,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { (*s_whitelist)[::prefs::kLiveCaptionEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; #endif +#if !defined(OS_CHROMEOS) + (*s_whitelist)[::prefs::kAccessibilityFocusHighlightEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; +#endif #if defined(OS_CHROMEOS) // Accounts / Users / People. @@ -407,6 +429,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[ash::prefs::kAccessibilityLargeCursorDipSize] = settings_api::PrefType::PREF_TYPE_NUMBER; + (*s_whitelist)[ash::prefs::kAccessibilityCursorColorEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[ash::prefs::kAccessibilityCursorColor] = + settings_api::PrefType::PREF_TYPE_NUMBER; (*s_whitelist)[ash::prefs::kAccessibilityScreenMagnifierEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[ash::prefs::kAccessibilityScreenMagnifierScale] = @@ -471,8 +497,6 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[plugin_vm::prefs::kPluginVmPrintersAllowed] = settings_api::PrefType::PREF_TYPE_BOOLEAN; - (*s_whitelist)[plugin_vm::prefs::kPluginVmCameraSharing] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; // Android Apps. (*s_whitelist)[arc::prefs::kArcEnabled] = @@ -511,12 +535,12 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[chromeos::kStatsReportingPref] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[::chromeos::prefs::kSuggestedContentEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[chromeos::kAttestationForContentProtectionEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[prefs::kRestoreLastLockScreenNote] = settings_api::PrefType::PREF_TYPE_BOOLEAN; - (*s_whitelist)[::prefs::kSettingsShowBrowserBanner] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[::prefs::kSettingsShowOSBanner] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/chromium/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc b/chromium/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc index bca4048309d..47c59642146 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/settings_private_apitest.cc @@ -11,11 +11,15 @@ #include "base/run_loop.h" #include "base/values.h" #include "build/build_config.h" +#include "chrome/browser/content_settings/host_content_settings_map_factory.h" #include "chrome/browser/extensions/api/settings_private/settings_private_delegate.h" #include "chrome/browser/extensions/api/settings_private/settings_private_delegate_factory.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/common/extensions/api/settings_private.h" #include "chrome/common/pref_names.h" +#include "components/content_settings/core/browser/host_content_settings_map.h" +#include "components/content_settings/core/test/content_settings_mock_provider.h" +#include "components/content_settings/core/test/content_settings_test_utils.h" #include "components/keyed_service/core/keyed_service.h" #include "components/policy/core/browser/browser_policy_connector.h" #include "components/policy/core/common/mock_configuration_policy_provider.h" @@ -102,6 +106,25 @@ IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetRecommendedPref) { EXPECT_TRUE(RunSettingsSubtest("getRecommendedPref")) << message_; } +IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetDisabledPref) { + HostContentSettingsMapFactory::GetForProfile(profile()) + ->SetDefaultContentSetting(ContentSettingsType::COOKIES, + ContentSetting::CONTENT_SETTING_BLOCK); + EXPECT_TRUE(RunSettingsSubtest("getDisabledPref")) << message_; +} + +IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetPartiallyManagedPref) { + auto provider = std::make_unique<content_settings::MockProvider>(); + provider->SetWebsiteSetting( + ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), + ContentSettingsType::COOKIES, std::string(), + std::make_unique<base::Value>(ContentSetting::CONTENT_SETTING_ALLOW)); + content_settings::TestUtils::OverrideProvider( + HostContentSettingsMapFactory::GetForProfile(profile()), + std::move(provider), HostContentSettingsMap::POLICY_PROVIDER); + EXPECT_TRUE(RunSettingsSubtest("getPartiallyManagedPref")) << message_; +} + IN_PROC_BROWSER_TEST_F(SettingsPrivateApiTest, GetAllPrefs) { EXPECT_TRUE(RunSettingsSubtest("getAllPrefs")) << message_; } diff --git a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc index beb1efb4780..bd741debea0 100644 --- a/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc +++ b/chromium/chrome/browser/extensions/api/storage/managed_value_store_cache.cc @@ -15,7 +15,6 @@ #include "base/memory/weak_ptr.h" #include "base/one_shot_event.h" #include "base/scoped_observer.h" -#include "base/task/post_task.h" #include "chrome/browser/extensions/api/storage/policy_value_store.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/policy/schema_registry_service.h" @@ -203,8 +202,8 @@ void ManagedValueStoreCache::ExtensionTracker::LoadSchemasOnFileTaskRunner( (*components)[(*it)->id()] = schema; } - base::PostTask(FROM_HERE, {BrowserThread::UI}, - base::BindOnce(&ExtensionTracker::Register, self, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&ExtensionTracker::Register, self, base::Owned(components.release()))); } diff --git a/chromium/chrome/browser/extensions/api/storage/policy_value_store.cc b/chromium/chrome/browser/extensions/api/storage/policy_value_store.cc index 51b4e2fb2cb..264828c2308 100644 --- a/chromium/chrome/browser/extensions/api/storage/policy_value_store.cc +++ b/chromium/chrome/browser/extensions/api/storage/policy_value_store.cc @@ -43,7 +43,7 @@ void PolicyValueStore::SetCurrentPolicy(const policy::PolicyMap& policy) { for (auto it = policy.begin(); it != policy.end(); ++it) { if (it->second.level == policy::POLICY_LEVEL_MANDATORY) { current_policy.SetWithoutPathExpansion( - it->first, it->second.value->CreateDeepCopy()); + it->first, it->second.value()->CreateDeepCopy()); } } diff --git a/chromium/chrome/browser/extensions/api/storage/settings_apitest.cc b/chromium/chrome/browser/extensions/api/storage/settings_apitest.cc index 7ab45ab303f..6c17d73a9ed 100644 --- a/chromium/chrome/browser/extensions/api/storage/settings_apitest.cc +++ b/chromium/chrome/browser/extensions/api/storage/settings_apitest.cc @@ -339,9 +339,8 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); } -// Disabled, see crbug.com/101110 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_OnChangedNotificationsFromSync) { + OnChangedNotificationsFromSync) { // We need 2 ResultCatchers because we'll be running the same test in both // regular and incognito mode. ResultCatcher catcher, catcher_incognito; @@ -381,12 +380,10 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); } -// Disabled, see crbug.com/101110 -// // TODO: boring test, already done in the unit tests. What we really should be // be testing is that the areas don't overlap. IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) { + OnChangedNotificationsFromSyncNotSentToLocal) { // We need 2 ResultCatchers because we'll be running the same test in both // regular and incognito mode. ResultCatcher catcher, catcher_incognito; @@ -517,6 +514,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) { extensions::DictionaryBuilder() .Set("string-policy", "value") .Set("string-enum-policy", "value-1") + .Set("another-string-policy", 123) // Test invalid policy value. .Set("int-policy", -123) .Set("int-enum-policy", 1) .Set("double-policy", 456e7) @@ -544,8 +542,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) { ASSERT_TRUE(RunExtensionTest("settings/managed_storage")) << message_; } -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_PRE_ManagedStorageEvents) { +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, PRE_ManagedStorageEvents) { ResultCatcher catcher; // This test starts without any test extensions installed. @@ -579,8 +576,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } -IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, - DISABLED_ManagedStorageEvents) { +IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageEvents) { // This test runs after PRE_ManagedStorageEvents without having deleted the // profile, so the extension is still around. While the browser restarted the // policy went back to the empty default, and so the extension should receive diff --git a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_api.cc b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_api.cc index 576c14641c4..4947eede0b4 100644 --- a/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_api.cc +++ b/chromium/chrome/browser/extensions/api/system_indicator/system_indicator_api.cc @@ -39,8 +39,10 @@ ExtensionFunction::ResponseAction SystemIndicatorSetIconFunction::Run() { if (const base::Value* canvas_set = set_icon_details.FindKeyOfType( "imageData", base::Value::Type::DICTIONARY)) { gfx::ImageSkia icon; - EXTENSION_FUNCTION_VALIDATE(ExtensionAction::ParseIconFromCanvasDictionary( - static_cast<const base::DictionaryValue&>(*canvas_set), &icon)); + EXTENSION_FUNCTION_VALIDATE( + ExtensionAction::ParseIconFromCanvasDictionary( + static_cast<const base::DictionaryValue&>(*canvas_set), &icon) == + ExtensionAction::IconParseResult::kSuccess); if (icon.isNull()) return RespondNow(Error("Icon invalid.")); diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc index f4d183ab977..38c40a7e868 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc @@ -919,7 +919,7 @@ ExtensionFunction::ResponseAction TabsQueryFunction::Run() { Browser* current_browser = ChromeExtensionFunctionDetails(this).GetCurrentBrowser(); for (auto* browser : *BrowserList::GetInstance()) { - if (!profile->IsSameProfile(browser->profile())) + if (!profile->IsSameOrParent(browser->profile())) continue; if (!browser->window()) @@ -1211,8 +1211,7 @@ bool TabsHighlightFunction::HighlightTab(TabStripModel* tabstrip, return true; } -TabsUpdateFunction::TabsUpdateFunction() : web_contents_(NULL) { -} +TabsUpdateFunction::TabsUpdateFunction() : web_contents_(nullptr) {} ExtensionFunction::ResponseAction TabsUpdateFunction::Run() { std::unique_ptr<tabs::Update::Params> params( @@ -1580,20 +1579,10 @@ ExtensionFunction::ResponseAction TabsReloadFunction::Run() { } } - if (web_contents->ShowingInterstitialPage()) { - // This does as same as Browser::ReloadInternal. - NavigationEntry* entry = web_contents->GetController().GetVisibleEntry(); - GURL reload_url = entry ? entry->GetURL() : GURL(url::kAboutBlankURL); - OpenURLParams params(reload_url, Referrer(), - WindowOpenDisposition::CURRENT_TAB, - ui::PAGE_TRANSITION_RELOAD, false); - current_browser->OpenURL(params); - } else { - web_contents->GetController().Reload( - bypass_cache ? content::ReloadType::BYPASSING_CACHE - : content::ReloadType::NORMAL, - true); - } + web_contents->GetController().Reload( + bypass_cache ? content::ReloadType::BYPASSING_CACHE + : content::ReloadType::NORMAL, + true); return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc index fd2ff75a3af..517e299ae20 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc @@ -177,7 +177,7 @@ TabsEventRouter::~TabsEventRouter() { } bool TabsEventRouter::ShouldTrackBrowser(Browser* browser) { - return profile_->IsSameProfile(browser->profile()) && + return profile_->IsSameOrParent(browser->profile()) && ExtensionTabUtil::BrowserSupportsTabs(browser); } @@ -571,7 +571,7 @@ void TabsEventRouter::DispatchEvent( std::unique_ptr<base::ListValue> args, EventRouter::UserGestureState user_gesture) { EventRouter* event_router = EventRouter::Get(profile); - if (!profile_->IsSameProfile(profile) || !event_router) + if (!profile_->IsSameOrParent(profile) || !event_router) return; auto event = std::make_unique<Event>(histogram_value, event_name, diff --git a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc index 5f41fa24c34..05b785e9ac0 100644 --- a/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc +++ b/chromium/chrome/browser/extensions/api/tabs/windows_event_router.cc @@ -180,14 +180,14 @@ WindowsEventRouter::~WindowsEventRouter() { } void WindowsEventRouter::OnAppWindowAdded(extensions::AppWindow* app_window) { - if (!profile_->IsSameProfile( + if (!profile_->IsSameOrParent( Profile::FromBrowserContext(app_window->browser_context()))) return; AddAppWindow(app_window); } void WindowsEventRouter::OnAppWindowRemoved(extensions::AppWindow* app_window) { - if (!profile_->IsSameProfile( + if (!profile_->IsSameOrParent( Profile::FromBrowserContext(app_window->browser_context()))) return; @@ -206,7 +206,7 @@ void WindowsEventRouter::OnWindowControllerAdded( WindowController* window_controller) { if (!HasEventListener(windows::OnCreated::kEventName)) return; - if (!profile_->IsSameProfile(window_controller->profile())) + if (!profile_->IsSameOrParent(window_controller->profile())) return; // Ignore any windows without an associated browser (e.g., AppWindows). if (!window_controller->GetBrowser()) @@ -228,7 +228,7 @@ void WindowsEventRouter::OnWindowControllerRemoved( WindowController* window_controller) { if (!HasEventListener(windows::OnRemoved::kEventName)) return; - if (!profile_->IsSameProfile(window_controller->profile())) + if (!profile_->IsSameOrParent(window_controller->profile())) return; // Ignore any windows without an associated browser (e.g., AppWindows). if (!window_controller->GetBrowser()) @@ -263,7 +263,7 @@ void WindowsEventRouter::OnActiveWindowChanged( Profile* window_profile = nullptr; int window_id = extension_misc::kUnknownWindowId; if (window_controller && - profile_->IsSameProfile(window_controller->profile())) { + profile_->IsSameOrParent(window_controller->profile())) { window_profile = window_controller->profile(); window_id = window_controller->GetWindowId(); } diff --git a/chromium/chrome/browser/extensions/api/terminal/crostini_startup_status.cc b/chromium/chrome/browser/extensions/api/terminal/crostini_startup_status.cc index e7d31651cd9..46b9d08c181 100644 --- a/chromium/chrome/browser/extensions/api/terminal/crostini_startup_status.cc +++ b/chromium/chrome/browser/extensions/api/terminal/crostini_startup_status.cc @@ -12,7 +12,6 @@ #include "base/strings/strcat.h" #include "base/strings/stringprintf.h" #include "base/system/sys_info.h" -#include "base/task/post_task.h" #include "base/time/time.h" #include "chrome/grit/generated_resources.h" #include "chromeos/dbus/util/version_loader.h" @@ -79,8 +78,8 @@ void CrostiniStartupStatus::ShowProgressAtInterval() { PrintProgress(); } ++spinner_index_; - base::PostDelayedTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostDelayedTask( + FROM_HERE, base::BindOnce(&CrostiniStartupStatus::ShowProgressAtInterval, weak_factory_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(300)); 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 f17fccee02c..e2d7220cd38 100644 --- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc +++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc @@ -17,7 +17,6 @@ #include "base/memory/scoped_refptr.h" #include "base/strings/stringprintf.h" #include "base/system/sys_info.h" -#include "base/task/post_task.h" #include "base/values.h" #include "chrome/browser/chromeos/crostini/crostini_features.h" #include "chrome/browser/chromeos/crostini/crostini_manager.h" @@ -93,8 +92,8 @@ void NotifyProcessOutput(content::BrowserContext* browser_context, const std::string& output_type, const std::string& output) { if (!content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)) { - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&NotifyProcessOutput, browser_context, tab_id, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&NotifyProcessOutput, browser_context, tab_id, terminal_id, output_type, output)); return; } @@ -249,12 +248,10 @@ TerminalPrivateOpenTerminalProcessFunction::OpenProcess( GetSwitch(¶ms_args, &vmshell_cmd, kSwitchTargetContainer, crostini::kCrostiniDefaultContainerName); std::string startup_id = params_args.GetSwitchValueASCII(kSwitchStartupId); + crostini::ContainerId container_id(vm_name, container_name); auto* mgr = crostini::CrostiniManager::GetForProfile(profile); - bool verbose = - !mgr->GetContainerInfo(crostini::kCrostiniDefaultVmName, - crostini::kCrostiniDefaultContainerName) - .has_value(); + bool verbose = !mgr->GetContainerInfo(container_id).has_value(); auto observer = std::make_unique<CrostiniStartupStatus>( base::BindRepeating(&NotifyProcessOutput, browser_context(), tab_id, startup_id, @@ -265,7 +262,7 @@ TerminalPrivateOpenTerminalProcessFunction::OpenProcess( CrostiniStartupStatus* observer_ptr = observer.get(); observer->ShowProgressAtInterval(); mgr->RestartCrostini( - vm_name, container_name, + container_id, base::BindOnce( &TerminalPrivateOpenTerminalProcessFunction::OnCrostiniRestarted, this, std::move(observer), user_id_hash, tab_id, @@ -331,8 +328,8 @@ void TerminalPrivateOpenTerminalProcessFunction::OpenOnRegistryTaskRunner( bool success = registry->OpenProcess(cmdline, user_id_hash, output_callback, &terminal_id); - base::PostTask(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(callback, success, terminal_id)); + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(callback, success, terminal_id)); } TerminalPrivateOpenVmshellProcessFunction:: @@ -379,8 +376,8 @@ void TerminalPrivateSendInputFunction::SendInputOnRegistryTaskRunner( bool success = chromeos::ProcessProxyRegistry::Get()->SendInput(terminal_id, text); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&TerminalPrivateSendInputFunction::RespondOnUIThread, this, success)); } @@ -412,8 +409,8 @@ void TerminalPrivateCloseTerminalProcessFunction::CloseOnRegistryTaskRunner( bool success = chromeos::ProcessProxyRegistry::Get()->CloseProcess(terminal_id); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &TerminalPrivateCloseTerminalProcessFunction::RespondOnUIThread, this, success)); @@ -450,8 +447,8 @@ void TerminalPrivateOnTerminalResizeFunction::OnResizeOnRegistryTaskRunner( bool success = chromeos::ProcessProxyRegistry::Get()->OnTerminalResize( terminal_id, width, height); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce( &TerminalPrivateOnTerminalResizeFunction::RespondOnUIThread, this, success)); @@ -537,8 +534,8 @@ void TerminalPrivateGetCroshSettingsFunction::AsyncRunWithStorage( ExtensionFunction::ResponseValue response = result.status().ok() ? OneArgument(result.PassSettings()) : Error(result.status().message); - base::PostTask( - FROM_HERE, {content::BrowserThread::UI}, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, base::BindOnce(&TerminalPrivateGetCroshSettingsFunction::Respond, this, std::move(response))); } 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 906e695f21f..dccb0b244d9 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 @@ -34,8 +34,8 @@ #include "media/audio/audio_system.h" #include "ui/aura/event_injector.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/ime/chromeos/ime_bridge.h" #include "ui/base/ime/constants.h" -#include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ui_base_features.h" @@ -409,10 +409,9 @@ void ChromeVirtualKeyboardDelegate::OnHasInputDevices( features->AppendString(GenerateFeatureFlag( "mozcinputlogic", base::FeatureList::IsEnabled(chromeos::features::kImeInputLogicMozc))); - // Flag used to enable decoder Mojo APIs instead of NaCl APIs. + // Flag used to enable UIL Mojo APIs instead of NaCl APIs. features->AppendString(GenerateFeatureFlag( - "usemojodecoder", base::FeatureList::IsEnabled( - chromeos::features::kImeDecoderWithSandbox))); + "usemojodecoder", chromeos::features::IsImeSandboxEnabled())); features->AppendString(GenerateFeatureFlag( "borderedkey", base::FeatureList::IsEnabled( chromeos::features::kVirtualKeyboardBorderedKey))); 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 edd6f2da449..34bc26bb443 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 @@ -80,7 +80,7 @@ WebNavigationEventRouter::WebNavigationEventRouter(Profile* profile) WebNavigationEventRouter::~WebNavigationEventRouter() = default; bool WebNavigationEventRouter::ShouldTrackBrowser(Browser* browser) { - return profile_->IsSameProfile(browser->profile()); + return profile_->IsSameOrParent(browser->profile()); } void WebNavigationEventRouter::OnTabStripModelChanged( 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 c0ad861dd45..2d5c8d6d994 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 @@ -46,6 +46,7 @@ #include "extensions/browser/api/web_request/web_request_api_constants.h" #include "extensions/browser/api/web_request/web_request_api_helpers.h" #include "extensions/browser/api/web_request/web_request_info.h" +#include "extensions/common/api/declarative_net_request.h" #include "extensions/common/api/web_request.h" #include "extensions/common/constants.h" #include "extensions/common/extension_messages.h" @@ -746,6 +747,8 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses4) { EXPECT_EQ(GURL(), effective_new_url); } +// TODO(crbug.com/1099066): Separate this test into subtests to improve +// readability. TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { net::HttpRequestHeaders base_headers; base_headers.SetHeader("key1", "value 1"); @@ -766,9 +769,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { WebRequestInfoInitParams info_params; WebRequestInfo info(std::move(info_params)); info.dnr_actions = std::vector<DNRRequestAction>(); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers0, &ignored_actions, - &ignore1, &ignore2, - &request_headers_modified0); + std::vector<const DNRRequestAction*> matched_dnr_actions; + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers0, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified0, &matched_dnr_actions); ASSERT_TRUE(headers0.GetHeader("key1", &header_value)); EXPECT_EQ("value 1", header_value); ASSERT_TRUE(headers0.GetHeader("key2", &header_value)); @@ -791,9 +795,9 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { bool request_headers_modified1; net::HttpRequestHeaders headers1; headers1.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers1, &ignored_actions, - &ignore1, &ignore2, - &request_headers_modified1); + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers1, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified1, &matched_dnr_actions); EXPECT_FALSE(headers1.HasHeader("key1")); ASSERT_TRUE(headers1.GetHeader("key2", &header_value)); EXPECT_EQ("value 3", header_value); @@ -818,9 +822,9 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { bool request_headers_modified2; net::HttpRequestHeaders headers2; headers2.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers2, &ignored_actions, - &ignore1, &ignore2, - &request_headers_modified2); + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers2, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified2, &matched_dnr_actions); EXPECT_FALSE(headers2.HasHeader("key1")); ASSERT_TRUE(headers2.GetHeader("key2", &header_value)); EXPECT_EQ("value 3", header_value); @@ -849,9 +853,9 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { bool request_headers_modified3; net::HttpRequestHeaders headers3; headers3.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers3, &ignored_actions, - &ignore1, &ignore2, - &request_headers_modified3); + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers3, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified3, &matched_dnr_actions); EXPECT_FALSE(headers3.HasHeader("key1")); ASSERT_TRUE(headers3.GetHeader("key2", &header_value)); EXPECT_EQ("value 3", header_value); @@ -864,6 +868,157 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { HasIgnoredAction(ignored_actions, "extid2", web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); EXPECT_TRUE(request_headers_modified3); + + // Check that headers removed by Declarative Net Request API can't be modified + // and result in a conflict. + ignored_actions.clear(); + ignore1.clear(); + ignore2.clear(); + bool request_headers_modified4 = false; + net::HttpRequestHeaders headers4; + headers4.MergeFrom(base_headers); + + DNRRequestAction modify_headers_action = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + modify_headers_action.request_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key5", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt)}; + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(modify_headers_action)); + + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers4, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified4, &matched_dnr_actions); + // Deleted by |d1|. + EXPECT_FALSE(headers4.HasHeader("key1")); + // Added by |d1|. + ASSERT_TRUE(headers4.GetHeader("key2", &header_value)); + EXPECT_EQ("value 3", header_value); + // Removed by Declarative Net Request API. + EXPECT_FALSE(headers4.HasHeader("key5")); + + EXPECT_EQ(2u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid3", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); + EXPECT_TRUE(request_headers_modified4); + + // Check that headers set by Declarative Net Request API can't be further + // modified and result in a conflict. + ignored_actions.clear(); + ignore1.clear(); + ignore2.clear(); + bool request_headers_modified5 = false; + net::HttpRequestHeaders headers5; + headers5.MergeFrom(base_headers); + + DNRRequestAction set_headers_action = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + + // Since key2 is set to "value 3" by both |set_headers_action| and + // |extid1|, |extid1| should not be ignored. + // Conversely, |set_headers_action| and |extid3| set different values for + // key5, therefore |extid4| should be ignored. + set_headers_action.request_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key2", api::declarative_net_request::HEADER_OPERATION_SET, + "value 3"), + DNRRequestAction::HeaderInfo( + "key5", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_value")}; + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(set_headers_action)); + + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers5, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified5, &matched_dnr_actions); + // Deleted by |d1|. + EXPECT_FALSE(headers5.HasHeader("key1")); + // Added by |d1| (same value as added by Declarative Net Request API). + ASSERT_TRUE(headers5.GetHeader("key2", &header_value)); + EXPECT_EQ("value 3", header_value); + // Set by Declarative Net Request API. + ASSERT_TRUE(headers5.GetHeader("key5", &header_value)); + EXPECT_EQ("dnr_value", header_value); + + EXPECT_EQ(2u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid3", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); + EXPECT_TRUE(request_headers_modified4); +} + +// Test conflict resolution for declarative net request actions from the same +// extension modifying the same request header. +TEST(ExtensionWebRequestHelpersTest, + TestMergeOnBeforeSendHeadersResponses_DeclarativeNetRequest) { + DNRRequestAction action_1 = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + action_1.request_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key1", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1"), + DNRRequestAction::HeaderInfo( + "key2", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1"), + DNRRequestAction::HeaderInfo( + "key3", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt)}; + + DNRRequestAction action_2 = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + action_2.request_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key1", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt), + DNRRequestAction::HeaderInfo( + "key2", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_2"), + DNRRequestAction::HeaderInfo( + "key3", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_2")}; + + WebRequestInfoInitParams info_params; + WebRequestInfo info(std::move(info_params)); + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(action_1)); + info.dnr_actions->push_back(std::move(action_2)); + + net::HttpRequestHeaders base_headers; + base_headers.SetHeader("key3", "value 3"); + helpers::IgnoredActions ignored_actions; + std::string header_value; + EventResponseDeltas deltas; + bool request_headers_modified; + std::set<std::string> ignore1, ignore2; + std::vector<const DNRRequestAction*> matched_dnr_actions; + + // Header modifications specified by |action1| are processed before those + // specified by |action2|. + MergeOnBeforeSendHeadersResponses( + info, deltas, &base_headers, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified, &matched_dnr_actions); + // Header set by a prior action cannot be removed by a subsequent action. + ASSERT_TRUE(base_headers.GetHeader("key1", &header_value)); + EXPECT_EQ("dnr_action_1", header_value); + + // Header set by a prior action cannot be set to a different value by a + // subsequent action. + ASSERT_TRUE(base_headers.GetHeader("key2", &header_value)); + EXPECT_EQ("dnr_action_1", header_value); + + // Header removed by a prior action cannot be set by a subsequent action. + EXPECT_FALSE(base_headers.HasHeader("key3")); + + EXPECT_EQ(0u, ignored_actions.size()); + EXPECT_TRUE(request_headers_modified); } // Ensure conflicts between different extensions are handled correctly with @@ -897,9 +1052,10 @@ TEST(ExtensionWebRequestHelpersTest, net::HttpRequestHeaders headers; headers.SetHeader("key1", "value 1"); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers, &ignored_actions, - &removed_headers, &set_headers, - &request_headers_modified); + std::vector<const DNRRequestAction*> matched_dnr_actions; + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers, &ignored_actions, &removed_headers, &set_headers, + &request_headers_modified, &matched_dnr_actions); std::string header_value; ASSERT_TRUE(headers.GetHeader("key1", &header_value)); @@ -962,9 +1118,11 @@ TEST(ExtensionWebRequestHelpersTest, WebRequestInfoInitParams info_params; WebRequestInfo info(std::move(info_params)); - MergeOnBeforeSendHeadersResponses(info, deltas, &headers1, &ignored_actions, - &ignore1, &ignore2, - &request_headers_modified1); + info.dnr_actions = std::vector<DNRRequestAction>(); + std::vector<const DNRRequestAction*> matched_dnr_actions; + MergeOnBeforeSendHeadersResponses( + info, deltas, &headers1, &ignored_actions, &ignore1, &ignore2, + &request_headers_modified1, &matched_dnr_actions); EXPECT_TRUE(headers1.HasHeader("Cookie")); ASSERT_TRUE(headers1.GetHeader("Cookie", &header_value)); EXPECT_EQ("name=new value; name2=new value; name4=\"value 4\"", header_value); @@ -1217,6 +1375,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { "HTTP/1.0 200 OK\r\n" "Key1: Value1\r\n" "Key2: Value2, Foo\r\n" + "Key4: Value4\r\n" "\r\n"; auto base_headers = base::MakeRefCounted<net::HttpResponseHeaders>( net::HttpUtil::AssembleRawHeaders(base_headers_string)); @@ -1233,11 +1392,12 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { info_params.url = GURL(kExampleUrl); WebRequestInfo info(std::move(info_params)); info.dnr_actions = std::vector<DNRRequestAction>(); + std::vector<const DNRRequestAction*> matched_dnr_actions; MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers0, &preserve_fragment_on_redirect_url0, &ignored_actions, - &response_headers_modified0); + &response_headers_modified0, &matched_dnr_actions); EXPECT_FALSE(new_headers0.get()); EXPECT_TRUE(preserve_fragment_on_redirect_url0.is_empty()); EXPECT_EQ(0u, ignored_actions.size()); @@ -1260,12 +1420,13 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers1, &preserve_fragment_on_redirect_url1, &ignored_actions, - &response_headers_modified1); + &response_headers_modified1, &matched_dnr_actions); ASSERT_TRUE(new_headers1.get()); EXPECT_TRUE(preserve_fragment_on_redirect_url1.is_empty()); std::multimap<std::string, std::string> expected1; - expected1.insert(std::pair<std::string, std::string>("Key2", "Value3")); - expected1.insert(std::pair<std::string, std::string>("Key3", "Foo")); + expected1.emplace("Key2", "Value3"); + expected1.emplace("Key3", "Foo"); + expected1.emplace("Key4", "Value4"); size_t iter = 0; std::string name; std::string value; @@ -1295,7 +1456,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers2, &preserve_fragment_on_redirect_url2, &ignored_actions, - &response_headers_modified2); + &response_headers_modified2, &matched_dnr_actions); ASSERT_TRUE(new_headers2.get()); EXPECT_TRUE(preserve_fragment_on_redirect_url2.is_empty()); iter = 0; @@ -1309,6 +1470,108 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { HasIgnoredAction(ignored_actions, "extid2", web_request::IGNORED_ACTION_TYPE_RESPONSE_HEADERS)); EXPECT_TRUE(response_headers_modified2); + + // Ensure headers removed by Declarative Net Request API can't be added by web + // request extensions and result in a conflict. + DNRRequestAction modify_headers_action = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + modify_headers_action.response_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key3", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt)}; + + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(modify_headers_action)); + + ignored_actions.clear(); + bool response_headers_modified3 = false; + scoped_refptr<net::HttpResponseHeaders> new_headers3; + GURL preserve_fragment_on_redirect_url3; + MergeOnHeadersReceivedResponses( + info, deltas, base_headers.get(), &new_headers3, + &preserve_fragment_on_redirect_url3, &ignored_actions, + &response_headers_modified3, &matched_dnr_actions); + ASSERT_TRUE(new_headers3.get()); + EXPECT_TRUE(preserve_fragment_on_redirect_url3.is_empty()); + iter = 0; + std::multimap<std::string, std::string> actual3; + while (new_headers3->EnumerateHeaderLines(&iter, &name, &value)) + actual3.emplace(name, value); + std::multimap<std::string, std::string> expected3; + expected3.emplace("Key2", "Value4"); + expected3.emplace("Key1", "Value1"); + expected3.emplace("Key4", "Value4"); + EXPECT_EQ(expected3, actual3); + EXPECT_EQ(1u, ignored_actions.size()); + + // The action specified by extid1 is ignored since it conflicted with + // |modify_headers_action| for the key3 header. + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid1", + web_request::IGNORED_ACTION_TYPE_RESPONSE_HEADERS)); + EXPECT_TRUE(response_headers_modified3); + + // Ensure headers appended by Declarative Net Request API can't be removed by + // web request extensions and result in a conflict, but can be further + // appended by web request extensions. + { + EventResponseDelta d3("extid3", base::Time::FromInternalValue(1000)); + d3.deleted_response_headers.push_back(ResponseHeader("Key4", "Value4")); + deltas.push_back(std::move(d3)); + } + deltas.sort(&InDecreasingExtensionInstallationTimeOrder); + + modify_headers_action = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + modify_headers_action.response_headers_to_modify = { + DNRRequestAction::HeaderInfo( + "key3", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_value_3"), + DNRRequestAction::HeaderInfo( + "key4", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_value_4")}; + + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(modify_headers_action)); + + ignored_actions.clear(); + bool response_headers_modified4 = false; + scoped_refptr<net::HttpResponseHeaders> new_headers4; + GURL preserve_fragment_on_redirect_url4; + MergeOnHeadersReceivedResponses( + info, deltas, base_headers.get(), &new_headers4, + &preserve_fragment_on_redirect_url4, &ignored_actions, + &response_headers_modified4, &matched_dnr_actions); + ASSERT_TRUE(new_headers4.get()); + EXPECT_TRUE(preserve_fragment_on_redirect_url4.is_empty()); + + iter = 0; + std::multimap<std::string, std::string> actual4; + while (new_headers4->EnumerateHeaderLines(&iter, &name, &value)) + actual4.emplace(name, value); + std::multimap<std::string, std::string> expected4; + + expected4.emplace("Key2", "Value3"); + expected4.emplace("Key3", "Foo"); + expected4.emplace("key3", "dnr_value_3"); + expected4.emplace("Key4", "Value4"); + expected4.emplace("key4", "dnr_value_4"); + EXPECT_EQ(expected4, actual4); + EXPECT_EQ(2u, ignored_actions.size()); + + // The action specified by extid1 is not ignored since it adds to the same + // header that the Declarative Net Request appends. The action specified by + // extid2 is ignored since it tries to replace the same header replaced by + // extid1. + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_RESPONSE_HEADERS)); + // The action specified by extid3 is ignored since it tries to remove Key4, + // which was appended by the Declarative Net Request API. + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid3", + web_request::IGNORED_ACTION_TYPE_RESPONSE_HEADERS)); + EXPECT_TRUE(response_headers_modified4); } // Check that we do not delete too much @@ -1341,11 +1604,12 @@ TEST(ExtensionWebRequestHelpersTest, info_params.url = GURL(kExampleUrl); WebRequestInfo info(std::move(info_params)); info.dnr_actions = std::vector<DNRRequestAction>(); + std::vector<const DNRRequestAction*> matched_dnr_actions; MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers1, &preserve_fragment_on_redirect_url1, &ignored_actions, - &response_headers_modified1); + &response_headers_modified1, &matched_dnr_actions); ASSERT_TRUE(new_headers1.get()); EXPECT_TRUE(preserve_fragment_on_redirect_url1.is_empty()); std::multimap<std::string, std::string> expected1; @@ -1390,11 +1654,13 @@ TEST(ExtensionWebRequestHelpersTest, WebRequestInfoInitParams info_params; info_params.url = GURL(kExampleUrl); WebRequestInfo info(std::move(info_params)); + info.dnr_actions = std::vector<DNRRequestAction>(); + std::vector<const DNRRequestAction*> matched_dnr_actions; MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers0, &preserve_fragment_on_redirect_url0, &ignored_actions, - &response_headers_modified0); + &response_headers_modified0, &matched_dnr_actions); EXPECT_FALSE(new_headers0.get()); EXPECT_TRUE(preserve_fragment_on_redirect_url0.is_empty()); EXPECT_EQ(0u, ignored_actions.size()); @@ -1415,7 +1681,7 @@ TEST(ExtensionWebRequestHelpersTest, MergeOnHeadersReceivedResponses( info, deltas, base_headers.get(), &new_headers1, &preserve_fragment_on_redirect_url1, &ignored_actions, - &response_headers_modified1); + &response_headers_modified1, &matched_dnr_actions); EXPECT_TRUE(new_headers1.get()); EXPECT_TRUE(new_headers1->HasHeaderValue("Location", new_url_1.spec())); @@ -1424,6 +1690,143 @@ TEST(ExtensionWebRequestHelpersTest, EXPECT_FALSE(response_headers_modified1); } +// Test conflict resolution for declarative net request actions modifying the +// same response header. +TEST(ExtensionWebRequestHelpersTest, + TestMergeOnHeadersReceivedResponses_DeclarativeNetRequest) { + using HeaderInfo = DNRRequestAction::HeaderInfo; + const ExtensionId ext_1 = "ext_1"; + const ExtensionId ext_2 = "ext_2"; + + // Test every combination of operations for two RequestActions from different + // extensions modifying the same header. + DNRRequestAction action_1 = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + action_1.extension_id = ext_1; + action_1.response_headers_to_modify = { + HeaderInfo("key1", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_1"), + HeaderInfo("key2", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_1"), + HeaderInfo("key3", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_1"), + + HeaderInfo("key4", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1"), + HeaderInfo("key5", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1"), + HeaderInfo("key6", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1"), + + HeaderInfo("key7", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt), + HeaderInfo("key8", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt), + + HeaderInfo("same_ext_key", + api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_1")}; + + DNRRequestAction action_2 = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + action_2.extension_id = ext_1; + action_2.response_headers_to_modify = {HeaderInfo( + "same_ext_key", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_2")}; + + DNRRequestAction action_3 = + CreateRequestActionForTesting(DNRRequestAction::Type::MODIFY_HEADERS); + action_3.extension_id = ext_2; + action_3.response_headers_to_modify = { + HeaderInfo("key1", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_3"), + HeaderInfo("key2", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_3"), + HeaderInfo("key3", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt), + + HeaderInfo("key4", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_3"), + HeaderInfo("key5", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_3"), + HeaderInfo("key6", api::declarative_net_request::HEADER_OPERATION_REMOVE, + base::nullopt), + + HeaderInfo("key7", api::declarative_net_request::HEADER_OPERATION_APPEND, + "dnr_action_3"), + HeaderInfo("key8", api::declarative_net_request::HEADER_OPERATION_SET, + "dnr_action_3")}; + + WebRequestInfoInitParams info_params; + info_params.url = GURL(kExampleUrl); + WebRequestInfo info(std::move(info_params)); + + info.dnr_actions = std::vector<DNRRequestAction>(); + info.dnr_actions->push_back(std::move(action_1)); + info.dnr_actions->push_back(std::move(action_2)); + info.dnr_actions->push_back(std::move(action_3)); + + helpers::IgnoredActions ignored_actions; + std::string header_value; + EventResponseDeltas deltas; + + char base_headers_string[] = + "HTTP/1.0 200 OK\r\n" + "key1: Value1\r\n" + "key4: Value4\r\n" + "key7: Value7\r\n" + "key8: Value8\r\n" + "\r\n"; + auto base_headers = base::MakeRefCounted<net::HttpResponseHeaders>( + net::HttpUtil::AssembleRawHeaders(base_headers_string)); + + bool response_headers_modified; + scoped_refptr<net::HttpResponseHeaders> new_headers; + GURL preserve_fragment_on_redirect_url; + std::vector<const DNRRequestAction*> matched_dnr_actions; + + MergeOnHeadersReceivedResponses( + info, deltas, base_headers.get(), &new_headers, + &preserve_fragment_on_redirect_url, &ignored_actions, + &response_headers_modified, &matched_dnr_actions); + EXPECT_TRUE(new_headers.get()); + EXPECT_TRUE(response_headers_modified); + + size_t iter = 0; + std::string name; + std::string value; + std::multimap<std::string, std::string> actual_headers; + while (new_headers->EnumerateHeaderLines(&iter, &name, &value)) + actual_headers.emplace(name, value); + + std::multimap<std::string, std::string> expected_headers; + // An append operation should allow subsequent appends, but not any other + // operations. + expected_headers.emplace("key1", "Value1"); + expected_headers.emplace("key1", "dnr_action_1"); + expected_headers.emplace("key1", "dnr_action_3"); + expected_headers.emplace("key2", "dnr_action_1"); + expected_headers.emplace("key3", "dnr_action_1"); + + // A set operation should not allow any subsequent operations from a different + // extension. + expected_headers.emplace("key4", "dnr_action_1"); + expected_headers.emplace("key5", "dnr_action_1"); + expected_headers.emplace("key6", "dnr_action_1"); + + // A remove operation should not allow any subsequent operations + // (key7 and key8 headers were removed by |action_1|). + + // A {set, append} sequence is allowed if both operations are specified by the + // same extension. + expected_headers.emplace("same_ext_key", "dnr_action_1"); + expected_headers.emplace("same_ext_key", "dnr_action_2"); + EXPECT_EQ(expected_headers, actual_headers); + + EXPECT_TRUE(preserve_fragment_on_redirect_url.is_empty()); + EXPECT_EQ(0u, ignored_actions.size()); +} + TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { helpers::IgnoredActions ignored_actions; EventResponseDeltas deltas; 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 1a8cccf02eb..a39706af881 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 @@ -58,6 +58,7 @@ #include "chrome/test/base/search_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "chromeos/login/login_state/scoped_test_public_session_login_state.h" +#include "components/embedder_support/switches.h" #include "components/google/core/common/google_switches.h" #include "components/policy/core/browser/browser_policy_connector.h" #include "components/policy/core/common/mock_configuration_policy_provider.h" @@ -131,6 +132,11 @@ namespace extensions { namespace { +// This is the public key of tools/origin_trials/eftest.key, used to validate +// origin trial tokens generated by tools/origin_trials/generate_token.py. +constexpr char kOriginTrialPublicKeyForTesting[] = + "dRCs+TocuKkocNKa0AtZ4awrt9XKH2SQCI6o4FY6BNA="; + class CancelLoginDialog : public content::NotificationObserver { public: CancelLoginDialog() { @@ -299,6 +305,8 @@ class ExtensionWebRequestApiTest : public ExtensionApiTest { void SetUpCommandLine(base::CommandLine* command_line) override { ExtensionApiTest::SetUpCommandLine(command_line); command_line->AppendSwitchASCII(switches::kGaiaUrl, "http://gaia.com"); + command_line->AppendSwitchASCII(embedder_support::kOriginTrialPublicKey, + kOriginTrialPublicKeyForTesting); } void RunPermissionTest( @@ -2155,14 +2163,13 @@ IN_PROC_BROWSER_TEST_F(LocalNTPInterceptionWebRequestAPITest, return result == "true"; }; - WebContents* web_contents = - browser()->tab_strip_model()->GetActiveWebContents(); - ASSERT_FALSE(GetAndResetOneGoogleBarRequestSeen()); ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUINewTabURL)); - ASSERT_TRUE(search::IsInstantNTP(web_contents)); - ASSERT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), - web_contents->GetController().GetVisibleEntry()->GetURL()); + ASSERT_EQ(local_ntp_test_utils::GetFinalNtpUrl(browser()->profile()), + browser() + ->tab_strip_model() + ->GetActiveWebContents() + ->GetLastCommittedURL()); WaitForOneGoogleBarDataUpdate(); ASSERT_TRUE(GetAndResetOneGoogleBarRequestSeen()); @@ -3084,11 +3091,11 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, } IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, AppCacheRequests) { - embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); - ASSERT_TRUE(StartEmbeddedTestServer()); - - GURL main_url = embedded_test_server()->GetURL( - "/appcache/simple_page_with_manifest.html"); + std::string origin = "http://127.0.0.1:8080"; + std::unique_ptr<content::URLLoaderInterceptor> url_loader_interceptor = + content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin( + "content/test/data", GURL(origin)); + GURL main_url(origin + "/appcache/simple_page_with_manifest.html"); base::string16 expected_title = base::ASCIIToUTF16("AppCache updated"); @@ -3139,11 +3146,16 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, AppCacheRequests) { // Regression test for http://crbug.com/996940. Requests that redirected to an // appcache handled URL could have request ID collisions. IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, RedirectToAppCacheRequest) { - embedded_test_server()->ServeFilesFromSourceDirectory("content/test/data"); + // Use the embedded test server to support server-redirect, but serve + // appcache from a fixed port using the url loader interceptor below + // so that the appcache origin trial works. ASSERT_TRUE(StartEmbeddedTestServer()); - GURL main_url = embedded_test_server()->GetURL( - "/appcache/simple_page_with_manifest.html"); + std::string origin = "http://127.0.0.1:8080"; + auto interceptor = + content::URLLoaderInterceptor::ServeFilesFromDirectoryAtOrigin( + "content/test/data", GURL(origin)); + GURL main_url(origin + "/appcache/simple_page_with_manifest.html"); base::string16 expected_title = base::ASCIIToUTF16("AppCache updated"); diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc index 2cdf1e053f9..f9336578432 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_browsertest.cc @@ -80,3 +80,9 @@ IN_PROC_BROWSER_TEST_F( << message_; } #endif + +IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiBrowserTest, TestStartStopStart) { + ASSERT_TRUE( + RunPlatformAppTest("api_test/webrtc_logging_private/start_stop_start")) + << message_; +} diff --git a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.cc b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.cc index 8e3faa800be..2425cd15e51 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.cc +++ b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.cc @@ -16,18 +16,76 @@ #include "extensions/common/extension.h" #include "extensions/common/extension_urls.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permission_set.h" namespace extensions { +namespace { + +// A helper function to determine if an extension from web store with given +// information should be blocked by enterprise policy. It checks extension's +// installation mode, permission and manifest type. +// Returns true if the extension |mode| is blocked, removed or allowed by +// wildcard/update_url but blocked by |manifest type| or |required permissions|. +bool IsExtensionInstallBlockedByPolicy( + ExtensionManagement* extension_management, + ExtensionManagement::InstallationMode mode, + const ExtensionId& extension_id, + const std::string& update_url, + Manifest::Type manifest_type, + const PermissionSet& required_permissions) { + switch (mode) { + case ExtensionManagement::INSTALLATION_BLOCKED: + case ExtensionManagement::INSTALLATION_REMOVED: + return true; + case ExtensionManagement::INSTALLATION_FORCED: + case ExtensionManagement::INSTALLATION_RECOMMENDED: + return false; + case ExtensionManagement::INSTALLATION_ALLOWED: + break; + } + + if (extension_management->IsInstallationExplicitlyAllowed(extension_id)) + return false; + + // Extension is allowed by wildcard or update_url, checks required permissions + // and manifest type. + // TODO(crbug.com/1088021): Find out the right way to handle extension policy + // priority. + if (!extension_management->IsAllowedManifestType(manifest_type, + extension_id)) { + return true; + } + + if (!extension_management->IsPermissionSetAllowed(extension_id, update_url, + required_permissions)) { + return true; + } + + return false; +} + +} // namespace ExtensionInstallStatus GetWebstoreExtensionInstallStatus( const ExtensionId& extension_id, Profile* profile) { + return GetWebstoreExtensionInstallStatus( + extension_id, profile, Manifest::Type::TYPE_UNKNOWN, PermissionSet()); +} + +ExtensionInstallStatus GetWebstoreExtensionInstallStatus( + const ExtensionId& extension_id, + Profile* profile, + const Manifest::Type manifest_type, + const PermissionSet& required_permission_set) { DCHECK(crx_file::id_util::IdIsValid(extension_id)); if (ExtensionPrefs::Get(profile)->HasDisableReason( extension_id, disable_reason::DISABLE_CUSTODIAN_APPROVAL_REQUIRED)) { return kCustodianApprovalRequired; } + + const GURL update_url = extension_urls::GetWebstoreUpdateUrl(); ExtensionManagement* extension_management = ExtensionManagementFactory::GetForBrowserContext(profile); // Always use webstore update url to check the installation mode because this @@ -35,8 +93,8 @@ ExtensionInstallStatus GetWebstoreExtensionInstallStatus( // |Extension| instance. Note that we don't handle the case where an offstore // extension with an identical ID is installed. ExtensionManagement::InstallationMode mode = - extension_management->GetInstallationMode( - extension_id, extension_urls::GetWebstoreUpdateUrl().spec()); + extension_management->GetInstallationMode(extension_id, + update_url.spec()); if (mode == ExtensionManagement::INSTALLATION_FORCED || mode == ExtensionManagement::INSTALLATION_RECOMMENDED) @@ -56,7 +114,9 @@ ExtensionInstallStatus GetWebstoreExtensionInstallStatus( // kBlockedByPolicy, kCanRequest or kRequestPending instead of kDisabled. // By doing so, user can still request an installed and policy blocked // extension. - if (mode == ExtensionManagement::INSTALLATION_ALLOWED) { + if (!IsExtensionInstallBlockedByPolicy( + extension_management, mode, extension_id, update_url.spec(), + manifest_type, required_permission_set)) { if (registry->disabled_extensions().Contains(extension_id)) return kDisabled; return kInstallable; @@ -67,6 +127,8 @@ ExtensionInstallStatus GetWebstoreExtensionInstallStatus( if (!profile->GetPrefs()->GetBoolean(prefs::kCloudExtensionRequestEnabled)) return kBlockedByPolicy; + // An extension which is explicitly blocked by enterprise policy can't be + // requested anymore. if (extension_management->IsInstallationExplicitlyBlocked(extension_id)) return kBlockedByPolicy; diff --git a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.h b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.h index bdb89f3e109..8164ef616e1 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.h +++ b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status.h @@ -6,11 +6,14 @@ #define CHROME_BROWSER_EXTENSIONS_API_WEBSTORE_PRIVATE_EXTENSION_INSTALL_STATUS_H_ #include "extensions/common/extension_id.h" +#include "extensions/common/manifest.h" class Profile; namespace extensions { +class PermissionSet; + enum ExtensionInstallStatus { // Extension is blocked by policy but can be requested. kCanRequest, @@ -34,12 +37,24 @@ enum ExtensionInstallStatus { kForceInstalled }; -// Returns the Extension install status for an Chrome web store extension with -// |extension_id| in |profile|. +// Returns the Extension install status for a Chrome web store extension with +// |extension_id| in |profile|. Note that this function won't check whether the +// extension's manifest type or required permissions are blocked by enterprise +// policy. type blocking or permission blocking. Please use this function only +// if manifest file is not available. ExtensionInstallStatus GetWebstoreExtensionInstallStatus( const ExtensionId& extension_id, Profile* profile); +// Returns the Extension install status for a Chrome web store extension with +// |extension_id| in |profile|. Also check if |manifest_type| or any permission +// in |required_permission_set| is blocked by enterprise policy. +ExtensionInstallStatus GetWebstoreExtensionInstallStatus( + const ExtensionId& extension_id, + Profile* profile, + const Manifest::Type manifest_type, + const PermissionSet& required_permission_set); + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_WEBSTORE_PRIVATE_EXTENSION_INSTALL_STATUS_H_ diff --git a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status_unittest.cc b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status_unittest.cc index d4cfc262b0a..9354e5382b6 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status_unittest.cc +++ b/chromium/chrome/browser/extensions/api/webstore_private/extension_install_status_unittest.cc @@ -20,6 +20,8 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/pref_names.h" #include "extensions/common/extension_builder.h" +#include "extensions/common/manifest.h" +#include "extensions/common/permissions/permission_set.h" namespace extensions { namespace { @@ -42,19 +44,6 @@ constexpr char kExtensionSettingsWithIdBlocked[] = R"({ } })"; -constexpr char kExtensionSettingsWithIdAllowed[] = R"({ - "abcdefghijklmnopabcdefghijklmnop": { - "installation_mode": "allowed" - } -})"; - -constexpr char kExtensionSettingsWithIdForced[] = R"({ - "abcdefghijklmnopabcdefghijklmnop": { - "installation_mode": "force_installed", - "update_url":"https://clients2.google.com/service/update2/crx" - } -})"; - } // namespace class ExtensionInstallStatusTest : public BrowserWithTestWindowTest { @@ -139,7 +128,12 @@ TEST_F(ExtensionInstallStatusTest, ExtensionAllowed) { } TEST_F(ExtensionInstallStatusTest, ExtensionForceInstalledByPolicy) { - SetExtensionSettings(kExtensionSettingsWithIdForced); + SetExtensionSettings(R"({ + "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "force_installed", + "update_url":"https://clients2.google.com/service/update2/crx" + } + })"); ExtensionRegistry::Get(profile())->AddEnabled(CreateExtension(kExtensionId)); EXPECT_EQ(ExtensionInstallStatus::kForceInstalled, GetWebstoreExtensionInstallStatus(kExtensionId, profile())); @@ -218,7 +212,11 @@ TEST_F(ExtensionInstallStatusTest, PendingExtenisonIsApproved) { SetPolicy(prefs::kCloudExtensionRequestEnabled, std::make_unique<base::Value>(true)); std::vector<ExtensionId> ids = {kExtensionId}; - SetExtensionSettings(kExtensionSettingsWithIdAllowed); + SetExtensionSettings(R"({ + "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "allowed" + } + })"); EXPECT_EQ(ExtensionInstallStatus::kInstallable, GetWebstoreExtensionInstallStatus(kExtensionId, profile())); } @@ -244,4 +242,282 @@ TEST_F(ExtensionInstallStatusTest, ExtensionCustodianApprovalRequired) { GetWebstoreExtensionInstallStatus(kExtensionId, profile())); } +TEST_F(ExtensionInstallStatusTest, ExtensionBlockedByManifestType) { + // TYPE_EXTENSION is blocked by policy + // TYPE_THEME and TYPE_HOSTED_APP are allowed. + SetExtensionSettings(R"({ + "*": { + "allowed_types": ["theme", "hosted_app"] + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_EXTENSION, + PermissionSet())); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_THEME, + PermissionSet())); + + SetPolicy(prefs::kCloudExtensionRequestEnabled, + std::make_unique<base::Value>(true)); + EXPECT_EQ(ExtensionInstallStatus::kCanRequest, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_EXTENSION, + PermissionSet())); + EXPECT_EQ(ExtensionInstallStatus::kCanRequest, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_HOSTED_APP, + PermissionSet())); + + // Request has been approved. Note that currently, manifest type blocking + // actually overrides per-id setup. We will find the right priority with + // crbug.com/1088016. + SetExtensionSettings(R"({ + "*": { + "allowed_types": ["theme", "hosted_app"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "allowed" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_EXTENSION, + PermissionSet())); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_HOSTED_APP, + PermissionSet())); + + // Request has been rejected. + SetExtensionSettings(R"({ + "*": { + "allowed_types": ["theme", "hosted_app"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "blocked" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_EXTENSION, + PermissionSet())); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_HOSTED_APP, + PermissionSet())); + + // Request has been forced installed. + SetExtensionSettings(R"({ + "*": { + "allowed_types": ["theme", "hosted_app"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "force_installed", + "update_url":"https://clients2.google.com/service/update2/crx" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kForceInstalled, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_EXTENSION, + PermissionSet())); + EXPECT_EQ(ExtensionInstallStatus::kForceInstalled, + GetWebstoreExtensionInstallStatus(kExtensionId, profile(), + Manifest::Type::TYPE_HOSTED_APP, + PermissionSet())); +} + +TEST_F(ExtensionInstallStatusTest, ExtensionBlockedByPermissions) { + // Block 'storage' for all extensions. + SetExtensionSettings(R"({ + "*": { + "blocked_permissions": ["storage"] + } + })"); + + // Extension with audio permission is still installable but not with storage. + APIPermissionSet api_permissions; + api_permissions.insert(APIPermission::kAudio); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + api_permissions.insert(APIPermission::kStorage); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // And they can be requested, + SetPolicy(prefs::kCloudExtensionRequestEnabled, + std::make_unique<base::Value>(true)); + EXPECT_EQ(ExtensionInstallStatus::kCanRequest, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been approved. + SetExtensionSettings(R"({ + "*": { + "blocked_permissions": ["storage"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "allowed" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been rejected. + SetExtensionSettings(R"({ + "*": { + "blocked_permissions": ["storage"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "blocked" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been force installed. + SetExtensionSettings(R"({ + "*": { + "blocked_permissions": ["storage"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "force_installed", + "update_url":"https://clients2.google.com/service/update2/crx" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kForceInstalled, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); +} + +TEST_F(ExtensionInstallStatusTest, ExtensionBlockedByPermissionsWithUpdateUrl) { + // Block 'downloads' for all extensions from web store. + SetExtensionSettings(R"({ + "update_url:https://clients2.google.com/service/update2/crx": { + "blocked_permissions": ["downloads"] + } + })"); + + APIPermissionSet api_permissions; + api_permissions.insert(APIPermission::kAudio); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + api_permissions.insert(APIPermission::kDownloads); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // And they can be requested, + SetPolicy(prefs::kCloudExtensionRequestEnabled, + std::make_unique<base::Value>(true)); + EXPECT_EQ(ExtensionInstallStatus::kCanRequest, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been approved. + SetExtensionSettings(R"({ + "update_url:https://clients2.google.com/service/update2/crx": { + "blocked_permissions": ["downloads"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "allowed" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been rejected. + SetExtensionSettings(R"({ + "update_url:https://clients2.google.com/service/update2/crx": { + "blocked_permissions": ["downloads"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "blocked" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kBlockedByPolicy, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); + + // Request has been force-installed. + SetExtensionSettings(R"({ + "update_url:https://clients2.google.com/service/update2/crx": { + "blocked_permissions": ["downloads"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "force_installed", + "update_url":"https://clients2.google.com/service/update2/crx" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kForceInstalled, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); +} + +TEST_F(ExtensionInstallStatusTest, + ExtensionBlockedByPermissionButWhitelistById) { + SetExtensionSettings(R"({ + "*": { + "blocked_permissions": ["storage"] + }, "abcdefghijklmnopabcdefghijklmnop": { + "installation_mode": "allowed" + }})"); + + // Per-id whitelisted has higher priority than blocked permissions. + APIPermissionSet api_permissions; + api_permissions.insert(APIPermission::kStorage); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); +} + +// Extension policies apply to non web store update url doesn't affect the +// status here. +TEST_F(ExtensionInstallStatusTest, NonWebstoreUpdateUrlPolicy) { + SetExtensionSettings(R"({ + "update_url:https://other.extensions/webstore": { + "installation_mode": "blocked" + } + })"); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus(kExtensionId, profile())); + + SetExtensionSettings(R"({ + "update_url:https://other.extensions/webstore": { + "blocked_permissions": ["downloads"] + } + })"); + APIPermissionSet api_permissions; + api_permissions.insert(APIPermission::kDownloads); + EXPECT_EQ(ExtensionInstallStatus::kInstallable, + GetWebstoreExtensionInstallStatus( + kExtensionId, profile(), Manifest::Type::TYPE_EXTENSION, + PermissionSet(api_permissions.Clone(), ManifestPermissionSet(), + URLPatternSet(), URLPatternSet()))); +} + } // namespace extensions 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 862650ef793..ebfeb3568a3 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 @@ -49,7 +49,10 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/common/extension.h" +#include "extensions/common/manifest.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/manifest_handlers/permissions_parser.h" +#include "extensions/common/permissions/permission_set.h" #include "net/base/load_flags.h" #include "net/url_request/url_request.h" #include "ui/base/l10n/l10n_util.h" @@ -118,7 +121,7 @@ std::unique_ptr<WebstoreInstaller::Approval> PendingApprovals::PopApproval( const std::string& id) { for (auto iter = approvals_.begin(); iter != approvals_.end(); ++iter) { if (iter->get()->extension_id == id && - profile->IsSameProfile(iter->get()->profile)) { + profile->IsSameOrParent(iter->get()->profile)) { std::unique_ptr<WebstoreInstaller::Approval> approval = std::move(*iter); approvals_.erase(iter); return approval; @@ -267,6 +270,9 @@ ConvertExtensionInstallStatusForAPI(ExtensionInstallStatus status) { // successfully. Otherwise, returns the initial extension install status. ExtensionInstallStatus AddExtensionToPendingList(const ExtensionId& id, Profile* profile) { + // There is no need to check whether the extension's required permissions or + // manifest type are blocked by the enterprise policy because extensions + // blocked by those are still requestable. ExtensionInstallStatus status = GetWebstoreExtensionInstallStatus(id, profile); // We put the |id| into the pending request list if it can be requested. @@ -447,8 +453,9 @@ void WebstorePrivateBeginInstallWithManifest3Function::OnWebstoreParseSuccess( #endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) // Check the management policy before the installation process begins. - ExtensionInstallStatus install_status = - GetWebstoreExtensionInstallStatus(id, profile); + ExtensionInstallStatus install_status = GetWebstoreExtensionInstallStatus( + id, profile, dummy_extension_->manifest()->type(), + PermissionsParser::GetRequiredPermissions(dummy_extension_.get())); if (install_status == kBlockedByPolicy) { ShowBlockedByPolicyDialog( dummy_extension_.get(), icon_, web_contents, @@ -582,7 +589,7 @@ bool WebstorePrivateBeginInstallWithManifest3Function:: parent_permission_dialog_ = ParentPermissionDialog::CreateParentPermissionDialogForExtension( - profile, web_contents, web_contents->GetTopLevelNativeWindow(), + profile, web_contents->GetTopLevelNativeWindow(), gfx::ImageSkia::CreateFrom1xBitmap(icon_), dummy_extension_.get(), std::move(done_callback)); parent_permission_dialog_->ShowDialog(); @@ -697,12 +704,13 @@ void WebstorePrivateBeginInstallWithManifest3Function::HandleInstallAbort( // The web store install histograms are a subset of the install histograms. // We need to record both histograms here since CrxInstaller::InstallUIAbort // is never called for web store install cancellations. - std::string histogram_name = user_initiated ? "WebStoreInstallCancel" - : "WebStoreInstallAbort"; - ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(), - histogram_name.c_str()); + if (user_initiated) { + ExtensionService::RecordPermissionMessagesHistogram( + dummy_extension_.get(), "WebStoreInstallCancel"); + } - histogram_name = user_initiated ? "InstallCancel" : "InstallAbort"; + std::string histogram_name = + user_initiated ? "InstallCancel" : "InstallAbort"; ExtensionService::RecordPermissionMessagesHistogram(dummy_extension_.get(), histogram_name.c_str()); @@ -1094,12 +1102,58 @@ WebstorePrivateGetExtensionStatusFunction::Run() { return RespondNow(Error(kWebstoreInvalidIdError)); } + if (!params->manifest) + return RespondNow(BuildResponseWithoutManifest(extension_id, profile)); + + data_decoder::DataDecoder::ParseJsonIsolated( + *(params->manifest), + base::BindOnce( + &WebstorePrivateGetExtensionStatusFunction::OnManifestParsed, this, + extension_id)); + return RespondLater(); +} + +ExtensionFunction::ResponseValue +WebstorePrivateGetExtensionStatusFunction::BuildResponseWithoutManifest( + const ExtensionId& extension_id, + Profile* profile) { ExtensionInstallStatus status = GetWebstoreExtensionInstallStatus(extension_id, profile); api::webstore_private::ExtensionInstallStatus api_status = ConvertExtensionInstallStatusForAPI(status); - return RespondNow( - OneArgument(GetExtensionStatus::Results::Create(api_status))); + return ArgumentList(GetExtensionStatus::Results::Create(api_status)); +} + +void WebstorePrivateGetExtensionStatusFunction::OnManifestParsed( + const ExtensionId& extension_id, + data_decoder::DataDecoder::ValueOrError result) { + if (!result.value || !result.value->is_dict()) { + Respond(Error(kWebstoreInvalidManifestError)); + return; + } + + if (!g_browser_process->profile_manager()->IsValidProfile( + chrome_details_.GetProfile())) { + Respond(Error(kWebstoreUserCancelledError)); + } + + std::string error; + auto dummy_extension = + Extension::Create(base::FilePath(), Manifest::INTERNAL, + base::Value::AsDictionaryValue(*result.value), + Extension::FROM_WEBSTORE, extension_id, &error); + + if (!dummy_extension) { + Respond(Error(kWebstoreInvalidManifestError)); + return; + } + + ExtensionInstallStatus status = GetWebstoreExtensionInstallStatus( + extension_id, chrome_details_.GetProfile(), dummy_extension->GetType(), + PermissionsParser::GetRequiredPermissions(dummy_extension.get())); + api::webstore_private::ExtensionInstallStatus api_status = + ConvertExtensionInstallStatusForAPI(status); + Respond(ArgumentList(GetExtensionStatus::Results::Create(api_status))); } WebstorePrivateRequestExtensionFunction:: @@ -1124,7 +1178,8 @@ WebstorePrivateRequestExtensionFunction::Run() { api::webstore_private::ExtensionInstallStatus api_status = ConvertExtensionInstallStatusForAPI(status); - return RespondNow(OneArgument(RequestExtension::Results::Create(api_status))); + return RespondNow( + ArgumentList(RequestExtension::Results::Create(api_status))); } } // namespace extensions 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 d1bbb96b944..b4b3e646698 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 @@ -372,6 +372,12 @@ class WebstorePrivateGetExtensionStatusFunction : public ExtensionFunction { private: ~WebstorePrivateGetExtensionStatusFunction() override; + ExtensionFunction::ResponseValue BuildResponseWithoutManifest( + const ExtensionId& extension_id, + Profile* profile); + void OnManifestParsed(const ExtensionId& extension_id, + data_decoder::DataDecoder::ValueOrError result); + // ExtensionFunction: ExtensionFunction::ResponseAction Run() override; diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc index 09b988d79e9..4a1771a1d79 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_apitest.cc @@ -55,7 +55,7 @@ #include "chrome/browser/supervised_user/supervised_user_service_factory.h" #include "chrome/browser/supervised_user/supervised_user_test_util.h" #include "chrome/browser/ui/supervised_user/parent_permission_dialog.h" -#include "chrome/browser/ui/views/parent_permission_dialog_view.h" +#include "chrome/browser/ui/views/supervised_user/parent_permission_dialog_view.h" #include "components/account_id/account_id.h" #include "components/signin/public/identity_manager/identity_test_environment.h" #include "extensions/common/extension_builder.h" diff --git a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc index 49e6ebbb3dc..1bff22cebc1 100644 --- a/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc +++ b/chromium/chrome/browser/extensions/api/webstore_private/webstore_private_unittest.cc @@ -23,6 +23,7 @@ #include "extensions/browser/extension_registry.h" #include "extensions/browser/pref_names.h" #include "extensions/common/extension_builder.h" +#include "services/data_decoder/public/cpp/test_support/in_process_data_decoder.h" namespace extensions { namespace { @@ -32,7 +33,10 @@ constexpr int kFakeTime = 12345; constexpr char kExtensionManifest[] = R"({ \"name\" : \"Extension\", \"manifest_version\": 3, - \"version\": \"0.1\"})"; + \"version\": \"0.1\", + \"permissions\": [ \"example.com\", \"downloads\"], + \"optional_permissions\" : [\"audio\"]})"; + constexpr char kBlockAllExtensionSettings[] = R"({ "*": { "installation_mode":"blocked", @@ -57,6 +61,24 @@ constexpr char kBlockedExtensionSettings[] = R"({ } })"; +constexpr char kBlockedManifestTypeExtensionSettings[] = R"({ + "*": { + "allowed_types": ["theme", "hosted_app"] + } +})"; + +constexpr char kBlockedDownloadsPermissionsExtensionSettings[] = R"({ + "*": { + "blocked_permissions": ["downloads"] + } +})"; + +constexpr char kBlockedAudioPermissionsExtensionSettings[] = R"({ + "*": { + "blocked_permissions": ["audio"] + } +})"; + constexpr char kWebstoreUserCancelledError[] = "User cancelled install"; constexpr char kWebstoreBlockByPolicy[] = "Extension installation is blocked by policy"; @@ -103,17 +125,18 @@ class WebstorePrivateExtensionInstallRequestBase : public ExtensionApiUnittest { return base::StringPrintf(R"(["%s"])", id); } + std::string GenerateArgs(const char* id, const char* manifest) { + return base::StringPrintf(R"(["%s", "%s"])", id, manifest); + } + scoped_refptr<const Extension> CreateExtension(const ExtensionId& id) { return ExtensionBuilder("extension").SetID(id).Build(); } void VerifyResponse(const ExtensionInstallStatus& expected_response, const base::Value* actual_response) { - ASSERT_TRUE(actual_response->is_list()); - const auto& actual_list = actual_response->GetList(); - ASSERT_EQ(1u, actual_list.size()); - ASSERT_TRUE(actual_list[0].is_string()); - EXPECT_EQ(ToString(expected_response), actual_list[0].GetString()); + ASSERT_TRUE(actual_response->is_string()); + EXPECT_EQ(ToString(expected_response), actual_response->GetString()); } private: @@ -121,7 +144,18 @@ class WebstorePrivateExtensionInstallRequestBase : public ExtensionApiUnittest { }; class WebstorePrivateGetExtensionStatusTest - : public WebstorePrivateExtensionInstallRequestBase {}; + : public WebstorePrivateExtensionInstallRequestBase { + public: + void SetUp() override { + WebstorePrivateExtensionInstallRequestBase::SetUp(); + in_process_data_decoder_ = + std::make_unique<data_decoder::test::InProcessDataDecoder>(); + } + + private: + std::unique_ptr<data_decoder::test::InProcessDataDecoder> + in_process_data_decoder_; +}; TEST_F(WebstorePrivateGetExtensionStatusTest, InvalidExtensionId) { auto function = @@ -141,6 +175,49 @@ TEST_F(WebstorePrivateGetExtensionStatusTest, ExtensionEnabled) { response.get()); } +TEST_F(WebstorePrivateGetExtensionStatusTest, InvalidManifest) { + auto function = + base::MakeRefCounted<WebstorePrivateGetExtensionStatusFunction>(); + EXPECT_EQ( + "Invalid manifest", + RunFunctionAndReturnError( + function.get(), GenerateArgs(kExtensionId, "invalid-manifest"))); +} + +TEST_F(WebstorePrivateGetExtensionStatusTest, ExtensionBlockdedByManifestType) { + SetExtensionSettings(kBlockedManifestTypeExtensionSettings, profile()); + auto function = + base::MakeRefCounted<WebstorePrivateGetExtensionStatusFunction>(); + std::unique_ptr<base::Value> response = RunFunctionAndReturnValue( + function.get(), GenerateArgs(kExtensionId, kExtensionManifest)); + VerifyResponse( + ExtensionInstallStatus::EXTENSION_INSTALL_STATUS_BLOCKED_BY_POLICY, + response.get()); +} + +TEST_F(WebstorePrivateGetExtensionStatusTest, ExtensionBlockdedByPermission) { + SetExtensionSettings(kBlockedDownloadsPermissionsExtensionSettings, + profile()); + auto function = + base::MakeRefCounted<WebstorePrivateGetExtensionStatusFunction>(); + std::unique_ptr<base::Value> response = RunFunctionAndReturnValue( + function.get(), GenerateArgs(kExtensionId, kExtensionManifest)); + VerifyResponse( + ExtensionInstallStatus::EXTENSION_INSTALL_STATUS_BLOCKED_BY_POLICY, + response.get()); +} + +TEST_F(WebstorePrivateGetExtensionStatusTest, + ExtensionNotBlockdedByOptionalPermission) { + SetExtensionSettings(kBlockedAudioPermissionsExtensionSettings, profile()); + auto function = + base::MakeRefCounted<WebstorePrivateGetExtensionStatusFunction>(); + std::unique_ptr<base::Value> response = RunFunctionAndReturnValue( + function.get(), GenerateArgs(kExtensionId, kExtensionManifest)); + VerifyResponse(ExtensionInstallStatus::EXTENSION_INSTALL_STATUS_INSTALLABLE, + response.get()); +} + class WebstorePrivateRequestExtensionTest : public WebstorePrivateExtensionInstallRequestBase { public: @@ -434,4 +511,57 @@ TEST_F(WebstorePrivateBeginInstallWithManifest3Test, VerifyBlockedByPolicyFunctionResult(function.get(), base::string16()); } +TEST_F(WebstorePrivateBeginInstallWithManifest3Test, + ExtensionBlockdedByManifestType) { + SetExtensionSettings(kBlockedManifestTypeExtensionSettings); + + std::unique_ptr<content::WebContents> web_contents = + content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + auto function = + base::MakeRefCounted<WebstorePrivateBeginInstallWithManifest3Function>(); + function->SetRenderFrameHost(web_contents->GetMainFrame()); + ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT); + + api_test_utils::RunFunction(function.get(), + GenerateArgs(kExtensionId, kExtensionManifest), + profile()); + VerifyBlockedByPolicyFunctionResult(function.get(), base::string16()); +} + +TEST_F(WebstorePrivateBeginInstallWithManifest3Test, + ExtensionBlockdedByPermission) { + SetExtensionSettings(kBlockedDownloadsPermissionsExtensionSettings); + + std::unique_ptr<content::WebContents> web_contents = + content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + auto function = + base::MakeRefCounted<WebstorePrivateBeginInstallWithManifest3Function>(); + function->SetRenderFrameHost(web_contents->GetMainFrame()); + ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT); + + api_test_utils::RunFunction(function.get(), + GenerateArgs(kExtensionId, kExtensionManifest), + profile()); + VerifyBlockedByPolicyFunctionResult(function.get(), base::string16()); +} + +TEST_F(WebstorePrivateBeginInstallWithManifest3Test, + ExtensionNotBlockdedByOptionalPermission) { + SetExtensionSettings(kBlockedAudioPermissionsExtensionSettings); + + std::unique_ptr<content::WebContents> web_contents = + content::WebContentsTester::CreateTestWebContents(profile(), nullptr); + auto function = + base::MakeRefCounted<WebstorePrivateBeginInstallWithManifest3Function>(); + function->SetRenderFrameHost(web_contents->GetMainFrame()); + ScopedTestDialogAutoConfirm auto_confirm(ScopedTestDialogAutoConfirm::ACCEPT); + + std::unique_ptr<base::Value> response = RunFunctionAndReturnValue( + function.get(), GenerateArgs(kExtensionId, kExtensionManifest)); + // The API returns empty string when extension is installed successfully. + ASSERT_TRUE(response); + ASSERT_TRUE(response->is_string()); + EXPECT_EQ(std::string(), response->GetString()); +} + } // namespace extensions |