diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-10-24 11:30:15 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-10-30 12:56:19 +0000 |
commit | 6036726eb981b6c4b42047513b9d3f4ac865daac (patch) | |
tree | 673593e70678e7789766d1f732eb51f613a2703b /chromium/chrome/browser/extensions/api | |
parent | 466052c4e7c052268fd931888cd58961da94c586 (diff) |
BASELINE: Update Chromium to 70.0.3538.78
Change-Id: Ie634710bf039e26c1957f4ae45e101bd4c434ae7
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chrome/browser/extensions/api')
165 files changed, 4984 insertions, 2980 deletions
diff --git a/chromium/chrome/browser/extensions/api/BUILD.gn b/chromium/chrome/browser/extensions/api/BUILD.gn index ee871a22d98..d6ca561c0b3 100644 --- a/chromium/chrome/browser/extensions/api/BUILD.gn +++ b/chromium/chrome/browser/extensions/api/BUILD.gn @@ -10,17 +10,15 @@ import("//tools/json_schema_compiler/json_schema_api.gni") assert(enable_extensions, "Cannot depend on extensions because enable_extensions=false.") -json_schema_api("api_registration") { - sources = chrome_extensions_api_schema_sources +function_registration("api_registration") { + sources = chrome_extensions_api_schema_sources + + chrome_extensions_api_uncompiled_sources impl_dir = "//chrome/browser/extensions/api" - bundle_registration = true configs = [ "//build/config:precompiled_headers" ] bundle_name = "Chrome" root_namespace = chrome_extensions_api_root_namespace schema_include_rules = chrome_extensions_api_schema_include_rules - uncompiled_sources = chrome_extensions_api_uncompiled_sources - deps = [ # Different APIs include headers from these targets. "//content/public/browser", diff --git a/chromium/chrome/browser/extensions/api/DEPS b/chromium/chrome/browser/extensions/api/DEPS index d7be18a52d9..5dc13a559c9 100644 --- a/chromium/chrome/browser/extensions/api/DEPS +++ b/chromium/chrome/browser/extensions/api/DEPS @@ -1,10 +1,7 @@ include_rules = [ "+apps", - "+chrome/browser/apps", - "+components/about_handler", - "+components/guest_view/common", - "+components/language/core/browser", "+services/device/public", + # Enable remote assistance on Chrome OS "+remoting/base", "+remoting/host", 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 76fa90e5d6f..6de7439ca22 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 @@ -14,8 +14,13 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/api/autofill_private/autofill_util.h" #include "chrome/common/extensions/api/autofill_private.h" +#include "components/autofill/content/browser/content_autofill_driver.h" +#include "components/autofill/content/browser/content_autofill_driver_factory.h" +#include "components/autofill/core/browser/autofill_manager.h" #include "components/autofill/core/browser/autofill_profile.h" +#include "components/autofill/core/browser/local_card_migration_manager.h" #include "components/autofill/core/browser/personal_data_manager.h" +#include "content/public/browser/web_contents.h" #include "extensions/browser/extension_function.h" #include "extensions/browser/extension_function_registry.h" #include "third_party/libaddressinput/src/cpp/include/libaddressinput/address_ui.h" @@ -531,4 +536,54 @@ AutofillPrivateGetCreditCardListFunction::Run() { credit_card_list))); } +//////////////////////////////////////////////////////////////////////////////// +// AutofillPrivateMigrateCreditCardsFunction + +AutofillPrivateMigrateCreditCardsFunction:: + AutofillPrivateMigrateCreditCardsFunction() + : chrome_details_(this) {} + +AutofillPrivateMigrateCreditCardsFunction:: + ~AutofillPrivateMigrateCreditCardsFunction() {} + +ExtensionFunction::ResponseAction +AutofillPrivateMigrateCreditCardsFunction::Run() { + autofill::PersonalDataManager* personal_data = + autofill::PersonalDataManagerFactory::GetForProfile( + chrome_details_.GetProfile()); + // Get the web contents to get autofill manager. + content::WebContents* web_contents = GetSenderWebContents(); + if (!personal_data || !personal_data->IsDataLoaded() || !web_contents) + return RespondNow(Error(kErrorDataUnavailable)); + + // Get the autofill manager from the web contains. Autofill manager owns an + // unique_ptr of form data importer. + autofill::AutofillManager* autofill_manager = + autofill::ContentAutofillDriverFactory::FromWebContents(web_contents) + ->DriverForFrame(web_contents->GetMainFrame()) + ->autofill_manager(); + if (!autofill_manager) + return RespondNow(Error(kErrorDataUnavailable)); + + // Get the form data importer from autofill manager. Form data importer owns + // an unique_ptr of local card migration manager. + autofill::FormDataImporter* form_data_importer = + autofill_manager->form_data_importer(); + if (!form_data_importer) + return RespondNow(Error(kErrorDataUnavailable)); + + // Get local card migration manager from form data importer. + autofill::LocalCardMigrationManager* local_card_migration_manager = + form_data_importer->local_card_migration_manager(); + if (!local_card_migration_manager) + return RespondNow(Error(kErrorDataUnavailable)); + + // Since we already check the migration requirements on the settings page, we + // don't check the migration requirements again. + local_card_migration_manager->GetMigratableCreditCards(); + local_card_migration_manager->AttemptToOfferLocalCardMigration( + /*is_from_settings_page=*/true); + return RespondNow(NoArguments()); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h index db7e98cfc25..a1b14bed656 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_private_api.h @@ -174,6 +174,25 @@ class AutofillPrivateGetCreditCardListFunction DISALLOW_COPY_AND_ASSIGN(AutofillPrivateGetCreditCardListFunction); }; +class AutofillPrivateMigrateCreditCardsFunction + : public UIThreadExtensionFunction { + public: + AutofillPrivateMigrateCreditCardsFunction(); + DECLARE_EXTENSION_FUNCTION("autofillPrivate.migrateCreditCards", + AUTOFILLPRIVATE_MIGRATECREDITCARDS); + + protected: + ~AutofillPrivateMigrateCreditCardsFunction() override; + + // ExtensionFunction overrides. + ResponseAction Run() override; + + private: + ChromeExtensionFunctionDetails chrome_details_; + + DISALLOW_COPY_AND_ASSIGN(AutofillPrivateMigrateCreditCardsFunction); +}; + } // namespace extensions #endif // CHROME_BROWSER_EXTENSIONS_API_AUTOFILL_PRIVATE_AUTOFILL_PRIVATE_API_H_ 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 2f3640b72fa..157f6c089b9 100644 --- a/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc +++ b/chromium/chrome/browser/extensions/api/autofill_private/autofill_util.cc @@ -132,7 +132,8 @@ autofill_private::CountryEntry CountryToCountryEntry( } autofill_private::CreditCardEntry CreditCardToCreditCardEntry( - const autofill::CreditCard& credit_card) { + const autofill::CreditCard& credit_card, + const autofill::PersonalDataManager& personal_data) { autofill_private::CreditCardEntry card; // Add all credit card fields to the entry. @@ -158,6 +159,11 @@ autofill_private::CreditCardEntry CreditCardToCreditCardEntry( credit_card.record_type() == autofill::CreditCard::LOCAL_CARD)); metadata->is_cached.reset(new bool( credit_card.record_type() == autofill::CreditCard::FULL_SERVER_CARD)); + // IsValid() checks if both card number and expiration date are valid. + // IsServerCard() checks whether there is a duplicated server card in + // |personal_data|. + metadata->is_migratable.reset(new bool( + credit_card.IsValid() && !personal_data.IsServerCard(&credit_card))); card.metadata = std::move(metadata); return card; @@ -210,7 +216,7 @@ CreditCardEntryList GenerateCreditCardList( CreditCardEntryList list; for (const autofill::CreditCard* card : cards) - list.push_back(CreditCardToCreditCardEntry(*card)); + list.push_back(CreditCardToCreditCardEntry(*card, personal_data)); return list; } diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc index b9eeb7c6b1e..ec98e3dff27 100644 --- a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc +++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.cc @@ -115,6 +115,9 @@ void AutomationEventRouter::DispatchTreeDestroyedEvent( api::automation_internal::OnAccessibilityTreeDestroyed::kEventName, std::move(args), browser_context); EventRouter::Get(browser_context)->BroadcastEvent(std::move(event)); + + if (tree_destroyed_callback_for_test_) + tree_destroyed_callback_for_test_.Run(tree_id); } void AutomationEventRouter::DispatchActionResult(const ui::AXActionData& data, @@ -135,6 +138,11 @@ void AutomationEventRouter::DispatchActionResult(const ui::AXActionData& data, ->DispatchEventToExtension(data.source_extension_id, std::move(event)); } +void AutomationEventRouter::SetTreeDestroyedCallbackForTest( + base::RepeatingCallback<void(int)> cb) { + tree_destroyed_callback_for_test_ = cb; +} + AutomationEventRouter::AutomationListener::AutomationListener() { } diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.h b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.h index 4994b1462a6..51dfc3f55d1 100644 --- a/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.h +++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_event_router.h @@ -8,6 +8,7 @@ #include <set> #include <vector> +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/singleton.h" #include "chrome/common/extensions/api/automation_internal.h" @@ -65,6 +66,8 @@ class AutomationEventRouter : public content::NotificationObserver { // Notify the source extension of the action of an action result. void DispatchActionResult(const ui::AXActionData& data, bool result); + void SetTreeDestroyedCallbackForTest(base::RepeatingCallback<void(int)> cb); + private: struct AutomationListener { AutomationListener(); @@ -109,6 +112,8 @@ class AutomationEventRouter : public content::NotificationObserver { Profile* active_profile_; + base::RepeatingCallback<void(int)> tree_destroyed_callback_for_test_; + friend struct base::DefaultSingletonTraits<AutomationEventRouter>; DISALLOW_COPY_AND_ASSIGN(AutomationEventRouter); diff --git a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc index 86fd2e7fe18..c2caa575b25 100644 --- a/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc +++ b/chromium/chrome/browser/extensions/api/automation_internal/automation_internal_api.cc @@ -52,8 +52,6 @@ namespace extensions { class AutomationWebContentsObserver; } // namespace extensions -DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::AutomationWebContentsObserver); - namespace extensions { namespace { diff --git a/chromium/chrome/browser/extensions/api/autotest_private/DEPS b/chromium/chrome/browser/extensions/api/autotest_private/DEPS new file mode 100644 index 00000000000..53fd62a1517 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/autotest_private/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+chrome/browser/ui/views/crostini", +] diff --git a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc index 49f4f3272c4..44c72495440 100644 --- a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc +++ b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.cc @@ -31,15 +31,19 @@ #include "ash/public/interfaces/constants.mojom.h" #include "base/feature_list.h" #include "chrome/browser/chromeos/arc/arc_util.h" +#include "chrome/browser/chromeos/crostini/crostini_manager.h" +#include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/login/lock/screen_locker.h" #include "chrome/browser/chromeos/printing/cups_printers_manager.h" #include "chrome/browser/chromeos/system/input_device_settings.h" #include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/ui/ash/login_screen_client.h" +#include "chrome/browser/ui/views/crostini/crostini_installer_view.h" #include "chrome/common/chrome_features.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/session_manager_client.h" #include "chromeos/printing/printer_configuration.h" +#include "components/arc/arc_prefs.h" #include "components/user_manager/user_manager.h" #include "content/public/common/service_manager_connection.h" #include "mojo/public/cpp/bindings/associated_binding.h" @@ -573,6 +577,8 @@ AutotestPrivateSetPlayStoreEnabledFunction::Run() { return RespondNow( Error("ARC enabled state cannot be changed for the current user")); } + profile->GetPrefs()->SetBoolean(arc::prefs::kArcLocationServiceEnabled, + true); return RespondNow(NoArguments()); } else { return RespondNow(Error("ARC is not available for the current user")); @@ -581,6 +587,44 @@ AutotestPrivateSetPlayStoreEnabledFunction::Run() { return RespondNow(Error("ARC is not available for the current platform")); } +ExtensionFunction::ResponseAction +AutotestPrivateRunCrostiniInstallerFunction::Run() { + DVLOG(1) << "AutotestPrivateInstallCrostiniFunction"; +#if defined(OS_CHROMEOS) + if (!IsCrostiniUIAllowedForProfile(ProfileManager::GetActiveUserProfile())) { + return RespondNow(Error("Crostini is not available for the current user")); + } + // Run GUI installer which will install crostini vm / container and + // start terminal app on completion. After starting the installer, + // we call RestartCrostini and we will be put in the pending restarters + // queue and be notified on success/otherwise of installation. + Profile* profile = Profile::FromBrowserContext(browser_context()); + CrostiniInstallerView::Show(profile); + CrostiniInstallerView::GetActiveViewForTesting()->Accept(); + crostini::CrostiniManager::GetInstance()->RestartCrostini( + profile, kCrostiniDefaultVmName, kCrostiniDefaultContainerName, + base::BindOnce( + &AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted, + this)); + + return RespondLater(); +#else + return RespondNow( + Error("Crostini is not available for the current platform")); +#endif +} + +#if defined(OS_CHROMEOS) +void AutotestPrivateRunCrostiniInstallerFunction::CrostiniRestarted( + crostini::ConciergeClientResult result) { + if (result == crostini::ConciergeClientResult::SUCCESS) { + Respond(NoArguments()); + } else { + Respond(Error("Error installing crostini")); + } +} +#endif + static base::LazyInstance<BrowserContextKeyedAPIFactory<AutotestPrivateAPI>>:: DestructorAtExit g_autotest_private_api_factory = LAZY_INSTANCE_INITIALIZER; diff --git a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h index 4575604fc44..b0623a31be7 100644 --- a/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h +++ b/chromium/chrome/browser/extensions/api/autotest_private/autotest_private_api.h @@ -21,6 +21,12 @@ namespace message_center { class Notification; } +#if defined(OS_CHROMEOS) +namespace crostini { +enum class ConciergeClientResult; +} +#endif + namespace extensions { class AutotestPrivateLogoutFunction : public UIThreadExtensionFunction { @@ -225,6 +231,23 @@ class AutotestPrivateSetPlayStoreEnabledFunction ResponseAction Run() override; }; +class AutotestPrivateRunCrostiniInstallerFunction + : public UIThreadExtensionFunction { + public: + AutotestPrivateRunCrostiniInstallerFunction() = default; + DECLARE_EXTENSION_FUNCTION("autotestPrivate.runCrostiniInstaller", + AUTOTESTPRIVATE_RUNCROSTINIINSTALLER) + + private: + ~AutotestPrivateRunCrostiniInstallerFunction() override = default; + ResponseAction Run() override; +#if defined(OS_CHROMEOS) + void CrostiniRestarted(crostini::ConciergeClientResult); +#endif + + DISALLOW_COPY_AND_ASSIGN(AutotestPrivateRunCrostiniInstallerFunction); +}; + class AutotestPrivateGetPrinterListFunction : public UIThreadExtensionFunction { public: AutotestPrivateGetPrinterListFunction() = default; 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 bbda385c078..17e44aa888b 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 @@ -51,9 +51,6 @@ using bookmarks::BookmarkNode; using bookmarks::BookmarkNodeData; using content::WebContents; -DEFINE_WEB_CONTENTS_USER_DATA_KEY( - extensions::BookmarkManagerPrivateDragEventRouter); - namespace extensions { namespace bookmark_keys = bookmark_api_constants; diff --git a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc index c4d79e3a704..5d6b31d7100 100644 --- a/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc +++ b/chromium/chrome/browser/extensions/api/bookmarks/bookmarks_api.cc @@ -21,7 +21,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/time/time.h" #include "build/build_config.h" #include "chrome/browser/bookmarks/bookmark_html_writer.h" diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller.h b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller.h index 5b575d2a32e..528720943d7 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller.h +++ b/chromium/chrome/browser/extensions/api/braille_display_private/braille_controller.h @@ -24,7 +24,7 @@ class BrailleController { static BrailleController* GetInstance(); virtual std::unique_ptr<DisplayState> GetDisplayState() = 0; - virtual void WriteDots(const std::vector<char>& cells, + virtual void WriteDots(const std::vector<uint8_t>& cells, unsigned int cols, unsigned int rows) = 0; virtual void AddObserver(BrailleObserver* observer) = 0; 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 a57126facdc..9b506217c61 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,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/time/time.h" #include "chrome/browser/extensions/api/braille_display_private/brlapi_connection.h" #include "chrome/browser/extensions/api/braille_display_private/brlapi_keycode_map.h" @@ -27,12 +27,17 @@ namespace api { namespace braille_display_private { namespace { + // Delay between detecting a directory update and trying to connect // to the brlapi. -const int64_t kConnectionDelayMs = 500; +constexpr base::TimeDelta kConnectionDelay = + base::TimeDelta::FromMilliseconds(500); + // How long to periodically retry connecting after a brltty restart. // Some displays are slow to connect. -const int64_t kConnectRetryTimeout = 20000; +constexpr base::TimeDelta kConnectRetryTimeout = + base::TimeDelta::FromSeconds(20); + } // namespace BrailleController::BrailleController() { @@ -100,7 +105,7 @@ std::unique_ptr<DisplayState> BrailleControllerImpl::GetDisplayState() { return display_state; } -void BrailleControllerImpl::WriteDots(const std::vector<char>& cells, +void BrailleControllerImpl::WriteDots(const std::vector<uint8_t>& cells, unsigned int cells_cols, unsigned int cells_rows) { DCHECK_CURRENTLY_ON(BrowserThread::IO); @@ -248,19 +253,16 @@ void BrailleControllerImpl::TryToConnect() { void BrailleControllerImpl::ResetRetryConnectHorizon() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - retry_connect_horizon_ = - base::Time::Now() + - base::TimeDelta::FromMilliseconds(kConnectRetryTimeout); + retry_connect_horizon_ = base::Time::Now() + kConnectRetryTimeout; } void BrailleControllerImpl::ScheduleTryToConnect() { DCHECK_CURRENTLY_ON(BrowserThread::IO); - base::TimeDelta delay(base::TimeDelta::FromMilliseconds(kConnectionDelayMs)); // Don't reschedule if there's already a connect scheduled or // the next attempt would fall outside of the retry limit. if (connect_scheduled_) return; - if (base::Time::Now() + delay > retry_connect_horizon_) { + if (base::Time::Now() + kConnectionDelay > retry_connect_horizon_) { VLOG(1) << "Stopping to retry to connect to brlapi"; return; } @@ -270,7 +272,7 @@ void BrailleControllerImpl::ScheduleTryToConnect() { BrowserThread::IO, FROM_HERE, base::BindOnce(&BrailleControllerImpl::TryToConnect, base::Unretained(this)), - delay); + kConnectionDelay); } void BrailleControllerImpl::Disconnect() { 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 82fe471bf73..fc98d3d832d 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 @@ -25,7 +25,7 @@ class BrailleControllerImpl : public BrailleController { public: static BrailleControllerImpl* GetInstance(); std::unique_ptr<DisplayState> GetDisplayState() override; - void WriteDots(const std::vector<char>& cells, + void WriteDots(const std::vector<uint8_t>& cells, unsigned int cols, unsigned int rows) override; void AddObserver(BrailleObserver* observer) override; @@ -78,7 +78,7 @@ class BrailleControllerImpl : public BrailleController { scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_; // Manipulated on the UI thread. - base::ObserverList<BrailleObserver> observers_; + base::ObserverList<BrailleObserver>::Unchecked observers_; // Manipulated by the SequencedTaskRunner. base::FilePathWatcher file_path_watcher_; diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.cc b/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.cc index 5e8a8c5be24..e732981f223 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.cc +++ b/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.cc @@ -15,7 +15,7 @@ std::unique_ptr<DisplayState> StubBrailleController::GetDisplayState() { return std::unique_ptr<DisplayState>(new DisplayState); } -void StubBrailleController::WriteDots(const std::vector<char>& cells, +void StubBrailleController::WriteDots(const std::vector<uint8_t>& cells, unsigned int cols, unsigned int rows) {} diff --git a/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h b/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h index cc159125575..44f36ad080b 100644 --- a/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h +++ b/chromium/chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h @@ -17,7 +17,7 @@ class StubBrailleController : public BrailleController { public: StubBrailleController(); std::unique_ptr<DisplayState> GetDisplayState() override; - void WriteDots(const std::vector<char>& cells, + void WriteDots(const std::vector<uint8_t>& cells, unsigned int cols, unsigned int rows) override; void AddObserver(BrailleObserver* observer) override; 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 244be025170..9c960c21cdb 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 @@ -14,11 +14,15 @@ #include "base/values.h" #include "chrome/browser/browsing_data/browsing_data_helper.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h" #include "chrome/browser/plugins/plugin_data_remover_helper.h" #include "chrome/browser/plugins/plugin_prefs.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/account_reconcilor_factory.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_features.h" #include "chrome/common/pref_names.h" @@ -123,6 +127,16 @@ bool IsRemovalPermitted(int removal_mask, PrefService* prefs) { return true; } +// Returns true if Sync is currently running (i.e. enabled and not in error). +bool IsSyncRunning(Profile* profile) { + browser_sync::ProfileSyncService* sync_service = + ProfileSyncServiceFactory::GetForProfile(profile); + SigninManagerBase* signin_manager = + SigninManagerFactory::GetForProfile(profile); + sync_ui_util::MessageType sync_status = + sync_ui_util::GetStatus(profile, sync_service, *signin_manager); + return sync_status == sync_ui_util::SYNCED; +} } // namespace bool BrowsingDataSettingsFunction::isDataTypeSelected( @@ -253,11 +267,9 @@ BrowsingDataRemoverFunction::BrowsingDataRemoverFunction() : observer_(this) {} void BrowsingDataRemoverFunction::OnBrowsingDataRemoverDone() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - + synced_data_deletion_.reset(); observer_.RemoveAll(); - this->SendResponse(true); - Release(); // Balanced in RunAsync. } @@ -316,6 +328,10 @@ bool BrowsingDataRemoverFunction::RunAsync() { BrowsingDataRemoverFunction::~BrowsingDataRemoverFunction() {} +bool BrowsingDataRemoverFunction::IsPauseSyncAllowed() { + return true; +} + void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported( scoped_refptr<PluginPrefs> plugin_prefs) { if (!PluginDataRemoverHelper::IsSupported(plugin_prefs.get())) @@ -327,11 +343,20 @@ void BrowsingDataRemoverFunction::CheckRemovingPluginDataSupported( } void BrowsingDataRemoverFunction::StartRemoving() { + Profile* profile = GetProfile(); content::BrowsingDataRemover* remover = - content::BrowserContext::GetBrowsingDataRemover(GetProfile()); + content::BrowserContext::GetBrowsingDataRemover(profile); + // Add a ref (Balanced in OnBrowsingDataRemoverDone) AddRef(); + // Prevent Sync from being paused, if required. + DCHECK(!synced_data_deletion_); + if (!IsPauseSyncAllowed() && IsSyncRunning(profile)) { + synced_data_deletion_ = AccountReconcilorFactory::GetForProfile(profile) + ->GetScopedSyncDataDeletion(); + } + // Create a BrowsingDataRemover, set the current object as an observer (so // that we're notified after removal) and call remove() with the arguments // we've generated above. We can use a raw pointer here, as the browsing data @@ -418,6 +443,10 @@ bool BrowsingDataRemoveFunction::GetRemovalMask(int* removal_mask) { return true; } +bool BrowsingDataRemoveFunction::IsPauseSyncAllowed() { + return false; +} + bool BrowsingDataRemoveAppcacheFunction::GetRemovalMask(int* removal_mask) { *removal_mask = content::BrowsingDataRemover::DATA_TYPE_APP_CACHE; 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 15db8b7d0ed..d288fc63ee5 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 @@ -14,6 +14,7 @@ #include "base/scoped_observer.h" #include "chrome/browser/extensions/chrome_extension_function.h" #include "components/browsing_data/core/browsing_data_utils.h" +#include "components/signin/core/browser/account_reconcilor.h" #include "content/public/browser/browsing_data_remover.h" class PluginPrefs; @@ -104,13 +105,19 @@ class BrowsingDataRemoverFunction protected: ~BrowsingDataRemoverFunction() override; + private: // Children should override this method to provide the proper removal mask // 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; - private: + // 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 + // being paused. This is important when synced data is being removed, and + // pausing Sync would prevent the data from being deleted on the server. + virtual bool IsPauseSyncAllowed(); + // Updates the removal bitmask according to whether removing plugin data is // supported or not. void CheckRemovingPluginDataSupported( @@ -131,6 +138,8 @@ class BrowsingDataRemoverFunction ScopedObserver<content::BrowsingDataRemover, content::BrowsingDataRemover::Observer> observer_; + std::unique_ptr<AccountReconcilor::ScopedSyncedDataDeletion> + synced_data_deletion_; }; class BrowsingDataRemoveAppcacheFunction : public BrowsingDataRemoverFunction { @@ -154,6 +163,7 @@ class BrowsingDataRemoveFunction : public BrowsingDataRemoverFunction { // BrowsingDataRemoverFunction: bool GetRemovalMask(int* removal_mask) override; + bool IsPauseSyncAllowed() override; }; class BrowsingDataRemoveCacheFunction : public BrowsingDataRemoverFunction { 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 aeea3eef7bb..4208518f151 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 @@ -5,23 +5,42 @@ #include <memory> #include <string> +#include "base/callback.h" #include "base/json/json_string_value_serializer.h" #include "base/memory/ref_counted.h" #include "base/strings/pattern.h" #include "base/strings/string_util.h" +#include "base/test/bind_test_util.h" #include "base/values.h" #include "chrome/browser/browsing_data/browsing_data_helper.h" #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h" #include "chrome/browser/extensions/api/browsing_data/browsing_data_api.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/signin/account_reconcilor_factory.h" +#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" +#include "chrome/browser/signin/scoped_account_consistency.h" +#include "chrome/browser/signin/signin_manager_factory.h" +#include "chrome/browser/sync/profile_sync_service_factory.h" +#include "chrome/browser/sync/sync_ui_util.h" #include "chrome/browser/ui/browser.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/in_process_browser_test.h" +#include "components/browser_sync/profile_sync_service.h" #include "components/browsing_data/core/browsing_data_utils.h" #include "components/browsing_data/core/pref_names.h" #include "components/prefs/pref_service.h" +#include "components/signin/core/browser/account_reconcilor.h" +#include "components/signin/core/browser/profile_oauth2_token_service.h" +#include "components/signin/core/browser/signin_buildflags.h" +#include "components/signin/core/browser/signin_manager.h" #include "content/public/browser/browsing_data_remover.h" +#include "content/public/browser/storage_partition.h" +#include "google_apis/gaia/gaia_urls.h" +#include "google_apis/gaia/google_service_auth_error.h" +#include "mojo/public/cpp/bindings/callback_helpers.h" +#include "net/cookies/canonical_cookie.h" +#include "url/gurl.h" using extension_function_test_utils::RunFunctionAndReturnError; using extension_function_test_utils::RunFunctionAndReturnSingleResult; @@ -298,6 +317,34 @@ class ExtensionBrowsingDataTest : public InProcessBrowserTest { content::BrowsingDataRemover* remover_; }; +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +// Sets the APISID Gaia cookie, which is monitored by the AccountReconcilor. +bool SetGaiaCookieForProfile(Profile* profile) { + GURL google_url = GaiaUrls::GetInstance()->google_url(); + net::CanonicalCookie cookie("APISID", std::string(), "." + google_url.host(), + "/", base::Time(), base::Time(), base::Time(), + false, false, net::CookieSameSite::DEFAULT_MODE, + net::COOKIE_PRIORITY_DEFAULT); + + bool success = false; + base::RunLoop loop; + base::OnceClosure loop_quit = loop.QuitClosure(); + base::OnceCallback<void(bool)> callback = + base::BindLambdaForTesting([&success, &loop_quit](bool s) { + success = s; + std::move(loop_quit).Run(); + }); + network::mojom::CookieManager* cookie_manager = + content::BrowserContext::GetDefaultStoragePartition(profile) + ->GetCookieManagerForBrowserProcess(); + cookie_manager->SetCanonicalCookie( + cookie, true, true, + mojo::WrapCallbackWithDefaultInvokeIfNotRun(std::move(callback), false)); + loop.Run(); + return success; +} +#endif + } // namespace IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemovalProhibited) { @@ -358,6 +405,101 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, RemoveBrowsingDataAll) { GetRemovalMask()); } +#if BUILDFLAG(ENABLE_DICE_SUPPORT) +// Test that Sync is not paused when browsing data is cleared. +IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, Syncing) { + Profile* profile = browser()->profile(); + // Set a Gaia cookie. + ASSERT_TRUE(SetGaiaCookieForProfile(profile)); + // Set a Sync account and a secondary account. + const char kPrimaryAccountId[] = "primary_account_id"; + const char kSecondaryAccountId[] = "secondary_account_id"; + ProfileOAuth2TokenService* token_service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile); + token_service->UpdateCredentials(kPrimaryAccountId, "token"); + ASSERT_TRUE(token_service->RefreshTokenIsAvailable(kPrimaryAccountId)); + token_service->UpdateCredentials(kSecondaryAccountId, "token"); + ASSERT_TRUE(token_service->RefreshTokenIsAvailable(kSecondaryAccountId)); + SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile); + signin_manager->SetAuthenticatedAccountInfo(kPrimaryAccountId, + "user@gmail.com"); + // Sync is running. + browser_sync::ProfileSyncService* sync_service = + ProfileSyncServiceFactory::GetForProfile(profile); + sync_service->SetFirstSetupComplete(); + sync_ui_util::MessageType sync_status = + sync_ui_util::GetStatus(profile, sync_service, *signin_manager); + ASSERT_EQ(sync_ui_util::SYNCED, sync_status); + // Clear browsing data. + scoped_refptr<BrowsingDataRemoveFunction> function = + new BrowsingDataRemoveFunction(); + EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( + function.get(), kRemoveEverythingArguments, browser())); + // Check that the Sync token was not revoked. + EXPECT_TRUE(token_service->RefreshTokenIsAvailable(kPrimaryAccountId)); + EXPECT_FALSE(token_service->RefreshTokenHasError(kPrimaryAccountId)); + // Check that the secondary token was revoked. + EXPECT_FALSE(token_service->RefreshTokenIsAvailable(kSecondaryAccountId)); +} + +// Test that Sync is paused when browsing data is cleared if Sync was in +// authentication error. +IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, SyncError) { + Profile* profile = browser()->profile(); + // Set a Gaia cookie. + ASSERT_TRUE(SetGaiaCookieForProfile(profile)); + // Set a Sync account with authentication error. + const char kAccountId[] = "account_id"; + ProfileOAuth2TokenService* token_service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile); + token_service->UpdateCredentials(kAccountId, "token"); + ASSERT_TRUE(token_service->RefreshTokenIsAvailable(kAccountId)); + SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile); + signin_manager->SetAuthenticatedAccountInfo(kAccountId, "user@gmail.com"); + token_service->GetDelegate()->UpdateAuthError( + kAccountId, GoogleServiceAuthError::FromInvalidGaiaCredentialsReason( + GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_SERVER)); + // Sync is not running. + sync_ui_util::MessageType sync_status = sync_ui_util::GetStatus( + profile, ProfileSyncServiceFactory::GetForProfile(profile), + *signin_manager); + ASSERT_NE(sync_ui_util::SYNCED, sync_status); + // Clear browsing data. + scoped_refptr<BrowsingDataRemoveFunction> function = + new BrowsingDataRemoveFunction(); + EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( + function.get(), kRemoveEverythingArguments, browser())); + // Check that the account was not removed and Sync was paused. + EXPECT_TRUE(token_service->RefreshTokenIsAvailable(kAccountId)); + EXPECT_EQ(GoogleServiceAuthError::InvalidGaiaCredentialsReason:: + CREDENTIALS_REJECTED_BY_CLIENT, + token_service->GetAuthError(kAccountId) + .GetInvalidGaiaCredentialsReason()); +} + +// Test that the tokens are revoked when browsing data is cleared when there is +// no primary account. +IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, NotSyncing) { + Profile* profile = browser()->profile(); + // Set a Gaia cookie. + ASSERT_TRUE(SetGaiaCookieForProfile(profile)); + // Set a non-Sync account. + const char kAccountId[] = "account_id"; + ProfileOAuth2TokenService* token_service = + ProfileOAuth2TokenServiceFactory::GetForProfile(profile); + token_service->UpdateCredentials(kAccountId, "token"); + ASSERT_TRUE(token_service->RefreshTokenIsAvailable(kAccountId)); + // Clear browsing data. + scoped_refptr<BrowsingDataRemoveFunction> function = + new BrowsingDataRemoveFunction(); + EXPECT_EQ(NULL, RunFunctionAndReturnSingleResult( + function.get(), kRemoveEverythingArguments, browser())); + // Check that the account was removed. + EXPECT_FALSE(token_service->RefreshTokenIsAvailable(kAccountId)); +} +#endif + IN_PROC_BROWSER_TEST_F(ExtensionBrowsingDataTest, BrowsingDataOriginTypeMask) { RunBrowsingDataRemoveFunctionAndCompareOriginTypeMask("{}", 0); 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 8596f7dd5f0..260ed83d77f 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 @@ -96,7 +96,7 @@ CertificateProviderInternalReportCertificatesFunction::Run() { } chromeos::certificate_provider::CertificateInfoList cert_infos; - std::vector<std::vector<char>> rejected_certificates; + std::vector<std::vector<uint8_t>> rejected_certificates; for (const api_cp::CertificateInfo& input_cert_info : *params->certificates) { chromeos::certificate_provider::CertificateInfo parsed_cert_info; @@ -122,7 +122,7 @@ bool CertificateProviderInternalReportCertificatesFunction:: ParseCertificateInfo( const api_cp::CertificateInfo& info, chromeos::certificate_provider::CertificateInfo* out_info) { - const std::vector<char>& cert_der = info.certificate; + const std::vector<uint8_t>& cert_der = info.certificate; if (cert_der.empty()) { WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR, kCertificateProviderErrorInvalidX509Cert); @@ -134,7 +134,7 @@ bool CertificateProviderInternalReportCertificatesFunction:: net::X509Certificate::UnsafeCreateOptions options; options.printable_string_is_utf8 = true; out_info->certificate = net::X509Certificate::CreateFromBytesUnsafeOptions( - cert_der.data(), cert_der.size(), options); + reinterpret_cast<const char*>(cert_der.data()), cert_der.size(), options); if (!out_info->certificate) { WriteToConsole(content::CONSOLE_MESSAGE_LEVEL_ERROR, kCertificateProviderErrorInvalidX509Cert); diff --git a/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc b/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc index 193801d9a5f..817b5ca8b26 100644 --- a/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc +++ b/chromium/chrome/browser/extensions/api/cloud_print_private/cloud_print_private_api.cc @@ -6,6 +6,7 @@ #include <memory> #include <string> +#include <utility> #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service.h" #include "chrome/browser/printing/cloud_print/cloud_print_proxy_service_factory.h" @@ -109,10 +110,8 @@ ExtensionFunction::ResponseAction CloudPrintPrivateGetPrintersFunction::Run() { if (!service) return RespondNow(Error(kErrorIncognito)); - // TODO(https://crbug.com/845250): CloudPrintProxyService::GetPrinters() may - // not invoke the callback, which means this function may never respond. service->GetPrinters( - base::Bind(&CloudPrintPrivateGetPrintersFunction::SendResults, this)); + base::BindOnce(&CloudPrintPrivateGetPrintersFunction::SendResults, this)); return RespondLater(); } diff --git a/chromium/chrome/browser/extensions/api/commands/command_service.h b/chromium/chrome/browser/extensions/api/commands/command_service.h index 378922ae5fe..22d82e817a1 100644 --- a/chromium/chrome/browser/extensions/api/commands/command_service.h +++ b/chromium/chrome/browser/extensions/api/commands/command_service.h @@ -265,7 +265,7 @@ class CommandService : public BrowserContextKeyedAPI, ScopedObserver<ExtensionRegistry, ExtensionRegistryObserver> extension_registry_observer_; - base::ObserverList<Observer> observers_; + base::ObserverList<Observer>::Unchecked observers_; DISALLOW_COPY_AND_ASSIGN(CommandService); }; 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 b361c7babdc..85bbb998db9 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 @@ -11,6 +11,8 @@ #include "base/bind.h" #include "base/command_line.h" +#include "base/feature_list.h" +#include "base/metrics/histogram_macros.h" #include "base/strings/stringprintf.h" #include "base/values.h" #include "chrome/browser/content_settings/cookie_settings_factory.h" @@ -24,6 +26,7 @@ #include "chrome/browser/plugins/plugin_finder.h" #include "chrome/browser/plugins/plugin_installer.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/api/content_settings.h" #include "components/content_settings/core/browser/content_settings_info.h" @@ -31,6 +34,7 @@ #include "components/content_settings/core/browser/content_settings_utils.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 "content/public/browser/browser_thread.h" #include "content/public/browser/plugin_service.h" #include "content/public/common/webplugininfo.h" @@ -214,6 +218,10 @@ ContentSettingsContentSettingSetFunction::Run() { ->Get(content_type) ->IsSettingValid(setting)); + const content_settings::ContentSettingsInfo* info = + content_settings::ContentSettingsRegistry::GetInstance()->Get( + content_type); + // Some content setting types support the full set of values listed in // content_settings.json only for exceptions. For the default setting, // some values might not be supported. @@ -221,9 +229,7 @@ ContentSettingsContentSettingSetFunction::Run() { // [ask, block] for the default setting. if (primary_pattern == ContentSettingsPattern::Wildcard() && secondary_pattern == ContentSettingsPattern::Wildcard() && - !content_settings::ContentSettingsRegistry::GetInstance() - ->Get(content_type) - ->IsDefaultSettingValid(setting)) { + !info->IsDefaultSettingValid(setting)) { static const char kUnsupportedDefaultSettingError[] = "'%s' is not supported as the default setting of %s."; @@ -243,6 +249,15 @@ ContentSettingsContentSettingSetFunction::Run() { readable_type_name.c_str()))); } + if (primary_pattern != secondary_pattern && + secondary_pattern != ContentSettingsPattern::Wildcard() && + !info->website_settings_info()->SupportsEmbeddedExceptions() && + base::FeatureList::IsEnabled(::features::kPermissionDelegation)) { + static const char kUnsupportedEmbeddedException[] = + "Embedded patterns are not supported for this setting."; + return RespondNow(Error(kUnsupportedEmbeddedException)); + } + ExtensionPrefsScope scope = kExtensionPrefsScopeRegular; bool incognito = false; if (params->details.scope == @@ -273,6 +288,18 @@ ContentSettingsContentSettingSetFunction::Run() { return RespondNow(Error(pref_keys::kIncognitoSessionOnlyErrorMessage)); } + size_t num_values = 0; + int histogram_value = + ContentSettingTypeToHistogramValue(content_type, &num_values); + if (primary_pattern != secondary_pattern && + secondary_pattern != ContentSettingsPattern::Wildcard()) { + UMA_HISTOGRAM_EXACT_LINEAR("ContentSettings.ExtensionEmbeddedSettingSet", + histogram_value, num_values); + } else { + UMA_HISTOGRAM_EXACT_LINEAR("ContentSettings.ExtensionNonEmbeddedSettingSet", + histogram_value, num_values); + } + scoped_refptr<ContentSettingsStore> store = ContentSettingsService::Get(browser_context())->content_settings_store(); store->SetExtensionContentSetting(extension_id(), primary_pattern, 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 2699c31961f..155603ec248 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 @@ -8,6 +8,8 @@ #include "base/location.h" #include "base/single_thread_task_runner.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "chrome/browser/chrome_notification_types.h" @@ -17,9 +19,11 @@ #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_features.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/keep_alive_registry/keep_alive_types.h" #include "components/keep_alive_registry/scoped_keep_alive.h" #include "components/prefs/pref_service.h" @@ -359,4 +363,50 @@ IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, << message_; } +IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, + EmbeddedSettingsMetric) { + base::HistogramTester histogram_tester; + const char kExtensionPath[] = "content_settings/embeddedsettingsmetric"; + EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; + + size_t num_values = 0; + int javascript_type = ContentSettingTypeToHistogramValue( + CONTENT_SETTINGS_TYPE_IMAGES, &num_values); + int geolocation_type = ContentSettingTypeToHistogramValue( + CONTENT_SETTINGS_TYPE_GEOLOCATION, &num_values); + int cookies_type = ContentSettingTypeToHistogramValue( + CONTENT_SETTINGS_TYPE_COOKIES, &num_values); + + histogram_tester.ExpectBucketCount( + "ContentSettings.ExtensionEmbeddedSettingSet", javascript_type, 1); + histogram_tester.ExpectBucketCount( + "ContentSettings.ExtensionEmbeddedSettingSet", geolocation_type, 1); + histogram_tester.ExpectTotalCount( + "ContentSettings.ExtensionEmbeddedSettingSet", 2); + + histogram_tester.ExpectBucketCount( + "ContentSettings.ExtensionNonEmbeddedSettingSet", javascript_type, 1); + histogram_tester.ExpectBucketCount( + "ContentSettings.ExtensionNonEmbeddedSettingSet", cookies_type, 1); + histogram_tester.ExpectTotalCount( + "ContentSettings.ExtensionNonEmbeddedSettingSet", 2); +} + +IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, EmbeddedSettings) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndDisableFeature(features::kPermissionDelegation); + const char kExtensionPath[] = "content_settings/embeddedsettings"; + EXPECT_TRUE(RunExtensionSubtest(kExtensionPath, "test.html")) << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionContentSettingsApiTest, + EmbeddedSettingsPermissionDelegation) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature(features::kPermissionDelegation); + const char kExtensionPath[] = "content_settings/embeddedsettings"; + EXPECT_TRUE( + RunExtensionSubtest(kExtensionPath, "test.html?permission_delegation")) + << message_; +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.cc b/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.cc index 83169fca6d7..272f5ee9eb2 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.cc +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.cc @@ -5,7 +5,6 @@ #include "chrome/browser/extensions/api/content_settings/content_settings_service.h" #include "base/lazy_instance.h" -#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_prefs_scope.h" #include "extensions/browser/pref_names.h" diff --git a/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.h b/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.h index 96fa0e8d102..0b4d70ca25f 100644 --- a/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.h +++ b/chromium/chrome/browser/extensions/api/content_settings/content_settings_service.h @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "chrome/browser/extensions/api/content_settings/content_settings_store.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_prefs_observer.h" 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 3a9033a4747..ea311c4b725 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 @@ -140,7 +140,7 @@ class ContentSettingsStore // The entries. ExtensionEntries entries_; - base::ObserverList<Observer, false> observers_; + base::ObserverList<Observer, false>::Unchecked observers_; mutable base::Lock lock_; diff --git a/chromium/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc b/chromium/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc index 4554ed65ee5..856b74eb55e 100644 --- a/chromium/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc +++ b/chromium/chrome/browser/extensions/api/context_menus/context_menu_apitest.cc @@ -273,11 +273,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuApiTest, VerifyMenuItem("parent", top_level_model_, top_level_index(), ui::MenuModel::TYPE_SUBMENU, true); - // Since the extension submenu is shown, the previous separator should be in - // the model. - EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, - top_level_model_->GetTypeAt(top_level_index() - 1)); - ui::MenuModel* submodel = top_level_model_->GetSubmenuModelAt(top_level_index()); ASSERT_TRUE(submodel); @@ -306,11 +301,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuApiTest, VerifyMenuItem("parent", top_level_model_, top_level_index(), ui::MenuModel::TYPE_SUBMENU, true); - // Since the extension submenu is shown, the previous separator should be in - // the model. - EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, - top_level_model_->GetTypeAt(top_level_index() - 1)); - ui::MenuModel* submodel = top_level_model_->GetSubmenuModelAt(top_level_index()); ASSERT_TRUE(submodel); @@ -339,11 +329,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuApiTest, VerifyMenuItem(extension()->name(), top_level_model_, top_level_index(), ui::MenuModel::TYPE_SUBMENU, true); - // Since the extension submenu is shown, the previous separator should be in - // the model. - EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, - top_level_model_->GetTypeAt(top_level_index() - 1)); - ui::MenuModel* submodel = top_level_model_->GetSubmenuModelAt(top_level_index()); ASSERT_TRUE(submodel); @@ -378,11 +363,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuApiTest, VerifyMenuItem(extension()->name(), top_level_model_, top_level_index(), ui::MenuModel::TYPE_SUBMENU, true); - // Since the extension submenu is shown, the previous separator should be in - // the model. - EXPECT_EQ(ui::MenuModel::TYPE_SEPARATOR, - top_level_model_->GetTypeAt(top_level_index() - 1)); - ui::MenuModel* submodel = top_level_model_->GetSubmenuModelAt(top_level_index()); ASSERT_TRUE(submodel); diff --git a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc index 44223d6702c..a36c6245c28 100644 --- a/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc +++ b/chromium/chrome/browser/extensions/api/cryptotoken_private/cryptotoken_private_api.cc @@ -32,7 +32,7 @@ constexpr const char* kGoogleGstaticAppIds[] = { // ContainsAppIdByHash returns true iff the SHA-256 hash of one of the // elements of |list| equals |hash|. bool ContainsAppIdByHash(const base::ListValue& list, - const std::vector<char>& hash) { + const std::vector<uint8_t>& hash) { if (hash.size() != crypto::kSHA256Length) { return false; } @@ -44,9 +44,10 @@ bool ContainsAppIdByHash(const base::ListValue& list, continue; } - if (crypto::SHA256HashString(s).compare(0, crypto::kSHA256Length, - hash.data(), - crypto::kSHA256Length) == 0) { + if (crypto::SHA256HashString(s).compare( + 0, crypto::kSHA256Length, + reinterpret_cast<const char*>(hash.data()), + crypto::kSHA256Length) == 0) { return true; } } diff --git a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc index c3ce2dda86d..70600589c31 100644 --- a/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc +++ b/chromium/chrome/browser/extensions/api/debugger/debugger_api.cc @@ -44,6 +44,7 @@ #include "content/public/browser/render_widget_host.h" #include "content/public/browser/web_contents.h" #include "content/public/common/content_client.h" +#include "content/public/common/url_constants.h" #include "content/public/common/url_utils.h" #include "extensions/browser/event_router.h" #include "extensions/browser/extension_host.h" @@ -90,6 +91,9 @@ bool ExtensionCanAttachToURL(const Extension& extension, const GURL& url, Profile* profile, std::string* error) { + if (url == content::kUnreachableWebDataURL) + return true; + // NOTE: The `debugger` permission implies all URLs access (and indicates // such to the user), so we don't check explicit page access. However, we // still need to check if it's an otherwise-restricted URL. @@ -361,9 +365,8 @@ bool ExtensionDevToolsClientHost::MayAttachToRenderer( const GURL& site_instance_url = render_frame_host->GetSiteInstance()->GetSiteURL(); - if (site_instance_url.is_empty()) { - // |site_instance_url| is empty for about:blank. Allow the extension to - // attach. + if (site_instance_url.is_empty() || site_instance_url == "about:") { + // Allow the extension to attach to about:blank. return true; } diff --git a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc index bf1db39d6bf..449b6e1bf25 100644 --- a/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative/declarative_apitest.cc @@ -17,6 +17,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/test/base/ui_test_utils.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" #include "extensions/browser/api/declarative/rules_registry_service.h" #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h" #include "extensions/browser/api/declarative_webrequest/webrequest_rules_registry.h" @@ -151,6 +152,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, PRE_PersistRules) { } IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, PersistRules) { + // Wait for declarative rules to be set up from PRE test. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); EXPECT_EQ(kTestTitle, GetTitle()); } @@ -180,6 +184,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, const Extension* extension = InstallExtensionWithUIAutoConfirm( ext_dir.Pack(), 1 /*+1 installed extension*/, browser()); ASSERT_TRUE(extension); + // Wait for declarative rules to be set up. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); std::string extension_id(extension->id()); ASSERT_TRUE(ready.WaitUntilSatisfied()); ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); @@ -254,6 +261,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, MAYBE_NoTracesAfterUninstalling) { const Extension* extension = InstallExtensionWithUIAutoConfirm( ext_dir.Pack(), 1 /*+1 installed extension*/, browser()); ASSERT_TRUE(extension); + // Wait for declarative rules to be set up. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); std::string extension_id(extension->id()); ASSERT_TRUE(ready.WaitUntilSatisfied()); ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); @@ -264,6 +274,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeApiTest, MAYBE_NoTracesAfterUninstalling) { // 2. Uninstall the extension. Rules are gone and preferences should be empty. UninstallExtension(extension_id); + // Wait for declarative rules to be removed. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); ui_test_utils::NavigateToURL(browser(), GURL(kArbitraryUrl)); EXPECT_NE(kTestTitle, GetTitle()); EXPECT_EQ(0u, NumberOfRegisteredRules(extension_id)); 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 df8b818e031..792970cdbab 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 @@ -20,6 +20,7 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "components/bookmarks/browser/bookmark_model.h" +#include "content/public/browser/storage_partition.h" #include "content/public/test/browser_test_utils.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" @@ -361,6 +362,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, ReusedActionInstance) { ExtensionTestMessageListener ready("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(); const ExtensionAction* page_action = ExtensionActionManager::Get(browser()->profile()) ->GetPageAction(*extension); @@ -605,6 +609,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, ext_dir_.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundHelpers); const Extension* extension = LoadExtension(ext_dir_.UnpackedPath()); ASSERT_TRUE(extension); + // Wait for declarative rules to be set up. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); const std::string extension_id = extension->id(); const ExtensionAction* page_action = ExtensionActionManager::Get( browser()->profile())->GetPageAction(*extension); @@ -631,6 +638,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab)); ReloadExtension(extension_id); // Invalidates page_action and extension. + // Wait for declarative rules to be removed. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); EXPECT_EQ("test_rule", ExecuteScriptInBackgroundPage(extension_id, kTestRule)); // TODO(jyasskin): Apply new rules to existing tabs, without waiting for a @@ -641,6 +651,9 @@ IN_PROC_BROWSER_TEST_F(DeclarativeContentApiTest, EXPECT_EQ(1u, extension_action_test_util::GetTotalPageActionCount(tab)); UnloadExtension(extension_id); + // Wait for declarative rules to be removed. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); NavigateInRenderer(tab, GURL("http://test/")); EXPECT_TRUE(WaitForPageActionVisibilityChangeTo(0)); EXPECT_EQ(0u, extension_action_test_util::GetVisiblePageActionCount(tab)); @@ -722,6 +735,9 @@ IN_PROC_BROWSER_TEST_P(ShowPageActionWithoutPageActionTest, Test) { scoped_refptr<const Extension> extension = loader.LoadExtension(ext_dir_.UnpackedPath()); ASSERT_TRUE(extension); + // Wait for declarative rules to be set up. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); const char kScript[] = "setRules([{\n" diff --git a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc index 72a79c3313d..b06f9309bf5 100644 --- a/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_content/set_icon_apitest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "components/version_info/version_info.h" +#include "content/public/browser/storage_partition.h" #include "extensions/common/features/feature_channel.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/test_extension_dir.h" @@ -74,6 +75,9 @@ IN_PROC_BROWSER_TEST_F(SetIconAPITest, Overview) { ExtensionTestMessageListener ready("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(); const ExtensionAction* page_action = ExtensionActionManager::Get(browser()->profile())-> GetPageAction(*extension); 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 43c0ea8cd18..5f086a51ea0 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 @@ -19,6 +19,7 @@ #include "base/path_service.h" #include "base/rand_util.h" #include "base/run_loop.h" +#include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" #include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_restrictions.h" @@ -52,6 +53,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/web_request/web_request_info.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" @@ -338,18 +340,10 @@ class DeclarativeNetRequestBrowserTest } ASSERT_TRUE(extension); - EXPECT_TRUE(HasValidIndexedRuleset(*extension, profile())); // Ensure the ruleset is also loaded on the IO thread. content::RunAllTasksUntilIdle(); - // Wait for the background page to load if needed. - if (has_background_script_) - WaitForBackgroundScriptToLoad(extension->id()); - - // Ensure no load errors were reported. - EXPECT_TRUE(LoadErrorReporter::GetInstance()->GetErrors()->empty()); - tester.ExpectTotalCount(kIndexRulesTimeHistogram, 1); tester.ExpectTotalCount(kIndexAndPersistRulesTimeHistogram, 1); tester.ExpectUniqueSample(kManifestRulesCountHistogram, @@ -359,6 +353,15 @@ class DeclarativeNetRequestBrowserTest tester.ExpectUniqueSample( "Extensions.DeclarativeNetRequest.LoadRulesetResult", RulesetMatcher::kLoadSuccess /*sample*/, 1 /*count*/); + + EXPECT_TRUE(HasValidIndexedRuleset(*extension, profile())); + + // Wait for the background page to load if needed. + if (has_background_script_) + WaitForBackgroundScriptToLoad(extension->id()); + + // Ensure no load errors were reported. + EXPECT_TRUE(LoadErrorReporter::GetInstance()->GetErrors()->empty()); } void LoadExtensionWithRules(const std::vector<TestRule>& rules) { @@ -480,7 +483,10 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, {"|http://*.us", 3}, {"pages_with_script/page2.html|", 4}, {"|http://msn*/pages_with_script/page.html|", 5}, - {"%20", 6}, // Block any urls with space. + {"%20", 6}, // Block any urls with space. + {"%C3%A9", 7}, // Percent-encoded non-ascii character Ă©. + // Internationalized domain "â±´ase.com" in punycode. + {"|http://xn--ase-7z0b.com", 8}, }; // Rule |i| is the rule with id |i|. @@ -504,6 +510,12 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, {"abc.com", "/pages_with_script/page.html?q=hi bye", false}, // Rule 6 {"abc.com", "/pages_with_script/page.html?q=hi%20bye", false}, // Rule 6 {"abc.com", "/pages_with_script/page.html?q=hibye", true}, + {"abc.com", + "/pages_with_script/page.html?q=" + base::WideToUTF8(L"\u00E9"), + false}, // Rule 7 + {base::WideToUTF8(L"\x2c74" + L"ase.com"), + "/pages_with_script/page.html", false}, // Rule 8 }; // Load the extension. @@ -653,7 +665,10 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, size_t id; std::vector<std::string> domains; std::vector<std::string> excluded_domains; - } rules_data[] = {{"child_frame.html?frame=1", 1, {"x.com"}, {"a.x.com"}}, + } rules_data[] = {{"child_frame.html?frame=1", + 1, + {"x.com", "xn--36c-tfa.com" /* punycode for 36°c.com */}, + {"a.x.com"}}, {"child_frame.html?frame=2", 2, {}, {"a.y.com"}}}; std::vector<TestRule> rules; @@ -679,6 +694,9 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, bool expect_frame_2_loaded; } test_cases[] = { {"x.com", false /* Rule 1 */, false /* Rule 2 */}, + {base::WideToUTF8(L"36\x00b0" + L"c.com" /* 36°c.com */), + false /*Rule 1*/, false /*Rule 2*/}, {"b.x.com", false /* Rule 1 */, false /* Rule 2 */}, {"a.x.com", true, false /* Rule 2 */}, {"b.a.x.com", true, false /* Rule 2 */}, @@ -1307,7 +1325,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // affecting requests. PrefService* pref_service = browser()->profile()->GetPrefs(); pref_service->Set(proxy_config::prefs::kProxy, - *ProxyConfigDictionary::CreatePacScript( + ProxyConfigDictionary::CreatePacScript( embedded_test_server()->GetURL("/self.pac").spec(), true /* pac_mandatory */)); // Flush the proxy configuration change over the Mojo pipe to avoid any races. @@ -1632,15 +1650,6 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, request->resource_type = content::ResourceType::RESOURCE_TYPE_SCRIPT; request->render_frame_id = MSG_ROUTING_NONE; - // TODO(https://crbug.com/857577): remove this hack. When an unrelated - // browser issued request (typically from GaiaAuthFetcher) has run, it causes - // the StoragePartitionImpl to create and cache a URLLoaderFactory without the - // web request proxying. This resets it so one with the web request proxying - // is created the next time a request is made. - base::RunLoop().RunUntilIdle(); - content::BrowserContext::GetDefaultStoragePartition(profile()) - ->ResetURLLoaderFactoryForBrowserProcessForTesting(); - auto loader = network::SimpleURLLoader::Create(std::move(request), TRAFFIC_ANNOTATION_FOR_TESTS); content::SimpleURLLoaderTestHelper loader_helper; @@ -1816,10 +1825,11 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, verify_page_load(false); } - // Overwrite the indexed ruleset file with arbitrary data to mimic corruption. + // Overwrite the indexed ruleset file with arbitrary data to mimic corruption, + // while maintaining the correct version header. { base::ScopedAllowBlockingForTesting scoped_allow_blocking; - std::string corrupted_data = "data"; + std::string corrupted_data = GetVersionHeaderForTesting() + "data"; ASSERT_EQ(static_cast<int>(corrupted_data.size()), base::WriteFile(file_util::GetIndexedRulesetPath(extension_path), corrupted_data.c_str(), corrupted_data.size())); @@ -1844,7 +1854,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, EXPECT_EQ(1, tester.GetBucketCount( "Extensions.DeclarativeNetRequest.LoadRulesetResult", RulesetMatcher::LoadRulesetResult:: - kLoadErrorRulesetVerification /*sample*/)); + kLoadErrorChecksumMismatch /*sample*/)); EXPECT_EQ(1, tester.GetBucketCount( "Extensions.DeclarativeNetRequest.LoadRulesetResult", @@ -1877,7 +1887,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, // Mimic extension prefs corruption by overwriting the indexed ruleset // checksum. const int kInvalidRulesetChecksum = -1; - ExtensionPrefs::Get(profile())->SetDNRRulesetChecksumForTesting( + ExtensionPrefs::Get(profile())->SetDNRRulesetChecksum( extension_id, kInvalidRulesetChecksum); TestExtensionRegistryObserver registry_observer( @@ -1904,7 +1914,7 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, EXPECT_EQ(1, tester.GetBucketCount( "Extensions.DeclarativeNetRequest.LoadRulesetResult", RulesetMatcher::LoadRulesetResult:: - kLoadErrorRulesetVerification /*sample*/)); + kLoadErrorChecksumMismatch /*sample*/)); // Verify that re-indexing the ruleset failed. tester.ExpectUniqueSample( @@ -1912,6 +1922,60 @@ IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest, false /*sample*/, 1 /*count*/); } +// Tests that we reindex the extension ruleset in case its ruleset format +// version is not the same as one used by Chrome. +IN_PROC_BROWSER_TEST_P(DeclarativeNetRequestBrowserTest_Packed, + ReindexOnRulesetVersionMismatch) { + // Set up an observer for RulesetMatcher to monitor the number of extension + // rulesets. + RulesetCountWaiter ruleset_count_waiter; + ScopedRulesetManagerTestObserver scoped_observer( + &ruleset_count_waiter, + base::WrapRefCounted(ExtensionSystem::Get(profile())->info_map())); + + TestRule rule = CreateGenericRule(); + rule.condition->url_filter = std::string("*"); + ASSERT_NO_FATAL_FAILURE(LoadExtensionWithRules({rule})); + ruleset_count_waiter.WaitForRulesetCount(1); + + const ExtensionId extension_id = last_loaded_extension_id(); + const auto* rules_monitor_service = BrowserContextKeyedAPIFactory< + declarative_net_request::RulesMonitorService>::Get(profile()); + EXPECT_TRUE(rules_monitor_service->HasRegisteredRuleset(extension_id)); + + DisableExtension(extension_id); + ruleset_count_waiter.WaitForRulesetCount(0); + EXPECT_FALSE(rules_monitor_service->HasRegisteredRuleset(extension_id)); + + // Now change the current indexed ruleset format version. This should cause a + // version mismatch when the extension is loaded again, but reindexing should + // still succeed. + const int kIndexedRulesetFormatVersion = 100; + std::string old_version_header = GetVersionHeaderForTesting(); + SetIndexedRulesetFormatVersionForTesting(kIndexedRulesetFormatVersion); + ASSERT_NE(old_version_header, GetVersionHeaderForTesting()); + + base::HistogramTester tester; + EnableExtension(extension_id); + ruleset_count_waiter.WaitForRulesetCount(1); + EXPECT_TRUE(rules_monitor_service->HasRegisteredRuleset(extension_id)); + + // Verify that loading the ruleset would have failed initially due to + // version header mismatch and later succeeded. + EXPECT_EQ(1, tester.GetBucketCount( + "Extensions.DeclarativeNetRequest.LoadRulesetResult", + RulesetMatcher::LoadRulesetResult:: + kLoadErrorVersionMismatch /*sample*/)); + EXPECT_EQ(1, tester.GetBucketCount( + "Extensions.DeclarativeNetRequest.LoadRulesetResult", + RulesetMatcher::LoadRulesetResult::kLoadSuccess /*sample*/)); + + // Verify that reindexing succeeded. + tester.ExpectUniqueSample( + "Extensions.DeclarativeNetRequest.RulesetReindexSuccessful", + true /*sample*/, 1 /*count*/); +} + // Test fixture to verify that host permissions for the request url and the // request initiator are properly checked. Loads an example.com url with four // sub-frames named frame_[1..4] from hosts frame_[1..4].com. The initiator for diff --git a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc index 90209860ec6..2948dde85c1 100644 --- a/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc +++ b/chromium/chrome/browser/extensions/api/declarative_net_request/ruleset_matcher_unittest.cc @@ -14,6 +14,7 @@ #include "chrome/browser/profiles/profile.h" #include "components/url_pattern_index/flat/url_pattern_index_generated.h" #include "extensions/browser/api/declarative_net_request/test_utils.h" +#include "extensions/browser/api/declarative_net_request/utils.h" #include "extensions/browser/extension_prefs.h" #include "extensions/common/api/declarative_net_request/constants.h" #include "extensions/common/api/declarative_net_request/test_utils.h" @@ -123,7 +124,8 @@ TEST_P(RulesetMatcherTest, FailedVerification) { base::FilePath indexed_ruleset_path = file_util::GetIndexedRulesetPath(extension()->path()); - // Persist invalid data to the ruleset file. + // Persist invalid data to the ruleset file and ensure that a version mismatch + // occurs. std::string data = "invalid data"; ASSERT_EQ(static_cast<int>(data.size()), base::WriteFile(indexed_ruleset_path, data.c_str(), data.size())); @@ -134,7 +136,17 @@ TEST_P(RulesetMatcherTest, FailedVerification) { ->GetDNRRulesetChecksum(extension()->id(), &expected_checksum)); std::unique_ptr<RulesetMatcher> matcher; - EXPECT_EQ(RulesetMatcher::kLoadErrorRulesetVerification, + EXPECT_EQ(RulesetMatcher::kLoadErrorVersionMismatch, + RulesetMatcher::CreateVerifiedMatcher(indexed_ruleset_path, + expected_checksum, &matcher)); + + // Now, persist invalid data to the ruleset file, while maintaining the + // correct version header. Ensure that it fails verification due to checksum + // mismatch. + data = GetVersionHeaderForTesting() + "invalid data"; + ASSERT_EQ(static_cast<int>(data.size()), + base::WriteFile(indexed_ruleset_path, data.c_str(), data.size())); + EXPECT_EQ(RulesetMatcher::kLoadErrorChecksumMismatch, RulesetMatcher::CreateVerifiedMatcher(indexed_ruleset_path, expected_checksum, &matcher)); } diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/OWNERS b/chromium/chrome/browser/extensions/api/desktop_capture/OWNERS index c43cfad6ede..62e34f14d33 100644 --- a/chromium/chrome/browser/extensions/api/desktop_capture/OWNERS +++ b/chromium/chrome/browser/extensions/api/desktop_capture/OWNERS @@ -1,7 +1,7 @@ -# Please send the changes to zijiehe@chromium.org first. +# Please send the changes to braveyao@chromium.org first. sergeyu@chromium.org wez@chromium.org -zijiehe@chromium.org +braveyao@chromium.org # TEAM: media-capture-and-streams@grotations.appspotmail.com # COMPONENT: Blink>GetUserMedia>Desktop diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc index 66e3efd2644..4345409664f 100644 --- a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc +++ b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_apitest.cc @@ -8,11 +8,10 @@ #include "base/macros.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" -#include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/desktop_capture/desktop_capture_api.h" #include "chrome/browser/extensions/extension_apitest.h" -#include "chrome/browser/media/webrtc/fake_desktop_media_list.h" +#include "chrome/browser/media/webrtc/fake_desktop_media_picker_factory.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -30,20 +29,6 @@ namespace extensions { namespace { -struct TestFlags { - bool expect_screens; - bool expect_windows; - bool expect_tabs; - bool expect_audio; - DesktopMediaID selected_source; - bool cancelled; - - // Following flags are set by FakeDesktopMediaPicker when it's created and - // deleted. - bool picker_created; - bool picker_deleted; -}; - // TODO(crbug.com/805145): Uncomment this when test is re-enabled. #if 0 DesktopMediaID MakeFakeWebContentsMediaId(bool audio_share) { @@ -56,104 +41,6 @@ DesktopMediaID MakeFakeWebContentsMediaId(bool audio_share) { } #endif -class FakeDesktopMediaPicker : public DesktopMediaPicker { - public: - explicit FakeDesktopMediaPicker(TestFlags* expectation) - : expectation_(expectation), - weak_factory_(this) { - expectation_->picker_created = true; - } - ~FakeDesktopMediaPicker() override { expectation_->picker_deleted = true; } - - // DesktopMediaPicker interface. - void Show(const DesktopMediaPicker::Params& params, - std::vector<std::unique_ptr<DesktopMediaList>> source_lists, - const DoneCallback& done_callback) override { - bool show_screens = false; - bool show_windows = false; - bool show_tabs = false; - - for (auto& source_list : source_lists) { - switch (source_list->GetMediaListType()) { - case DesktopMediaID::TYPE_NONE: - break; - case DesktopMediaID::TYPE_SCREEN: - show_screens = true; - break; - case DesktopMediaID::TYPE_WINDOW: - show_windows = true; - break; - case DesktopMediaID::TYPE_WEB_CONTENTS: - show_tabs = true; - break; - } - } - EXPECT_EQ(expectation_->expect_screens, show_screens); - EXPECT_EQ(expectation_->expect_windows, show_windows); - EXPECT_EQ(expectation_->expect_tabs, show_tabs); - EXPECT_EQ(expectation_->expect_audio, params.request_audio); - EXPECT_EQ(params.modality, ui::ModalType::MODAL_TYPE_CHILD); - - if (!expectation_->cancelled) { - // Post a task to call the callback asynchronously. - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, base::BindOnce(&FakeDesktopMediaPicker::CallCallback, - weak_factory_.GetWeakPtr(), done_callback)); - } else { - // If we expect the dialog to be cancelled then store the callback to - // retain reference to the callback handler. - done_callback_ = done_callback; - } - } - - private: - void CallCallback(DoneCallback done_callback) { - done_callback.Run(expectation_->selected_source); - } - - TestFlags* expectation_; - DoneCallback done_callback_; - - base::WeakPtrFactory<FakeDesktopMediaPicker> weak_factory_; - - DISALLOW_COPY_AND_ASSIGN(FakeDesktopMediaPicker); -}; - -class FakeDesktopMediaPickerFactory : - public DesktopCaptureChooseDesktopMediaFunction::PickerFactory { - public: - FakeDesktopMediaPickerFactory() {} - ~FakeDesktopMediaPickerFactory() override {} - - void SetTestFlags(TestFlags* test_flags, int tests_count) { - test_flags_ = test_flags; - tests_count_ = tests_count; - current_test_ = 0; - } - - std::unique_ptr<DesktopMediaPicker> CreatePicker() override { - EXPECT_LE(current_test_, tests_count_); - if (current_test_ >= tests_count_) - return std::unique_ptr<DesktopMediaPicker>(); - ++current_test_; - return std::unique_ptr<DesktopMediaPicker>( - new FakeDesktopMediaPicker(test_flags_ + current_test_ - 1)); - } - - std::unique_ptr<DesktopMediaList> CreateMediaList( - DesktopMediaID::Type type) override { - EXPECT_LE(current_test_, tests_count_); - return std::unique_ptr<DesktopMediaList>(new FakeDesktopMediaList(type)); - } - - private: - TestFlags* test_flags_; - int tests_count_; - int current_test_; - - DISALLOW_COPY_AND_ASSIGN(FakeDesktopMediaPickerFactory); -}; - class DesktopCaptureApiTest : public ExtensionApiTest { public: DesktopCaptureApiTest() { @@ -195,7 +82,7 @@ class DesktopCaptureApiTest : public ExtensionApiTest { IN_PROC_BROWSER_TEST_F(DesktopCaptureApiTest, MAYBE_ChooseDesktopMedia) { // Each element in the following array corresponds to one test in // chrome/test/data/extensions/api_test/desktop_capture/test.js . - TestFlags test_flags[] = { + FakeDesktopMediaPickerFactory::TestFlags test_flags[] = { // pickerUiCanceled() {true, true, false, false, DesktopMediaID()}, // chooseMedia() @@ -269,7 +156,7 @@ IN_PROC_BROWSER_TEST_F(DesktopCaptureApiTest, DISABLED_Delegation) { ui_test_utils::NavigateToURL( browser(), GetURLForPath("example.com", "/example.com.html")); - TestFlags test_flags[] = { + FakeDesktopMediaPickerFactory::TestFlags test_flags[] = { {true, true, false, false, DesktopMediaID(DesktopMediaID::TYPE_SCREEN, DesktopMediaID::kNullId)}, {true, true, false, false, diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc index 4e8b6cc9ffa..29040b29d65 100644 --- a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc +++ b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.cc @@ -13,7 +13,7 @@ #include "build/build_config.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/media/webrtc/desktop_media_list_ash.h" -#include "chrome/browser/media/webrtc/desktop_streams_registry.h" +#include "chrome/browser/media/webrtc/desktop_media_picker_factory_impl.h" #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/browser/media/webrtc/native_desktop_media_list.h" #include "chrome/browser/media/webrtc/tab_desktop_media_list.h" @@ -21,6 +21,7 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/grit/chromium_strings.h" #include "content/public/browser/desktop_capture.h" +#include "content/public/browser/desktop_streams_registry.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" @@ -39,20 +40,18 @@ const char kInvalidSourceNameError[] = "Invalid source type specified."; const char kEmptySourcesListError[] = "At least one source type must be specified."; -DesktopCaptureChooseDesktopMediaFunctionBase::PickerFactory* g_picker_factory = - NULL; +DesktopMediaPickerFactory* g_picker_factory = nullptr; } // namespace // static void DesktopCaptureChooseDesktopMediaFunctionBase::SetPickerFactoryForTests( - PickerFactory* factory) { + DesktopMediaPickerFactory* factory) { g_picker_factory = factory; } DesktopCaptureChooseDesktopMediaFunctionBase:: - DesktopCaptureChooseDesktopMediaFunctionBase() { -} + DesktopCaptureChooseDesktopMediaFunctionBase() = default; DesktopCaptureChooseDesktopMediaFunctionBase:: ~DesktopCaptureChooseDesktopMediaFunctionBase() { @@ -94,12 +93,8 @@ bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute( parent_window = target_browser->window()->GetNativeWindow(); } - // Keep same order as the input |sources| and avoid duplicates. - std::vector<std::unique_ptr<DesktopMediaList>> source_lists; - bool have_screen_list = false; - bool have_window_list = false; - bool have_tab_list = false; bool request_audio = false; + std::vector<content::DesktopMediaID::Type> media_types; for (auto source_type : sources) { switch (source_type) { case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_NONE: { @@ -107,98 +102,45 @@ bool DesktopCaptureChooseDesktopMediaFunctionBase::Execute( return false; } case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_SCREEN: { - if (have_screen_list) { - continue; - } - std::unique_ptr<DesktopMediaList> screen_list; - if (g_picker_factory) { - screen_list = - g_picker_factory->CreateMediaList(DesktopMediaID::TYPE_SCREEN); - } else { -#if defined(OS_CHROMEOS) - screen_list = std::make_unique<DesktopMediaListAsh>( - DesktopMediaID::TYPE_SCREEN); -#else // !defined(OS_CHROMEOS) - screen_list = std::make_unique<NativeDesktopMediaList>( - content::DesktopMediaID::TYPE_SCREEN, - content::desktop_capture::CreateScreenCapturer()); -#endif // !defined(OS_CHROMEOS) - } - have_screen_list = true; - source_lists.push_back(std::move(screen_list)); + media_types.push_back(content::DesktopMediaID::TYPE_SCREEN); break; } case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_WINDOW: { - if (have_window_list) { - continue; - } - std::unique_ptr<DesktopMediaList> window_list; - if (g_picker_factory) { - window_list = - g_picker_factory->CreateMediaList(DesktopMediaID::TYPE_WINDOW); - } else { -#if defined(OS_CHROMEOS) - window_list = std::make_unique<DesktopMediaListAsh>( - DesktopMediaID::TYPE_WINDOW); -#else // !defined(OS_CHROMEOS) - // NativeDesktopMediaList calls the capturers on a background thread. - // This means that the two DesktopCapturer instances (for screens and - // windows) created here cannot share the same DesktopCaptureOptions - // instance. DesktopCaptureOptions owns X connection, which cannot be - // used on multiple threads concurrently. - window_list = std::make_unique<NativeDesktopMediaList>( - content::DesktopMediaID::TYPE_WINDOW, - content::desktop_capture::CreateWindowCapturer()); -#endif // !defined(OS_CHROMEOS) - } - have_window_list = true; - source_lists.push_back(std::move(window_list)); + media_types.push_back(content::DesktopMediaID::TYPE_WINDOW); break; } case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_TAB: { if (base::CommandLine::ForCurrentProcess()->HasSwitch( - extensions::switches::kDisableTabForDesktopShare) || - have_tab_list) { + extensions::switches::kDisableTabForDesktopShare)) { continue; } - std::unique_ptr<DesktopMediaList> tab_list; - if (g_picker_factory) { - tab_list = g_picker_factory->CreateMediaList( - DesktopMediaID::TYPE_WEB_CONTENTS); - } else { - tab_list = std::make_unique<TabDesktopMediaList>(); - } - have_tab_list = true; - source_lists.push_back(std::move(tab_list)); + media_types.push_back(content::DesktopMediaID::TYPE_WEB_CONTENTS); break; } case api::desktop_capture::DESKTOP_CAPTURE_SOURCE_TYPE_AUDIO: { - bool audio_capture_disabled = - base::CommandLine::ForCurrentProcess()->HasSwitch( - extensions::switches::kDisableDesktopCaptureAudio); - if (!audio_capture_disabled) { + if (!base::CommandLine::ForCurrentProcess()->HasSwitch( + extensions::switches::kDisableDesktopCaptureAudio)) { request_audio = true; } break; } } } + + DesktopMediaPickerFactory* picker_factory = + g_picker_factory ? g_picker_factory + : DesktopMediaPickerFactoryImpl::GetInstance(); + // Keep same order as the input |sources| and avoid duplicates. + std::vector<std::unique_ptr<DesktopMediaList>> source_lists = + picker_factory->CreateMediaList(media_types); if (source_lists.empty()) { error_ = kEmptySourcesListError; return false; } - - if (g_picker_factory) { - picker_ = g_picker_factory->CreatePicker(); - } else { - // DesktopMediaPicker is implemented only for Windows, OSX and - // Aura Linux builds. -#if defined(TOOLKIT_VIEWS) || defined(OS_MACOSX) - picker_ = DesktopMediaPicker::Create(); -#else + picker_ = picker_factory->CreatePicker(); + if (!picker_) { error_ = "Desktop Capture API is not yet implemented for this platform."; return false; -#endif } DesktopMediaPicker::DoneCallback callback = base::Bind( @@ -234,20 +176,15 @@ void DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults( DesktopMediaID source) { std::string result; if (source.type != DesktopMediaID::TYPE_NONE && web_contents()) { - DesktopStreamsRegistry* registry = - MediaCaptureDevicesDispatcher::GetInstance()-> - GetDesktopStreamsRegistry(); // TODO(miu): Once render_frame_host() is being set, we should register the // exact RenderFrame requesting the stream, not the main RenderFrame. With // that change, also update // MediaCaptureDevicesDispatcher::ProcessDesktopCaptureAccessRequest(). // http://crbug.com/304341 content::RenderFrameHost* const main_frame = web_contents()->GetMainFrame(); - result = registry->RegisterStream(main_frame->GetProcess()->GetID(), - main_frame->GetRoutingID(), - origin_, - source, - extension()->name()); + result = content::DesktopStreamsRegistry::GetInstance()->RegisterStream( + main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(), origin_, + source, extension()->name(), content::kRegistryStreamTypeDesktop); } Options options; @@ -258,9 +195,7 @@ void DesktopCaptureChooseDesktopMediaFunctionBase::OnPickerDialogResults( DesktopCaptureRequestsRegistry::RequestId::RequestId(int process_id, int request_id) - : process_id(process_id), - request_id(request_id) { -} + : process_id(process_id), request_id(request_id) {} bool DesktopCaptureRequestsRegistry::RequestId::operator<( const RequestId& other) const { @@ -312,5 +247,4 @@ void DesktopCaptureRequestsRegistry::CancelRequest(int process_id, it->second->Cancel(); } - } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h index a56432871db..595a331a6a2 100644 --- a/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h +++ b/chromium/chrome/browser/extensions/api/desktop_capture/desktop_capture_base.h @@ -14,6 +14,7 @@ #include "chrome/browser/extensions/chrome_extension_function.h" #include "chrome/browser/media/webrtc/desktop_media_list.h" #include "chrome/browser/media/webrtc/desktop_media_picker.h" +#include "chrome/browser/media/webrtc/desktop_media_picker_factory.h" #include "chrome/common/extensions/api/desktop_capture.h" #include "content/public/browser/web_contents_observer.h" #include "url/gurl.h" @@ -24,26 +25,10 @@ class DesktopCaptureChooseDesktopMediaFunctionBase : public ChromeAsyncExtensionFunction, public content::WebContentsObserver { public: - // Factory creating DesktopMediaList and DesktopMediaPicker instances. - // Used for tests to supply fake picker. - class PickerFactory { - public: - virtual std::unique_ptr<DesktopMediaPicker> CreatePicker() = 0; - virtual std::unique_ptr<DesktopMediaList> CreateMediaList( - content::DesktopMediaID::Type type) = 0; - - protected: - PickerFactory() = default; - virtual ~PickerFactory() {} - - private: - DISALLOW_COPY_AND_ASSIGN(PickerFactory); - }; - // Used to set PickerFactory used to create mock DesktopMediaPicker instances // for tests. Calling tests keep ownership of the factory. Can be called with // |factory| set to NULL at the end of the test. - static void SetPickerFactoryForTests(PickerFactory* factory); + static void SetPickerFactoryForTests(DesktopMediaPickerFactory* factory); DesktopCaptureChooseDesktopMediaFunctionBase(); @@ -77,6 +62,7 @@ class DesktopCaptureChooseDesktopMediaFunctionBase // URL of page that desktop capture was requested for. GURL origin_; + std::unique_ptr<DesktopMediaPickerFactory> picker_factory_; std::unique_ptr<DesktopMediaPicker> picker_; }; diff --git a/chromium/chrome/browser/extensions/api/developer_private/DEPS b/chromium/chrome/browser/extensions/api/developer_private/DEPS index 5ad83b64961..5b9436cd654 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/DEPS +++ b/chromium/chrome/browser/extensions/api/developer_private/DEPS @@ -1,4 +1,8 @@ specific_include_rules = { + "show_permissions_dialog_helper.cc": [ + #TODO(https://crbug.com/873872): Remove this. + "+chrome/browser/apps/platform_apps/app_load_service.h", + ], "developer_private_api_unittest.cc": [ # Allow the unittest to create a data_decoder service. "+services/data_decoder" 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 27e3b46092f..52d4edb8c3e 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 @@ -18,7 +18,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/devtools/devtools_window.h" #include "chrome/browser/extensions/api/developer_private/developer_private_mangle.h" #include "chrome/browser/extensions/api/developer_private/entry_picker.h" @@ -27,12 +27,14 @@ #include "chrome/browser/extensions/chrome_zipfile_installer.h" #include "chrome/browser/extensions/crx_installer.h" #include "chrome/browser/extensions/devtools_util.h" +#include "chrome/browser/extensions/error_console/error_console_factory.h" #include "chrome/browser/extensions/extension_commands_global_registry.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_tab_util.h" #include "chrome/browser/extensions/extension_ui_util.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/install_verifier.h" +#include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/extensions/shared_module_service.h" #include "chrome/browser/extensions/unpacked_installer.h" @@ -52,7 +54,9 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_source.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/site_instance.h" @@ -66,15 +70,20 @@ #include "extensions/browser/content_verifier.h" #include "extensions/browser/disable_reason.h" #include "extensions/browser/error_map.h" +#include "extensions/browser/event_router_factory.h" #include "extensions/browser/extension_error.h" #include "extensions/browser/extension_prefs.h" +#include "extensions/browser/extension_prefs_factory.h" #include "extensions/browser/extension_registry.h" +#include "extensions/browser/extension_registry_factory.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/file_highlighter.h" #include "extensions/browser/management_policy.h" #include "extensions/browser/notification_types.h" #include "extensions/browser/path_util.h" +#include "extensions/browser/process_manager_factory.h" #include "extensions/browser/warning_service.h" +#include "extensions/browser/warning_service_factory.h" #include "extensions/browser/zipfile_installer.h" #include "extensions/common/extension_set.h" #include "extensions/common/feature_switch.h" @@ -259,6 +268,25 @@ developer::LoadError CreateLoadError( return response; } +base::Optional<URLPattern> ParseRuntimePermissionsPattern( + const std::string& pattern_str) { + constexpr int kValidRuntimePermissionSchemes = URLPattern::SCHEME_HTTP | + URLPattern::SCHEME_HTTPS | + URLPattern::SCHEME_FILE; + + URLPattern pattern(kValidRuntimePermissionSchemes); + if (pattern.Parse(pattern_str) != URLPattern::PARSE_SUCCESS) + return base::nullopt; + + // We don't allow adding paths for permissions, because they aren't meaningful + // in terms of origin access. The frontend should validate this, but there's + // a chance something can slip through, so we should fail gracefully. + if (pattern.path() != "/*") + return base::nullopt; + + return pattern; +} + } // namespace namespace ChoosePath = api::developer_private::ChoosePath; @@ -302,6 +330,20 @@ DeveloperPrivateAPI::GetFactoryInstance() { return g_developer_private_api_factory.Pointer(); } +template <> +void BrowserContextKeyedAPIFactory< + DeveloperPrivateAPI>::DeclareFactoryDependencies() { + DependsOn(ExtensionRegistryFactory::GetInstance()); + DependsOn(ErrorConsoleFactory::GetInstance()); + DependsOn(ProcessManagerFactory::GetInstance()); + DependsOn(AppWindowRegistry::Factory::GetInstance()); + DependsOn(WarningServiceFactory::GetInstance()); + DependsOn(ExtensionPrefsFactory::GetInstance()); + DependsOn(ExtensionManagementFactory::GetInstance()); + DependsOn(CommandService::GetFactoryInstance()); + DependsOn(EventRouterFactory::GetInstance()); +} + // static DeveloperPrivateAPI* DeveloperPrivateAPI::Get( content::BrowserContext* context) { @@ -341,6 +383,9 @@ DeveloperPrivateEventRouter::DeveloperPrivateEventRouter(Profile* profile) prefs::kExtensionsUIDeveloperMode, base::Bind(&DeveloperPrivateEventRouter::OnProfilePrefChanged, base::Unretained(this))); + notification_registrar_.Add( + this, extensions::NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED, + content::Source<Profile>(profile_)); } DeveloperPrivateEventRouter::~DeveloperPrivateEventRouter() { @@ -448,6 +493,12 @@ void DeveloperPrivateEventRouter::OnExtensionDisableReasonsChanged( BroadcastItemStateChanged(developer::EVENT_TYPE_PREFS_CHANGED, extension_id); } +void DeveloperPrivateEventRouter::OnExtensionRuntimePermissionsChanged( + const std::string& extension_id) { + BroadcastItemStateChanged(developer::EVENT_TYPE_PERMISSIONS_CHANGED, + extension_id); +} + void DeveloperPrivateEventRouter::OnExtensionManagementSettingsChanged() { std::unique_ptr<base::ListValue> args(new base::ListValue()); args->Append(CreateProfileInfo(profile_)->ToValue()); @@ -463,6 +514,18 @@ void DeveloperPrivateEventRouter::ExtensionWarningsChanged( BroadcastItemStateChanged(developer::EVENT_TYPE_WARNINGS_CHANGED, id); } +void DeveloperPrivateEventRouter::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + DCHECK_EQ(NOTIFICATION_EXTENSION_PERMISSIONS_UPDATED, type); + + UpdatedExtensionPermissionsInfo* info = + content::Details<UpdatedExtensionPermissionsInfo>(details).ptr(); + BroadcastItemStateChanged(developer::EVENT_TYPE_PERMISSIONS_CHANGED, + info->extension->id()); +} + void DeveloperPrivateEventRouter::OnProfilePrefChanged() { std::unique_ptr<base::ListValue> args(new base::ListValue()); args->Append(CreateProfileInfo(profile_)->ToValue()); @@ -1932,30 +1995,27 @@ DeveloperPrivateAddHostPermissionFunction::Run() { developer::AddHostPermission::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - GURL host(params->host); - if (!host.is_valid() || host.path_piece().length() > 1 || host.has_query() || - host.has_ref()) { + base::Optional<URLPattern> pattern = + ParseRuntimePermissionsPattern(params->host); + if (!pattern) return RespondNow(Error(kInvalidHost)); - } const Extension* extension = GetExtensionById(params->extension_id); if (!extension) return RespondNow(Error(kNoSuchExtensionError)); - ScriptingPermissionsModifier scripting_modifier(browser_context(), extension); - if (!scripting_modifier.CanAffectExtension()) + if (!ScriptingPermissionsModifier(browser_context(), extension) + .CanAffectExtension()) { return RespondNow(Error(kCannotChangeHostPermissions)); - - // Only grant withheld permissions. This also ensures that we won't grant - // any permission for a host that shouldn't be accessible to the extension, - // like chrome:-scheme urls. - if (!extension->permissions_data() - ->withheld_permissions() - .HasEffectiveAccessToURL(host)) { - return RespondNow(Error("Cannot grant a permission that wasn't withheld.")); } - scripting_modifier.GrantHostPermission(host); + URLPatternSet new_host_permissions({*pattern}); + PermissionsUpdater(browser_context()) + .GrantRuntimePermissions( + *extension, + PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + new_host_permissions, new_host_permissions)); + return RespondNow(NoArguments()); } @@ -1970,11 +2030,10 @@ DeveloperPrivateRemoveHostPermissionFunction::Run() { developer::RemoveHostPermission::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - GURL host(params->host); - if (!host.is_valid() || host.path_piece().length() > 1 || host.has_query() || - host.has_ref()) { + base::Optional<URLPattern> pattern = + ParseRuntimePermissionsPattern(params->host); + if (!pattern) return RespondNow(Error(kInvalidHost)); - } const Extension* extension = GetExtensionById(params->extension_id); if (!extension) @@ -1984,10 +2043,18 @@ DeveloperPrivateRemoveHostPermissionFunction::Run() { if (!scripting_modifier.CanAffectExtension()) return RespondNow(Error(kCannotChangeHostPermissions)); - if (!scripting_modifier.HasGrantedHostPermission(host)) + URLPatternSet host_permissions_to_remove({*pattern}); + std::unique_ptr<const PermissionSet> permissions_to_remove = + PermissionSet::CreateIntersection( + PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + host_permissions_to_remove, host_permissions_to_remove), + *scripting_modifier.GetRevokablePermissions(), + URLPatternSet::IntersectionBehavior::kDetailed); + if (permissions_to_remove->IsEmpty()) return RespondNow(Error("Cannot remove a host that hasn't been granted.")); - scripting_modifier.RemoveGrantedHostPermission(host); + PermissionsUpdater(browser_context()) + .RevokeRuntimePermissions(*extension, *permissions_to_remove); return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h index 1d59e38c25a..373a4300e99 100644 --- a/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h +++ b/chromium/chrome/browser/extensions/api/developer_private/developer_private_api.h @@ -23,6 +23,8 @@ #include "chrome/common/extensions/api/developer_private.h" #include "chrome/common/extensions/webstore_install_result.h" #include "components/prefs/pref_change_registrar.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" #include "extensions/browser/api/file_system/file_system_api.h" #include "extensions/browser/app_window/app_window_registry.h" #include "extensions/browser/browser_context_keyed_api_factory.h" @@ -59,7 +61,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, public CommandService::Observer, public ExtensionPrefsObserver, public ExtensionManagement::Observer, - public WarningService::Observer { + public WarningService::Observer, + public content::NotificationObserver { public: explicit DeveloperPrivateEventRouter(Profile* profile); ~DeveloperPrivateEventRouter() override; @@ -107,6 +110,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, // ExtensionPrefsObserver: void OnExtensionDisableReasonsChanged(const std::string& extension_id, int disable_reasons) override; + void OnExtensionRuntimePermissionsChanged( + const std::string& extension_id) override; // ExtensionManagement::Observer: void OnExtensionManagementSettingsChanged() override; @@ -115,6 +120,11 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, void ExtensionWarningsChanged( const ExtensionIdSet& affected_extensions) override; + // content::NotificationObserver: + void Observe(int notification_type, + const content::NotificationSource& source, + const content::NotificationDetails& details) override; + // Handles a profile preferance change. void OnProfilePrefChanged(); @@ -157,6 +167,8 @@ class DeveloperPrivateEventRouter : public ExtensionRegistryObserver, PrefChangeRegistrar pref_change_registrar_; + content::NotificationRegistrar notification_registrar_; + base::WeakPtrFactory<DeveloperPrivateEventRouter> weak_factory_; DISALLOW_COPY_AND_ASSIGN(DeveloperPrivateEventRouter); @@ -267,6 +279,10 @@ class DeveloperPrivateAPI : public BrowserContextKeyedAPI, DISALLOW_COPY_AND_ASSIGN(DeveloperPrivateAPI); }; +template <> +void BrowserContextKeyedAPIFactory< + DeveloperPrivateAPI>::DeclareFactoryDependencies(); + namespace api { class DeveloperPrivateAPIFunction : public UIThreadExtensionFunction { 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 e5c2c3880ee..a4579c439a6 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 @@ -10,6 +10,7 @@ #include "base/files/file_util.h" #include "base/macros.h" #include "base/scoped_observer.h" +#include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/test/scoped_feature_list.h" #include "chrome/browser/chrome_notification_types.h" @@ -21,6 +22,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_service_test_with_install.h" #include "chrome/browser/extensions/extension_util.h" +#include "chrome/browser/extensions/permissions_updater.h" #include "chrome/browser/extensions/scripting_permissions_modifier.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/browser/extensions/unpacked_installer.h" @@ -41,6 +43,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/test/web_contents_tester.h" #include "extensions/browser/api_test_utils.h" +#include "extensions/browser/event_router.h" #include "extensions/browser/event_router_factory.h" #include "extensions/browser/extension_dialog_auto_confirm.h" #include "extensions/browser/extension_error_test_util.h" @@ -51,12 +54,15 @@ #include "extensions/browser/extension_util.h" #include "extensions/browser/install/extension_install_ui.h" #include "extensions/browser/mock_external_provider.h" +#include "extensions/browser/test_event_router_observer.h" #include "extensions/browser/test_extension_registry_observer.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_features.h" #include "extensions/common/extension_set.h" #include "extensions/common/manifest_constants.h" +#include "extensions/common/permissions/permission_set.h" +#include "extensions/common/permissions/permissions_data.h" #include "extensions/common/value_builder.h" #include "extensions/test/test_extension_dir.h" #include "services/data_decoder/data_decoder_service.h" @@ -90,6 +96,34 @@ bool HasPrefsPermission(bool (*has_pref)(const std::string&, return has_pref(id, context); } +bool WasPermissionsUpdatedEventDispatched( + const TestEventRouterObserver& observer, + const ExtensionId& extension_id) { + const std::string kEventName = + api::developer_private::OnItemStateChanged::kEventName; + const auto& event_map = observer.events(); + auto iter = event_map.find(kEventName); + if (iter == event_map.end()) + return false; + + const Event& event = *iter->second; + CHECK(event.event_args); + CHECK_GE(1u, event.event_args->GetList().size()); + std::unique_ptr<api::developer_private::EventData> event_data = + api::developer_private::EventData::FromValue( + event.event_args->GetList()[0]); + if (!event_data) + return false; + + if (event_data->item_id != extension_id || + event_data->event_type != + api::developer_private::EVENT_TYPE_PERMISSIONS_CHANGED) { + return false; + } + + return true; +} + } // namespace class DeveloperPrivateApiUnitTest : public ExtensionServiceTestWithInstall { @@ -1341,19 +1375,33 @@ TEST_F(DeveloperPrivateApiUnitTest, GrantHostPermission) { } }; - GURL host("https://example.com"); - EXPECT_FALSE(modifier.HasGrantedHostPermission(host)); - run_add_host_permission(host.spec(), true, nullptr); + const GURL kExampleCom("https://example.com/"); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kExampleCom)); + run_add_host_permission("https://example.com/*", true, nullptr); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kExampleCom)); + + const GURL kGoogleCom("https://google.com"); + const GURL kMapsGoogleCom("https://maps.google.com/"); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kGoogleCom)); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kMapsGoogleCom)); + run_add_host_permission("https://*.google.com/*", true, nullptr); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kGoogleCom)); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kMapsGoogleCom)); run_add_host_permission(kInvalidHost, false, kInvalidHostError); + // Path of the pattern must exactly match "/*". + run_add_host_permission("https://example.com/", false, kInvalidHostError); run_add_host_permission("https://example.com/foobar", false, kInvalidHostError); run_add_host_permission("https://example.com/#foobar", false, kInvalidHostError); + run_add_host_permission("https://example.com/*foobar", false, + kInvalidHostError); + + // Cannot grant chrome:-scheme URLs. + GURL chrome_host("chrome://settings/*"); + run_add_host_permission(chrome_host.spec(), false, kInvalidHostError); - GURL chrome_host("chrome://settings"); - run_add_host_permission(chrome_host.spec(), false, - "Cannot grant a permission that wasn't withheld."); EXPECT_FALSE(modifier.HasGrantedHostPermission(chrome_host)); } @@ -1386,22 +1434,41 @@ TEST_F(DeveloperPrivateApiUnitTest, RemoveHostPermission) { } }; - GURL host("https://example.com"); - run_remove_host_permission(host.spec(), false, + run_remove_host_permission("https://example.com/*", false, "Cannot remove a host that hasn't been granted."); - modifier.GrantHostPermission(host); - EXPECT_TRUE(modifier.HasGrantedHostPermission(host)); + const GURL kExampleCom("https://example.com"); + modifier.GrantHostPermission(kExampleCom); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kExampleCom)); + // Path of the pattern must exactly match "/*". + run_remove_host_permission("https://example.com/", false, kInvalidHostError); run_remove_host_permission("https://example.com/foobar", false, kInvalidHostError); run_remove_host_permission("https://example.com/#foobar", false, kInvalidHostError); + run_remove_host_permission("https://example.com/*foobar", false, + kInvalidHostError); run_remove_host_permission(kInvalidHost, false, kInvalidHostError); - EXPECT_TRUE(modifier.HasGrantedHostPermission(host)); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kExampleCom)); + + run_remove_host_permission("https://example.com/*", true, nullptr); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kExampleCom)); + + URLPattern new_pattern(Extension::kValidHostPermissionSchemes, + "https://*.google.com/*"); + PermissionsUpdater(profile()).GrantRuntimePermissions( + *extension, PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + URLPatternSet({new_pattern}), URLPatternSet())); + + const GURL kGoogleCom("https://google.com/"); + const GURL kMapsGoogleCom("https://maps.google.com/"); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kGoogleCom)); + EXPECT_TRUE(modifier.HasGrantedHostPermission(kMapsGoogleCom)); - run_remove_host_permission(host.spec(), true, nullptr); - EXPECT_FALSE(modifier.HasGrantedHostPermission(host)); + run_remove_host_permission("https://*.google.com/*", true, nullptr); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kGoogleCom)); + EXPECT_FALSE(modifier.HasGrantedHostPermission(kMapsGoogleCom)); } TEST_F(DeveloperPrivateApiUnitTest, UpdateHostAccess) { @@ -1494,6 +1561,165 @@ TEST_F(DeveloperPrivateApiUnitTest, EXPECT_FALSE(modifier.HasGrantedHostPermission(example_com)); } +TEST_F(DeveloperPrivateApiUnitTest, + UpdateHostAccess_GrantScopeGreaterThanRequestedScope) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kRuntimeHostPermissions); + + scoped_refptr<const Extension> extension = + ExtensionBuilder("test").AddPermission("http://*/*").Build(); + service()->AddExtension(extension.get()); + ScriptingPermissionsModifier modifier(profile(), extension.get()); + modifier.SetWithholdHostPermissions(true); + + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(profile()); + EXPECT_EQ(PermissionSet(), + extension->permissions_data()->active_permissions()); + EXPECT_EQ(PermissionSet(), + *extension_prefs->GetRuntimeGrantedPermissions(extension->id())); + + { + scoped_refptr<UIThreadExtensionFunction> function = + base::MakeRefCounted<api::DeveloperPrivateAddHostPermissionFunction>(); + std::string args = base::StringPrintf( + R"(["%s", "%s"])", extension->id().c_str(), "*://chromium.org/*"); + EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile())) + << function->GetError(); + } + + // The active permissions (which are given to the extension process) should + // only include the intersection of what was requested by the extension and + // the runtime granted permissions - which is http://chromium.org/*. + URLPattern http_chromium(Extension::kValidHostPermissionSchemes, + "http://chromium.org/*"); + const PermissionSet http_chromium_set( + APIPermissionSet(), ManifestPermissionSet(), + URLPatternSet({http_chromium}), URLPatternSet()); + EXPECT_EQ(http_chromium_set, + extension->permissions_data()->active_permissions()); + + // The runtime granted permissions should include all of what was approved by + // the user, which is *://chromium.org/*, and should be present in both the + // scriptable and explicit hosts. + URLPattern all_chromium(Extension::kValidHostPermissionSchemes, + "*://chromium.org/*"); + const PermissionSet all_chromium_set( + APIPermissionSet(), ManifestPermissionSet(), + URLPatternSet({all_chromium}), URLPatternSet({all_chromium})); + EXPECT_EQ(all_chromium_set, + *extension_prefs->GetRuntimeGrantedPermissions(extension->id())); + + { + scoped_refptr<UIThreadExtensionFunction> function = base::MakeRefCounted< + api::DeveloperPrivateRemoveHostPermissionFunction>(); + std::string args = base::StringPrintf( + R"(["%s", "%s"])", extension->id().c_str(), "*://chromium.org/*"); + EXPECT_TRUE(api_test_utils::RunFunction(function.get(), args, profile())) + << function->GetError(); + } + + // Removing the granted permission should remove it entirely from both + // the active and the stored permissions. + EXPECT_EQ(PermissionSet(), + extension->permissions_data()->active_permissions()); + EXPECT_EQ(PermissionSet(), + *extension_prefs->GetRuntimeGrantedPermissions(extension->id())); +} + +TEST_F(DeveloperPrivateApiUnitTest, + UpdateHostAccess_UnrequestedHostsDispatchUpdateEvents) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kRuntimeHostPermissions); + + scoped_refptr<const Extension> extension = + ExtensionBuilder("test").AddPermission("http://google.com/*").Build(); + service()->AddExtension(extension.get()); + ScriptingPermissionsModifier modifier(profile(), extension.get()); + modifier.SetWithholdHostPermissions(true); + + // We need to call DeveloperPrivateAPI::Get() in order to instantiate the + // keyed service, since it's not created by default in unit tests. + DeveloperPrivateAPI::Get(profile()); + const ExtensionId listener_id = crx_file::id_util::GenerateId("listener"); + EventRouter* event_router = EventRouter::Get(profile()); + + // The DeveloperPrivateEventRouter will only dispatch events if there's at + // least one listener to dispatch to. Create one. + content::RenderProcessHost* process = nullptr; + const char* kEventName = + api::developer_private::OnItemStateChanged::kEventName; + event_router->AddEventListener(kEventName, process, listener_id); + + TestEventRouterObserver test_observer(event_router); + EXPECT_FALSE( + WasPermissionsUpdatedEventDispatched(test_observer, extension->id())); + + URLPatternSet hosts({URLPattern(Extension::kValidHostPermissionSchemes, + "https://example.com/*")}); + PermissionSet permissions(APIPermissionSet(), ManifestPermissionSet(), hosts, + hosts); + PermissionsUpdater(profile()).GrantRuntimePermissions(*extension, + permissions); + // The event router fetches icons from a blocking thread when sending the + // update event; allow it to finish before verifying the event was dispatched. + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE( + WasPermissionsUpdatedEventDispatched(test_observer, extension->id())); + + test_observer.ClearEvents(); + + PermissionsUpdater(profile()).RevokeRuntimePermissions(*extension, + permissions); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE( + WasPermissionsUpdatedEventDispatched(test_observer, extension->id())); +} + +TEST_F(DeveloperPrivateApiUnitTest, ExtensionUpdatedEventOnPermissionsChange) { + // We need to call DeveloperPrivateAPI::Get() in order to instantiate the + // keyed service, since it's not created by default in unit tests. + DeveloperPrivateAPI::Get(profile()); + const ExtensionId listener_id = crx_file::id_util::GenerateId("listener"); + EventRouter* event_router = EventRouter::Get(profile()); + + // The DeveloperPrivateEventRouter will only dispatch events if there's at + // least one listener to dispatch to. Create one. + content::RenderProcessHost* process = nullptr; + const char* kEventName = + api::developer_private::OnItemStateChanged::kEventName; + event_router->AddEventListener(kEventName, process, listener_id); + + scoped_refptr<const Extension> dummy_extension = + ExtensionBuilder("dummy") + .SetManifestKey("optional_permissions", + ListBuilder().Append("tabs").Build()) + .Build(); + + TestEventRouterObserver test_observer(event_router); + EXPECT_FALSE(WasPermissionsUpdatedEventDispatched(test_observer, + dummy_extension->id())); + + APIPermissionSet apis; + apis.insert(APIPermission::kTab); + PermissionSet permissions(apis, ManifestPermissionSet(), URLPatternSet(), + URLPatternSet()); + PermissionsUpdater(profile()).GrantOptionalPermissions(*dummy_extension, + permissions); + // The event router fetches icons from a blocking thread when sending the + // update event; allow it to finish before verifying the event was dispatched. + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(WasPermissionsUpdatedEventDispatched(test_observer, + dummy_extension->id())); + + test_observer.ClearEvents(); + + PermissionsUpdater(profile()).RevokeOptionalPermissions( + *dummy_extension, permissions, PermissionsUpdater::REMOVE_HARD); + base::RunLoop().RunUntilIdle(); + EXPECT_TRUE(WasPermissionsUpdatedEventDispatched(test_observer, + dummy_extension->id())); +} + class DeveloperPrivateZipInstallerUnitTest : public DeveloperPrivateApiUnitTest { public: 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 98edfbd90b7..d17531186eb 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 @@ -273,24 +273,33 @@ void AddPermissionsInfo(content::BrowserContext* browser_context, if (!permissions_modifier.HasWithheldHostPermissions()) { permissions->host_access = developer::HOST_ACCESS_ON_ALL_SITES; } else { - constexpr bool include_rcd = true; - constexpr bool exclude_file_scheme = true; - std::set<std::string> distinct_hosts = - permission_message_util::GetDistinctHosts( - active_permissions.effective_hosts(), include_rcd, - exclude_file_scheme); - // TODO(devlin): This isn't quite right - it's possible the user just - // selected "on specific sites" from the dropdown, and hasn't yet added - // any sites. We'll need to handle this. - // https://crbug.com/844128. - if (!distinct_hosts.empty()) { + ExtensionPrefs* extension_prefs = ExtensionPrefs::Get(browser_context); + std::unique_ptr<const PermissionSet> runtime_granted_permissions = + extension_prefs->GetRuntimeGrantedPermissions(extension.id()); + if (runtime_granted_permissions->effective_hosts().is_empty()) { + // TODO(devlin): This isn't quite right - it's possible the user just + // selected "on specific sites" from the dropdown, and hasn't yet added + // any sites. We'll need to handle this. + // https://crbug.com/844128. + permissions->host_access = developer::HOST_ACCESS_ON_CLICK; + } else { permissions->host_access = developer::HOST_ACCESS_ON_SPECIFIC_SITES; + std::set<std::string> distinct_hosts; + for (auto pattern : runtime_granted_permissions->effective_hosts()) { + // We only allow addition/removal of full hosts (since from a + // permissions point of view, path is irrelevant). We always make the + // path wildcard when adding through this UI, but the optional + // permissions API may allow adding permissions with paths. + // TODO(devlin): Investigate, and possibly change the optional + // permissions API. + pattern.SetPath("/*"); + distinct_hosts.insert(pattern.GetAsString()); + } + permissions->runtime_host_permissions = std::make_unique<std::vector<std::string>>( std::make_move_iterator(distinct_hosts.begin()), std::make_move_iterator(distinct_hosts.end())); - } else { - permissions->host_access = developer::HOST_ACCESS_ON_CLICK; } } } 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 a8184d3e998..9d8496fb56c 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 @@ -36,7 +36,10 @@ #include "extensions/common/extension_features.h" #include "extensions/common/feature_switch.h" #include "extensions/common/permissions/permission_message.h" +#include "extensions/common/permissions/permission_set.h" #include "extensions/common/permissions/permissions_data.h" +#include "extensions/common/url_pattern.h" +#include "extensions/common/url_pattern_set.h" #include "extensions/common/value_builder.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -430,7 +433,7 @@ TEST_F(ExtensionInfoGeneratorUnitTest, RuntimeHostPermissions) { info->permissions.host_access); ASSERT_TRUE(info->permissions.runtime_host_permissions); EXPECT_THAT(*info->permissions.runtime_host_permissions, - testing::UnorderedElementsAre("example.com")); + testing::UnorderedElementsAre("https://example.com/*")); EXPECT_THAT(info->permissions.simple_permissions, testing::IsEmpty()); // An extension that doesn't request any host permissions should not have @@ -457,6 +460,50 @@ TEST_F(ExtensionInfoGeneratorUnitTest, RuntimeHostPermissionsWithoutFeature) { info->permissions.simple_permissions[0].message); } +// Tests that runtime_host_permissions is correctly populated when permissions +// are granted by the user beyond what the extension originally requested in the +// manifest. +TEST_F(ExtensionInfoGeneratorUnitTest, + RuntimeHostPermissionsBeyondRequestedScope) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kRuntimeHostPermissions); + + scoped_refptr<const Extension> extension = + CreateExtension("extension", ListBuilder().Append("http://*/*").Build(), + Manifest::INTERNAL); + + std::unique_ptr<developer::ExtensionInfo> info = + GenerateExtensionInfo(extension->id()); + + // Withhold permissions, and grant *://chromium.org/*. + ScriptingPermissionsModifier permissions_modifier(profile(), extension); + permissions_modifier.SetWithholdHostPermissions(true); + URLPattern all_chromium(Extension::kValidHostPermissionSchemes, + "*://chromium.org/*"); + PermissionSet all_chromium_set(APIPermissionSet(), ManifestPermissionSet(), + URLPatternSet({all_chromium}), + URLPatternSet({all_chromium})); + PermissionsUpdater(profile()).GrantRuntimePermissions(*extension, + all_chromium_set); + + // The extension should only be granted http://chromium.org/* (since that's + // the intersection with what it requested). + URLPattern http_chromium(Extension::kValidHostPermissionSchemes, + "http://chromium.org/*"); + EXPECT_EQ(PermissionSet(APIPermissionSet(), ManifestPermissionSet(), + URLPatternSet({http_chromium}), URLPatternSet()), + extension->permissions_data()->active_permissions()); + + // The generated info should use the entirety of the granted permission, + // which is *://chromium.org/*. + info = GenerateExtensionInfo(extension->id()); + EXPECT_EQ(developer::HOST_ACCESS_ON_SPECIFIC_SITES, + info->permissions.host_access); + ASSERT_TRUE(info->permissions.runtime_host_permissions); + EXPECT_THAT(*info->permissions.runtime_host_permissions, + testing::UnorderedElementsAre("*://chromium.org/*")); +} + // Test that file:// access checkbox does not show up when the user can't // modify an extension's settings. https://crbug.com/173640. TEST_F(ExtensionInfoGeneratorUnitTest, ExtensionInfoLockedAllUrls) { diff --git a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc index 1205f08f044..9ad91ec7fcf 100644 --- a/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc +++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api.cc @@ -642,12 +642,7 @@ class ExtensionDownloadsEventRouterData : public base::SupportsUserData::Data { download_item->SetUserData(kKey, base::WrapUnique(this)); } - ~ExtensionDownloadsEventRouterData() override { - if (updated_ > 0) { - UMA_HISTOGRAM_PERCENTAGE("Download.OnChanged", - (changed_fired_ * 100 / updated_)); - } - } + ~ExtensionDownloadsEventRouterData() override = default; void set_is_download_completed(bool is_download_completed) { is_download_completed_ = is_download_completed; 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 a6529fc0fdb..e0fbac65374 100644 --- a/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/downloads/downloads_api_browsertest.cc @@ -1496,12 +1496,21 @@ 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, - DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) { +IN_PROC_BROWSER_TEST_F( + DownloadExtensionTest, + MAYBE_DownloadExtensionTest_SearchPauseResumeCancelGetFileIconIncognito) { std::unique_ptr<base::Value> result_value; base::ListValue* result_list = NULL; base::DictionaryValue* result_dict = NULL; diff --git a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc index d29ba90273d..72e1f6400f1 100644 --- a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc +++ b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc @@ -13,7 +13,7 @@ #include "base/lazy_instance.h" #include "base/numerics/safe_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/default_tick_clock.h" #include "base/time/time.h" @@ -26,6 +26,7 @@ #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service_regular.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager.h" #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h" +#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection.h" #include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/proximity_auth/proximity_auth_error_bubble.h" diff --git a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection.h b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection.h index 90b1b9080e2..987e88500e4 100644 --- a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection.h +++ b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection.h @@ -11,12 +11,20 @@ #include "content/public/browser/browser_thread.h" #include "extensions/browser/api/api_resource.h" #include "extensions/browser/api/api_resource_manager.h" +#include "extensions/browser/browser_context_keyed_api_factory.h" namespace cryptauth { class Connection; } // namespace cryptauth namespace extensions { + +class EasyUnlockPrivateConnection; + +template <> +BrowserContextKeyedAPIFactory<ApiResourceManager<EasyUnlockPrivateConnection>>* +ApiResourceManager<EasyUnlockPrivateConnection>::GetFactoryInstance(); + // An ApiResource wrapper for a cryptauth::Connection. class EasyUnlockPrivateConnection : public ApiResource { public: diff --git a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc index 3d694cbfe8d..58ec3c09ffa 100644 --- a/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc +++ b/chromium/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_connection_manager.cc @@ -136,7 +136,7 @@ void EasyUnlockPrivateConnectionManager::OnMessageReceived( std::string event_name = api::easy_unlock_private::OnDataReceived::kEventName; events::HistogramValue histogram_value = events::EASY_UNLOCK_PRIVATE_ON_DATA_RECEIVED; - std::vector<char> data(message.body().begin(), message.body().end()); + std::vector<uint8_t> data(message.body().begin(), message.body().end()); std::unique_ptr<base::ListValue> args = api::easy_unlock_private::OnDataReceived::Create(0, data); DispatchConnectionEvent(event_name, histogram_value, &connection, @@ -156,7 +156,7 @@ void EasyUnlockPrivateConnectionManager::OnSendCompleted( api::easy_unlock_private::OnSendCompleted::kEventName; events::HistogramValue histogram_value = events::EASY_UNLOCK_PRIVATE_ON_SEND_COMPLETED; - std::vector<char> data(message.payload().begin(), message.payload().end()); + std::vector<uint8_t> data(message.payload().begin(), message.payload().end()); std::unique_ptr<base::ListValue> args = api::easy_unlock_private::OnSendCompleted::Create(0, data, success); DispatchConnectionEvent(event_name, histogram_value, &connection, 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 c923b2d663b..07767f021ca 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 @@ -55,10 +55,17 @@ constexpr char kAnotherAffiliationID[] = "another-affiliation-id"; constexpr char kTestExtensionID[] = "nbiliclbejdndfpchgkbmfoppjplbdok"; struct Params { - explicit Params(bool affiliated) : affiliated_(affiliated) {} - bool affiliated_; + 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"); +} + base::Value BuildCustomArg(const std::string& expected_directory_device_id, const std::string& expected_serial_number, const std::string& expected_asset_id, @@ -78,9 +85,9 @@ base::Value BuildCustomArg(const std::string& expected_directory_device_id, namespace extensions { -class EnterpriseDeviceAttributesTest : - public ExtensionApiTest, - public ::testing::WithParamInterface<Params> { +class EnterpriseDeviceAttributesTest + : public ExtensionApiTest, + public ::testing::WithParamInterface<Params> { public: EnterpriseDeviceAttributesTest() { fake_statistics_provider_.SetMachineStatistic( @@ -93,8 +100,8 @@ class EnterpriseDeviceAttributesTest : // ExtensionApiTest void SetUpCommandLine(base::CommandLine* command_line) override { ExtensionApiTest::SetUpCommandLine(command_line); - policy::affiliation_test_helper:: - AppendCommandLineSwitchesForLoginManager(command_line); + policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager( + command_line); } void SetUpInProcessBrowserTestFixture() override { @@ -106,29 +113,24 @@ class EnterpriseDeviceAttributesTest : std::unique_ptr<chromeos::SessionManagerClient>( fake_session_manager_client)); + policy::AffiliationTestHelper affiliation_helper = + policy::AffiliationTestHelper::CreateForCloud( + fake_session_manager_client); + std::set<std::string> device_affiliation_ids; device_affiliation_ids.insert(kAffiliationID); - policy::affiliation_test_helper::SetDeviceAffiliationID( - &test_helper_, fake_session_manager_client, device_affiliation_ids); + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs( + &test_helper_, device_affiliation_ids)); std::set<std::string> user_affiliation_ids; - if (GetParam().affiliated_) { + if (GetParam().affiliated) { user_affiliation_ids.insert(kAffiliationID); } else { user_affiliation_ids.insert(kAnotherAffiliationID); } policy::UserPolicyBuilder user_policy; - policy::affiliation_test_helper::SetUserAffiliationIDs( - &user_policy, fake_session_manager_client, affiliated_account_id_, - user_affiliation_ids); - - // Set up fake install attributes. - std::unique_ptr<chromeos::StubInstallAttributes> attributes = - std::make_unique<chromeos::StubInstallAttributes>(); - - attributes->SetCloudManaged("fake-domain", "fake-id"); - policy::BrowserPolicyConnectorChromeOS::SetInstallAttributesForTesting( - attributes.release()); + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs( + &user_policy, affiliated_account_id_, user_affiliation_ids)); test_helper_.InstallOwnerKey(); // Init the device policy. @@ -157,7 +159,7 @@ class EnterpriseDeviceAttributesTest : const base::ListValue* users = g_browser_process->local_state()->GetList("LoggedInUsers"); if (!users->empty()) - policy::affiliation_test_helper::LoginUser(affiliated_account_id_); + policy::AffiliationTestHelper::LoginUser(affiliated_account_id_); ExtensionApiTest::SetUpOnMainThread(); } @@ -221,13 +223,16 @@ class EnterpriseDeviceAttributesTest : kAffiliatedUserGaiaId); private: + chromeos::ScopedStubInstallAttributes test_install_attributes_{ + chromeos::StubInstallAttributes::CreateCloudManaged("fake-domain", + "fake-id")}; policy::MockConfigurationPolicyProvider policy_provider_; policy::DevicePolicyCrosTestHelper test_helper_; chromeos::system::ScopedFakeStatisticsProvider fake_statistics_provider_; }; IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, PRE_Success) { - policy::affiliation_test_helper::PreLoginUser(affiliated_account_id_); + policy::AffiliationTestHelper::PreLoginUser(affiliated_account_id_); } IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, Success) { @@ -237,17 +242,18 @@ IN_PROC_BROWSER_TEST_P(EnterpriseDeviceAttributesTest, Success) { SetPolicy(); - EXPECT_EQ(GetParam().affiliated_, user_manager::UserManager::Get()-> - FindUser(affiliated_account_id_)->IsAffiliated()); + EXPECT_EQ(GetParam().affiliated, user_manager::UserManager::Get() + ->FindUser(affiliated_account_id_) + ->IsAffiliated()); // Device attributes are available only for affiliated user. std::string expected_directory_device_id = - GetParam().affiliated_ ? kDeviceId : ""; + GetParam().affiliated ? kDeviceId : ""; std::string expected_serial_number = - GetParam().affiliated_ ? kSerialNumber : ""; - std::string expected_asset_id = GetParam().affiliated_ ? kAssetId : ""; + GetParam().affiliated ? kSerialNumber : ""; + std::string expected_asset_id = GetParam().affiliated ? kAssetId : ""; std::string expected_annotated_location = - GetParam().affiliated_ ? kAnnotatedLocation : ""; + GetParam().affiliated ? kAnnotatedLocation : ""; // Pass the expected value (device_id) to test. ASSERT_TRUE(TestExtension( @@ -284,6 +290,8 @@ IN_PROC_BROWSER_TEST_F( // Both cases of affiliated and non-affiliated on the device user are tested. INSTANTIATE_TEST_CASE_P(AffiliationCheck, - EnterpriseDeviceAttributesTest, - ::testing::Values(Params(true), Params(false))); + EnterpriseDeviceAttributesTest, + ::testing::Values(Params(true /* affiliated */), + Params(false /* affiliated */)), + PrintParam); } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/OWNERS b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/OWNERS new file mode 100644 index 00000000000..05cb9bd3c02 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/OWNERS @@ -0,0 +1 @@ +guidou@chromium.org diff --git a/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.cc b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.cc new file mode 100644 index 00000000000..3a75d928dd4 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.cc @@ -0,0 +1,38 @@ +// Copyright 2018 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_hardware_platform/enterprise_hardware_platform_api.h" + +#include <utility> + +#include "base/bind.h" +#include "chrome/common/extensions/api/enterprise_hardware_platform.h" + +namespace extensions { + +EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction:: + EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction() = default; + +EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction:: + ~EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction() = default; + +ExtensionFunction::ResponseAction +EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction::Run() { + base::SysInfo::GetHardwareInfo(base::BindOnce( + &EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction:: + OnHardwarePlatformInfo, + this)); + return RespondLater(); +} + +void EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction:: + OnHardwarePlatformInfo(base::SysInfo::HardwareInfo info) { + api::enterprise_hardware_platform::HardwarePlatformInfo result; + result.manufacturer = std::move(info.manufacturer); + result.model = std::move(info.model); + Respond(ArgumentList(api::enterprise_hardware_platform:: + GetHardwarePlatformInfo::Results::Create(result))); +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.h b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.h new file mode 100644 index 00000000000..70726063ac1 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api.h @@ -0,0 +1,37 @@ +// Copyright 2018 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_HARDWARE_PLATFORM_ENTERPRISE_HARDWARE_PLATFORM_API_H_ +#define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_HARDWARE_PLATFORM_ENTERPRISE_HARDWARE_PLATFORM_API_H_ + +#include "base/macros.h" +#include "base/sys_info.h" +#include "extensions/browser/extension_function.h" + +namespace extensions { + +class EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction + : public UIThreadExtensionFunction { + public: + EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction(); + + protected: + ~EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction() override; + + ResponseAction Run() override; + + private: + DECLARE_EXTENSION_FUNCTION( + "enterprise.hardwarePlatform.getHardwarePlatformInfo", + ENTERPRISE_HARDWAREPLATFORM_GETHARDWAREPLATFORMINFO); + + void OnHardwarePlatformInfo(base::SysInfo::HardwareInfo info); + + DISALLOW_COPY_AND_ASSIGN( + EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_HARDWARE_PLATFORM_ENTERPRISE_HARDWARE_PLATFORM_API_H_ diff --git a/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc new file mode 100644 index 00000000000..285c63683a1 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_hardware_platform/enterprise_hardware_platform_api_unittest.cc @@ -0,0 +1,84 @@ +// Copyright 2018 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_hardware_platform/enterprise_hardware_platform_api.h" + +#include <memory> +#include <string> + +#include "base/json/json_writer.h" +#include "chrome/browser/extensions/extension_api_unittest.h" +#include "chrome/browser/extensions/extension_function_test_utils.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_service_test_with_install.h" +#include "components/crx_file/id_util.h" +#include "extensions/common/extension_builder.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { + +class EnterpriseHardwarePlatformAPITest + : public ExtensionServiceTestWithInstall { + public: + EnterpriseHardwarePlatformAPITest() = default; + ~EnterpriseHardwarePlatformAPITest() override = default; + Browser* browser() { return browser_.get(); } + + private: + void SetUp() override { + ExtensionServiceTestWithInstall::SetUp(); + InitializeEmptyExtensionService(); + browser_window_ = std::make_unique<TestBrowserWindow>(); + Browser::CreateParams params(profile(), true); + params.type = Browser::TYPE_TABBED; + params.window = browser_window_.get(); + browser_ = std::make_unique<Browser>(params); + } + + void TearDown() override { + browser_.reset(); + browser_window_.reset(); + ExtensionServiceTestWithInstall::TearDown(); + } + + std::unique_ptr<TestBrowserWindow> browser_window_; + std::unique_ptr<Browser> browser_; + + DISALLOW_COPY_AND_ASSIGN(EnterpriseHardwarePlatformAPITest); +}; + +TEST_F(EnterpriseHardwarePlatformAPITest, GetHardwarePlatformInfo) { + scoped_refptr<const Extension> extension = ExtensionBuilder("Test").Build(); + scoped_refptr<EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction> + function = + new EnterpriseHardwarePlatformGetHardwarePlatformInfoFunction(); + function->set_extension(extension.get()); + function->set_has_callback(true); + + std::string args; + base::JSONWriter::Write(base::ListValue(), &args); + + std::unique_ptr<base::Value> result( + extension_function_test_utils::RunFunctionAndReturnSingleResult( + function.get(), args, browser())); + base::RunLoop().RunUntilIdle(); + + ASSERT_TRUE(result); + ASSERT_TRUE(result->is_dict()); + ASSERT_EQ(result->DictSize(), 2u); + + const base::Value* val = + result->FindKeyOfType("manufacturer", base::Value::Type::STRING); + ASSERT_TRUE(val); + const std::string& manufacturer = val->GetString(); + + val = result->FindKeyOfType("model", base::Value::Type::STRING); + ASSERT_TRUE(val); + const std::string& model = val->GetString(); + + EXPECT_FALSE(manufacturer.empty()); + EXPECT_FALSE(model.empty()); +} + +} // 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 20f62befb7d..7136b037ebe 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 @@ -32,11 +32,11 @@ const char kEnterprisePlatformErrorInternal[] = "Internal Error."; const char kEnterprisePlatformErrorInvalidX509Cert[] = "Certificate is not a valid X.509 certificate."; -std::vector<char> VectorFromString(const std::string& s) { - return std::vector<char>(s.begin(), s.end()); +std::vector<uint8_t> VectorFromString(const std::string& s) { + return std::vector<uint8_t>(s.begin(), s.end()); } -std::string StringFromVector(const std::vector<char>& v) { +std::string StringFromVector(const std::vector<uint8_t>& v) { return std::string(v.begin(), v.end()); } @@ -77,7 +77,7 @@ void EnterprisePlatformKeysInternalGenerateKeyFunction::OnGeneratedKey( DCHECK_CURRENTLY_ON(content::BrowserThread::UI); if (error_message.empty()) { Respond(ArgumentList(api_epki::GenerateKey::Results::Create( - std::vector<char>(public_key_der.begin(), public_key_der.end())))); + std::vector<uint8_t>(public_key_der.begin(), public_key_der.end())))); } else { Respond(Error(error_message)); } @@ -142,14 +142,15 @@ EnterprisePlatformKeysImportCertificateFunction::Run() { if (!platform_keys::ValidateToken(params->token_id, &platform_keys_token_id)) return RespondNow(Error(platform_keys::kErrorInvalidToken)); - const std::vector<char>& cert_der = params->certificate; + const std::vector<uint8_t>& cert_der = params->certificate; // Allow UTF-8 inside PrintableStrings in client certificates. See // crbug.com/770323 and crbug.com/788655. net::X509Certificate::UnsafeCreateOptions options; options.printable_string_is_utf8 = true; scoped_refptr<net::X509Certificate> cert_x509 = net::X509Certificate::CreateFromBytesUnsafeOptions( - cert_der.data(), cert_der.size(), options); + reinterpret_cast<const char*>(cert_der.data()), cert_der.size(), + options); if (!cert_x509.get()) return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert)); @@ -185,14 +186,15 @@ EnterprisePlatformKeysRemoveCertificateFunction::Run() { if (!platform_keys::ValidateToken(params->token_id, &platform_keys_token_id)) return RespondNow(Error(platform_keys::kErrorInvalidToken)); - const std::vector<char>& cert_der = params->certificate; + const std::vector<uint8_t>& cert_der = params->certificate; // Allow UTF-8 inside PrintableStrings in client certificates. See // crbug.com/770323 and crbug.com/788655. net::X509Certificate::UnsafeCreateOptions options; options.printable_string_is_utf8 = true; scoped_refptr<net::X509Certificate> cert_x509 = net::X509Certificate::CreateFromBytesUnsafeOptions( - cert_der.data(), cert_der.size(), options); + reinterpret_cast<const char*>(cert_der.data()), cert_der.size(), + options); if (!cert_x509.get()) return RespondNow(Error(kEnterprisePlatformErrorInvalidX509Cert)); diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc index d6cdb2102e3..30be624ce38 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc @@ -13,6 +13,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/chromeos/settings/stub_install_attributes.h" #include "chrome/browser/extensions/extension_function_test_utils.h" @@ -24,6 +25,7 @@ #include "chrome/test/base/testing_profile_manager.h" #include "chromeos/attestation/mock_attestation_flow.h" #include "chromeos/cryptohome/async_method_caller.h" +#include "chromeos/cryptohome/cryptohome_parameters.h" #include "chromeos/cryptohome/mock_async_method_caller.h" #include "chromeos/dbus/attestation_constants.h" #include "chromeos/dbus/dbus_method_call_status.h" @@ -366,7 +368,7 @@ TEST_F(EPKChallengeMachineKeyTest, Success) { ASSERT_TRUE(value->is_blob()); EXPECT_EQ("response", - std::string(value->GetBlob().data(), value->GetBlob().size())); + std::string(value->GetBlob().begin(), value->GetBlob().end())); } TEST_F(EPKChallengeMachineKeyTest, KeyRegisteredSuccess) { @@ -395,7 +397,7 @@ TEST_F(EPKChallengeMachineKeyTest, KeyRegisteredSuccess) { ASSERT_TRUE(value->is_blob()); EXPECT_EQ("response", - std::string(value->GetBlob().data(), value->GetBlob().size())); + std::string(value->GetBlob().begin(), value->GetBlob().end())); } TEST_F(EPKChallengeMachineKeyTest, AttestationNotPrepared) { @@ -511,7 +513,8 @@ TEST_F(EPKChallengeUserKeyTest, KeyRegistrationFailed) { TEST_F(EPKChallengeUserKeyTest, KeyExists) { cryptohome_client_.SetTpmAttestationUserCertificate( - cryptohome::Identification(AccountId::FromUserEmail(kUserEmail)), + cryptohome::CreateAccountIdentifierFromAccountId( + AccountId::FromUserEmail(kUserEmail)), "attest-ent-user", std::string()); // GetCertificate must not be called if the key exists. EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)).Times(0); @@ -563,7 +566,7 @@ TEST_F(EPKChallengeUserKeyTest, Success) { ASSERT_TRUE(value->is_blob()); EXPECT_EQ("response", - std::string(value->GetBlob().data(), value->GetBlob().size())); + std::string(value->GetBlob().begin(), value->GetBlob().end())); } TEST_F(EPKChallengeUserKeyTest, AttestationNotPrepared) { 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 8435980e7af..ba1f6042883 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 @@ -224,7 +224,8 @@ void EPKPChallengeKeyBase::IsAttestationPreparedCallback( } // Attestation is available, see if the key we need already exists. cryptohome_client_->TpmAttestationDoesKeyExist( - context.key_type, cryptohome::Identification(context.account_id), + context.key_type, + cryptohome::CreateAccountIdentifierFromAccountId(context.account_id), context.key_name, base::BindOnce(&EPKPChallengeKeyBase::DoesKeyExistCallback, base::Unretained(this), context)); diff --git a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc index fb7241aa4fa..62671ff64c8 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api_unittest.cc @@ -14,6 +14,7 @@ #include "base/values.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/scoped_cros_settings_test_helper.h" #include "chrome/browser/chromeos/settings/stub_install_attributes.h" #include "chrome/browser/extensions/extension_function_test_utils.h" @@ -485,7 +486,8 @@ TEST_F(EPKPChallengeUserKeyTest, KeyRegistrationFailed) { TEST_F(EPKPChallengeUserKeyTest, KeyExists) { cryptohome_client_.SetTpmAttestationUserCertificate( - cryptohome::Identification(AccountId::FromUserEmail(kUserEmail)), + cryptohome::CreateAccountIdentifierFromAccountId( + AccountId::FromUserEmail(kUserEmail)), "attest-ent-user", std::string()); // GetCertificate must not be called if the key exists. EXPECT_CALL(mock_attestation_flow_, GetCertificate(_, _, _, _, _)) 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 a9177d0b6db..b31517c9a8c 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 @@ -12,6 +12,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/enterprise_reporting_private/prefs.h" #include "chrome/browser/policy/chrome_browser_policy_connector.h" #include "chrome/browser/policy/machine_level_user_cloud_policy_controller.h" #include "chrome/browser/policy/policy_conversions.h" @@ -77,42 +78,53 @@ int64_t GetMachineLevelUserCloudPolicyFetchTimestamp() { void AppendAdditionalBrowserInformation(em::ChromeDesktopReportRequest* request, Profile* profile) { - // Set Chrome version number - request->mutable_browser_report()->set_browser_version( - version_info::GetVersionNumber()); - // Set Chrome channel - request->mutable_browser_report()->set_channel( - static_cast<em::BrowserReport_Channel>(chrome::GetChannel())); - // Set Chrome executable path - request->mutable_browser_report()->set_executable_path(GetChromePath()); + const PrefService* prefs = profile->GetPrefs(); + + if (prefs->GetBoolean(enterprise_reporting::kReportVersionData)) { + // Set Chrome version number + request->mutable_browser_report()->set_browser_version( + version_info::GetVersionNumber()); + // Set Chrome channel + request->mutable_browser_report()->set_channel( + policy::ConvertToProtoChannel(chrome::GetChannel())); + } // Add a new profile report if extension doesn't report any profile. if (request->browser_report().chrome_user_profile_reports_size() == 0) request->mutable_browser_report()->add_chrome_user_profile_reports(); DCHECK_EQ(1, request->browser_report().chrome_user_profile_reports_size()); - // Set profile ID for the first profile. - request->mutable_browser_report() - ->mutable_chrome_user_profile_reports(0) - ->set_id(GetProfileId(profile)); - - // Set policy data of the first profile. Extension will report this data in - // the future. - request->mutable_browser_report() - ->mutable_chrome_user_profile_reports(0) - ->set_policy_data(policy::GetAllPolicyValuesAsJSON(profile, true, false)); - - int64_t timestamp = GetMachineLevelUserCloudPolicyFetchTimestamp(); - if (timestamp > 0) { + + if (prefs->GetBoolean(enterprise_reporting::kReportUserIDData)) { + // Set Chrome executable path + request->mutable_browser_report()->set_executable_path(GetChromePath()); + + // Set profile ID for the first profile. + request->mutable_browser_report() + ->mutable_chrome_user_profile_reports(0) + ->set_id(GetProfileId(profile)); + + // Set the profile name request->mutable_browser_report() ->mutable_chrome_user_profile_reports(0) - ->set_policy_fetched_timestamp(timestamp); + ->set_name(prefs->GetString(prefs::kProfileName)); } - // Set the profile name - request->mutable_browser_report() - ->mutable_chrome_user_profile_reports(0) - ->set_name(profile->GetPrefs()->GetString(prefs::kProfileName)); + if (prefs->GetBoolean(enterprise_reporting::kReportPolicyData)) { + // Set policy data of the first profile. Extension will report this data in + // the future. + request->mutable_browser_report() + ->mutable_chrome_user_profile_reports(0) + ->set_policy_data( + policy::GetAllPolicyValuesAsJSON(profile, true, false)); + + int64_t timestamp = GetMachineLevelUserCloudPolicyFetchTimestamp(); + if (timestamp > 0) { + request->mutable_browser_report() + ->mutable_chrome_user_profile_reports(0) + ->set_policy_fetched_timestamp(timestamp); + } + } } bool UpdateJSONEncodedStringEntry(const base::Value& dict_value, @@ -135,37 +147,49 @@ bool UpdateJSONEncodedStringEntry(const base::Value& dict_value, return true; } -void AppendPlatformInformation(em::ChromeDesktopReportRequest* request) { - const char kComputerName[] = "computername"; - const char kUsername[] = "username"; - - base::Value os_info = base::Value(base::Value::Type::DICTIONARY); - os_info.SetKey(kOS, base::Value(policy::GetOSPlatform())); - os_info.SetKey(kOSVersion, base::Value(policy::GetOSVersion())); - os_info.SetKey(kOSArch, base::Value(policy::GetOSArchitecture())); - base::JSONWriter::Write(os_info, request->mutable_os_info()); +void AppendPlatformInformation(em::ChromeDesktopReportRequest* request, + const PrefService* prefs) { + if (prefs->GetBoolean(enterprise_reporting::kReportVersionData)) { + base::Value os_info = base::Value(base::Value::Type::DICTIONARY); + os_info.SetKey(kOS, base::Value(policy::GetOSPlatform())); + os_info.SetKey(kOSVersion, base::Value(policy::GetOSVersion())); + os_info.SetKey(kOSArch, base::Value(policy::GetOSArchitecture())); + base::JSONWriter::Write(os_info, request->mutable_os_info()); + } - base::Value machine_name = base::Value(base::Value::Type::DICTIONARY); - machine_name.SetKey(kComputerName, base::Value(policy::GetMachineName())); - base::JSONWriter::Write(machine_name, request->mutable_machine_name()); + if (prefs->GetBoolean(enterprise_reporting::kReportMachineIDData)) { + const char kComputerName[] = "computername"; + base::Value machine_name = base::Value(base::Value::Type::DICTIONARY); + machine_name.SetKey(kComputerName, base::Value(policy::GetMachineName())); + base::JSONWriter::Write(machine_name, request->mutable_machine_name()); + } - base::Value os_user = base::Value(base::Value::Type::DICTIONARY); - os_user.SetKey(kUsername, base::Value(policy::GetOSUsername())); - base::JSONWriter::Write(os_user, request->mutable_os_user()); + if (prefs->GetBoolean(enterprise_reporting::kReportUserIDData)) { + const char kUsername[] = "username"; + base::Value os_user = base::Value(base::Value::Type::DICTIONARY); + os_user.SetKey(kUsername, base::Value(policy::GetOSUsername())); + base::JSONWriter::Write(os_user, request->mutable_os_user()); + } } std::unique_ptr<em::ChromeUserProfileReport> -GenerateChromeUserProfileReportRequest(const base::Value& profile_report) { +GenerateChromeUserProfileReportRequest(const base::Value& profile_report, + const PrefService* prefs) { if (!profile_report.is_dict()) return nullptr; std::unique_ptr<em::ChromeUserProfileReport> request = std::make_unique<em::ChromeUserProfileReport>(); - if (!UpdateJSONEncodedStringEntry(profile_report, kChromeSignInUser, - request->mutable_chrome_signed_in_user(), - DICTIONARY) || - !UpdateJSONEncodedStringEntry(profile_report, kExtensionData, + if (prefs->GetBoolean(enterprise_reporting::kReportUserIDData)) { + if (!UpdateJSONEncodedStringEntry(profile_report, kChromeSignInUser, + request->mutable_chrome_signed_in_user(), + DICTIONARY)) { + return nullptr; + } + } + + if (!UpdateJSONEncodedStringEntry(profile_report, kExtensionData, request->mutable_extension_data(), LIST) || !UpdateJSONEncodedStringEntry(profile_report, kPlugins, request->mutable_plugins(), LIST)) { @@ -197,7 +221,9 @@ GenerateChromeDesktopReportRequest(const base::DictionaryValue& report, std::unique_ptr<em::ChromeDesktopReportRequest> request = std::make_unique<em::ChromeDesktopReportRequest>(); - AppendPlatformInformation(request.get()); + const PrefService* prefs = profile->GetPrefs(); + + AppendPlatformInformation(request.get(), prefs); if (const base::Value* browser_report = report.FindKeyOfType(kBrowserReport, base::Value::Type::DICTIONARY)) { @@ -208,7 +234,7 @@ GenerateChromeDesktopReportRequest(const base::DictionaryValue& report, // Currently, profile send their browser reports individually. std::unique_ptr<em::ChromeUserProfileReport> profile_report_request = GenerateChromeUserProfileReportRequest( - profile_reports->GetList()[0]); + profile_reports->GetList()[0], prefs); if (!profile_report_request) return nullptr; request->mutable_browser_report() diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper_unittest.cc index 97d58f1c3ac..2c669521c57 100644 --- a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper_unittest.cc +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/chrome_desktop_report_request_helper_unittest.cc @@ -7,6 +7,7 @@ #include "base/json/json_reader.h" #include "base/json/string_escape.h" #include "base/values.h" +#include "chrome/browser/extensions/api/enterprise_reporting_private/prefs.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/testing_profile.h" #include "components/policy/core/common/cloud/cloud_policy_util.h" @@ -19,6 +20,20 @@ namespace em = enterprise_management; namespace extensions { +namespace { + +base::DictionaryValue CreateReport(base::Value profile_report) { + base::Value profile_reports(base::Value::Type::LIST); + profile_reports.GetList().push_back(std::move(profile_report)); + + base::DictionaryValue report; + report.SetPath({"browserReport", "chromeUserProfileReport"}, + std::move(profile_reports)); + return report; +} + +} // namespace + class ChromeDesktopReportRequestGeneratorTest : public ::testing::Test { protected: content::TestBrowserThreadBundle test_browser_thread_bundle_; @@ -183,4 +198,67 @@ TEST_F(ChromeDesktopReportRequestGeneratorTest, SafeBrowsing) { .safe_browsing_warnings_click_through()); } +TEST_F(ChromeDesktopReportRequestGeneratorTest, DontReportVersionData) { + PrefService* prefs = profile_.GetPrefs(); + prefs->SetBoolean(enterprise_reporting::kReportVersionData, false); + + std::unique_ptr<em::ChromeDesktopReportRequest> request; + + request = + GenerateChromeDesktopReportRequest(base::DictionaryValue(), &profile_); + + ASSERT_TRUE(request); + EXPECT_FALSE(request->has_os_info()); + EXPECT_FALSE(request->browser_report().has_browser_version()); + EXPECT_FALSE(request->browser_report().has_channel()); +} + +TEST_F(ChromeDesktopReportRequestGeneratorTest, DontReportPolicyData) { + PrefService* prefs = profile_.GetPrefs(); + prefs->SetBoolean(enterprise_reporting::kReportPolicyData, false); + + std::unique_ptr<em::ChromeDesktopReportRequest> request = + GenerateChromeDesktopReportRequest(base::DictionaryValue(), &profile_); + + ASSERT_TRUE(request); + const em::ChromeUserProfileReport& profile = + request->browser_report().chrome_user_profile_reports(0); + EXPECT_FALSE(profile.has_policy_data()); + EXPECT_FALSE(profile.has_policy_fetched_timestamp()); +} + +TEST_F(ChromeDesktopReportRequestGeneratorTest, DontReportMachineIDData) { + PrefService* prefs = profile_.GetPrefs(); + prefs->SetBoolean(enterprise_reporting::kReportMachineIDData, false); + + std::unique_ptr<em::ChromeDesktopReportRequest> request = + GenerateChromeDesktopReportRequest(base::DictionaryValue(), &profile_); + + ASSERT_TRUE(request); + EXPECT_FALSE(request->has_machine_name()); +} + +TEST_F(ChromeDesktopReportRequestGeneratorTest, DontReportUserIDData) { + PrefService* prefs = profile_.GetPrefs(); + prefs->SetBoolean(enterprise_reporting::kReportUserIDData, false); + + base::Value profile_report(base::Value::Type::DICTIONARY); + profile_report.SetPath({"chromeSignInUser", "email"}, + base::Value("john_doe@gmail.com")); + profile_report.SetPath({"chromeSignInUser", "id"}, base::Value("123456789")); + + std::unique_ptr<em::ChromeDesktopReportRequest> request = + GenerateChromeDesktopReportRequest( + CreateReport(std::move(profile_report)), &profile_); + + ASSERT_TRUE(request); + EXPECT_FALSE(request->has_os_user()); + EXPECT_FALSE(request->browser_report().has_executable_path()); + const em::ChromeUserProfileReport& profile = + request->browser_report().chrome_user_profile_reports(0); + EXPECT_FALSE(profile.has_id()); + EXPECT_FALSE(profile.has_name()); + EXPECT_FALSE(profile.has_chrome_signed_in_user()); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.cc new file mode 100644 index 00000000000..50cf0293da0 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.cc @@ -0,0 +1,40 @@ +// Copyright 2018 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_reporting_private/enterprise_reporting_policy_migrator.h" + +#include "components/policy/policy_constants.h" + +namespace extensions { +namespace enterprise_reporting { + +namespace { + +// Extension ID for the Chrome Reporting Extension. +// https://chrome.google.com/webstore/detail/chrome-reporting-extensio/emahakmocgideepebncgnmlmliepgpgb +const char kStableExtensionId[] = "emahakmocgideepebncgnmlmliepgpgb"; + +// Beta extension ID. +const char kBetaExtensionId[] = "kigjhoekjcpdfjpimbdjegmgecmlicaf"; + +const policy::ExtensionPolicyMigrator::Migration kMigrations[] = { + {"report_version_data", policy::key::kReportVersionData}, + {"report_policy_data", policy::key::kReportPolicyData}, + {"report_machine_id_data", policy::key::kReportMachineIDData}, + {"report_user_id_data", policy::key::kReportUserIDData}, +}; + +} // namespace + +EnterpriseReportingPolicyMigrator::EnterpriseReportingPolicyMigrator() {} + +EnterpriseReportingPolicyMigrator::~EnterpriseReportingPolicyMigrator() {} + +void EnterpriseReportingPolicyMigrator::Migrate(policy::PolicyBundle* bundle) { + CopyPoliciesIfUnset(bundle, kStableExtensionId, kMigrations); + CopyPoliciesIfUnset(bundle, kBetaExtensionId, kMigrations); +} + +} // namespace enterprise_reporting +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.h b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.h new file mode 100644 index 00000000000..f08a41a3944 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator.h @@ -0,0 +1,25 @@ +// Copyright 2018 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_REPORTING_PRIVATE_ENTERPRISE_REPORTING_POLICY_MIGRATOR_H_ +#define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_ENTERPRISE_REPORTING_POLICY_MIGRATOR_H_ + +#include "components/policy/core/common/extension_policy_migrator.h" + +namespace extensions { +namespace enterprise_reporting { + +class EnterpriseReportingPolicyMigrator + : public policy::ExtensionPolicyMigrator { + public: + EnterpriseReportingPolicyMigrator(); + ~EnterpriseReportingPolicyMigrator() override; + + void Migrate(policy::PolicyBundle* bundle) override; +}; + +} // namespace enterprise_reporting +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_ENTERPRISE_REPORTING_POLICY_MIGRATOR_H_ diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator_unittest.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator_unittest.cc new file mode 100644 index 00000000000..2b40d89276d --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/enterprise_reporting_policy_migrator_unittest.cc @@ -0,0 +1,72 @@ +// Copyright 2018 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_reporting_private/enterprise_reporting_policy_migrator.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "components/policy/policy_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace extensions { +namespace enterprise_reporting { + +namespace { + +const char kStableExtensionId[] = "emahakmocgideepebncgnmlmliepgpgb"; +const char kBetaExtensionId[] = "kigjhoekjcpdfjpimbdjegmgecmlicaf"; + +const policy::ExtensionPolicyMigrator::Migration kMigrations[] = { + {"report_version_data", policy::key::kReportVersionData}, + {"report_policy_data", policy::key::kReportPolicyData}, + {"report_machine_id_data", policy::key::kReportMachineIDData}, + {"report_user_id_data", policy::key::kReportUserIDData}, +}; + +void SetPolicy(policy::PolicyMap* policy, + const char* policy_name, + std::unique_ptr<base::Value> value) { + policy->Set(policy_name, policy::POLICY_LEVEL_MANDATORY, + policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD, + std::move(value), nullptr); +} + +} // namespace + +TEST(EnterpriseReportingPolicyMigratorTest, Migrate) { + policy::PolicyBundle bundle; + + policy::PolicyMap& stable_extension_map = bundle.Get(policy::PolicyNamespace( + policy::POLICY_DOMAIN_EXTENSIONS, kStableExtensionId)); + SetPolicy(&stable_extension_map, kMigrations[0].old_name, + std::make_unique<base::Value>(false)); + SetPolicy(&stable_extension_map, kMigrations[1].old_name, + std::make_unique<base::Value>(false)); + SetPolicy(&stable_extension_map, kMigrations[2].old_name, + std::make_unique<base::Value>(false)); + + policy::PolicyMap& beta_extension_map = bundle.Get(policy::PolicyNamespace( + policy::POLICY_DOMAIN_EXTENSIONS, kBetaExtensionId)); + SetPolicy(&beta_extension_map, kMigrations[2].old_name, + std::make_unique<base::Value>(true)); + SetPolicy(&beta_extension_map, kMigrations[3].old_name, + std::make_unique<base::Value>(true)); + + EnterpriseReportingPolicyMigrator().Migrate(&bundle); + + policy::PolicyMap& chrome_map = bundle.Get(policy::PolicyNamespace( + policy::POLICY_DOMAIN_CHROME, /* component_id */ std::string())); + + EXPECT_EQ(4u, chrome_map.size()); + EXPECT_EQ(base::Value(false), *chrome_map.GetValue(kMigrations[0].new_name)); + EXPECT_EQ(base::Value(false), *chrome_map.GetValue(kMigrations[1].new_name)); + // Stable takes priority over Beta, when the policy is set. + EXPECT_EQ(base::Value(false), *chrome_map.GetValue(kMigrations[2].new_name)); + EXPECT_EQ(base::Value(true), *chrome_map.GetValue(kMigrations[3].new_name)); +} + +} // namespace enterprise_reporting +} // 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 4d3ed0a610f..b7ac4535e8d 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 @@ -19,7 +19,6 @@ #include "components/policy/core/common/cloud/cloud_policy_client.h" #include "components/policy/core/common/cloud/device_management_service.h" #include "components/policy/proto/device_management_backend.pb.h" -#include "net/url_request/url_request_context_getter.h" #include "services/network/public/cpp/shared_url_loader_factory.h" namespace em = enterprise_management; @@ -60,7 +59,7 @@ EnterpriseReportingPrivateUploadChromeDesktopReportFunction:: cloud_policy_client_ = std::make_unique<policy::CloudPolicyClient>( std::string() /* machine_id */, std::string() /* machine_model */, std::string() /* brand_code */, device_management_service, - g_browser_process->system_request_context(), url_loader_factory, nullptr, + std::move(url_loader_factory), nullptr, policy::CloudPolicyClient::DeviceDMTokenCallback()); dm_token_ = policy::BrowserDMTokenStorage::Get()->RetrieveDMToken(); client_id_ = policy::BrowserDMTokenStorage::Get()->RetrieveClientId(); 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 6cd8da022ff..58dfc380424 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 @@ -27,7 +27,9 @@ const char kFakeMachineNameReport[] = "{\"computername\":\"name\"}"; class MockCloudPolicyClient : public policy::MockCloudPolicyClient { public: - MockCloudPolicyClient() = default; + explicit MockCloudPolicyClient( + scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) + : policy::MockCloudPolicyClient(std::move(url_loader_factory)) {} void UploadChromeDesktopReport( std::unique_ptr<enterprise_management::ChromeDesktopReportRequest> @@ -69,8 +71,8 @@ class EnterpriseReportingPrivateTest : public ExtensionApiUnittest { EnterpriseReportingPrivateUploadChromeDesktopReportFunction* function = EnterpriseReportingPrivateUploadChromeDesktopReportFunction:: CreateForTesting(test_shared_loader_factory_); - std::unique_ptr<MockCloudPolicyClient> client = - std::make_unique<MockCloudPolicyClient>(); + auto client = + std::make_unique<MockCloudPolicyClient>(test_shared_loader_factory_); client_ = client.get(); function->SetCloudPolicyClientForTesting(std::move(client)); function->SetRegistrationInfoForTesting(dm_token, kFakeClientId); diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.cc b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.cc new file mode 100644 index 00000000000..09239d5b5d5 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.cc @@ -0,0 +1,29 @@ +// Copyright 2018 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_reporting_private/prefs.h" + +#include "components/pref_registry/pref_registry_syncable.h" + +namespace extensions { +namespace enterprise_reporting { + +const char kReportVersionData[] = "enterprise_reporting.report_version_data"; + +const char kReportPolicyData[] = "enterprise_reporting.report_policy_data"; + +const char kReportMachineIDData[] = + "enterprise_reporting.report_machine_id_data"; + +const char kReportUserIDData[] = "enterprise_reporting.report_user_id_data"; + +void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) { + registry->RegisterBooleanPref(kReportVersionData, true); + registry->RegisterBooleanPref(kReportPolicyData, true); + registry->RegisterBooleanPref(kReportMachineIDData, true); + registry->RegisterBooleanPref(kReportUserIDData, true); +} + +} // namespace enterprise_reporting +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.h b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.h new file mode 100644 index 00000000000..8471671b8a4 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/enterprise_reporting_private/prefs.h @@ -0,0 +1,32 @@ +// Copyright 2018 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_REPORTING_PRIVATE_PREFS_H_ +#define CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_PREFS_H_ + +namespace user_prefs { +class PrefRegistrySyncable; +} // namespace user_prefs + +namespace extensions { +namespace enterprise_reporting { + +// Controls reporting of OS/Chrome version information. +extern const char kReportVersionData[]; + +// Controls reporting of Chrome policy data and policy fetch timestamps. +extern const char kReportPolicyData[]; + +// Controls reporting of information that can identify machines. +extern const char kReportMachineIDData[]; + +// Controls reporting of information that can identify users. +extern const char kReportUserIDData[]; + +void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry); + +} // namespace enterprise_reporting +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_ENTERPRISE_REPORTING_PRIVATE_PREFS_H_ 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 d7bd3505ef5..cac23b180ec 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 @@ -9,6 +9,8 @@ #include "base/files/scoped_temp_dir.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "chrome/browser/download/download_prefs.h" @@ -34,6 +36,7 @@ #include "components/prefs/pref_service.h" #include "content/public/browser/browser_context.h" #include "content/public/browser/notification_service.h" +#include "content/public/browser/picture_in_picture_window_controller.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/web_contents.h" #include "content/public/test/browser_test_utils.h" @@ -368,6 +371,60 @@ IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, DynamicBrowserAction) { EXPECT_EQ(kEmptyPathError, catcher.message()); } +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, InvisibleIconBrowserAction) { + // Turn this on so errors are reported. + ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting( + true); + ASSERT_TRUE(RunExtensionTest("browser_action/invisible_icon")) << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + // Test there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); + EXPECT_TRUE(GetBrowserActionsBar()->HasIcon(0)); + gfx::Image initial_bar_icon = GetBrowserActionsBar()->GetIcon(0); + + ExtensionHost* background_page = + ProcessManager::Get(profile())->GetBackgroundHostForExtension( + extension->id()); + ASSERT_TRUE(background_page); + + static constexpr char kScript[] = + "setIcon(%s).then(function(arg) {" + " domAutomationController.send(arg);" + "});"; + + const std::string histogram_name = + "Extensions.DynamicExtensionActionIconWasVisible"; + { + base::HistogramTester histogram_tester; + std::string result; + EXPECT_TRUE(ExecuteScriptAndExtractString( + background_page->host_contents(), + base::StringPrintf(kScript, "invisible"), &result)); + EXPECT_EQ("Icon not sufficiently visible.", result); + // The icon should not have changed. + EXPECT_TRUE(gfx::test::AreImagesEqual(initial_bar_icon, + GetBrowserActionsBar()->GetIcon(0))); + EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), + testing::ElementsAre(base::Bucket(0, 1))); + } + + { + base::HistogramTester histogram_tester; + std::string result; + EXPECT_TRUE(ExecuteScriptAndExtractString( + background_page->host_contents(), + base::StringPrintf(kScript, "visible"), &result)); + EXPECT_EQ("", result); + // The icon should have changed. + EXPECT_FALSE(gfx::test::AreImagesEqual(initial_bar_icon, + GetBrowserActionsBar()->GetIcon(0))); + EXPECT_THAT(histogram_tester.GetAllSamples(histogram_name), + testing::ElementsAre(base::Bucket(1, 1))); + } +} + IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, TabSpecificBrowserActionState) { ASSERT_TRUE(RunExtensionTest("browser_action/tab_specific_state")) << message_; @@ -1059,5 +1116,48 @@ IN_PROC_BROWSER_TEST_F(NavigatingExtensionPopupBrowserTest, DownloadViaPost) { #endif } +// Verify video can enter and exit Picture-in_Picture when browser action icon +// is clicked. +IN_PROC_BROWSER_TEST_F(BrowserActionApiTest, + TestPictureInPictureOnBrowserActionIconClick) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + ASSERT_TRUE( + RunExtensionTest("trigger_actions/browser_action_picture_in_picture")) + << message_; + const Extension* extension = GetSingleLoadedExtension(); + ASSERT_TRUE(extension) << message_; + + // Test that there is a browser action in the toolbar. + ASSERT_EQ(1, GetBrowserActionsBar()->NumberOfBrowserActions()); + + ExtensionAction* browser_action = GetBrowserAction(*extension); + EXPECT_TRUE(browser_action); + + // Find the background page. + ProcessManager* process_manager = + extensions::ProcessManager::Get(browser()->profile()); + content::WebContents* web_contents = + process_manager->GetBackgroundHostForExtension(extension->id()) + ->web_contents(); + ASSERT_TRUE(web_contents); + content::PictureInPictureWindowController* window_controller = + content::PictureInPictureWindowController::GetOrCreateForWebContents( + web_contents); + ASSERT_TRUE(window_controller->GetWindowForTesting()); + EXPECT_FALSE(window_controller->GetWindowForTesting()->IsVisible()); + + // Click on the browser action icon to enter Picture-in-Picture. + ResultCatcher catcher; + GetBrowserActionsBar()->Press(0); + EXPECT_TRUE(catcher.GetNextResult()); + EXPECT_TRUE(window_controller->GetWindowForTesting()->IsVisible()); + + // Click on the browser action icon to exit Picture-in-Picture. + GetBrowserActionsBar()->Press(0); + EXPECT_TRUE(catcher.GetNextResult()); + EXPECT_FALSE(window_controller->GetWindowForTesting()->IsVisible()); +} + } // namespace } // namespace extensions 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 9e4a41060ab..a12b3d54afa 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/lazy_instance.h" #include "base/location.h" #include "base/macros.h" +#include "base/metrics/histogram_macros.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -59,6 +60,8 @@ const char kOpenPopupError[] = const char kInvalidColorError[] = "The color specification could not be parsed."; +bool g_report_error_for_invisible_icon = false; + } // namespace // @@ -421,6 +424,12 @@ ExtensionActionHideFunction::RunExtensionAction() { return RespondNow(NoArguments()); } +// static +void ExtensionActionSetIconFunction::SetReportErrorForInvisibleIconForTesting( + bool value) { + g_report_error_for_invisible_icon = value; +} + ExtensionFunction::ResponseAction ExtensionActionSetIconFunction::RunExtensionAction() { EXTENSION_FUNCTION_VALIDATE(details_); @@ -438,7 +447,17 @@ ExtensionActionSetIconFunction::RunExtensionAction() { if (icon.isNull()) return RespondNow(Error("Icon invalid.")); - extension_action_->SetIcon(tab_id_, gfx::Image(icon)); + gfx::Image icon_image(icon); + + const bool is_visible = + image_util::IsIconSufficientlyVisible(icon_image.AsBitmap()); + UMA_HISTOGRAM_BOOLEAN("Extensions.DynamicExtensionActionIconWasVisible", + is_visible); + + if (!is_visible && g_report_error_for_invisible_icon) + return RespondNow(Error("Icon not sufficiently visible.")); + + extension_action_->SetIcon(tab_id_, icon_image); } else if (details_->GetInteger("iconIndex", &icon_index)) { // Obsolete argument: ignore it. return RespondNow(NoArguments()); diff --git a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.h b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.h index 051518beed5..a9c08ccb71e 100644 --- a/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.h +++ b/chromium/chrome/browser/extensions/api/extension_action/extension_action_api.h @@ -128,7 +128,7 @@ class ExtensionActionAPI : public BrowserContextKeyedAPI { static const char* service_name() { return "ExtensionActionAPI"; } static const bool kServiceRedirectedInIncognito = true; - base::ObserverList<Observer> observers_; + base::ObserverList<Observer>::Unchecked observers_; content::BrowserContext* browser_context_; @@ -197,6 +197,9 @@ class ExtensionActionHideFunction : public ExtensionActionFunction { // setIcon class ExtensionActionSetIconFunction : public ExtensionActionFunction { + public: + static void SetReportErrorForInvisibleIconForTesting(bool value); + protected: ~ExtensionActionSetIconFunction() override {} ResponseAction RunExtensionAction() override; 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 f12831ad5e7..c4229c5e970 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 @@ -6,9 +6,9 @@ #include <string> #include "base/macros.h" -#include "base/run_loop.h" #include "base/scoped_observer.h" #include "base/strings/stringprintf.h" +#include "chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h" #include "chrome/browser/extensions/extension_action.h" #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_apitest.h" @@ -57,42 +57,6 @@ class TestStateStoreObserver : public StateStore::TestObserver { DISALLOW_COPY_AND_ASSIGN(TestStateStoreObserver); }; -// A helper class to observe ExtensionActionAPI changes. -class TestExtensionActionAPIObserver : public ExtensionActionAPI::Observer { - public: - TestExtensionActionAPIObserver(content::BrowserContext* context, - const std::string& extension_id) - : extension_id_(extension_id), scoped_observer_(this) { - scoped_observer_.Add(ExtensionActionAPI::Get(context)); - } - ~TestExtensionActionAPIObserver() override {} - - void OnExtensionActionUpdated( - ExtensionAction* extension_action, - content::WebContents* web_contents, - content::BrowserContext* browser_context) override { - if (extension_action->extension_id() == extension_id_) { - last_web_contents_ = web_contents; - run_loop_.QuitWhenIdle(); - } - } - - const content::WebContents* last_web_contents() const { - return last_web_contents_; - } - - void Wait() { run_loop_.Run(); } - - private: - content::WebContents* last_web_contents_ = nullptr; - std::string extension_id_; - base::RunLoop run_loop_; - ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer> - scoped_observer_; - - DISALLOW_COPY_AND_ASSIGN(TestExtensionActionAPIObserver); -}; - } // namespace using ExtensionActionAPITest = ExtensionApiTest; diff --git a/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.cc b/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.cc new file mode 100644 index 00000000000..2083a406aa9 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.cc @@ -0,0 +1,32 @@ +// Copyright 2018 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/test_extension_action_api_observer.h" + +namespace extensions { + +TestExtensionActionAPIObserver::TestExtensionActionAPIObserver( + content::BrowserContext* context, + const ExtensionId& extension_id) + : extension_id_(extension_id), scoped_observer_(this) { + scoped_observer_.Add(ExtensionActionAPI::Get(context)); +} + +TestExtensionActionAPIObserver::~TestExtensionActionAPIObserver() = default; + +void TestExtensionActionAPIObserver::Wait() { + run_loop_.Run(); +} + +void TestExtensionActionAPIObserver::OnExtensionActionUpdated( + ExtensionAction* extension_action, + content::WebContents* web_contents, + content::BrowserContext* browser_context) { + if (extension_action->extension_id() == extension_id_) { + last_web_contents_ = web_contents; + run_loop_.QuitWhenIdle(); + } +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h b/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h new file mode 100644 index 00000000000..11b35f5b279 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h @@ -0,0 +1,55 @@ +// Copyright 2018 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_EXTENSION_ACTION_TEST_EXTENSION_ACTION_API_OBSERVER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_TEST_EXTENSION_ACTION_API_OBSERVER_H_ + +#include "base/macros.h" +#include "base/run_loop.h" +#include "base/scoped_observer.h" +#include "chrome/browser/extensions/api/extension_action/extension_action_api.h" +#include "extensions/common/extension_id.h" + +namespace content { +class BrowserContext; +class WebContents; +} // namespace content + +namespace extensions { + +// A helper class to observe ExtensionActionAPI changes. +class TestExtensionActionAPIObserver : public ExtensionActionAPI::Observer { + public: + TestExtensionActionAPIObserver(content::BrowserContext* context, + const ExtensionId& extension_id); + ~TestExtensionActionAPIObserver() override; + + // Waits till the extension action is updated. + void Wait(); + + // Returns the web contents for which the extension action was updated. Must + // be called after calling Wait(). + const content::WebContents* last_web_contents() const { + return last_web_contents_; + } + + private: + // ExtensionActionAPI::Observer override: + void OnExtensionActionUpdated( + ExtensionAction* extension_action, + content::WebContents* web_contents, + content::BrowserContext* browser_context) override; + + content::WebContents* last_web_contents_ = nullptr; + ExtensionId extension_id_; + base::RunLoop run_loop_; + ScopedObserver<ExtensionActionAPI, ExtensionActionAPI::Observer> + scoped_observer_; + + DISALLOW_COPY_AND_ASSIGN(TestExtensionActionAPIObserver); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_EXTENSION_ACTION_TEST_EXTENSION_ACTION_API_OBSERVER_H_ diff --git a/chromium/chrome/browser/extensions/api/file_system/DEPS b/chromium/chrome/browser/extensions/api/file_system/DEPS index b9a9cec2c20..9bf73e30601 100644 --- a/chromium/chrome/browser/extensions/api/file_system/DEPS +++ b/chromium/chrome/browser/extensions/api/file_system/DEPS @@ -1,9 +1,3 @@ include_rules = [ "+chrome/browser/ui/views/extensions", ] - -specific_include_rules = { - "file_system_apitest_chromeos\.cc": [ - "+components/drive" - ], -} diff --git a/chromium/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc b/chromium/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc index 1657c78bb85..3c640ff82b5 100644 --- a/chromium/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc +++ b/chromium/chrome/browser/extensions/api/identity/gaia_web_auth_flow.cc @@ -11,7 +11,7 @@ #include "base/strings/stringprintf.h" #include "base/trace_event/trace_event.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/signin/chrome_signin_client_factory.h" +#include "chrome/browser/signin/chrome_device_id_helper.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/signin/signin_manager_factory.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" @@ -58,11 +58,8 @@ GaiaWebAuthFlow::GaiaWebAuthFlow(Delegate* delegate, std::reverse(client_id_parts.begin(), client_id_parts.end()); redirect_scheme_ = base::JoinString(client_id_parts, "."); std::string signin_scoped_device_id; - // profile_ can be nullptr in unittests. - SigninClient* signin_client = - profile_ ? ChromeSigninClientFactory::GetForProfile(profile_) : nullptr; - if (signin_client) - signin_scoped_device_id = signin_client->GetSigninScopedDeviceId(); + if (profile_) + signin_scoped_device_id = GetSigninScopedDeviceIdForProfile(profile_); redirect_path_prefix_ = base::StringPrintf(kOAuth2RedirectPathFormat, token_key->extension_id.c_str()); diff --git a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc index 574f1a83551..be2c71148f1 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_apitest.cc @@ -65,6 +65,7 @@ #include "net/test/embedded_test_server/embedded_test_server.h" #include "services/identity/public/cpp/identity_manager.h" #include "services/identity/public/cpp/identity_test_utils.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "url/gurl.h" @@ -169,7 +170,7 @@ class TestHangOAuth2MintTokenFlow : public OAuth2MintTokenFlow { TestHangOAuth2MintTokenFlow() : OAuth2MintTokenFlow(NULL, OAuth2MintTokenFlow::Parameters()) {} - void Start(net::URLRequestContextGetter* context, + void Start(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, const std::string& access_token) override { // Do nothing, simulating a hanging network call. } @@ -191,7 +192,7 @@ class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow { result_(result), delegate_(delegate) {} - void Start(net::URLRequestContextGetter* context, + void Start(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory, const std::string& access_token) override { switch (result_) { case ISSUE_ADVICE_SUCCESS: { @@ -465,9 +466,6 @@ class IdentityTestWithSignin : public AsyncExtensionBrowserTest { ProfileOAuth2TokenServiceFactory::GetInstance()->GetForProfile( profile())); ASSERT_TRUE(token_service_); - GaiaCookieManagerServiceFactory::GetInstance() - ->GetForProfile(profile()) - ->Init(); #if defined(OS_CHROMEOS) // On ChromeOS, ProfileOAuth2TokenService does not fire @@ -862,6 +860,19 @@ IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, EXPECT_TRUE(func->login_ui_shown()); EXPECT_FALSE(func->scope_ui_shown()); } + +IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, + InteractiveNotSignedAndSigninNotAllowed) { + browser()->profile()->GetPrefs()->SetBoolean(prefs::kSigninAllowed, false); + scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction()); + func->set_extension(CreateExtension(CLIENT_ID | SCOPES)); + func->set_login_ui_result(false); + std::string error = utils::RunFunctionAndReturnError( + func.get(), "[{\"interactive\": true}]", browser()); + EXPECT_EQ(std::string(errors::kBrowserSigninNotAllowed), error); + EXPECT_FALSE(func->login_ui_shown()); + EXPECT_FALSE(func->scope_ui_shown()); +} #endif IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, @@ -1936,21 +1947,13 @@ class GetAuthTokenFunctionPublicSessionTest : public GetAuthTokenFunctionTest { protected: void SetUpInProcessBrowserTestFixture() override { - GetAuthTokenFunctionTest::SetUpInProcessBrowserTestFixture(); - - // Set up the user manager to fake a public session. - EXPECT_CALL(*user_manager_, IsLoggedInAsKioskApp()) - .WillRepeatedly(Return(false)); - EXPECT_CALL(*user_manager_, IsLoggedInAsPublicAccount()) - .WillRepeatedly(Return(true)); - - // Set up fake install attributes to make the device appeared as - // enterprise-managed. - std::unique_ptr<chromeos::StubInstallAttributes> attributes = - std::make_unique<chromeos::StubInstallAttributes>(); - attributes->SetCloudManaged("example.com", "fake-id"); - policy::BrowserPolicyConnectorChromeOS::SetInstallAttributesForTesting( - attributes.release()); + GetAuthTokenFunctionTest::SetUpInProcessBrowserTestFixture(); + + // Set up the user manager to fake a public session. + EXPECT_CALL(*user_manager_, IsLoggedInAsKioskApp()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*user_manager_, IsLoggedInAsPublicAccount()) + .WillRepeatedly(Return(true)); } scoped_refptr<Extension> CreateTestExtension(const std::string& id) { @@ -1964,6 +1967,12 @@ class GetAuthTokenFunctionPublicSessionTest : public GetAuthTokenFunctionTest { .Build(); } + // Set up fake install attributes to make the device appeared as + // enterprise-managed. + chromeos::ScopedStubInstallAttributes test_install_attributes_{ + chromeos::StubInstallAttributes::CreateCloudManaged("example.com", + "fake-id")}; + // Owned by |user_manager_enabler|. chromeos::MockUserManager* user_manager_; }; diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc index 8676090894f..fefac477308 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_constants.cc +++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.cc @@ -13,6 +13,7 @@ const char kAuthFailure[] = "OAuth2 request failed: "; const char kNoGrant[] = "OAuth2 not granted or revoked."; const char kUserRejected[] = "The user did not approve access."; const char kUserNotSignedIn[] = "The user is not signed in."; +const char kBrowserSigninNotAllowed[] = "The user turned off browser signin"; const char kInteractionRequired[] = "User interaction required."; const char kInvalidRedirect[] = "Did not redirect to the right URL."; const char kOffTheRecord[] = "Identity API is disabled in incognito windows."; diff --git a/chromium/chrome/browser/extensions/api/identity/identity_constants.h b/chromium/chrome/browser/extensions/api/identity/identity_constants.h index 8fc01ff187d..e849ccd2222 100644 --- a/chromium/chrome/browser/extensions/api/identity/identity_constants.h +++ b/chromium/chrome/browser/extensions/api/identity/identity_constants.h @@ -14,6 +14,7 @@ extern const char kAuthFailure[]; extern const char kNoGrant[]; extern const char kUserRejected[]; extern const char kUserNotSignedIn[]; +extern const char kBrowserSigninNotAllowed[]; extern const char kInteractionRequired[]; extern const char kInvalidRedirect[]; extern const char kOffTheRecord[]; 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 2549540f7f9..41b2cc15afb 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 @@ -16,14 +16,16 @@ #include "chrome/browser/extensions/api/identity/identity_constants.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/account_tracker_service_factory.h" -#include "chrome/browser/signin/chrome_signin_client_factory.h" +#include "chrome/browser/signin/chrome_device_id_helper.h" #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" #include "chrome/browser/ui/webui/signin/login_ui_service.h" #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" #include "chrome/common/extensions/api/identity.h" +#include "components/prefs/pref_service.h" #include "components/signin/core/browser/account_tracker_service.h" #include "components/signin/core/browser/profile_management_switches.h" #include "components/signin/core/browser/profile_oauth2_token_service.h" +#include "components/signin/core/browser/signin_pref_names.h" #include "content/public/common/service_manager_connection.h" #include "extensions/common/extension_l10n_util.h" #include "google_apis/gaia/gaia_urls.h" @@ -56,6 +58,10 @@ const char* const kPublicSessionAllowedOrigins[] = { "chrome-extension://gbchcmhmhahfdphkhkmpfmihenigjmpp/"}; #endif +bool IsBrowserSigninAllowed(Profile* profile) { + return profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed); +} + } // namespace IdentityGetAuthTokenFunction::IdentityGetAuthTokenFunction() @@ -92,7 +98,8 @@ bool IdentityGetAuthTokenFunction::RunAsync() { *params->details->interactive; should_prompt_for_scopes_ = interactive_; - should_prompt_for_signin_ = interactive_; + should_prompt_for_signin_ = + interactive_ && IsBrowserSigninAllowed(GetProfile()); const OAuth2Info& oauth2_info = OAuth2Info::GetOAuth2Info(extension()); @@ -203,7 +210,10 @@ void IdentityGetAuthTokenFunction::OnReceivedExtensionAccountInfo( if (!account_state.has_refresh_token) { if (!ShouldStartSigninFlow()) { - CompleteFunctionWithError(identity_constants::kUserNotSignedIn); + CompleteFunctionWithError( + IsBrowserSigninAllowed(GetProfile()) + ? identity_constants::kUserNotSignedIn + : identity_constants::kBrowserSigninNotAllowed); return; } // Display a login prompt. @@ -593,10 +603,10 @@ void IdentityGetAuthTokenFunction::OnGetAccessTokenComplete( #if defined(OS_CHROMEOS) void IdentityGetAuthTokenFunction::OnGetTokenSuccess( const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) { + const OAuth2AccessTokenConsumer::TokenResponse& token_response) { login_token_request_.reset(); - OnGetAccessTokenComplete(access_token, expiration_time, + OnGetAccessTokenComplete(token_response.access_token, + token_response.expiration_time, GoogleServiceAuthError::AuthErrorNone()); } @@ -684,7 +694,7 @@ void IdentityGetAuthTokenFunction::StartGaiaRequest( const std::string& login_access_token) { DCHECK(!login_access_token.empty()); mint_token_flow_.reset(CreateMintTokenFlow()); - mint_token_flow_->Start(GetProfile()->GetRequestContext(), + mint_token_flow_->Start(GetProfile()->GetURLLoaderFactory(), login_access_token); } @@ -704,10 +714,8 @@ void IdentityGetAuthTokenFunction::ShowOAuthApprovalDialog( } OAuth2MintTokenFlow* IdentityGetAuthTokenFunction::CreateMintTokenFlow() { - SigninClient* signin_client = - ChromeSigninClientFactory::GetForProfile(GetProfile()); std::string signin_scoped_device_id = - signin_client->GetSigninScopedDeviceId(); + GetSigninScopedDeviceIdForProfile(GetProfile()); OAuth2MintTokenFlow* mint_token_flow = new OAuth2MintTokenFlow( this, OAuth2MintTokenFlow::Parameters( 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 ed99f975a37..ace71040bff 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 @@ -73,9 +73,9 @@ class IdentityGetAuthTokenFunction : public ChromeAsyncExtensionFunction, // OAuth2TokenService::Consumer. #if defined(OS_CHROMEOS) // OAuth2TokenService::Consumer implementation: - void OnGetTokenSuccess(const OAuth2TokenService::Request* request, - const std::string& access_token, - const base::Time& expiration_time) override; + void OnGetTokenSuccess( + const OAuth2TokenService::Request* request, + const OAuth2AccessTokenConsumer::TokenResponse& token_response) override; void OnGetTokenFailure(const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) override; #endif diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc b/chromium/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc index 793a0a5d6b2..6779605e610 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/destroy_partitions_operation.cc @@ -34,7 +34,7 @@ DestroyPartitionsOperation::~DestroyPartitionsOperation() {} void DestroyPartitionsOperation::StartImpl() { DCHECK(IsRunningInCorrectSequence()); - if (!base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &image_path_)) { + if (!base::CreateTemporaryFileInDir(temp_dir_->GetPath(), &image_path_)) { Error(error::kTempFileError); return; } 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 ab1999a5135..22830d6a2af 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,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/sequenced_task_runner.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/extensions/api/image_writer_private/operation.h" #include "chrome/services/removable_storage_writer/public/mojom/removable_storage_writer.mojom.h" 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 58739bb2437..125db2b83a5 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,7 @@ #include "base/files/file_enumerator.h" #include "base/files/file_util.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" #include "chrome/browser/extensions/api/image_writer_private/operation_manager.h" @@ -39,6 +39,7 @@ Operation::Operation(base::WeakPtr<OperationManager> manager, #else device_path_(device_path), #endif + temp_dir_(std::make_unique<base::ScopedTempDir>()), connector_(std::move(connector)), stage_(image_writer_api::STAGE_UNKNOWN), progress_(0), @@ -50,6 +51,11 @@ Operation::Operation(base::WeakPtr<OperationManager> manager, Operation::~Operation() { // The connector_ is bound to the |task_runner_| and must be deleted there. task_runner_->DeleteSoon(FROM_HERE, std::move(connector_)); + + // base::ScopedTempDir must be destroyed on a thread that allows blocking IO + // because it will try delete the directory if a call to Delete() hasn't been + // made or was unsuccessful. + task_runner_->DeleteSoon(FROM_HERE, std::move(temp_dir_)); } void Operation::Cancel() { @@ -81,9 +87,9 @@ void Operation::Start() { DCHECK(IsRunningInCorrectSequence()); #if defined(OS_CHROMEOS) if (download_folder_.empty() || - !temp_dir_.CreateUniqueTempDirUnderPath(download_folder_)) { + !temp_dir_->CreateUniqueTempDirUnderPath(download_folder_)) { #else - if (!temp_dir_.CreateUniqueTempDir()) { + if (!temp_dir_->CreateUniqueTempDir()) { #endif Error(error::kTempDirError); return; @@ -91,7 +97,7 @@ void Operation::Start() { AddCleanUpFunction( base::BindOnce(base::IgnoreResult(&base::ScopedTempDir::Delete), - base::Unretained(&temp_dir_))); + base::Unretained(temp_dir_.get()))); StartImpl(); } @@ -119,7 +125,7 @@ void Operation::Unzip(const base::Closure& continuation) { base::Bind(&Operation::CompleteAndContinue, this, continuation), base::Bind(&Operation::OnUnzipFailure, this), base::Bind(&Operation::OnUnzipProgress, this)); - unzip_helper->Unzip(image_path_, temp_dir_.GetPath()); + unzip_helper->Unzip(image_path_, temp_dir_->GetPath()); } void Operation::Finish() { diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation.h b/chromium/chrome/browser/extensions/api/image_writer_private/operation.h index f629f58d7e4..ba12212379a 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/operation.h +++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation.h @@ -16,12 +16,16 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/weak_ptr.h" #include "base/sequenced_task_runner.h" -#include "base/task_scheduler/task_traits.h" +#include "base/task/task_traits.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/image_writer_utility_client.h" #include "chrome/common/extensions/api/image_writer_private.h" #include "extensions/common/extension_id.h" +#if defined(OS_CHROMEOS) +#include "chromeos/disks/disk_mount_manager.h" +#endif + namespace image_writer_api = extensions::api::image_writer_private; namespace base { @@ -155,7 +159,7 @@ class Operation : public base::RefCountedThreadSafe<Operation> { base::FilePath device_path_; // Temporary directory to store files as we go. - base::ScopedTempDir temp_dir_; + std::unique_ptr<base::ScopedTempDir> temp_dir_; private: friend class base::RefCountedThreadSafe<Operation>; @@ -183,7 +187,8 @@ class Operation : public base::RefCountedThreadSafe<Operation> { // Unmounts all volumes on |device_path_|. void UnmountVolumes(const base::Closure& continuation); // Starts the write after unmounting. - void UnmountVolumesCallback(const base::Closure& continuation, bool success); + void UnmountVolumesCallback(const base::Closure& continuation, + chromeos::MountError error_code); // Starts the ImageBurner write. Note that target_path is the file path of // the device where device_path has been a system device path. void StartWriteOnUIThread(const std::string& target_path, 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 56b833274df..6817c9db7f2 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 @@ -8,6 +8,7 @@ #include "chrome/browser/extensions/api/image_writer_private/operation.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/image_burner_client.h" +#include "chromeos/disks/disk.h" #include "chromeos/disks/disk_mount_manager.h" #include "content/public/browser/browser_thread.h" @@ -61,11 +62,11 @@ void Operation::UnmountVolumes(const base::Closure& continuation) { } void Operation::UnmountVolumesCallback(const base::Closure& continuation, - bool success) { + chromeos::MountError error_code) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (!success) { - LOG(ERROR) << "Volume unmounting failed."; + if (error_code != chromeos::MOUNT_ERROR_NONE) { + LOG(ERROR) << "Volume unmounting failed with error code " << error_code; PostTask(base::Bind(&Operation::Error, this, error::kUnmountVolumesError)); return; } diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc index f923cc582c0..037e29ae338 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/operation_manager.cc @@ -86,11 +86,15 @@ void OperationManager::StartWriteFromUrl( return; } + network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_info; + content::BrowserContext::GetDefaultStoragePartition(browser_context_) + ->GetURLLoaderFactoryForBrowserProcess() + ->Clone(mojo::MakeRequest(&url_loader_factory_info)); + scoped_refptr<Operation> operation(new WriteFromUrlOperation( weak_factory_.GetWeakPtr(), CreateConnector(), extension_id, - content::BrowserContext::GetDefaultStoragePartition(browser_context_) - ->GetURLRequestContext(), - url, hash, device_path, GetAssociatedDownloadFolder())); + std::move(url_loader_factory_info), url, hash, device_path, + GetAssociatedDownloadFolder())); operations_[extension_id] = operation; operation->PostTask(base::BindOnce(&Operation::Start, operation)); diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider.cc index c16b847be5a..a0f60c4059b 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider.cc @@ -5,7 +5,7 @@ #include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h" #include "base/lazy_instance.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "content/public/browser/browser_thread.h" diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos.cc b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos.cc index c3599770427..525557ccce8 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/removable_storage_provider_chromeos.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "chrome/browser/extensions/api/image_writer_private/removable_storage_provider.h" + +#include "chromeos/disks/disk.h" #include "chromeos/disks/disk_mount_manager.h" namespace extensions { @@ -10,6 +12,7 @@ namespace extensions { const char kUnknownSDDiskModel[] = "SD Card"; const char kUnknownUSBDiskModel[] = "USB Drive"; +using chromeos::disks::Disk; using chromeos::disks::DiskMountManager; // The Chrome OS implementation takes advantage of the Chrome OS @@ -26,7 +29,7 @@ RemovableStorageProvider::PopulateDeviceList() { for (DiskMountManager::DiskMap::const_iterator iter = disks.begin(); iter != disks.end(); ++iter) { - const DiskMountManager::Disk& disk = *iter->second; + const Disk& disk = *iter->second; if (disk.is_parent() && !disk.on_boot_device() && disk.has_media() && (disk.device_type() == chromeos::DEVICE_TYPE_USB || disk.device_type() == chromeos::DEVICE_TYPE_SD)) { diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.cc b/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.cc index d8204d5ad57..f6d4020a22b 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.cc @@ -9,7 +9,7 @@ #include "base/location.h" #include "base/single_thread_task_runner.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" @@ -18,6 +18,7 @@ #if defined(OS_CHROMEOS) #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_image_burner_client.h" +#include "chromeos/disks/disk.h" #endif namespace extensions { @@ -73,9 +74,10 @@ FakeDiskMountManager::~FakeDiskMountManager() {} void FakeDiskMountManager::UnmountDeviceRecursively( const std::string& device_path, - const UnmountDeviceRecursivelyCallbackType& callback) { - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, - base::BindOnce(callback, true)); + UnmountDeviceRecursivelyCallbackType callback) { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(std::move(callback), chromeos::MOUNT_ERROR_NONE)); } #endif diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.h b/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.h index 1031b57f7fa..aa0397989d1 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.h +++ b/chromium/chrome/browser/extensions/api/image_writer_private/test_utils.h @@ -70,7 +70,7 @@ class FakeDiskMountManager : public chromeos::disks::MockDiskMountManager { void UnmountDeviceRecursively( const std::string& device_path, - const UnmountDeviceRecursivelyCallbackType& callback) override; + UnmountDeviceRecursivelyCallbackType callback) override; private: DiskMap disks_; diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/unzip_helper.cc b/chromium/chrome/browser/extensions/api/image_writer_private/unzip_helper.cc index dbca44a3e9d..41f1ecce153 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/unzip_helper.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/unzip_helper.cc @@ -8,7 +8,7 @@ #include "base/files/file_util.h" #include "base/single_thread_task_runner.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/extensions/api/image_writer_private/error_messages.h" #include "third_party/zlib/google/zip_reader.h" diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc index cedfb9b5446..1a9629190e7 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc +++ b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.cc @@ -9,6 +9,8 @@ #include "content/public/browser/browser_thread.h" #include "net/traffic_annotation/network_traffic_annotation.h" #include "net/url_request/url_fetcher.h" +#include "services/network/public/cpp/resource_response.h" +#include "services/network/public/cpp/simple_url_loader.h" #include "services/service_manager/public/cpp/connector.h" namespace extensions { @@ -20,7 +22,7 @@ WriteFromUrlOperation::WriteFromUrlOperation( base::WeakPtr<OperationManager> manager, std::unique_ptr<service_manager::Connector> connector, const ExtensionId& extension_id, - net::URLRequestContextGetter* request_context, + network::mojom::URLLoaderFactoryPtrInfo factory_info, GURL url, const std::string& hash, const std::string& device_path, @@ -30,7 +32,7 @@ WriteFromUrlOperation::WriteFromUrlOperation( extension_id, device_path, download_folder), - request_context_(request_context), + url_loader_factory_ptr_info_(std::move(factory_info)), url_(url), hash_(hash), download_continuation_() {} @@ -59,14 +61,14 @@ void WriteFromUrlOperation::GetDownloadTarget(base::OnceClosure continuation) { } if (url_.ExtractFileName().empty()) { - if (!base::CreateTemporaryFileInDir(temp_dir_.GetPath(), &image_path_)) { + if (!base::CreateTemporaryFileInDir(temp_dir_->GetPath(), &image_path_)) { Error(error::kTempFileError); return; } } else { base::FilePath file_name = base::FilePath::FromUTF8Unsafe(url_.ExtractFileName()); - image_path_ = temp_dir_.GetPath().Append(file_name); + image_path_ = temp_dir_->GetPath().Append(file_name); } PostTask(std::move(continuation)); @@ -110,49 +112,53 @@ void WriteFromUrlOperation::Download(base::OnceClosure continuation) { "Not implemented, considered not useful." })"); - // Store the URL fetcher on this object so that it is destroyed before this - // object is. - url_fetcher_ = net::URLFetcher::Create(url_, net::URLFetcher::GET, this, - traffic_annotation); + auto request = std::make_unique<network::ResourceRequest>(); + request->url = GURL(url_); + simple_url_loader_ = + network::SimpleURLLoader::Create(std::move(request), traffic_annotation); - url_fetcher_->SetRequestContext(request_context_); - url_fetcher_->SaveResponseToFileAtPath(image_path_, task_runner()); + simple_url_loader_->SetOnDownloadProgressCallback(base::BindRepeating( + &WriteFromUrlOperation::OnDataDownloaded, base::Unretained(this))); + simple_url_loader_->SetOnResponseStartedCallback(base::BindOnce( + &WriteFromUrlOperation::OnResponseStarted, base::Unretained(this))); AddCleanUpFunction( - base::BindOnce(&WriteFromUrlOperation::DestroyUrlFetcher, this)); + base::BindOnce(&WriteFromUrlOperation::DestroySimpleURLLoader, this)); - url_fetcher_->Start(); + network::mojom::URLLoaderFactoryPtr url_loader_factory_ptr; + url_loader_factory_ptr.Bind(std::move(url_loader_factory_ptr_info_)); + + simple_url_loader_->DownloadToFile( + url_loader_factory_ptr.get(), + base::BindOnce(&WriteFromUrlOperation::OnSimpleLoaderComplete, + base::Unretained(this)), + image_path_); } -void WriteFromUrlOperation::DestroyUrlFetcher() { url_fetcher_.reset(); } +void WriteFromUrlOperation::DestroySimpleURLLoader() { + simple_url_loader_.reset(); +} -void WriteFromUrlOperation::OnURLFetchUploadProgress( - const net::URLFetcher* source, - int64_t current, - int64_t total) { - // No-op +void WriteFromUrlOperation::OnResponseStarted( + const GURL& final_url, + const network::ResourceResponseHead& response_head) { + total_response_bytes_ = response_head.content_length; } -void WriteFromUrlOperation::OnURLFetchDownloadProgress( - const net::URLFetcher* source, - int64_t current, - int64_t total, - int64_t current_network_bytes) { +void WriteFromUrlOperation::OnDataDownloaded(uint64_t current) { DCHECK(IsRunningInCorrectSequence()); - if (IsCancelled()) { - url_fetcher_.reset(NULL); - } + if (IsCancelled()) + DestroySimpleURLLoader(); - int progress = (kProgressComplete * current) / total; + int progress = (kProgressComplete * current) / total_response_bytes_; SetProgress(progress); } -void WriteFromUrlOperation::OnURLFetchComplete(const net::URLFetcher* source) { +void WriteFromUrlOperation::OnSimpleLoaderComplete(base::FilePath file_path) { DCHECK(IsRunningInCorrectSequence()); - - if (source->GetStatus().is_success() && source->GetResponseCode() == 200) { + if (!file_path.empty()) { SetProgress(kProgressComplete); std::move(download_continuation_).Run(); diff --git a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h index b2c5a9cfa9b..757fc07ffb9 100644 --- a/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h +++ b/chromium/chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h @@ -8,13 +8,13 @@ #include <stdint.h> #include "chrome/browser/extensions/api/image_writer_private/operation.h" -#include "net/url_request/url_fetcher_delegate.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" #include "url/gurl.h" -namespace net { -class URLFetcher; -class URLRequestContextGetter; -} // namespace net +namespace network { +struct ResourceResponseHead; +class SimpleURLLoader; +} // namespace network namespace extensions { namespace image_writer { @@ -22,12 +22,12 @@ namespace image_writer { class OperationManager; // Encapsulates a write of an image accessed via URL. -class WriteFromUrlOperation : public Operation, public net::URLFetcherDelegate { +class WriteFromUrlOperation : public Operation { public: WriteFromUrlOperation(base::WeakPtr<OperationManager> manager, std::unique_ptr<service_manager::Connector> connector, const ExtensionId& extension_id, - net::URLRequestContextGetter* request_context, + network::mojom::URLLoaderFactoryPtrInfo factory_info, GURL url, const std::string& hash, const std::string& storage_unit_id, @@ -52,33 +52,24 @@ class WriteFromUrlOperation : public Operation, public net::URLFetcherDelegate { void VerifyDownload(base::OnceClosure continuation); private: - // Destroys the URLFetcher. The URLFetcher needs to be destroyed on the same - // thread it was created on. The Operation may be deleted on the UI thread - // and so we must first delete the URLFetcher on the FILE thread. - void DestroyUrlFetcher(); - - // URLFetcherDelegate implementation. - void OnURLFetchComplete(const net::URLFetcher* source) override; - void OnURLFetchDownloadProgress(const net::URLFetcher* source, - int64_t current, - int64_t total, - int64_t current_network_bytes) override; - void OnURLFetchUploadProgress(const net::URLFetcher* source, - int64_t current, - int64_t total) override; - + void DestroySimpleURLLoader(); + void OnResponseStarted(const GURL& final_url, + const network::ResourceResponseHead& response_head); + void OnDataDownloaded(uint64_t current); + void OnSimpleLoaderComplete(base::FilePath file_path); void VerifyDownloadCompare(base::OnceClosure continuation, const std::string& download_hash); void VerifyDownloadComplete(base::OnceClosure continuation); // Arguments - net::URLRequestContextGetter* request_context_; + network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info_; GURL url_; const std::string hash_; // Local state - std::unique_ptr<net::URLFetcher> url_fetcher_; + std::unique_ptr<network::SimpleURLLoader> simple_url_loader_; base::OnceClosure download_continuation_; + int total_response_bytes_ = -1; }; } // namespace image_writer 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 c79d6998b8f..20f3c870603 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 @@ -5,11 +5,12 @@ #include "chrome/browser/extensions/api/image_writer_private/write_from_url_operation.h" #include "base/run_loop.h" -#include "base/task_scheduler/post_task.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" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/storage_partition.h" #include "net/url_request/test_url_request_interceptor.h" #include "services/service_manager/public/cpp/connector.h" @@ -35,16 +36,17 @@ typedef net::LocalHostTestURLRequestInterceptor GetInterceptor; // the current path to the image file. class WriteFromUrlOperationForTest : public WriteFromUrlOperation { public: - WriteFromUrlOperationForTest(base::WeakPtr<OperationManager> manager, - const ExtensionId& extension_id, - net::URLRequestContextGetter* request_context, - GURL url, - const std::string& hash, - const std::string& storage_unit_id) + WriteFromUrlOperationForTest( + base::WeakPtr<OperationManager> manager, + const ExtensionId& extension_id, + network::mojom::URLLoaderFactoryPtrInfo factory_info, + GURL url, + const std::string& hash, + const std::string& storage_unit_id) : WriteFromUrlOperation(manager, /*connector=*/nullptr, extension_id, - request_context, + std::move(factory_info), url, hash, storage_unit_id, @@ -99,7 +101,7 @@ class ImageWriterWriteFromUrlOperationTest : public ImageWriterUnitTestBase { get_interceptor_.reset(new GetInterceptor( BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, + {base::MayBlock(), base::TaskPriority::BEST_EFFORT, base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN}))); get_interceptor_->SetResponse(GURL(kTestImageUrl), test_utils_.GetImagePath()); @@ -112,10 +114,15 @@ class ImageWriterWriteFromUrlOperationTest : public ImageWriterUnitTestBase { scoped_refptr<WriteFromUrlOperationForTest> CreateOperation( const GURL& url, const std::string& hash) { + network::mojom::URLLoaderFactoryPtrInfo url_loader_factory_ptr_info; + content::BrowserContext::GetDefaultStoragePartition(&test_profile_) + ->GetURLLoaderFactoryForBrowserProcess() + ->Clone(mojo::MakeRequest(&url_loader_factory_ptr_info)); + scoped_refptr<WriteFromUrlOperationForTest> operation( new WriteFromUrlOperationForTest( manager_.AsWeakPtr(), kDummyExtensionId, - test_profile_.GetRequestContext(), url, hash, + std::move(url_loader_factory_ptr_info), url, hash, test_utils_.GetDevicePath().AsUTF8Unsafe())); operation->Start(); return operation; @@ -182,10 +189,6 @@ TEST_F(ImageWriterWriteFromUrlOperationTest, DownloadFile) { EXPECT_CALL( manager_, - OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, _)) - .Times(AtLeast(1)); - EXPECT_CALL( - manager_, OnProgress(kDummyExtensionId, image_writer_api::STAGE_DOWNLOAD, 0)) .Times(AnyNumber()); EXPECT_CALL( 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 f4aedc8c0fa..c5f26972ffb 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 @@ -197,6 +197,11 @@ bool ImeObserver::HasListener(const std::string& event_name) const { return extensions::EventRouter::Get(profile_)->HasEventListener(event_name); } +bool ImeObserver::ExtensionHasListener(const std::string& event_name) const { + return extensions::EventRouter::Get(profile_)->ExtensionHasEventListener( + extension_id_, event_name); +} + std::string ImeObserver::ConvertInputContextType( ui::IMEEngineHandlerInterface::InputContext input_context) { std::string input_context_type = "text"; 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 ca9b9522dd9..78e9fce74fd 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 @@ -81,8 +81,12 @@ class ImeObserver : public input_method::InputMethodEngineBase::Observer { bool ShouldForwardKeyEvent() const; // Returns true if there are any listeners on the given event. + // TODO(https://crbug.com/835699): Merge this with |ExtensionHasListener|. bool HasListener(const std::string& event_name) const; + // Returns true if the extension has any listeners on the given event. + bool ExtensionHasListener(const std::string& event_name) const; + // Functions used to convert InputContext struct to string std::string ConvertInputContextType( IMEEngineHandlerInterface::InputContext input_context); 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 7f26164f67f..b437fc65997 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 @@ -202,7 +202,7 @@ class ImeObserverChromeOS : public ui::ImeObserver { // There is both a public and private OnFocus event. The private OnFocus // event is only for ChromeOS and contains additional information about pen // inputs. We ensure that we only trigger one OnFocus event. - if (HasListener(input_method_private::OnFocus::kEventName) && + if (ExtensionHasListener(input_method_private::OnFocus::kEventName) && keyboard::IsStylusVirtualKeyboardEnabled()) { input_method_private::InputContext input_context; input_context.context_id = context.id; @@ -210,6 +210,8 @@ class ImeObserverChromeOS : public ui::ImeObserver { ConvertInputContextType(context)); input_context.auto_correct = ConvertInputContextAutoCorrect(context); input_context.auto_complete = ConvertInputContextAutoComplete(context); + input_context.auto_capitalize = (input_method_private::AutoCapitalizeType) + ConvertInputContextAutoCapitalize(context); input_context.spell_check = ConvertInputContextSpellCheck(context); input_context.should_do_learning = context.should_do_learning; input_context.focus_reason = input_method_private::ParseFocusReason( 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 0a2368e6f81..19df4358af6 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 @@ -532,7 +532,7 @@ LanguageSettingsPrivateGetTranslateTargetLanguageFunction::Run() { Profile* profile = chrome_details_.GetProfile(); language::LanguageModel* language_model = LanguageModelManagerFactory::GetForBrowserContext(profile) - ->GetDefaultModel(); + ->GetPrimaryModel(); return RespondNow(OneArgument( std::make_unique<base::Value>(TranslateService::GetTargetLanguage( profile->GetPrefs(), language_model)))); @@ -562,7 +562,9 @@ void PopulateInputMethodListFromDescriptors( input_method.enabled.reset(new bool(true)); if (descriptor.options_page_url().is_valid()) input_method.has_options_page.reset(new bool(true)); - if (!allowed_ids.empty() && util->IsKeyboardLayout(input_method.id) && + if (!allowed_ids.empty() && + (util->IsKeyboardLayout(input_method.id) || + chromeos::extension_ime_util::IsArcIME(input_method.id)) && allowed_ids.count(input_method.id) == 0) { input_method.is_prohibited_by_policy.reset(new bool(true)); } diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc index f1057324374..634e75751ea 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.cc @@ -32,6 +32,7 @@ LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate( : custom_dictionary_(nullptr), context_(context), listening_spellcheck_(false), + listening_input_method_(false), profile_added_(false) { // Register with the event router so we know when renderers are listening to // our events. We first check and see if there *is* an event router, because @@ -45,6 +46,10 @@ LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate( language_settings_private::OnSpellcheckDictionariesChanged::kEventName); event_router->RegisterObserver(this, language_settings_private::OnCustomDictionaryChanged::kEventName); + event_router->RegisterObserver( + this, language_settings_private::OnInputMethodAdded::kEventName); + event_router->RegisterObserver( + this, language_settings_private::OnInputMethodRemoved::kEventName); // SpellcheckService cannot be created until Profile::DoFinalInit() has been // called. http://crbug.com/171406 @@ -56,10 +61,14 @@ LanguageSettingsPrivateDelegate::LanguageSettingsPrivateDelegate( GetPrefs()); StartOrStopListeningForSpellcheckChanges(); +#if defined(OS_CHROMEOS) + StartOrStopListeningForInputMethodChanges(); +#endif // defined(OS_CHROMEOS) } LanguageSettingsPrivateDelegate::~LanguageSettingsPrivateDelegate() { DCHECK(!listening_spellcheck_); + DCHECK(!listening_input_method_); pref_change_registrar_.RemoveAll(); notification_registrar_.RemoveAll(); } @@ -101,6 +110,16 @@ void LanguageSettingsPrivateDelegate::Shutdown() { RemoveDictionaryObservers(); listening_spellcheck_ = false; } + +#if defined(OS_CHROMEOS) + if (listening_input_method_) { + auto* input_method_manager = + chromeos::input_method::InputMethodManager::Get(); + if (input_method_manager) + input_method_manager->RemoveObserver(this); + listening_input_method_ = false; + } +#endif // defined(OS_CHROMEOS) } void LanguageSettingsPrivateDelegate::OnListenerAdded( @@ -111,13 +130,26 @@ void LanguageSettingsPrivateDelegate::OnListenerAdded( details.event_name == language_settings_private::OnCustomDictionaryChanged::kEventName) { StartOrStopListeningForSpellcheckChanges(); + return; + } +#if defined(OS_CHROMEOS) + if (details.event_name == + language_settings_private::OnInputMethodAdded::kEventName || + details.event_name == + language_settings_private::OnInputMethodRemoved::kEventName) { + StartOrStopListeningForInputMethodChanges(); + return; } +#endif // defined(OS_CHROMEOS) } void LanguageSettingsPrivateDelegate::OnListenerRemoved( const EventListenerInfo& details) { // Stop listening to events if there are no more listeners. StartOrStopListeningForSpellcheckChanges(); +#if defined(OS_CHROMEOS) + StartOrStopListeningForInputMethodChanges(); +#endif // defined(OS_CHROMEOS) } void LanguageSettingsPrivateDelegate::Observe( @@ -128,6 +160,37 @@ void LanguageSettingsPrivateDelegate::Observe( StartOrStopListeningForSpellcheckChanges(); } +#if defined(OS_CHROMEOS) +void LanguageSettingsPrivateDelegate::InputMethodChanged( + chromeos::input_method::InputMethodManager* manager, + Profile* profile, + bool show_message) { + // Nothing to do. +} + +void LanguageSettingsPrivateDelegate::OnInputMethodExtensionAdded( + const std::string& extension_id) { + std::unique_ptr<base::ListValue> args( + language_settings_private::OnInputMethodAdded::Create(extension_id)); + std::unique_ptr<extensions::Event> extension_event(new extensions::Event( + events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_ADDED, + language_settings_private::OnInputMethodAdded::kEventName, + std::move(args))); + EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event)); +} + +void LanguageSettingsPrivateDelegate::OnInputMethodExtensionRemoved( + const std::string& extension_id) { + std::unique_ptr<base::ListValue> args( + language_settings_private::OnInputMethodRemoved::Create(extension_id)); + std::unique_ptr<extensions::Event> extension_event(new extensions::Event( + events::LANGUAGE_SETTINGS_PRIVATE_ON_INPUT_METHOD_REMOVED, + language_settings_private::OnInputMethodRemoved::kEventName, + std::move(args))); + EventRouter::Get(context_)->BroadcastEvent(std::move(extension_event)); +} +#endif // defined(OS_CHROMEOS) + void LanguageSettingsPrivateDelegate::OnHunspellDictionaryInitialized( const std::string& language) { BroadcastDictionariesChangedEvent(); @@ -229,6 +292,29 @@ void LanguageSettingsPrivateDelegate:: listening_spellcheck_ = should_listen; } +#if defined(OS_CHROMEOS) +void LanguageSettingsPrivateDelegate:: + StartOrStopListeningForInputMethodChanges() { + EventRouter* event_router = EventRouter::Get(context_); + bool should_listen = + event_router->HasEventListener( + language_settings_private::OnInputMethodAdded::kEventName) || + event_router->HasEventListener( + language_settings_private::OnInputMethodRemoved::kEventName); + + auto* input_method_manager = + chromeos::input_method::InputMethodManager::Get(); + if (input_method_manager) { + if (should_listen && !listening_input_method_) + input_method_manager->AddObserver(this); + else if (!should_listen && listening_input_method_) + input_method_manager->RemoveObserver(this); + } + + listening_input_method_ = should_listen; +} +#endif // defined(OS_CHROMEOS) + void LanguageSettingsPrivateDelegate::RetryDownloadHunspellDictionary( const std::string& language) { for (const base::WeakPtr<SpellcheckHunspellDictionary> dictionary : diff --git a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h index 873dec91a69..17098f43ce8 100644 --- a/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h +++ b/chromium/chrome/browser/extensions/api/language_settings_private/language_settings_private_delegate.h @@ -5,6 +5,7 @@ #ifndef CHROME_BROWSER_EXTENSIONS_API_LANGUAGE_SETTINGS_PRIVATE_LANGUAGE_SETTINGS_PRIVATE_DELEGATE_H_ #define CHROME_BROWSER_EXTENSIONS_API_LANGUAGE_SETTINGS_PRIVATE_LANGUAGE_SETTINGS_PRIVATE_DELEGATE_H_ +#include <string> #include <vector> #include "base/macros.h" @@ -18,6 +19,10 @@ #include "content/public/browser/notification_registrar.h" #include "extensions/browser/event_router.h" +#if defined(OS_CHROMEOS) +#include "ui/base/ime/chromeos/input_method_manager.h" +#endif + namespace content { class BrowserContext; } @@ -30,6 +35,9 @@ class LanguageSettingsPrivateDelegate : public KeyedService, public EventRouter::Observer, public content::NotificationObserver, +#if defined(OS_CHROMEOS) + public chromeos::input_method::InputMethodManager::Observer, +#endif // defined(OS_CHROMEOS) public SpellcheckHunspellDictionary::Observer, public SpellcheckCustomDictionary::Observer { public: @@ -60,6 +68,15 @@ class LanguageSettingsPrivateDelegate void OnListenerAdded(const EventListenerInfo& details) override; void OnListenerRemoved(const EventListenerInfo& details) override; +#if defined(OS_CHROMEOS) + // chromeos::input_method::InputMethodManager::Observer implementation. + void InputMethodChanged(chromeos::input_method::InputMethodManager* manager, + Profile* profile, + bool show_message) override; + void OnInputMethodExtensionAdded(const std::string& extension_id) override; + void OnInputMethodExtensionRemoved(const std::string& extension_id) override; +#endif // defined(OS_CHROMEOS) + // SpellcheckHunspellDictionary::Observer implementation. void OnHunspellDictionaryInitialized(const std::string& language) override; void OnHunspellDictionaryDownloadBegin(const std::string& language) override; @@ -88,6 +105,13 @@ class LanguageSettingsPrivateDelegate // any observers. void StartOrStopListeningForSpellcheckChanges(); +#if defined(OS_CHROMEOS) + // If there are any JavaScript listeners registered for input method events, + // ensures we are registered for change notifications. Otherwise, unregisters + // any observers. + void StartOrStopListeningForInputMethodChanges(); +#endif // defined(OS_CHROMEOS) + // Handles the preference for which languages should be used for spellcheck // by resetting the dictionaries and broadcasting an event. void OnSpellcheckDictionariesChanged(); @@ -110,6 +134,8 @@ class LanguageSettingsPrivateDelegate // True if there are observers listening for spellcheck events. bool listening_spellcheck_; + // True if there are observers listening for input method events. + bool listening_input_method_; // True if the profile has finished initializing. bool profile_added_; diff --git a/chromium/chrome/browser/extensions/api/management/management_browsertest.cc b/chromium/chrome/browser/extensions/api/management/management_browsertest.cc index 870c2eb71cb..24da4131ac6 100644 --- a/chromium/chrome/browser/extensions/api/management/management_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/management/management_browsertest.cc @@ -11,7 +11,8 @@ #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/stringprintf.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" +#include "base/test/bind_test_util.h" #include "build/build_config.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_management.h" @@ -31,6 +32,7 @@ #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/url_loader_interceptor.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" @@ -343,18 +345,32 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, MAYBE_AutoUpdate) { basedir.AppendASCII("v2"), temp_dir.GetPath().AppendASCII("v2.crx"), pem_path, base::FilePath()); - // Note: This interceptor gets requests on the IO thread. - net::LocalHostTestURLRequestInterceptor interceptor( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), - base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); - - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v2.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v2.crx"), - v2_path); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](content::URLLoaderInterceptor::RequestParams* params) -> bool { + if (params->url_request.url.path() == "/autoupdate/v2.crx") { + content::URLLoaderInterceptor::WriteResponse(v2_path, + params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/v3.crx") { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("v3.crx"), params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/manifest") { + static bool first = true; + if (first) { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v2.xml"), params->client.get()); + first = false; + } else { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v3.xml"), params->client.get()); + } + return true; + } + return false; + })); // Install version 1 of the extension. ExtensionTestMessageListener listener1("v1 installed", false); @@ -394,11 +410,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, MAYBE_AutoUpdate) { // Now try doing an update to version 3, which has been incorrectly // signed. This should fail. - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v3.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v3.crx"), - basedir.AppendASCII("v3.crx")); extensions::ExtensionUpdater::CheckParams params2; params2.callback = base::BindOnce(&NotificationListener::OnFinished, @@ -447,18 +458,20 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, basedir.AppendASCII("v2"), temp_dir.GetPath().AppendASCII("v2.crx"), pem_path, base::FilePath()); - // Note: This interceptor gets requests on the IO thread. - net::LocalHostTestURLRequestInterceptor interceptor( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), - base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); - - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v2.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v2.crx"), - v2_path); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](content::URLLoaderInterceptor::RequestParams* params) -> bool { + if (params->url_request.url.path() == "/autoupdate/manifest") { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v2.xml"), params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/v2.crx") { + content::URLLoaderInterceptor::WriteResponse(v2_path, + params->client.get()); + return true; + } + return false; + })); // Install version 1 of the extension. ExtensionTestMessageListener listener1("v1 installed", false); @@ -523,18 +536,20 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalUrlUpdate) { basedir.AppendASCII("v2"), temp_dir.GetPath().AppendASCII("v2.crx"), pem_path, base::FilePath()); - // Note: This interceptor gets requests on the IO thread. - net::LocalHostTestURLRequestInterceptor interceptor( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), - base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); - - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v2.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v2.crx"), - v2_path); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](content::URLLoaderInterceptor::RequestParams* params) -> bool { + if (params->url_request.url.path() == "/autoupdate/manifest") { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v2.xml"), params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/v2.crx") { + content::URLLoaderInterceptor::WriteResponse(v2_path, + params->client.get()); + return true; + } + return false; + })); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); const size_t size_before = registry->enabled_extensions().size(); @@ -630,18 +645,20 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, ExternalPolicyRefresh) { basedir.AppendASCII("v2"), temp_dir.GetPath().AppendASCII("v2.crx"), pem_path, base::FilePath()); - // Note: This interceptor gets requests on the IO thread. - net::LocalHostTestURLRequestInterceptor interceptor( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), - base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); - - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v2.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v2.crx"), - v2_path); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](content::URLLoaderInterceptor::RequestParams* params) -> bool { + if (params->url_request.url.path() == "/autoupdate/manifest") { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v2.xml"), params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/v2.crx") { + content::URLLoaderInterceptor::WriteResponse(v2_path, + params->client.get()); + return true; + } + return false; + })); ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile()); const size_t size_before = registry->enabled_extensions().size(); @@ -722,18 +739,20 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, basedir.AppendASCII("v2"), temp_dir.GetPath().AppendASCII("v2.crx"), pem_path, base::FilePath()); - // Note: This interceptor gets requests on the IO thread. - net::LocalHostTestURLRequestInterceptor interceptor( - BrowserThread::GetTaskRunnerForThread(BrowserThread::IO), - base::CreateTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND, - base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); - - interceptor.SetResponseIgnoreQuery( - GURL("http://localhost/autoupdate/manifest"), - basedir.AppendASCII("manifest_v2.xml")); - interceptor.SetResponseIgnoreQuery(GURL("http://localhost/autoupdate/v2.crx"), - v2_path); + content::URLLoaderInterceptor interceptor(base::BindLambdaForTesting( + [&](content::URLLoaderInterceptor::RequestParams* params) -> bool { + if (params->url_request.url.path() == "/autoupdate/manifest") { + content::URLLoaderInterceptor::WriteResponse( + basedir.AppendASCII("manifest_v2.xml"), params->client.get()); + return true; + } + if (params->url_request.url.path() == "/autoupdate/v2.crx") { + content::URLLoaderInterceptor::WriteResponse(v2_path, + params->client.get()); + return true; + } + return false; + })); // Check that the policy is initially empty. ASSERT_TRUE(extensions::ExtensionManagementFactory::GetForBrowserContext( diff --git a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc index 2bf04ff08ba..e27d9f13054 100644 --- a/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc +++ b/chromium/chrome/browser/extensions/api/mdns/mdns_api.cc @@ -74,7 +74,7 @@ void MDnsAPI::SetDnsSdRegistryForTesting(DnsSdRegistry* dns_sd_registry) { void MDnsAPI::ForceDiscovery() { DCHECK(thread_checker_.CalledOnValidThread()); DnsSdRegistry* registry = dns_sd_registry(); - return registry->ForceDiscovery(); + return registry->ResetAndDiscover(); } DnsSdRegistry* MDnsAPI::dns_sd_registry() { diff --git a/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc b/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc index 7751cefc276..495366644a2 100644 --- a/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc +++ b/chromium/chrome/browser/extensions/api/mdns/mdns_apitest.cc @@ -100,7 +100,7 @@ IN_PROC_BROWSER_TEST_F(MDnsAPITest, MAYBE_ForceDiscovery) { EXPECT_CALL(*dns_sd_registry_, RegisterDnsSdListener(service_type)).Times(1); EXPECT_CALL(*dns_sd_registry_, UnregisterDnsSdListener(service_type)) .Times(1); - EXPECT_CALL(*dns_sd_registry_, ForceDiscovery()).Times(1); + EXPECT_CALL(*dns_sd_registry_, ResetAndDiscover()).Times(1); EXPECT_CALL(*dns_sd_registry_, RemoveObserver(A<DnsSdRegistry::DnsSdObserver*>())) .Times(1); diff --git a/chromium/chrome/browser/extensions/api/messaging/DEPS b/chromium/chrome/browser/extensions/api/messaging/DEPS deleted file mode 100644 index 06590ef7e73..00000000000 --- a/chromium/chrome/browser/extensions/api/messaging/DEPS +++ /dev/null @@ -1,8 +0,0 @@ -specific_include_rules = { - "native_message_host_chromeos.cc": [ - # TODO(erg): This allows us to switch between mus and classic ash/ozone as - # targets. It should be removed when support for classic ash is removed; - # which means that remoting never touches ozone. - "+ui/ozone/public/ozone_platform.h", - ] -} 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 3c9932703ad..fcc93f51bfa 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 @@ -12,12 +12,9 @@ #include "base/bind_helpers.h" #include "base/json/json_reader.h" #include "base/json/json_writer.h" -#include "base/lazy_instance.h" #include "base/location.h" #include "base/macros.h" #include "base/single_thread_task_runner.h" -#include "base/task_scheduler/post_task.h" -#include "base/task_scheduler/task_traits.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "chrome/browser/browser_process.h" @@ -26,18 +23,10 @@ #include "extensions/common/constants.h" #include "extensions/common/url_pattern.h" #include "net/url_request/url_request_context_getter.h" -#include "remoting/base/auto_thread_task_runner.h" -#include "remoting/host/chromoting_host_context.h" -#include "remoting/host/it2me/it2me_native_messaging_host.h" -#include "remoting/host/policy_watcher.h" -#include "ui/events/system_input_injector.h" +#include "remoting/host/it2me/it2me_native_messaging_host_chromeos.h" #include "ui/gfx/native_widget_types.h" #include "url/gurl.h" -#if defined(USE_OZONE) -#include "ui/ozone/public/ozone_platform.h" -#endif - namespace extensions { namespace { @@ -102,52 +91,14 @@ struct BuiltInHost { std::unique_ptr<NativeMessageHost> (*create_function)(); }; -#if defined(USE_OZONE) -class OzoneSystemInputInjectorAdaptor : public ui::SystemInputInjectorFactory { - public: - std::unique_ptr<ui::SystemInputInjector> CreateSystemInputInjector() - override { - return ui::OzonePlatform::GetInstance()->CreateSystemInputInjector(); - } -}; - -base::LazyInstance<OzoneSystemInputInjectorAdaptor>::Leaky - g_ozone_system_input_injector_adaptor = LAZY_INSTANCE_INITIALIZER; -#endif - -ui::SystemInputInjectorFactory* GetInputInjector() { - ui::SystemInputInjectorFactory* system = ui::GetSystemInputInjectorFactory(); - if (system) - return system; - -#if defined(USE_OZONE) - return g_ozone_system_input_injector_adaptor.Pointer(); -#endif - - return nullptr; -} - std::unique_ptr<NativeMessageHost> CreateIt2MeHost() { - std::unique_ptr<remoting::It2MeHostFactory> host_factory( - new remoting::It2MeHostFactory()); - std::unique_ptr<remoting::ChromotingHostContext> context = - remoting::ChromotingHostContext::CreateForChromeOS( - base::WrapRefCounted(g_browser_process->system_request_context()), - content::BrowserThread::GetTaskRunnerForThread( - content::BrowserThread::IO), - content::BrowserThread::GetTaskRunnerForThread( - content::BrowserThread::UI), - base::CreateSingleThreadTaskRunnerWithTraits( - {base::MayBlock(), base::TaskPriority::BACKGROUND}), - GetInputInjector()); - std::unique_ptr<remoting::PolicyWatcher> policy_watcher = - remoting::PolicyWatcher::CreateWithPolicyService( - g_browser_process->policy_service()); - std::unique_ptr<NativeMessageHost> host( - new remoting::It2MeNativeMessagingHost( - /*needs_elevation=*/false, std::move(policy_watcher), - std::move(context), std::move(host_factory))); - return host; + return remoting::CreateIt2MeNativeMessagingHostForChromeOS( + g_browser_process->system_request_context(), + content::BrowserThread::GetTaskRunnerForThread( + content::BrowserThread::IO), + content::BrowserThread::GetTaskRunnerForThread( + content::BrowserThread::UI), + g_browser_process->policy_service()); } // If you modify the list of allowed_origins, don't forget to update 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 9b8591d4d4e..274257d18bf 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 @@ -12,7 +12,7 @@ #include "base/files/file_path.h" #include "base/logging.h" #include "base/process/kill.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" #include "chrome/browser/extensions/api/messaging/native_process_launcher.h" @@ -71,7 +71,7 @@ NativeMessageProcessHost::~NativeMessageProcessHost() { // block, so we have to post a task on the blocking pool. #if defined(OS_MACOSX) base::PostTaskWithTraits( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, base::BindOnce(&base::EnsureProcessTerminated, Passed(&process_))); #else base::EnsureProcessTerminated(std::move(process_)); @@ -298,8 +298,11 @@ void NativeMessageProcessHost::DoWrite() { !current_write_buffer_->BytesRemaining()) { if (write_queue_.empty()) return; - current_write_buffer_ = new net::DrainableIOBuffer( - write_queue_.front().get(), write_queue_.front()->size()); + scoped_refptr<net::IOBufferWithSize> buffer = + std::move(write_queue_.front()); + int buffer_size = buffer->size(); + current_write_buffer_ = base::MakeRefCounted<net::DrainableIOBuffer>( + std::move(buffer), buffer_size); write_queue_.pop(); } 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 0d36259d062..eda727af052 100644 --- a/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc +++ b/chromium/chrome/browser/extensions/api/messaging/native_process_launcher.cc @@ -15,7 +15,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/strings/stringprintf.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "build/build_config.h" #include "chrome/browser/extensions/api/messaging/native_messaging_host_manifest.h" #include "chrome/common/chrome_paths.h" diff --git a/chromium/chrome/browser/extensions/api/music_manager_private/device_id.h b/chromium/chrome/browser/extensions/api/music_manager_private/device_id.h index 7a1cf5dafef..623a00b6d4b 100644 --- a/chromium/chrome/browser/extensions/api/music_manager_private/device_id.h +++ b/chromium/chrome/browser/extensions/api/music_manager_private/device_id.h @@ -10,7 +10,7 @@ #include <string> #include "base/bind.h" -#include "base/task_scheduler/task_traits.h" +#include "base/task/task_traits.h" namespace extensions { namespace api { diff --git a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_linux.cc b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_linux.cc index ddc0c9a1214..a31cba28ced 100644 --- a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_linux.cc +++ b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_linux.cc @@ -20,7 +20,7 @@ #include "base/macros.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "content/public/browser/browser_thread.h" diff --git a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_mac.cc b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_mac.cc index 300e1bb2134..2081b232621 100644 --- a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_mac.cc +++ b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_mac.cc @@ -21,7 +21,7 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "content/public/browser/browser_thread.h" diff --git a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_win.cc b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_win.cc index 2b49d9a9f79..151fa8fdf22 100644 --- a/chromium/chrome/browser/extensions/api/music_manager_private/device_id_win.cc +++ b/chromium/chrome/browser/extensions/api/music_manager_private/device_id_win.cc @@ -22,7 +22,7 @@ #include "base/scoped_native_library.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "base/win/windows_version.h" #include "content/public/browser/browser_thread.h" diff --git a/chromium/chrome/browser/extensions/api/networking_cast_private/chrome_networking_cast_private_delegate.cc b/chromium/chrome/browser/extensions/api/networking_cast_private/chrome_networking_cast_private_delegate.cc index 508de031a40..1c1d786f10c 100644 --- a/chromium/chrome/browser/extensions/api/networking_cast_private/chrome_networking_cast_private_delegate.cc +++ b/chromium/chrome/browser/extensions/api/networking_cast_private/chrome_networking_cast_private_delegate.cc @@ -13,7 +13,7 @@ #include "base/sequenced_task_runner.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/extensions/api/networking_private/networking_private_credentials_getter.h" #include "chrome/browser/extensions/api/networking_private/networking_private_crypto.h" diff --git a/chromium/chrome/browser/extensions/api/networking_private/DEPS b/chromium/chrome/browser/extensions/api/networking_private/DEPS index a070d53ba99..d6abddad069 100644 --- a/chromium/chrome/browser/extensions/api/networking_private/DEPS +++ b/chromium/chrome/browser/extensions/api/networking_private/DEPS @@ -1,5 +1,3 @@ include_rules = [ - "+components/onc", - "+components/wifi", "+dbus", ] diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc index 475010dec10..83532ae924b 100644 --- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc +++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_chromeos_apitest.cc @@ -220,10 +220,9 @@ class NetworkingPrivateChromeOSApiTest : public extensions::ExtensionApiTest { // TODO(pneubeck): Remove the following hack, once the NetworkingPrivateAPI // uses the ProfileHelper to obtain the userhash crbug/238623. - const cryptohome::Identification login_user = - cryptohome::Identification::FromString( - user_manager::CanonicalizeUserID(command_line->GetSwitchValueNative( - chromeos::switches::kLoginUser))); + cryptohome::AccountIdentifier login_user; + login_user.set_account_id(user_manager::CanonicalizeUserID( + command_line->GetSwitchValueNative(chromeos::switches::kLoginUser))); const std::string sanitized_user = CryptohomeClient::GetStubSanitizedUsername(login_user); command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, @@ -236,7 +235,7 @@ class NetworkingPrivateChromeOSApiTest : public extensions::ExtensionApiTest { CHECK(user); std::string userhash; DBusThreadManager::Get()->GetCryptohomeClient()->GetSanitizedUsername( - cryptohome::Identification(user->GetAccountId()), + cryptohome::CreateAccountIdentifierFromAccountId(user->GetAccountId()), base::BindOnce( [](std::string* out, base::Optional<std::string> result) { CHECK(result.has_value()); @@ -424,6 +423,9 @@ class NetworkingPrivateChromeOSApiTest : public extensions::ExtensionApiTest { kWifi1ServicePath, shill::kConnectableProperty, base::Value(true)); service_test_->SetServiceProperty(kWifi1ServicePath, shill::kDeviceProperty, base::Value(kWifiDevicePath)); + service_test_->SetServiceProperty( + kWifi1ServicePath, shill::kTetheringProperty, + base::Value(shill::kTetheringNotDetectedState)); base::DictionaryValue static_ipconfig; static_ipconfig.SetKey(shill::kAddressProperty, base::Value("1.2.3.4")); service_test_->SetServiceProperty( @@ -445,6 +447,9 @@ class NetworkingPrivateChromeOSApiTest : public extensions::ExtensionApiTest { kWifi2ServicePath, shill::kSignalStrengthProperty, base::Value(80)); service_test_->SetServiceProperty( kWifi2ServicePath, shill::kConnectableProperty, base::Value(true)); + service_test_->SetServiceProperty( + kWifi2ServicePath, shill::kTetheringProperty, + base::Value(shill::kTetheringNotDetectedState)); AddService("stub_wimax", "wimax", shill::kTypeWimax, shill::kStateOnline); service_test_->SetServiceProperty( diff --git a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc index 9ab7402b18d..f01292fcec9 100644 --- a/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/networking_private/networking_private_credentials_getter_browsertest.cc @@ -6,7 +6,7 @@ #include "base/callback.h" #include "base/macros.h" #include "base/run_loop.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "chrome/browser/extensions/api/networking_private/networking_private_credentials_getter.h" #include "chrome/services/wifi_util_win/public/mojom/wifi_credentials_getter.mojom.h" #include "chrome/test/base/in_process_browser_test.h" @@ -29,7 +29,7 @@ class NetworkingPrivateCredentialsGetterTest : public InProcessBrowserTest { done_called_ = false; base::PostTaskWithTraits( - FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND}, + FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT}, base::Bind(&NetworkingPrivateCredentialsGetterTest::GetCredentials, base::Unretained(this))); run_loop.Run(); diff --git a/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc b/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc index e62669225cc..66cfea4b410 100644 --- a/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc +++ b/chromium/chrome/browser/extensions/api/notifications/notifications_api.cc @@ -152,7 +152,7 @@ bool NotificationBitmapToGfxImage( return false; // Ensure we have rgba data. - std::vector<char>* rgba_data = notification_bitmap.data.get(); + std::vector<uint8_t>* rgba_data = notification_bitmap.data.get(); if (!rgba_data) return false; @@ -172,7 +172,7 @@ bool NotificationBitmapToGfxImage( return false; uint32_t* pixels = bitmap.getAddr32(0, 0); - const char* c_rgba_data = rgba_data->data(); + const uint8_t* c_rgba_data = rgba_data->data(); for (size_t t = 0; t < rgba_area; ++t) { // |c_rgba_data| is RGBA, pixels is ARGB. @@ -287,6 +287,9 @@ bool NotificationsApiFunction::CreateNotification( if (options->event_time.get()) optional_fields.timestamp = base::Time::FromJsTime(*options->event_time); + if (options->silent) + optional_fields.silent = *options->silent; + if (options->buttons.get()) { // Currently we allow up to 2 buttons. size_t number_of_buttons = options->buttons->size(); @@ -446,6 +449,9 @@ bool NotificationsApiFunction::UpdateNotification( if (options->event_time) notification->set_timestamp(base::Time::FromJsTime(*options->event_time)); + if (options->silent) + notification->set_silent(*options->silent); + if (options->buttons) { // Currently we allow up to 2 buttons. size_t number_of_buttons = options->buttons->size(); diff --git a/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc b/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc index 5a4c13177f1..d5974129a30 100644 --- a/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc +++ b/chromium/chrome/browser/extensions/api/notifications/notifications_apitest.cc @@ -292,6 +292,7 @@ IN_PROC_BROWSER_TEST_F(NotificationsApiTest, TestPartialUpdate) { EXPECT_EQ(base::ASCIIToUTF16(kNewTitle), notification->title()); EXPECT_EQ(base::ASCIIToUTF16(kNewMessage), notification->message()); EXPECT_EQ(kNewPriority, notification->priority()); + EXPECT_TRUE(notification->silent()); EXPECT_EQ(1u, notification->buttons().size()); EXPECT_EQ(base::ASCIIToUTF16(kButtonTitle), notification->buttons()[0].title); } 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 c64ad578f6d..e328205b19c 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 @@ -10,7 +10,7 @@ #include "base/bind.h" #include "base/bind_helpers.h" #include "base/files/file_util.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_restrictions.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/extension_tab_util.h" @@ -95,7 +95,7 @@ bool PageCaptureSaveAsMHTMLFunction::RunAsync() { // time, we show the user a dialog where they can choose whether to allow the // extension access to the API. #if defined(OS_CHROMEOS) - if (profiles::IsPublicSession()) { + if (profiles::ArePublicSessionRestrictionsEnabled()) { WebContents* web_contents = GetWebContents(); if (!web_contents) { ReturnFailure(kTabClosedError); 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 3cfa9e91a8b..2983149f56b 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 @@ -129,7 +129,7 @@ PlatformKeysInternalGetPublicKeyFunction::Run() { api_pki::GetPublicKey::Params::Create(*args_)); EXTENSION_FUNCTION_VALIDATE(params); - const std::vector<char>& cert_der = params->certificate; + const std::vector<uint8_t>& cert_der = params->certificate; if (cert_der.empty()) return RespondNow(Error(platform_keys::kErrorInvalidX509Cert)); // Allow UTF-8 inside PrintableStrings in client certificates. See @@ -138,7 +138,8 @@ PlatformKeysInternalGetPublicKeyFunction::Run() { options.printable_string_is_utf8 = true; scoped_refptr<net::X509Certificate> cert_x509 = net::X509Certificate::CreateFromBytesUnsafeOptions( - cert_der.data(), cert_der.size(), options); + reinterpret_cast<const char*>(cert_der.data()), cert_der.size(), + options); if (!cert_x509) return RespondNow(Error(platform_keys::kErrorInvalidX509Cert)); @@ -163,8 +164,8 @@ PlatformKeysInternalGetPublicKeyFunction::Run() { &algorithm.additional_properties); return RespondNow(ArgumentList(api_pki::GetPublicKey::Results::Create( - std::vector<char>(key_info.public_key_spki_der.begin(), - key_info.public_key_spki_der.end()), + std::vector<uint8_t>(key_info.public_key_spki_der.begin(), + key_info.public_key_spki_der.end()), algorithm))); } @@ -184,7 +185,7 @@ PlatformKeysInternalSelectClientCertificatesFunction::Run() { DCHECK(service); chromeos::platform_keys::ClientCertificateRequest request; - for (const std::vector<char>& cert_authority : + for (const std::vector<uint8_t>& cert_authority : params->details.request.certificate_authorities) { request.certificate_authorities.push_back( std::string(cert_authority.begin(), cert_authority.end())); @@ -208,7 +209,7 @@ PlatformKeysInternalSelectClientCertificatesFunction::Run() { std::unique_ptr<net::CertificateList> client_certs; if (params->details.client_certs) { client_certs.reset(new net::CertificateList); - for (const std::vector<char>& client_cert_der : + for (const std::vector<uint8_t>& client_cert_der : *params->details.client_certs) { if (client_cert_der.empty()) return RespondNow(Error(platform_keys::kErrorInvalidX509Cert)); @@ -218,7 +219,8 @@ PlatformKeysInternalSelectClientCertificatesFunction::Run() { options.printable_string_is_utf8 = true; scoped_refptr<net::X509Certificate> client_cert_x509 = net::X509Certificate::CreateFromBytesUnsafeOptions( - client_cert_der.data(), client_cert_der.size(), options); + reinterpret_cast<const char*>(client_cert_der.data()), + client_cert_der.size(), options); if (!client_cert_x509) return RespondNow(Error(platform_keys::kErrorInvalidX509Cert)); client_certs->push_back(client_cert_x509); @@ -344,7 +346,7 @@ void PlatformKeysInternalSignFunction::OnSigned( if (error_message.empty()) Respond(ArgumentList(api_pki::Sign::Results::Create( - std::vector<char>(signature.begin(), signature.end())))); + std::vector<uint8_t>(signature.begin(), signature.end())))); else Respond(Error(error_message)); } 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 24934f6a747..c64afa4782c 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 @@ -34,9 +34,6 @@ const char kAffiliationID[] = "some-affiliation-id"; const char kTestUserinfoToken[] = "fake-userinfo-token"; -using policy::affiliation_test_helper::kEnterpriseUserEmail; -using policy::affiliation_test_helper::kEnterpriseUserGaiaId; - PlatformKeysTestBase::PlatformKeysTestBase( SystemTokenStatus system_token_status, EnrollmentStatus enrollment_status, @@ -44,8 +41,9 @@ PlatformKeysTestBase::PlatformKeysTestBase( : system_token_status_(system_token_status), enrollment_status_(enrollment_status), user_status_(user_status), - account_id_(AccountId::FromUserEmailGaiaId(kEnterpriseUserEmail, - kEnterpriseUserGaiaId)) { + account_id_(AccountId::FromUserEmailGaiaId( + policy::AffiliationTestHelper::kEnterpriseUserEmail, + policy::AffiliationTestHelper::kEnterpriseUserGaiaId)) { // Command line should not be tweaked as if user is already logged in. set_chromeos_user_ = false; // We log in without running browser. @@ -78,7 +76,7 @@ void PlatformKeysTestBase::SetUp() { void PlatformKeysTestBase::SetUpCommandLine(base::CommandLine* command_line) { extensions::ExtensionApiTest::SetUpCommandLine(command_line); - policy::affiliation_test_helper::AppendCommandLineSwitchesForLoginManager( + policy::AffiliationTestHelper::AppendCommandLineSwitchesForLoginManager( command_line); const GURL gaia_url = gaia_https_forwarder_.GetURLForSSLHost(std::string()); @@ -99,21 +97,23 @@ void PlatformKeysTestBase::SetUpInProcessBrowserTestFixture() { std::unique_ptr<chromeos::SessionManagerClient>( fake_session_manager_client)); + policy::AffiliationTestHelper affiliation_helper = + policy::AffiliationTestHelper::CreateForCloud( + fake_session_manager_client); + if (enrollment_status() == EnrollmentStatus::ENROLLED) { std::set<std::string> device_affiliation_ids; device_affiliation_ids.insert(kAffiliationID); - policy::affiliation_test_helper::SetDeviceAffiliationID( - &device_policy_test_helper_, fake_session_manager_client, - device_affiliation_ids); + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetDeviceAffiliationIDs( + &device_policy_test_helper_, device_affiliation_ids)); } if (user_status() == UserStatus::MANAGED_AFFILIATED_DOMAIN) { std::set<std::string> user_affiliation_ids; user_affiliation_ids.insert(kAffiliationID); policy::UserPolicyBuilder user_policy; - policy::affiliation_test_helper::SetUserAffiliationIDs( - &user_policy, fake_session_manager_client, account_id_, - user_affiliation_ids); + ASSERT_NO_FATAL_FAILURE(affiliation_helper.SetUserAffiliationIDs( + &user_policy, account_id_, user_affiliation_ids)); } EXPECT_CALL(mock_policy_provider_, IsInitializationComplete(testing::_)) @@ -135,7 +135,7 @@ void PlatformKeysTestBase::SetUpOnMainThread() { token_info.audience = GaiaUrls::GetInstance()->oauth2_chrome_client_id(); token_info.token = kTestUserinfoToken; token_info.email = account_id_.GetUserEmail(); - fake_gaia_.IssueOAuthToken(policy::affiliation_test_helper::kFakeRefreshToken, + fake_gaia_.IssueOAuthToken(policy::AffiliationTestHelper::kFakeRefreshToken, token_info); // On PRE_ test stage list of users is empty at this point. Then in the body @@ -143,7 +143,7 @@ void PlatformKeysTestBase::SetUpOnMainThread() { // after PRE_ test the list of user contains one kEnterpriseUser user. // This user logs in. if (!IsPreTest()) { - policy::affiliation_test_helper::LoginUser(account_id_); + policy::AffiliationTestHelper::LoginUser(account_id_); if (user_status() != UserStatus::UNMANAGED) { policy::ProfilePolicyConnector* const connector = @@ -183,7 +183,7 @@ void PlatformKeysTestBase::PrepareTestSystemSlotOnIO( crypto::ScopedTestSystemNSSKeySlot* system_slot) {} void PlatformKeysTestBase::RunPreTest() { - policy::affiliation_test_helper::PreLoginUser(account_id_); + policy::AffiliationTestHelper::PreLoginUser(account_id_); } bool PlatformKeysTestBase::TestExtension(const std::string& page_url) { 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 e1e4d05bc8e..04e63f93299 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 @@ -18,7 +18,6 @@ #include "net/cert/cert_verify_result.h" #include "net/cert/x509_certificate.h" #include "net/log/net_log_with_source.h" -#include "net/ssl/ssl_config_service.h" namespace extensions { @@ -162,7 +161,8 @@ void VerifyTrustAPI::IOPart::Verify(std::unique_ptr<Params> params, } std::vector<base::StringPiece> der_cert_chain; - for (const std::vector<char>& cert_der : details.server_certificate_chain) { + for (const std::vector<uint8_t>& cert_der : + details.server_certificate_chain) { if (cert_der.empty()) { callback.Run(platform_keys::kErrorInvalidX509Cert, 0, 0); return; @@ -197,10 +197,8 @@ void VerifyTrustAPI::IOPart::Verify(std::unique_ptr<Params> params, const int return_value = verifier->Verify( net::CertVerifier::RequestParams(std::move(cert_chain), details.hostname, - flags, ocsp_response, - net::CertificateList()), - net::SSLConfigService::GetCRLSet().get(), verify_result_ptr, - bound_callback, &request_state->request, *net_log); + flags, ocsp_response), + verify_result_ptr, bound_callback, &request_state->request, *net_log); if (return_value != net::ERR_IO_PENDING) { bound_callback.Run(return_value); diff --git a/chromium/chrome/browser/extensions/api/preference/preference_api.cc b/chromium/chrome/browser/extensions/api/preference/preference_api.cc index 5119a448e46..44cc6d7e252 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_api.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_api.cc @@ -25,7 +25,7 @@ #include "chrome/browser/net/prediction_options.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" -#include "components/autofill/core/common/autofill_pref_names.h" +#include "components/autofill/core/common/autofill_prefs.h" #include "components/content_settings/core/common/pref_names.h" #include "components/data_reduction_proxy/core/common/data_reduction_proxy_pref_names.h" #include "components/password_manager/core/common/password_manager_pref_names.h" @@ -94,7 +94,11 @@ const PrefMappingEntry kPrefMapping[] = { APIPermission::kDataReductionProxy, APIPermission::kDataReductionProxy}, {"alternateErrorPagesEnabled", prefs::kAlternateErrorPagesEnabled, APIPermission::kPrivacy, APIPermission::kPrivacy}, - {"autofillEnabled", autofill::prefs::kAutofillEnabled, + {"autofillEnabled", autofill::prefs::kAutofillEnabledDeprecated, + APIPermission::kPrivacy, APIPermission::kPrivacy}, + {"autofillAddressEnabled", autofill::prefs::kAutofillProfileEnabled, + APIPermission::kPrivacy, APIPermission::kPrivacy}, + {"autofillCreditCardEnabled", autofill::prefs::kAutofillCreditCardEnabled, APIPermission::kPrivacy, APIPermission::kPrivacy}, {"hyperlinkAuditingEnabled", prefs::kEnableHyperlinkAuditing, APIPermission::kPrivacy, APIPermission::kPrivacy}, @@ -782,9 +786,24 @@ ExtensionFunction::ResponseAction SetPreferenceFunction::Run() { transformer->BrowserToExtensionPref(browser_pref_value.get())); EXTENSION_FUNCTION_VALIDATE(extension_pref_value); - PreferenceAPI::Get(browser_context()) - ->SetExtensionControlledPref(extension_id(), browser_pref, scope, - browser_pref_value.release()); + PreferenceAPI* preference_api = PreferenceAPI::Get(browser_context()); + + // Set the new Autofill prefs if the extension sets the deprecated pref in + // order to maintain backward compatibility in the extensions preference API. + // TODO(crbug.com/870328): Remove this once the deprecated pref is retired. + if (autofill::prefs::kAutofillEnabledDeprecated == browser_pref) { + // |SetExtensionControlledPref| takes ownership of the base::Value pointer. + preference_api->SetExtensionControlledPref( + extension_id(), autofill::prefs::kAutofillCreditCardEnabled, scope, + new base::Value(browser_pref_value->GetBool())); + preference_api->SetExtensionControlledPref( + extension_id(), autofill::prefs::kAutofillProfileEnabled, scope, + new base::Value(browser_pref_value->GetBool())); + } + + preference_api->SetExtensionControlledPref( + extension_id(), browser_pref, scope, browser_pref_value.release()); + return RespondNow(NoArguments()); } diff --git a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc index 1c25420c612..9f9ea511da8 100644 --- a/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc +++ b/chromium/chrome/browser/extensions/api/preference/preference_apitest.cc @@ -19,7 +19,7 @@ #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/test/base/ui_test_utils.h" -#include "components/autofill/core/common/autofill_pref_names.h" +#include "components/autofill/core/common/autofill_prefs.h" #include "components/content_settings/core/common/pref_names.h" #include "components/keep_alive_registry/keep_alive_types.h" #include "components/keep_alive_registry/scoped_keep_alive.h" @@ -46,7 +46,9 @@ class ExtensionPreferenceApiTest : public extensions::ExtensionApiTest { ASSERT_TRUE(pref); EXPECT_TRUE(pref->IsExtensionControlled()); EXPECT_TRUE(prefs->GetBoolean(prefs::kAlternateErrorPagesEnabled)); - EXPECT_TRUE(prefs->GetBoolean(autofill::prefs::kAutofillEnabled)); + EXPECT_TRUE(prefs->GetBoolean(autofill::prefs::kAutofillEnabledDeprecated)); + EXPECT_TRUE(prefs->GetBoolean(autofill::prefs::kAutofillCreditCardEnabled)); + EXPECT_TRUE(prefs->GetBoolean(autofill::prefs::kAutofillProfileEnabled)); EXPECT_FALSE(prefs->GetBoolean(prefs::kBlockThirdPartyCookies)); EXPECT_TRUE(prefs->GetBoolean(prefs::kEnableHyperlinkAuditing)); EXPECT_TRUE(prefs->GetBoolean(prefs::kEnableReferrers)); @@ -66,7 +68,11 @@ class ExtensionPreferenceApiTest : public extensions::ExtensionApiTest { ASSERT_TRUE(pref); EXPECT_FALSE(pref->IsExtensionControlled()); EXPECT_FALSE(prefs->GetBoolean(prefs::kAlternateErrorPagesEnabled)); - EXPECT_FALSE(prefs->GetBoolean(autofill::prefs::kAutofillEnabled)); + EXPECT_FALSE( + prefs->GetBoolean(autofill::prefs::kAutofillEnabledDeprecated)); + EXPECT_FALSE( + prefs->GetBoolean(autofill::prefs::kAutofillCreditCardEnabled)); + EXPECT_FALSE(prefs->GetBoolean(autofill::prefs::kAutofillProfileEnabled)); EXPECT_TRUE(prefs->GetBoolean(prefs::kBlockThirdPartyCookies)); EXPECT_FALSE(prefs->GetBoolean(prefs::kEnableHyperlinkAuditing)); EXPECT_FALSE(prefs->GetBoolean(prefs::kEnableReferrers)); @@ -117,7 +123,9 @@ class ExtensionPreferenceApiTest : public extensions::ExtensionApiTest { IN_PROC_BROWSER_TEST_F(ExtensionPreferenceApiTest, MAYBE_Standard) { PrefService* prefs = profile_->GetPrefs(); prefs->SetBoolean(prefs::kAlternateErrorPagesEnabled, false); - prefs->SetBoolean(autofill::prefs::kAutofillEnabled, false); + prefs->SetBoolean(autofill::prefs::kAutofillEnabledDeprecated, false); + prefs->SetBoolean(autofill::prefs::kAutofillCreditCardEnabled, false); + prefs->SetBoolean(autofill::prefs::kAutofillProfileEnabled, false); prefs->SetBoolean(prefs::kBlockThirdPartyCookies, true); prefs->SetBoolean(prefs::kEnableHyperlinkAuditing, false); prefs->SetBoolean(prefs::kEnableReferrers, false); diff --git a/chromium/chrome/browser/extensions/api/processes/processes_api.cc b/chromium/chrome/browser/extensions/api/processes/processes_api.cc index 1de247454bc..517c2595222 100644 --- a/chromium/chrome/browser/extensions/api/processes/processes_api.cc +++ b/chromium/chrome/browser/extensions/api/processes/processes_api.cc @@ -520,7 +520,7 @@ base::ProcessHandle ProcessesTerminateFunction::GetProcessHandleOnIO( auto* host = content::BrowserChildProcessHost::FromID(child_process_host_id); if (host) - return host->GetData().handle; + return host->GetData().GetHandle(); return base::kNullProcessHandle; } diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc index 1faf2e10fa3..9bb8242b4e7 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api.cc @@ -111,7 +111,7 @@ std::unique_ptr<base::Value> ProxyPrefTransformer::ExtensionToBrowserPref( // chrome.proxy.settings.set(). Several of these strings will // remain blank no respective values have been passed to set(). // If a values has been passed to set but could not be parsed, we bail - // out and return NULL. + // out and return null. ProxyPrefs::ProxyMode mode_enum; bool pac_mandatory; std::string pac_url; @@ -144,9 +144,7 @@ std::unique_ptr<base::Value> ProxyPrefTransformer::BrowserToExtensionPref( // This is a dictionary wrapper that exposes the proxy configuration stored in // the browser preferences. - ProxyConfigDictionary config( - static_cast<const base::DictionaryValue*>(browser_pref) - ->CreateDeepCopy()); + ProxyConfigDictionary config(browser_pref->Clone()); ProxyPrefs::ProxyMode mode; if (!config.GetMode(&mode)) { 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 c3ad97eb799..153d46d923d 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.cc @@ -84,8 +84,10 @@ bool GetPacMandatoryFromExtensionPref(const base::DictionaryValue* proxy_config, const base::DictionaryValue* pac_dict = NULL; proxy_config->GetDictionary(proxy_api_constants::kProxyConfigPacScript, &pac_dict); - if (!pac_dict) + if (!pac_dict) { + *out = false; return true; + } bool mandatory_pac = false; if (pac_dict->HasKey(proxy_api_constants::kProxyConfigPacScriptMandatory) && @@ -322,7 +324,7 @@ bool GetBypassListFromExtensionPref(const base::DictionaryValue* proxy_config, return JoinUrlList(bypass_list, ",", out, error, bad_message); } -std::unique_ptr<base::DictionaryValue> CreateProxyConfigDict( +std::unique_ptr<base::Value> CreateProxyConfigDict( ProxyPrefs::ProxyMode mode_enum, bool pac_mandatory, const std::string& pac_url, @@ -330,14 +332,13 @@ std::unique_ptr<base::DictionaryValue> CreateProxyConfigDict( const std::string& proxy_rules_string, const std::string& bypass_list, std::string* error) { - std::unique_ptr<base::DictionaryValue> result_proxy_config; switch (mode_enum) { case ProxyPrefs::MODE_DIRECT: - result_proxy_config = ProxyConfigDictionary::CreateDirect(); - break; + return std::make_unique<base::Value>( + ProxyConfigDictionary::CreateDirect()); case ProxyPrefs::MODE_AUTO_DETECT: - result_proxy_config = ProxyConfigDictionary::CreateAutoDetect(); - break; + return std::make_unique<base::Value>( + ProxyConfigDictionary::CreateAutoDetect()); case ProxyPrefs::MODE_PAC_SCRIPT: { std::string url; if (!pac_url.empty()) { @@ -348,30 +349,30 @@ std::unique_ptr<base::DictionaryValue> CreateProxyConfigDict( return nullptr; } } else { - *error = "Proxy mode 'pac_script' requires a 'pacScript' field with " - "either a 'url' field or a 'data' field."; + *error = + "Proxy mode 'pac_script' requires a 'pacScript' field with " + "either a 'url' field or a 'data' field."; return nullptr; } - result_proxy_config = - ProxyConfigDictionary::CreatePacScript(url, pac_mandatory); - break; + return std::make_unique<base::Value>( + ProxyConfigDictionary::CreatePacScript(url, pac_mandatory)); } case ProxyPrefs::MODE_FIXED_SERVERS: { if (proxy_rules_string.empty()) { *error = "Proxy mode 'fixed_servers' requires a 'rules' field."; return nullptr; } - result_proxy_config = ProxyConfigDictionary::CreateFixedServers( - proxy_rules_string, bypass_list); - break; + return std::make_unique<base::Value>( + ProxyConfigDictionary::CreateFixedServers(proxy_rules_string, + bypass_list)); } case ProxyPrefs::MODE_SYSTEM: - result_proxy_config = ProxyConfigDictionary::CreateSystem(); - break; + return std::make_unique<base::Value>( + ProxyConfigDictionary::CreateSystem()); case ProxyPrefs::kModeCount: NOTREACHED(); } - return result_proxy_config; + return nullptr; } std::unique_ptr<base::DictionaryValue> CreateProxyRulesDict( diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h index 43428988aa6..00da93813d5 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers.h @@ -18,6 +18,7 @@ class ProxyConfigDictionary; namespace base { class DictionaryValue; class ListValue; +class Value; } namespace extensions { @@ -43,6 +44,8 @@ bool CreatePACScriptFromDataURL( // // - If there are NO entries for the respective pieces of data, the functions // return true. +// The GetPacMandatoryFromExtensionPref() function sets |out| to false in this +// case. // - If there ARE entries and they could be parsed, the functions set |out| // and return true. // - If there are entries that could not be parsed, the functions set |error| @@ -79,7 +82,7 @@ bool GetBypassListFromExtensionPref(const base::DictionaryValue* proxy_config, // Creates and returns a ProxyConfig dictionary (as defined in the extension // API) from the given parameters. Ownership is passed to the caller. // Depending on the value of |mode_enum|, several of the strings may be empty. -std::unique_ptr<base::DictionaryValue> CreateProxyConfigDict( +std::unique_ptr<base::Value> CreateProxyConfigDict( ProxyPrefs::ProxyMode mode_enum, bool pac_mandatory, const std::string& pac_url, diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc index 85ff80162b4..93eee864b1e 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_api_helpers_unittest.cc @@ -225,47 +225,44 @@ TEST(ExtensionProxyApiHelpers, GetBypassListFromExtensionPref) { TEST(ExtensionProxyApiHelpers, CreateProxyConfigDict) { std::string error; - std::unique_ptr<base::DictionaryValue> exp_direct = - ProxyConfigDictionary::CreateDirect(); - std::unique_ptr<base::DictionaryValue> out_direct(CreateProxyConfigDict( + base::Value exp_direct = ProxyConfigDictionary::CreateDirect(); + std::unique_ptr<base::Value> out_direct(CreateProxyConfigDict( ProxyPrefs::MODE_DIRECT, false, std::string(), std::string(), std::string(), std::string(), &error)); - EXPECT_EQ(*exp_direct, *out_direct); + EXPECT_EQ(exp_direct, *out_direct); - std::unique_ptr<base::DictionaryValue> exp_auto = - ProxyConfigDictionary::CreateAutoDetect(); - std::unique_ptr<base::DictionaryValue> out_auto(CreateProxyConfigDict( + base::Value exp_auto = ProxyConfigDictionary::CreateAutoDetect(); + std::unique_ptr<base::Value> out_auto(CreateProxyConfigDict( ProxyPrefs::MODE_AUTO_DETECT, false, std::string(), std::string(), std::string(), std::string(), &error)); - EXPECT_EQ(*exp_auto, *out_auto); + EXPECT_EQ(exp_auto, *out_auto); - std::unique_ptr<base::DictionaryValue> exp_pac_url = + base::Value exp_pac_url = ProxyConfigDictionary::CreatePacScript(kSamplePacScriptUrl, false); - std::unique_ptr<base::DictionaryValue> out_pac_url(CreateProxyConfigDict( + std::unique_ptr<base::Value> out_pac_url(CreateProxyConfigDict( ProxyPrefs::MODE_PAC_SCRIPT, false, kSamplePacScriptUrl, std::string(), std::string(), std::string(), &error)); - EXPECT_EQ(*exp_pac_url, *out_pac_url); + EXPECT_EQ(exp_pac_url, *out_pac_url); - std::unique_ptr<base::DictionaryValue> exp_pac_data = + base::Value exp_pac_data = ProxyConfigDictionary::CreatePacScript(kSamplePacScriptAsDataUrl, false); - std::unique_ptr<base::DictionaryValue> out_pac_data(CreateProxyConfigDict( + std::unique_ptr<base::Value> out_pac_data(CreateProxyConfigDict( ProxyPrefs::MODE_PAC_SCRIPT, false, std::string(), kSamplePacScript, std::string(), std::string(), &error)); - EXPECT_EQ(*exp_pac_data, *out_pac_data); + EXPECT_EQ(exp_pac_data, *out_pac_data); - std::unique_ptr<base::DictionaryValue> exp_fixed = + base::Value exp_fixed = ProxyConfigDictionary::CreateFixedServers("foo:80", "localhost"); - std::unique_ptr<base::DictionaryValue> out_fixed(CreateProxyConfigDict( + std::unique_ptr<base::Value> out_fixed(CreateProxyConfigDict( ProxyPrefs::MODE_FIXED_SERVERS, false, std::string(), std::string(), "foo:80", "localhost", &error)); - EXPECT_EQ(*exp_fixed, *out_fixed); + EXPECT_EQ(exp_fixed, *out_fixed); - std::unique_ptr<base::DictionaryValue> exp_system = - ProxyConfigDictionary::CreateSystem(); - std::unique_ptr<base::DictionaryValue> out_system(CreateProxyConfigDict( + base::Value exp_system = ProxyConfigDictionary::CreateSystem(); + std::unique_ptr<base::Value> out_system(CreateProxyConfigDict( ProxyPrefs::MODE_SYSTEM, false, std::string(), std::string(), std::string(), std::string(), &error)); - EXPECT_EQ(*exp_system, *out_system); + EXPECT_EQ(exp_system, *out_system); // Neither of them should have set an error. EXPECT_EQ(std::string(), error); @@ -311,11 +308,9 @@ TEST(ExtensionProxyApiHelpers, JoinUrlList) { // This tests CreateProxyServerDict as well. TEST(ExtensionProxyApiHelpers, CreateProxyRulesDict) { - std::unique_ptr<base::DictionaryValue> browser_pref( - ProxyConfigDictionary::CreateFixedServers( - "http=proxy1:80;https=proxy2:80;ftp=proxy3:80;socks=proxy4:80", - "localhost")); - ProxyConfigDictionary config(std::move(browser_pref)); + ProxyConfigDictionary config(ProxyConfigDictionary::CreateFixedServers( + "http=proxy1:80;https=proxy2:80;ftp=proxy3:80;socks=proxy4:80", + "localhost")); std::unique_ptr<base::DictionaryValue> extension_pref( CreateProxyRulesDict(config)); ASSERT_TRUE(extension_pref.get()); @@ -338,12 +333,10 @@ TEST(ExtensionProxyApiHelpers, CreateProxyRulesDict) { // Test multiple proxies per scheme -- expect that only the first is returned. TEST(ExtensionProxyApiHelpers, CreateProxyRulesDictMultipleProxies) { - std::unique_ptr<base::DictionaryValue> browser_pref( - ProxyConfigDictionary::CreateFixedServers( - "http=proxy1:80,default://;https=proxy2:80,proxy1:80;ftp=proxy3:80," - "https://proxy5:443;socks=proxy4:80,proxy1:80", - "localhost")); - ProxyConfigDictionary config(std::move(browser_pref)); + ProxyConfigDictionary config(ProxyConfigDictionary::CreateFixedServers( + "http=proxy1:80,default://;https=proxy2:80,proxy1:80;ftp=proxy3:80," + "https://proxy5:443;socks=proxy4:80,proxy1:80", + "localhost")); std::unique_ptr<base::DictionaryValue> extension_pref( CreateProxyRulesDict(config)); ASSERT_TRUE(extension_pref.get()); @@ -366,9 +359,8 @@ TEST(ExtensionProxyApiHelpers, CreateProxyRulesDictMultipleProxies) { // Test if a PAC script URL is specified. TEST(ExtensionProxyApiHelpers, CreatePacScriptDictWithUrl) { - std::unique_ptr<base::DictionaryValue> browser_pref( + ProxyConfigDictionary config( ProxyConfigDictionary::CreatePacScript(kSamplePacScriptUrl, false)); - ProxyConfigDictionary config(std::move(browser_pref)); std::unique_ptr<base::DictionaryValue> extension_pref( CreatePacScriptDict(config)); ASSERT_TRUE(extension_pref.get()); @@ -382,9 +374,8 @@ TEST(ExtensionProxyApiHelpers, CreatePacScriptDictWithUrl) { // Test if a PAC script is encoded in a data URL. TEST(ExtensionProxyApiHelpers, CreatePacScriptDictWidthData) { - std::unique_ptr<base::DictionaryValue> browser_pref( + ProxyConfigDictionary config( ProxyConfigDictionary::CreatePacScript(kSamplePacScriptAsDataUrl, false)); - ProxyConfigDictionary config(std::move(browser_pref)); std::unique_ptr<base::DictionaryValue> extension_pref( CreatePacScriptDict(config)); ASSERT_TRUE(extension_pref.get()); diff --git a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc index 981c9b2d1b2..2d954803cb0 100644 --- a/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc +++ b/chromium/chrome/browser/extensions/api/proxy/proxy_apitest.cc @@ -43,8 +43,7 @@ class ProxySettingsApiTest : public ExtensionApiTest { EXPECT_TRUE(pref->IsExtensionControlled()); ProxyConfigDictionary dict( - pref_service->GetDictionary(proxy_config::prefs::kProxy) - ->CreateDeepCopy()); + pref_service->GetDictionary(proxy_config::prefs::kProxy)->Clone()); ProxyPrefs::ProxyMode mode; ASSERT_TRUE(dict.GetMode(&mode)); @@ -397,20 +396,10 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, pref_service); } -// This test sets proxy to an inavalid host "does.not.exist" and then fetches -// a page from localhost, expecting an error since host is invalid. -// On ChromeOS, localhost is by default bypassed, so the page from localhost -// will be fetched successfully, resulting in no error. Hence this test -// shouldn't run on ChromeOS. -#if defined(OS_CHROMEOS) -#define MAYBE_ProxyEventsInvalidProxy DISABLED_ProxyEventsInvalidProxy -#else -#define MAYBE_ProxyEventsInvalidProxy ProxyEventsInvalidProxy -#endif // defined(OS_CHROMEOS) - -// Tests error events: invalid proxy -IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, MAYBE_ProxyEventsInvalidProxy) { - ASSERT_TRUE(StartEmbeddedTestServer()); +// This test sets the HTTP proxy to an unreachable host "does.not.exist" and +// then attempts to fetch "example.test", expecting the listeners of +// chrome.proxy.onProxyError to fire with ERR_PROXY_CONNECTION_FAILED. +IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyEventsInvalidProxy) { ASSERT_TRUE( RunExtensionSubtest("proxy/events", "invalid_proxy.html")) << message_; } @@ -421,4 +410,11 @@ IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyEventsParseError) { RunExtensionSubtest("proxy/events", "parse_error.html")) << message_; } +// Tests that chrome.proxy.onProxyError is NOT called in the case of a +// non-proxy error. +IN_PROC_BROWSER_TEST_F(ProxySettingsApiTest, ProxyEventsOtherError) { + ASSERT_TRUE(RunExtensionSubtest("proxy/events", "other_error.html")) + << message_; +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc index 6b5dc75cc8a..20644c9da22 100644 --- a/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc +++ b/chromium/chrome/browser/extensions/api/sessions/sessions_apitest.cc @@ -268,7 +268,7 @@ void ExtensionSessionsTest::CreateSessionModels() { std::unique_ptr<syncer::DataTypeActivationResponse> activation_response; base::RunLoop loop; ProfileSyncServiceFactory::GetForProfile(browser_->profile()) - ->GetSessionSyncControllerDelegateOnUIThread() + ->GetSessionSyncControllerDelegate() ->OnSyncStarting( request, base::BindLambdaForTesting( [&](std::unique_ptr<syncer::DataTypeActivationResponse> 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 a634903329b..3222055de56 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h +++ b/chromium/chrome/browser/extensions/api/settings_private/generated_pref.h @@ -63,7 +63,7 @@ class GeneratedPref { void NotifyObservers(const std::string& pref_name); private: - base::ObserverList<Observer> observers_; + base::ObserverList<Observer>::Unchecked observers_; DISALLOW_COPY_AND_ASSIGN(GeneratedPref); }; 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 a64508aaa09..cda1e3351d1 100644 --- a/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chromium/chrome/browser/extensions/api/settings_private/prefs_util.cc @@ -14,7 +14,7 @@ #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "chromeos/components/proximity_auth/proximity_auth_pref_names.h" -#include "components/autofill/core/common/autofill_pref_names.h" +#include "components/autofill/core/common/autofill_prefs.h" #include "components/bookmarks/common/bookmark_pref_names.h" #include "components/browsing_data/core/pref_names.h" #include "components/component_updater/pref_names.h" @@ -117,8 +117,6 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { // Miscellaneous (*s_whitelist)[::prefs::kAlternateErrorPagesEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; - (*s_whitelist)[autofill::prefs::kAutofillEnabled] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[autofill::prefs::kAutofillProfileEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[autofill::prefs::kAutofillCreditCardEnabled] = @@ -178,6 +176,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[drive::prefs::kDisableDrive] = settings_api::PrefType::PREF_TYPE_BOOLEAN; +#if defined(OS_CHROMEOS) + (*s_whitelist)[::prefs::kNetworkFileSharesAllowed] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; +#endif // Printing settings. (*s_whitelist)[::prefs::kLocalDiscoveryNotificationsEnabled] = @@ -197,6 +199,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { (*s_whitelist)[password_manager::prefs::kCredentialsEnableAutosignin] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + // Privacy page + (*s_whitelist)[::prefs::kSigninAllowedOnNextStartup] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; + // Sync and personalization page. (*s_whitelist)[::prefs::kSafeBrowsingEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; @@ -289,12 +295,12 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { // kEnableAutoScreenLock is read-only. (*s_whitelist)[ash::prefs::kEnableAutoScreenLock] = settings_api::PrefType::PREF_TYPE_BOOLEAN; - (*s_whitelist)[::prefs::kEnableQuickUnlockFingerprint] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[proximity_auth::prefs::kEasyUnlockProximityThreshold] = settings_api::PrefType::PREF_TYPE_NUMBER; (*s_whitelist)[proximity_auth::prefs::kProximityAuthIsChromeOSLoginEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[ash::prefs::kMessageCenterLockScreenMode] = + settings_api::PrefType::PREF_TYPE_STRING; // Accessibility. (*s_whitelist)[ash::prefs::kAccessibilitySpokenFeedbackEnabled] = @@ -363,6 +369,8 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[arc::prefs::kVoiceInteractionHotwordEnabled] = settings_api::PrefType::PREF_TYPE_BOOLEAN; + (*s_whitelist)[arc::prefs::kVoiceInteractionNotificationEnabled] = + settings_api::PrefType::PREF_TYPE_BOOLEAN; // Misc. (*s_whitelist)[::prefs::kUse24HourClock] = @@ -461,6 +469,10 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { settings_api::PrefType::PREF_TYPE_NUMBER; (*s_whitelist)[::prefs::kLanguageRemapDiamondKeyTo] = settings_api::PrefType::PREF_TYPE_NUMBER; + (*s_whitelist)[::prefs::kLanguageRemapExternalCommandKeyTo] = + settings_api::PrefType::PREF_TYPE_NUMBER; + (*s_whitelist)[::prefs::kLanguageRemapExternalMetaKeyTo] = + settings_api::PrefType::PREF_TYPE_NUMBER; (*s_whitelist)[::prefs::kLanguageSendFunctionKeys] = settings_api::PrefType::PREF_TYPE_BOOLEAN; (*s_whitelist)[::prefs::kLanguageXkbAutoRepeatEnabled] = @@ -470,10 +482,6 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetWhitelistedKeys() { (*s_whitelist)[::prefs::kLanguageXkbAutoRepeatInterval] = settings_api::PrefType::PREF_TYPE_NUMBER; - // Multidevice settings. - (*s_whitelist)[arc::prefs::kSmsConnectEnabled] = - settings_api::PrefType::PREF_TYPE_BOOLEAN; - // Native Printing settings. (*s_whitelist)[::prefs::kUserNativePrintersAllowed] = settings_api::PrefType::PREF_TYPE_BOOLEAN; diff --git a/chromium/chrome/browser/extensions/api/socket/combined_socket_unittest.cc b/chromium/chrome/browser/extensions/api/socket/combined_socket_unittest.cc deleted file mode 100644 index 6feb6fe9361..00000000000 --- a/chromium/chrome/browser/extensions/api/socket/combined_socket_unittest.cc +++ /dev/null @@ -1,145 +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. - -#include <memory> - -#include "chrome/browser/extensions/api/socket/mock_tcp_client_socket.h" -#include "extensions/browser/api/socket/socket.h" -#include "extensions/browser/api/socket/tcp_socket.h" -#include "extensions/browser/api/socket/tls_socket.h" -#include "net/socket/stream_socket.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace extensions { - -const int kBufferLength = 10; - -template <typename T> -std::unique_ptr<T> CreateTestSocket( - std::unique_ptr<MockTCPClientSocket> stream); - -template <> -std::unique_ptr<TCPSocket> CreateTestSocket( - std::unique_ptr<MockTCPClientSocket> stream) { - return std::make_unique<TCPSocket>(std::move(stream), "fake id", - true /* is_connected */); -} - -template <> -std::unique_ptr<TLSSocket> CreateTestSocket( - std::unique_ptr<MockTCPClientSocket> stream) { - return std::make_unique<TLSSocket>(std::move(stream), "fake id"); -} - -class CombinedSocketTest : public testing::Test { - public: - CombinedSocketTest() : count_(0), io_buffer_(nullptr) {} - - // Strict test for synchronous (immediate) read behavior - template <typename T> - void TestRead() { - net::IOBuffer* buffer = nullptr; - - std::unique_ptr<MockTCPClientSocket> stream( - new testing::StrictMock<MockTCPClientSocket>()); - EXPECT_CALL(*stream, Read(testing::NotNull(), kBufferLength, testing::_)) - .WillOnce(DoAll(testing::SaveArg<0>(&buffer), - testing::Return(kBufferLength))); - EXPECT_CALL(*stream, Disconnect()); - - std::unique_ptr<T> socket = CreateTestSocket<T>(std::move(stream)); - ReadCompletionCallback read_callback = - base::Bind(&CombinedSocketTest::OnRead, base::Unretained(this)); - socket->Read(kBufferLength, read_callback); - EXPECT_EQ(kBufferLength, count_); - EXPECT_NE(nullptr, buffer); - EXPECT_EQ(buffer, io_buffer_); - } - - // Strict test for async read behavior (read returns PENDING) - template <typename T> - void TestReadPending() { - net::IOBuffer* buffer = nullptr; - net::CompletionCallback socket_cb; - - std::unique_ptr<MockTCPClientSocket> stream( - new testing::StrictMock<MockTCPClientSocket>()); - EXPECT_CALL(*stream, Read(testing::NotNull(), kBufferLength, testing::_)) - .WillOnce(DoAll(testing::SaveArg<0>(&buffer), - testing::SaveArg<2>(&socket_cb), - testing::Return(net::ERR_IO_PENDING))); - EXPECT_CALL(*stream, Disconnect()); - - std::unique_ptr<T> socket = CreateTestSocket<T>(std::move(stream)); - ReadCompletionCallback read_callback = - base::Bind(&CombinedSocketTest::OnRead, base::Unretained(this)); - socket->Read(kBufferLength, read_callback); - EXPECT_EQ(0, count_); - EXPECT_EQ(nullptr, io_buffer_); - socket_cb.Run(kBufferLength); - EXPECT_EQ(kBufferLength, count_); - EXPECT_NE(nullptr, buffer); - EXPECT_EQ(buffer, io_buffer_); - } - - // Even if the socket is closed, it may still have data left to read. - template <typename T> - void TestReadAfterDisconnect() { - net::IOBuffer* buffer = nullptr; - - std::unique_ptr<MockTCPClientSocket> stream( - new testing::NiceMock<MockTCPClientSocket>()); - EXPECT_CALL(*stream, Read(testing::NotNull(), kBufferLength, testing::_)) - .WillOnce(DoAll(testing::SaveArg<0>(&buffer), - testing::Return(kBufferLength))); - ON_CALL(*stream, IsConnected()).WillByDefault(testing::Return(false)); - EXPECT_CALL(*stream, Disconnect()); - - std::unique_ptr<T> socket = CreateTestSocket<T>(std::move(stream)); - ReadCompletionCallback read_callback = - base::Bind(&CombinedSocketTest::OnRead, base::Unretained(this)); - socket->Read(kBufferLength, read_callback); - EXPECT_EQ(kBufferLength, count_); - EXPECT_NE(nullptr, buffer); - EXPECT_EQ(buffer, io_buffer_); - } - - void OnRead(int count, - scoped_refptr<net::IOBuffer> io_buffer, - bool socket_destroying) { - count_ = count; - io_buffer_ = io_buffer.get(); - } - - protected: - int count_; - net::IOBuffer* io_buffer_; -}; - -TEST_F(CombinedSocketTest, TlsRead) { - TestRead<TLSSocket>(); -} - -TEST_F(CombinedSocketTest, TcpRead) { - TestRead<TCPSocket>(); -} - -TEST_F(CombinedSocketTest, TlsReadPending) { - TestReadPending<TLSSocket>(); -} - -TEST_F(CombinedSocketTest, TcpReadPending) { - TestReadPending<TCPSocket>(); -} - -TEST_F(CombinedSocketTest, TlsReadAfterDisconnect) { - TestReadAfterDisconnect<TLSSocket>(); -} - -TEST_F(CombinedSocketTest, TcpReadAfterDisconnect) { - TestReadAfterDisconnect<TCPSocket>(); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/socket/mock_tcp_client_socket.h b/chromium/chrome/browser/extensions/api/socket/mock_tcp_client_socket.h deleted file mode 100644 index fddad102ac6..00000000000 --- a/chromium/chrome/browser/extensions/api/socket/mock_tcp_client_socket.h +++ /dev/null @@ -1,82 +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_SOCKET_MOCK_TCP_CLIENT_SOCKET_H_ -#define CHROME_BROWSER_EXTENSIONS_API_SOCKET_MOCK_TCP_CLIENT_SOCKET_H_ - -#include "base/callback_helpers.h" -#include "net/log/net_log_source.h" -#include "net/log/net_log_with_source.h" -#include "net/socket/tcp_client_socket.h" -#include "net/traffic_annotation/network_traffic_annotation_test_helper.h" -#include "testing/gmock/include/gmock/gmock.h" - -namespace extensions { -class MockTCPClientSocket : public net::TCPClientSocket { - public: - MockTCPClientSocket(); - ~MockTCPClientSocket() override; - - int Read(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback) override { - return Read(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback))); - } - - int Write(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback, - const net::NetworkTrafficAnnotationTag& tag) override { - return Write(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback)), tag); - } - - int Connect(net::CompletionOnceCallback callback) override { - return Connect(base::AdaptCallbackForRepeating(std::move(callback))); - } - - MOCK_METHOD3(Read, int(net::IOBuffer*, int, const net::CompletionCallback&)); - MOCK_METHOD4(Write, - int(net::IOBuffer*, - int, - const net::CompletionCallback&, - const net::NetworkTrafficAnnotationTag&)); - MOCK_METHOD1(SetReceiveBufferSize, int(int32_t)); - MOCK_METHOD1(SetSendBufferSize, int(int32_t)); - MOCK_METHOD1(Connect, int(const net::CompletionCallback&)); - MOCK_METHOD0(Disconnect, void()); - MOCK_CONST_METHOD0(IsConnected, bool()); - MOCK_CONST_METHOD0(IsConnectedAndIdle, bool()); - MOCK_CONST_METHOD1(GetPeerAddress, int(net::IPEndPoint*)); - MOCK_CONST_METHOD1(GetLocalAddress, int(net::IPEndPoint*)); - MOCK_CONST_METHOD0(NetLog, const net::NetLogWithSource&()); - MOCK_CONST_METHOD0(WasEverUsed, bool()); - MOCK_CONST_METHOD0(UsingTCPFastOpen, bool()); - MOCK_CONST_METHOD0(NumBytesRead, int64_t()); - MOCK_CONST_METHOD0(GetConnectTimeMicros, base::TimeDelta()); - MOCK_CONST_METHOD0(WasAlpnNegotiated, bool()); - MOCK_CONST_METHOD0(GetNegotiatedProtocol, net::NextProto()); - MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*)); - MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*)); - MOCK_METHOD0(ClearConnectionAttempts, void()); - MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&)); - MOCK_CONST_METHOD0(GetTotalReceivedBytes, int64_t()); - - // Methods specific to MockTCPClientSocket - MOCK_METHOD1(Bind, int(const net::IPEndPoint&)); - MOCK_METHOD2(SetKeepAlive, bool(bool, int)); - MOCK_METHOD1(SetNoDelay, bool(bool)); -}; - -MockTCPClientSocket::MockTCPClientSocket() - : TCPClientSocket(net::AddressList(), - nullptr, - nullptr, - net::NetLogSource()) {} -MockTCPClientSocket::~MockTCPClientSocket() {} - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_SOCKET_MOCK_TCP_CLIENT_SOCKET_H_ diff --git a/chromium/chrome/browser/extensions/api/socket/socket_apitest.cc b/chromium/chrome/browser/extensions/api/socket/socket_apitest.cc index ae11686ed95..ee35b81809e 100644 --- a/chromium/chrome/browser/extensions/api/socket/socket_apitest.cc +++ b/chromium/chrome/browser/extensions/api/socket/socket_apitest.cc @@ -9,8 +9,6 @@ #include "chrome/browser/ui/browser.h" #include "chrome/common/chrome_paths.h" #include "chrome/test/base/in_process_browser_test.h" -#include "extensions/browser/api/dns/host_resolver_wrapper.h" -#include "extensions/browser/api/dns/mock_host_resolver_creator.h" #include "extensions/browser/api/socket/socket_api.h" #include "extensions/test/extension_test_message_listener.h" #include "extensions/test/result_catcher.h" @@ -22,35 +20,15 @@ using extensions::ResultCatcher; namespace { -const char kHostname[] = "127.0.0.1"; +const char kHostname[] = "www.foo.com"; const int kPort = 8888; class SocketApiTest : public extensions::ExtensionApiTest { public: - SocketApiTest() - : resolver_event_(base::WaitableEvent::ResetPolicy::MANUAL, - base::WaitableEvent::InitialState::NOT_SIGNALED), - resolver_creator_(new extensions::MockHostResolverCreator()) {} - void SetUpOnMainThread() override { extensions::ExtensionApiTest::SetUpOnMainThread(); - extensions::HostResolverWrapper::GetInstance()->SetHostResolverForTesting( - resolver_creator_->CreateMockHostResolver()); - } - - void TearDownOnMainThread() override { - extensions::HostResolverWrapper::GetInstance()-> - SetHostResolverForTesting(NULL); - resolver_creator_->DeleteMockHostResolver(); + host_resolver()->AddRule(kHostname, "127.0.0.1"); } - - private: - base::WaitableEvent resolver_event_; - - // The MockHostResolver asserts that it's used on the same thread on which - // it's created, which is actually a stronger rule than its real counterpart. - // But that's fine; it's good practice. - scoped_refptr<extensions::MockHostResolverCreator> resolver_creator_; }; } // namespace @@ -67,7 +45,7 @@ IN_PROC_BROWSER_TEST_F(SocketApiTest, SocketUDPExtension) { ASSERT_GT(port, 0); // Test that sendTo() is properly resolving hostnames. - host_port_pair.set_host("LOCALhost"); + host_port_pair.set_host(kHostname); ResultCatcher catcher; catcher.RestrictToBrowserContext(browser()->profile()); @@ -115,7 +93,7 @@ IN_PROC_BROWSER_TEST_F(SocketApiTest, SocketTCPServerExtension) { ExtensionTestMessageListener listener("info_please", true); ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("socket/api"))); EXPECT_TRUE(listener.WaitUntilSatisfied()); - listener.Reply(base::StringPrintf("tcp_server:%s:%d", kHostname, kPort)); + listener.Reply(base::StringPrintf("tcp_server:127.0.0.1:%d", kPort)); EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); } diff --git a/chromium/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc b/chromium/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc index 6351e3d6ca2..8fc0c4bfcca 100644 --- a/chromium/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc +++ b/chromium/chrome/browser/extensions/api/socket/tcp_socket_unittest.cc @@ -4,274 +4,719 @@ #include <memory> -#include "base/callback_helpers.h" #include "base/macros.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/bind_test_util.h" +#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/test_storage_partition.h" #include "extensions/browser/api/socket/tcp_socket.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" -#include "net/base/rand_callback.h" -#include "net/log/net_log_source.h" -#include "net/socket/tcp_client_socket.h" -#include "net/socket/tcp_server_socket.h" +#include "net/base/test_completion_callback.h" +#include "net/socket/socket_test_util.h" #include "net/traffic_annotation/network_traffic_annotation.h" -#include "testing/gmock/include/gmock/gmock.h" - -using testing::_; -using testing::DoAll; -using testing::Return; -using testing::SaveArg; +#include "net/url_request/url_request_test_util.h" +#include "services/network/network_context.h" +#include "services/network/public/mojom/network_context.mojom.h" namespace extensions { -class MockTCPSocket : public net::TCPClientSocket { +namespace { + +const char kTestMsg[] = "abcdefghij"; +const int kTestMsgLength = strlen(kTestMsg); + +const char FAKE_ID[] = "abcdefghijklmnopqrst"; + +class TCPSocketUnitTestBase : public extensions::ExtensionServiceTestBase { public: - explicit MockTCPSocket(const net::AddressList& address_list) - : net::TCPClientSocket(address_list, NULL, NULL, net::NetLogSource()) {} - - int Read(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback) override { - return Read(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback))); + TCPSocketUnitTestBase() + : url_request_context_(true /* delay_initialization */) {} + ~TCPSocketUnitTestBase() override {} + + std::unique_ptr<TCPSocket> CreateSocket() { + auto socket = std::make_unique<TCPSocket>(&profile_, FAKE_ID); + socket->SetStoragePartitionForTest(&partition_); + return socket; } - int Write(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback, - const net::NetworkTrafficAnnotationTag& tag) override { - return Write(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback)), tag); + std::unique_ptr<TCPSocket> CreateAndConnectSocketWithAddress( + const net::IPEndPoint& ip_end_point) { + auto socket = CreateSocket(); + net::AddressList address(ip_end_point); + net::TestCompletionCallback callback; + socket->Connect(address, callback.callback()); + EXPECT_EQ(net::OK, callback.WaitForResult()); + return socket; } - MOCK_METHOD3(Read, int(net::IOBuffer* buf, int buf_len, - const net::CompletionCallback& callback)); - MOCK_METHOD4(Write, - int(net::IOBuffer* buf, - int buf_len, - const net::CompletionCallback& callback, - const net::NetworkTrafficAnnotationTag&)); - MOCK_METHOD2(SetKeepAlive, bool(bool enable, int delay)); - MOCK_METHOD1(SetNoDelay, bool(bool no_delay)); - bool IsConnected() const override { - return true; + std::unique_ptr<TCPSocket> CreateAndConnectSocket() { + net::IPEndPoint ip_end_point(net::IPAddress::IPv4Localhost(), 1234); + return CreateAndConnectSocketWithAddress(ip_end_point); } - private: - DISALLOW_COPY_AND_ASSIGN(MockTCPSocket); -}; + // Reads data from |socket| and compares it with |expected_data|. + void ReadData(Socket* socket, const std::string& expected_data) { + std::string received_data; + const int count = 512; + while (true) { + base::RunLoop run_loop; + int net_error = net::ERR_FAILED; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_error = result; + EXPECT_FALSE(socket_destroying); + if (result > 0) + received_data.append(io_buffer->data(), result); + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; + } + EXPECT_EQ(expected_data, received_data); + } -class MockTCPServerSocket : public net::TCPServerSocket { - public: - MockTCPServerSocket() : net::TCPServerSocket(NULL, net::NetLogSource()) {} - MOCK_METHOD2(Listen, int(const net::IPEndPoint& address, int backlog)); - MOCK_METHOD2(Accept, - int(std::unique_ptr<net::StreamSocket>* socket, - const net::CompletionCallback& callback)); + protected: + // extensions::ExtensionServiceTestBase implementation. + void SetUp() override { InitializeEmptyExtensionService(); } + + void Initialize() { + url_request_context_.Init(); + network_context_ = std::make_unique<network::NetworkContext>( + nullptr, mojo::MakeRequest(&network_context_ptr_), + &url_request_context_); + partition_.set_network_context(network_context_ptr_.get()); + } + + net::TestURLRequestContext url_request_context_; private: - DISALLOW_COPY_AND_ASSIGN(MockTCPServerSocket); + TestingProfile profile_; + content::TestStoragePartition partition_; + std::unique_ptr<network::NetworkContext> network_context_; + network::mojom::NetworkContextPtr network_context_ptr_; }; -class CompleteHandler { +} // namespace + +class TCPSocketUnitTest : public TCPSocketUnitTestBase, + public ::testing::WithParamInterface<net::IoMode> { public: - CompleteHandler() {} - MOCK_METHOD1(OnComplete, void(int result_code)); - MOCK_METHOD3(OnReadComplete, - void(int result_code, - scoped_refptr<net::IOBuffer> io_buffer, - bool socket_destroying)); - - // MOCK_METHOD cannot mock a scoped_ptr argument. - MOCK_METHOD2(OnAcceptMock, void(int, net::TCPClientSocket*)); - void OnAccept(int count, std::unique_ptr<net::TCPClientSocket> socket) { - OnAcceptMock(count, socket.get()); + TCPSocketUnitTest() : TCPSocketUnitTestBase() { + mock_client_socket_factory_.set_enable_read_if_ready(true); + url_request_context_.set_client_socket_factory( + &mock_client_socket_factory_); + Initialize(); + } + ~TCPSocketUnitTest() override {} + + net::MockClientSocketFactory* mock_client_socket_factory() { + return &mock_client_socket_factory_; } private: - DISALLOW_COPY_AND_ASSIGN(CompleteHandler); + net::MockClientSocketFactory mock_client_socket_factory_; }; -const char FAKE_ID[] = "abcdefghijklmnopqrst"; +INSTANTIATE_TEST_CASE_P(/* no prefix */, + TCPSocketUnitTest, + testing::Values(net::SYNCHRONOUS, net::ASYNC)); + +TEST_F(TCPSocketUnitTest, SocketConnectError) { + net::IPEndPoint ip_end_point(net::IPAddress::IPv4Localhost(), 1234); + net::StaticSocketDataProvider data_provider((base::span<net::MockRead>()), + base::span<net::MockWrite>()); + data_provider.set_connect_data( + net::MockConnect(net::ASYNC, net::ERR_FAILED, ip_end_point)); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateSocket(); + + net::AddressList address(ip_end_point); + net::TestCompletionCallback callback; + socket->Connect(address, callback.callback()); + EXPECT_EQ(net::ERR_FAILED, callback.WaitForResult()); +} + +TEST_P(TCPSocketUnitTest, SocketConnectAfterDisconnect) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = {net::MockRead(io_mode, net::OK)}; + net::StaticSocketDataProvider data_provider1(kReads, + base::span<net::MockWrite>()); + net::StaticSocketDataProvider data_provider2(kReads, + base::span<net::MockWrite>()); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider1); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider2); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + socket->Disconnect(false /* socket_destroying */); + net::TestCompletionCallback callback2; + net::IPEndPoint ip_end_point(net::IPAddress::IPv4Localhost(), 1234); + net::AddressList address(ip_end_point); + socket->Connect(address, callback2.callback()); + EXPECT_EQ(net::OK, callback2.WaitForResult()); + + EXPECT_TRUE(data_provider1.AllReadDataConsumed()); + EXPECT_TRUE(data_provider1.AllWriteDataConsumed()); + EXPECT_TRUE(data_provider2.AllReadDataConsumed()); + EXPECT_TRUE(data_provider2.AllWriteDataConsumed()); +} + +TEST_F(TCPSocketUnitTest, DestroyWhileReadPending) { + const net::MockRead kReads[] = { + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + int net_result = net::ERR_FAILED; + base::RunLoop run_loop; + int count = 1; + // Read one byte, and it should be pending. + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_result = result; + // |socket_destroying| should correctly denote that this + // read callback is invoked through the destructor of + // TCPSocket. + EXPECT_TRUE(socket_destroying); + run_loop.Quit(); + })); + // Destroy socket. + socket = nullptr; + // Wait for read callback. + run_loop.Run(); + EXPECT_EQ(net::ERR_CONNECTION_CLOSED, net_result); +} -TEST(SocketTest, TestTCPSocketRead) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); - CompleteHandler handler; +TEST_P(TCPSocketUnitTest, Read) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(io_mode, kTestMsg, kTestMsgLength), + net::MockRead(io_mode, net::OK)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); - EXPECT_CALL(*tcp_client_socket, Read(_, _, _)) - .Times(1); - EXPECT_CALL(handler, OnReadComplete(_, _, _)).Times(1); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + ReadData(socket.get(), kTestMsg); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); +} + +// Tests the case where a message is split over two separate socket reads. +TEST_P(TCPSocketUnitTest, SocketMultipleRead) { + const char kFirstHalfTestMsg[] = "abcde"; + const char kSecondHalfTestMsg[] = "fghij"; + EXPECT_EQ(kTestMsg, std::string(kFirstHalfTestMsg) + kSecondHalfTestMsg); + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(io_mode, kFirstHalfTestMsg, strlen(kFirstHalfTestMsg)), + net::MockRead(io_mode, kSecondHalfTestMsg, strlen(kSecondHalfTestMsg)), + net::MockRead(io_mode, net::OK)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + ReadData(socket.get(), kTestMsg); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); +} + +// Tests the case where read size is smaller than the actual message. +TEST_P(TCPSocketUnitTest, SocketPartialRead) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(io_mode, kTestMsg, kTestMsgLength), + net::MockRead(io_mode, net::OK)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + int count = 1; + std::string received_data; + while (true) { + int net_result = net::ERR_FAILED; + base::RunLoop run_loop; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_result = result; + EXPECT_FALSE(socket_destroying); + if (result > 0) + received_data.append(io_buffer->data(), result); + run_loop.Quit(); + })); + run_loop.Run(); + if (net_result <= 0) + break; + // Double the read size in the next iteration. + count *= 2; + } + EXPECT_EQ(kTestMsg, received_data); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); +} - std::unique_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( - std::move(tcp_client_socket), FAKE_ID, true)); +TEST_P(TCPSocketUnitTest, ReadError) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(io_mode, kTestMsg, kTestMsgLength), + net::MockRead(io_mode, net::ERR_INSUFFICIENT_RESOURCES)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); const int count = 512; - socket->Read(count, base::Bind(&CompleteHandler::OnReadComplete, - base::Unretained(&handler))); + int net_error = net::OK; + while (true) { + base::RunLoop run_loop; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_error = result; + EXPECT_FALSE(socket_destroying); + if (result <= 0) { + EXPECT_FALSE(socket->IsConnected()); + EXPECT_EQ(nullptr, io_buffer); + } else { + EXPECT_TRUE(socket->IsConnected()); + } + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; + } + // Note that TCPSocket only detects that receive pipe is broken and propagates + // it as 0 byte read. It doesn't know the specific net error code. To know the + // specific net error code, it needs to register itself as a + // network::mojom::SocketObserver. However, that gets tricky because of two + // separate mojo pipes. + EXPECT_EQ(0, net_error); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); } -TEST(SocketTest, TestTCPSocketWrite) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); - CompleteHandler handler; - - net::CompletionCallback callback; - EXPECT_CALL(*tcp_client_socket, Write(_, _, _, _)) - .Times(2) - .WillRepeatedly(testing::DoAll(SaveArg<2>(&callback), Return(128))); - EXPECT_CALL(handler, OnComplete(_)) - .Times(1); - - std::unique_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( - std::move(tcp_client_socket), FAKE_ID, true)); - - scoped_refptr<net::IOBufferWithSize> io_buffer( - new net::IOBufferWithSize(256)); - socket->Write(io_buffer.get(), io_buffer->size(), - base::Bind(&CompleteHandler::OnComplete, base::Unretained(&handler))); +TEST_P(TCPSocketUnitTest, Write) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, kTestMsg, kTestMsgLength)}; + + net::StaticSocketDataProvider data_provider(kReads, kWrites); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + net::TestCompletionCallback write_callback; + socket->Write(io_buffer.get(), kTestMsgLength, write_callback.callback()); + EXPECT_EQ(kTestMsgLength, write_callback.WaitForResult()); } -TEST(SocketTest, TestTCPSocketBlockedWrite) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); - CompleteHandler handler; - - net::CompletionCallback callback; - EXPECT_CALL(*tcp_client_socket, Write(_, _, _, _)) - .Times(2) - .WillRepeatedly( - testing::DoAll(SaveArg<2>(&callback), Return(net::ERR_IO_PENDING))); - - std::unique_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( - std::move(tcp_client_socket), FAKE_ID, true)); - - scoped_refptr<net::IOBufferWithSize> io_buffer(new net::IOBufferWithSize(42)); - socket->Write(io_buffer.get(), io_buffer->size(), - base::Bind(&CompleteHandler::OnComplete, base::Unretained(&handler))); - - // Good. Original call came back unable to complete. Now pretend the socket - // finished, and confirm that we passed the error back. - EXPECT_CALL(handler, OnComplete(42)) - .Times(1); - callback.Run(40); - callback.Run(2); +// Tests the case where a message is split over two separate socket writes. +TEST_P(TCPSocketUnitTest, MultipleWrite) { + const char kFirstHalfTestMsg[] = "abcde"; + const char kSecondHalfTestMsg[] = "fghij"; + EXPECT_EQ(kTestMsg, std::string(kFirstHalfTestMsg) + kSecondHalfTestMsg); + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, kFirstHalfTestMsg, strlen(kFirstHalfTestMsg)), + net::MockWrite(io_mode, kSecondHalfTestMsg, strlen(kSecondHalfTestMsg))}; + + net::StaticSocketDataProvider data_provider(kReads, kWrites); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + int num_bytes_written = 0; + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + auto drainable_io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>( + io_buffer.get(), kTestMsgLength); + while (num_bytes_written < kTestMsgLength) { + net::TestCompletionCallback write_callback; + socket->Write(drainable_io_buffer.get(), kTestMsgLength - num_bytes_written, + write_callback.callback()); + int result = write_callback.WaitForResult(); + ASSERT_GT(result, net::OK); + drainable_io_buffer->DidConsume(result); + num_bytes_written += result; + // Flushes the write. + base::RunLoop().RunUntilIdle(); + } + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); } -TEST(SocketTest, TestTCPSocketBlockedWriteReentry) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); - CompleteHandler handlers[5]; - - net::CompletionCallback callback; - EXPECT_CALL(*tcp_client_socket, Write(_, _, _, _)) - .Times(5) - .WillRepeatedly( - testing::DoAll(SaveArg<2>(&callback), Return(net::ERR_IO_PENDING))); - - std::unique_ptr<TCPSocket> socket(TCPSocket::CreateSocketForTesting( - std::move(tcp_client_socket), FAKE_ID, true)); - - scoped_refptr<net::IOBufferWithSize> io_buffers[5]; - int i; - for (i = 0; i < 5; i++) { - io_buffers[i] = new net::IOBufferWithSize(128 + i * 50); - scoped_refptr<net::IOBufferWithSize> io_buffer1( - new net::IOBufferWithSize(42)); - socket->Write(io_buffers[i].get(), io_buffers[i]->size(), - base::Bind(&CompleteHandler::OnComplete, - base::Unretained(&handlers[i]))); - - EXPECT_CALL(handlers[i], OnComplete(io_buffers[i]->size())) - .Times(1); +TEST_P(TCPSocketUnitTest, PartialWrite) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, "a", 1), net::MockWrite(io_mode, "bc", 2), + net::MockWrite(io_mode, "defg", 4), net::MockWrite(io_mode, "hij", 3)}; + + net::StaticSocketDataProvider data_provider(kReads, kWrites); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + // Start with writing one byte, and double that in the next iteration. + int num_bytes_to_write = 1; + int num_bytes_written = 0; + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + auto drainable_io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>( + io_buffer.get(), kTestMsgLength); + while (num_bytes_written < kTestMsgLength) { + net::TestCompletionCallback write_callback; + socket->Write( + drainable_io_buffer.get(), + std::max(kTestMsgLength - num_bytes_written, num_bytes_to_write), + write_callback.callback()); + int result = write_callback.WaitForResult(); + ASSERT_GT(result, net::OK); + drainable_io_buffer->DidConsume(result); + num_bytes_written += result; + num_bytes_to_write *= 2; + // Flushes the write. + base::RunLoop().RunUntilIdle(); } + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); +} - for (i = 0; i < 5; i++) { - callback.Run(128 + i * 50); +TEST_P(TCPSocketUnitTest, WriteError) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::SYNCHRONOUS, net::ERR_IO_PENDING)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, net::ERR_INSUFFICIENT_RESOURCES)}; + + net::StaticSocketDataProvider data_provider(kReads, kWrites); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + + // Mojo data pipe might buffer some write data, so continue writing until the + // write error is received. + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + int net_error = net::OK; + while (true) { + base::RunLoop run_loop; + socket->Write(io_buffer.get(), kTestMsgLength, + base::BindLambdaForTesting([&](int result) { + EXPECT_EQ(result > 0, socket->IsConnected()); + net_error = result; + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; } + // Note that TCPSocket only detects that send pipe is broken and propagates + // it as a net::ERR_FAILED. It doesn't know the specific net error code. To do + // that, it needs to register itself as a network::mojom::SocketObserver. + EXPECT_EQ(net::ERR_FAILED, net_error); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); } -TEST(SocketTest, TestTCPSocketSetNoDelay) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); +namespace { - bool no_delay = false; - { - testing::InSequence dummy; - EXPECT_CALL(*tcp_client_socket, SetNoDelay(_)) - .WillOnce(testing::DoAll(SaveArg<0>(&no_delay), Return(true))); - EXPECT_CALL(*tcp_client_socket, SetNoDelay(_)) - .WillOnce(testing::DoAll(SaveArg<0>(&no_delay), Return(false))); +class ExtensionsMockClientSocket : public net::MockTCPClientSocket { + public: + ExtensionsMockClientSocket(net::SocketDataProvider* provider, bool success) + : MockTCPClientSocket( + net::AddressList( + net::IPEndPoint(net::IPAddress::IPv4Localhost(), 1234)), + nullptr /* netlog */, + provider), + success_(success) { + this->set_enable_read_if_ready(true); } + ~ExtensionsMockClientSocket() override {} - std::unique_ptr<TCPSocket> socket( - TCPSocket::CreateSocketForTesting(std::move(tcp_client_socket), FAKE_ID)); + bool SetNoDelay(bool no_delay) override { return success_; } + bool SetKeepAlive(bool enable, int delay) override { return success_; } - EXPECT_FALSE(no_delay); - int result = socket->SetNoDelay(true); - EXPECT_TRUE(result); - EXPECT_TRUE(no_delay); + private: + // Whether to return success for SetNoDelay() and SetKeepAlive(). + const bool success_; - result = socket->SetNoDelay(false); - EXPECT_FALSE(result); - EXPECT_FALSE(no_delay); -} + DISALLOW_COPY_AND_ASSIGN(ExtensionsMockClientSocket); +}; -TEST(SocketTest, TestTCPSocketSetKeepAlive) { - net::AddressList address_list; - std::unique_ptr<MockTCPSocket> tcp_client_socket( - new MockTCPSocket(address_list)); +static const net::MockRead kMockReads[] = {net::MockRead(net::ASYNC, net::OK)}; - bool enable = false; - int delay = 0; - { - testing::InSequence dummy; - EXPECT_CALL(*tcp_client_socket, SetKeepAlive(_, _)) - .WillOnce(testing::DoAll(SaveArg<0>(&enable), SaveArg<1>(&delay), - Return(true))); - EXPECT_CALL(*tcp_client_socket, SetKeepAlive(_, _)) - .WillOnce(testing::DoAll(SaveArg<0>(&enable), SaveArg<1>(&delay), - Return(false))); +// A ClientSocketFactory to create sockets that simulate SetNoDelay and +// SetKeepAlive success and failures. +class TestSocketFactory : public net::ClientSocketFactory { + public: + explicit TestSocketFactory(bool success) : success_(success) {} + ~TestSocketFactory() override = default; + + std::unique_ptr<net::DatagramClientSocket> CreateDatagramClientSocket( + net::DatagramSocket::BindType, + net::NetLog*, + const net::NetLogSource&) override { + NOTIMPLEMENTED(); + return nullptr; + } + std::unique_ptr<net::TransportClientSocket> CreateTransportClientSocket( + const net::AddressList&, + std::unique_ptr<net::SocketPerformanceWatcher>, + net::NetLog*, + const net::NetLogSource&) override { + providers_.push_back(std::make_unique<net::StaticSocketDataProvider>( + kMockReads, base::span<net::MockWrite>())); + return std::make_unique<ExtensionsMockClientSocket>(providers_.back().get(), + success_); + } + std::unique_ptr<net::SSLClientSocket> CreateSSLClientSocket( + std::unique_ptr<net::ClientSocketHandle>, + const net::HostPortPair&, + const net::SSLConfig&, + const net::SSLClientSocketContext&) override { + NOTIMPLEMENTED(); + return std::unique_ptr<net::SSLClientSocket>(); + } + std::unique_ptr<net::ProxyClientSocket> CreateProxyClientSocket( + std::unique_ptr<net::ClientSocketHandle> transport_socket, + const std::string& user_agent, + const net::HostPortPair& endpoint, + net::HttpAuthController* http_auth_controller, + bool tunnel, + bool using_spdy, + net::NextProto negotiated_protocol, + bool is_https_proxy, + const net::NetworkTrafficAnnotationTag& traffic_annotation) override { + NOTIMPLEMENTED(); + return nullptr; + } + void ClearSSLSessionCache() override { NOTIMPLEMENTED(); } + + private: + std::vector<std::unique_ptr<net::StaticSocketDataProvider>> providers_; + // Whether to return success for net::TransportClientSocket::SetNoDelay() and + // SetKeepAlive(). + const bool success_; + + DISALLOW_COPY_AND_ASSIGN(TestSocketFactory); +}; + +} // namespace + +class TCPSocketSettingsTest : public TCPSocketUnitTestBase, + public ::testing::WithParamInterface<bool> { + public: + TCPSocketSettingsTest() + : TCPSocketUnitTestBase(), client_socket_factory_(GetParam()) { + url_request_context_.set_client_socket_factory(&client_socket_factory_); + Initialize(); } + ~TCPSocketSettingsTest() override {} - std::unique_ptr<TCPSocket> socket( - TCPSocket::CreateSocketForTesting(std::move(tcp_client_socket), FAKE_ID)); + private: + TestSocketFactory client_socket_factory_; +}; - EXPECT_FALSE(enable); - int result = socket->SetKeepAlive(true, 4500); - EXPECT_TRUE(result); - EXPECT_TRUE(enable); - EXPECT_EQ(4500, delay); +INSTANTIATE_TEST_CASE_P(/* no prefix */, + TCPSocketSettingsTest, + testing::Bool()); - result = socket->SetKeepAlive(false, 0); - EXPECT_FALSE(result); - EXPECT_FALSE(enable); - EXPECT_EQ(0, delay); +TEST_P(TCPSocketSettingsTest, SetNoDelay) { + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + bool expected_success = GetParam(); + { + base::RunLoop run_loop; + socket->SetNoDelay(true, base::BindLambdaForTesting([&](bool success) { + EXPECT_EQ(expected_success, success); + run_loop.Quit(); + })); + run_loop.Run(); + } + + { + base::RunLoop run_loop; + socket->SetNoDelay(false, base::BindLambdaForTesting([&](bool success) { + EXPECT_EQ(expected_success, success); + run_loop.Quit(); + })); + run_loop.Run(); + } } -TEST(SocketTest, TestTCPServerSocketListenAccept) { - std::unique_ptr<MockTCPServerSocket> tcp_server_socket( - new MockTCPServerSocket()); - CompleteHandler handler; +TEST_P(TCPSocketSettingsTest, SetKeepAlive) { + std::unique_ptr<TCPSocket> socket = CreateAndConnectSocket(); + bool expected_success = GetParam(); + { + base::RunLoop run_loop; + socket->SetKeepAlive(true /* enable */, 123 /* delay */, + base::BindLambdaForTesting([&](bool success) { + EXPECT_EQ(expected_success, success); + run_loop.Quit(); + })); + run_loop.Run(); + } - EXPECT_CALL(*tcp_server_socket, Accept(_, _)).Times(1); - EXPECT_CALL(*tcp_server_socket, Listen(_, _)).Times(1); + { + base::RunLoop run_loop; + socket->SetKeepAlive(false /* enable */, 123 /* delay */, + base::BindLambdaForTesting([&](bool success) { + EXPECT_EQ(expected_success, success); + run_loop.Quit(); + })); + run_loop.Run(); + } +} + +class TCPSocketServerTest : public TCPSocketUnitTestBase { + public: + TCPSocketServerTest() : TCPSocketUnitTestBase() { Initialize(); } + ~TCPSocketServerTest() override {} - std::unique_ptr<TCPSocket> socket(TCPSocket::CreateServerSocketForTesting( - std::move(tcp_server_socket), FAKE_ID)); + private: + net::MockClientSocketFactory mock_client_socket_factory_; +}; - EXPECT_CALL(handler, OnAcceptMock(_, _)); +TEST_F(TCPSocketServerTest, ListenAccept) { + // Create a server socket. + std::unique_ptr<TCPSocket> socket = CreateSocket(); + net::TestCompletionCallback callback; + base::RunLoop run_loop; + socket->Listen( + "127.0.0.1", 0 /* port */, 1 /* backlog */, + base::BindLambdaForTesting([&](int result, const std::string& error_msg) { + EXPECT_EQ(net::OK, result); + run_loop.Quit(); + })); + run_loop.Run(); + net::IPEndPoint server_addr; + EXPECT_TRUE(socket->GetLocalAddress(&server_addr)); + + base::RunLoop accept_run_loop; + net::IPEndPoint accept_client_addr; + socket->Accept(base::BindLambdaForTesting( + [&](int result, network::mojom::TCPConnectedSocketPtr accepted_socket, + const base::Optional<net::IPEndPoint>& remote_addr, + mojo::ScopedDataPipeConsumerHandle receive_handle, + mojo::ScopedDataPipeProducerHandle send_handle) { + EXPECT_EQ(net::OK, result); + accept_client_addr = remote_addr.value(); + accept_run_loop.Quit(); + })); + + // Create a client socket to talk to the server socket. + auto client_socket = CreateAndConnectSocketWithAddress(server_addr); + accept_run_loop.Run(); + + net::IPEndPoint peer_addr; + EXPECT_TRUE(client_socket->GetPeerAddress(&peer_addr)); + net::IPEndPoint client_addr; + EXPECT_TRUE(client_socket->GetLocalAddress(&client_addr)); + EXPECT_EQ(server_addr, peer_addr); + EXPECT_EQ(client_addr, accept_client_addr); +} - std::string err_msg; - EXPECT_EQ(net::OK, socket->Listen("127.0.0.1", 9999, 10, &err_msg)); - socket->Accept(base::Bind(&CompleteHandler::OnAccept, - base::Unretained(&handler))); +TEST_F(TCPSocketServerTest, ReadAndWrite) { + // Create a server socket. + std::unique_ptr<TCPSocket> socket = CreateSocket(); + net::TestCompletionCallback callback; + base::RunLoop run_loop; + socket->Listen( + "127.0.0.1", 0 /* port */, 1 /* backlog */, + base::BindLambdaForTesting([&](int result, const std::string& error_msg) { + EXPECT_EQ(net::OK, result); + run_loop.Quit(); + })); + run_loop.Run(); + net::IPEndPoint server_addr; + EXPECT_TRUE(socket->GetLocalAddress(&server_addr)); + + base::RunLoop accept_run_loop; + std::unique_ptr<TCPSocket> accepted_socket; + + socket->Accept(base::BindLambdaForTesting( + [&](int result, network::mojom::TCPConnectedSocketPtr connected_socket, + const base::Optional<net::IPEndPoint>& remote_addr, + mojo::ScopedDataPipeConsumerHandle receive_handle, + mojo::ScopedDataPipeProducerHandle send_handle) { + EXPECT_EQ(net::OK, result); + accepted_socket = std::make_unique<TCPSocket>( + std::move(connected_socket), std::move(receive_handle), + std::move(send_handle), remote_addr, FAKE_ID); + accept_run_loop.Quit(); + })); + + // Create a client socket to talk to the server socket. + auto client_socket = CreateAndConnectSocketWithAddress(server_addr); + net::TestCompletionCallback connect_callback; + accept_run_loop.Run(); + + // Send data from the client to the server. + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + net::TestCompletionCallback write_callback; + client_socket->Write(io_buffer.get(), kTestMsgLength, + write_callback.callback()); + EXPECT_EQ(kTestMsgLength, write_callback.WaitForResult()); + + std::string received_contents; + while (received_contents.size() < kTestMsgLength) { + base::RunLoop run_loop; + accepted_socket->Read( + kTestMsgLength, + base::BindLambdaForTesting([&](int result, + scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + ASSERT_GT(result, 0); + EXPECT_FALSE(socket_destroying); + received_contents.append(std::string(io_buffer->data(), result)); + run_loop.Quit(); + })); + run_loop.Run(); + } + EXPECT_EQ(kTestMsg, received_contents); + + // Send data from the server to the client. + net::TestCompletionCallback write_callback2; + accepted_socket->Write(io_buffer.get(), kTestMsgLength, + write_callback2.callback()); + EXPECT_EQ(kTestMsgLength, write_callback2.WaitForResult()); + + std::string sent_contents; + while (sent_contents.size() < kTestMsgLength) { + base::RunLoop run_loop; + client_socket->Read( + kTestMsgLength, + base::BindLambdaForTesting([&](int result, + scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + ASSERT_GT(result, 0); + EXPECT_FALSE(socket_destroying); + sent_contents.append(std::string(io_buffer->data(), result)); + run_loop.Quit(); + })); + run_loop.Run(); + } + EXPECT_EQ(kTestMsg, sent_contents); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/socket/tls_socket_unittest.cc b/chromium/chrome/browser/extensions/api/socket/tls_socket_unittest.cc index 678b630744e..16fa23874e7 100644 --- a/chromium/chrome/browser/extensions/api/socket/tls_socket_unittest.cc +++ b/chromium/chrome/browser/extensions/api/socket/tls_socket_unittest.cc @@ -2,372 +2,488 @@ // 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 <memory> #include <utility> -#include "base/callback_helpers.h" -#include "base/containers/circular_deque.h" #include "base/macros.h" -#include "base/strings/string_piece.h" +#include "base/test/bind_test_util.h" +#include "chrome/browser/extensions/extension_service_test_base.h" +#include "chrome/test/base/testing_profile.h" +#include "content/public/browser/browser_context.h" +#include "content/public/test/test_storage_partition.h" #include "extensions/browser/api/socket/tls_socket.h" #include "net/base/address_list.h" #include "net/base/completion_callback.h" #include "net/base/io_buffer.h" #include "net/base/net_errors.h" -#include "net/base/rand_callback.h" -#include "net/log/net_log_source.h" -#include "net/log/net_log_with_source.h" -#include "net/socket/next_proto.h" -#include "net/socket/socket_tag.h" -#include "net/socket/ssl_client_socket.h" -#include "net/socket/tcp_client_socket.h" -#include "net/traffic_annotation/network_traffic_annotation.h" -#include "testing/gmock/include/gmock/gmock.h" - -using testing::_; -using testing::DoAll; -using testing::Invoke; -using testing::Gt; -using testing::Return; -using testing::SaveArg; -using testing::WithArgs; -using base::StringPiece; +#include "net/base/test_completion_callback.h" +#include "net/socket/socket_test_util.h" +#include "net/url_request/url_request_test_util.h" +#include "services/network/network_context.h" +#include "services/network/public/mojom/network_context.mojom.h" namespace extensions { -class MockSSLClientSocket : public net::SSLClientSocket { +namespace { + +const char kTestMsg[] = "abcdefghij"; +const int kTestMsgLength = strlen(kTestMsg); + +const char FAKE_ID[] = "abcdefghijklmnopqrst"; + +class TLSSocketTestBase : public extensions::ExtensionServiceTestBase { public: - MockSSLClientSocket() {} - int Read(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback) override { - return Read(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback))); + TLSSocketTestBase() : url_request_context_(true) {} + ~TLSSocketTestBase() override {} + + // Creates a TCP socket. + std::unique_ptr<TCPSocket> CreateTCPSocket() { + auto socket = std::make_unique<TCPSocket>(&profile_, FAKE_ID); + socket->SetStoragePartitionForTest(&partition_); + net::TestCompletionCallback connect_callback; + net::IPEndPoint ip_end_point(net::IPAddress::IPv4Localhost(), kPort); + socket->Connect(net::AddressList(ip_end_point), + connect_callback.callback()); + if (net::OK != connect_callback.WaitForResult()) { + return nullptr; + } + return socket; } - int Write(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback, - const net::NetworkTrafficAnnotationTag& tag) override { - return Write(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback)), tag); + // Create a TCP socket and upgrade it to TLS. + std::unique_ptr<TLSSocket> CreateSocket() { + auto socket = CreateTCPSocket(); + if (!socket) + return nullptr; + base::RunLoop run_loop; + net::HostPortPair host_port_pair("example.com", kPort); + net::IPEndPoint local_addr; + net::IPEndPoint peer_addr; + if (!socket->GetLocalAddress(&local_addr) || + !socket->GetPeerAddress(&peer_addr)) { + return nullptr; + } + std::unique_ptr<TLSSocket> tls_socket; + socket->UpgradeToTLS( + nullptr /* options */, + base::BindLambdaForTesting( + [&](int result, network::mojom::TLSClientSocketPtr tls_socket_ptr, + const net::IPEndPoint& local_addr, + const net::IPEndPoint& peer_addr, + mojo::ScopedDataPipeConsumerHandle receive_handle, + mojo::ScopedDataPipeProducerHandle send_handle) { + if (net::OK == result) { + tls_socket = std::make_unique<TLSSocket>( + std::move(tls_socket_ptr), local_addr, peer_addr, + std::move(receive_handle), std::move(send_handle), FAKE_ID); + } + run_loop.Quit(); + })); + run_loop.Run(); + return tls_socket; } - int Connect(net::CompletionOnceCallback callback) override { - return Connect(base::AdaptCallbackForRepeating(std::move(callback))); + protected: + // extensions::ExtensionServiceTestBase implementation. + void SetUp() override { InitializeEmptyExtensionService(); } + + void Initialize() { + url_request_context_.Init(); + network_context_ = std::make_unique<network::NetworkContext>( + nullptr, mojo::MakeRequest(&network_context_ptr_), + &url_request_context_); + partition_.set_network_context(network_context_ptr_.get()); } - MOCK_METHOD0(Disconnect, void()); - MOCK_METHOD3(Read, - int(net::IOBuffer* buf, - int buf_len, - const net::CompletionCallback& callback)); - MOCK_METHOD4(Write, - int(net::IOBuffer* buf, - int buf_len, - const net::CompletionCallback& callback, - const net::NetworkTrafficAnnotationTag&)); - MOCK_METHOD1(SetReceiveBufferSize, int(int32_t)); - MOCK_METHOD1(SetSendBufferSize, int(int32_t)); - MOCK_METHOD1(Connect, int(const CompletionCallback&)); - MOCK_CONST_METHOD0(IsConnectedAndIdle, bool()); - MOCK_CONST_METHOD1(GetPeerAddress, int(net::IPEndPoint*)); - MOCK_CONST_METHOD1(GetLocalAddress, int(net::IPEndPoint*)); - MOCK_CONST_METHOD0(NetLog, const net::NetLogWithSource&()); - MOCK_CONST_METHOD0(WasEverUsed, bool()); - MOCK_CONST_METHOD0(UsingTCPFastOpen, bool()); - MOCK_CONST_METHOD0(WasAlpnNegotiated, bool()); - MOCK_CONST_METHOD0(GetNegotiatedProtocol, net::NextProto()); - MOCK_METHOD1(GetSSLInfo, bool(net::SSLInfo*)); - MOCK_CONST_METHOD1(GetConnectionAttempts, void(net::ConnectionAttempts*)); - MOCK_METHOD0(ClearConnectionAttempts, void()); - MOCK_METHOD1(AddConnectionAttempts, void(const net::ConnectionAttempts&)); - MOCK_CONST_METHOD0(GetTotalReceivedBytes, int64_t()); - MOCK_METHOD1(ApplySocketTag, void(const net::SocketTag&)); - MOCK_METHOD5(ExportKeyingMaterial, - int(const StringPiece&, - bool, - const StringPiece&, - unsigned char*, - unsigned int)); - MOCK_CONST_METHOD1(GetSSLCertRequestInfo, void(net::SSLCertRequestInfo*)); - MOCK_CONST_METHOD0(GetUnverifiedServerCertificateChain, - scoped_refptr<net::X509Certificate>()); - MOCK_CONST_METHOD0(GetChannelIDService, net::ChannelIDService*()); - MOCK_METHOD3(GetTokenBindingSignature, - net::Error(crypto::ECPrivateKey*, - net::TokenBindingType, - std::vector<uint8_t>*)); - MOCK_CONST_METHOD0(GetChannelIDKey, crypto::ECPrivateKey*()); - bool IsConnected() const override { return true; } + net::TestURLRequestContext url_request_context_; private: - DISALLOW_COPY_AND_ASSIGN(MockSSLClientSocket); + static const int kPort = 1234; + TestingProfile profile_; + content::TestStoragePartition partition_; + std::unique_ptr<network::NetworkContext> network_context_; + network::mojom::NetworkContextPtr network_context_ptr_; }; -class MockTCPSocket : public net::TCPClientSocket { +} // namespace + +class TLSSocketTest : public TLSSocketTestBase, + public ::testing::WithParamInterface<net::IoMode> { public: - explicit MockTCPSocket(const net::AddressList& address_list) - : net::TCPClientSocket(address_list, NULL, NULL, net::NetLogSource()) {} - - int Read(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback) override { - return Read(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback))); + TLSSocketTest() : TLSSocketTestBase() { + mock_client_socket_factory_.set_enable_read_if_ready(true); + url_request_context_.set_client_socket_factory( + &mock_client_socket_factory_); + Initialize(); } + ~TLSSocketTest() override {} - int Write(net::IOBuffer* buffer, - int bytes, - net::CompletionOnceCallback callback, - const net::NetworkTrafficAnnotationTag& tag) override { - return Write(buffer, bytes, - base::AdaptCallbackForRepeating(std::move(callback)), tag); + net::MockClientSocketFactory* mock_client_socket_factory() { + return &mock_client_socket_factory_; } - MOCK_METHOD3(Read, - int(net::IOBuffer* buf, - int buf_len, - const net::CompletionCallback& callback)); - MOCK_METHOD4(Write, - int(net::IOBuffer* buf, - int buf_len, - const net::CompletionCallback& callback, - const net::NetworkTrafficAnnotationTag&)); - MOCK_METHOD2(SetKeepAlive, bool(bool enable, int delay)); - MOCK_METHOD1(SetNoDelay, bool(bool no_delay)); - - bool IsConnected() const override { return true; } - private: - DISALLOW_COPY_AND_ASSIGN(MockTCPSocket); + net::MockClientSocketFactory mock_client_socket_factory_; }; -class CompleteHandler { - public: - CompleteHandler() {} - MOCK_METHOD1(OnComplete, void(int result_code)); - MOCK_METHOD3(OnReadComplete, - void(int result_code, - scoped_refptr<net::IOBuffer> io_buffer, - bool socket_destroying)); - MOCK_METHOD2(OnAccept, void(int, net::TCPClientSocket*)); - - private: - DISALLOW_COPY_AND_ASSIGN(CompleteHandler); -}; - -class TLSSocketTest : public ::testing::Test { - public: - TLSSocketTest() {} - - void SetUp() override { - net::AddressList address_list; - // |ssl_socket_| is owned by |socket_|. TLSSocketTest keeps a pointer to - // it to expect invocations from TLSSocket to |ssl_socket_|. - std::unique_ptr<MockSSLClientSocket> ssl_sock(new MockSSLClientSocket); - ssl_socket_ = ssl_sock.get(); - socket_.reset(new TLSSocket(std::move(ssl_sock), "test_extension_id")); - EXPECT_CALL(*ssl_socket_, Disconnect()).Times(1); - }; - - void TearDown() override { - ssl_socket_ = NULL; - socket_.reset(); - }; - - protected: - MockSSLClientSocket* ssl_socket_; - std::unique_ptr<TLSSocket> socket_; -}; - -// Verify that a Read() on TLSSocket will pass through into a Read() on -// |ssl_socket_| and invoke its completion callback. -TEST_F(TLSSocketTest, TestTLSSocketRead) { - CompleteHandler handler; - - EXPECT_CALL(*ssl_socket_, Read(_, _, _)).Times(1); - EXPECT_CALL(handler, OnReadComplete(_, _, _)).Times(1); - - const int count = 512; - socket_->Read( - count, - base::Bind(&CompleteHandler::OnReadComplete, base::Unretained(&handler))); +TEST_F(TLSSocketTest, DestroyWhileReadPending) { + const net::MockRead kReads[] = {net::MockRead(net::SYNCHRONOUS, net::OK, 1)}; + const net::MockWrite kWrites[] = { + net::MockWrite(net::ASYNC, kTestMsg, kTestMsgLength, 0)}; + net::StaticSocketDataProvider data_provider(kReads, kWrites); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + int net_result = net::ERR_FAILED; + base::RunLoop run_loop; + int count = 1; + // Read one byte, and it should be pending because it is blocked on the mock + // write. + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_result = result; + // |socket_destroying| should correctly denote that this + // read callback is invoked through the destructor of + // TLSSocket. + EXPECT_TRUE(socket_destroying); + run_loop.Quit(); + })); + // Destroy socket. + socket = nullptr; + // Wait for read callback. + run_loop.Run(); + EXPECT_EQ(net::ERR_CONNECTION_CLOSED, net_result); } -// Verify that a Write() on a TLSSocket will pass through to Write() -// invocations on |ssl_socket_|, handling partial writes correctly, and calls -// the completion callback correctly. -TEST_F(TLSSocketTest, TestTLSSocketWrite) { - CompleteHandler handler; - net::CompletionCallback callback; - - EXPECT_CALL(*ssl_socket_, Write(_, _, _, _)) - .Times(2) - .WillRepeatedly(DoAll(SaveArg<2>(&callback), Return(128))); - EXPECT_CALL(handler, OnComplete(_)).Times(1); - - scoped_refptr<net::IOBufferWithSize> io_buffer( - new net::IOBufferWithSize(256)); - socket_->Write( - io_buffer.get(), - io_buffer->size(), - base::Bind(&CompleteHandler::OnComplete, base::Unretained(&handler))); +// UpgradeToTLS() fails when there is a pending read. +TEST_F(TLSSocketTest, UpgradeToTLSWhilePendingRead) { + const net::MockRead kReads[] = { + net::MockRead(net::ASYNC, net::ERR_IO_PENDING)}; + net::StaticSocketDataProvider data_provider(kReads, + base::span<net::MockWrite>()); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + auto socket = CreateTCPSocket(); + // This read will be pending when UpgradeToTLS() is called. + socket->Read(1 /* count */, base::DoNothing()); + network::mojom::TLSClientSocketPtr tls_socket_ptr; + base::RunLoop run_loop; + socket->UpgradeToTLS( + nullptr /* options */, + base::BindLambdaForTesting( + [&](int result, network::mojom::TLSClientSocketPtr tls_socket_ptr, + const net::IPEndPoint& local_addr, + const net::IPEndPoint& peer_addr, + mojo::ScopedDataPipeConsumerHandle receive_handle, + mojo::ScopedDataPipeProducerHandle send_handle) { + EXPECT_EQ(net::ERR_FAILED, result); + run_loop.Quit(); + })); + run_loop.Run(); } -// Simulate a blocked Write, and verify that, when simulating the Write going -// through, the callback gets invoked. -TEST_F(TLSSocketTest, TestTLSSocketBlockedWrite) { - CompleteHandler handler; - net::CompletionCallback callback; - - // Return ERR_IO_PENDING to say the Write()'s blocked. Save the |callback| - // Write()'s passed. - EXPECT_CALL(*ssl_socket_, Write(_, _, _, _)) - .Times(2) - .WillRepeatedly( - DoAll(SaveArg<2>(&callback), Return(net::ERR_IO_PENDING))); - - scoped_refptr<net::IOBufferWithSize> io_buffer(new net::IOBufferWithSize(42)); - socket_->Write( - io_buffer.get(), - io_buffer->size(), - base::Bind(&CompleteHandler::OnComplete, base::Unretained(&handler))); - - // After the simulated asynchronous writes come back (via calls to - // callback.Run()), hander's OnComplete() should get invoked with the total - // amount written. - EXPECT_CALL(handler, OnComplete(42)).Times(1); - callback.Run(40); - callback.Run(2); +TEST_F(TLSSocketTest, UpgradeToTLSWithCustomOptions) { + // Mock data are not consumed. These are here so that net::StreamSocket::Read + // is always pending and blocked on the write. Otherwise, mock socket data + // will complains that there aren't any data to read. + const net::MockRead kReads[] = { + net::MockRead(net::ASYNC, kTestMsg, kTestMsgLength, 1), + net::MockRead(net::ASYNC, net::OK, 2)}; + const net::MockWrite kWrites[] = { + net::MockWrite(net::ASYNC, kTestMsg, kTestMsgLength, 0)}; + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(net::ASYNC, net::OK); + ssl_socket.expected_ssl_version_min = net::SSL_PROTOCOL_VERSION_TLS1_1; + ssl_socket.expected_ssl_version_max = net::SSL_PROTOCOL_VERSION_TLS1_2; + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + + auto socket = CreateTCPSocket(); + network::mojom::TLSClientSocketPtr tls_socket_ptr; + api::socket::SecureOptions options; + options.tls_version = std::make_unique<api::socket::TLSVersionConstraints>(); + options.tls_version->min = std::make_unique<std::string>("tls1.1"); + options.tls_version->max = std::make_unique<std::string>("tls1.2"); + int net_error = net::ERR_FAILED; + base::RunLoop run_loop; + socket->UpgradeToTLS( + &options, + base::BindLambdaForTesting( + [&](int result, network::mojom::TLSClientSocketPtr tls_socket_ptr, + const net::IPEndPoint& local_addr, + const net::IPEndPoint& peer_addr, + mojo::ScopedDataPipeConsumerHandle receive_handle, + mojo::ScopedDataPipeProducerHandle send_handle) { + net_error = result; + run_loop.Quit(); + })); + run_loop.Run(); + EXPECT_EQ(net::OK, net_error); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); } -// Simulate multiple blocked Write()s. -TEST_F(TLSSocketTest, TestTLSSocketBlockedWriteReentry) { - const int kNumIOs = 5; - CompleteHandler handlers[kNumIOs]; - net::CompletionCallback callback; - scoped_refptr<net::IOBufferWithSize> io_buffers[kNumIOs]; - - // The implementation of TLSSocket::Write() is inherited from - // Socket::Write(), which implements an internal write queue that wraps - // TLSSocket::WriteImpl(). Each call from TLSSocket::WriteImpl() will invoke - // |ssl_socket_|'s Write() (mocked here). Save the |callback| (assume they - // will all be equivalent), and return ERR_IO_PENDING, to indicate a blocked - // request. The mocked SSLClientSocket::Write() will get one request per - // TLSSocket::Write() request invoked on |socket_| below. - EXPECT_CALL(*ssl_socket_, Write(_, _, _, _)) - .Times(kNumIOs) - .WillRepeatedly( - DoAll(SaveArg<2>(&callback), Return(net::ERR_IO_PENDING))); - - // Send out |kNuMIOs| requests, each with a different size. - for (int i = 0; i < kNumIOs; i++) { - io_buffers[i] = new net::IOBufferWithSize(128 + i * 50); - socket_->Write(io_buffers[i].get(), - io_buffers[i]->size(), - base::Bind(&CompleteHandler::OnComplete, - base::Unretained(&handlers[i]))); - - // Set up expectations on all |kNumIOs| handlers. - EXPECT_CALL(handlers[i], OnComplete(io_buffers[i]->size())).Times(1); +INSTANTIATE_TEST_CASE_P(/* no prefix */, + TLSSocketTest, + testing::Values(net::SYNCHRONOUS, net::ASYNC)); + +TEST_P(TLSSocketTest, ReadWrite) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::ASYNC, kTestMsg, kTestMsgLength, 1), + net::MockRead(io_mode, net::OK, 2)}; + const net::MockWrite kWrites[] = { + net::MockWrite(net::SYNCHRONOUS, kTestMsg, kTestMsgLength, 0)}; + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + net::TestCompletionCallback write_callback; + socket->Write(io_buffer.get(), kTestMsgLength, write_callback.callback()); + EXPECT_EQ(kTestMsgLength, write_callback.WaitForResult()); + + std::string received_data; + int count = 512; + while (true) { + base::RunLoop run_loop; + int net_error = net::ERR_FAILED; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_error = result; + EXPECT_FALSE(socket_destroying); + if (result > 0) + received_data.append(io_buffer->data(), result); + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; } + EXPECT_EQ(kTestMsg, received_data); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); +} - // Finish each pending I/O. This should satisfy the expectations on the - // handlers. - for (int i = 0; i < kNumIOs; i++) { - callback.Run(128 + i * 50); +// Tests the case where read size is smaller than the actual message. +TEST_P(TLSSocketTest, PartialRead) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::ASYNC, kTestMsg, kTestMsgLength, 1), + net::MockRead(io_mode, net::OK, 2)}; + const net::MockWrite kWrites[] = { + net::MockWrite(net::SYNCHRONOUS, kTestMsg, kTestMsgLength, 0)}; + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + net::TestCompletionCallback write_callback; + socket->Write(io_buffer.get(), kTestMsgLength, write_callback.callback()); + EXPECT_EQ(kTestMsgLength, write_callback.WaitForResult()); + + int count = 1; + std::string received_data; + while (true) { + int net_result = net::ERR_FAILED; + base::RunLoop run_loop; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_result = result; + EXPECT_FALSE(socket_destroying); + if (result > 0) + received_data.append(io_buffer->data(), result); + run_loop.Quit(); + })); + run_loop.Run(); + if (net_result <= 0) + break; + // Double the read size in the next iteration. + count *= 2; } + EXPECT_EQ(kTestMsg, received_data); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); } -typedef std::pair<net::CompletionCallback, int> PendingCallback; +TEST_P(TLSSocketTest, ReadError) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = { + net::MockRead(net::ASYNC, net::ERR_INSUFFICIENT_RESOURCES, 1)}; + const net::MockWrite kWrites[] = { + net::MockWrite(net::SYNCHRONOUS, kTestMsg, kTestMsgLength, 0)}; + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); -class CallbackList : public base::circular_deque<PendingCallback> { - public: - void append(const net::CompletionCallback& cb, int arg) { - push_back(std::make_pair(cb, arg)); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + net::TestCompletionCallback write_callback; + socket->Write(io_buffer.get(), kTestMsgLength, write_callback.callback()); + EXPECT_EQ(kTestMsgLength, write_callback.WaitForResult()); + + const int count = 512; + int net_error = net::OK; + while (true) { + base::RunLoop run_loop; + socket->Read(count, + base::BindLambdaForTesting( + [&](int result, scoped_refptr<net::IOBuffer> io_buffer, + bool socket_destroying) { + net_error = result; + EXPECT_FALSE(socket_destroying); + if (result <= 0) { + EXPECT_FALSE(socket->IsConnected()); + EXPECT_EQ(nullptr, io_buffer); + } else { + EXPECT_TRUE(socket->IsConnected()); + } + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; } -}; + // Note that TLSSocket only detects that receive pipe is broken and propagates + // it as 0 byte read. It doesn't know the specific net error code. To know the + // specific net error code, it needs to register itself as a + // network::mojom::SocketObserver. However, that gets tricky because of two + // separate mojo pipes. + EXPECT_EQ(0, net_error); + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); +} -// Simulate Write()s above and below a SSLClientSocket size limit. -TEST_F(TLSSocketTest, TestTLSSocketLargeWrites) { - const int kSizeIncrement = 4096; - const int kNumIncrements = 10; - const int kFragmentIncrement = 4; - const int kSizeLimit = kSizeIncrement * kFragmentIncrement; - net::CompletionCallback callback; - CompleteHandler handler; - scoped_refptr<net::IOBufferWithSize> io_buffers[kNumIncrements]; - CallbackList pending_callbacks; - size_t total_bytes_requested = 0; - size_t total_bytes_written = 0; - - // Some implementations of SSLClientSocket may have write-size limits (e.g, - // max 1 TLS record, which is 16k). This test mocks a size limit at - // |kSizeIncrement| and calls Write() above and below that limit. It - // simulates SSLClientSocket::Write() behavior in only writing up to the size - // limit, requiring additional calls for the remaining data to be sent. - // Socket::Write() (and supporting methods) execute the additional calls as - // needed. This test verifies that this inherited implementation does - // properly issue additional calls, and that the total amount returned from - // all mocked SSLClientSocket::Write() calls is the same as originally - // requested. - - // |ssl_socket_|'s Write() will write at most |kSizeLimit| bytes. The - // inherited Socket::Write() will repeatedly call |ssl_socket_|'s Write() - // until the entire original request is sent. Socket::Write() will queue any - // additional write requests until the current request is complete. A - // request is complete when the callback passed to Socket::WriteImpl() is - // invoked with an argument equal to the original number of bytes requested - // from Socket::Write(). If the callback is invoked with a smaller number, - // Socket::WriteImpl() will get repeatedly invoked until the sum of the - // callbacks' arguments is equal to the original requested amount. - EXPECT_CALL(*ssl_socket_, Write(_, _, _, _)) - .WillRepeatedly(DoAll( - WithArgs<2, 1>(Invoke(&pending_callbacks, &CallbackList::append)), - Return(net::ERR_IO_PENDING))); - - // Observe what comes back from Socket::Write() here. - EXPECT_CALL(handler, OnComplete(Gt(0))).Times(kNumIncrements); - - // Send out |kNumIncrements| requests, each with a different size. The - // last request is the same size as the first, and the ones in the middle - // are monotonically increasing from the first. - for (int i = 0; i < kNumIncrements; i++) { - const bool last = i == (kNumIncrements - 1); - io_buffers[i] = new net::IOBufferWithSize(last ? kSizeIncrement - : kSizeIncrement * (i + 1)); - total_bytes_requested += io_buffers[i]->size(); - - // Invoke Socket::Write(). This will invoke |ssl_socket_|'s Write(), which - // this test mocks out. That mocked Write() is in an asynchronous waiting - // state until the passed callback (saved in the EXPECT_CALL for - // |ssl_socket_|'s Write()) is invoked. - socket_->Write( - io_buffers[i].get(), - io_buffers[i]->size(), - base::Bind(&CompleteHandler::OnComplete, base::Unretained(&handler))); +// Tests the case where a message is split over two separate socket writes. +TEST_P(TLSSocketTest, MultipleWrite) { + const char kFirstHalfTestMsg[] = "abcde"; + const char kSecondHalfTestMsg[] = "fghij"; + EXPECT_EQ(kTestMsg, std::string(kFirstHalfTestMsg) + kSecondHalfTestMsg); + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 2)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, kFirstHalfTestMsg, strlen(kFirstHalfTestMsg), 0), + net::MockWrite(io_mode, kSecondHalfTestMsg, strlen(kSecondHalfTestMsg), + 1)}; + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + int num_bytes_written = 0; + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + auto drainable_io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>( + io_buffer.get(), kTestMsgLength); + while (num_bytes_written < kTestMsgLength) { + net::TestCompletionCallback write_callback; + socket->Write(drainable_io_buffer.get(), kTestMsgLength - num_bytes_written, + write_callback.callback()); + int result = write_callback.WaitForResult(); + ASSERT_GT(result, net::OK); + drainable_io_buffer->DidConsume(result); + num_bytes_written += result; + // Flushes the write. + base::RunLoop().RunUntilIdle(); } + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); +} - // Invoke callbacks for pending I/Os. These can synchronously invoke more of - // |ssl_socket_|'s Write() as needed. The callback checks how much is left - // in the request, and then starts issuing any queued Socket::Write() - // invocations. - while (!pending_callbacks.empty()) { - PendingCallback cb = pending_callbacks.front(); - pending_callbacks.pop_front(); - - int amount_written_invocation = std::min(kSizeLimit, cb.second); - total_bytes_written += amount_written_invocation; - cb.first.Run(amount_written_invocation); +TEST_P(TLSSocketTest, PartialWrite) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 4)}; + const net::MockWrite kWrites[] = {net::MockWrite(io_mode, "a", 1, 0), + net::MockWrite(io_mode, "bc", 2, 1), + net::MockWrite(io_mode, "defg", 4, 2), + net::MockWrite(io_mode, "hij", 3, 3)}; + + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + // Start with writing one byte, and double that in the next iteration. + int num_bytes_to_write = 1; + int num_bytes_written = 0; + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + auto drainable_io_buffer = base::MakeRefCounted<net::DrainableIOBuffer>( + io_buffer.get(), kTestMsgLength); + while (num_bytes_written < kTestMsgLength) { + net::TestCompletionCallback write_callback; + socket->Write( + drainable_io_buffer.get(), + std::max(kTestMsgLength - num_bytes_written, num_bytes_to_write), + write_callback.callback()); + int result = write_callback.WaitForResult(); + ASSERT_GT(result, net::OK); + drainable_io_buffer->DidConsume(result); + num_bytes_written += result; + num_bytes_to_write *= 2; + // Flushes the write. + base::RunLoop().RunUntilIdle(); } + EXPECT_TRUE(data_provider.AllReadDataConsumed()); + EXPECT_TRUE(data_provider.AllWriteDataConsumed()); + EXPECT_TRUE(ssl_socket.ConnectDataConsumed()); +} - ASSERT_EQ(total_bytes_requested, total_bytes_written) - << "There should be exactly as many bytes written as originally " - << "requested to Write()."; +TEST_P(TLSSocketTest, WriteError) { + net::IoMode io_mode = GetParam(); + const net::MockRead kReads[] = {net::MockRead(net::ASYNC, net::OK, 1)}; + const net::MockWrite kWrites[] = { + net::MockWrite(io_mode, net::ERR_INSUFFICIENT_RESOURCES, 0)}; + + net::SequencedSocketData data_provider(kReads, kWrites); + net::SSLSocketDataProvider ssl_socket(io_mode, net::OK); + + mock_client_socket_factory()->AddSocketDataProvider(&data_provider); + mock_client_socket_factory()->AddSSLSocketDataProvider(&ssl_socket); + std::unique_ptr<TLSSocket> socket = CreateSocket(); + + // Mojo data pipe might buffer some write data, so continue writing until the + // write error is received. + auto io_buffer = base::MakeRefCounted<net::StringIOBuffer>(kTestMsg); + int net_error = net::OK; + while (true) { + base::RunLoop run_loop; + socket->Write(io_buffer.get(), kTestMsgLength, + base::BindLambdaForTesting([&](int result) { + if (result == net::ERR_FAILED) + EXPECT_FALSE(socket->IsConnected()); + net_error = result; + run_loop.Quit(); + })); + run_loop.Run(); + if (net_error <= 0) + break; + } + // Note that TCPSocket only detects that send pipe is broken and propagates + // it as a net::ERR_FAILED. It doesn't know the specific net error code. To do + // that, it needs to register itself as a network::mojom::SocketObserver. + EXPECT_EQ(net::ERR_FAILED, net_error); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/sync_file_system/DEPS b/chromium/chrome/browser/extensions/api/sync_file_system/DEPS deleted file mode 100644 index 73bc109ad21..00000000000 --- a/chromium/chrome/browser/extensions/api/sync_file_system/DEPS +++ /dev/null @@ -1,5 +0,0 @@ -specific_include_rules = { - "sync_file_system_browsertest\.cc": [ - "+components/drive" - ], -} diff --git a/chromium/chrome/browser/extensions/api/sync_file_system/OWNERS b/chromium/chrome/browser/extensions/api/sync_file_system/OWNERS index 9d970c731ce..89ed524015e 100644 --- a/chromium/chrome/browser/extensions/api/sync_file_system/OWNERS +++ b/chromium/chrome/browser/extensions/api/sync_file_system/OWNERS @@ -1,4 +1,5 @@ kinuko@chromium.org +pwnall@chromium.org tzik@chromium.org # TEAM: storage-dev@chromium.org diff --git a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc index 383b5c631be..233bfca0c7b 100644 --- a/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc +++ b/chromium/chrome/browser/extensions/api/sync_file_system/sync_file_system_browsertest.cc @@ -9,7 +9,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/sequenced_task_runner.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/threading/thread_task_runner_handle.h" #include "base/values.h" #include "chrome/browser/apps/platform_apps/app_browsertest_util.h" @@ -106,7 +106,7 @@ class SyncFileSystemTest : public extensions::PlatformAppBrowserTest, // drive::FakeDriveService::ChangeObserver override. void OnNewChangeAvailable() override { - sync_engine()->OnNotificationReceived(); + sync_engine()->OnNotificationTimerFired(); } SyncFileSystemService* sync_file_system_service() { diff --git a/chromium/chrome/browser/extensions/api/system_private/system_private_api.cc b/chromium/chrome/browser/extensions/api/system_private/system_private_api.cc index 132abe1537a..bc91204ef8c 100644 --- a/chromium/chrome/browser/extensions/api/system_private/system_private_api.cc +++ b/chromium/chrome/browser/extensions/api/system_private/system_private_api.cc @@ -22,7 +22,7 @@ #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/update_engine_client.h" #else -#include "chrome/browser/upgrade_detector.h" +#include "chrome/browser/upgrade_detector/upgrade_detector.h" #endif namespace { diff --git a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc deleted file mode 100644 index 6706c0fab95..00000000000 --- a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.cc +++ /dev/null @@ -1,422 +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. - -#include "chrome/browser/extensions/api/tab_capture/offscreen_tab.h" - -#include <algorithm> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/stl_util.h" -#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" -#include "chrome/browser/media/router/presentation/presentation_navigation_policy.h" -#include "chrome/browser/media/router/presentation/receiver_presentation_service_delegate_impl.h" // nogncheck -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/web_contents_sizer.h" -#include "content/public/browser/keyboard_event_processing_result.h" -#include "content/public/browser/navigation_handle.h" -#include "content/public/browser/render_widget_host_view.h" -#include "content/public/browser/web_contents.h" -#include "extensions/browser/extension_host.h" -#include "extensions/browser/process_manager.h" -#include "third_party/blink/public/web/web_presentation_receiver_flags.h" - -using content::WebContents; - -DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::OffscreenTabsOwner); - -namespace { - -// Upper limit on the number of simultaneous off-screen tabs per extension -// instance. -const int kMaxOffscreenTabsPerExtension = 4; - -// Time intervals used by the logic that detects when the capture of an -// offscreen tab has stopped, to automatically tear it down and free resources. -const int kMaxSecondsToWaitForCapture = 60; -const int kPollIntervalInSeconds = 1; - -} // namespace - -namespace extensions { - -OffscreenTabsOwner::OffscreenTabsOwner(WebContents* extension_web_contents) - : extension_web_contents_(extension_web_contents) { - DCHECK(extension_web_contents_); -} - -OffscreenTabsOwner::~OffscreenTabsOwner() {} - -// static -OffscreenTabsOwner* OffscreenTabsOwner::Get( - content::WebContents* extension_web_contents) { - // CreateForWebContents() really means "create if not exists." - CreateForWebContents(extension_web_contents); - return FromWebContents(extension_web_contents); -} - -OffscreenTab* OffscreenTabsOwner::OpenNewTab( - const GURL& start_url, - const gfx::Size& initial_size, - const std::string& optional_presentation_id) { - if (tabs_.size() >= kMaxOffscreenTabsPerExtension) - return nullptr; // Maximum number of offscreen tabs reached. - - // OffscreenTab cannot be created with MakeUnique<OffscreenTab> since the - // constructor is protected. So create it separately, and then move it to - // |tabs_| below. - std::unique_ptr<OffscreenTab> offscreen_tab(new OffscreenTab(this)); - tabs_.push_back(std::move(offscreen_tab)); - tabs_.back()->Start(start_url, initial_size, optional_presentation_id); - return tabs_.back().get(); -} - -void OffscreenTabsOwner::DestroyTab(OffscreenTab* tab) { - for (std::vector<std::unique_ptr<OffscreenTab>>::iterator iter = - tabs_.begin(); - iter != tabs_.end(); ++iter) { - if (iter->get() == tab) { - tabs_.erase(iter); - break; - } - } -} - -OffscreenTab::OffscreenTab(OffscreenTabsOwner* owner) - : owner_(owner), - otr_profile_registration_( - IndependentOTRProfileManager::GetInstance() - ->CreateFromOriginalProfile( - Profile::FromBrowserContext( - owner->extension_web_contents()->GetBrowserContext()), - base::BindOnce(&OffscreenTab::DieIfOriginalProfileDestroyed, - base::Unretained(this)))), - content_capture_was_detected_(false), - navigation_policy_( - std::make_unique<media_router::DefaultNavigationPolicy>()) { - DCHECK(otr_profile_registration_->profile()); -} - -OffscreenTab::~OffscreenTab() { - DVLOG(1) << "Destroying OffscreenTab for start_url=" << start_url_.spec(); -} - -void OffscreenTab::Start(const GURL& start_url, - const gfx::Size& initial_size, - const std::string& optional_presentation_id) { - DCHECK(start_time_.is_null()); - start_url_ = start_url; - DVLOG(1) << "Starting OffscreenTab with initial size of " - << initial_size.ToString() << " for start_url=" << start_url_.spec(); - - // Create the WebContents to contain the off-screen tab's page. - WebContents::CreateParams params(otr_profile_registration_->profile()); - if (!optional_presentation_id.empty()) - params.starting_sandbox_flags = blink::kPresentationReceiverSandboxFlags; - - offscreen_tab_web_contents_ = WebContents::Create(params); - offscreen_tab_web_contents_->SetDelegate(this); - WebContentsObserver::Observe(offscreen_tab_web_contents_.get()); - - // Set initial size, if specified. - if (!initial_size.IsEmpty()) - ResizeWebContents(offscreen_tab_web_contents_.get(), - gfx::Rect(initial_size)); - - // Mute audio output. When tab capture starts, the audio will be - // automatically unmuted, but will be captured into the MediaStream. - offscreen_tab_web_contents_->SetAudioMuted(true); - - if (!optional_presentation_id.empty()) { - // This offscreen tab is a presentation created through the Presentation - // API. https://www.w3.org/TR/presentation-api/ - // - // Create a ReceiverPresentationServiceDelegateImpl associated with the - // offscreen tab's WebContents. The new instance will set up the necessary - // infrastructure to allow controlling pages the ability to connect to the - // offscreen tab. - DVLOG(1) << "Register with ReceiverPresentationServiceDelegateImpl, " - << "presentation_id=" << optional_presentation_id; - media_router::ReceiverPresentationServiceDelegateImpl::CreateForWebContents( - offscreen_tab_web_contents_.get(), optional_presentation_id); - - // Presentations are not allowed to perform top-level navigations after - // initial load. This is enforced through sandboxing flags, but we also - // enforce it here. - navigation_policy_ = - std::make_unique<media_router::PresentationNavigationPolicy>(); - } - - // Navigate to the initial URL. - content::NavigationController::LoadURLParams load_params(start_url_); - load_params.should_replace_current_entry = true; - load_params.should_clear_history_list = true; - offscreen_tab_web_contents_->GetController().LoadURLWithParams(load_params); - - start_time_ = base::TimeTicks::Now(); - DieIfContentCaptureEnded(); -} - -void OffscreenTab::Close() { - if (offscreen_tab_web_contents_) - offscreen_tab_web_contents_->ClosePage(); -} - -void OffscreenTab::CloseContents(WebContents* source) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Javascript in the page called window.close(). - DVLOG(1) << "OffscreenTab for start_url=" << start_url_.spec() << " will die"; - owner_->DestroyTab(this); - // |this| is no longer valid. -} - -bool OffscreenTab::ShouldSuppressDialogs(WebContents* source) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Suppress all because there is no possible direct user interaction with - // dialogs. - // TODO(crbug.com/734191): This does not suppress window.print(). - return true; -} - -bool OffscreenTab::ShouldFocusLocationBarByDefault(WebContents* source) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Indicate the location bar should be focused instead of the page, even - // though there is no location bar. This will prevent the page from - // automatically receiving input focus, which should never occur since there - // is not supposed to be any direct user interaction. - return true; -} - -bool OffscreenTab::ShouldFocusPageAfterCrash() { - // Never focus the page. Not even after a crash. - return false; -} - -void OffscreenTab::CanDownload(const GURL& url, - const std::string& request_method, - const base::Callback<void(bool)>& callback) { - // Offscreen tab pages are not allowed to download files. - callback.Run(false); -} - -bool OffscreenTab::HandleContextMenu(const content::ContextMenuParams& params) { - // Context menus should never be shown. Do nothing, but indicate the context - // menu was shown so that default implementation in libcontent does not - // attempt to do so on its own. - return true; -} - -content::KeyboardEventProcessingResult OffscreenTab::PreHandleKeyboardEvent( - WebContents* source, - const content::NativeWebKeyboardEvent& event) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Intercept and silence all keyboard events before they can be sent to the - // renderer. - return content::KeyboardEventProcessingResult::HANDLED; -} - -bool OffscreenTab::PreHandleGestureEvent(WebContents* source, - const blink::WebGestureEvent& event) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Intercept and silence all gesture events before they can be sent to the - // renderer. - return true; -} - -bool OffscreenTab::CanDragEnter( - WebContents* source, - const content::DropData& data, - blink::WebDragOperationsMask operations_allowed) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), source); - // Halt all drag attempts onto the page since there should be no direct user - // interaction with it. - return false; -} - -bool OffscreenTab::ShouldCreateWebContents( - content::WebContents* web_contents, - content::RenderFrameHost* opener, - content::SiteInstance* source_site_instance, - int32_t route_id, - int32_t main_frame_route_id, - int32_t main_frame_widget_route_id, - content::mojom::WindowContainerType window_container_type, - const GURL& opener_url, - const std::string& frame_name, - const GURL& target_url, - const std::string& partition_id, - content::SessionStorageNamespace* session_storage_namespace) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), web_contents); - // Disallow creating separate WebContentses. The WebContents implementation - // uses this to spawn new windows/tabs, which is also not allowed for - // offscreen tabs. - return false; -} - -bool OffscreenTab::EmbedsFullscreenWidget() const { - // OffscreenTab will manage fullscreen widgets. - return true; -} - -void OffscreenTab::EnterFullscreenModeForTab( - WebContents* contents, - const GURL& origin, - const blink::WebFullscreenOptions& options) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), contents); - - if (in_fullscreen_mode()) - return; - - non_fullscreen_size_ = - contents->GetRenderWidgetHostView()->GetViewBounds().size(); - if (contents->IsBeingCaptured() && !contents->GetPreferredSize().IsEmpty()) - ResizeWebContents(contents, gfx::Rect(contents->GetPreferredSize())); -} - -void OffscreenTab::ExitFullscreenModeForTab(WebContents* contents) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), contents); - - if (!in_fullscreen_mode()) - return; - - ResizeWebContents(contents, gfx::Rect(non_fullscreen_size_)); - non_fullscreen_size_ = gfx::Size(); -} - -bool OffscreenTab::IsFullscreenForTabOrPending( - const WebContents* contents) const { - DCHECK_EQ(offscreen_tab_web_contents_.get(), contents); - return in_fullscreen_mode(); -} - -blink::WebDisplayMode OffscreenTab::GetDisplayMode( - const WebContents* contents) const { - DCHECK_EQ(offscreen_tab_web_contents_.get(), contents); - return in_fullscreen_mode() ? blink::kWebDisplayModeFullscreen - : blink::kWebDisplayModeBrowser; -} - -void OffscreenTab::RequestMediaAccessPermission( - WebContents* contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), contents); - - // This method is being called to check whether an extension is permitted to - // capture the page. Verify that the request is being made by the extension - // that spawned this OffscreenTab. - - // Find the extension ID associated with the extension background page's - // WebContents. - content::BrowserContext* const extension_browser_context = - owner_->extension_web_contents()->GetBrowserContext(); - const extensions::Extension* const extension = - ProcessManager::Get(extension_browser_context)-> - GetExtensionForWebContents(owner_->extension_web_contents()); - const std::string extension_id = extension ? extension->id() : ""; - LOG_IF(DFATAL, extension_id.empty()) - << "Extension that started this OffscreenTab was not found."; - - // If verified, allow any tab capture audio/video devices that were requested. - extensions::TabCaptureRegistry* const tab_capture_registry = - extensions::TabCaptureRegistry::Get(extension_browser_context); - content::MediaStreamDevices devices; - if (tab_capture_registry && tab_capture_registry->VerifyRequest( - request.render_process_id, - request.render_frame_id, - extension_id)) { - if (request.audio_type == content::MEDIA_TAB_AUDIO_CAPTURE) { - devices.push_back(content::MediaStreamDevice( - content::MEDIA_TAB_AUDIO_CAPTURE, std::string(), std::string())); - } - if (request.video_type == content::MEDIA_TAB_VIDEO_CAPTURE) { - devices.push_back(content::MediaStreamDevice( - content::MEDIA_TAB_VIDEO_CAPTURE, std::string(), std::string())); - } - } - - DVLOG(2) << "Allowing " << devices.size() - << " capture devices for OffscreenTab content."; - - std::move(callback).Run(devices, - devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE - : content::MEDIA_DEVICE_OK, - std::unique_ptr<content::MediaStreamUI>(nullptr)); -} - -bool OffscreenTab::CheckMediaAccessPermission( - content::RenderFrameHost* render_frame_host, - const GURL& security_origin, - content::MediaStreamType type) { - DCHECK_EQ(offscreen_tab_web_contents_.get(), - content::WebContents::FromRenderFrameHost(render_frame_host)); - return type == content::MEDIA_TAB_AUDIO_CAPTURE || - type == content::MEDIA_TAB_VIDEO_CAPTURE; -} - -void OffscreenTab::DidShowFullscreenWidget() { - if (!offscreen_tab_web_contents_->IsBeingCaptured() || - offscreen_tab_web_contents_->GetPreferredSize().IsEmpty()) - return; // Do nothing, since no preferred size is specified. - content::RenderWidgetHostView* const current_fs_view = - offscreen_tab_web_contents_->GetFullscreenRenderWidgetHostView(); - if (current_fs_view) - current_fs_view->SetSize(offscreen_tab_web_contents_->GetPreferredSize()); -} - -void OffscreenTab::DidStartNavigation( - content::NavigationHandle* navigation_handle) { - DCHECK(offscreen_tab_web_contents_.get()); - if (!navigation_policy_->AllowNavigation(navigation_handle)) { - DVLOG(2) << "Closing because NavigationPolicy disallowed " - << "StartNavigation to " << navigation_handle->GetURL().spec(); - Close(); - } -} - -void OffscreenTab::DieIfContentCaptureEnded() { - DCHECK(offscreen_tab_web_contents_.get()); - - if (content_capture_was_detected_) { - if (!offscreen_tab_web_contents_->IsBeingCaptured()) { - DVLOG(2) << "Capture of OffscreenTab content has stopped for start_url=" - << start_url_.spec(); - owner_->DestroyTab(this); - return; // |this| is no longer valid. - } else { - DVLOG(3) << "Capture of OffscreenTab content continues for start_url=" - << start_url_.spec(); - } - } else if (offscreen_tab_web_contents_->IsBeingCaptured()) { - DVLOG(2) << "Capture of OffscreenTab content has started for start_url=" - << start_url_.spec(); - content_capture_was_detected_ = true; - } else if (base::TimeTicks::Now() - start_time_ > - base::TimeDelta::FromSeconds(kMaxSecondsToWaitForCapture)) { - // More than a minute has elapsed since this OffscreenTab was started and - // content capture still hasn't started. As a safety precaution, assume - // that content capture is never going to start and die to free up - // resources. - LOG(WARNING) << "Capture of OffscreenTab content did not start " - "within timeout for start_url=" << start_url_.spec(); - owner_->DestroyTab(this); - return; // |this| is no longer valid. - } - - // Schedule the timer to check again in a second. - capture_poll_timer_.Start( - FROM_HERE, - base::TimeDelta::FromSeconds(kPollIntervalInSeconds), - base::Bind(&OffscreenTab::DieIfContentCaptureEnded, - base::Unretained(this))); -} - -void OffscreenTab::DieIfOriginalProfileDestroyed(Profile* profile) { - DCHECK(profile == otr_profile_registration_->profile()); - owner_->DestroyTab(this); -} - -} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.h b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.h deleted file mode 100644 index 93e51b9fc51..00000000000 --- a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tab.h +++ /dev/null @@ -1,236 +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. - -#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_ -#define CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_ - -#include <stdint.h> - -#include <memory> -#include <string> -#include <vector> - -#include "base/macros.h" -#include "base/time/time.h" -#include "base/timer/timer.h" -#include "chrome/browser/media/router/presentation/independent_otr_profile_manager.h" -#include "content/public/browser/web_contents_delegate.h" -#include "content/public/browser/web_contents_observer.h" -#include "content/public/browser/web_contents_user_data.h" -#include "ui/gfx/geometry/size.h" - -namespace media_router { -class NavigationPolicy; -} // namespace media_router - -namespace extensions { - -class OffscreenTab; // Forward declaration. See below. - -// Creates, owns, and manages all OffscreenTab instances created by the same -// extension background page. When the extension background page's WebContents -// is about to be destroyed, its associated OffscreenTabsOwner and all of its -// OffscreenTab instances are destroyed. -// -// Usage: -// -// OffscreenTabsOwner::Get(extension_contents) -// ->OpenNewTab(start_url, size, std::string()); -// -// This class operates exclusively on the UI thread and so is not thread-safe. -class OffscreenTabsOwner - : public content::WebContentsUserData<OffscreenTabsOwner> { - public: - ~OffscreenTabsOwner() final; - - // Returns the OffscreenTabsOwner instance associated with the given extension - // background page's WebContents. Never returns nullptr. - static OffscreenTabsOwner* Get(content::WebContents* extension_web_contents); - - // Instantiate a new offscreen tab and navigate it to |start_url|. The new - // tab's main frame will start out with the given |initial_size| in DIP - // coordinates. If too many offscreen tabs are already running, nothing - // happens and nullptr is returned. - // - // If |optional_presentation_id| is non-empty, the offscreen tab is registered - // for use by the Media Router (chrome/browser/media/router/...) as the - // receiving browsing context for the W3C Presentation API. - OffscreenTab* OpenNewTab(const GURL& start_url, - const gfx::Size& initial_size, - const std::string& optional_presentation_id); - - protected: - friend class OffscreenTab; - - // Accessor to the extension background page's WebContents. - content::WebContents* extension_web_contents() const { - return extension_web_contents_; - } - - // Shuts down and destroys the |tab|. - void DestroyTab(OffscreenTab* tab); - - private: - friend class content::WebContentsUserData<OffscreenTabsOwner>; - - explicit OffscreenTabsOwner(content::WebContents* extension_web_contents); - - content::WebContents* const extension_web_contents_; - std::vector<std::unique_ptr<OffscreenTab>> tabs_; - - DISALLOW_COPY_AND_ASSIGN(OffscreenTabsOwner); -}; - -// Owns and controls a sandboxed WebContents instance hosting the rendering -// engine for an offscreen tab. Since the offscreen tab does not interact with -// the user in any direct way, the WebContents is not attached to any Browser -// window/UI, and any input and focusing capabilities are blocked. -// -// OffscreenTab is instantiated by OffscreenTabsOwner. An instance is shut down -// one of three ways: -// -// 1. When WebContents::IsBeingCaptured() returns false, indicating there are -// no more consumers of its captured content (e.g., when all MediaStreams -// have been closed). OffscreenTab will auto-detect this case and -// self-destruct. -// 2. By the renderer, where the WebContents implementation will invoke the -// WebContentsDelegate::CloseContents() override. This occurs, for -// example, when a page calls window.close(). -// 3. Automatically, when the extension background page's WebContents is -// destroyed. -// -// This class operates exclusively on the UI thread and so is not thread-safe. -class OffscreenTab : protected content::WebContentsDelegate, - protected content::WebContentsObserver { - public: - ~OffscreenTab() final; - - // The WebContents instance hosting the rendering engine for this - // OffscreenTab. - content::WebContents* web_contents() const { - return offscreen_tab_web_contents_.get(); - } - - protected: - friend class OffscreenTabsOwner; - - explicit OffscreenTab(OffscreenTabsOwner* owner); - - // Creates the WebContents instance containing the offscreen tab's page, - // configures it for offscreen rendering at the given |initial_size|, and - // navigates it to |start_url|. This is invoked once by OffscreenTabsOwner - // just after construction. - void Start(const GURL& start_url, - const gfx::Size& initial_size, - const std::string& optional_presentation_id); - - // Closes the underlying WebContents. - void Close(); - - // content::WebContentsDelegate overrides to provide the desired behaviors. - void CloseContents(content::WebContents* source) final; - bool ShouldSuppressDialogs(content::WebContents* source) final; - bool ShouldFocusLocationBarByDefault(content::WebContents* source) final; - bool ShouldFocusPageAfterCrash() final; - void CanDownload(const GURL& url, - const std::string& request_method, - const base::Callback<void(bool)>& callback) final; - bool HandleContextMenu(const content::ContextMenuParams& params) final; - content::KeyboardEventProcessingResult PreHandleKeyboardEvent( - content::WebContents* source, - const content::NativeWebKeyboardEvent& event) final; - bool PreHandleGestureEvent(content::WebContents* source, - const blink::WebGestureEvent& event) final; - bool CanDragEnter(content::WebContents* source, - const content::DropData& data, - blink::WebDragOperationsMask operations_allowed) final; - bool ShouldCreateWebContents( - content::WebContents* web_contents, - content::RenderFrameHost* opener, - content::SiteInstance* source_site_instance, - int32_t route_id, - int32_t main_frame_route_id, - int32_t main_frame_widget_route_id, - content::mojom::WindowContainerType window_container_type, - const GURL& opener_url, - const std::string& frame_name, - const GURL& target_url, - const std::string& partition_id, - content::SessionStorageNamespace* session_storage_namespace) final; - bool EmbedsFullscreenWidget() const final; - void EnterFullscreenModeForTab( - content::WebContents* contents, - const GURL& origin, - const blink::WebFullscreenOptions& options) final; - void ExitFullscreenModeForTab(content::WebContents* contents) final; - bool IsFullscreenForTabOrPending( - const content::WebContents* contents) const final; - blink::WebDisplayMode GetDisplayMode( - const content::WebContents* contents) const final; - void RequestMediaAccessPermission( - content::WebContents* contents, - const content::MediaStreamRequest& request, - content::MediaResponseCallback callback) final; - bool CheckMediaAccessPermission(content::RenderFrameHost* render_frame_host, - const GURL& security_origin, - content::MediaStreamType type) final; - - // content::WebContentsObserver overrides - void DidShowFullscreenWidget() final; - void DidStartNavigation(content::NavigationHandle* navigation_handle) final; - - private: - bool in_fullscreen_mode() const { return !non_fullscreen_size_.IsEmpty(); } - - // Called by |capture_poll_timer_| to automatically destroy this OffscreenTab - // when the capturer count returns to zero. - void DieIfContentCaptureEnded(); - - // Called if the profile that our OTR profile is based on is being destroyed - // and |this| therefore needs to be destroyed also. - void DieIfOriginalProfileDestroyed(Profile* profile); - - OffscreenTabsOwner* const owner_; - - // The initial navigation URL, which may or may not match the current URL if - // page-initiated navigations have occurred. - GURL start_url_; - - // A non-shared off-the-record profile based on the profile of the extension - // background page. - const std::unique_ptr<IndependentOTRProfileManager::OTRProfileRegistration> - otr_profile_registration_; - - // The WebContents containing the off-screen tab's page. - std::unique_ptr<content::WebContents> offscreen_tab_web_contents_; - - // The time at which Start() finished creating |offscreen_tab_web_contents_|. - base::TimeTicks start_time_; - - // Set to the original size of the renderer just before entering fullscreen - // mode. When not in fullscreen mode, this is an empty size. - gfx::Size non_fullscreen_size_; - - // Poll timer to monitor the capturer count on |offscreen_tab_web_contents_|. - // When the capturer count returns to zero, this OffscreenTab is automatically - // destroyed. - // - // TODO(miu): Add a method to WebContentsObserver to report capturer count - // changes and get rid of this polling-based approach. - // http://crbug.com/540965 - base::OneShotTimer capture_poll_timer_; - - // This is false until after the Start() method is called, and capture of the - // |offscreen_tab_web_contents_| is first detected. - bool content_capture_was_detected_; - - // Object consulted to determine which offscreen tab navigations are allowed. - std::unique_ptr<media_router::NavigationPolicy> navigation_policy_; - - DISALLOW_COPY_AND_ASSIGN(OffscreenTab); -}; - -} // namespace extensions - -#endif // CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TAB_H_ diff --git a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc new file mode 100644 index 00000000000..202e849c408 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.cc @@ -0,0 +1,114 @@ +// 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. + +#include "chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h" + +#include <algorithm> +#include <utility> +#include <vector> + +#include "base/bind.h" +#include "base/stl_util.h" +#include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" +#include "chrome/browser/profiles/profile.h" +#include "content/public/browser/web_contents.h" +#include "extensions/browser/extension_host.h" +#include "extensions/browser/process_manager.h" + +using content::WebContents; + +namespace { + +// Upper limit on the number of simultaneous off-screen tabs per extension +// instance. +const int kMaxOffscreenTabsPerExtension = 4; + +} // namespace + +namespace extensions { + +OffscreenTabsOwner::OffscreenTabsOwner(WebContents* extension_web_contents) + : extension_web_contents_(extension_web_contents) { + DCHECK(extension_web_contents_); +} + +OffscreenTabsOwner::~OffscreenTabsOwner() {} + +// static +OffscreenTabsOwner* OffscreenTabsOwner::Get( + content::WebContents* extension_web_contents) { + // CreateForWebContents() really means "create if not exists." + CreateForWebContents(extension_web_contents); + return FromWebContents(extension_web_contents); +} + +OffscreenTab* OffscreenTabsOwner::OpenNewTab( + const GURL& start_url, + const gfx::Size& initial_size, + const std::string& optional_presentation_id) { + if (tabs_.size() >= kMaxOffscreenTabsPerExtension) + return nullptr; // Maximum number of offscreen tabs reached. + + tabs_.emplace_back(std::make_unique<OffscreenTab>( + this, extension_web_contents_->GetBrowserContext())); + tabs_.back()->Start(start_url, initial_size, optional_presentation_id); + return tabs_.back().get(); +} + +void OffscreenTabsOwner::RequestMediaAccessPermission( + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) { + // This method is being called to check whether an extension is permitted to + // capture the page. Verify that the request is being made by the extension + // that spawned this OffscreenTab. + + // Find the extension ID associated with the extension background page's + // WebContents. + content::BrowserContext* const extension_browser_context = + extension_web_contents_->GetBrowserContext(); + const extensions::Extension* const extension = + ProcessManager::Get(extension_browser_context) + ->GetExtensionForWebContents(extension_web_contents_); + const std::string extension_id = extension ? extension->id() : ""; + LOG_IF(DFATAL, extension_id.empty()) + << "Extension that started this OffscreenTab was not found."; + + // If verified, allow any tab capture audio/video devices that were requested. + extensions::TabCaptureRegistry* const tab_capture_registry = + extensions::TabCaptureRegistry::Get(extension_browser_context); + content::MediaStreamDevices devices; + if (tab_capture_registry && + tab_capture_registry->VerifyRequest( + request.render_process_id, request.render_frame_id, extension_id)) { + if (request.audio_type == content::MEDIA_GUM_TAB_AUDIO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_GUM_TAB_AUDIO_CAPTURE, std::string(), std::string())); + } + if (request.video_type == content::MEDIA_GUM_TAB_VIDEO_CAPTURE) { + devices.push_back(content::MediaStreamDevice( + content::MEDIA_GUM_TAB_VIDEO_CAPTURE, std::string(), std::string())); + } + } + + DVLOG(2) << "Allowing " << devices.size() + << " capture devices for OffscreenTab content."; + + std::move(callback).Run(devices, + devices.empty() ? content::MEDIA_DEVICE_INVALID_STATE + : content::MEDIA_DEVICE_OK, + nullptr); +} + +void OffscreenTabsOwner::DestroyTab(OffscreenTab* tab) { + for (std::vector<std::unique_ptr<OffscreenTab>>::iterator iter = + tabs_.begin(); + iter != tabs_.end(); ++iter) { + if (iter->get() == tab) { + tabs_.erase(iter); + break; + } + } +} + +} // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h new file mode 100644 index 00000000000..badea71d804 --- /dev/null +++ b/chromium/chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "chrome/browser/media/offscreen_tab.h" +#include "content/public/browser/web_contents_user_data.h" +#include "ui/gfx/geometry/size.h" + +class OffscreenTab; + +namespace extensions { + +// Creates, owns, and manages all OffscreenTab instances created by the same +// extension background page. When the extension background page's WebContents +// is about to be destroyed, its associated OffscreenTabsOwner and all of its +// OffscreenTab instances are destroyed. +// +// Usage: +// +// OffscreenTabsOwner::Get(extension_contents) +// ->OpenNewTab(start_url, size, std::string()); +// +// This class operates exclusively on the UI thread and so is not thread-safe. +class OffscreenTabsOwner final + : public OffscreenTab::Owner, + public content::WebContentsUserData<OffscreenTabsOwner> { + public: + ~OffscreenTabsOwner() final; + + // Returns the OffscreenTabsOwner instance associated with the given extension + // background page's WebContents. Never returns nullptr. + static OffscreenTabsOwner* Get(content::WebContents* extension_web_contents); + + // Instantiate a new offscreen tab and navigate it to |start_url|. The new + // tab's main frame will start out with the given |initial_size| in DIP + // coordinates. If too many offscreen tabs are already running, nothing + // happens and nullptr is returned. + // + // If |optional_presentation_id| is non-empty, the offscreen tab is registered + // for use by the Media Router (chrome/browser/media/router/...) as the + // receiving browsing context for the W3C Presentation API. + OffscreenTab* OpenNewTab(const GURL& start_url, + const gfx::Size& initial_size, + const std::string& optional_presentation_id); + + private: + friend class content::WebContentsUserData<OffscreenTabsOwner>; + + explicit OffscreenTabsOwner(content::WebContents* extension_web_contents); + + // OffscreenTab::Owner implementation. + void RequestMediaAccessPermission( + const content::MediaStreamRequest& request, + content::MediaResponseCallback callback) override; + void DestroyTab(OffscreenTab* tab) override; + + content::WebContents* const extension_web_contents_; + std::vector<std::unique_ptr<OffscreenTab>> tabs_; + + DISALLOW_COPY_AND_ASSIGN(OffscreenTabsOwner); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_TAB_CAPTURE_OFFSCREEN_TABS_OWNER_H_ diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc index 529970f3025..7ea18093f69 100644 --- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc +++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_api.cc @@ -18,7 +18,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/values.h" -#include "chrome/browser/extensions/api/tab_capture/offscreen_tab.h" +#include "chrome/browser/extensions/api/tab_capture/offscreen_tabs_owner.h" #include "chrome/browser/extensions/api/tab_capture/tab_capture_registry.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/sessions/session_tab_helper.h" @@ -26,6 +26,7 @@ #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_switches.h" +#include "content/public/browser/desktop_media_id.h" #include "content/public/browser/render_frame_host.h" #include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" @@ -35,6 +36,8 @@ #include "extensions/common/permissions/permissions_data.h" #include "extensions/common/switches.h" +using content::DesktopMediaID; +using content::WebContentsMediaCaptureId; using extensions::api::tab_capture::MediaStreamConstraint; namespace TabCapture = extensions::api::tab_capture; @@ -116,21 +119,7 @@ void FilterDeprecatedGoogConstraints(TabCapture::CaptureOptions* options) { } } -// Add Chrome-specific source identifiers to the MediaStreamConstraints objects -// in |options| to provide references to the |target_contents| to be captured. -void AddMediaStreamSourceConstraints(content::WebContents* target_contents, - TabCapture::CaptureOptions* options) { - DCHECK(options); - DCHECK(target_contents); - - MediaStreamConstraint* constraints_to_modify[2] = { nullptr, nullptr }; - - if (options->audio && *options->audio) { - if (!options->audio_constraints) - options->audio_constraints.reset(new MediaStreamConstraint); - constraints_to_modify[0] = options->audio_constraints.get(); - } - +bool GetAutoThrottlingFromOptions(TabCapture::CaptureOptions* options) { bool enable_auto_throttling = false; if (options->video && *options->video) { if (options->video_constraints) { @@ -146,21 +135,45 @@ void AddMediaStreamSourceConstraints(content::WebContents* target_contents, // Remove the key from the properties to avoid an "unrecognized // constraint" error in the renderer. props.RemoveWithoutPathExpansion(kEnableAutoThrottlingKey, nullptr); - } else { - options->video_constraints.reset(new MediaStreamConstraint); } - constraints_to_modify[1] = options->video_constraints.get(); } - // Format the device ID that references the target tab. - content::RenderFrameHost* const main_frame = target_contents->GetMainFrame(); - // TODO(miu): We should instead use a "randomly generated device ID" scheme, - // like that employed by the desktop capture API. http://crbug.com/163100 - const std::string device_id = base::StringPrintf( - "web-contents-media-stream://%i:%i%s", - main_frame->GetProcess()->GetID(), - main_frame->GetRoutingID(), - enable_auto_throttling ? "?throttling=auto" : ""); + return enable_auto_throttling; +} + +DesktopMediaID BuildDesktopMediaID(content::WebContents* target_contents, + TabCapture::CaptureOptions* options) { + content::RenderFrameHost* const target_frame = + target_contents->GetMainFrame(); + DesktopMediaID source( + DesktopMediaID::TYPE_WEB_CONTENTS, DesktopMediaID::kNullId, + WebContentsMediaCaptureId(target_frame->GetProcess()->GetID(), + target_frame->GetRoutingID(), + GetAutoThrottlingFromOptions(options), false)); + return source; +} + +// Add Chrome-specific source identifiers to the MediaStreamConstraints objects +// in |options| to provide references to the |target_contents| to be captured. +void AddMediaStreamSourceConstraints(content::WebContents* target_contents, + TabCapture::CaptureOptions* options, + const std::string& device_id) { + DCHECK(options); + DCHECK(target_contents); + + MediaStreamConstraint* constraints_to_modify[2] = {nullptr, nullptr}; + + if (options->audio && *options->audio) { + if (!options->audio_constraints) + options->audio_constraints.reset(new MediaStreamConstraint); + constraints_to_modify[0] = options->audio_constraints.get(); + } + + if (options->video && *options->video) { + if (!options->video_constraints) + options->video_constraints.reset(new MediaStreamConstraint); + constraints_to_modify[1] = options->video_constraints.get(); + } // Append chrome specific tab constraints. for (MediaStreamConstraint* msc : constraints_to_modify) { @@ -217,14 +230,21 @@ ExtensionFunction::ResponseAction TabCaptureCaptureFunction::Run() { if (!OptionsSpecifyAudioOrVideo(params->options)) return RespondNow(Error(kNoAudioOrVideo)); + DesktopMediaID source = + BuildDesktopMediaID(target_contents, ¶ms->options); + content::WebContents* const extension_web_contents = GetSenderWebContents(); + EXTENSION_FUNCTION_VALIDATE(extension_web_contents); TabCaptureRegistry* registry = TabCaptureRegistry::Get(browser_context()); - if (!registry->AddRequest(target_contents, extension_id, false)) { + std::string device_id = registry->AddRequest( + target_contents, extension_id, false, extension()->url(), source, + extension()->name(), extension_web_contents); + if (device_id.empty()) { // TODO(miu): Allow multiple consumers of single tab capture. // http://crbug.com/535336 return RespondNow(Error(kCapturingSameTab)); } FilterDeprecatedGoogConstraints(¶ms->options); - AddMediaStreamSourceConstraints(target_contents, ¶ms->options); + AddMediaStreamSourceConstraints(target_contents, ¶ms->options, device_id); // At this point, everything is set up in the browser process. It's now up to // the custom JS bindings in the extension's render process to request a @@ -283,15 +303,21 @@ ExtensionFunction::ResponseAction TabCaptureCaptureOffscreenTabFunction::Run() { if (!offscreen_tab) return RespondNow(Error(kTooManyOffscreenTabs)); - if (!TabCaptureRegistry::Get(browser_context())->AddRequest( - offscreen_tab->web_contents(), extension()->id(), true)) { + content::WebContents* target_contents = offscreen_tab->web_contents(); + const std::string& extension_id = extension()->id(); + DesktopMediaID source = + BuildDesktopMediaID(target_contents, ¶ms->options); + TabCaptureRegistry* registry = TabCaptureRegistry::Get(browser_context()); + std::string device_id = registry->AddRequest( + target_contents, extension_id, true, extension()->url(), source, + extension()->name(), extension_web_contents); + if (device_id.empty()) { // TODO(miu): Allow multiple consumers of single tab capture. // http://crbug.com/535336 return RespondNow(Error(kCapturingSameOffscreenTab)); } FilterDeprecatedGoogConstraints(¶ms->options); - AddMediaStreamSourceConstraints(offscreen_tab->web_contents(), - ¶ms->options); + AddMediaStreamSourceConstraints(target_contents, ¶ms->options, device_id); // At this point, everything is set up in the browser process. It's now up to // the custom JS bindings in the extension's render process to complete the diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc index c8a4493a740..52b033eb2cb 100644 --- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc +++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.cc @@ -13,7 +13,9 @@ #include "chrome/browser/sessions/session_tab_helper.h" #include "components/keyed_service/content/browser_context_dependency_manager.h" #include "content/public/browser/browser_thread.h" +#include "content/public/browser/desktop_streams_registry.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_observer.h" #include "extensions/browser/event_router.h" @@ -138,8 +140,9 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver { #if defined(USE_AURA) window_agent_(target_contents->GetNativeView()), #endif - render_process_id_(-1), - render_frame_id_(-1) { + render_process_id_( + target_contents->GetMainFrame()->GetProcess()->GetID()), + render_frame_id_(target_contents->GetMainFrame()->GetRoutingID()) { DCHECK(web_contents()); DCHECK(registry_); } @@ -165,17 +168,8 @@ class TabCaptureRegistry::LiveRequest : public content::WebContentsObserver { is_verified_ = true; } - // TODO(miu): See TODO(miu) in VerifyRequest() below. - void SetOriginallyTargettedRenderFrameID(int render_process_id, - int render_frame_id) { - DCHECK_GT(render_frame_id, 0); - DCHECK_EQ(render_frame_id_, -1); // Setting ID only once. - render_process_id_ = render_process_id; - render_frame_id_ = render_frame_id; - } - - bool WasOriginallyTargettingRenderFrameID(int render_process_id, - int render_frame_id) const { + bool WasTargettingRenderFrameID(int render_process_id, + int render_frame_id) const { return render_process_id_ == render_process_id && render_frame_id_ == render_frame_id; } @@ -298,16 +292,22 @@ void TabCaptureRegistry::OnExtensionUnloaded( } } -bool TabCaptureRegistry::AddRequest(content::WebContents* target_contents, - const std::string& extension_id, - bool is_anonymous) { +std::string TabCaptureRegistry::AddRequest( + content::WebContents* target_contents, + const std::string& extension_id, + bool is_anonymous, + const GURL& origin, + content::DesktopMediaID source, + const std::string& extension_name, + content::WebContents* caller_contents) { + std::string device_id; LiveRequest* const request = FindRequest(target_contents); // Currently, we do not allow multiple active captures for same tab. if (request != NULL) { if (request->capture_state() == tab_capture::TAB_CAPTURE_STATE_PENDING || request->capture_state() == tab_capture::TAB_CAPTURE_STATE_ACTIVE) { - return false; + return device_id; } else { // Delete the request before creating its replacement (below). KillRequest(request); @@ -316,7 +316,15 @@ bool TabCaptureRegistry::AddRequest(content::WebContents* target_contents, requests_.push_back(std::make_unique<LiveRequest>( target_contents, extension_id, is_anonymous, this)); - return true; + + content::RenderFrameHost* const main_frame = caller_contents->GetMainFrame(); + if (main_frame) { + device_id = content::DesktopStreamsRegistry::GetInstance()->RegisterStream( + main_frame->GetProcess()->GetID(), main_frame->GetRoutingID(), origin, + source, extension_name, content::kRegistryStreamTypeTab); + } + + return device_id; } bool TabCaptureRegistry::VerifyRequest( @@ -325,56 +333,35 @@ bool TabCaptureRegistry::VerifyRequest( const std::string& extension_id) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - LiveRequest* const request = FindRequest( - content::WebContents::FromRenderFrameHost( - content::RenderFrameHost::FromID( - render_process_id, render_frame_id))); - if (!request) + LiveRequest* const request = FindRequest(render_process_id, render_frame_id); + if (!request) { return false; // Unknown RenderFrameHost ID, or frame has gone away. + } - // TODO(miu): We should probably also verify the origin URL, like the desktop - // capture API. http://crbug.com/163100 if (request->is_verified() || request->extension_id() != extension_id || (request->capture_state() != tab_capture::TAB_CAPTURE_STATE_NONE && request->capture_state() != tab_capture::TAB_CAPTURE_STATE_PENDING)) return false; - // TODO(miu): The RenderFrameHost IDs should be set when LiveRequest is - // constructed, but ExtensionFunction does not yet support use of - // render_frame_host() to determine the exact RenderFrameHost for the call to - // AddRequest() above. Fix tab_capture_api.cc, and then fix this ugly hack. - // http://crbug.com/304341 - request->SetOriginallyTargettedRenderFrameID( - render_process_id, render_frame_id); - request->SetIsVerified(); return true; } void TabCaptureRegistry::OnRequestUpdate( - int original_target_render_process_id, - int original_target_render_frame_id, + int target_render_process_id, + int target_render_frame_id, content::MediaStreamType stream_type, const content::MediaRequestState new_state) { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (stream_type != content::MEDIA_TAB_VIDEO_CAPTURE && - stream_type != content::MEDIA_TAB_AUDIO_CAPTURE) { + if (stream_type != content::MEDIA_GUM_TAB_VIDEO_CAPTURE && + stream_type != content::MEDIA_GUM_TAB_AUDIO_CAPTURE) { return; } - LiveRequest* request = FindRequest(original_target_render_process_id, - original_target_render_frame_id); + LiveRequest* request = + FindRequest(target_render_process_id, target_render_frame_id); if (!request) { - // Fall-back: Search again using WebContents since this method may have been - // called before VerifyRequest() set the RenderFrameHost ID. If the - // RenderFrameHost has gone away, that's okay since the upcoming call to - // VerifyRequest() will fail, and that means the tracking of request updates - // doesn't matter anymore. - request = FindRequest(content::WebContents::FromRenderFrameHost( - content::RenderFrameHost::FromID(original_target_render_process_id, - original_target_render_frame_id))); - if (!request) return; // Stale or invalid request update. } @@ -444,12 +431,11 @@ TabCaptureRegistry::LiveRequest* TabCaptureRegistry::FindRequest( } TabCaptureRegistry::LiveRequest* TabCaptureRegistry::FindRequest( - int original_target_render_process_id, - int original_target_render_frame_id) const { + int target_render_process_id, + int target_render_frame_id) const { for (const std::unique_ptr<LiveRequest>& request : requests_) { - if (request->WasOriginallyTargettingRenderFrameID( - original_target_render_process_id, - original_target_render_frame_id)) { + if (request->WasTargettingRenderFrameID(target_render_process_id, + target_render_frame_id)) { return request.get(); } } diff --git a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h index 4d4abd65d1e..80b2b7e361a 100644 --- a/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h +++ b/chromium/chrome/browser/extensions/api/tab_capture/tab_capture_registry.h @@ -13,6 +13,7 @@ #include "base/scoped_observer.h" #include "chrome/browser/media/webrtc/media_capture_devices_dispatcher.h" #include "chrome/common/extensions/api/tab_capture.h" +#include "content/public/browser/desktop_media_id.h" #include "content/public/browser/media_request_state.h" #include "extensions/browser/browser_context_keyed_api_factory.h" #include "extensions/browser/extension_registry_observer.h" @@ -47,18 +48,23 @@ class TabCaptureRegistry : public BrowserContextKeyedAPI, base::ListValue* list_of_capture_info) const; // Add a tab capture request to the registry when a stream is requested - // through the API. |target_contents| refers to the WebContents associated - // with the tab to be captured. |extension_id| refers to the Extension - // initiating the request. |is_anonymous| is true if GetCapturedTabs() should - // not list the captured tab, and no status change events should be dispatched - // for it. - // - // TODO(miu): This is broken in that it's possible for a later WebContents - // instance to have the same pointer value as a previously-destroyed one. To - // be fixed while working on http://crbug.com/163100. - bool AddRequest(content::WebContents* target_contents, - const std::string& extension_id, - bool is_anonymous); + // through the API and create a randomly generated device id after user + // initiated access to |source| for the |origin|. If capture is already + // taking place for the same tab, this operation fails and returns an + // empty string. + // |target_contents|: the WebContents associated with the tab to be captured. + // |extension_id|: the Extension initiating the request. + // |is_anonymous| is true if GetCapturedTabs() should not list the captured + // tab, and no status change events should be dispatched for it. + // |caller_contents|: the WebContents associated with the tab/extension that + // starts the capture. + std::string AddRequest(content::WebContents* target_contents, + const std::string& extension_id, + bool is_anonymous, + const GURL& origin, + content::DesktopMediaID source, + const std::string& extension_name, + content::WebContents* caller_contents); // Called by MediaStreamDevicesController to verify the request before // creating the stream. |render_process_id| and |render_frame_id| are used to @@ -91,8 +97,8 @@ class TabCaptureRegistry : public BrowserContextKeyedAPI, UnloadedExtensionReason reason) override; // MediaCaptureDevicesDispatcher::Observer implementation. - void OnRequestUpdate(int original_target_render_process_id, - int original_target_render_frame_id, + void OnRequestUpdate(int target_render_process_id, + int target_render_frame_id, content::MediaStreamType stream_type, const content::MediaRequestState state) override; @@ -102,8 +108,8 @@ class TabCaptureRegistry : public BrowserContextKeyedAPI, // Look-up a LiveRequest associated with the given |target_contents| (or // the originally targetted RenderFrameHost), if any. LiveRequest* FindRequest(const content::WebContents* target_contents) const; - LiveRequest* FindRequest(int original_target_render_process_id, - int original_target_render_frame_id) const; + LiveRequest* FindRequest(int target_render_process_id, + int target_render_frame_id) const; // Removes the |request| from |requests_|, thus causing its destruction. void KillRequest(LiveRequest* request); diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc index 6dbd04f2fb0..e5dbcdc147d 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_api.cc @@ -53,7 +53,7 @@ #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/ui/tabs/tab_utils.h" #include "chrome/browser/ui/window_sizer/window_sizer.h" -#include "chrome/browser/web_applications/extensions/web_app_extension_helpers.h" +#include "chrome/browser/web_applications/components/web_app_helpers.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/api/tabs.h" #include "chrome/common/extensions/api/windows.h" @@ -280,7 +280,7 @@ void SetLockedFullscreenState(Browser* browser, bool locked) { // Disallow screenshots in locked fullscreen mode. // TODO(isandrk, 816900): ChromeScreenshotGrabber isn't implemented in Mash // yet, remove this conditional when it becomes available. - if (features::IsAshInBrowserProcess()) + if (!features::IsMultiProcessMash()) ChromeScreenshotGrabber::Get()->set_screenshots_allowed(!locked); // Reset the clipboard and kill dev tools when entering or exiting locked @@ -593,7 +593,7 @@ ExtensionFunction::ResponseAction WindowsCreateFunction::Run() { create_params.initial_bounds = window_bounds; } else { create_params = Browser::CreateParams::CreateForApp( - web_app::GenerateApplicationNameFromExtensionId(extension_id), + web_app::GenerateApplicationNameFromAppId(extension_id), false /* trusted_source */, window_bounds, window_profile, user_gesture()); } 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 43d4ab78ab9..a2c3f3ab41d 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.cc @@ -63,6 +63,23 @@ bool WillDispatchTabUpdatedEvent( return true; } +bool WillDispatchTabCreatedEvent(WebContents* contents, + bool active, + content::BrowserContext* context, + const Extension* extension, + Event* event, + const base::DictionaryValue* listener_filter) { + event->event_args->Clear(); + std::unique_ptr<base::DictionaryValue> tab_value = + ExtensionTabUtil::CreateTabObject(contents, ExtensionTabUtil::kScrubTab, + extension) + ->ToValue(); + tab_value->SetBoolean(tabs_constants::kSelectedKey, active); + tab_value->SetBoolean(tabs_constants::kActiveKey, active); + event->event_args->Append(std::move(tab_value)); + return true; +} + } // namespace TabsEventRouter::TabEntry::TabEntry(TabsEventRouter* router, @@ -155,74 +172,143 @@ bool TabsEventRouter::ShouldTrackBrowser(Browser* browser) { ExtensionTabUtil::BrowserSupportsTabs(browser); } -void TabsEventRouter::RegisterForTabNotifications(WebContents* contents) { - favicon_scoped_observer_.Add( - favicon::ContentFaviconDriver::FromWebContents(contents)); +void TabsEventRouter::OnBrowserSetLastActive(Browser* browser) { + TabsWindowsAPI* tabs_window_api = TabsWindowsAPI::Get(profile_); + if (tabs_window_api) { + tabs_window_api->windows_event_router()->OnActiveWindowChanged( + browser ? browser->extension_window_controller() : NULL); + } +} - ZoomController::FromWebContents(contents)->AddObserver(this); +void TabsEventRouter::OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) { + switch (change.type()) { + case TabStripModelChange::kInserted: { + for (const auto& delta : change.deltas()) { + DispatchTabInsertedAt(tab_strip_model, delta.insert.contents, + delta.insert.index, + selection.new_contents == delta.insert.contents); + } + break; + } + case TabStripModelChange::kRemoved: { + for (const auto& delta : change.deltas()) { + if (delta.remove.will_be_deleted) + DispatchTabClosingAt(tab_strip_model, delta.remove.contents, + delta.remove.index); + + DispatchTabDetachedAt(delta.remove.contents, delta.remove.index, + selection.old_contents == delta.remove.contents); + } + break; + } + case TabStripModelChange::kMoved: { + for (const auto& delta : change.deltas()) { + DispatchTabMoved(delta.move.contents, delta.move.from_index, + delta.move.to_index); + } + break; + } + case TabStripModelChange::kReplaced: { + for (const auto& delta : change.deltas()) { + DispatchTabReplacedAt(delta.replace.old_contents, + delta.replace.new_contents, delta.replace.index); + } + break; + } + case TabStripModelChange::kSelectionOnly: + break; + } - int tab_id = ExtensionTabUtil::GetTabId(contents); - DCHECK(tab_entries_.find(tab_id) == tab_entries_.end()); - tab_entries_[tab_id] = std::make_unique<TabEntry>(this, contents); + if (tab_strip_model->empty()) + return; + + if (selection.active_tab_changed()) { + DispatchActiveTabChanged(selection.old_contents, selection.new_contents, + selection.new_model.active(), selection.reason); + } + + if (selection.selection_changed()) { + DispatchTabSelectionChanged(tab_strip_model, selection.old_model); + } } -void TabsEventRouter::UnregisterForTabNotifications(WebContents* contents) { - favicon_scoped_observer_.Remove( - favicon::ContentFaviconDriver::FromWebContents(contents)); +void TabsEventRouter::TabChangedAt(WebContents* contents, + int index, + TabChangeType change_type) { + TabEntry* entry = GetTabEntry(contents); + // TabClosingAt() may have already removed the entry for |contents| even + // though the tab has not yet been detached. + if (entry) + TabUpdated(entry, entry->UpdateLoadState()); +} - ZoomController::FromWebContents(contents)->RemoveObserver(this); +void TabsEventRouter::TabPinnedStateChanged(TabStripModel* tab_strip_model, + WebContents* contents, + int index) { + std::set<std::string> changed_property_names; + changed_property_names.insert(tabs_constants::kPinnedKey); + DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); +} - int tab_id = ExtensionTabUtil::GetTabId(contents); - int removed_count = tab_entries_.erase(tab_id); - DCHECK_GT(removed_count, 0); +void TabsEventRouter::OnZoomChanged( + const ZoomController::ZoomChangedEventData& data) { + DCHECK(data.web_contents); + int tab_id = ExtensionTabUtil::GetTabId(data.web_contents); + if (tab_id < 0) + return; + + // Prepare the zoom change information. + api::tabs::OnZoomChange::ZoomChangeInfo zoom_change_info; + zoom_change_info.tab_id = tab_id; + zoom_change_info.old_zoom_factor = + content::ZoomLevelToZoomFactor(data.old_zoom_level); + zoom_change_info.new_zoom_factor = + content::ZoomLevelToZoomFactor(data.new_zoom_level); + ZoomModeToZoomSettings(data.zoom_mode, &zoom_change_info.zoom_settings); + + // Dispatch the |onZoomChange| event. + Profile* profile = + Profile::FromBrowserContext(data.web_contents->GetBrowserContext()); + DispatchEvent(profile, events::TABS_ON_ZOOM_CHANGE, + api::tabs::OnZoomChange::kEventName, + api::tabs::OnZoomChange::Create(zoom_change_info), + EventRouter::USER_GESTURE_UNKNOWN); } -void TabsEventRouter::OnBrowserSetLastActive(Browser* browser) { - TabsWindowsAPI* tabs_window_api = TabsWindowsAPI::Get(profile_); - if (tabs_window_api) { - tabs_window_api->windows_event_router()->OnActiveWindowChanged( - browser ? browser->extension_window_controller() : NULL); +void TabsEventRouter::OnFaviconUpdated( + favicon::FaviconDriver* favicon_driver, + NotificationIconType notification_icon_type, + const GURL& icon_url, + bool icon_url_changed, + const gfx::Image& image) { + if (notification_icon_type == NON_TOUCH_16_DIP && icon_url_changed) { + favicon::ContentFaviconDriver* content_favicon_driver = + static_cast<favicon::ContentFaviconDriver*>(favicon_driver); + FaviconUrlUpdated(content_favicon_driver->web_contents()); } } -static bool WillDispatchTabCreatedEvent( - WebContents* contents, - bool active, - content::BrowserContext* context, - const Extension* extension, - Event* event, - const base::DictionaryValue* listener_filter) { - event->event_args->Clear(); - std::unique_ptr<base::DictionaryValue> tab_value = - ExtensionTabUtil::CreateTabObject(contents, ExtensionTabUtil::kScrubTab, - extension) - ->ToValue(); - tab_value->SetBoolean(tabs_constants::kSelectedKey, active); - tab_value->SetBoolean(tabs_constants::kActiveKey, active); - event->event_args->Append(std::move(tab_value)); - return true; +void TabsEventRouter::OnDiscardedStateChange(WebContents* contents, + bool is_discarded) { + std::set<std::string> changed_property_names; + changed_property_names.insert(tabs_constants::kDiscardedKey); + DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); } -void TabsEventRouter::TabCreatedAt(WebContents* contents, - int index, - bool active) { - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - std::unique_ptr<base::ListValue> args(new base::ListValue); - auto event = std::make_unique<Event>(events::TABS_ON_CREATED, - api::tabs::OnCreated::kEventName, - std::move(args), profile); - event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED; - event->will_dispatch_callback = - base::Bind(&WillDispatchTabCreatedEvent, contents, active); - EventRouter::Get(profile)->BroadcastEvent(std::move(event)); - - RegisterForTabNotifications(contents); +void TabsEventRouter::OnAutoDiscardableStateChange(WebContents* contents, + bool is_auto_discardable) { + std::set<std::string> changed_property_names; + changed_property_names.insert(tabs_constants::kAutoDiscardableKey); + DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); } -void TabsEventRouter::TabInsertedAt(TabStripModel* tab_strip_model, - WebContents* contents, - int index, - bool active) { +void TabsEventRouter::DispatchTabInsertedAt(TabStripModel* tab_strip_model, + WebContents* contents, + int index, + bool active) { if (!GetTabEntry(contents)) { // We've never seen this tab, send create event as long as we're not in the // constructor. @@ -252,9 +338,33 @@ void TabsEventRouter::TabInsertedAt(TabStripModel* tab_strip_model, EventRouter::USER_GESTURE_UNKNOWN); } -void TabsEventRouter::TabDetachedAt(WebContents* contents, - int index, - bool was_active) { +void TabsEventRouter::DispatchTabClosingAt(TabStripModel* tab_strip_model, + WebContents* contents, + int index) { + int tab_id = ExtensionTabUtil::GetTabId(contents); + + std::unique_ptr<base::ListValue> args(new base::ListValue); + args->AppendInteger(tab_id); + + std::unique_ptr<base::DictionaryValue> object_args( + new base::DictionaryValue()); + object_args->SetInteger(tabs_constants::kWindowIdKey, + ExtensionTabUtil::GetWindowIdOfTab(contents)); + object_args->SetBoolean(tabs_constants::kWindowClosing, + tab_strip_model->closing_all()); + args->Append(std::move(object_args)); + + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + DispatchEvent(profile, events::TABS_ON_REMOVED, + api::tabs::OnRemoved::kEventName, std::move(args), + EventRouter::USER_GESTURE_UNKNOWN); + + UnregisterForTabNotifications(contents); +} + +void TabsEventRouter::DispatchTabDetachedAt(WebContents* contents, + int index, + bool was_active) { if (!GetTabEntry(contents)) { // The tab was removed. Don't send detach event. return; @@ -278,34 +388,10 @@ void TabsEventRouter::TabDetachedAt(WebContents* contents, EventRouter::USER_GESTURE_UNKNOWN); } -void TabsEventRouter::TabClosingAt(TabStripModel* tab_strip_model, - WebContents* contents, - int index) { - int tab_id = ExtensionTabUtil::GetTabId(contents); - - std::unique_ptr<base::ListValue> args(new base::ListValue); - args->AppendInteger(tab_id); - - std::unique_ptr<base::DictionaryValue> object_args( - new base::DictionaryValue()); - object_args->SetInteger(tabs_constants::kWindowIdKey, - ExtensionTabUtil::GetWindowIdOfTab(contents)); - object_args->SetBoolean(tabs_constants::kWindowClosing, - tab_strip_model->closing_all()); - args->Append(std::move(object_args)); - - Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); - DispatchEvent(profile, events::TABS_ON_REMOVED, - api::tabs::OnRemoved::kEventName, std::move(args), - EventRouter::USER_GESTURE_UNKNOWN); - - UnregisterForTabNotifications(contents); -} - -void TabsEventRouter::ActiveTabChanged(WebContents* old_contents, - WebContents* new_contents, - int index, - int reason) { +void TabsEventRouter::DispatchActiveTabChanged(WebContents* old_contents, + WebContents* new_contents, + int index, + int reason) { auto args = std::make_unique<base::ListValue>(); int tab_id = ExtensionTabUtil::GetTabId(new_contents); args->AppendInteger(tab_id); @@ -340,7 +426,7 @@ void TabsEventRouter::ActiveTabChanged(WebContents* old_contents, std::move(on_activated_args), gesture); } -void TabsEventRouter::TabSelectionChanged( +void TabsEventRouter::DispatchTabSelectionChanged( TabStripModel* tab_strip_model, const ui::ListSelectionModel& old_model) { ui::ListSelectionModel::SelectedIndices new_selection = @@ -378,9 +464,9 @@ void TabsEventRouter::TabSelectionChanged( EventRouter::USER_GESTURE_UNKNOWN); } -void TabsEventRouter::TabMoved(WebContents* contents, - int from_index, - int to_index) { +void TabsEventRouter::DispatchTabMoved(WebContents* contents, + int from_index, + int to_index) { std::unique_ptr<base::ListValue> args(new base::ListValue); args->AppendInteger(ExtensionTabUtil::GetTabId(contents)); @@ -400,6 +486,43 @@ void TabsEventRouter::TabMoved(WebContents* contents, std::move(args), EventRouter::USER_GESTURE_UNKNOWN); } +void TabsEventRouter::DispatchTabReplacedAt(WebContents* old_contents, + WebContents* new_contents, + int index) { + // Notify listeners that the next tabs closing or being added are due to + // WebContents being swapped. + const int new_tab_id = ExtensionTabUtil::GetTabId(new_contents); + const int old_tab_id = ExtensionTabUtil::GetTabId(old_contents); + std::unique_ptr<base::ListValue> args(new base::ListValue); + args->AppendInteger(new_tab_id); + args->AppendInteger(old_tab_id); + + DispatchEvent(Profile::FromBrowserContext(new_contents->GetBrowserContext()), + events::TABS_ON_REPLACED, api::tabs::OnReplaced::kEventName, + std::move(args), EventRouter::USER_GESTURE_UNKNOWN); + + UnregisterForTabNotifications(old_contents); + + if (!GetTabEntry(new_contents)) + RegisterForTabNotifications(new_contents); +} + +void TabsEventRouter::TabCreatedAt(WebContents* contents, + int index, + bool active) { + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); + std::unique_ptr<base::ListValue> args(new base::ListValue); + auto event = std::make_unique<Event>(events::TABS_ON_CREATED, + api::tabs::OnCreated::kEventName, + std::move(args), profile); + event->user_gesture = EventRouter::USER_GESTURE_NOT_ENABLED; + event->will_dispatch_callback = + base::Bind(&WillDispatchTabCreatedEvent, contents, active); + EventRouter::Get(profile)->BroadcastEvent(std::move(event)); + + RegisterForTabNotifications(contents); +} + void TabsEventRouter::TabUpdated(TabEntry* entry, std::set<std::string> changed_property_names) { auto* audible_helper = @@ -476,103 +599,32 @@ void TabsEventRouter::DispatchTabUpdatedEvent( EventRouter::Get(profile)->BroadcastEvent(std::move(event)); } -TabsEventRouter::TabEntry* TabsEventRouter::GetTabEntry(WebContents* contents) { - const auto it = tab_entries_.find(ExtensionTabUtil::GetTabId(contents)); - - return it == tab_entries_.end() ? nullptr : it->second.get(); -} - -void TabsEventRouter::TabChangedAt(WebContents* contents, - int index, - TabChangeType change_type) { - TabEntry* entry = GetTabEntry(contents); - // TabClosingAt() may have already removed the entry for |contents| even - // though the tab has not yet been detached. - if (entry) - TabUpdated(entry, entry->UpdateLoadState()); -} - -void TabsEventRouter::TabReplacedAt(TabStripModel* tab_strip_model, - WebContents* old_contents, - WebContents* new_contents, - int index) { - // Notify listeners that the next tabs closing or being added are due to - // WebContents being swapped. - const int new_tab_id = ExtensionTabUtil::GetTabId(new_contents); - const int old_tab_id = ExtensionTabUtil::GetTabId(old_contents); - std::unique_ptr<base::ListValue> args(new base::ListValue); - args->AppendInteger(new_tab_id); - args->AppendInteger(old_tab_id); - - DispatchEvent(Profile::FromBrowserContext(new_contents->GetBrowserContext()), - events::TABS_ON_REPLACED, api::tabs::OnReplaced::kEventName, - std::move(args), EventRouter::USER_GESTURE_UNKNOWN); - - UnregisterForTabNotifications(old_contents); +void TabsEventRouter::RegisterForTabNotifications(WebContents* contents) { + favicon_scoped_observer_.Add( + favicon::ContentFaviconDriver::FromWebContents(contents)); - if (!GetTabEntry(new_contents)) - RegisterForTabNotifications(new_contents); -} + ZoomController::FromWebContents(contents)->AddObserver(this); -void TabsEventRouter::TabPinnedStateChanged(TabStripModel* tab_strip_model, - WebContents* contents, - int index) { - std::set<std::string> changed_property_names; - changed_property_names.insert(tabs_constants::kPinnedKey); - DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); + int tab_id = ExtensionTabUtil::GetTabId(contents); + DCHECK(tab_entries_.find(tab_id) == tab_entries_.end()); + tab_entries_[tab_id] = std::make_unique<TabEntry>(this, contents); } -void TabsEventRouter::OnZoomChanged( - const ZoomController::ZoomChangedEventData& data) { - DCHECK(data.web_contents); - int tab_id = ExtensionTabUtil::GetTabId(data.web_contents); - if (tab_id < 0) - return; - - // Prepare the zoom change information. - api::tabs::OnZoomChange::ZoomChangeInfo zoom_change_info; - zoom_change_info.tab_id = tab_id; - zoom_change_info.old_zoom_factor = - content::ZoomLevelToZoomFactor(data.old_zoom_level); - zoom_change_info.new_zoom_factor = - content::ZoomLevelToZoomFactor(data.new_zoom_level); - ZoomModeToZoomSettings(data.zoom_mode, - &zoom_change_info.zoom_settings); +void TabsEventRouter::UnregisterForTabNotifications(WebContents* contents) { + favicon_scoped_observer_.Remove( + favicon::ContentFaviconDriver::FromWebContents(contents)); - // Dispatch the |onZoomChange| event. - Profile* profile = Profile::FromBrowserContext( - data.web_contents->GetBrowserContext()); - DispatchEvent(profile, events::TABS_ON_ZOOM_CHANGE, - api::tabs::OnZoomChange::kEventName, - api::tabs::OnZoomChange::Create(zoom_change_info), - EventRouter::USER_GESTURE_UNKNOWN); -} + ZoomController::FromWebContents(contents)->RemoveObserver(this); -void TabsEventRouter::OnFaviconUpdated( - favicon::FaviconDriver* favicon_driver, - NotificationIconType notification_icon_type, - const GURL& icon_url, - bool icon_url_changed, - const gfx::Image& image) { - if (notification_icon_type == NON_TOUCH_16_DIP && icon_url_changed) { - favicon::ContentFaviconDriver* content_favicon_driver = - static_cast<favicon::ContentFaviconDriver*>(favicon_driver); - FaviconUrlUpdated(content_favicon_driver->web_contents()); - } + int tab_id = ExtensionTabUtil::GetTabId(contents); + int removed_count = tab_entries_.erase(tab_id); + DCHECK_GT(removed_count, 0); } -void TabsEventRouter::OnDiscardedStateChange(WebContents* contents, - bool is_discarded) { - std::set<std::string> changed_property_names; - changed_property_names.insert(tabs_constants::kDiscardedKey); - DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); -} +TabsEventRouter::TabEntry* TabsEventRouter::GetTabEntry(WebContents* contents) { + const auto it = tab_entries_.find(ExtensionTabUtil::GetTabId(contents)); -void TabsEventRouter::OnAutoDiscardableStateChange(WebContents* contents, - bool is_auto_discardable) { - std::set<std::string> changed_property_names; - changed_property_names.insert(tabs_constants::kAutoDiscardableKey); - DispatchTabUpdatedEvent(contents, std::move(changed_property_names)); + return it == tab_entries_.end() ? nullptr : it->second.get(); } } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.h b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.h index d0b11d82c81..a7796a93f9d 100644 --- a/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.h +++ b/chromium/chrome/browser/extensions/api/tabs/tabs_event_router.h @@ -54,32 +54,14 @@ class TabsEventRouter : public TabStripModelObserver, void OnBrowserSetLastActive(Browser* browser) override; // TabStripModelObserver: - void TabInsertedAt(TabStripModel* tab_strip_model, - content::WebContents* contents, - int index, - bool active) override; - void TabClosingAt(TabStripModel* tab_strip_model, - content::WebContents* contents, - int index) override; - void TabDetachedAt(content::WebContents* contents, - int index, - bool was_active) override; - void ActiveTabChanged(content::WebContents* old_contents, - content::WebContents* new_contents, - int index, - int reason) override; - void TabSelectionChanged(TabStripModel* tab_strip_model, - const ui::ListSelectionModel& old_model) override; - void TabMoved(content::WebContents* contents, - int from_index, - int to_index) override; + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override; + void TabChangedAt(content::WebContents* contents, int index, TabChangeType change_type) override; - void TabReplacedAt(TabStripModel* tab_strip_model, - content::WebContents* old_contents, - content::WebContents* new_contents, - int index) override; void TabPinnedStateChanged(TabStripModel* tab_strip_model, content::WebContents* contents, int index) override; @@ -102,7 +84,32 @@ class TabsEventRouter : public TabStripModelObserver, bool is_auto_discardable) override; private: - // "Synthetic" event. Called from TabInsertedAt if new tab is detected. + // Methods called from OnTabStripModelChanged. + void DispatchTabInsertedAt(TabStripModel* tab_strip_model, + content::WebContents* contents, + int index, + bool active); + void DispatchTabClosingAt(TabStripModel* tab_strip_model, + content::WebContents* contents, + int index); + void DispatchTabDetachedAt(content::WebContents* contents, + int index, + bool was_active); + void DispatchActiveTabChanged(content::WebContents* old_contents, + content::WebContents* new_contents, + int index, + int reason); + void DispatchTabSelectionChanged(TabStripModel* tab_strip_model, + const ui::ListSelectionModel& old_model); + void DispatchTabMoved(content::WebContents* contents, + int from_index, + int to_index); + void DispatchTabReplacedAt(content::WebContents* old_contents, + content::WebContents* new_contents, + int index); + + // "Synthetic" event. Called from DispatchTabInsertedAt if new tab is + // detected. void TabCreatedAt(content::WebContents* contents, int index, bool active); // Internal processing of tab updated events. Intended to be called when @@ -123,12 +130,6 @@ class TabsEventRouter : public TabStripModelObserver, std::unique_ptr<base::ListValue> args, EventRouter::UserGestureState user_gesture); - void DispatchEventsAcrossIncognito( - Profile* profile, - const std::string& event_name, - std::unique_ptr<base::ListValue> event_args, - std::unique_ptr<base::ListValue> cross_incognito_args); - // Packages |changed_property_names| as a tab updated event for the tab // |contents| and dispatches the event to the extension. void DispatchTabUpdatedEvent( 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 6a0a795e8bb..b550c6a0e6e 100644 --- a/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc +++ b/chromium/chrome/browser/extensions/api/terminal/terminal_private_api.cc @@ -12,7 +12,7 @@ #include "base/command_line.h" #include "base/json/json_writer.h" #include "base/sys_info.h" -#include "base/task_scheduler/post_task.h" +#include "base/task/post_task.h" #include "base/values.h" #include "chrome/browser/extensions/api/terminal/terminal_extension_helper.h" #include "chrome/browser/extensions/extension_service.h" 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 7c5099fbe81..c546379bcd8 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 @@ -33,8 +33,6 @@ namespace GetFrame = extensions::api::web_navigation::GetFrame; namespace GetAllFrames = extensions::api::web_navigation::GetAllFrames; -DEFINE_WEB_CONTENTS_USER_DATA_KEY(extensions::WebNavigationTabObserver); - namespace extensions { namespace web_navigation = api::web_navigation; @@ -90,25 +88,32 @@ bool WebNavigationEventRouter::ShouldTrackBrowser(Browser* browser) { return profile_->IsSameProfile(browser->profile()); } -void WebNavigationEventRouter::TabReplacedAt( +void WebNavigationEventRouter::OnTabStripModelChanged( TabStripModel* tab_strip_model, - content::WebContents* old_contents, - content::WebContents* new_contents, - int index) { - WebNavigationTabObserver* tab_observer = - WebNavigationTabObserver::Get(old_contents); - if (!tab_observer) { - // If you hit this DCHECK(), please add reproduction steps to - // http://crbug.com/109464. - DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS); - return; - } - if (!FrameNavigationState::IsValidUrl(old_contents->GetURL()) || - !FrameNavigationState::IsValidUrl(new_contents->GetURL())) + const TabStripModelChange& change, + const TabStripSelectionChange& selection) { + if (change.type() != TabStripModelChange::kReplaced) return; - web_navigation_api_helpers::DispatchOnTabReplaced(old_contents, profile_, - new_contents); + for (const auto& delta : change.deltas()) { + content::WebContents* old_contents = delta.replace.old_contents; + content::WebContents* new_contents = delta.replace.new_contents; + + WebNavigationTabObserver* tab_observer = + WebNavigationTabObserver::Get(old_contents); + if (!tab_observer) { + // If you hit this DCHECK(), please add reproduction steps to + // http://crbug.com/109464. + DCHECK(GetViewType(old_contents) != VIEW_TYPE_TAB_CONTENTS); + continue; + } + if (!FrameNavigationState::IsValidUrl(old_contents->GetURL()) || + !FrameNavigationState::IsValidUrl(new_contents->GetURL())) + continue; + + web_navigation_api_helpers::DispatchOnTabReplaced(old_contents, profile_, + new_contents); + } } void WebNavigationEventRouter::Observe( @@ -388,10 +393,14 @@ void WebNavigationTabObserver::DidOpenRequestedURL( if (!router) return; - router->RecordNewWebContents(web_contents(), - source_render_frame_host->GetProcess()->GetID(), - source_render_frame_host->GetRoutingID(), url, - new_contents, renderer_initiated); + TabStripModel* ignored_tab_strip_model = nullptr; + int ignored_tab_index = -1; + bool new_contents_is_present_in_tabstrip = ExtensionTabUtil::GetTabStripModel( + new_contents, &ignored_tab_strip_model, &ignored_tab_index); + router->RecordNewWebContents( + web_contents(), source_render_frame_host->GetProcess()->GetID(), + source_render_frame_host->GetRoutingID(), url, new_contents, + !new_contents_is_present_in_tabstrip); } void WebNavigationTabObserver::WebContentsDestroyed() { diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.h b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.h index 1b4b6645194..30e90fc1077 100644 --- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.h +++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_api.h @@ -148,10 +148,10 @@ class WebNavigationEventRouter : public TabStripModelObserver, bool ShouldTrackBrowser(Browser* browser) override; // TabStripModelObserver implementation. - void TabReplacedAt(TabStripModel* tab_strip_model, - content::WebContents* old_contents, - content::WebContents* new_contents, - int index) override; + void OnTabStripModelChanged( + TabStripModel* tab_strip_model, + const TabStripModelChange& change, + const TabStripSelectionChange& selection) override; // content::NotificationObserver implementation. void Observe(int type, diff --git a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc index 45bae3a8d76..06d2a2591bf 100644 --- a/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc +++ b/chromium/chrome/browser/extensions/api/web_navigation/web_navigation_apitest.cc @@ -218,9 +218,16 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, Api) { ASSERT_TRUE(RunExtensionTest("webnavigation/api")) << message_; } -IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, GetFrame) { +// Flaky on Windows. See http://crbug.com/874782 +#if defined(OS_WIN) +#define MAYBE_GetFrame DISABLED_GetFrame +#else +#define MAYBE_GetFrame GetFrame +#endif +IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, MAYBE_GetFrame) { ASSERT_TRUE(RunExtensionTest("webnavigation/getFrame")) << message_; } +#undef MAYBE_GetFrame IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ClientRedirect) { ASSERT_TRUE(RunExtensionTest("webnavigation/clientRedirect")) @@ -246,9 +253,18 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, Download) { } IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, ServerRedirectSingleProcess) { - // TODO(lukasza): https://crbug.com/671734: Investigate why this test fails - // with --site-per-process. - if (content::AreAllSitesIsolatedForTesting()) + // TODO(lukasza): https://crbug.com/671734: In the long-term, //chrome-layer + // tests should only be run with site-per-process - remove the early return + // below when fixing https://crbug.com/870761 and removing the + // not_site_per_process_browser_tests step. + // + // This test has its expectations in + // serverRedirectSingleProcess/test_serverRedirectSingleProcess.js. The + // expectations include exact |processId| ("exact" meaning that one cannot use + // a wildcard - the verification is done via chrome.test.checkDeepEq). + // Inclusion of |processId| means that the expectation change in + // site-per-process mode. + if (!content::AreAllSitesIsolatedForTesting()) return; ASSERT_TRUE(StartEmbeddedTestServer()); @@ -346,7 +362,16 @@ IN_PROC_BROWSER_TEST_F(WebNavigationApiTest, MAYBE_UserAction) { GURL url = extension->GetResourceURL( "a.html?" + base::IntToString(embedded_test_server()->port())); + // Register an observer for the navigation in the subframe, so the test + // can wait until it is fully complete. Otherwise the context menu + // navigation is non-deterministic on which process it will get associated + // with, leading to test flakiness. + content::TestNavigationManager nav_manager( + tab, embedded_test_server()->GetURL( + "/extensions/api_test/webnavigation/userAction/subframe.html")); ui_test_utils::NavigateToURL(browser(), url); + nav_manager.WaitForNavigationFinished(); + EXPECT_TRUE(nav_manager.was_successful()); // This corresponds to "Open link in new tab". content::ContextMenuParams params; diff --git a/chromium/chrome/browser/extensions/api/web_request/chrome_extension_web_request_event_router_delegate.cc b/chromium/chrome/browser/extensions/api/web_request/chrome_extension_web_request_event_router_delegate.cc index 547d704e804..f9b9e4ac4cb 100644 --- a/chromium/chrome/browser/extensions/api/web_request/chrome_extension_web_request_event_router_delegate.cc +++ b/chromium/chrome/browser/extensions/api/web_request/chrome_extension_web_request_event_router_delegate.cc @@ -43,6 +43,21 @@ void NotifyWebRequestWithheldOnUI(int render_process_id, if (!extension) return; + // If the extension doesn't request access to the tab, return. The user + // invoking the extension on a site grants access to the tab's origin if + // and only if the extension requested it; without requesting the tab, + // clicking on the extension won't grant access to the resource. + // https://crbug.com/891586. + // TODO(https://157736): We can remove this if extensions require host + // permissions to the initiator, since then we'll never get into this type + // of circumstance (the request would be blocked, rather than withheld). + if (!extension->permissions_data() + ->withheld_permissions() + .explicit_hosts() + .MatchesURL(rfh->GetLastCommittedURL())) { + return; + } + runner->OnWebRequestBlocked(extension); } 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 ec0e9ea2710..566f1087400 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,7 +46,6 @@ #include "extensions/browser/api/web_request/web_request_api.h" #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/warning_set.h" #include "extensions/common/api/web_request.h" #include "extensions/common/extension_messages.h" #include "extensions/common/features/feature.h" @@ -116,12 +115,14 @@ static void EventHandledOnIOThread( } // Returns whether |warnings| contains an extension for |extension_id|. -bool HasWarning(const WarningSet& warnings, - const std::string& extension_id) { - for (WarningSet::const_iterator i = warnings.begin(); - i != warnings.end(); ++i) { - if (i->extension_id() == extension_id) +bool HasIgnoredAction(const helpers::IgnoredActions& ignored_actions, + const std::string& extension_id, + web_request::IgnoredActionType action_type) { + for (const auto& ignored_action : ignored_actions) { + if (ignored_action.extension_id == extension_id && + ignored_action.action_type == action_type) { return true; + } } return false; } @@ -228,8 +229,6 @@ class ExtensionWebRequestTest : public testing::Test { protected: void SetUp() override { ASSERT_TRUE(profile_manager_.SetUp()); - ChromeNetworkDelegate::InitializePrefsOnUIThread( - nullptr, nullptr, nullptr, profile_.GetTestingPrefService()); network_delegate_.reset(new ChromeNetworkDelegate(event_router_.get())); network_delegate_->set_profile(&profile_); network_delegate_->set_cookie_settings( @@ -1121,8 +1120,6 @@ class ExtensionWebRequestHeaderModificationTest protected: void SetUp() override { ASSERT_TRUE(profile_manager_.SetUp()); - ChromeNetworkDelegate::InitializePrefsOnUIThread( - nullptr, nullptr, nullptr, profile_.GetTestingPrefService()); network_delegate_.reset(new ChromeNetworkDelegate(event_router_.get())); network_delegate_->set_profile(&profile_); network_delegate_->set_cookie_settings( @@ -1543,14 +1540,14 @@ TEST(ExtensionWebRequestHelpersTest, TestCalculateOnBeforeSendHeadersDelta) { const bool cancel = true; std::string value; net::HttpRequestHeaders old_headers; - old_headers.AddHeadersFromString("key1: value1\r\n" - "key2: value2\r\n"); + old_headers.SetHeader("key1", "value1"); + old_headers.SetHeader("key2", "value2"); // Test adding a header. net::HttpRequestHeaders new_headers_added; - new_headers_added.AddHeadersFromString("key1: value1\r\n" - "key3: value3\r\n" - "key2: value2\r\n"); + new_headers_added.SetHeader("key1", "value1"); + new_headers_added.SetHeader("key3", "value3"); + new_headers_added.SetHeader("key2", "value2"); std::unique_ptr<EventResponseDelta> delta_added( CalculateOnBeforeSendHeadersDelta("extid", base::Time::Now(), cancel, &old_headers, &new_headers_added)); @@ -1561,7 +1558,7 @@ TEST(ExtensionWebRequestHelpersTest, TestCalculateOnBeforeSendHeadersDelta) { // Test deleting a header. net::HttpRequestHeaders new_headers_deleted; - new_headers_deleted.AddHeadersFromString("key1: value1\r\n"); + new_headers_deleted.SetHeader("key1", "value1"); std::unique_ptr<EventResponseDelta> delta_deleted( CalculateOnBeforeSendHeadersDelta("extid", base::Time::Now(), cancel, &old_headers, &new_headers_deleted)); @@ -1571,8 +1568,8 @@ TEST(ExtensionWebRequestHelpersTest, TestCalculateOnBeforeSendHeadersDelta) { // Test modifying a header. net::HttpRequestHeaders new_headers_modified; - new_headers_modified.AddHeadersFromString("key1: value1\r\n" - "key2: value3\r\n"); + new_headers_modified.SetHeader("key1", "value1"); + new_headers_modified.SetHeader("key2", "value3"); std::unique_ptr<EventResponseDelta> delta_modified( CalculateOnBeforeSendHeadersDelta("extid", base::Time::Now(), cancel, &old_headers, &new_headers_modified)); @@ -1586,9 +1583,9 @@ TEST(ExtensionWebRequestHelpersTest, TestCalculateOnBeforeSendHeadersDelta) { // value) pair with a key that existed before. This is incorrect // usage of the API that shall be handled gracefully. net::HttpRequestHeaders new_headers_modified2; - new_headers_modified2.AddHeadersFromString("key1: value1\r\n" - "key2: value2\r\n" - "key2: value3\r\n"); + new_headers_modified2.SetHeader("key1", "value1"); + new_headers_modified2.SetHeader("key2", "value2"); + new_headers_modified2.SetHeader("key2", "value3"); std::unique_ptr<EventResponseDelta> delta_modified2( CalculateOnBeforeSendHeadersDelta("extid", base::Time::Now(), cancel, &old_headers, &new_headers_modified)); @@ -1712,7 +1709,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeCancelOfResponses) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { EventResponseDeltas deltas; TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; GURL effective_new_url; // No redirect @@ -1720,7 +1717,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { new EventResponseDelta("extid0", base::Time::FromInternalValue(0))); deltas.push_back(d0); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_TRUE(effective_new_url.is_empty()); // Single redirect. @@ -1732,9 +1729,9 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { deltas.sort(&InDecreasingExtensionInstallationTimeOrder); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_TRUE(warning_set.empty()); + EXPECT_TRUE(ignored_actions.empty()); EXPECT_EQ(1u, logger.log_size()); // Ignored redirect (due to precedence). @@ -1744,13 +1741,14 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { d2->new_url = GURL(new_url_2); deltas.push_back(d2); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); EXPECT_EQ(2u, logger.log_size()); // Overriding redirect. @@ -1760,14 +1758,16 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { d3->new_url = GURL(new_url_3); deltas.push_back(d3); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_3, effective_new_url); - EXPECT_EQ(2u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid1")); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(2u, ignored_actions.size()); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid1", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); EXPECT_EQ(3u, logger.log_size()); // Check that identical redirects don't cause a conflict. @@ -1776,14 +1776,16 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { d4->new_url = GURL(new_url_3); deltas.push_back(d4); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_3, effective_new_url); - EXPECT_EQ(2u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid1")); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(2u, ignored_actions.size()); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid1", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); EXPECT_EQ(4u, logger.log_size()); } @@ -1792,7 +1794,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { EventResponseDeltas deltas; TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; GURL effective_new_url; // Single redirect. @@ -1802,7 +1804,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { d0->new_url = GURL(new_url_0); deltas.push_back(d0); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_0, effective_new_url); // Cancel request by redirecting to a data:// URL. This shall override @@ -1813,12 +1815,12 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { d1->new_url = GURL(new_url_1); deltas.push_back(d1); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_TRUE(warning_set.empty()); + EXPECT_TRUE(ignored_actions.empty()); EXPECT_EQ(1u, logger.log_size()); // Cancel request by redirecting to the same data:// URL. This shall @@ -1829,13 +1831,13 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { d2->new_url = GURL(new_url_2); deltas.push_back(d2); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_TRUE(warning_set.empty()); + EXPECT_TRUE(ignored_actions.empty()); EXPECT_EQ(2u, logger.log_size()); // Cancel redirect by redirecting to a different data:// URL. This needs @@ -1846,13 +1848,14 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { d3->new_url = GURL(new_url_3); deltas.push_back(d3); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid3")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE(HasIgnoredAction(ignored_actions, "extid3", + web_request::IGNORED_ACTION_TYPE_REDIRECT)); EXPECT_EQ(3u, logger.log_size()); } @@ -1861,7 +1864,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses2) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses3) { EventResponseDeltas deltas; TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; GURL effective_new_url; // Single redirect. @@ -1871,7 +1874,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses3) { d0->new_url = GURL(new_url_0); deltas.push_back(d0); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_0, effective_new_url); // Cancel request by redirecting to about:blank. This shall override @@ -1882,12 +1885,12 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses3) { d1->new_url = GURL(new_url_1); deltas.push_back(d1); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); MergeOnBeforeRequestResponses(GURL(kExampleUrl), deltas, &effective_new_url, - &warning_set, &logger); + &ignored_actions, &logger); EXPECT_EQ(new_url_1, effective_new_url); - EXPECT_TRUE(warning_set.empty()); + EXPECT_TRUE(ignored_actions.empty()); EXPECT_EQ(1u, logger.log_size()); } @@ -1895,7 +1898,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses3) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses4) { EventResponseDeltas deltas; TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; GURL effective_new_url; // Single redirect. @@ -1904,7 +1907,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeRequestResponses4) { delta->new_url = GURL("http://foo.com"); deltas.push_back(delta); MergeOnBeforeRequestResponses(GURL("ws://example.com"), deltas, - &effective_new_url, &warning_set, &logger); + &effective_new_url, &ignored_actions, &logger); EXPECT_EQ(GURL(), effective_new_url); } @@ -1913,7 +1916,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { base_headers.SetHeader("key1", "value 1"); base_headers.SetHeader("key2", "value 2"); TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; std::string header_value; EventResponseDeltas deltas; @@ -1924,13 +1927,13 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { bool request_headers_modified0; net::HttpRequestHeaders headers0; headers0.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers0, &warning_set, + MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers0, &ignored_actions, &logger, &request_headers_modified0); ASSERT_TRUE(headers0.GetHeader("key1", &header_value)); EXPECT_EQ("value 1", header_value); ASSERT_TRUE(headers0.GetHeader("key2", &header_value)); EXPECT_EQ("value 2", header_value); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(0u, logger.log_size()); EXPECT_FALSE(request_headers_modified0); @@ -1942,19 +1945,19 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { d1->modified_request_headers.SetHeader("key3", "value 3"); deltas.push_back(d1); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); bool request_headers_modified1; net::HttpRequestHeaders headers1; headers1.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &warning_set, + MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &ignored_actions, &logger, &request_headers_modified1); EXPECT_FALSE(headers1.HasHeader("key1")); ASSERT_TRUE(headers1.GetHeader("key2", &header_value)); EXPECT_EQ("value 3", header_value); ASSERT_TRUE(headers1.GetHeader("key3", &header_value)); EXPECT_EQ("value 3", header_value); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(1u, logger.log_size()); EXPECT_TRUE(request_headers_modified1); @@ -1967,12 +1970,12 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { d2->modified_request_headers.SetHeader("key4", "value 4"); deltas.push_back(d2); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); bool request_headers_modified2; net::HttpRequestHeaders headers2; headers2.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers2, &warning_set, + MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers2, &ignored_actions, &logger, &request_headers_modified2); EXPECT_FALSE(headers2.HasHeader("key1")); ASSERT_TRUE(headers2.GetHeader("key2", &header_value)); @@ -1980,8 +1983,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { ASSERT_TRUE(headers2.GetHeader("key3", &header_value)); EXPECT_EQ("value 3", header_value); EXPECT_FALSE(headers2.HasHeader("key4")); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); EXPECT_EQ(2u, logger.log_size()); EXPECT_TRUE(request_headers_modified2); @@ -1994,12 +1999,12 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { d3->modified_request_headers.SetHeader("key5", "value 5"); deltas.push_back(d3); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); bool request_headers_modified3; net::HttpRequestHeaders headers3; headers3.MergeFrom(base_headers); - MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers3, &warning_set, + MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers3, &ignored_actions, &logger, &request_headers_modified3); EXPECT_FALSE(headers3.HasHeader("key1")); ASSERT_TRUE(headers3.GetHeader("key2", &header_value)); @@ -2008,8 +2013,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { EXPECT_EQ("value 3", header_value); ASSERT_TRUE(headers3.GetHeader("key5", &header_value)); EXPECT_EQ("value 5", header_value); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_REQUEST_HEADERS)); EXPECT_EQ(3u, logger.log_size()); EXPECT_TRUE(request_headers_modified3); } @@ -2017,10 +2024,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnBeforeSendHeadersResponses_Cookies) { net::HttpRequestHeaders base_headers; - base_headers.AddHeaderFromString( - "Cookie: name=value; name2=value2; name3=\"value3\""); + base_headers.SetHeader("Cookie", + "name=value; name2=value2; name3=\"value3\""); TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; std::string header_value; EventResponseDeltas deltas; @@ -2066,13 +2073,13 @@ TEST(ExtensionWebRequestHelpersTest, bool request_headers_modified1; net::HttpRequestHeaders headers1; headers1.MergeFrom(base_headers); - warning_set.clear(); - MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &warning_set, + ignored_actions.clear(); + MergeOnBeforeSendHeadersResponses(GURL(), deltas, &headers1, &ignored_actions, &logger, &request_headers_modified1); 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); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(0u, logger.log_size()); EXPECT_FALSE(request_headers_modified1); } @@ -2106,7 +2113,6 @@ std::string GetCookieExpirationDate(int delta_secs) { TEST(ExtensionWebRequestHelpersTest, TestMergeCookiesInOnHeadersReceivedResponses) { TestLogger logger; - WarningSet warning_set; std::string header_value; EventResponseDeltas deltas; @@ -2140,10 +2146,9 @@ TEST(ExtensionWebRequestHelpersTest, new EventResponseDelta("extid0", base::Time::FromInternalValue(3000))); deltas.push_back(d0); scoped_refptr<net::HttpResponseHeaders> new_headers0; - MergeCookiesInOnHeadersReceivedResponses( - GURL(), deltas, base_headers.get(), &new_headers0, &warning_set, &logger); + MergeCookiesInOnHeadersReceivedResponses(GURL(), deltas, base_headers.get(), + &new_headers0, &logger); EXPECT_FALSE(new_headers0.get()); - EXPECT_EQ(0u, warning_set.size()); EXPECT_EQ(0u, logger.log_size()); linked_ptr<ResponseCookieModification> add_cookie = @@ -2308,9 +2313,8 @@ TEST(ExtensionWebRequestHelpersTest, net::HttpUtil::AssembleRawHeaders( base_headers_string.c_str(), base_headers_string.size()))); scoped_refptr<net::HttpResponseHeaders> new_headers1; - warning_set.clear(); - MergeCookiesInOnHeadersReceivedResponses( - GURL(), deltas, headers1.get(), &new_headers1, &warning_set, &logger); + MergeCookiesInOnHeadersReceivedResponses(GURL(), deltas, headers1.get(), + &new_headers1, &logger); EXPECT_TRUE(new_headers1->HasHeader("Foo")); size_t iter = 0; @@ -2334,13 +2338,12 @@ TEST(ExtensionWebRequestHelpersTest, while (new_headers1->EnumerateHeader(&iter, "Set-Cookie", &cookie_string)) actual_cookies.insert(cookie_string); EXPECT_EQ(expected_cookies, actual_cookies); - EXPECT_EQ(0u, warning_set.size()); EXPECT_EQ(0u, logger.log_size()); } TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; std::string header_value; EventResponseDeltas deltas; @@ -2363,11 +2366,11 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { GURL allowed_unsafe_redirect_url0; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers0, &allowed_unsafe_redirect_url0, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified0); EXPECT_FALSE(new_headers0.get()); EXPECT_TRUE(allowed_unsafe_redirect_url0.is_empty()); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(0u, logger.log_size()); EXPECT_FALSE(response_headers_modified0); @@ -2378,14 +2381,14 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { d1->added_response_headers.push_back(ResponseHeader("Key2", "Value3")); deltas.push_back(d1); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); bool response_headers_modified1; scoped_refptr<net::HttpResponseHeaders> new_headers1; GURL allowed_unsafe_redirect_url1; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers1, &allowed_unsafe_redirect_url1, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified1); ASSERT_TRUE(new_headers1.get()); EXPECT_TRUE(allowed_unsafe_redirect_url1.is_empty()); @@ -2399,7 +2402,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { actual1.insert(std::pair<std::string, std::string>(name, value)); } EXPECT_EQ(expected1, actual1); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(1u, logger.log_size()); EXPECT_TRUE(response_headers_modified1); @@ -2412,14 +2415,14 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { d2->added_response_headers.push_back(ResponseHeader("Key2", "Value4")); deltas.push_back(d2); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); bool response_headers_modified2; scoped_refptr<net::HttpResponseHeaders> new_headers2; GURL allowed_unsafe_redirect_url2; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers2, &allowed_unsafe_redirect_url2, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified2); ASSERT_TRUE(new_headers2.get()); EXPECT_TRUE(allowed_unsafe_redirect_url2.is_empty()); @@ -2429,8 +2432,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { actual2.insert(std::pair<std::string, std::string>(name, value)); } EXPECT_EQ(expected1, actual2); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_RESPONSE_HEADERS)); EXPECT_EQ(2u, logger.log_size()); EXPECT_TRUE(response_headers_modified2); } @@ -2439,7 +2444,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponses) { TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponsesDeletion) { TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; std::string header_value; EventResponseDeltas deltas; @@ -2464,7 +2469,7 @@ TEST(ExtensionWebRequestHelpersTest, GURL allowed_unsafe_redirect_url1; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers1, &allowed_unsafe_redirect_url1, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified1); ASSERT_TRUE(new_headers1.get()); EXPECT_TRUE(allowed_unsafe_redirect_url1.is_empty()); @@ -2480,7 +2485,7 @@ TEST(ExtensionWebRequestHelpersTest, actual1.insert(std::pair<std::string, std::string>(name, value)); } EXPECT_EQ(expected1, actual1); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(1u, logger.log_size()); EXPECT_TRUE(response_headers_modified1); } @@ -2492,7 +2497,7 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnHeadersReceivedResponsesRedirect) { EventResponseDeltas deltas; TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; char base_headers_string[] = "HTTP/1.0 200 OK\r\n" @@ -2510,11 +2515,11 @@ TEST(ExtensionWebRequestHelpersTest, GURL allowed_unsafe_redirect_url0; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers0, &allowed_unsafe_redirect_url0, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified0); EXPECT_FALSE(new_headers0.get()); EXPECT_TRUE(allowed_unsafe_redirect_url0.is_empty()); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(0u, logger.log_size()); EXPECT_FALSE(response_headers_modified0); @@ -2532,20 +2537,20 @@ TEST(ExtensionWebRequestHelpersTest, GURL allowed_unsafe_redirect_url1; MergeOnHeadersReceivedResponses(GURL(kExampleUrl), deltas, base_headers.get(), &new_headers1, &allowed_unsafe_redirect_url1, - &warning_set, &logger, + &ignored_actions, &logger, &response_headers_modified1); EXPECT_TRUE(new_headers1.get()); EXPECT_TRUE(new_headers1->HasHeaderValue("Location", new_url_1.spec())); EXPECT_EQ(new_url_1, allowed_unsafe_redirect_url1); - EXPECT_TRUE(warning_set.empty()); + EXPECT_TRUE(ignored_actions.empty()); EXPECT_EQ(1u, logger.log_size()); EXPECT_FALSE(response_headers_modified1); } TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { TestLogger logger; - WarningSet warning_set; + helpers::IgnoredActions ignored_actions; EventResponseDeltas deltas; base::string16 username = base::ASCIIToUTF16("foo"); base::string16 password = base::ASCIIToUTF16("bar"); @@ -2557,10 +2562,10 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { deltas.push_back(d0); net::AuthCredentials auth0; bool credentials_set = - MergeOnAuthRequiredResponses(deltas, &auth0, &warning_set, &logger); + MergeOnAuthRequiredResponses(deltas, &auth0, &ignored_actions, &logger); EXPECT_FALSE(credentials_set); EXPECT_TRUE(auth0.Empty()); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(0u, logger.log_size()); // Check that we can set AuthCredentials. @@ -2569,16 +2574,16 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { d1->auth_credentials.reset(new net::AuthCredentials(username, password)); deltas.push_back(d1); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); net::AuthCredentials auth1; credentials_set = - MergeOnAuthRequiredResponses(deltas, &auth1, &warning_set, &logger); + MergeOnAuthRequiredResponses(deltas, &auth1, &ignored_actions, &logger); EXPECT_TRUE(credentials_set); EXPECT_FALSE(auth1.Empty()); EXPECT_EQ(username, auth1.username()); EXPECT_EQ(password, auth1.password()); - EXPECT_EQ(0u, warning_set.size()); + EXPECT_EQ(0u, ignored_actions.size()); EXPECT_EQ(1u, logger.log_size()); // Check that we set AuthCredentials only once. @@ -2587,17 +2592,19 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { d2->auth_credentials.reset(new net::AuthCredentials(username, password2)); deltas.push_back(d2); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); net::AuthCredentials auth2; credentials_set = - MergeOnAuthRequiredResponses(deltas, &auth2, &warning_set, &logger); + MergeOnAuthRequiredResponses(deltas, &auth2, &ignored_actions, &logger); EXPECT_TRUE(credentials_set); EXPECT_FALSE(auth2.Empty()); EXPECT_EQ(username, auth1.username()); EXPECT_EQ(password, auth1.password()); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_AUTH_CREDENTIALS)); EXPECT_EQ(2u, logger.log_size()); // Check that we can set identical AuthCredentials twice without causing @@ -2607,17 +2614,19 @@ TEST(ExtensionWebRequestHelpersTest, TestMergeOnAuthRequiredResponses) { d3->auth_credentials.reset(new net::AuthCredentials(username, password)); deltas.push_back(d3); deltas.sort(&InDecreasingExtensionInstallationTimeOrder); - warning_set.clear(); + ignored_actions.clear(); logger.clear(); net::AuthCredentials auth3; credentials_set = - MergeOnAuthRequiredResponses(deltas, &auth3, &warning_set, &logger); + MergeOnAuthRequiredResponses(deltas, &auth3, &ignored_actions, &logger); EXPECT_TRUE(credentials_set); EXPECT_FALSE(auth3.Empty()); EXPECT_EQ(username, auth1.username()); EXPECT_EQ(password, auth1.password()); - EXPECT_EQ(1u, warning_set.size()); - EXPECT_TRUE(HasWarning(warning_set, "extid2")); + EXPECT_EQ(1u, ignored_actions.size()); + EXPECT_TRUE( + HasIgnoredAction(ignored_actions, "extid2", + web_request::IGNORED_ACTION_TYPE_AUTH_CREDENTIALS)); EXPECT_EQ(3u, logger.log_size()); } 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 3b61cddcfb3..34dab4c0c64 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 @@ -15,12 +15,15 @@ #include "base/synchronization/lock.h" #include "base/test/bind_test_util.h" #include "base/test/scoped_feature_list.h" +#include "base/time/time.h" +#include "base/time/time_override.h" #include "base/values.h" #include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/devtools/url_constants.h" #include "chrome/browser/extensions/active_tab_permission_granter.h" +#include "chrome/browser/extensions/api/extension_action/test_extension_action_api_observer.h" #include "chrome/browser/extensions/extension_action_runner.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_service.h" @@ -31,6 +34,8 @@ #include "chrome/browser/net/profile_network_context_service_factory.h" #include "chrome/browser/net/system_network_context_manager.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/profiles/profile_destroyer.h" +#include "chrome/browser/profiles/profile_manager.h" #include "chrome/browser/search/one_google_bar/one_google_bar_loader.h" #include "chrome/browser/search/one_google_bar/one_google_bar_service.h" #include "chrome/browser/search/one_google_bar/one_google_bar_service_factory.h" @@ -47,7 +52,7 @@ #include "chrome/test/base/search_test_utils.h" #include "chrome/test/base/ui_test_utils.h" #include "chromeos/login/scoped_test_public_session_login_state.h" -#include "components/google/core/browser/google_switches.h" +#include "components/google/core/common/google_switches.h" #include "components/prefs/pref_service.h" #include "components/proxy_config/proxy_config_dictionary.h" #include "components/proxy_config/proxy_config_pref_names.h" @@ -58,6 +63,7 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_frame_host.h" +#include "content/public/browser/render_process_host.h" #include "content/public/browser/render_view_host.h" #include "content/public/browser/render_widget_host.h" #include "content/public/browser/storage_partition.h" @@ -68,6 +74,7 @@ #include "content/public/test/url_loader_interceptor.h" #include "extensions/browser/api/web_request/web_request_api.h" #include "extensions/browser/blocked_action_type.h" +#include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_system.h" #include "extensions/common/extension_builder.h" #include "extensions/common/extension_features.h" @@ -89,6 +96,7 @@ #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/resource_request.h" #include "services/network/public/cpp/simple_url_loader.h" +#include "services/network/test/test_url_loader_client.h" #include "third_party/blink/public/platform/web_input_event.h" #if defined(OS_CHROMEOS) @@ -268,16 +276,36 @@ class ExtensionWebRequestApiTest : public ExtensionApiTest { const char* expected_content_regular_window, const char* exptected_content_incognito_window); - // TODO(https://crbug.com/857577): remove this hack. When an unrelated - // browser issued request (typically from GaiaAuthFetcher) has run, it causes - // the StoragePartitionImpl to create and cache a URLLoaderFactory without the - // web request proxying. This resets it so one with the web request proxying - // is created the next time a request is made. - void ResetStoragePartitionURLLoaderFactory() { - base::RunLoop().RunUntilIdle(); + network::mojom::URLLoaderFactoryPtr CreateURLLoaderFactory() { + network::mojom::URLLoaderFactoryParamsPtr params = + network::mojom::URLLoaderFactoryParams::New(); + params->process_id = network::mojom::kBrowserProcessId; + params->is_corb_enabled = false; + network::mojom::URLLoaderFactoryPtr loader_factory; content::BrowserContext::GetDefaultStoragePartition(profile()) - ->ResetURLLoaderFactoryForBrowserProcessForTesting(); + ->GetNetworkContext() + ->CreateURLLoaderFactory(mojo::MakeRequest(&loader_factory), + std::move(params)); + return loader_factory; } + + void InstallWebRequestExtension(const std::string& name) { + constexpr char kManifest[] = R"({ + "name": "%s", + "version": "1", + "manifest_version": 2, + "permissions": [ + "webRequest" + ] + })"; + auto dir = std::make_unique<TestExtensionDir>(); + dir->WriteManifest(base::StringPrintf(kManifest, name.c_str())); + LoadExtension(dir->UnpackedPath()); + test_dirs_.push_back(std::move(dir)); + } + + private: + std::vector<std::unique_ptr<TestExtensionDir>> test_dirs_; }; class DevToolsFrontendInWebRequestApiTest : public ExtensionApiTest { @@ -493,8 +521,53 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, #endif IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_WebRequestBlocking) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_blocking.html")) << - message_; + ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_blocking.html")) + << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebRequestBlockingSetCookieHeader) { + ASSERT_TRUE(StartEmbeddedTestServer()); + ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_blocking_cookie.html")) + << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestRedirects) { + ASSERT_TRUE(StartEmbeddedTestServer()); + ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_redirects.html")) + << message_; +} + +// Tests that redirects from secure to insecure don't send the referrer header. +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebRequestRedirectsToInsecure) { + ASSERT_TRUE(StartEmbeddedTestServer()); + GURL insecure_destination = + embedded_test_server()->GetURL("/extensions/test_file.html"); + net::EmbeddedTestServer https_test_server( + net::EmbeddedTestServer::TYPE_HTTPS); + https_test_server.ServeFilesFromDirectory(test_data_dir_); + ASSERT_TRUE(https_test_server.Start()); + + GURL url = https_test_server.GetURL("/webrequest/simulate_click.html"); + + base::ListValue custom_args; + custom_args.AppendString(url.spec()); + custom_args.AppendString(insecure_destination.spec()); + + std::string config_string; + base::JSONWriter::Write(custom_args, &config_string); + ASSERT_TRUE(RunExtensionSubtestWithArg( + "webrequest", "test_redirects_from_secure.html", config_string.c_str())) + << message_; +} + +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebRequestSubresourceRedirects) { + ASSERT_TRUE(StartEmbeddedTestServer()); + ASSERT_TRUE( + RunExtensionSubtest("webrequest", "test_subresource_redirects.html")) + << message_; } // Fails often on Windows dbg bots. http://crbug.com/177163 @@ -555,7 +628,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MAYBE_WebRequestDeclarative2) { ASSERT_TRUE(StartEmbeddedTestServer()); - ASSERT_TRUE(RunExtensionSubtest("webrequest", "test_declarative2.html")) + const char* network_service_arg = + base::FeatureList::IsEnabled(network::features::kNetworkService) + ? "NetworkServiceEnabled" + : "NetworkServiceDisabled"; + ASSERT_TRUE(RunExtensionSubtestWithArg("webrequest", "test_declarative2.html", + network_service_arg)) << message_; } @@ -897,14 +975,22 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, // frames, this shouldn't show up as a blocked action. EXPECT_EQ(BLOCKED_ACTION_NONE, runner->GetBlockedActions(extension)); - // If we revoke the extension's tab permissions, it should no longer receive - // webRequest events. + // Revoke the extension's tab permissions. ActiveTabPermissionGranter* granter = TabHelper::FromWebContents(web_contents)->active_tab_permission_granter(); ASSERT_TRUE(granter); granter->RevokeForTesting(); base::RunLoop().RunUntilIdle(); + + // The extension should no longer receive webRequest events since they are + // withheld. The extension icon should get updated to show the wants-to-run + // badge UI. + TestExtensionActionAPIObserver action_updated_waiter(profile(), + extension->id()); PerformXhrInFrame(main_frame, kHost, port, kXhrPath); + action_updated_waiter.Wait(); + EXPECT_EQ(web_contents, action_updated_waiter.last_web_contents()); + EXPECT_EQ(xhr_count, GetWebRequestCountFromBackgroundPage(extension, profile())); EXPECT_EQ(BLOCKED_ACTION_WEB_REQUEST, runner->GetBlockedActions(extension)); @@ -963,6 +1049,75 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, HasSeenWebRequestInBackgroundPage(extension, profile(), kCrossSiteHost)); } +// Tests behavior when an extension has withheld access to a request's URL, but +// not the initiator's (tab's) URL. Regression test for +// https://crbug.com/891586. +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WithheldHostPermissionsForCrossOriginWithoutInitiator) { + base::test::ScopedFeatureList scoped_feature_list; + scoped_feature_list.InitAndEnableFeature( + extensions::features::kRuntimeHostPermissions); + + content::SetupCrossSiteRedirector(embedded_test_server()); + ASSERT_TRUE(embedded_test_server()->Start()); + + // TODO(devlin): This is essentially copied from the webrequest_activetab + // API test extension, but has different permissions. Maybe it's worth having + // all tests use a common pattern? + TestExtensionDir test_dir; + test_dir.WriteManifest( + R"({ + "name": "Web Request Withheld Hosts", + "manifest_version": 2, + "version": "0.1", + "background": { "scripts": ["background.js"] }, + "permissions": ["*://b.com:*/*", "webRequest"] + })"); + test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), + R"(window.webRequestCount = 0; + window.requestedHostnames = []; + + chrome.webRequest.onBeforeRequest.addListener(function(details) { + ++window.webRequestCount; + window.requestedHostnames.push((new URL(details.url)).hostname); + }, {urls:['<all_urls>']}); + chrome.test.sendMessage('ready');)"); + + // Load an extension that registers a listener for webRequest events, and + // wait until it's initialized. + ExtensionTestMessageListener listener("ready", false); + const Extension* extension = LoadExtension(test_dir.UnpackedPath()); + ASSERT_TRUE(extension) << message_; + ScriptingPermissionsModifier(profile(), base::WrapRefCounted(extension)) + .SetWithholdHostPermissions(true); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + + // Navigate to example.com, which has a cross-site script to b.com. + ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "example.com", "/extensions/cross_site_script.html")); + + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + + ExtensionActionRunner* runner = + ExtensionActionRunner::GetForWebContents(web_contents); + ASSERT_TRUE(runner); + + // Even though the extension has access to b.com, it shouldn't show that it + // wants to run, because example.com is not a requested host. + EXPECT_EQ(BLOCKED_ACTION_NONE, runner->GetBlockedActions(extension)); + EXPECT_FALSE( + HasSeenWebRequestInBackgroundPage(extension, profile(), "b.com")); + + // Navigating to b.com (so that the script is hosted on the same origin as + // the WebContents) should show the extension wants to run. + ui_test_utils::NavigateToURL( + browser(), embedded_test_server()->GetURL( + "b.com", "/extensions/cross_site_script.html")); + EXPECT_EQ(BLOCKED_ACTION_WEB_REQUEST, runner->GetBlockedActions(extension)); +} + // Verify that requests to clientsX.google.com are protected properly. // First test requests from a standard renderer and then a request from the // browser process. @@ -1039,9 +1194,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, EXPECT_EQ(200, loader->ResponseInfo()->headers->response_code()); }; - // TODO(https://crbug.com/857577): remove this call. - ResetStoragePartitionURLLoaderFactory(); - // Now perform a request to "client1.google.com" from the browser process. // This should *not* be visible to the WebRequest API. We should still have // only seen the single render-initiated request from the first half of the @@ -1075,7 +1227,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, // affecting requests. PrefService* pref_service = browser()->profile()->GetPrefs(); pref_service->Set(proxy_config::prefs::kProxy, - *ProxyConfigDictionary::CreatePacScript( + ProxyConfigDictionary::CreatePacScript( embedded_test_server()->GetURL("/self.pac").spec(), true /* pac_mandatory */)); // Flush the proxy configuration change over the Mojo pipe to avoid any races. @@ -1238,6 +1390,33 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebSocketRequestOnWorker) { << message_; } +// Tests the WebRequestProxyingWebSocket does not crash when there is a +// connection error before AddChannelRequest is called. Regression test for +// http://crbug.com/878574. +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebSocketConnectionErrorBeforeChannelRequest) { + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) + return; + + InstallWebRequestExtension("extension"); + + network::mojom::WebSocketPtr web_socket; + network::mojom::WebSocketRequest request = mojo::MakeRequest(&web_socket); + network::mojom::AuthenticationHandlerPtr auth_handler; + content::RenderFrameHost* host = + browser()->tab_strip_model()->GetActiveWebContents()->GetMainFrame(); + extensions::BrowserContextKeyedAPIFactory<extensions::WebRequestAPI>::Get( + profile()) + ->MaybeProxyWebSocket(host, &request, &auth_handler); + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->GetNetworkContext() + ->CreateWebSocket(std::move(request), network::mojom::kBrowserProcessId, + host->GetProcess()->GetID(), + url::Origin::Create(GURL("http://example.com")), + std::move(auth_handler)); + web_socket.reset(); +} + // Test behavior when intercepting requests from a browser-initiated url fetch. IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, WebRequestURLLoaderInterception) { @@ -1362,9 +1541,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, } }; - // TODO(https://crbug.com/857577): remove this call. - ResetStoragePartitionURLLoaderFactory(); - // Next, try a series of requests through URLRequestFetchers (rather than a // renderer). auto* url_loader_factory = @@ -1452,6 +1628,80 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, MinimumAccessInitiator) { } } +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebRequestApiClearsBindingOnFirstListener) { + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) + return; + + auto loader_factory = CreateURLLoaderFactory(); + bool has_connection_error = false; + loader_factory.set_connection_error_handler( + base::BindLambdaForTesting([&]() { has_connection_error = true; })); + + InstallWebRequestExtension("extension1"); + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); + EXPECT_TRUE(has_connection_error); + + // The second time there should be no connection error. + loader_factory = CreateURLLoaderFactory(); + has_connection_error = false; + loader_factory.set_connection_error_handler( + base::BindLambdaForTesting([&]() { has_connection_error = true; })); + InstallWebRequestExtension("extension2"); + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); + EXPECT_FALSE(has_connection_error); +} + +// Regression test for http://crbug.com/878366. +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, + WebRequestApiDoesNotCrashOnErrorAfterProfileDestroyed) { + if (!base::FeatureList::IsEnabled(network::features::kNetworkService)) + return; + + ASSERT_TRUE(StartEmbeddedTestServer()); + + // Create a profile that will be destroyed later. + base::ScopedAllowBlockingForTesting allow_blocking; + ProfileManager* profile_manager = g_browser_process->profile_manager(); + Profile* tmp_profile = Profile::CreateProfile( + profile_manager->user_data_dir().AppendASCII("profile"), nullptr, + Profile::CreateMode::CREATE_MODE_SYNCHRONOUS); + + // Create a WebRequestAPI instance that we can control the lifetime of. + auto api = std::make_unique<WebRequestAPI>(tmp_profile); + // Make sure we are proxying for |tmp_profile|. + api->ForceProxyForTesting(); + content::BrowserContext::GetDefaultStoragePartition(tmp_profile) + ->FlushNetworkInterfaceForTesting(); + + network::mojom::URLLoaderFactoryPtr factory; + auto request = mojo::MakeRequest(&factory); + EXPECT_TRUE(api->MaybeProxyURLLoaderFactory(nullptr, false, &request)); + auto params = network::mojom::URLLoaderFactoryParams::New(); + params->process_id = 0; + content::BrowserContext::GetDefaultStoragePartition(tmp_profile) + ->GetNetworkContext() + ->CreateURLLoaderFactory(std::move(request), std::move(params)); + + network::TestURLLoaderClient client; + network::mojom::URLLoaderPtr loader; + network::ResourceRequest resource_request; + resource_request.url = embedded_test_server()->GetURL("/hung"); + factory->CreateLoaderAndStart( + mojo::MakeRequest(&loader), 0, 0, network::mojom::kURLLoadOptionNone, + resource_request, client.CreateInterfacePtr(), + net::MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS)); + + // Destroy profile, unbind client to cause a connection error, and delete the + // WebRequestAPI. This will cause the connection error that will reach the + // proxy before the ProxySet shutdown code runs on the IO thread. + ProfileDestroyer::DestroyProfileWhenAppropriate(tmp_profile); + client.Unbind(); + api.reset(); +} + // Test fixture which sets a custom NTP Page. class NTPInterceptionWebRequestAPITest : public ExtensionApiTest { public: @@ -1491,6 +1741,9 @@ IN_PROC_BROWSER_TEST_F(NTPInterceptionWebRequestAPITest, LoadExtension(test_data_dir_.AppendASCII("extension")); ASSERT_TRUE(extension); EXPECT_TRUE(listener.WaitUntilSatisfied()); + // Wait for webRequest listeners to be set up. + content::BrowserContext::GetDefaultStoragePartition(profile()) + ->FlushNetworkInterfaceForTesting(); // Have the extension listen for requests to |fake_ntp_script.js|. listener.Reply(https_test_server()->GetURL("/fake_ntp_script.js").spec()); @@ -1552,9 +1805,7 @@ class LocalNTPInterceptionWebRequestAPITest base::Unretained(this))); ASSERT_TRUE(https_test_server_.InitializeAndListen()); ExtensionApiTest::SetUp(); - feature_list_.InitWithFeatures( - {::features::kUseGoogleLocalNtp, ::features::kOneGoogleBarOnLocalNtp}, - {}); + feature_list_.InitWithFeatures({::features::kUseGoogleLocalNtp}, {}); } void SetUpCommandLine(base::CommandLine* command_line) override { ExtensionApiTest::SetUpCommandLine(command_line); @@ -1891,4 +2142,110 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTestWithManagementPolicy, EXPECT_EQ(0, GetWebRequestCountFromBackgroundPage(extension, profile())); } +// A test fixture which mocks the Time::Now() function to ensure that the +// default clock returns monotonically increasing time. +class ExtensionWebRequestMockedClockTest : public ExtensionWebRequestApiTest { + public: + ExtensionWebRequestMockedClockTest() + : scoped_time_clock_override_(&ExtensionWebRequestMockedClockTest::Now, + nullptr, + nullptr) {} + + private: + static base::Time Now() { + static base::Time now_time = base::Time::UnixEpoch(); + now_time += base::TimeDelta::FromMilliseconds(1); + return now_time; + } + + base::subtle::ScopedTimeClockOverrides scoped_time_clock_override_; + DISALLOW_COPY_AND_ASSIGN(ExtensionWebRequestMockedClockTest); +}; + +// Tests that we correctly dispatch the OnActionIgnored event on an extension +// if the extension's proposed redirect is ignored. +IN_PROC_BROWSER_TEST_F(ExtensionWebRequestMockedClockTest, + OnActionIgnored_Redirect) { + ASSERT_TRUE(StartEmbeddedTestServer()); + + // Load the two extensions. They redirect "google.com" main-frame urls to + // the corresponding "example.com and "foo.com" urls. + base::FilePath test_dir = + test_data_dir_.AppendASCII("webrequest/on_action_ignored"); + + // Load the first extension. + ExtensionTestMessageListener ready_1_listener("ready_1", + false /*will_reply*/); + const Extension* extension_1 = + LoadExtension(test_dir.AppendASCII("extension_1")); + ASSERT_TRUE(extension_1); + ASSERT_TRUE(ready_1_listener.WaitUntilSatisfied()); + const std::string extension_id_1 = extension_1->id(); + + // Load the second extension. + ExtensionTestMessageListener ready_2_listener("ready_2", + false /*will_reply*/); + const Extension* extension_2 = + LoadExtension(test_dir.AppendASCII("extension_2")); + ASSERT_TRUE(extension_2); + ASSERT_TRUE(ready_2_listener.WaitUntilSatisfied()); + const std::string extension_id_2 = extension_2->id(); + + const ExtensionPrefs* prefs = ExtensionPrefs::Get(profile()); + EXPECT_LT(prefs->GetInstallTime(extension_id_1), + prefs->GetInstallTime(extension_id_2)); + + // The extensions will notify the browser if their proposed redirect was + // successful or not. + ExtensionTestMessageListener redirect_ignored_listener("redirect_ignored", + false /*will_reply*/); + ExtensionTestMessageListener redirect_successful_listener( + "redirect_successful", false /*will_reply*/); + + const GURL url = embedded_test_server()->GetURL("google.com", "/simple.html"); + const GURL expected_redirect_url_1 = + embedded_test_server()->GetURL("example.com", "/simple.html"); + const GURL expected_redirect_url_2 = + embedded_test_server()->GetURL("foo.com", "/simple.html"); + + ui_test_utils::NavigateToURL(browser(), url); + + content::WebContents* web_contents = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(web_contents); + + // The second extension is the latest installed, hence it's redirect url + // should take precedence. + EXPECT_EQ(expected_redirect_url_2, web_contents->GetLastCommittedURL()); + EXPECT_TRUE(redirect_ignored_listener.WaitUntilSatisfied()); + EXPECT_EQ(extension_id_1, + redirect_ignored_listener.extension_id_for_message()); + EXPECT_TRUE(redirect_successful_listener.WaitUntilSatisfied()); + EXPECT_EQ(extension_id_2, + redirect_successful_listener.extension_id_for_message()); + + // Now let |extension_1| be installed after |extension_2|. For an unpacked + // extension, reloading is equivalent to a reinstall. + ready_1_listener.Reset(); + ReloadExtension(extension_id_1); + ASSERT_TRUE(ready_1_listener.WaitUntilSatisfied()); + + EXPECT_LT(prefs->GetInstallTime(extension_id_2), + prefs->GetInstallTime(extension_id_1)); + + redirect_ignored_listener.Reset(); + redirect_successful_listener.Reset(); + ui_test_utils::NavigateToURL(browser(), url); + + // The first extension is the latest installed, hence it's redirect url + // should take precedence. + EXPECT_EQ(expected_redirect_url_1, web_contents->GetLastCommittedURL()); + EXPECT_TRUE(redirect_ignored_listener.WaitUntilSatisfied()); + EXPECT_EQ(extension_id_2, + redirect_ignored_listener.extension_id_for_message()); + EXPECT_TRUE(redirect_successful_listener.WaitUntilSatisfied()); + EXPECT_EQ(extension_id_1, + redirect_successful_listener.extension_id_for_message()); +} + } // namespace extensions diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc index 0bb5252379c..64673d94b64 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.cc @@ -578,7 +578,7 @@ bool WebrtcLoggingPrivateStartEventLoggingFunction::RunAsync() { BrowserThread::UI, FROM_HERE, base::BindOnce(&WebRtcLoggingHandlerHost::StartEventLogging, webrtc_logging_handler_host, params->peer_connection_id, - params->max_log_size_bytes, callback)); + params->max_log_size_bytes, params->web_app_id, callback)); return true; } diff --git a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc index 086ddfa4c0a..ec714ee3042 100644 --- a/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc +++ b/chromium/chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_apitest.cc @@ -13,19 +13,23 @@ #include "base/strings/stringprintf.h" #include "base/test/scoped_command_line.h" #include "base/test/scoped_feature_list.h" +#include "build/build_config.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/extensions/api/webrtc_logging_private/webrtc_logging_private_api.h" #include "chrome/browser/extensions/extension_apitest.h" #include "chrome/browser/extensions/extension_function_test_utils.h" #include "chrome/browser/extensions/extension_tab_util.h" +#include "chrome/browser/media/webrtc/webrtc_event_log_manager.h" #include "chrome/browser/media/webrtc/webrtc_event_log_manager_common.h" #include "chrome/browser/media/webrtc/webrtc_log_uploader.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/common/chrome_features.h" #include "chrome/common/chrome_switches.h" +#include "components/policy/core/browser/browser_policy_connector.h" +#include "components/policy/core/common/mock_configuration_policy_provider.h" +#include "components/policy/policy_constants.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/render_view_host.h" -#include "content/public/browser/webrtc_event_logger.h" #include "content/public/test/test_utils.h" #include "extensions/common/extension_builder.h" #include "third_party/zlib/google/compression_utils.h" @@ -34,16 +38,26 @@ using compression::GzipUncompress; using extensions::Extension; using extensions::WebrtcLoggingPrivateDiscardFunction; using extensions::WebrtcLoggingPrivateSetMetaDataFunction; +using extensions::WebrtcLoggingPrivateStartAudioDebugRecordingsFunction; +using extensions::WebrtcLoggingPrivateStartEventLoggingFunction; using extensions::WebrtcLoggingPrivateStartFunction; using extensions::WebrtcLoggingPrivateStartRtpDumpFunction; +using extensions::WebrtcLoggingPrivateStopAudioDebugRecordingsFunction; using extensions::WebrtcLoggingPrivateStopFunction; using extensions::WebrtcLoggingPrivateStopRtpDumpFunction; using extensions::WebrtcLoggingPrivateStoreFunction; using extensions::WebrtcLoggingPrivateUploadFunction; using extensions::WebrtcLoggingPrivateUploadStoredFunction; -using extensions::WebrtcLoggingPrivateStartAudioDebugRecordingsFunction; -using extensions::WebrtcLoggingPrivateStopAudioDebugRecordingsFunction; -using extensions::WebrtcLoggingPrivateStartEventLoggingFunction; +using webrtc_event_logging::kMaxRemoteLogFileSizeBytes; +using webrtc_event_logging::kStartRemoteLoggingFailureAlreadyLogging; +using webrtc_event_logging::kStartRemoteLoggingFailureFeatureDisabled; +using webrtc_event_logging::kStartRemoteLoggingFailureMaxSizeTooLarge; +using webrtc_event_logging::kStartRemoteLoggingFailureMaxSizeTooSmall; +using webrtc_event_logging:: + kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; +using webrtc_event_logging::kStartRemoteLoggingFailureUnlimitedSizeDisallowed; +using webrtc_event_logging::kWebRtcEventLogManagerUnlimitedFileSize; +using webrtc_event_logging::WebRtcEventLogManager; namespace utils = extension_function_test_utils; @@ -53,6 +67,8 @@ static const char kTestLoggingSessionIdKey[] = "app_session_id"; static const char kTestLoggingSessionIdValue[] = "0123456789abcdef"; static const char kTestLoggingUrl[] = "dummy url string"; +constexpr int kWebAppId = 15; // Arbitrary. + std::string ParamsToString(const base::ListValue& parameters) { std::string parameter_string; EXPECT_TRUE(base::JSONWriter::Write(parameters, ¶meter_string)); @@ -76,7 +92,6 @@ void InitializeTestMetaData(base::ListValue* parameters) { class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { protected: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature(features::kWebRtcRemoteEventLog); extensions::ExtensionApiTest::SetUp(); extension_ = extensions::ExtensionBuilder("Test").Build(); } @@ -310,6 +325,7 @@ class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { // TODO(crbug.com/829419): Return success/failure of the executed function. void StartEventLogging(const std::string& peerConnectionId, int maxLogSizeBytes, + int webAppId, bool expect_success, const std::string& expected_error = std::string()) { DCHECK_EQ(expect_success, expected_error.empty()); @@ -318,6 +334,7 @@ class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { AppendTabIdAndUrl(¶ms); params.AppendString(peerConnectionId); params.AppendInteger(maxLogSizeBytes); + params.AppendInteger(webAppId); if (expect_success) { scoped_refptr<WebrtcLoggingPrivateStartEventLoggingFunction> function( @@ -346,7 +363,7 @@ class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { } void SetUpPeerConnection(const std::string& peer_connection_id) { - auto* manager = content::WebRtcEventLogger::Get(); + auto* manager = WebRtcEventLogManager::GetInstance(); auto* rph = web_contents()->GetRenderViewHost()->GetProcess(); const int render_process_id = rph->GetID(); @@ -361,30 +378,6 @@ class WebrtcLoggingPrivateApiTest : public extensions::ExtensionApiTest { scoped_refptr<Extension> extension_; }; -class WebrtcLoggingPrivateApiTestDisabledRemoteLogging - : public WebrtcLoggingPrivateApiTest { - protected: - void SetUp() override { - scoped_feature_list_.InitAndDisableFeature(features::kWebRtcRemoteEventLog); - extensions::ExtensionApiTest::SetUp(); - extension_ = extensions::ExtensionBuilder("Test").Build(); - } -}; - -class WebrtcLoggingPrivateApiTestInIncognitoMode - : public WebrtcLoggingPrivateApiTest { - protected: - Browser* GetBrowser() override { - if (!browser_) { - browser_ = CreateIncognitoBrowser(); - } - return browser_; - } - - private: - Browser* browser_{nullptr}; // Does not own the object. -}; - // Helper class to temporarily tell the uploader to save the multipart buffer to // a test string instead of uploading. class ScopedOverrideUploadBuffer { @@ -644,52 +637,143 @@ IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, ASSERT_TRUE(StartAudioDebugRecordings(1)); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingForKnownPeerConnectionSucceeds) { +#if !defined(OS_ANDROID) + +// Fixture for various tests over StartEventLogging. Intended to be sub-classed +// to test different scenarios. +class WebrtcLoggingPrivateApiStartEventLoggingTestBase + : public WebrtcLoggingPrivateApiTest { + public: + ~WebrtcLoggingPrivateApiStartEventLoggingTestBase() override = default; + + protected: + void SetUp() override { + SetUpFeatures(); + WebrtcLoggingPrivateApiTest::SetUp(); + } + + void SetUpFeatures() { + std::vector<base::Feature> enabled; + std::vector<base::Feature> disabled; + + if (WebRtcEventLogCollectionFeature()) { + enabled.push_back(features::kWebRtcRemoteEventLog); + } else { + disabled.push_back(features::kWebRtcRemoteEventLog); + } + + enabled.push_back(features::kWebRtcRemoteEventLogGzipped); + + scoped_feature_list_.InitWithFeatures(enabled, disabled); + } + + void SetUpInProcessBrowserTestFixture() override { + EXPECT_CALL(provider_, IsInitializationComplete(testing::_)) + .WillRepeatedly(testing::Return(true)); + + policy::BrowserPolicyConnector::SetPolicyProviderForTesting(&provider_); + policy::PolicyMap values; + + values.Set(policy::key::kWebRtcEventLogCollectionAllowed, + policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER, + policy::POLICY_SOURCE_ENTERPRISE_DEFAULT, + std::make_unique<base::Value>(WebRtcEventLogCollectionPolicy()), + nullptr); + + provider_.UpdateChromePolicy(values); + } + + // Whether the test should have WebRTC remote-bound event logging generally + // enabled (default behavior), or disabled (Finch kill-switch engaged). + virtual bool WebRtcEventLogCollectionFeature() const = 0; + + // Whether the test should simulate running on a user profile which + // has the kWebRtcEventLogCollectionAllowed policy configured or not. + virtual bool WebRtcEventLogCollectionPolicy() const = 0; + + private: + policy::MockConfigurationPolicyProvider provider_; +}; + +// Test StartEventLogging's behavior when the feature is active (kill-switch +// from Finch *not* engaged, working in a profile where the policy is +// configured). +class WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled + : public WebrtcLoggingPrivateApiStartEventLoggingTestBase { + public: + ~WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled() + override = default; + + bool WebRtcEventLogCollectionFeature() const override { return true; } + + bool WebRtcEventLogCollectionPolicy() const override { return true; } +}; + +// Also covers StartEventLoggingForLegalWebAppIdSucceeds scenario. +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForKnownPeerConnectionSucceeds) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); const int max_size_bytes = kMaxRemoteLogFileSizeBytes; constexpr bool expect_success = true; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingWithUnlimitedSizeFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingWithUnlimitedSizeFails) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); const int max_size_bytes = kWebRtcEventLogManagerUnlimitedFileSize; constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureUnlimitedSizeDisallowed; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingWithExcessiveMaxSizeFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingWithTooSmallMaxSize) { + const std::string peer_connection_id = "id"; + SetUpPeerConnection(peer_connection_id); + const int max_size_bytes = 1; + constexpr bool expect_success = false; + const std::string error_message = kStartRemoteLoggingFailureMaxSizeTooSmall; + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); +} + +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingWithExcessiveMaxSizeFails) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); const int max_size_bytes = kMaxRemoteLogFileSizeBytes + 1; constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureMaxSizeTooLarge; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingForNeverAddedPeerConnectionFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForNeverAddedPeerConnectionFails) { // Note that manager->PeerConnectionAdded() is not called. const std::string peer_connection_id = "id"; const int max_size_bytes = kMaxRemoteLogFileSizeBytes; constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingForWrongPeerConnectionIdFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForWrongPeerConnectionIdFails) { const std::string peer_connection_id_1 = "id1"; const std::string peer_connection_id_2 = "id2"; @@ -698,12 +782,13 @@ IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureUnknownOrInactivePeerConnection; - StartEventLogging(peer_connection_id_2, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id_2, max_size_bytes, kWebAppId, + expect_success, error_message); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, - StartEventLoggingForAlreadyLoggedPeerConnectionFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForAlreadyLoggedPeerConnectionFails) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); @@ -712,36 +797,127 @@ IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTest, // First call succeeds. { constexpr bool expect_success = true; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success); } // Second call fails. { constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureAlreadyLogging; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTestDisabledRemoteLogging, - StartEventLoggingFails) { +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForTooLowWebAppIdFails) { + const std::string peer_connection_id = "id"; + SetUpPeerConnection(peer_connection_id); + const int max_size_bytes = kMaxRemoteLogFileSizeBytes; + const size_t web_app_id = + webrtc_event_logging::kMinWebRtcEventLogWebAppId - 1; + ASSERT_LT(web_app_id, webrtc_event_logging::kMinWebRtcEventLogWebAppId); + constexpr bool expect_success = false; + const std::string error_message = + webrtc_event_logging::kStartRemoteLoggingFailureIllegalWebAppId; + StartEventLogging(peer_connection_id, max_size_bytes, web_app_id, + expect_success, error_message); +} + +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureAndPolicyEnabled, + StartEventLoggingForTooHighWebAppIdFails) { + const std::string peer_connection_id = "id"; + SetUpPeerConnection(peer_connection_id); + const int max_size_bytes = kMaxRemoteLogFileSizeBytes; + const size_t web_app_id = + webrtc_event_logging::kMaxWebRtcEventLogWebAppId + 1; + ASSERT_GT(web_app_id, webrtc_event_logging::kMaxWebRtcEventLogWebAppId); + constexpr bool expect_success = false; + const std::string error_message = + webrtc_event_logging::kStartRemoteLoggingFailureIllegalWebAppId; + StartEventLogging(peer_connection_id, max_size_bytes, web_app_id, + expect_success, error_message); +} + +// Testing with either the feature or the policy disabled (not both). +class WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled + : public WebrtcLoggingPrivateApiStartEventLoggingTestBase, + public ::testing::WithParamInterface<bool> { + public: + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled() + : feature_enabled_(GetParam()), policy_enabled_(!feature_enabled_) {} + + ~WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled() + override = default; + + protected: + bool WebRtcEventLogCollectionFeature() const override { + return feature_enabled_; + } + + bool WebRtcEventLogCollectionPolicy() const override { + return policy_enabled_; + } + + private: + const bool feature_enabled_; + const bool policy_enabled_; +}; + +IN_PROC_BROWSER_TEST_P( + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled, + StartEventLoggingFails) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); const int max_size_bytes = kMaxRemoteLogFileSizeBytes; constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureFeatureDisabled; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } -IN_PROC_BROWSER_TEST_F(WebrtcLoggingPrivateApiTestInIncognitoMode, - StartEventLoggingFails) { +INSTANTIATE_TEST_CASE_P( + FeatureEnabled, + WebrtcLoggingPrivateApiStartEventLoggingTestFeatureOrPolicyDisabled, + ::testing::Bool()); + +// Make sure that, even if both the feature and the policy enable remote-bound +// event logging, it will be blocked for incognito sessions. +class WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode + : public WebrtcLoggingPrivateApiStartEventLoggingTestBase { + public: + ~WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode() override = + default; + + protected: + Browser* GetBrowser() override { + if (!browser_) { + browser_ = CreateIncognitoBrowser(); + } + return browser_; + } + + bool WebRtcEventLogCollectionFeature() const override { return true; } + + bool WebRtcEventLogCollectionPolicy() const override { return true; } + + private: + Browser* browser_{nullptr}; // Does not own the object. +}; + +IN_PROC_BROWSER_TEST_F( + WebrtcLoggingPrivateApiStartEventLoggingTestInIncognitoMode, + StartEventLoggingFails) { const std::string peer_connection_id = "id"; SetUpPeerConnection(peer_connection_id); const int max_size_bytes = kMaxRemoteLogFileSizeBytes; constexpr bool expect_success = false; const std::string error_message = kStartRemoteLoggingFailureFeatureDisabled; - StartEventLogging(peer_connection_id, max_size_bytes, expect_success, - error_message); + StartEventLogging(peer_connection_id, max_size_bytes, kWebAppId, + expect_success, error_message); } + +#endif // !defined(OS_ANDROID) 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 264e9c2a469..aa5ee121352 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 @@ -8,7 +8,7 @@ #include "build/build_config.h" #include "chrome/browser/apps/platform_apps/app_browsertest_util.h" #include "chrome/common/chrome_switches.h" -#include "components/webrtc_logging/browser/log_list.h" +#include "components/webrtc_logging/browser/text_log_list.h" class WebrtcLoggingPrivateApiBrowserTest : public extensions::PlatformAppBrowserTest { @@ -17,8 +17,8 @@ class WebrtcLoggingPrivateApiBrowserTest ~WebrtcLoggingPrivateApiBrowserTest() override = default; base::FilePath webrtc_logs_path() { - return webrtc_logging::LogList::GetWebRtcLogDirectoryForBrowserContextPath( - profile()->GetPath()); + return webrtc_logging::TextLogList:: + GetWebRtcLogDirectoryForBrowserContextPath(profile()->GetPath()); } private: 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 4819cef550e..e1ecf590919 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 @@ -28,7 +28,9 @@ #include "content/public/browser/gpu_data_manager.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" +#include "content/public/browser/render_frame_host.h" #include "content/public/test/browser_test_utils.h" +#include "content/public/test/test_navigation_observer.h" #include "extensions/browser/api/management/management_api.h" #include "extensions/browser/extension_dialog_auto_confirm.h" #include "extensions/browser/extension_system.h" @@ -187,34 +189,52 @@ class ExtensionWebstorePrivateApiTest : public ExtensionApiTest { }; // Test cases for webstore origin frame blocking. -// TODO(mkwst): Disabled until new X-Frame-Options behavior rolls into -// Chromium, see crbug.com/226018. IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, - DISABLED_FrameWebstorePageBlocked) { - base::string16 expected_title = base::UTF8ToUTF16("PASS: about:blank"); - base::string16 failure_title = base::UTF8ToUTF16("FAIL"); - content::TitleWatcher watcher(GetWebContents(), expected_title); - watcher.AlsoWaitForTitle(failure_title); + FrameWebstorePageBlocked) { GURL url = embedded_test_server()->GetURL( "/extensions/api_test/webstore_private/noframe.html"); + content::WebContents* web_contents = GetWebContents(); ui_test_utils::NavigateToURL(browser(), url); - base::string16 final_title = watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, final_title); + + // Try to load the same URL, but with the current Chrome web store origin in + // an iframe (i.e. http://www.example.com) + content::TestNavigationObserver observer(web_contents); + ASSERT_TRUE(content::ExecuteScript(web_contents, "dropFrame()")); + WaitForLoadStop(web_contents); + content::RenderFrameHost* subframe = + content::ChildFrameAt(web_contents->GetMainFrame(), 0); + ASSERT_TRUE(subframe); + + // The subframe load should fail due to XFO. + GURL iframe_url = embedded_test_server()->GetURL( + "www.example.com", "/extensions/api_test/webstore_private/noframe.html"); + EXPECT_EQ(iframe_url, subframe->GetLastCommittedURL()); + EXPECT_FALSE(observer.last_navigation_succeeded()); + EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE, observer.last_net_error_code()); } -// TODO(mkwst): Disabled until new X-Frame-Options behavior rolls into -// Chromium, see crbug.com/226018. -IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, - DISABLED_FrameErrorPageBlocked) { - base::string16 expected_title = base::UTF8ToUTF16("PASS: about:blank"); - base::string16 failure_title = base::UTF8ToUTF16("FAIL"); - content::TitleWatcher watcher(GetWebContents(), expected_title); - watcher.AlsoWaitForTitle(failure_title); +IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, FrameErrorPageBlocked) { GURL url = embedded_test_server()->GetURL( "/extensions/api_test/webstore_private/noframe2.html"); + content::WebContents* web_contents = GetWebContents(); ui_test_utils::NavigateToURL(browser(), url); - base::string16 final_title = watcher.WaitAndGetTitle(); - EXPECT_EQ(expected_title, final_title); + + // Try to load the same URL, but with the current Chrome web store origin in + // an iframe (i.e. http://www.example.com) + content::TestNavigationObserver observer(web_contents); + ASSERT_TRUE(content::ExecuteScript(web_contents, "dropFrame()")); + WaitForLoadStop(web_contents); + content::RenderFrameHost* subframe = + content::ChildFrameAt(web_contents->GetMainFrame(), 0); + ASSERT_TRUE(subframe); + + // The subframe load should fail due to XFO. + GURL iframe_url = embedded_test_server()->GetURL( + "www.example.com", + "/nonesuch/extensions/api_test/webstore_private/noframe2.html "); + EXPECT_EQ(iframe_url, subframe->GetLastCommittedURL()); + EXPECT_FALSE(observer.last_navigation_succeeded()); + EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE, observer.last_net_error_code()); } // Test cases where the user accepts the install confirmation dialog. @@ -351,24 +371,6 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, EmptyCrx) { } #if BUILDFLAG(ENABLE_SUPERVISED_USERS) - -class ExtensionWebstorePrivateApiTestSupervised - : public ExtensionWebstorePrivateApiTest { - public: - void SetUpCommandLine(base::CommandLine* command_line) override { - ExtensionWebstorePrivateApiTest::SetUpCommandLine(command_line); - command_line->AppendSwitchASCII(switches::kSupervisedUserId, "not_empty"); - } -}; - -// Tests that extension installation is blocked for supervised users. -// Note: This will have to be updated if we enable SU-initiated installs. -IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestSupervised, - InstallBlocked) { - ASSERT_TRUE( - RunInstallTest("begin_install_fail_supervised.html", "extension.crx")); -} - class ExtensionWebstorePrivateApiTestChild : public ExtensionWebstorePrivateApiTest { public: |