diff options
Diffstat (limited to 'chromium/chrome/browser/ui/webui/settings')
113 files changed, 5454 insertions, 1574 deletions
diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.cc b/chromium/chrome/browser/ui/webui/settings/about_handler.cc index 9f615ac73cb..a1bb190d093 100644 --- a/chromium/chrome/browser/ui/webui/settings/about_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/about_handler.cc @@ -101,15 +101,12 @@ struct RegulatoryLabel { // Returns message that informs user that for update it's better to // connect to a network of one of the allowed types. base::string16 GetAllowedConnectionTypesMessage() { - const chromeos::NetworkState* network = chromeos::NetworkHandler::Get() - ->network_state_handler() - ->DefaultNetwork(); - const bool mobile_data = - network && network->IsConnectedState() && network->IsUsingMobileData(); - if (help_utils_chromeos::IsUpdateOverCellularAllowed( - true /* interactive */)) { - return mobile_data + /*interactive=*/true)) { + const bool metered = chromeos::NetworkHandler::Get() + ->network_state_handler() + ->default_network_is_metered(); + return metered ? l10n_util::GetStringUTF16( IDS_UPGRADE_NETWORK_LIST_CELLULAR_ALLOWED_NOT_AUTOMATIC) : l10n_util::GetStringUTF16( @@ -652,6 +649,7 @@ void AboutHandler::RequestUpdate() { void AboutHandler::SetUpdateStatus(VersionUpdater::Status status, int progress, bool rollback, + bool powerwash, const std::string& version, int64_t size, const base::string16& message) { @@ -663,6 +661,7 @@ void AboutHandler::SetUpdateStatus(VersionUpdater::Status status, event->SetString("message", message); event->SetInteger("progress", progress); event->SetBoolean("rollback", rollback); + event->SetBoolean("powerwash", powerwash); event->SetString("version", version); // DictionaryValue does not support int64_t, so convert to string. event->SetString("size", base::NumberToString(size)); diff --git a/chromium/chrome/browser/ui/webui/settings/about_handler.h b/chromium/chrome/browser/ui/webui/settings/about_handler.h index 4b8bf98b5a3..bc71e2da278 100644 --- a/chromium/chrome/browser/ui/webui/settings/about_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/about_handler.h @@ -135,6 +135,7 @@ class AboutHandler : public settings::SettingsPageUIHandler, void SetUpdateStatus(VersionUpdater::Status status, int progress, bool rollback, + bool powerwash, const std::string& version, int64_t size, const base::string16& fail_message); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.cc index ea9dc60efd8..a90a99bfebb 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.cc @@ -4,11 +4,11 @@ #include "chrome/browser/ui/webui/settings/chromeos/about_section.h" +#include "base/feature_list.h" #include "base/i18n/message_formatter.h" #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "base/system/sys_info.h" -#include "build/branding_buildflags.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/chromeos/arc/arc_util.h" @@ -19,10 +19,13 @@ #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/channel_info.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "chrome/grit/chromium_strings.h" #include "chrome/grit/generated_resources.h" +#include "chromeos/constants/chromeos_features.h" #include "chromeos/dbus/constants/dbus_switches.h" +#include "components/prefs/pref_service.h" #include "components/strings/grit/components_chromium_strings.h" #include "components/strings/grit/components_strings.h" #include "components/user_manager/user_manager.h" @@ -44,16 +47,14 @@ const std::vector<SearchConcept>& GetAboutSearchConcepts() { mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, - {.subpage = mojom::Subpage::kDetailedBuildInfo}, - {IDS_OS_SETTINGS_TAG_ABOUT_CHROME_OS_DETAILED_BUILD_ALT1, - SearchConcept::kAltTagEnd}}, - {IDS_OS_SETTINGS_TAG_ABOUT_CHROME_OS, + {.subpage = mojom::Subpage::kDetailedBuildInfo}}, + {IDS_SETTINGS_ABOUT_OS, mojom::kAboutChromeOsDetailsSubpagePath, mojom::SearchResultIcon::kChrome, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kAboutChromeOsDetails}}, - {IDS_OS_SETTINGS_TAG_ABOUT_CHROME_OS_VERSION, + {IDS_OS_SETTINGS_TAG_OS_VERSION, mojom::kAboutChromeOsDetailsSubpagePath, mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium, @@ -62,13 +63,67 @@ const std::vector<SearchConcept>& GetAboutSearchConcepts() { {IDS_OS_SETTINGS_TAG_ABOUT_CHROME_OS_CHANNEL, mojom::kDetailedBuildInfoSubpagePath, mojom::SearchResultIcon::kChrome, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kChangeChromeChannel}}, + {IDS_OS_SETTINGS_TAG_ABOUT_OS_UPDATE, + mojom::kAboutChromeOsDetailsSubpagePath, + mojom::SearchResultIcon::kChrome, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kCheckForOsUpdate}}, + {IDS_OS_SETTINGS_TAG_ABOUT_HELP, + mojom::kAboutChromeOsDetailsSubpagePath, + mojom::SearchResultIcon::kChrome, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kGetHelpWithChromeOs}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetAboutReleaseNotesSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_ABOUT_RELEASE_NOTES, + mojom::kAboutChromeOsDetailsSubpagePath, + mojom::SearchResultIcon::kChrome, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kSeeWhatsNew}, + {IDS_OS_SETTINGS_TAG_ABOUT_RELEASE_NOTES_ALT1, + SearchConcept::kAltTagEnd}}, + }); + return *tags; +} + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +const std::vector<SearchConcept>& GetAboutTermsOfServiceSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_ABOUT_TERMS_OF_SERVICE, + mojom::kAboutChromeOsDetailsSubpagePath, + mojom::SearchResultIcon::kChrome, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kTermsOfService}}, }); return *tags; } +const std::vector<SearchConcept>& GetAboutReportIssueSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_ABOUT_REPORT_ISSUE, + mojom::kAboutChromeOsDetailsSubpagePath, + mojom::SearchResultIcon::kChrome, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kReportAnIssue}, + {IDS_OS_SETTINGS_TAG_ABOUT_REPORT_ISSUE_ALT1, + SearchConcept::kAltTagEnd}}, + }); + return *tags; +} +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + // Returns the link to the safety info for the device (if it exists). std::string GetSafetyInfoLink() { const std::vector<std::string> board = @@ -99,10 +154,33 @@ bool IsDeviceManaged() { } // namespace +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +AboutSection::AboutSection(Profile* profile, + SearchTagRegistry* search_tag_registry, + PrefService* pref_service) + : AboutSection(profile, search_tag_registry) { + pref_service_ = pref_service; + + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetAboutTermsOfServiceSearchConcepts()); + + pref_change_registrar_.Init(pref_service_); + pref_change_registrar_.Add( + prefs::kUserFeedbackAllowed, + base::BindRepeating(&AboutSection::UpdateReportIssueSearchTags, + base::Unretained(this))); + UpdateReportIssueSearchTags(); +} +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + AboutSection::AboutSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetAboutSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetAboutSearchConcepts()); + + if (base::FeatureList::IsEnabled(features::kReleaseNotes)) + updater.AddSearchTags(GetAboutReleaseNotesSearchConcepts()); } AboutSection::~AboutSection() = default; @@ -270,5 +348,58 @@ void AboutSection::AddHandlers(content::WebUI* web_ui) { web_ui->AddMessageHandler(std::make_unique<::settings::AboutHandler>()); } +int AboutSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_ABOUT_OS; +} + +mojom::Section AboutSection::GetSection() const { + return mojom::Section::kAboutChromeOs; +} + +mojom::SearchResultIcon AboutSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kChrome; +} + +std::string AboutSection::GetSectionPath() const { + return mojom::kAboutChromeOsSectionPath; +} + +void AboutSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // About Chrome OS. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_ABOUT_OS, mojom::Subpage::kAboutChromeOsDetails, + mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium, + mojom::kAboutChromeOsDetailsSubpagePath); + static constexpr mojom::Setting kAboutChromeOsDetailsSettings[] = { + mojom::Setting::kCheckForOsUpdate, mojom::Setting::kSeeWhatsNew, + mojom::Setting::kGetHelpWithChromeOs, mojom::Setting::kReportAnIssue, + mojom::Setting::kTermsOfService}; + RegisterNestedSettingBulk(mojom::Subpage::kAboutChromeOsDetails, + kAboutChromeOsDetailsSettings, generator); + + // Detailed build info. + generator->RegisterNestedSubpage( + IDS_SETTINGS_ABOUT_PAGE_DETAILED_BUILD_INFO, + mojom::Subpage::kDetailedBuildInfo, mojom::Subpage::kAboutChromeOsDetails, + mojom::SearchResultIcon::kChrome, mojom::SearchResultDefaultRank::kMedium, + mojom::kDetailedBuildInfoSubpagePath); + static constexpr mojom::Setting kDetailedBuildInfoSettings[] = { + mojom::Setting::kChangeChromeChannel, + mojom::Setting::kCopyDetailedBuildInfo}; + RegisterNestedSettingBulk(mojom::Subpage::kDetailedBuildInfo, + kDetailedBuildInfoSettings, generator); +} + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) +void AboutSection::UpdateReportIssueSearchTags() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + if (pref_service_->GetBoolean(prefs::kUserFeedbackAllowed)) + updater.AddSearchTags(GetAboutReportIssueSearchConcepts()); + else + updater.RemoveSearchTags(GetAboutReportIssueSearchConcepts()); +} +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.h index 0e619e80d91..506f0b67d56 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/about_section.h @@ -5,7 +5,9 @@ #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ABOUT_SECTION_H_ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_ABOUT_SECTION_H_ +#include "build/branding_buildflags.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" +#include "components/prefs/pref_change_registrar.h" namespace content { class WebUIDataSource; @@ -19,6 +21,11 @@ class SearchTagRegistry; // Provides UI strings and search tags for the settings "About Chrome OS" page. class AboutSection : public OsSettingsSection { public: +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + AboutSection(Profile* profile, + SearchTagRegistry* search_tag_registry, + PrefService* pref_service); +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) AboutSection(Profile* profile, SearchTagRegistry* search_tag_registry); ~AboutSection() override; @@ -26,6 +33,18 @@ class AboutSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; + +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + void UpdateReportIssueSearchTags(); + + PrefService* pref_service_; + PrefChangeRegistrar pref_change_registrar_; +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc index 1e7b9bf8b08..a38be955b4e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.cc @@ -23,6 +23,7 @@ #include "content/public/browser/web_ui_data_source.h" #include "content/public/common/content_features.h" #include "media/base/media_switches.h" +#include "ui/accessibility/accessibility_features.h" #include "ui/accessibility/accessibility_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" @@ -82,16 +83,18 @@ const std::vector<SearchConcept>& GetA11ySearchConcepts() { {IDS_OS_SETTINGS_TAG_A11Y_MONO_AUDIO, mojom::kManageAccessibilitySubpagePath, mojom::SearchResultIcon::kA11y, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kMonoAudio}, {IDS_OS_SETTINGS_TAG_A11Y_MONO_AUDIO_ALT1, SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH, - mojom::kManageAccessibilitySubpagePath, + mojom::kTextToSpeechSubpagePath, mojom::SearchResultIcon::kA11y, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, - {.subpage = mojom::Subpage::kManageAccessibility}}, + {.subpage = mojom::Subpage::kTextToSpeech}, + {IDS_OS_SETTINGS_TAG_A11Y_TEXT_TO_SPEECH_ALT1, + SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_A11Y_CAPTIONS, mojom::kCaptionsSubpagePath, mojom::SearchResultIcon::kA11y, @@ -262,11 +265,43 @@ const std::vector<SearchConcept>& GetA11yLabelsSearchConcepts() { return *tags; } +const std::vector<SearchConcept>& GetA11yLiveCaptionSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_A11Y_LIVE_CAPTIONS, + mojom::kManageAccessibilitySubpagePath, + mojom::SearchResultIcon::kA11y, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kLiveCaptions}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetA11yCursorColorSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_A11Y_CURSOR_COLOR, + mojom::kManageAccessibilitySubpagePath, + mojom::SearchResultIcon::kA11y, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kEnableCursorColor}}, + }); + return *tags; +} + bool AreExperimentalA11yLabelsAllowed() { return base::FeatureList::IsEnabled( ::features::kExperimentalAccessibilityLabels); } +bool AreLiveCaptionsAllowed() { + return base::FeatureList::IsEnabled(media::kLiveCaption); +} + +bool IsCursorColorAllowed() { + return features::IsAccessibilityCursorColorEnabled(); +} + bool IsSwitchAccessAllowed() { return base::CommandLine::ForCurrentProcess()->HasSwitch( ::switches::kEnableExperimentalAccessibilitySwitchAccess); @@ -285,7 +320,8 @@ AccessibilitySection::AccessibilitySection( PrefService* pref_service) : OsSettingsSection(profile, search_tag_registry), pref_service_(pref_service) { - registry()->AddSearchTags(GetA11ySearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetA11ySearchConcepts()); pref_change_registrar_.Init(pref_service_); pref_change_registrar_.Add( @@ -323,6 +359,13 @@ void AccessibilitySection::AddLoadTimeData( IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_DEFAULT_LABEL}, {"largeMouseCursorSizeLargeLabel", IDS_SETTINGS_LARGE_MOUSE_CURSOR_SIZE_LARGE_LABEL}, + {"cursorColorEnabledLabel", IDS_SETTINGS_CURSOR_COLOR_ENABLED_LABEL}, + {"cursorColorOptionsLabel", IDS_SETTINGS_CURSOR_COLOR_OPTIONS_LABEL}, + {"cursorColorRed", IDS_SETTINGS_CURSOR_COLOR_RED}, + {"cursorColorOrange", IDS_SETTINGS_CURSOR_COLOR_ORANGE}, + {"cursorColorGreen", IDS_SETTINGS_CURSOR_COLOR_GREEN}, + {"cursorColorBlue", IDS_SETTINGS_CURSOR_COLOR_BLUE}, + {"cursorColorPurple", IDS_SETTINGS_CURSOR_COLOR_PURPLE}, {"highContrastLabel", IDS_SETTINGS_HIGH_CONTRAST_LABEL}, {"stickyKeysLabel", IDS_SETTINGS_STICKY_KEYS_LABEL}, {"chromeVoxLabel", IDS_SETTINGS_CHROMEVOX_LABEL}, @@ -473,6 +516,10 @@ void AccessibilitySection::AddLoadTimeData( IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_LABEL}, {"tabletModeShelfNavigationButtonsSettingDescription", IDS_SETTINGS_A11Y_TABLET_MODE_SHELF_BUTTONS_DESCRIPTION}, + {"captionsEnableLiveCaptionTitle", + IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_TITLE}, + {"captionsEnableLiveCaptionSubtitle", + IDS_SETTINGS_CAPTIONS_ENABLE_LIVE_CAPTION_SUBTITLE}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); @@ -495,8 +542,10 @@ void AccessibilitySection::AddLoadTimeData( html_source->AddString("tabletModeShelfNavigationButtonsLearnMoreUrl", chrome::kTabletModeGesturesLearnMoreURL); - html_source->AddBoolean("enableLiveCaption", - base::FeatureList::IsEnabled(media::kLiveCaption)); + html_source->AddBoolean("enableLiveCaption", AreLiveCaptionsAllowed()); + + html_source->AddBoolean("showExperimentalAccessibilityCursorColor", + IsCursorColorAllowed()); ::settings::AddCaptionSubpageStrings(html_source); } @@ -510,33 +559,132 @@ void AccessibilitySection::AddHandlers(content::WebUI* web_ui) { std::make_unique<::settings::FontHandler>(profile())); } +int AccessibilitySection::GetSectionNameMessageId() const { + return IDS_SETTINGS_ACCESSIBILITY; +} + +mojom::Section AccessibilitySection::GetSection() const { + return mojom::Section::kAccessibility; +} + +mojom::SearchResultIcon AccessibilitySection::GetSectionIcon() const { + return mojom::SearchResultIcon::kA11y; +} + +std::string AccessibilitySection::GetSectionPath() const { + return mojom::kAccessibilitySectionPath; +} + +void AccessibilitySection::RegisterHierarchy( + HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kA11yQuickSettings); + + // Manage accessibility. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_ACCESSIBILITY_MANAGE_ACCESSIBILITY_FEATURES, + mojom::Subpage::kManageAccessibility, mojom::SearchResultIcon::kA11y, + mojom::SearchResultDefaultRank::kMedium, + mojom::kManageAccessibilitySubpagePath); + static constexpr mojom::Setting kManageAccessibilitySettings[] = { + mojom::Setting::kChromeVox, + mojom::Setting::kSelectToSpeak, + mojom::Setting::kHighContrastMode, + mojom::Setting::kFullscreenMagnifier, + mojom::Setting::kDockedMagnifier, + mojom::Setting::kStickyKeys, + mojom::Setting::kOnScreenKeyboard, + mojom::Setting::kDictation, + mojom::Setting::kSpeakToType, + mojom::Setting::kEnableSwitchAccess, + mojom::Setting::kHighlightTextCaret, + mojom::Setting::kAutoClickWhenCursorStops, + mojom::Setting::kLargeCursor, + mojom::Setting::kHighlightCursorWhileMoving, + mojom::Setting::kTabletNavigationButtons, + mojom::Setting::kMonoAudio, + mojom::Setting::kStartupSound, + mojom::Setting::kGetImageDescriptionsFromGoogle, + mojom::Setting::kLiveCaptions, + mojom::Setting::kEnableCursorColor, + }; + RegisterNestedSettingBulk(mojom::Subpage::kManageAccessibility, + kManageAccessibilitySettings, generator); + + // Text-to-Speech. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_MANAGE_TTS_SETTINGS, mojom::Subpage::kTextToSpeech, + mojom::SearchResultIcon::kA11y, mojom::SearchResultDefaultRank::kMedium, + mojom::kTextToSpeechSubpagePath); + static constexpr mojom::Setting kTextToSpeechSettings[] = { + mojom::Setting::kTextToSpeechRate, mojom::Setting::kTextToSpeechPitch, + mojom::Setting::kTextToSpeechVolume, mojom::Setting::kTextToSpeechVoice, + mojom::Setting::kTextToSpeechEngines, + }; + RegisterNestedSettingBulk(mojom::Subpage::kTextToSpeech, + kTextToSpeechSettings, generator); + + // Switch access. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_MANAGE_SWITCH_ACCESS_SETTINGS, + mojom::Subpage::kSwitchAccessOptions, + mojom::SearchResultIcon::kA11y, + mojom::SearchResultDefaultRank::kMedium, + mojom::kSwitchAccessOptionsSubpagePath); + static constexpr mojom::Setting kSwitchAccessSettings[] = { + mojom::Setting::kSwitchActionAssignment, + mojom::Setting::kSwitchActionAutoScan, + mojom::Setting::kSwitchActionAutoScanKeyboard, + }; + RegisterNestedSettingBulk(mojom::Subpage::kSwitchAccessOptions, + kSwitchAccessSettings, generator); + + // Captions. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_CAPTIONS, mojom::Subpage::kCaptions, + mojom::SearchResultIcon::kA11y, mojom::SearchResultDefaultRank::kMedium, + mojom::kCaptionsSubpagePath); +} + void AccessibilitySection::UpdateSearchTags() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (accessibility_state_utils::IsScreenReaderEnabled() && AreExperimentalA11yLabelsAllowed()) { - registry()->AddSearchTags(GetA11yLabelsSearchConcepts()); + updater.AddSearchTags(GetA11yLabelsSearchConcepts()); } else { - registry()->RemoveSearchTags(GetA11yLabelsSearchConcepts()); + updater.RemoveSearchTags(GetA11yLabelsSearchConcepts()); } - registry()->RemoveSearchTags(GetA11ySwitchAccessSearchConcepts()); - registry()->RemoveSearchTags(GetA11ySwitchAccessOnSearchConcepts()); - registry()->RemoveSearchTags(GetA11ySwitchAccessKeyboardSearchConcepts()); + updater.RemoveSearchTags(GetA11ySwitchAccessSearchConcepts()); + updater.RemoveSearchTags(GetA11ySwitchAccessOnSearchConcepts()); + updater.RemoveSearchTags(GetA11ySwitchAccessKeyboardSearchConcepts()); + + if (AreLiveCaptionsAllowed()) { + updater.AddSearchTags(GetA11yLiveCaptionSearchConcepts()); + } else { + updater.RemoveSearchTags(GetA11yLiveCaptionSearchConcepts()); + } + + if (IsCursorColorAllowed()) { + updater.AddSearchTags(GetA11yCursorColorSearchConcepts()); + } else { + updater.RemoveSearchTags(GetA11yCursorColorSearchConcepts()); + } if (!IsSwitchAccessAllowed()) return; - registry()->AddSearchTags(GetA11ySwitchAccessSearchConcepts()); + updater.AddSearchTags(GetA11ySwitchAccessSearchConcepts()); if (!pref_service_->GetBoolean( ash::prefs::kAccessibilitySwitchAccessEnabled)) { return; } - registry()->AddSearchTags(GetA11ySwitchAccessOnSearchConcepts()); + updater.AddSearchTags(GetA11ySwitchAccessOnSearchConcepts()); if (pref_service_->GetBoolean( ash::prefs::kAccessibilitySwitchAccessAutoScanEnabled)) { - registry()->AddSearchTags(GetA11ySwitchAccessKeyboardSearchConcepts()); + updater.AddSearchTags(GetA11ySwitchAccessKeyboardSearchConcepts()); } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.h index 546454007b1..77f82db40d3 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/accessibility_section.h @@ -31,6 +31,11 @@ class AccessibilitySection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; void UpdateSearchTags(); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc index 0d6b946d6c3..69266feb260 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/account_manager_handler.cc @@ -17,10 +17,10 @@ #include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/policy/profile_policy_connector.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/chromeos/account_manager_welcome_dialog.h" -#include "chrome/browser/ui/webui/chromeos/account_migration_welcome_dialog.h" +#include "chrome/browser/ui/webui/chromeos/account_manager/account_manager_welcome_dialog.h" +#include "chrome/browser/ui/webui/chromeos/account_manager/account_migration_welcome_dialog.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" -#include "chrome/browser/ui/webui/signin/inline_login_handler_dialog_chromeos.h" +#include "chrome/browser/ui/webui/signin/inline_login_dialog_chromeos.h" #include "chrome/grit/generated_resources.h" #include "chromeos/components/account_manager/account_manager_factory.h" #include "components/signin/public/identity_manager/consent_level.h" @@ -343,8 +343,8 @@ base::ListValue AccountManagerUIHandler::GetSecondaryGaiaAccounts( void AccountManagerUIHandler::HandleAddAccount(const base::ListValue* args) { AllowJavascript(); - InlineLoginHandlerDialogChromeOS::Show( - InlineLoginHandlerDialogChromeOS::Source::kSettingsAddAccountButton); + InlineLoginDialogChromeOS::Show( + InlineLoginDialogChromeOS::Source::kSettingsAddAccountButton); } void AccountManagerUIHandler::HandleReauthenticateAccount( @@ -354,9 +354,9 @@ void AccountManagerUIHandler::HandleReauthenticateAccount( CHECK(!args->GetList().empty()); const std::string& account_email = args->GetList()[0].GetString(); - InlineLoginHandlerDialogChromeOS::Show( + InlineLoginDialogChromeOS::Show( account_email, - InlineLoginHandlerDialogChromeOS::Source::kSettingsReauthAccountButton); + InlineLoginDialogChromeOS::Source::kSettingsReauthAccountButton); } void AccountManagerUIHandler::HandleMigrateAccount( diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc index 276327a54c8..fd4f14a6d09 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.cc @@ -4,13 +4,34 @@ #include "chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h" +#include "ash/public/cpp/ambient/ambient_backend_controller.h" +#include "ash/public/cpp/ambient/common/ambient_settings.h" #include "base/bind.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/values.h" namespace chromeos { namespace settings { +namespace { + +// Width and height of the preview image for personal album. +constexpr int kBannerWidth = 512; +constexpr int kBannderHeight = 512; + +ash::AmbientModeTopicSource ExtractTopicSource(const base::ListValue* args) { + CHECK_EQ(args->GetSize(), 1U); + ash::AmbientModeTopicSource topic_source = + static_cast<ash::AmbientModeTopicSource>(args->GetList()[0].GetInt()); + // Check the |topic_source| has valid value. + CHECK_GE(topic_source, ash::AmbientModeTopicSource::kMinValue); + CHECK_LE(topic_source, ash::AmbientModeTopicSource::kMaxValue); + return topic_source; +} + +} // namespace + AmbientModeHandler::AmbientModeHandler() = default; AmbientModeHandler::~AmbientModeHandler() = default; @@ -22,36 +43,94 @@ void AmbientModeHandler::RegisterMessages() { base::Unretained(this))); web_ui()->RegisterMessageCallback( - "onTopicSourceSelectedChanged", - base::BindRepeating(&AmbientModeHandler::HandleTopicSourceSelectedChanged, + "setSelectedTopicSource", + base::BindRepeating(&AmbientModeHandler::HandleSetSelectedTopicSource, + base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "requestPhotosContainers", + base::BindRepeating(&AmbientModeHandler::RequestPhotosContainers, base::Unretained(this))); + + web_ui()->RegisterMessageCallback( + "setSelectedPhotosContainers", + base::BindRepeating( + &AmbientModeHandler::HandleSetSelectedPhotosContainers, + base::Unretained(this))); } void AmbientModeHandler::OnJavascriptAllowed() { - if (topic_source_.has_value()) - SendTopicSource(); + GetSettings(); + + // Prefetch albums. + FetchPersonalAlbums(); +} + +void AmbientModeHandler::OnJavascriptDisallowed() { + weak_factory_.InvalidateWeakPtrs(); } void AmbientModeHandler::HandleInitialized(const base::ListValue* args) { CHECK(args); CHECK(args->empty()); + init_from_ambient_mode_page_ = true; AllowJavascript(); - GetSettings(); } -void AmbientModeHandler::HandleTopicSourceSelectedChanged( +void AmbientModeHandler::RequestPhotosContainers(const base::ListValue* args) { + // TODO(b/159747583): Handle deep linking to ambientMode/photos page. + // For now it will not load the page contents if visited directly. + if (!init_from_ambient_mode_page_) + return; + + ash::AmbientModeTopicSource topic_source = ExtractTopicSource(args); + DCHECK_EQ(topic_source, settings_->topic_source); + + if (topic_source == ash::AmbientModeTopicSource::kGooglePhotos) { + FetchPersonalAlbums(); + } + SendPhotosContainers(); +} + +void AmbientModeHandler::HandleSetSelectedTopicSource( const base::ListValue* args) { - CHECK_EQ(args->GetSize(), 1U); - int topic_source; - CHECK(base::StringToInt(args->GetList()[0].GetString(), &topic_source)); - // Check the |topic_source| has valid value. - CHECK_GE(topic_source, - static_cast<int>(ash::AmbientModeTopicSource::kMinValue)); - CHECK_LE(topic_source, - static_cast<int>(ash::AmbientModeTopicSource::kMaxValue)); + ash::AmbientModeTopicSource topic_source = ExtractTopicSource(args); + settings_->topic_source = topic_source; + UpdateSettings(); +} - UpdateSettings(static_cast<ash::AmbientModeTopicSource>(topic_source)); +void AmbientModeHandler::HandleSetSelectedPhotosContainers( + const base::ListValue* args) { + switch (settings_->topic_source) { + case ash::AmbientModeTopicSource::kGooglePhotos: + // For Google Photos, we will populate the |selected_album_ids| with IDs + // of selected albums. + settings_->selected_album_ids.clear(); + for (const auto& value : args->GetList()) { + std::string name = value.GetString(); + auto it = std::find_if( + personal_albums_.albums.begin(), personal_albums_.albums.end(), + [name](const auto& album) { return album.album_name == name; }); + CHECK(it != personal_albums_.albums.end()); + settings_->selected_album_ids.emplace_back(it->album_id); + } + break; + case ash::AmbientModeTopicSource::kArtGallery: + // For Art gallery, we set the corresponding setting to be enabled or not + // based on the selections. + for (auto& art_setting : settings_->art_settings) { + std::string title = art_setting.title; + auto it = std::find_if( + args->GetList().begin(), args->GetList().end(), + [title](const auto& value) { return value.GetString() == title; }); + const bool checked = it != args->GetList().end(); + art_setting.enabled = checked; + } + break; + } + + UpdateSettings(); } void AmbientModeHandler::GetSettings() { @@ -60,37 +139,63 @@ void AmbientModeHandler::GetSettings() { } void AmbientModeHandler::OnGetSettings( - base::Optional<ash::AmbientModeTopicSource> topic_source) { - if (!topic_source.has_value()) { + const base::Optional<ash::AmbientSettings>& settings) { + if (!settings) { // TODO(b/152921891): Retry a small fixed number of times, then only retry // when user confirms in the error message dialog. return; } - topic_source_ = topic_source; - if (!IsJavascriptAllowed()) - return; - + settings_ = settings; SendTopicSource(); } void AmbientModeHandler::SendTopicSource() { + DCHECK(settings_); FireWebUIListener("topic-source-changed", - base::Value( + base::Value(static_cast<int>(settings_->topic_source))); +} - static_cast<int>(topic_source_.value()))); +void AmbientModeHandler::SendPhotosContainers() { + DCHECK(settings_); + + base::Value dictionary(base::Value::Type::DICTIONARY); + base::Value containers(base::Value::Type::LIST); + switch (settings_->topic_source) { + case ash::AmbientModeTopicSource::kGooglePhotos: + for (const auto& album : personal_albums_.albums) { + base::Value value(base::Value::Type::DICTIONARY); + value.SetKey("title", base::Value(album.album_name)); + value.SetKey("checked", + base::Value(base::Contains(settings_->selected_album_ids, + album.album_id))); + containers.Append(std::move(value)); + } + break; + case ash::AmbientModeTopicSource::kArtGallery: + for (const auto& setting : settings_->art_settings) { + base::Value value(base::Value::Type::DICTIONARY); + value.SetKey("title", base::Value(setting.title)); + value.SetKey("checked", base::Value(setting.enabled)); + containers.Append(std::move(value)); + } + break; + } + + dictionary.SetKey("topicSource", + base::Value(static_cast<int>(settings_->topic_source))); + dictionary.SetKey("topicContainers", std::move(containers)); + FireWebUIListener("photos-containers-changed", std::move(dictionary)); } -void AmbientModeHandler::UpdateSettings( - ash::AmbientModeTopicSource topic_source) { +void AmbientModeHandler::UpdateSettings() { + DCHECK(settings_); ash::AmbientBackendController::Get()->UpdateSettings( - topic_source, base::BindOnce(&AmbientModeHandler::OnUpdateSettings, - weak_factory_.GetWeakPtr(), topic_source)); + *settings_, base::BindOnce(&AmbientModeHandler::OnUpdateSettings, + weak_factory_.GetWeakPtr())); } -void AmbientModeHandler::OnUpdateSettings( - ash::AmbientModeTopicSource topic_source, - bool success) { +void AmbientModeHandler::OnUpdateSettings(bool success) { if (success) return; @@ -98,5 +203,31 @@ void AmbientModeHandler::OnUpdateSettings( // when user confirms in the error message dialog. } +void AmbientModeHandler::FetchPersonalAlbums() { + // TODO: Add a helper function to get all the albums. + ash::AmbientBackendController::Get()->FetchPersonalAlbums( + kBannerWidth, kBannderHeight, /*num_albums=*/100, /*resume_token=*/"", + base::BindOnce(&AmbientModeHandler::OnPersonalAlbumsFetched, + weak_factory_.GetWeakPtr())); +} + +void AmbientModeHandler::OnPersonalAlbumsFetched( + ash::PersonalAlbums personal_albums) { + personal_albums_ = std::move(personal_albums); + + // If the |topic_source| is not |kGooglePhotos|, no need to refresh the + // photos subpage. + // |settings_| could be null because we call GetSettings() and + // FetchPersonalAlbums() in OnJavascriptAllowed(). |settings_| is populated by + // OnGetSettings(), which could be called later. The purpose to call + // FetchPersonalAlbums() is to prefetch albums, which takes several seconds, + // This improves the experience when we click into the ambientMode/photos page + // to show the albums list faster. + if (settings_ && + settings_->topic_source == ash::AmbientModeTopicSource::kGooglePhotos) { + SendPhotosContainers(); + } +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h index 8e26f721b60..ac0c6441433 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/ambient_mode_handler.h @@ -12,6 +12,10 @@ #include "base/optional.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" +namespace ash { +struct AmbientSettings; +} // namespace ash + namespace base { class ListValue; } // namespace base @@ -31,34 +35,52 @@ class AmbientModeHandler : public ::settings::SettingsPageUIHandler { // settings::SettingsPageUIHandler: void RegisterMessages() override; void OnJavascriptAllowed() override; - void OnJavascriptDisallowed() override {} + void OnJavascriptDisallowed() override; private: // WebUI call to signal js side is ready. void HandleInitialized(const base::ListValue* args); + // WebUI call to request photos containers, e.g. personal albums or art + // categories. + void RequestPhotosContainers(const base::ListValue* args); + // WebUI call to sync topic source with server. - void HandleTopicSourceSelectedChanged(const base::ListValue* args); + void HandleSetSelectedTopicSource(const base::ListValue* args); + + // WebUI call to sync photos containers with server. + void HandleSetSelectedPhotosContainers(const base::ListValue* args); // Retrieve the initial settings from server. void GetSettings(); // Called when the initial settings is retrieved. - void OnGetSettings(base::Optional<ash::AmbientModeTopicSource> topic_source); + void OnGetSettings(const base::Optional<ash::AmbientSettings>& settings); // Send the "topic-source-changed" WebUIListener event when the initial // settings is retrieved. void SendTopicSource(); - // Update the selected topic source to server. - void UpdateSettings(ash::AmbientModeTopicSource topic_source); + // Send the "photos-containers-changed" WebUIListener event when the personal + // albums are retrieved. + void SendPhotosContainers(); + + // Update the local |settings_| to server. + void UpdateSettings(); // Called when the settings is updated. - // |topic_source| is the value to retry if the update was failed. - void OnUpdateSettings(ash::AmbientModeTopicSource topic_source, bool success); + void OnUpdateSettings(bool success); + + void FetchPersonalAlbums(); + + void OnPersonalAlbumsFetched(ash::PersonalAlbums personal_albums); + + // Whether the Javascript is inited from the ambientMode page. + bool init_from_ambient_mode_page_ = false; + + base::Optional<ash::AmbientSettings> settings_; - // The topic source, i.e. from which category the photos will be displayed. - base::Optional<ash::AmbientModeTopicSource> topic_source_; + ash::PersonalAlbums personal_albums_; base::WeakPtrFactory<AmbientModeHandler> weak_factory_{this}; }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.cc index 7ae2a6a8724..6d8ebbc5421 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.cc @@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/apps_section.h" +#include "base/feature_list.h" #include "base/no_destructor.h" #include "chrome/browser/chromeos/arc/arc_util.h" #include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h" @@ -17,6 +18,7 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/os_settings_resources.h" +#include "chromeos/constants/chromeos_features.h" #include "components/arc/arc_prefs.h" #include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" @@ -39,7 +41,7 @@ const std::vector<SearchConcept>& GetAppsSearchConcepts() { {IDS_OS_SETTINGS_TAG_APPS_MANAGEMENT, mojom::kAppManagementSubpagePath, mojom::SearchResultIcon::kAppsGrid, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kAppManagement}, {IDS_OS_SETTINGS_TAG_APPS_MANAGEMENT_ALT1, SearchConcept::kAltTagEnd}}, @@ -143,7 +145,8 @@ AppsSection::AppsSection(Profile* profile, : OsSettingsSection(profile, search_tag_registry), pref_service_(pref_service), arc_app_list_prefs_(arc_app_list_prefs) { - registry()->AddSearchTags(GetAppsSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetAppsSearchConcepts()); if (arc::IsArcAllowedForProfile(profile)) { pref_change_registrar_.Init(pref_service_); @@ -179,17 +182,11 @@ void AppsSection::AddLoadTimeData(content::WebUIDataSource* html_source) { "app-management/types.mojom-lite.js", IDR_OS_SETTINGS_APP_MANAGEMENT_TYPES_MOJO_LITE_JS); html_source->AddResourcePath( - "app-management/bitmap.mojom-lite.js", - IDR_OS_SETTINGS_APP_MANAGEMENT_BITMAP_MOJO_LITE_JS); - html_source->AddResourcePath( "app-management/file_path.mojom-lite.js", IDR_OS_SETTINGS_APP_MANAGEMENT_FILE_PATH_MOJO_LITE_JS); html_source->AddResourcePath( "app-management/image.mojom-lite.js", IDR_OS_SETTINGS_APP_MANAGEMENT_IMAGE_MOJO_LITE_JS); - html_source->AddResourcePath( - "app-management/image_info.mojom-lite.js", - IDR_OS_SETTINGS_APP_MANAGEMENT_IMAGE_INFO_MOJO_LITE_JS); // We have 2 variants of Android apps settings. Default case, when the Play // Store app exists we show expandable section that allows as to @@ -220,6 +217,57 @@ void AppsSection::AddHandlers(content::WebUI* web_ui) { } } +int AppsSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_APPS_TITLE; +} + +mojom::Section AppsSection::GetSection() const { + return mojom::Section::kApps; +} + +mojom::SearchResultIcon AppsSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kAppsGrid; +} + +std::string AppsSection::GetSectionPath() const { + return mojom::kAppsSectionPath; +} + +void AppsSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // Manage apps. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_APPS_LINK_TEXT, + mojom::Subpage::kAppManagement, + mojom::SearchResultIcon::kAppsGrid, + mojom::SearchResultDefaultRank::kMedium, + mojom::kAppManagementSubpagePath); + // Note: The subpage name in the UI is updated dynamically based on the app + // being shown, but we use a generic "App details" string here. + generator->RegisterNestedSubpage( + IDS_SETTINGS_APP_DETAILS_TITLE, mojom::Subpage::kAppDetails, + mojom::Subpage::kAppManagement, mojom::SearchResultIcon::kAppsGrid, + mojom::SearchResultDefaultRank::kMedium, mojom::kAppDetailsSubpagePath); + generator->RegisterNestedSubpage(IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS, + mojom::Subpage::kPluginVmSharedPaths, + mojom::Subpage::kAppManagement, + mojom::SearchResultIcon::kAppsGrid, + mojom::SearchResultDefaultRank::kMedium, + mojom::kPluginVmSharedPathsSubpagePath); + + // Google Play Store. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_ANDROID_APPS_LABEL, + mojom::Subpage::kGooglePlayStore, + mojom::SearchResultIcon::kGooglePlay, + mojom::SearchResultDefaultRank::kMedium, + mojom::kGooglePlayStoreSubpagePath); + static constexpr mojom::Setting kGooglePlayStoreSettings[] = { + mojom::Setting::kManageAndroidPreferences, + mojom::Setting::kRemovePlayStore, + mojom::Setting::kTurnOnPlayStore, + }; + RegisterNestedSettingBulk(mojom::Subpage::kGooglePlayStore, + kGooglePlayStoreSettings, generator); +} + void AppsSection::OnAppRegistered(const std::string& app_id, const ArcAppListPrefs::AppInfo& app_info) { UpdateAndroidSearchTags(); @@ -264,34 +312,52 @@ void AppsSection::AddPluginVmLoadTimeData( IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE}, {"pluginVmSharedPathsRemoveSharing", IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING}, + {"pluginVmSharedPathsListEmptyMessage", + IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE}, + {"pluginVmPermissionDialogCameraLabel", + IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_CAMERA_LABEL}, + {"pluginVmPermissionDialogMicrophoneLabel", + IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_MICROPHONE_LABEL}, + {"pluginVmPermissionDialogRelaunchButton", + IDS_SETTINGS_APPS_PLUGIN_VM_PERMISSION_DIALOG_RELAUNCH_BUTTON}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); html_source->AddBoolean("showPluginVm", ShowPluginVm(profile(), *pref_service_)); + html_source->AddBoolean( + "showPluginVmCameraPermissions", + base::FeatureList::IsEnabled( + chromeos::features::kPluginVmShowCameraPermissions)); + html_source->AddBoolean( + "showPluginVmMicrophonePermissions", + base::FeatureList::IsEnabled( + chromeos::features::kPluginVmShowMicrophonePermissions)); } void AppsSection::UpdateAndroidSearchTags() { - registry()->RemoveSearchTags(GetAndroidNoPlayStoreSearchConcepts()); - registry()->RemoveSearchTags(GetAndroidPlayStoreDisabledSearchConcepts()); - registry()->RemoveSearchTags(GetAndroidPlayStoreSearchConcepts()); - registry()->RemoveSearchTags(GetAndroidSettingsSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + updater.RemoveSearchTags(GetAndroidNoPlayStoreSearchConcepts()); + updater.RemoveSearchTags(GetAndroidPlayStoreDisabledSearchConcepts()); + updater.RemoveSearchTags(GetAndroidPlayStoreSearchConcepts()); + updater.RemoveSearchTags(GetAndroidSettingsSearchConcepts()); if (!arc::IsPlayStoreAvailable()) { - registry()->AddSearchTags(GetAndroidNoPlayStoreSearchConcepts()); + updater.AddSearchTags(GetAndroidNoPlayStoreSearchConcepts()); return; } if (!arc::IsArcPlayStoreEnabledForProfile(profile())) { - registry()->AddSearchTags(GetAndroidPlayStoreDisabledSearchConcepts()); + updater.AddSearchTags(GetAndroidPlayStoreDisabledSearchConcepts()); return; } - registry()->AddSearchTags(GetAndroidPlayStoreSearchConcepts()); + updater.AddSearchTags(GetAndroidPlayStoreSearchConcepts()); if (arc_app_list_prefs_ && arc_app_list_prefs_->IsRegistered(arc::kSettingsAppId)) { - registry()->AddSearchTags(GetAndroidSettingsSearchConcepts()); + updater.AddSearchTags(GetAndroidSettingsSearchConcepts()); } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.h index ba926ba1652..d5217f6c693 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/apps_section.h @@ -33,6 +33,11 @@ class AppsSection : public OsSettingsSection, public ArcAppListPrefs::Observer { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // ArcAppListPrefs::Observer: void OnAppRegistered(const std::string& app_id, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc index 9b6fad55c74..2de04401ca6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.cc @@ -17,6 +17,8 @@ #include "chrome/grit/generated_resources.h" #include "content/public/browser/web_ui_data_source.h" #include "device/bluetooth/bluetooth_adapter_factory.h" +#include "device/bluetooth/bluetooth_device.h" +#include "device/bluetooth/chromeos/bluetooth_utils.h" #include "device/bluetooth/dbus/bluez_dbus_manager.h" #include "device/bluetooth/strings/grit/bluetooth_strings.h" #include "ui/base/l10n/l10n_util.h" @@ -28,37 +30,12 @@ namespace { const std::vector<SearchConcept>& GetBluetoothSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - {IDS_OS_SETTINGS_TAG_BLUETOOTH_PAIR, - mojom::kBluetoothDevicesSubpagePath, - mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kBluetoothPairDevice}}, - {IDS_OS_SETTINGS_TAG_BLUETOOTH_CONNECT, - mojom::kBluetoothDevicesSubpagePath, - mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kBluetoothConnectToDevice}}, {IDS_OS_SETTINGS_TAG_BLUETOOTH, mojom::kBluetoothDevicesSubpagePath, mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kBluetoothDevices}}, - {IDS_OS_SETTINGS_TAG_BLUETOOTH_DISCONNECT, - mojom::kBluetoothDevicesSubpagePath, - mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kBluetoothDisconnectFromDevice}}, - {IDS_OS_SETTINGS_TAG_BLUETOOTH_UNPAIR, - mojom::kBluetoothDevicesSubpagePath, - mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kBluetoothUnpairDevice}, - {IDS_OS_SETTINGS_TAG_BLUETOOTH_UNPAIR_ALT1, SearchConcept::kAltTagEnd}}, }); return *tags; } @@ -68,7 +45,7 @@ const std::vector<SearchConcept>& GetBluetoothOnSearchConcepts() { {IDS_OS_SETTINGS_TAG_BLUETOOTH_TURN_OFF, mojom::kBluetoothDevicesSubpagePath, mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kBluetoothOnOff}, {IDS_OS_SETTINGS_TAG_BLUETOOTH_TURN_OFF_ALT1, @@ -82,7 +59,7 @@ const std::vector<SearchConcept>& GetBluetoothOffSearchConcepts() { {IDS_OS_SETTINGS_TAG_BLUETOOTH_TURN_ON, mojom::kBluetoothDevicesSubpagePath, mojom::SearchResultIcon::kBluetooth, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kBluetoothOnOff}, {IDS_OS_SETTINGS_TAG_BLUETOOTH_TURN_ON_ALT1, SearchConcept::kAltTagEnd}}, @@ -90,6 +67,55 @@ const std::vector<SearchConcept>& GetBluetoothOffSearchConcepts() { return *tags; } +const std::vector<SearchConcept>& GetBluetoothConnectableSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_BLUETOOTH_CONNECT, + mojom::kBluetoothDevicesSubpagePath, + mojom::SearchResultIcon::kBluetooth, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kBluetoothConnectToDevice}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetBluetoothConnectedSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_BLUETOOTH_DISCONNECT, + mojom::kBluetoothDevicesSubpagePath, + mojom::SearchResultIcon::kBluetooth, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kBluetoothDisconnectFromDevice}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetBluetoothPairableSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_BLUETOOTH_PAIR, + mojom::kBluetoothDevicesSubpagePath, + mojom::SearchResultIcon::kBluetooth, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kBluetoothPairDevice}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetBluetoothPairedSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_BLUETOOTH_UNPAIR, + mojom::kBluetoothDevicesSubpagePath, + mojom::SearchResultIcon::kBluetooth, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kBluetoothUnpairDevice}, + {IDS_OS_SETTINGS_TAG_BLUETOOTH_UNPAIR_ALT1, SearchConcept::kAltTagEnd}}, + }); + return *tags; +} + } // namespace BluetoothSection::BluetoothSection(Profile* profile, @@ -165,6 +191,40 @@ void BluetoothSection::AddLoadTimeData(content::WebUIDataSource* html_source) { chromeos::bluetooth_dialog::AddLocalizedStrings(html_source); } +int BluetoothSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_BLUETOOTH; +} + +mojom::Section BluetoothSection::GetSection() const { + return mojom::Section::kBluetooth; +} + +mojom::SearchResultIcon BluetoothSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kBluetooth; +} + +std::string BluetoothSection::GetSectionPath() const { + return mojom::kBluetoothSectionPath; +} + +void BluetoothSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSubpage(IDS_SETTINGS_BLUETOOTH, + mojom::Subpage::kBluetoothDevices, + mojom::SearchResultIcon::kBluetooth, + mojom::SearchResultDefaultRank::kMedium, + mojom::kBluetoothDevicesSubpagePath); + static constexpr mojom::Setting kBluetoothDevicesSettings[] = { + mojom::Setting::kBluetoothOnOff, + mojom::Setting::kBluetoothConnectToDevice, + mojom::Setting::kBluetoothDisconnectFromDevice, + mojom::Setting::kBluetoothPairDevice, + mojom::Setting::kBluetoothUnpairDevice, + }; + RegisterNestedSettingBulk(mojom::Subpage::kBluetoothDevices, + kBluetoothDevicesSettings, generator); + generator->RegisterTopLevelAltSetting(mojom::Setting::kBluetoothOnOff); +} + void BluetoothSection::AdapterPresentChanged(device::BluetoothAdapter* adapter, bool present) { UpdateSearchTags(); @@ -175,6 +235,21 @@ void BluetoothSection::AdapterPoweredChanged(device::BluetoothAdapter* adapter, UpdateSearchTags(); } +void BluetoothSection::DeviceAdded(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) { + UpdateSearchTags(); +} + +void BluetoothSection::DeviceChanged(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) { + UpdateSearchTags(); +} + +void BluetoothSection::DeviceRemoved(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) { + UpdateSearchTags(); +} + void BluetoothSection::OnFetchBluetoothAdapter( scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) { bluetooth_adapter_ = bluetooth_adapter; @@ -183,20 +258,60 @@ void BluetoothSection::OnFetchBluetoothAdapter( } void BluetoothSection::UpdateSearchTags() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + // Start with no search tags, then add them below if appropriate. - registry()->RemoveSearchTags(GetBluetoothSearchConcepts()); - registry()->RemoveSearchTags(GetBluetoothOnSearchConcepts()); - registry()->RemoveSearchTags(GetBluetoothOffSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothOnSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothOffSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothConnectableSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothConnectedSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothPairableSearchConcepts()); + updater.RemoveSearchTags(GetBluetoothPairedSearchConcepts()); if (!bluetooth_adapter_->IsPresent()) return; - registry()->AddSearchTags(GetBluetoothSearchConcepts()); + updater.AddSearchTags(GetBluetoothSearchConcepts()); + + if (!bluetooth_adapter_->IsPowered()) { + updater.AddSearchTags(GetBluetoothOffSearchConcepts()); + return; + } + + updater.AddSearchTags(GetBluetoothOnSearchConcepts()); + + // Filter devices so that only those shown in the UI are returned. Note that + // passing |max_devices| of 0 indicates that there is no maximum. + device::BluetoothAdapter::DeviceList devices = + device::FilterBluetoothDeviceList(bluetooth_adapter_->GetDevices(), + device::BluetoothFilterType::KNOWN, + /*max_devices=*/0); + + bool connectable_device_exists = false; + bool connected_device_exists = false; + bool pairable_device_exists = false; + bool paired_device_exists = false; + for (const device::BluetoothDevice* device : devices) { + // Note: Device must be paired to be connectable. + if (device->IsPaired() && device->IsConnectable() && !device->IsConnected()) + connectable_device_exists = true; + if (device->IsConnected()) + connected_device_exists = true; + if (device->IsPairable() && !device->IsPaired()) + pairable_device_exists = true; + if (device->IsPaired()) + paired_device_exists = true; + } - if (bluetooth_adapter_->IsPowered()) - registry()->AddSearchTags(GetBluetoothOnSearchConcepts()); - else - registry()->AddSearchTags(GetBluetoothOffSearchConcepts()); + if (connectable_device_exists) + updater.AddSearchTags(GetBluetoothConnectableSearchConcepts()); + if (connected_device_exists) + updater.AddSearchTags(GetBluetoothConnectedSearchConcepts()); + if (pairable_device_exists) + updater.AddSearchTags(GetBluetoothPairableSearchConcepts()); + if (paired_device_exists) + updater.AddSearchTags(GetBluetoothPairedSearchConcepts()); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.h index 0586828bb1f..e187a244e2b 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/bluetooth_section.h @@ -31,12 +31,23 @@ class BluetoothSection : public OsSettingsSection, private: // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // device::BluetoothAdapter::Observer: void AdapterPresentChanged(device::BluetoothAdapter* adapter, bool present) override; void AdapterPoweredChanged(device::BluetoothAdapter* adapter, bool powered) override; + void DeviceAdded(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) override; + void DeviceChanged(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) override; + void DeviceRemoved(device::BluetoothAdapter* adapter, + device::BluetoothDevice* device) override; void OnFetchBluetoothAdapter( scoped_refptr<device::BluetoothAdapter> bluetooth_adapter); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc index 7c8d870bb91..5fd2a36e803 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.cc @@ -17,6 +17,7 @@ #include "chrome/browser/profiles/profile.h" #include "chromeos/cryptohome/cryptohome_util.h" #include "chromeos/dbus/cryptohome/cryptohome_client.h" +#include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "components/arc/arc_service_manager.h" #include "components/arc/session/arc_bridge_service.h" #include "components/arc/storage_manager/arc_storage_manager.h" @@ -392,6 +393,32 @@ void OtherUsersSizeCalculator::OnGetOtherUserSize( NotifySizeCalculated(other_users_total_bytes); } +DlcsSizeCalculator::DlcsSizeCalculator() + : SizeCalculator(CalculationType::kDlcs) {} + +DlcsSizeCalculator::~DlcsSizeCalculator() = default; + +void DlcsSizeCalculator::PerformCalculation() { + DlcserviceClient::Get()->GetExistingDlcs(base::BindOnce( + &DlcsSizeCalculator::OnGetExistingDlcs, weak_ptr_factory_.GetWeakPtr())); +} + +void DlcsSizeCalculator::OnGetExistingDlcs( + const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content) { + if (err != dlcservice::kErrorNone) { + NotifySizeCalculated(0); + return; + } + base::ListValue dlc_metadata_list; + int64_t dlc_total_size_in_bytes = 0; + for (int i = 0; i < dlcs_with_content.dlc_infos_size(); i++) { + const auto& dlc_info = dlcs_with_content.dlc_infos(i); + dlc_total_size_in_bytes += dlc_info.used_bytes_on_disk(); + } + NotifySizeCalculated(dlc_total_size_in_bytes); +} + } // namespace calculator } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h index 8f483f50b95..23fc718f6f6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator.h @@ -25,6 +25,10 @@ class Profile; +namespace dlcservice { +class DlcsWithContent; +} // namespace dlcservice + namespace chromeos { namespace settings { namespace calculator { @@ -42,7 +46,8 @@ class SizeCalculator { kAppsExtensions, kCrostini, kOtherUsers, - kLast = kOtherUsers, + kDlcs, + kLast = kDlcs, kSystem, }; @@ -289,6 +294,27 @@ class OtherUsersSizeCalculator : public SizeCalculator { base::WeakPtrFactory<OtherUsersSizeCalculator> weak_ptr_factory_{this}; }; +// Class handling the calculation of all DLC size. +class DlcsSizeCalculator : public SizeCalculator { + public: + DlcsSizeCalculator(); + ~DlcsSizeCalculator() override; + + DlcsSizeCalculator(const DlcsSizeCalculator&) = delete; + DlcsSizeCalculator& operator=(const DlcsSizeCalculator&) = delete; + + private: + friend class DlcsSizeTestAPI; + + void PerformCalculation() override; + + // Callback to update the total size of existing DLCs. + void OnGetExistingDlcs(const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content); + + base::WeakPtrFactory<DlcsSizeCalculator> weak_ptr_factory_{this}; +}; + } // namespace calculator } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h index 2e0850df244..4a15d946c26 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/calculator/size_calculator_test_api.h @@ -149,6 +149,25 @@ class OtherUsersSizeTestAPI { OtherUsersSizeCalculator* other_users_size_calculator_; }; +class DlcsSizeTestAPI { + public: + explicit DlcsSizeTestAPI(StorageHandler* handler, + DlcsSizeCalculator* dlcs_size_calculator) { + dlcs_size_calculator_ = dlcs_size_calculator; + dlcs_size_calculator_->AddObserver(handler); + } + + void StartCalculation() { dlcs_size_calculator_->StartCalculation(); } + + void SimulateOnGetExistingDlcs( + const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content) { + dlcs_size_calculator_->OnGetExistingDlcs(err, dlcs_with_content); + } + + private: + DlcsSizeCalculator* dlcs_size_calculator_; +}; } // namespace calculator } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/constants_util.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/constants_util.cc index d5bdaf20d53..263d760b558 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/constants_util.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/constants_util.cc @@ -17,7 +17,7 @@ std::vector<T> All() { int32_t max_value = static_cast<int32_t>(T::kMaxValue); std::vector<T> all; - for (int32_t i = min_value; i < max_value; ++i) { + for (int32_t i = min_value; i <= max_value; ++i) { T current = static_cast<T>(i); // Not every value between the min and max values is valid: diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom index b1837490f1c..ecb574c87e6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom @@ -49,6 +49,7 @@ enum Subpage { // MultiDevice section. kMultiDeviceFeatures = 200, kSmartLock = 201, + kNearbyShare = 202, // People section. kMyAccounts = 300, @@ -61,19 +62,19 @@ enum Subpage { kKerberos = 307, // Device section. - kDevice = 400, - kPointers = 401, - kKeyboard = 402, - kStylus = 403, - kDisplay = 404, - kStorage = 405, - kExternalStorage = 406, - kDlc = 407, - kPower = 408, + kPointers = 400, + kKeyboard = 401, + kStylus = 402, + kDisplay = 403, + kStorage = 404, + kExternalStorage = 405, + kDlc = 406, + kPower = 407, // Personalization section. kChangePicture = 500, kAmbientMode = 501, + kAmbientModePhotos= 502, // Search and Assistant section. kAssistant = 600, @@ -91,7 +92,6 @@ enum Subpage { kCrostiniBackupAndRestore = 803, kCrostiniDevelopAndroidApps = 804, kCrostiniPortForwarding = 805, - kCrostiniDiskResize = 806, // Note: Deprecated Plugin VM section has no subpages. @@ -104,6 +104,7 @@ enum Subpage { kLanguagesAndInputDetails = 1200, kManageInputMethods = 1201, kSmartInputs = 1202, + kInputMethodOptions = 1203, // Files section. kNetworkFileShares = 1300, @@ -143,6 +144,7 @@ const string kBluetoothDevicesSubpagePath = "bluetoothDevices"; const string kMultiDeviceSectionPath = "multidevice"; const string kMultiDeviceFeaturesSubpagePath = "multidevice/features"; const string kSmartLockSubpagePath = "multidevice/features/smartLock"; +const string kNearbyShareSubpagePath = "multidevice/nearbyshare"; // People section. const string kPeopleSectionPath = "osPeople"; @@ -151,7 +153,7 @@ const string kSyncSubpagePath = "osSync"; const string kSyncDeprecatedSubpagePath = "syncSetup"; const string kSyncDeprecatedAdvancedSubpagePath = "syncSetup/advanced"; const string kSecurityAndSignInSubpagePath = "lockScreen"; -const string kFingerprintSubpathPath = "lockScreen/fingerprint"; +const string kFingerprintSubpagePath = "lockScreen/fingerprint"; const string kManageOtherPeopleSubpagePath = "accounts"; const string kKerberosSubpagePath = "kerberosAccounts"; @@ -170,6 +172,7 @@ const string kPowerSubpagePath = "power"; const string kPersonalizationSectionPath = "personalization"; const string kChangePictureSubpagePath = "changePicture"; const string kAmbientModeSubpagePath = "ambientMode"; +const string kAmbientModePhotosSubpagePath = "ambientMode/photos"; // Search and Assistant section. const string kSearchAndAssistantSectionPath = "osSearch"; @@ -191,7 +194,6 @@ const string kCrostiniUsbPreferencesSubpagePath = "crostini/sharedUsbDevices"; const string kCrostiniBackupAndRestoreSubpagePath = "crostini/exportImport"; const string kCrostiniDevelopAndroidAppsSubpagePath = "crostini/androidAdb"; const string kCrostiniPortForwardingSubpagePath = "crostini/portForwarding"; -const string kCrostiniDiskResizeSubpagePath = "crostini/diskResize"; // Date and Time section. const string kDateAndTimeSectionPath = "dateTime"; @@ -204,7 +206,8 @@ const string kPrivacyAndSecuritySectionPath = "osPrivacy"; const string kLanguagesAndInputSectionPath = "osLanguages"; const string kLanguagesAndInputDetailsSubpagePath = "osLanguages/details"; const string kManageInputMethodsSubpagePath = "osLanguages/inputMethods"; -const string kSmartInputsSubagePath = "osLanguages/smartInputs"; +const string kSmartInputsSubpagePath = "osLanguages/smartInputs"; +const string kInputMethodOptionsSubpagePath = "osLanguages/inputMethodOptions"; // Files section. const string kFilesSectionPath = "files"; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc index 957b28fd7cc..d2fa7499076 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/routes_util.cc @@ -37,13 +37,14 @@ bool IsOSSettingsSubPage(const std::string& sub_page) { chromeos::settings::mojom::kMultiDeviceSectionPath, chromeos::settings::mojom::kMultiDeviceFeaturesSubpagePath, chromeos::settings::mojom::kSmartLockSubpagePath, + chromeos::settings::mojom::kNearbyShareSubpagePath, // People section. chromeos::settings::mojom::kPeopleSectionPath, chromeos::settings::mojom::kMyAccountsSubpagePath, chromeos::settings::mojom::kSyncSubpagePath, chromeos::settings::mojom::kSecurityAndSignInSubpagePath, - chromeos::settings::mojom::kFingerprintSubpathPath, + chromeos::settings::mojom::kFingerprintSubpagePath, chromeos::settings::mojom::kManageOtherPeopleSubpagePath, chromeos::settings::mojom::kKerberosSubpagePath, @@ -82,7 +83,6 @@ bool IsOSSettingsSubPage(const std::string& sub_page) { chromeos::settings::mojom::kCrostiniBackupAndRestoreSubpagePath, chromeos::settings::mojom::kCrostiniDevelopAndroidAppsSubpagePath, chromeos::settings::mojom::kCrostiniPortForwardingSubpagePath, - chromeos::settings::mojom::kCrostiniDiskResizeSubpagePath, // Date and Time section. chromeos::settings::mojom::kDateAndTimeSectionPath, @@ -95,7 +95,8 @@ bool IsOSSettingsSubPage(const std::string& sub_page) { chromeos::settings::mojom::kLanguagesAndInputSectionPath, chromeos::settings::mojom::kLanguagesAndInputDetailsSubpagePath, chromeos::settings::mojom::kManageInputMethodsSubpagePath, - chromeos::settings::mojom::kSmartInputsSubagePath, + chromeos::settings::mojom::kSmartInputsSubpagePath, + chromeos::settings::mojom::kInputMethodOptionsSubpagePath, // Files section. chromeos::settings::mojom::kFilesSectionPath, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom index 634b625b075..19339269f3b 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom @@ -16,7 +16,7 @@ enum Setting { kDisconnectWifiNetwork = 5, kPreferWifiNetwork = 6, kForgetWifiNetwork = 7, - kConfigureWifi = 8, + kWifiAddNetwork = 8, kWifiAutoConfigureIp = 9, kWifiDns = 10, kWifiProxy = 11, @@ -31,7 +31,9 @@ enum Setting { kCellularProxy = 20, kCellularAutoConnectToNetwork = 21, kInstantTetheringOnOff = 22, - kDisconnectTetherNetwork = 22, + kDisconnectTetherNetwork = 23, + kWifiMetered = 24, + kCellularMetered = 25, // Bluetooth section. kBluetoothOnOff = 100, @@ -49,11 +51,12 @@ enum Setting { kMessagesSetUp = 205, kMessagesOnOff = 206, kForgetPhone = 207, + kNearbyShareOnOff = 208, // People section. kAddAccount = 300, kRemoveAccount = 301, - kSyncOnOff = 302, + kSplitSyncOnOff = 302, kLockScreen = 303, kChangeAuthPin = 304, kGuestBrowsing = 305, @@ -67,6 +70,10 @@ enum Setting { kAddFingerprint = 313, kRemoveFingerprint = 314, kSetUpParentalControls = 315, + kNonSplitSyncEncryptionOptions = 316, + kAutocompleteSearchesAndUrls = 317, + kMakeSearchesAndBrowsingBetter = 318, + kGoogleDriveSearchSuggestions = 319, // Device section. kTouchpadTapToClick = 400, @@ -91,16 +98,26 @@ enum Setting { kStylusLatestNoteOnLockScreen = 419, kDisplayOrientation = 420, kDisplayArrangement = 421, - kPowerIdleBehavior = 422, + kPowerIdleBehaviorWhileCharging = 422, kPowerSource = 423, kSleepWhenLaptopLidClosed = 424, kDisplayResolution = 425, kDisplayRefreshRate = 426, + kRemoveDlc = 427, + kDisplayMirroring = 428, + kAllowWindowsToSpanDisplays = 429, + kAmbientColors = 430, + kTouchscreenCalibration = 431, + kNightLightColorTemperature = 432, + kPowerIdleBehaviorWhileOnBattery = 433, + kDisplayOverscan = 434, // Personalization section. kOpenWallpaper = 500, kAmbientModeOnOff = 501, kAmbientModeSource = 502, + kChangeDeviceAccountImage = 503, + kAmbientModeUpdatePhotosContainers = 504, // Search and Assistant section. kPreferredSearchEngine = 600, @@ -110,6 +127,7 @@ enum Setting { kAssistantOkGoogle = 604, kAssistantNotifications = 605, kAssistantVoiceInput = 606, + kTrainAssistantVoiceModel = 607, // Apps section. kManageAndroidPreferences = 700, @@ -141,6 +159,7 @@ enum Setting { kAddLanguage = 1200, kShowInputOptionsInShelf = 1201, kShowPersonalInformationSuggestions = 1202, + kShowEmojiSuggestions = 1203, // Files section. kGoogleDriveConnection = 1300, @@ -178,6 +197,8 @@ enum Setting { kSwitchActionAutoScan = 1524, kSwitchActionAutoScanKeyboard = 1525, kGetImageDescriptionsFromGoogle = 1526, + kLiveCaptions = 1527, + kEnableCursorColor = 1528, // Reset section. kPowerwash = 1600, @@ -185,4 +206,9 @@ enum Setting { // About Chrome OS section. kChangeChromeChannel = 1700, kCopyDetailedBuildInfo = 1701, + kCheckForOsUpdate = 1702, + kSeeWhatsNew = 1703, + kGetHelpWithChromeOs = 1704, + kReportAnIssue = 1705, + kTermsOfService = 1706, }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc index bf1d6db4c78..5c934d15a17 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.cc @@ -104,6 +104,11 @@ void CrostiniHandler::RegisterMessages() { base::BindRepeating(&CrostiniHandler::HandleQueryArcAdbRequest, weak_ptr_factory_.GetWeakPtr())); web_ui()->RegisterMessageCallback( + "getCanChangeArcAdbSideloading", + base::BindRepeating( + &CrostiniHandler::HandleCanChangeArcAdbSideloadingRequest, + weak_ptr_factory_.GetWeakPtr())); + web_ui()->RegisterMessageCallback( "enableArcAdbSideload", base::BindRepeating(&CrostiniHandler::HandleEnableArcAdbRequest, weak_ptr_factory_.GetWeakPtr())); @@ -191,6 +196,20 @@ void CrostiniHandler::OnJavascriptAllowed() { } crostini::CrostiniExportImport::GetForProfile(profile_)->AddObserver(this); crostini::CrostiniPortForwarder::GetForProfile(profile_)->AddObserver(this); + + // Observe ADB sideloading device policy and react to its changes + adb_sideloading_device_policy_subscription_ = + chromeos::CrosSettings::Get()->AddSettingsObserver( + chromeos::kDeviceCrostiniArcAdbSideloadingAllowed, + base::BindRepeating(&CrostiniHandler::FetchCanChangeAdbSideloading, + weak_ptr_factory_.GetWeakPtr())); + + // Observe ADB sideloading user policy and react to its changes + pref_change_registrar_.Init(profile_->GetPrefs()); + pref_change_registrar_.Add( + crostini::prefs::kCrostiniArcAdbSideloadingUserPref, + base::BindRepeating(&CrostiniHandler::FetchCanChangeAdbSideloading, + weak_ptr_factory_.GetWeakPtr())); } void CrostiniHandler::OnJavascriptDisallowed() { @@ -206,6 +225,9 @@ void CrostiniHandler::OnJavascriptDisallowed() { crostini::CrostiniExportImport::GetForProfile(profile_)->RemoveObserver(this); crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemoveObserver( this); + + adb_sideloading_device_policy_subscription_.reset(); + pref_change_registrar_.RemoveAll(); } void CrostiniHandler::HandleRequestCrostiniInstallerView( @@ -437,7 +459,15 @@ void CrostiniHandler::OnQueryAdbSideload( void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) { CHECK_EQ(0U, args->GetList().size()); - if (!CheckEligibilityToChangeArcAdbSideloading()) + + crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading( + profile_, base::BindOnce(&CrostiniHandler::OnCanEnableArcAdbSideloading, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniHandler::OnCanEnableArcAdbSideloading( + bool can_change_adb_sideloading) { + if (!can_change_adb_sideloading) return; LogEvent(CrostiniSettingsEvent::kEnableAdbSideloading); @@ -451,7 +481,15 @@ void CrostiniHandler::HandleEnableArcAdbRequest(const base::ListValue* args) { void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) { CHECK_EQ(0U, args->GetList().size()); - if (!CheckEligibilityToChangeArcAdbSideloading()) + + crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading( + profile_, base::BindOnce(&CrostiniHandler::OnCanDisableArcAdbSideloading, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniHandler::OnCanDisableArcAdbSideloading( + bool can_change_adb_sideloading) { + if (!can_change_adb_sideloading) return; LogEvent(CrostiniSettingsEvent::kDisableAdbSideloading); @@ -464,10 +502,6 @@ void CrostiniHandler::HandleDisableArcAdbRequest(const base::ListValue* args) { power_manager::REQUEST_RESTART_FOR_USER, "disable adb sideloading"); } -bool CrostiniHandler::CheckEligibilityToChangeArcAdbSideloading() const { - return crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(profile_); -} - void CrostiniHandler::LaunchTerminal() { crostini::LaunchCrostiniApp( profile_, crostini::GetTerminalId(), @@ -502,6 +536,26 @@ void CrostiniHandler::HandleQueryArcAdbRequest(const base::ListValue* args) { weak_ptr_factory_.GetWeakPtr())); } +void CrostiniHandler::HandleCanChangeArcAdbSideloadingRequest( + const base::ListValue* args) { + AllowJavascript(); + CHECK_EQ(0U, args->GetList().size()); + + FetchCanChangeAdbSideloading(); +} + +void CrostiniHandler::FetchCanChangeAdbSideloading() { + crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading( + profile_, base::BindOnce(&CrostiniHandler::OnCanChangeArcAdbSideloading, + weak_ptr_factory_.GetWeakPtr())); +} + +void CrostiniHandler::OnCanChangeArcAdbSideloading( + bool can_change_arc_adb_sideloading) { + FireWebUIListener("crostini-can-change-arc-adb-sideload-changed", + base::Value(can_change_arc_adb_sideloading)); +} + void CrostiniHandler::HandleCrostiniUpgraderDialogStatusRequest( const base::ListValue* args) { AllowJavascript(); @@ -536,6 +590,11 @@ void CrostiniHandler::HandleAddCrostiniPortForward( int protocol_type = args->GetList()[4].GetInt(); std::string label = args->GetList()[5].GetString(); + if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) { + OnPortForwardComplete(callback_id, false); + return; + } + crostini::CrostiniPortForwarder::GetForProfile(profile_)->AddPort( crostini::ContainerId(std::move(vm_name), std::move(container_name)), port_number, @@ -559,6 +618,11 @@ void CrostiniHandler::HandleRemoveCrostiniPortForward( int protocol_type; CHECK(args->GetInteger(4, &protocol_type)); + if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) { + OnPortForwardComplete(callback_id, false); + return; + } + crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemovePort( crostini::ContainerId(std::move(vm_name), std::move(container_name)), port_number, @@ -574,6 +638,10 @@ void CrostiniHandler::HandleRemoveAllCrostiniPortForwards( std::string vm_name = args_list[0].GetString(); std::string container_name = args_list[1].GetString(); + if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) { + return; + } + crostini::CrostiniPortForwarder::GetForProfile(profile_)->RemoveAllPorts( crostini::ContainerId(std::move(vm_name), std::move(container_name))); } @@ -593,6 +661,11 @@ void CrostiniHandler::HandleActivateCrostiniPortForward( int protocol_type; CHECK(args->GetInteger(4, &protocol_type)); + if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) { + OnPortForwardComplete(callback_id, false); + return; + } + crostini::CrostiniPortForwarder::GetForProfile(profile_)->ActivatePort( crostini::ContainerId(std::move(vm_name), std::move(container_name)), port_number, @@ -616,6 +689,11 @@ void CrostiniHandler::HandleDeactivateCrostiniPortForward( int protocol_type; CHECK(args->GetInteger(4, &protocol_type)); + if (!crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile_)) { + OnPortForwardComplete(callback_id, false); + return; + } + crostini::CrostiniPortForwarder::GetForProfile(profile_)->DeactivatePort( crostini::ContainerId(std::move(vm_name), std::move(container_name)), port_number, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h index b1f5b8162bc..5867245850c 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_handler.h @@ -11,9 +11,11 @@ #include "chrome/browser/chromeos/crostini/crostini_export_import.h" #include "chrome/browser/chromeos/crostini/crostini_manager.h" #include "chrome/browser/chromeos/crostini/crostini_port_forwarder.h" +#include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/usb/cros_usb_detector.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chromeos/dbus/session_manager/session_manager_client.h" +#include "components/prefs/pref_change_registrar.h" class Profile; @@ -83,8 +85,14 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void HandleQueryArcAdbRequest(const base::ListValue* args); // Handle a request for enabling adb sideloading in ARC. void HandleEnableArcAdbRequest(const base::ListValue* args); + // Called after establishing whether enabling adb sideloading is allowed for + // the user and device + void OnCanEnableArcAdbSideloading(bool can_change_adb_sideloading); // Handle a request for disabling adb sideloading in ARC. void HandleDisableArcAdbRequest(const base::ListValue* args); + // Called after establishing whether disabling adb sideloading is allowed for + // the user and device + void OnCanDisableArcAdbSideloading(bool can_change_adb_sideloading); // Launch the Crostini terminal. void LaunchTerminal(); // Handle a request for showing the container upgrade view. @@ -93,9 +101,6 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void OnQueryAdbSideload( SessionManagerClient::AdbSideloadResponseCode response_code, bool enabled); - // Returns whether the current user can change adb sideloading configuration - // on current device. - bool CheckEligibilityToChangeArcAdbSideloading() const; // Handle a request for the CrostiniUpgraderDialog status. void HandleCrostiniUpgraderDialogStatusRequest(const base::ListValue* args); // Handle a request for the availability of a container upgrade. @@ -142,8 +147,17 @@ class CrostiniHandler : public ::settings::SettingsPageUIHandler, void HandleSetCrostiniMicSharingEnabled(const base::ListValue* args); // Handles a request for getting the permissions for Crostini Mic access. void HandleGetCrostiniMicSharingEnabled(const base::ListValue* args); + // Handle a request for checking permission for changing ARC adb sideloading. + void HandleCanChangeArcAdbSideloadingRequest(const base::ListValue* args); + // Get permission of changing ARC adb sideloading + void FetchCanChangeAdbSideloading(); + // Callback of FetchCanChangeAdbSideloading. + void OnCanChangeArcAdbSideloading(bool can_change_arc_adb_sideloading); Profile* profile_; + std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> + adb_sideloading_device_policy_subscription_; + PrefChangeRegistrar pref_change_registrar_; // weak_ptr_factory_ should always be last member. base::WeakPtrFactory<CrostiniHandler> weak_ptr_factory_{this}; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc index f25ce803272..546c2ff9270 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.cc @@ -36,21 +36,15 @@ namespace chromeos { namespace settings { namespace { -const std::vector<SearchConcept>& GetCrostiniSearchConcepts() { +const std::vector<SearchConcept>& GetCrostiniOptedInSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_CROSTINI, mojom::kCrostiniDetailsSubpagePath, mojom::SearchResultIcon::kPenguin, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kHigh, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kCrostiniDetails}, {IDS_OS_SETTINGS_TAG_CROSTINI_ALT1, SearchConcept::kAltTagEnd}}, - }); - return *tags; -} - -const std::vector<SearchConcept>& GetCrostiniOptedInSearchConcepts() { - static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_CROSTINI_USB_PREFERENCES, mojom::kCrostiniUsbPreferencesSubpagePath, mojom::SearchResultIcon::kPenguin, @@ -88,6 +82,13 @@ const std::vector<SearchConcept>& GetCrostiniOptedInSearchConcepts() { const std::vector<SearchConcept>& GetCrostiniOptedOutSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_CROSTINI, + mojom::kCrostiniSectionPath, + mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSection, + {.section = mojom::Section::kCrostini}, + {IDS_OS_SETTINGS_TAG_CROSTINI_ALT1, SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_CROSTINI_SETUP, mojom::kCrostiniSectionPath, mojom::SearchResultIcon::kPenguin, @@ -205,10 +206,6 @@ bool IsAdbSideloadingAllowed() { return base::FeatureList::IsEnabled(features::kArcAdbSideloadingFeature); } -bool IsPortForwardingAllowed() { - return base::FeatureList::IsEnabled(features::kCrostiniPortForwarding); -} - bool IsDiskResizingAllowed() { return base::FeatureList::IsEnabled(features::kCrostiniDiskResizing); } @@ -320,16 +317,22 @@ void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) { IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_BUTTON_DESCRIPTION}, {"crostiniPortForwardingAddPortDialogTitle", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_TITLE}, - {"crostiniPortForwardingAddPortDialogLabel", - IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL}, + {"crostiniPortForwardingAddPortDialogPortNumberLabel", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_PORT_NUMBER_LABEL}, + {"crostiniPortForwardingAddPortDialogLabelLabel", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_PORT_DIALOG_LABEL_LABEL}, {"crostiniPortForwardingTCP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TCP}, {"crostiniPortForwardingUDP", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_UDP}, {"crostiniPortForwardingAddError", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_ERROR}, + {"crostiniPortForwardingAddExisting", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ADD_EXISTING}, {"crostiniPortForwardingRemoveAllPorts", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_ALL_PORTS}, {"crostiniPortForwardingRemovePort", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_REMOVE_PORT}, + {"crostiniPortForwardingActivatePortError", + IDS_SETTINGS_CROSTINI_PORT_FORWARDING_ACTIVATE_PORT_ERROR}, {"crostiniPortForwardingToggleAriaLabel", IDS_SETTINGS_CROSTINI_PORT_FORWARDING_TOGGLE_PORT_ARIA_LABEL}, {"crostiniPortForwardingRemoveAllPortsAriaLabel", @@ -432,9 +435,6 @@ void CrostiniSection::AddLoadTimeData(content::WebUIDataSource* html_source) { chromeos::ProfileHelper::IsOwnerProfile(profile())); html_source->AddBoolean("isEnterpriseManaged", IsDeviceManaged() || IsProfileManaged(profile())); - html_source->AddBoolean( - "canChangeAdbSideloading", - crostini::CrostiniFeatures::Get()->CanChangeAdbSideloading(profile())); html_source->AddBoolean("showCrostiniContainerUpgrade", IsContainerUpgradeAllowed()); html_source->AddBoolean("showCrostiniDiskResize", IsDiskResizingAllowed()); @@ -448,6 +448,89 @@ void CrostiniSection::AddHandlers(content::WebUI* web_ui) { } } +int CrostiniSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_CROSTINI_TITLE; +} + +mojom::Section CrostiniSection::GetSection() const { + return mojom::Section::kCrostini; +} + +mojom::SearchResultIcon CrostiniSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kPenguin; +} + +std::string CrostiniSection::GetSectionPath() const { + return mojom::kCrostiniSectionPath; +} + +void CrostiniSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kSetUpCrostini); + + // Crostini details. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_CROSTINI_LABEL, + mojom::Subpage::kCrostiniDetails, + mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniDetailsSubpagePath); + static constexpr mojom::Setting kCrostiniDetailsSettings[] = { + mojom::Setting::kCrostiniContainerUpgrade, + mojom::Setting::kCrostiniDiskResize, + mojom::Setting::kCrostiniMicAccess, + mojom::Setting::kUninstallCrostini, + }; + RegisterNestedSettingBulk(mojom::Subpage::kCrostiniDetails, + kCrostiniDetailsSettings, generator); + + // Manage shared folders. + generator->RegisterNestedSubpage( + IDS_SETTINGS_CROSTINI_SHARED_PATHS, + mojom::Subpage::kCrostiniManageSharedFolders, + mojom::Subpage::kCrostiniDetails, mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniManageSharedFoldersSubpagePath); + + // USB preferences. + generator->RegisterNestedSubpage( + IDS_SETTINGS_CROSTINI_SHARED_USB_DEVICES_LABEL, + mojom::Subpage::kCrostiniUsbPreferences, mojom::Subpage::kCrostiniDetails, + mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniUsbPreferencesSubpagePath); + + // Backup and restore. + generator->RegisterNestedSubpage(IDS_SETTINGS_CROSTINI_EXPORT_IMPORT_TITLE, + mojom::Subpage::kCrostiniBackupAndRestore, + mojom::Subpage::kCrostiniDetails, + mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniBackupAndRestoreSubpagePath); + static constexpr mojom::Setting kCrostiniBackupAndRestoreSettings[] = { + mojom::Setting::kBackupLinuxAppsAndFiles, + mojom::Setting::kRestoreLinuxAppsAndFiles, + }; + RegisterNestedSettingBulk(mojom::Subpage::kCrostiniBackupAndRestore, + kCrostiniBackupAndRestoreSettings, generator); + + // Develop Android apps. + generator->RegisterNestedSubpage( + IDS_SETTINGS_CROSTINI_ARC_ADB_TITLE, + mojom::Subpage::kCrostiniDevelopAndroidApps, + mojom::Subpage::kCrostiniDetails, mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniDevelopAndroidAppsSubpagePath); + generator->RegisterNestedSetting(mojom::Setting::kCrostiniAdbDebugging, + mojom::Subpage::kCrostiniDevelopAndroidApps); + + // Port forwarding. + generator->RegisterNestedSubpage(IDS_SETTINGS_CROSTINI_PORT_FORWARDING, + mojom::Subpage::kCrostiniPortForwarding, + mojom::Subpage::kCrostiniDetails, + mojom::SearchResultIcon::kPenguin, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCrostiniPortForwardingSubpagePath); +} + bool CrostiniSection::IsCrostiniAllowed() { return crostini::CrostiniFeatures::Get()->IsUIAllowed(profile(), /*check_policy=*/false); @@ -461,48 +544,51 @@ bool CrostiniSection::IsContainerUpgradeAllowed() { return crostini::ShouldAllowContainerUpgrade(profile()); } +bool CrostiniSection::IsPortForwardingAllowed() { + return crostini::CrostiniFeatures::Get()->IsPortForwardingAllowed(profile()); +} + void CrostiniSection::UpdateSearchTags() { - registry()->RemoveSearchTags(GetCrostiniSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniOptedInSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniOptedOutSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniExportImportSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniAdbSideloadingSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniPortForwardingSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniContainerUpgradeSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniDiskResizingSearchConcepts()); - registry()->RemoveSearchTags(GetCrostiniMicSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + updater.RemoveSearchTags(GetCrostiniOptedInSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniOptedOutSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniExportImportSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniAdbSideloadingSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniPortForwardingSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniContainerUpgradeSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniDiskResizingSearchConcepts()); + updater.RemoveSearchTags(GetCrostiniMicSearchConcepts()); if (!IsCrostiniAllowed()) return; - registry()->AddSearchTags(GetCrostiniSearchConcepts()); - if (!pref_service_->GetBoolean(crostini::prefs::kCrostiniEnabled)) { - registry()->AddSearchTags(GetCrostiniOptedOutSearchConcepts()); + updater.AddSearchTags(GetCrostiniOptedOutSearchConcepts()); return; } - registry()->AddSearchTags(GetCrostiniOptedInSearchConcepts()); + updater.AddSearchTags(GetCrostiniOptedInSearchConcepts()); if (IsExportImportAllowed()) - registry()->AddSearchTags(GetCrostiniExportImportSearchConcepts()); + updater.AddSearchTags(GetCrostiniExportImportSearchConcepts()); if (IsAdbSideloadingAllowed() && pref_service_->GetBoolean(arc::prefs::kArcEnabled)) { - registry()->AddSearchTags(GetCrostiniAdbSideloadingSearchConcepts()); + updater.AddSearchTags(GetCrostiniAdbSideloadingSearchConcepts()); } if (IsPortForwardingAllowed()) - registry()->AddSearchTags(GetCrostiniPortForwardingSearchConcepts()); + updater.AddSearchTags(GetCrostiniPortForwardingSearchConcepts()); if (IsContainerUpgradeAllowed()) - registry()->AddSearchTags(GetCrostiniContainerUpgradeSearchConcepts()); + updater.AddSearchTags(GetCrostiniContainerUpgradeSearchConcepts()); if (IsDiskResizingAllowed()) - registry()->AddSearchTags(GetCrostiniDiskResizingSearchConcepts()); + updater.AddSearchTags(GetCrostiniDiskResizingSearchConcepts()); if (IsMicSettingAllowed()) - registry()->AddSearchTags(GetCrostiniMicSearchConcepts()); + updater.AddSearchTags(GetCrostiniMicSearchConcepts()); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.h index ee51c4be36f..e7caf6a65ea 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/crostini_section.h @@ -31,10 +31,16 @@ class CrostiniSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; bool IsCrostiniAllowed(); bool IsExportImportAllowed(); bool IsContainerUpgradeAllowed(); + bool IsPortForwardingAllowed(); void UpdateSearchTags(); PrefService* pref_service_; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc index 6031f1951fa..74b1ac7cc36 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler.cc @@ -27,6 +27,7 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chromeos/printing/cups_printers_manager.h" #include "chrome/browser/chromeos/printing/ppd_provider_factory.h" +#include "chrome/browser/chromeos/printing/print_management/print_management_uma.h" #include "chrome/browser/chromeos/printing/printer_configurer.h" #include "chrome/browser/chromeos/printing/printer_event_tracker.h" #include "chrome/browser/chromeos/printing/printer_event_tracker_factory.h" @@ -241,15 +242,15 @@ Printer::PpdReference GetPpdReference(const base::Value* info) { Printer::PpdReference ret; - if (user_supplied_ppd_url != nullptr) { + if (user_supplied_ppd_url) { ret.user_supplied_ppd_url = user_supplied_ppd_url->GetString(); } - if (effective_make_and_model != nullptr) { + if (effective_make_and_model) { ret.effective_make_and_model = effective_make_and_model->GetString(); } - if (autoconf != nullptr) { + if (autoconf) { ret.autoconf = autoconf->GetBool(); } @@ -998,6 +999,7 @@ void CupsPrintersHandler::HandleStartDiscovery(const base::ListValue* args) { UMA_HISTOGRAM_COUNTS_100( "Printing.CUPS.PrintersDiscovered", discovered_printers_.size() + automatic_printers_.size()); + printers_manager_->RecordNearbyNetworkPrinterCounts(); // Scan completes immediately right now. Emit done. FireWebUIListener("on-printer-discovery-done"); } @@ -1328,7 +1330,8 @@ void CupsPrintersHandler::HandleOpenPrintManagementApp( DCHECK(args->empty()); DCHECK( base::FeatureList::IsEnabled(chromeos::features::kPrintJobManagementApp)); - chrome::ShowPrintManagementApp(profile_); + chrome::ShowPrintManagementApp(profile_, + PrintManagementAppEntryPoint::kSettings); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc index 36193fc66fa..00cd8e56cb4 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/cups_printers_handler_unittest.cc @@ -10,13 +10,17 @@ #include "base/bind_helpers.h" #include "base/files/file_path.h" #include "base/json/json_string_value_serializer.h" +#include "base/test/metrics/histogram_tester.h" +#include "base/test/scoped_feature_list.h" #include "base/values.h" +#include "chrome/browser/chromeos/printing/print_management/print_management_uma.h" #include "chrome/browser/chromeos/printing/printing_stubs.h" #include "chrome/browser/download/chrome_download_manager_delegate.h" #include "chrome/browser/download/download_core_service_factory.h" #include "chrome/browser/download/download_core_service_impl.h" #include "chrome/browser/ui/chrome_select_file_policy.h" #include "chrome/test/base/testing_profile.h" +#include "chromeos/constants/chromeos_features.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/debug_daemon/debug_daemon_client.h" #include "content/public/test/browser_task_environment.h" @@ -192,6 +196,8 @@ class CupsPrintersHandlerTest : public testing::Test { ~CupsPrintersHandlerTest() override = default; void SetUp() override { + scoped_feature_list_.InitWithFeatures( + {chromeos::features::kPrintJobManagementApp}, {}); printers_handler_ = CupsPrintersHandler::CreateForTesting( &profile_, base::MakeRefCounted<FakePpdProvider>(), std::make_unique<StubPrinterConfigurer>(), &printers_manager_); @@ -201,11 +207,13 @@ class CupsPrintersHandlerTest : public testing::Test { protected: // Must outlive |profile_|. + base::HistogramTester histogram_tester_; content::BrowserTaskEnvironment task_environment_; TestingProfile profile_; content::TestWebUI web_ui_; std::unique_ptr<CupsPrintersHandler> printers_handler_; TestCupsPrintersManager printers_manager_; + base::test::ScopedFeatureList scoped_feature_list_; }; TEST_F(CupsPrintersHandlerTest, RemoveCorrectPrinter) { @@ -253,5 +261,24 @@ TEST_F(CupsPrintersHandlerTest, VerifyOnlyPpdFilesAllowed) { web_ui_.HandleReceivedMessage("selectPPDFile", &base::Value::AsListValue(args)); } + +TEST_F(CupsPrintersHandlerTest, VerifyPrintManagementAppEntryPointHistogram) { + base::Value args(base::Value::Type::LIST); + web_ui_.HandleReceivedMessage("openPrintManagementApp", + &base::Value::AsListValue(args)); + histogram_tester_.ExpectBucketCount( + "Printing.Cups.PrintManagementAppEntryPoint", + PrintManagementAppEntryPoint::kSettings, 1); + histogram_tester_.ExpectBucketCount( + "Printing.Cups.PrintManagementAppEntryPoint", + PrintManagementAppEntryPoint::kNotification, 0); + histogram_tester_.ExpectBucketCount( + "Printing.Cups.PrintManagementAppEntryPoint", + PrintManagementAppEntryPoint::kLauncher, 0); + histogram_tester_.ExpectBucketCount( + "Printing.Cups.PrintManagementAppEntryPoint", + PrintManagementAppEntryPoint::kBrowser, 0); +} + } // namespace settings. } // namespace chromeos. diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc index dac0db6e52d..0c1df37cc1e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_handler.cc @@ -58,7 +58,7 @@ bool IsTimezoneAutomaticDetectionUserEditable() { if (IsSystemTimezoneAutomaticDetectionManaged()) { return GetSystemTimezoneAutomaticDetectionPolicyValue() == - enterprise_management::SystemTimezoneProto::USERS_DECIDE; + enterprise_management::SystemTimezoneProto::USERS_DECIDE; } return true; @@ -156,7 +156,7 @@ void DateTimeHandler::HandleShowParentAccessForTimeZone( base::BindOnce(&DateTimeHandler::OnParentAccessValidation, weak_ptr_factory_.GetWeakPtr()), ash::ParentAccessRequestReason::kChangeTimezone, false /* extra_dimmer */, - base::Time()); + base::Time::Now()); } void DateTimeHandler::OnParentAccessValidation(bool success) { @@ -166,10 +166,9 @@ void DateTimeHandler::OnParentAccessValidation(bool success) { void DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy() { bool managed = !IsTimezoneAutomaticDetectionUserEditable(); - bool force_enabled = managed && - g_browser_process->platform_part() - ->GetTimezoneResolverManager() - ->ShouldApplyResolvedTimezone(); + bool force_enabled = managed && g_browser_process->platform_part() + ->GetTimezoneResolverManager() + ->ShouldApplyResolvedTimezone(); FireWebUIListener("time-zone-auto-detect-policy", base::Value(managed), base::Value(force_enabled)); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc index 529689f68dc..8899d28d0e1 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.cc @@ -19,6 +19,7 @@ #include "chromeos/settings/cros_settings_names.h" #include "chromeos/settings/system_settings_provider.h" #include "chromeos/settings/timezone_settings.h" +#include "components/user_manager/user_manager.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" @@ -71,18 +72,23 @@ const std::vector<SearchConcept>& GetNoFineGrainedTimeZoneSearchConcepts() { return *tags; } +bool IsFineGrainedTimeZoneEnabled() { + SystemSettingsProvider provider; + return provider.Get(chromeos::kFineGrainedTimeZoneResolveEnabled)->GetBool(); +} + } // namespace DateTimeSection::DateTimeSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetDateTimeSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetDateTimeSearchConcepts()); - SystemSettingsProvider provider; - if (provider.Get(chromeos::kFineGrainedTimeZoneResolveEnabled)->GetBool()) - registry()->AddSearchTags(GetFineGrainedTimeZoneSearchConcepts()); + if (IsFineGrainedTimeZoneEnabled()) + updater.AddSearchTags(GetFineGrainedTimeZoneSearchConcepts()); else - registry()->AddSearchTags(GetNoFineGrainedTimeZoneSearchConcepts()); + updater.AddSearchTags(GetNoFineGrainedTimeZoneSearchConcepts()); } DateTimeSection::~DateTimeSection() = default; @@ -127,11 +133,49 @@ void DateTimeSection::AddLoadTimeData(content::WebUIDataSource* html_source) { html_source->AddBoolean( "timeActionsProtectedForChild", base::FeatureList::IsEnabled(features::kParentAccessCodeForTimeChange)); + + bool is_child = user_manager::UserManager::Get()->GetActiveUser()->IsChild(); + html_source->AddBoolean("isChild", is_child); } void DateTimeSection::AddHandlers(content::WebUI* web_ui) { web_ui->AddMessageHandler(std::make_unique<DateTimeHandler>()); } +int DateTimeSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_DATE_TIME; +} + +mojom::Section DateTimeSection::GetSection() const { + return mojom::Section::kDateAndTime; +} + +mojom::SearchResultIcon DateTimeSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kClock; +} + +std::string DateTimeSection::GetSectionPath() const { + return mojom::kDateAndTimeSectionPath; +} + +void DateTimeSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::k24HourClock); + + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_TIME_ZONE_SUBPAGE_TITLE, mojom::Subpage::kTimeZone, + mojom::SearchResultIcon::kClock, mojom::SearchResultDefaultRank::kMedium, + mojom::kTimeZoneSubpagePath); + + // When fine-grained time zone is enabled, users change the time zone on the + // time zone subpage; otherwise, the setting is directly embedded in the + // section. + if (IsFineGrainedTimeZoneEnabled()) { + generator->RegisterNestedSetting(mojom::Setting::kChangeTimeZone, + mojom::Subpage::kTimeZone); + } else { + generator->RegisterTopLevelSetting(mojom::Setting::kChangeTimeZone); + } +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.h index 2c1d8e0f5c3..7eef9df9516 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/date_time_section.h @@ -26,6 +26,11 @@ class DateTimeSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.cc index 38eb1b57fef..c90f34eff69 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.cc @@ -36,28 +36,32 @@ DlcHandler::~DlcHandler() = default; void DlcHandler::RegisterMessages() { web_ui()->RegisterMessageCallback( - "getDlcList", base::BindRepeating(&DlcHandler::HandleGetDlcList, - weak_ptr_factory_.GetWeakPtr())); + "dlcSubpageReady", base::BindRepeating(&DlcHandler::HandleDlcSubpageReady, + base::Unretained(this))); web_ui()->RegisterMessageCallback( - "purgeDlc", base::BindRepeating(&DlcHandler::HandlePurgeDlc, - weak_ptr_factory_.GetWeakPtr())); + "purgeDlc", + base::BindRepeating(&DlcHandler::HandlePurgeDlc, base::Unretained(this))); +} + +void DlcHandler::OnJavascriptAllowed() { + dlcservice_client_observer_.Add(DlcserviceClient::Get()); } void DlcHandler::OnJavascriptDisallowed() { + dlcservice_client_observer_.RemoveAll(); + // Ensure that pending callbacks do not complete and cause JS to be evaluated. weak_ptr_factory_.InvalidateWeakPtrs(); } -void DlcHandler::HandleGetDlcList(const base::ListValue* args) { - AllowJavascript(); - CHECK_EQ(1U, args->GetSize()); - const base::Value* callback_id; - CHECK(args->Get(0, &callback_id)); +void DlcHandler::OnDlcStateChanged(const dlcservice::DlcState& dlc_state) { + FetchDlcList(); +} - DlcserviceClient::Get()->GetExistingDlcs( - base::BindOnce(&DlcHandler::GetDlcListCallback, - weak_ptr_factory_.GetWeakPtr(), callback_id->Clone())); +void DlcHandler::HandleDlcSubpageReady(const base::ListValue* args) { + AllowJavascript(); + FetchDlcList(); } void DlcHandler::HandlePurgeDlc(const base::ListValue* args) { @@ -74,16 +78,18 @@ void DlcHandler::HandlePurgeDlc(const base::ListValue* args) { weak_ptr_factory_.GetWeakPtr(), callback_id->Clone())); } -void DlcHandler::GetDlcListCallback( - const base::Value& callback_id, +void DlcHandler::FetchDlcList() { + DlcserviceClient::Get()->GetExistingDlcs( + base::BindOnce(&DlcHandler::SendDlcList, weak_ptr_factory_.GetWeakPtr())); +} + +void DlcHandler::SendDlcList( const std::string& err, const dlcservice::DlcsWithContent& dlcs_with_content) { - if (err == dlcservice::kErrorNone) { - ResolveJavascriptCallback(callback_id, - DlcsWithContentToListValue(dlcs_with_content)); - return; - } - ResolveJavascriptCallback(callback_id, base::ListValue()); + FireWebUIListener("dlc-list-changed", + err == dlcservice::kErrorNone + ? DlcsWithContentToListValue(dlcs_with_content) + : base::ListValue()); } void DlcHandler::PurgeDlcCallback(const base::Value& callback_id, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.h index 27afcaf2de6..cbd3f798c74 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_DEVICE_DLC_HANDLER_H_ #include "base/memory/weak_ptr.h" +#include "base/scoped_observer.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "chromeos/dbus/dlcservice/dlcservice_client.h" @@ -17,7 +18,8 @@ namespace chromeos { namespace settings { // Chrome OS Downloaded Content settings page UI handler. -class DlcHandler : public ::settings::SettingsPageUIHandler { +class DlcHandler : public ::settings::SettingsPageUIHandler, + public DlcserviceClient::Observer { public: DlcHandler(); DlcHandler(const DlcHandler&) = delete; @@ -26,22 +28,32 @@ class DlcHandler : public ::settings::SettingsPageUIHandler { // SettingsPageUIHandler: void RegisterMessages() override; - void OnJavascriptAllowed() override {} + void OnJavascriptAllowed() override; void OnJavascriptDisallowed() override; + // DlcserviceClient::Observer: + void OnDlcStateChanged(const dlcservice::DlcState& dlc_state) override; + private: - // Handler to get the latest list of DLCs. - void HandleGetDlcList(const base::ListValue* args); + // Handler called when DLC subpage is attached. + void HandleDlcSubpageReady(const base::ListValue* args); // Handler to purge a DLC. void HandlePurgeDlc(const base::ListValue* args); - void GetDlcListCallback(const base::Value& callback_id, - const std::string& err, - const dlcservice::DlcsWithContent& dlcs_with_content); + // Fetches the latest DLC list from DlcserviceClient, passing SendDlcList() as + // the callback. + void FetchDlcList(); + + // Sends DLC list to web UIs listening in on 'dlc-list-changed' events. + void SendDlcList(const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content); void PurgeDlcCallback(const base::Value& callback_id, const std::string& err); + ScopedObserver<DlcserviceClient, DlcserviceClient::Observer> + dlcservice_client_observer_{this}; + base::WeakPtrFactory<DlcHandler> weak_ptr_factory_{this}; }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler_unittest.cc index cb4dbc5dde8..b99ed70fcde 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_dlc_handler_unittest.cc @@ -42,14 +42,14 @@ class DlcHandlerTest : public testing::Test { void SetUp() override { test_web_ui_ = std::make_unique<content::TestWebUI>(); + chromeos::DlcserviceClient::InitializeFake(); + fake_dlcservice_client_ = static_cast<chromeos::FakeDlcserviceClient*>( + chromeos::DlcserviceClient::Get()); + handler_ = std::make_unique<TestDlcHandler>(); handler_->set_web_ui(test_web_ui_.get()); handler_->RegisterMessages(); handler_->AllowJavascriptForTesting(); - - chromeos::DlcserviceClient::InitializeFake(); - fake_dlcservice_client_ = static_cast<chromeos::FakeDlcserviceClient*>( - chromeos::DlcserviceClient::Get()); } void TearDown() override { @@ -70,12 +70,12 @@ class DlcHandlerTest : public testing::Test { return *test_web_ui_->call_data()[index]; } - base::Value::ConstListView CallGetDlcListAndReturnList() { + base::Value::ConstListView NotifyDlcSubpageReadyAndReturnDlcList() { size_t call_data_count_before_call = test_web_ui()->call_data().size(); base::ListValue args; args.AppendString("handlerFunctionName"); - test_web_ui()->HandleReceivedMessage("getDlcList", &args); + test_web_ui()->HandleReceivedMessage("dlcSubpageReady", &args); task_environment_.RunUntilIdle(); EXPECT_EQ(call_data_count_before_call + 1u, @@ -83,9 +83,9 @@ class DlcHandlerTest : public testing::Test { const content::TestWebUI::CallData& call_data = CallDataAtIndex(call_data_count_before_call); - EXPECT_EQ("cr.webUIResponse", call_data.function_name()); - EXPECT_EQ("handlerFunctionName", call_data.arg1()->GetString()); - return call_data.arg3()->GetList(); + EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name()); + EXPECT_EQ("dlc-list-changed", call_data.arg1()->GetString()); + return call_data.arg2()->GetList(); } bool CallPurgeDlcAndReturnSuccess() { @@ -108,26 +108,45 @@ class DlcHandlerTest : public testing::Test { } }; -TEST_F(DlcHandlerTest, GetDlcList) { +TEST_F(DlcHandlerTest, SendDlcListOnDlcStatusChange) { + size_t call_data_count_before_call = test_web_ui()->call_data().size(); + fake_dlcservice_client_->set_dlcs_with_content(CreateDlcModuleListOfSize(2u)); + + dlcservice::DlcState dlc_state; + dlc_state.set_state(dlcservice::DlcState::INSTALLING); + fake_dlcservice_client_->NotifyObserversForTest(dlc_state); + task_environment_.RunUntilIdle(); + + EXPECT_EQ(call_data_count_before_call + 1u, + test_web_ui()->call_data().size()); + + const content::TestWebUI::CallData& call_data = + CallDataAtIndex(call_data_count_before_call); + EXPECT_EQ("cr.webUIListenerCallback", call_data.function_name()); + EXPECT_EQ("dlc-list-changed", call_data.arg1()->GetString()); + EXPECT_EQ(call_data.arg2()->GetList().size(), 2u); +} + +TEST_F(DlcHandlerTest, CorrectlyReturnsDlcMetadataListOnSubpageReady) { fake_dlcservice_client_->set_dlcs_with_content(CreateDlcModuleListOfSize(2u)); fake_dlcservice_client_->SetGetExistingDlcsError(dlcservice::kErrorInternal); - EXPECT_EQ(CallGetDlcListAndReturnList().size(), 0u); + EXPECT_EQ(NotifyDlcSubpageReadyAndReturnDlcList().size(), 0u); fake_dlcservice_client_->SetGetExistingDlcsError( dlcservice::kErrorNeedReboot); - EXPECT_EQ(CallGetDlcListAndReturnList().size(), 0u); + EXPECT_EQ(NotifyDlcSubpageReadyAndReturnDlcList().size(), 0u); fake_dlcservice_client_->SetGetExistingDlcsError( dlcservice::kErrorInvalidDlc); - EXPECT_EQ(CallGetDlcListAndReturnList().size(), 0u); + EXPECT_EQ(NotifyDlcSubpageReadyAndReturnDlcList().size(), 0u); fake_dlcservice_client_->SetGetExistingDlcsError( dlcservice::kErrorAllocation); - EXPECT_EQ(CallGetDlcListAndReturnList().size(), 0u); + EXPECT_EQ(NotifyDlcSubpageReadyAndReturnDlcList().size(), 0u); fake_dlcservice_client_->SetGetExistingDlcsError(dlcservice::kErrorNone); - EXPECT_EQ(CallGetDlcListAndReturnList().size(), 2u); + EXPECT_EQ(NotifyDlcSubpageReadyAndReturnDlcList().size(), 2u); } TEST_F(DlcHandlerTest, PurgeDlc) { @@ -157,7 +176,7 @@ TEST_F(DlcHandlerTest, FormattedCorrectly) { fake_dlcservice_client_->set_dlcs_with_content(dlcs_with_content); - auto result_list = CallGetDlcListAndReturnList(); + auto result_list = NotifyDlcSubpageReadyAndReturnDlcList(); EXPECT_EQ(1UL, result_list.size()); EXPECT_EQ("fake id", result_list[0].FindKey("id")->GetString()); EXPECT_EQ("fake name", result_list[0].FindKey("name")->GetString()); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.cc index 440b8feb397..7921e8838dd 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.cc @@ -48,7 +48,7 @@ const std::vector<SearchConcept>& GetDeviceSearchConcepts() { {IDS_OS_SETTINGS_TAG_KEYBOARD, mojom::kKeyboardSubpagePath, mojom::SearchResultIcon::kKeyboard, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kKeyboard}}, {IDS_OS_SETTINGS_TAG_KEYBOARD_AUTO_REPEAT, @@ -81,7 +81,7 @@ const std::vector<SearchConcept>& GetDeviceSearchConcepts() { {IDS_OS_SETTINGS_TAG_STORAGE, mojom::kStorageSubpagePath, mojom::SearchResultIcon::kHardDrive, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kStorage}, {IDS_OS_SETTINGS_TAG_STORAGE_ALT1, IDS_OS_SETTINGS_TAG_STORAGE_ALT2, @@ -89,7 +89,7 @@ const std::vector<SearchConcept>& GetDeviceSearchConcepts() { {IDS_OS_SETTINGS_TAG_DISPLAY_NIGHT_LIGHT, mojom::kDisplaySubpagePath, mojom::SearchResultIcon::kDisplay, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kNightLight}, {IDS_OS_SETTINGS_TAG_DISPLAY_NIGHT_LIGHT_ALT1, @@ -98,7 +98,7 @@ const std::vector<SearchConcept>& GetDeviceSearchConcepts() { {IDS_OS_SETTINGS_TAG_DISPLAY, mojom::kDisplaySubpagePath, mojom::SearchResultIcon::kDisplay, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kDisplay}, {IDS_OS_SETTINGS_TAG_DISPLAY_ALT1, IDS_OS_SETTINGS_TAG_DISPLAY_ALT2, @@ -121,13 +121,22 @@ const std::vector<SearchConcept>& GetDeviceSearchConcepts() { mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kKeyboardFunctionKeys}}, - {IDS_OS_SETTINGS_TAG_POWER_IDLE, + {IDS_OS_SETTINGS_TAG_POWER_IDLE_WHILE_CHARGING, mojom::kPowerSubpagePath, mojom::SearchResultIcon::kPower, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kPowerIdleBehavior}, - {IDS_OS_SETTINGS_TAG_POWER_IDLE_ALT1, SearchConcept::kAltTagEnd}}, + {.setting = mojom::Setting::kPowerIdleBehaviorWhileCharging}, + {IDS_OS_SETTINGS_TAG_POWER_IDLE_WHILE_CHARGING_ALT1, + SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_POWER_IDLE_WHILE_ON_BATTERY, + mojom::kPowerSubpagePath, + mojom::SearchResultIcon::kPower, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kPowerIdleBehaviorWhileOnBattery}, + {IDS_OS_SETTINGS_TAG_POWER_IDLE_WHILE_ON_BATTERY_ALT1, + SearchConcept::kAltTagEnd}}, }); return *tags; } @@ -155,7 +164,7 @@ const std::vector<SearchConcept>& GetTouchpadSearchConcepts() { {IDS_OS_SETTINGS_TAG_TOUCHPAD, mojom::kPointersSubpagePath, mojom::SearchResultIcon::kLaptop, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kPointers}, {IDS_OS_SETTINGS_TAG_TOUCHPAD_ALT1, SearchConcept::kAltTagEnd}}, @@ -210,7 +219,7 @@ const std::vector<SearchConcept>& GetMouseSearchConcepts() { {IDS_OS_SETTINGS_TAG_MOUSE, mojom::kPointersSubpagePath, mojom::SearchResultIcon::kMouse, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kPointers}}, {IDS_OS_SETTINGS_TAG_MOUSE_SCROLL_ACCELERATION, @@ -281,36 +290,30 @@ const std::vector<SearchConcept>& GetDisplayArrangementSearchConcepts() { const std::vector<SearchConcept>& GetDisplayMirrorSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display mirror" search concepts. + {IDS_OS_SETTINGS_TAG_MIRRORING, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kDisplayMirroring}}, }); return *tags; } const std::vector<SearchConcept>& GetDisplayUnifiedDesktopSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display with Unified Desktop" search concepts. - }); - return *tags; -} - -const std::vector<SearchConcept>& GetDisplayMultipleSearchConcepts() { - static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display multiple" search concepts. + {IDS_OS_SETTINGS_TAG_UNIFIED_DESKTOP, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kAllowWindowsToSpanDisplays}}, }); return *tags; } const std::vector<SearchConcept>& GetDisplayExternalSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - {IDS_OS_SETTINGS_TAG_DISPLAY_ORIENTATION, - mojom::kDisplaySubpagePath, - mojom::SearchResultIcon::kDisplay, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kDisplayOrientation}, - {IDS_OS_SETTINGS_TAG_DISPLAY_ORIENTATION_ALT1, - IDS_OS_SETTINGS_TAG_DISPLAY_ORIENTATION_ALT2, - SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_DISPLAY_RESOLUTION, mojom::kDisplaySubpagePath, mojom::SearchResultIcon::kDisplay, @@ -320,6 +323,12 @@ const std::vector<SearchConcept>& GetDisplayExternalSearchConcepts() { {IDS_OS_SETTINGS_TAG_DISPLAY_RESOLUTION_ALT1, IDS_OS_SETTINGS_TAG_DISPLAY_RESOLUTION_ALT2, SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_DISPLAY_OVERSCAN, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kDisplayOverscan}}, }); return *tags; } @@ -342,28 +351,74 @@ GetDisplayExternalWithRefreshSearchConcepts() { const std::vector<SearchConcept>& GetDisplayOrientationSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display orientation" search concepts. + {IDS_OS_SETTINGS_TAG_DISPLAY_ORIENTATION, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kDisplayOrientation}, + {IDS_OS_SETTINGS_TAG_DISPLAY_ORIENTATION_ALT1, + SearchConcept::kAltTagEnd}}, }); return *tags; } const std::vector<SearchConcept>& GetDisplayAmbientSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display ambient" search concepts. + {IDS_OS_SETTINGS_TAG_DISPLAY_AMBIENT_COLORS, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kAmbientColors}}, }); return *tags; } const std::vector<SearchConcept>& GetDisplayTouchCalibrationSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display touch calibration" search concepts. + {IDS_OS_SETTINGS_TAG_DISPLAY_TOUCHSCREEN_CALIBRATION, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kTouchscreenCalibration}}, }); return *tags; } const std::vector<SearchConcept>& GetDisplayNightLightOnSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Display Night Light on" search concepts. + {IDS_OS_SETTINGS_TAG_NIGHT_LIGHT_COLOR_TEMPERATURE, + mojom::kDisplaySubpagePath, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kNightLightColorTemperature}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetDlcSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_DOWNLOADED_CONTENT, + mojom::kDlcSubpagePath, + mojom::SearchResultIcon::kHardDrive, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSubpage, + {.subpage = mojom::Subpage::kDlc}, + {IDS_OS_SETTINGS_TAG_DOWNLOADED_CONTENT_ALT1, + SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_REMOVE_DOWNLOADED_CONTENT, + mojom::kDlcSubpagePath, + mojom::SearchResultIcon::kHardDrive, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kRemoveDlc}, + {IDS_OS_SETTINGS_TAG_REMOVE_DOWNLOADED_CONTENT_ALT1, + IDS_OS_SETTINGS_TAG_REMOVE_DOWNLOADED_CONTENT_ALT2, + IDS_OS_SETTINGS_TAG_REMOVE_DOWNLOADED_CONTENT_ALT3, + SearchConcept::kAltTagEnd}}, }); return *tags; } @@ -694,10 +749,11 @@ DeviceSection::DeviceSection(Profile* profile, PrefService* pref_service) : OsSettingsSection(profile, search_tag_registry), pref_service_(pref_service) { - registry()->AddSearchTags(GetDeviceSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetDeviceSearchConcepts()); if (features::ShouldShowExternalStorageSettings(profile)) - registry()->AddSearchTags(GetExternalStorageSearchConcepts()); + updater.AddSearchTags(GetExternalStorageSearchConcepts()); PowerManagerClient* power_manager_client = PowerManagerClient::Get(); if (power_manager_client) { @@ -739,6 +795,14 @@ DeviceSection::DeviceSection(Profile* profile, OnNightLightEnabledChanged( ash::NightLightController::GetInstance()->GetEnabled()); } + + // DLC settings search tags are added/removed dynamically. + DlcserviceClient* dlcservice_client = DlcserviceClient::Get(); + if (features::ShouldShowDlcSettings() && dlcservice_client) { + dlcservice_client->AddObserver(this); + dlcservice_client->GetExistingDlcs(base::BindOnce( + &DeviceSection::OnGetExistingDlcs, weak_ptr_factory_.GetWeakPtr())); + } } DeviceSection::~DeviceSection() { @@ -753,6 +817,10 @@ DeviceSection::~DeviceSection() { ash::NightLightController::GetInstance(); if (night_light_controller) night_light_controller->RemoveObserver(this); + + DlcserviceClient* dlcservice_client = DlcserviceClient::Get(); + if (features::ShouldShowDlcSettings() && dlcservice_client) + dlcservice_client->RemoveObserver(this); } void DeviceSection::AddLoadTimeData(content::WebUIDataSource* html_source) { @@ -797,18 +865,140 @@ void DeviceSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<chromeos::settings::StylusHandler>()); } +int DeviceSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_DEVICE_TITLE; +} + +mojom::Section DeviceSection::GetSection() const { + return mojom::Section::kDevice; +} + +mojom::SearchResultIcon DeviceSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kLaptop; +} + +std::string DeviceSection::GetSectionPath() const { + return mojom::kDeviceSectionPath; +} + +void DeviceSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // Pointers. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_MOUSE_AND_TOUCHPAD_TITLE, mojom::Subpage::kPointers, + mojom::SearchResultIcon::kMouse, mojom::SearchResultDefaultRank::kMedium, + mojom::kPointersSubpagePath); + static constexpr mojom::Setting kPointersSettings[] = { + mojom::Setting::kTouchpadTapToClick, + mojom::Setting::kTouchpadTapDragging, + mojom::Setting::kTouchpadReverseScrolling, + mojom::Setting::kTouchpadAcceleration, + mojom::Setting::kTouchpadScrollAcceleration, + mojom::Setting::kTouchpadSpeed, + mojom::Setting::kMouseSwapPrimaryButtons, + mojom::Setting::kMouseReverseScrolling, + mojom::Setting::kMouseAcceleration, + mojom::Setting::kMouseScrollAcceleration, + mojom::Setting::kMouseSpeed, + }; + RegisterNestedSettingBulk(mojom::Subpage::kPointers, kPointersSettings, + generator); + + // Keyboard. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_KEYBOARD_TITLE, mojom::Subpage::kKeyboard, + mojom::SearchResultIcon::kKeyboard, + mojom::SearchResultDefaultRank::kMedium, mojom::kKeyboardSubpagePath); + static constexpr mojom::Setting kKeyboardSettings[] = { + mojom::Setting::kKeyboardFunctionKeys, + mojom::Setting::kKeyboardAutoRepeat, + mojom::Setting::kKeyboardShortcuts, + }; + RegisterNestedSettingBulk(mojom::Subpage::kKeyboard, kKeyboardSettings, + generator); + + // Stylus. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_STYLUS_TITLE, mojom::Subpage::kStylus, + mojom::SearchResultIcon::kStylus, mojom::SearchResultDefaultRank::kMedium, + mojom::kStylusSubpagePath); + static constexpr mojom::Setting kStylusSettings[] = { + mojom::Setting::kStylusToolsInShelf, + mojom::Setting::kStylusNoteTakingApp, + mojom::Setting::kStylusNoteTakingFromLockScreen, + mojom::Setting::kStylusLatestNoteOnLockScreen, + }; + RegisterNestedSettingBulk(mojom::Subpage::kStylus, kStylusSettings, + generator); + + // Display. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_DISPLAY_TITLE, mojom::Subpage::kDisplay, + mojom::SearchResultIcon::kDisplay, + mojom::SearchResultDefaultRank::kMedium, mojom::kDisplaySubpagePath); + static constexpr mojom::Setting kDisplaySettings[] = { + mojom::Setting::kDisplaySize, + mojom::Setting::kNightLight, + mojom::Setting::kDisplayOrientation, + mojom::Setting::kDisplayArrangement, + mojom::Setting::kDisplayResolution, + mojom::Setting::kDisplayRefreshRate, + mojom::Setting::kDisplayMirroring, + mojom::Setting::kAllowWindowsToSpanDisplays, + mojom::Setting::kAmbientColors, + mojom::Setting::kTouchscreenCalibration, + mojom::Setting::kNightLightColorTemperature, + mojom::Setting::kDisplayOverscan, + }; + RegisterNestedSettingBulk(mojom::Subpage::kDisplay, kDisplaySettings, + generator); + + // Storage. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_STORAGE_TITLE, mojom::Subpage::kStorage, + mojom::SearchResultIcon::kHardDrive, + mojom::SearchResultDefaultRank::kMedium, mojom::kStorageSubpagePath); + generator->RegisterNestedSubpage( + IDS_SETTINGS_STORAGE_EXTERNAL, mojom::Subpage::kExternalStorage, + mojom::Subpage::kStorage, mojom::SearchResultIcon::kHardDrive, + mojom::SearchResultDefaultRank::kMedium, + mojom::kExternalStorageSubpagePath); + generator->RegisterNestedSubpage( + IDS_SETTINGS_DLC_SUBPAGE_TITLE, mojom::Subpage::kDlc, + mojom::Subpage::kStorage, mojom::SearchResultIcon::kHardDrive, + mojom::SearchResultDefaultRank::kMedium, mojom::kDlcSubpagePath); + generator->RegisterNestedSetting(mojom::Setting::kRemoveDlc, + mojom::Subpage::kDlc); + + // Power. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_POWER_TITLE, mojom::Subpage::kPower, + mojom::SearchResultIcon::kPower, mojom::SearchResultDefaultRank::kMedium, + mojom::kPowerSubpagePath); + static constexpr mojom::Setting kPowerSettings[] = { + mojom::Setting::kPowerIdleBehaviorWhileCharging, + mojom::Setting::kPowerIdleBehaviorWhileOnBattery, + mojom::Setting::kPowerSource, + mojom::Setting::kSleepWhenLaptopLidClosed, + }; + RegisterNestedSettingBulk(mojom::Subpage::kPower, kPowerSettings, generator); +} + void DeviceSection::TouchpadExists(bool exists) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (exists) - registry()->AddSearchTags(GetTouchpadSearchConcepts()); + updater.AddSearchTags(GetTouchpadSearchConcepts()); else - registry()->RemoveSearchTags(GetTouchpadSearchConcepts()); + updater.RemoveSearchTags(GetTouchpadSearchConcepts()); } void DeviceSection::MouseExists(bool exists) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (exists) - registry()->AddSearchTags(GetMouseSearchConcepts()); + updater.AddSearchTags(GetMouseSearchConcepts()); else - registry()->RemoveSearchTags(GetMouseSearchConcepts()); + updater.RemoveSearchTags(GetMouseSearchConcepts()); } void DeviceSection::OnDeviceListsComplete() { @@ -828,12 +1018,32 @@ void DeviceSection::OnDisplayConfigChanged() { void DeviceSection::PowerChanged( const power_manager::PowerSupplyProperties& properties) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (properties.battery_state() != power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT) { - registry()->AddSearchTags(GetPowerWithBatterySearchConcepts()); + updater.AddSearchTags(GetPowerWithBatterySearchConcepts()); } } +void DeviceSection::OnGetExistingDlcs( + const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + if (err != dlcservice::kErrorNone || + dlcs_with_content.dlc_infos_size() == 0) { + updater.RemoveSearchTags(GetDlcSearchConcepts()); + return; + } + updater.AddSearchTags(GetDlcSearchConcepts()); +} + +void DeviceSection::OnDlcStateChanged(const dlcservice::DlcState& dlc_state) { + DlcserviceClient::Get()->GetExistingDlcs(base::BindOnce( + &DeviceSection::OnGetExistingDlcs, weak_ptr_factory_.GetWeakPtr())); +} + void DeviceSection::OnGetDisplayUnitInfoList( std::vector<ash::mojom::DisplayUnitInfoPtr> display_unit_info_list) { cros_display_config_->GetDisplayLayoutInfo(base::BindOnce( @@ -863,73 +1073,71 @@ void DeviceSection::OnGetDisplayLayoutInfo( ash::mojom::DisplayLayoutMode::kUnified; } + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + // Arrangement UI. if (has_multiple_displays || is_mirrored) - registry()->AddSearchTags(GetDisplayArrangementSearchConcepts()); + updater.AddSearchTags(GetDisplayArrangementSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayArrangementSearchConcepts()); + updater.RemoveSearchTags(GetDisplayArrangementSearchConcepts()); // Mirror toggle. if (is_mirrored || (!unified_desktop_mode && has_multiple_displays)) - registry()->AddSearchTags(GetDisplayMirrorSearchConcepts()); + updater.AddSearchTags(GetDisplayMirrorSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayMirrorSearchConcepts()); + updater.RemoveSearchTags(GetDisplayMirrorSearchConcepts()); // Unified Desktop toggle. if (unified_desktop_mode || (IsUnifiedDesktopAvailable() && has_multiple_displays && !is_mirrored)) { - registry()->AddSearchTags(GetDisplayUnifiedDesktopSearchConcepts()); + updater.AddSearchTags(GetDisplayUnifiedDesktopSearchConcepts()); } else { - registry()->RemoveSearchTags(GetDisplayUnifiedDesktopSearchConcepts()); + updater.RemoveSearchTags(GetDisplayUnifiedDesktopSearchConcepts()); } - // Multiple displays UI. - if (has_multiple_displays) - registry()->AddSearchTags(GetDisplayMultipleSearchConcepts()); - else - registry()->RemoveSearchTags(GetDisplayMultipleSearchConcepts()); - // External display settings. if (has_external_display) - registry()->AddSearchTags(GetDisplayExternalSearchConcepts()); + updater.AddSearchTags(GetDisplayExternalSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayExternalSearchConcepts()); + updater.RemoveSearchTags(GetDisplayExternalSearchConcepts()); // Refresh Rate dropdown. if (has_external_display && IsListAllDisplayModesEnabled()) - registry()->AddSearchTags(GetDisplayExternalWithRefreshSearchConcepts()); + updater.AddSearchTags(GetDisplayExternalWithRefreshSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayExternalWithRefreshSearchConcepts()); + updater.RemoveSearchTags(GetDisplayExternalWithRefreshSearchConcepts()); // Orientation settings. if (!unified_desktop_mode) - registry()->AddSearchTags(GetDisplayOrientationSearchConcepts()); + updater.AddSearchTags(GetDisplayOrientationSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayOrientationSearchConcepts()); + updater.RemoveSearchTags(GetDisplayOrientationSearchConcepts()); // Ambient color settings. if (DoesDeviceSupportAmbientColor() && has_internal_display) - registry()->AddSearchTags(GetDisplayAmbientSearchConcepts()); + updater.AddSearchTags(GetDisplayAmbientSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayAmbientSearchConcepts()); + updater.RemoveSearchTags(GetDisplayAmbientSearchConcepts()); // Touch calibration settings. if (IsTouchCalibrationAvailable()) - registry()->AddSearchTags(GetDisplayTouchCalibrationSearchConcepts()); + updater.AddSearchTags(GetDisplayTouchCalibrationSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayTouchCalibrationSearchConcepts()); + updater.RemoveSearchTags(GetDisplayTouchCalibrationSearchConcepts()); // Night Light on settings. if (ash::NightLightController::GetInstance()->GetEnabled()) - registry()->AddSearchTags(GetDisplayNightLightOnSearchConcepts()); + updater.AddSearchTags(GetDisplayNightLightOnSearchConcepts()); else - registry()->RemoveSearchTags(GetDisplayNightLightOnSearchConcepts()); + updater.RemoveSearchTags(GetDisplayNightLightOnSearchConcepts()); } void DeviceSection::OnGotSwitchStates( base::Optional<PowerManagerClient::SwitchStates> result) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (result && result->lid_state != PowerManagerClient::LidState::NOT_PRESENT) - registry()->AddSearchTags(GetPowerWithLaptopLidSearchConcepts()); + updater.AddSearchTags(GetPowerWithLaptopLidSearchConcepts()); } void DeviceSection::UpdateStylusSearchTags() { @@ -937,13 +1145,15 @@ void DeviceSection::UpdateStylusSearchTags() { if (!ui::DeviceDataManager::GetInstance()->AreDeviceListsComplete()) return; + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + // TODO(https://crbug.com/1071905): Only show stylus settings if a stylus has // been set up. HasStylusInput() will return true for any stylus-compatible // device, even if it doesn't have a stylus. if (ash::stylus_utils::HasStylusInput()) - registry()->AddSearchTags(GetStylusSearchConcepts()); + updater.AddSearchTags(GetStylusSearchConcepts()); else - registry()->RemoveSearchTags(GetStylusSearchConcepts()); + updater.RemoveSearchTags(GetStylusSearchConcepts()); } void DeviceSection::AddDevicePointersStrings( diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.h index 255f63a4963..541b8dbaafc 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_section.h @@ -13,6 +13,7 @@ #include "base/optional.h" #include "chrome/browser/chromeos/system/pointer_device_observer.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" +#include "chromeos/dbus/dlcservice/dlcservice_client.h" #include "chromeos/dbus/power/power_manager_client.h" #include "mojo/public/cpp/bindings/associated_receiver.h" #include "mojo/public/cpp/bindings/remote.h" @@ -24,6 +25,10 @@ namespace content { class WebUIDataSource; } // namespace content +namespace dlcservice { +class DlcsWithContent; +} // namespace dlcservice + namespace chromeos { namespace settings { @@ -35,7 +40,8 @@ class DeviceSection : public OsSettingsSection, public ui::InputDeviceEventObserver, public ash::NightLightController::Observer, public ash::mojom::CrosDisplayConfigObserver, - public PowerManagerClient::Observer { + public PowerManagerClient::Observer, + public DlcserviceClient::Observer { public: DeviceSection(Profile* profile, SearchTagRegistry* search_tag_registry, @@ -46,6 +52,11 @@ class DeviceSection : public OsSettingsSection, // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // system::PointerDeviceObserver::Observer: void TouchpadExists(bool exists) override; @@ -63,6 +74,9 @@ class DeviceSection : public OsSettingsSection, // PowerManagerClient::Observer: void PowerChanged(const power_manager::PowerSupplyProperties& proto) override; + // DlcserviceClient::Observer: + void OnDlcStateChanged(const dlcservice::DlcState& dlc_state) override; + void OnGotSwitchStates( base::Optional<PowerManagerClient::SwitchStates> result); @@ -74,6 +88,9 @@ class DeviceSection : public OsSettingsSection, std::vector<ash::mojom::DisplayUnitInfoPtr> display_unit_info_list, ash::mojom::DisplayLayoutInfoPtr display_layout_info); + void OnGetExistingDlcs(const std::string& err, + const dlcservice::DlcsWithContent& dlcs_with_content); + void AddDevicePointersStrings(content::WebUIDataSource* html_source); PrefService* pref_service_; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc index 584226636b8..e3961f58389 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.cc @@ -48,6 +48,8 @@ const char* CalculationTypeToEventName( return "storage-crostini-size-changed"; case calculator::SizeCalculator::CalculationType::kOtherUsers: return "storage-other-users-size-changed"; + case calculator::SizeCalculator::CalculationType::kDlcs: + return "storage-dlcs-size-changed"; } NOTREACHED(); return ""; @@ -63,6 +65,7 @@ StorageHandler::StorageHandler(Profile* profile, apps_size_calculator_(profile), crostini_size_calculator_(profile), other_users_size_calculator_(), + dlcs_size_calculator_(), profile_(profile), source_name_(html_source->GetSource()), arc_observer_(this), @@ -115,6 +118,7 @@ void StorageHandler::OnJavascriptAllowed() { apps_size_calculator_.AddObserver(this); crostini_size_calculator_.AddObserver(this); other_users_size_calculator_.AddObserver(this); + dlcs_size_calculator_.AddObserver(this); } void StorageHandler::OnJavascriptDisallowed() { @@ -133,6 +137,9 @@ int64_t StorageHandler::RoundByteSize(int64_t bytes) { return -1; } + if (bytes == 0) + return 0; + // Subtract one to the original number of bytes. bytes--; // Set all the lower bits to 1. @@ -164,6 +171,7 @@ void StorageHandler::HandleUpdateStorageInfo(const base::ListValue* args) { apps_size_calculator_.StartCalculation(); crostini_size_calculator_.StartCalculation(); other_users_size_calculator_.StartCalculation(); + dlcs_size_calculator_.StartCalculation(); } void StorageHandler::HandleOpenMyFiles(const base::ListValue* unused_args) { @@ -259,6 +267,7 @@ void StorageHandler::StopObservingEvents() { apps_size_calculator_.RemoveObserver(this); crostini_size_calculator_.RemoveObserver(this); other_users_size_calculator_.RemoveObserver(this); + dlcs_size_calculator_.RemoveObserver(this); } void StorageHandler::UpdateStorageItem( @@ -282,6 +291,12 @@ void StorageHandler::UpdateStorageItem( bool no_other_users = (total_bytes == 0); FireWebUIListener(CalculationTypeToEventName(calculation_type), base::Value(message), base::Value(no_other_users)); + } else if (calculation_type == + calculator::SizeCalculator::CalculationType::kDlcs) { + bool dlcs_exist = (total_bytes > 0); + FireWebUIListener(CalculationTypeToEventName(calculation_type), + base::Value(dlcs_exist), base::Value(message)); + } else { FireWebUIListener(CalculationTypeToEventName(calculation_type), base::Value(message)); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h index cdd352797a6..d6b4e79a376 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler.h @@ -113,6 +113,7 @@ class StorageHandler : public ::settings::SettingsPageUIHandler, calculator::AppsSizeCalculator apps_size_calculator_; calculator::CrostiniSizeCalculator crostini_size_calculator_; calculator::OtherUsersSizeCalculator other_users_size_calculator_; + calculator::DlcsSizeCalculator dlcs_size_calculator_; // Controls if the size of each storage item has been calculated. std::bitset<calculator::SizeCalculator::kCalculationTypeCount> diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc index 57c1ab89f3c..86e80c50226 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/device_storage_handler_unittest.cc @@ -23,6 +23,7 @@ #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile_manager.h" +#include "chromeos/dbus/dlcservice/fake_dlcservice_client.h" #include "components/arc/arc_service_manager.h" #include "components/arc/test/fake_arc_session.h" #include "content/public/browser/web_ui_data_source.h" @@ -96,6 +97,8 @@ class StorageHandlerTest : public testing::Test { other_users_size_test_api_ = std::make_unique<calculator::OtherUsersSizeTestAPI>( handler_.get(), new calculator::OtherUsersSizeCalculator()); + dlcs_size_test_api_ = std::make_unique<calculator::DlcsSizeTestAPI>( + handler_.get(), new calculator::DlcsSizeCalculator()); // Create and register My files directory. // By emulating chromeos running, GetMyFilesFolderForProfile will return the @@ -109,6 +112,10 @@ class StorageHandlerTest : public testing::Test { file_manager::util::GetDownloadsMountPointName(profile_), storage::kFileSystemTypeNativeLocal, storage::FileSystemMountOption(), my_files_path)); + + chromeos::DlcserviceClient::InitializeFake(); + fake_dlcservice_client_ = static_cast<chromeos::FakeDlcserviceClient*>( + chromeos::DlcserviceClient::Get()); } void TearDown() override { @@ -119,7 +126,9 @@ class StorageHandlerTest : public testing::Test { apps_size_test_api_.reset(); crostini_size_test_api_.reset(); other_users_size_test_api_.reset(); + dlcs_size_test_api_.reset(); chromeos::disks::DiskMountManager::Shutdown(); + chromeos::DlcserviceClient::Shutdown(); storage::ExternalMountPoints::GetSystemInstance()->RevokeAllFileSystems(); } @@ -148,8 +157,11 @@ class StorageHandlerTest : public testing::Test { !data->arg1()->GetAsString(&name)) { continue; } - if (name == event_name) + if (name == event_name) { + if (name == "storage-dlcs-size-changed") + return data->arg3(); return data->arg2(); + } } return nullptr; } @@ -191,6 +203,7 @@ class StorageHandlerTest : public testing::Test { content::BrowserTaskEnvironment task_environment_; std::unique_ptr<TestingProfileManager> profile_manager_; Profile* profile_; + chromeos::FakeDlcserviceClient* fake_dlcservice_client_; std::unique_ptr<calculator::SizeStatTestAPI> size_stat_test_api_; std::unique_ptr<calculator::MyFilesSizeTestAPI> my_files_size_test_api_; std::unique_ptr<calculator::BrowsingDataSizeTestAPI> @@ -198,6 +211,7 @@ class StorageHandlerTest : public testing::Test { std::unique_ptr<calculator::AppsSizeTestAPI> apps_size_test_api_; std::unique_ptr<calculator::CrostiniSizeTestAPI> crostini_size_test_api_; std::unique_ptr<calculator::OtherUsersSizeTestAPI> other_users_size_test_api_; + std::unique_ptr<calculator::DlcsSizeTestAPI> dlcs_size_test_api_; private: std::unique_ptr<arc::ArcServiceManager> arc_service_manager_; @@ -341,6 +355,26 @@ TEST_F(StorageHandlerTest, MyFilesSize) { EXPECT_EQ("81.4 KB", callback->GetString()); } +TEST_F(StorageHandlerTest, DlcsSize) { + dlcservice::DlcsWithContent dlcs_with_content; + auto* dlc_info = dlcs_with_content.add_dlc_infos(); + dlc_info->set_used_bytes_on_disk(1); + dlc_info = dlcs_with_content.add_dlc_infos(); + dlc_info->set_used_bytes_on_disk(2); + fake_dlcservice_client_->set_dlcs_with_content(dlcs_with_content); + + // Calculate DLC size. + dlcs_size_test_api_->StartCalculation(); + task_environment_.RunUntilIdle(); + + const base::Value* callback = + GetWebUICallbackMessage("storage-dlcs-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-dlcs-size-changed' callback"; + + // Check return value. + EXPECT_EQ("3 B", callback->GetString()); +} + TEST_F(StorageHandlerTest, AppsExtensionsSize) { // The data for apps and extensions apps_size_test_api_installed from the // webstore is stored in the Extensions folder. Add data at a random location @@ -449,10 +483,22 @@ TEST_F(StorageHandlerTest, SystemSize) { ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); // Simulate crostini size callback. - crostini_size_test_api_->SimulateOnGetCrostiniSize(70 * GB); + crostini_size_test_api_->SimulateOnGetCrostiniSize(50 * GB); callback = GetWebUICallbackMessage("storage-crostini-size-changed"); ASSERT_TRUE(callback) << "No 'storage-crostini-size-changed' callback"; - EXPECT_EQ("70.0 GB", callback->GetString()); + EXPECT_EQ("50.0 GB", callback->GetString()); + ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); + + // Simulate DLC size callback + dlcservice::DlcsWithContent dlcs_with_content; + auto* dlc_info = dlcs_with_content.add_dlc_infos(); + dlc_info->set_used_bytes_on_disk(20 * GB); + dlc_info = dlcs_with_content.add_dlc_infos(); + dlcs_size_test_api_->SimulateOnGetExistingDlcs(dlcservice::kErrorNone, + dlcs_with_content); + callback = GetWebUICallbackMessage("storage-dlcs-size-changed"); + ASSERT_TRUE(callback) << "No 'storage-dlcs-size-changed' callback"; + EXPECT_EQ("20.0 GB", callback->GetString()); ASSERT_FALSE(GetWebUICallbackMessage("storage-system-size-changed")); // Simulate other users size callback. No callback message until the sizes of diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc new file mode 100644 index 00000000000..3cf8646c001 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.cc @@ -0,0 +1,53 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h" + +#include <utility> + +#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h" + +namespace chromeos { +namespace settings { + +FakeHierarchy::FakeHierarchy(const OsSettingsSections* sections) + : Hierarchy(sections) {} + +FakeHierarchy::~FakeHierarchy() = default; + +void FakeHierarchy::AddSubpageMetadata( + int name_message_id, + mojom::Section section, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters, + base::Optional<mojom::Subpage> parent_subpage) { + auto pair = subpage_map_.emplace( + std::piecewise_construct, std::forward_as_tuple(subpage), + std::forward_as_tuple(name_message_id, section, subpage, icon, + default_rank, url_path_with_parameters, this)); + DCHECK(pair.second); + pair.first->second.parent_subpage = parent_subpage; +} + +void FakeHierarchy::AddSettingMetadata( + mojom::Section section, + mojom::Setting setting, + base::Optional<mojom::Subpage> parent_subpage) { + auto pair = setting_map_.emplace(setting, section); + DCHECK(pair.second); + pair.first->second.primary.second = parent_subpage; +} + +std::string FakeHierarchy::ModifySearchResultUrl( + mojom::Section section, + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const { + return FakeOsSettingsSection::ModifySearchResultUrl(section, url_to_modify); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h new file mode 100644 index 00000000000..d0fbf813ff3 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h @@ -0,0 +1,50 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_HIERARCHY_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_HIERARCHY_H_ + +#include "base/optional.h" +#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h" + +namespace chromeos { +namespace settings { + +class OsSettingsSections; + +// Fake Hierarchy implementation. Note that this class currently does not +// provide "alternate settings location" functionality. +class FakeHierarchy : public Hierarchy { + public: + explicit FakeHierarchy(const OsSettingsSections* sections); + FakeHierarchy(const FakeHierarchy& other) = delete; + FakeHierarchy& operator=(const FakeHierarchy& other) = delete; + ~FakeHierarchy() override; + + void AddSubpageMetadata( + int name_message_id, + mojom::Section section, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters, + base::Optional<mojom::Subpage> parent_subpage = base::nullopt); + void AddSettingMetadata( + mojom::Section section, + mojom::Setting setting, + base::Optional<mojom::Subpage> parent_subpage = base::nullopt); + + private: + // Hierarchy: + std::string ModifySearchResultUrl( + mojom::Section section, + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const override; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_HIERARCHY_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc new file mode 100644 index 00000000000..473287ebec0 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.cc @@ -0,0 +1,52 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h" + +#include <sstream> + +#include "chrome/grit/generated_resources.h" + +namespace chromeos { +namespace settings { + +FakeOsSettingsSection::FakeOsSettingsSection(mojom::Section section) + : section_(section) {} + +FakeOsSettingsSection::~FakeOsSettingsSection() = default; + +int FakeOsSettingsSection::GetSectionNameMessageId() const { + return IDS_INTERNAL_APP_SETTINGS; +} + +mojom::Section FakeOsSettingsSection::GetSection() const { + return section_; +} + +mojom::SearchResultIcon FakeOsSettingsSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kWifi; +} + +std::string FakeOsSettingsSection::GetSectionPath() const { + return std::string(); +} + +std::string FakeOsSettingsSection::ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const { + return ModifySearchResultUrl(section_, url_to_modify); +} + +// static +std::string FakeOsSettingsSection::ModifySearchResultUrl( + mojom::Section section, + const std::string& url_to_modify) { + std::stringstream ss; + ss << section << "::" << url_to_modify; + return ss.str(); +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h new file mode 100644 index 00000000000..4a58417228a --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h @@ -0,0 +1,56 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_ + +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" + +namespace chromeos { +namespace settings { + +// Fake OsSettingsSection implementation. +class FakeOsSettingsSection : public OsSettingsSection { + public: + explicit FakeOsSettingsSection(mojom::Section section); + ~FakeOsSettingsSection() override; + + FakeOsSettingsSection(const FakeOsSettingsSection& other) = delete; + FakeOsSettingsSection& operator=(const FakeOsSettingsSection& other) = delete; + + mojom::Section section() { return section_; } + + // OsSettingsSection: + void AddLoadTimeData(content::WebUIDataSource* html_source) override {} + void RegisterHierarchy(HierarchyGenerator* generator) const override {} + + // Returns the settings app name as a default value. + int GetSectionNameMessageId() const override; + + mojom::Section GetSection() const override; + + // These functions return arbitrary dummy values. + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + + // Prepends the section name and "::" to the URL in |concept|. For example, if + // the URL is "networkDetails" and the section is mojom::Section::kNetwork, + // the returned URL is "Section::kNetwork::networkDetails". + std::string ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const override; + + // Static function used to implement the function above. + static std::string ModifySearchResultUrl(mojom::Section section, + const std::string& url_to_modify); + + private: + const mojom::Section section_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTION_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.cc new file mode 100644 index 00000000000..253d965f65e --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.cc @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h" + +#include "chrome/browser/ui/webui/settings/chromeos/constants/constants_util.h" +#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_section.h" + +namespace chromeos { +namespace settings { + +FakeOsSettingsSections::FakeOsSettingsSections() : OsSettingsSections() { + for (const auto& section : constants::AllSections()) { + auto fake_section = std::make_unique<FakeOsSettingsSection>(section); + sections_map_[section] = fake_section.get(); + sections_.push_back(std::move(fake_section)); + } +} + +FakeOsSettingsSections::~FakeOsSettingsSections() = default; + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h new file mode 100644 index 00000000000..2c148a72a69 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h @@ -0,0 +1,26 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTIONS_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTIONS_H_ + +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" + +namespace chromeos { +namespace settings { + +// Collection of FakeOsSettingsSections. +class FakeOsSettingsSections : public OsSettingsSections { + public: + FakeOsSettingsSections(); + FakeOsSettingsSections(const FakeOsSettingsSections& other) = delete; + FakeOsSettingsSections& operator=(const FakeOsSettingsSections& other) = + delete; + ~FakeOsSettingsSections() override; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_FAKE_OS_SETTINGS_SECTIONS_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.cc index 3b1788101b1..7163b682b59 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.cc @@ -52,7 +52,8 @@ const std::vector<SearchConcept>& GetFilesSearchConcepts() { FilesSection::FilesSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetFilesSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetFilesSearchConcepts()); } FilesSection::~FilesSection() = default; @@ -102,5 +103,31 @@ void FilesSection::AddHandlers(content::WebUI* web_ui) { profile(), base::DoNothing())); } +int FilesSection::GetSectionNameMessageId() const { + return IDS_OS_SETTINGS_FILES; +} + +mojom::Section FilesSection::GetSection() const { + return mojom::Section::kFiles; +} + +mojom::SearchResultIcon FilesSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kFolder; +} + +std::string FilesSection::GetSectionPath() const { + return mojom::kFilesSectionPath; +} + +void FilesSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kGoogleDriveConnection); + + // Network file shares. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_DOWNLOADS_SMB_SHARES, mojom::Subpage::kNetworkFileShares, + mojom::SearchResultIcon::kFolder, mojom::SearchResultDefaultRank::kMedium, + mojom::kNetworkFileSharesSubpagePath); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.h index 635bcb9abc0..38b9dc0b236 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/files_section.h @@ -26,6 +26,11 @@ class FilesSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc index 9ef5ec87581..337e66bd4bb 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/google_assistant_handler.cc @@ -6,8 +6,8 @@ #include <utility> -#include "ash/public/cpp/assistant/assistant_settings.h" #include "ash/public/cpp/assistant/assistant_setup.h" +#include "ash/public/cpp/assistant/controller/assistant_controller.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/values.h" @@ -74,7 +74,7 @@ void GoogleAssistantHandler::RegisterMessages() { void GoogleAssistantHandler::HandleShowGoogleAssistantSettings( const base::ListValue* args) { CHECK_EQ(0U, args->GetSize()); - ash::OpenAssistantSettings(); + ash::AssistantController::Get()->OpenAssistantSettings(); } void GoogleAssistantHandler::HandleRetrainVoiceModel( diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc new file mode 100644 index 00000000000..7eef6651e04 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.cc @@ -0,0 +1,318 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h" + +#include <utility> + +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/constants_util.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" + +namespace chromeos { +namespace settings { +namespace { + +// Used to generate localized names. +constexpr double kDummyRelevanceScore = 0; + +} // namespace + +class Hierarchy::PerSectionHierarchyGenerator + : public OsSettingsSection::HierarchyGenerator { + public: + PerSectionHierarchyGenerator(mojom::Section section, + bool* only_contains_link_to_subpage, + Hierarchy* hierarchy) + : section_(section), + only_contains_link_to_subpage_(only_contains_link_to_subpage), + hierarchy_(hierarchy) {} + + void RegisterTopLevelSubpage( + int name_message_id, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters) override { + Hierarchy::SubpageMetadata& metadata = GetSubpageMetadata( + name_message_id, subpage, icon, default_rank, url_path_with_parameters); + CHECK_EQ(section_, metadata.section) + << "Subpage registered in multiple sections: " << subpage; + + ++num_top_level_subpages_so_far_; + + // If there are multiple top-level subpages, the section contains more than + // just a link to a subpage. + if (num_top_level_subpages_so_far_ > 1u) + *only_contains_link_to_subpage_ = false; + } + + void RegisterNestedSubpage( + int name_message_id, + mojom::Subpage subpage, + mojom::Subpage parent_subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters) override { + Hierarchy::SubpageMetadata& metadata = GetSubpageMetadata( + name_message_id, subpage, icon, default_rank, url_path_with_parameters); + CHECK_EQ(section_, metadata.section) + << "Subpage registered in multiple sections: " << subpage; + CHECK(!metadata.parent_subpage) + << "Subpage has multiple registered parent subpages: " << subpage; + metadata.parent_subpage = parent_subpage; + } + + void RegisterTopLevelSetting(mojom::Setting setting) override { + Hierarchy::SettingMetadata& metadata = GetSettingMetadata(setting); + CHECK_EQ(section_, metadata.primary.first) + << "Setting registered in multiple primary sections: " << setting; + CHECK(!metadata.primary.second) + << "Setting registered in multiple primary locations: " << setting; + + // If a top-level setting exists, the section contains more than just a link + // link to a subpage. + *only_contains_link_to_subpage_ = false; + } + + void RegisterNestedSetting(mojom::Setting setting, + mojom::Subpage subpage) override { + Hierarchy::SettingMetadata& metadata = GetSettingMetadata(setting); + CHECK_EQ(section_, metadata.primary.first) + << "Setting registered in multiple primary sections: " << setting; + CHECK(!metadata.primary.second) + << "Setting registered in multiple primary locations: " << setting; + metadata.primary.second = subpage; + } + + void RegisterTopLevelAltSetting(mojom::Setting setting) override { + Hierarchy::SettingMetadata& metadata = GetSettingMetadata(setting); + CHECK(metadata.primary.first != section_ || metadata.primary.second) + << "Setting's primary and alternate locations are identical: " + << setting; + for (const auto& alternate : metadata.alternates) { + CHECK(alternate.first != section_ || alternate.second) + << "Setting has multiple identical alternate locations: " << setting; + } + metadata.alternates.emplace_back(section_, /*subpage=*/base::nullopt); + + // If a top-level setting exists, the section contains more than just a link + // link to a subpage. + *only_contains_link_to_subpage_ = false; + } + + void RegisterNestedAltSetting(mojom::Setting setting, + mojom::Subpage subpage) override { + Hierarchy::SettingMetadata& metadata = GetSettingMetadata(setting); + CHECK(metadata.primary.first != section_ || + metadata.primary.second != subpage) + << "Setting's primary and alternate locations are identical: " + << setting; + for (const auto& alternate : metadata.alternates) { + CHECK(alternate.first != section_ || alternate.second != subpage) + << "Setting has multiple identical alternate locations: " << setting; + } + metadata.alternates.emplace_back(section_, subpage); + } + + private: + Hierarchy::SubpageMetadata& GetSubpageMetadata( + int name_message_id, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters) { + auto& subpage_map = hierarchy_->subpage_map_; + + auto it = subpage_map.find(subpage); + + // Metadata already exists; return it. + if (it != subpage_map.end()) + return it->second; + + // Metadata does not exist yet; insert then return it. + auto pair = subpage_map.emplace( + std::piecewise_construct, std::forward_as_tuple(subpage), + std::forward_as_tuple(name_message_id, section_, subpage, icon, + default_rank, url_path_with_parameters, + hierarchy_)); + CHECK(pair.second); + return pair.first->second; + } + + Hierarchy::SettingMetadata& GetSettingMetadata(mojom::Setting setting) { + auto& settings_map = hierarchy_->setting_map_; + + auto it = settings_map.find(setting); + + // Metadata already exists; return it. + if (it != settings_map.end()) + return it->second; + + // Metadata does not exist yet; insert then return it. + auto pair = settings_map.emplace(setting, section_); + CHECK(pair.second); + return pair.first->second; + } + + size_t num_top_level_subpages_so_far_ = 0u; + mojom::Section section_; + bool* only_contains_link_to_subpage_; + Hierarchy* hierarchy_; +}; + +// Note: |only_contains_link_to_subpage| starts out as true and is set to false +// if other content is added. +Hierarchy::SectionMetadata::SectionMetadata(mojom::Section section, + const Hierarchy* hierarchy) + : only_contains_link_to_subpage(true), + section_(section), + hierarchy_(hierarchy) {} + +Hierarchy::SectionMetadata::~SectionMetadata() = default; + +mojom::SearchResultPtr Hierarchy::SectionMetadata::ToSearchResult( + double relevance_score) const { + return hierarchy_->sections_->GetSection(section_) + ->GenerateSectionSearchResult(relevance_score); +} + +Hierarchy::SubpageMetadata::SubpageMetadata( + int name_message_id, + mojom::Section section, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters, + const Hierarchy* hierarchy) + : section(section), + subpage_(subpage), + name_message_id_(name_message_id), + icon_(icon), + default_rank_(default_rank), + unmodified_url_path_with_parameters_(url_path_with_parameters), + hierarchy_(hierarchy) {} + +Hierarchy::SubpageMetadata::~SubpageMetadata() = default; + +mojom::SearchResultPtr Hierarchy::SubpageMetadata::ToSearchResult( + double relevance_score) const { + return mojom::SearchResult::New( + /*result_text=*/l10n_util::GetStringUTF16(name_message_id_), + /*canonical_result_text=*/l10n_util::GetStringUTF16(name_message_id_), + hierarchy_->ModifySearchResultUrl( + section, mojom::SearchResultType::kSubpage, {.subpage = subpage_}, + unmodified_url_path_with_parameters_), + icon_, relevance_score, + hierarchy_->GenerateAncestorHierarchyStrings(subpage_), default_rank_, + /*was_generated_from_text_match=*/false, + mojom::SearchResultType::kSubpage, + mojom::SearchResultIdentifier::NewSubpage(subpage_)); +} + +Hierarchy::SettingMetadata::SettingMetadata(mojom::Section primary_section) + : primary(primary_section, /*subpage=*/base::nullopt) {} + +Hierarchy::SettingMetadata::~SettingMetadata() = default; + +Hierarchy::Hierarchy(const OsSettingsSections* sections) : sections_(sections) { + for (const auto& section : constants::AllSections()) { + auto pair = section_map_.emplace(std::piecewise_construct, + std::forward_as_tuple(section), + std::forward_as_tuple(section, this)); + CHECK(pair.second); + + PerSectionHierarchyGenerator generator( + section, &pair.first->second.only_contains_link_to_subpage, this); + sections->GetSection(section)->RegisterHierarchy(&generator); + } +} + +Hierarchy::~Hierarchy() = default; + +const Hierarchy::SectionMetadata& Hierarchy::GetSectionMetadata( + mojom::Section section) const { + const auto it = section_map_.find(section); + CHECK(it != section_map_.end()) + << "Section missing from settings hierarchy: " << section; + return it->second; +} + +const Hierarchy::SubpageMetadata& Hierarchy::GetSubpageMetadata( + mojom::Subpage subpage) const { + const auto it = subpage_map_.find(subpage); + CHECK(it != subpage_map_.end()) + << "Subpage missing from settings hierarchy: " << subpage; + return it->second; +} + +const Hierarchy::SettingMetadata& Hierarchy::GetSettingMetadata( + mojom::Setting setting) const { + const auto it = setting_map_.find(setting); + CHECK(it != setting_map_.end()) + << "Setting missing from settings hierarchy: " << setting; + return it->second; +} + +std::string Hierarchy::ModifySearchResultUrl( + mojom::Section section, + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const { + return sections_->GetSection(section)->ModifySearchResultUrl(type, id, + url_to_modify); +} + +std::vector<base::string16> Hierarchy::GenerateAncestorHierarchyStrings( + mojom::Subpage subpage) const { + const SubpageMetadata& subpage_metadata = GetSubpageMetadata(subpage); + + // Top-level subpage; simply return section hierarchy. + if (!subpage_metadata.parent_subpage) + return GenerateHierarchyStrings(subpage_metadata.section); + + // Nested subpage; use recursive call, then append parent subpage name itself. + std::vector<base::string16> hierarchy_strings = + GenerateAncestorHierarchyStrings(*subpage_metadata.parent_subpage); + hierarchy_strings.push_back( + GetSubpageMetadata(*subpage_metadata.parent_subpage) + .ToSearchResult(kDummyRelevanceScore) + ->result_text); + return hierarchy_strings; +} + +std::vector<base::string16> Hierarchy::GenerateAncestorHierarchyStrings( + mojom::Setting setting) const { + const SettingMetadata& setting_metadata = GetSettingMetadata(setting); + + // Top-level setting; simply return section hierarchy. + if (!setting_metadata.primary.second) + return GenerateHierarchyStrings(setting_metadata.primary.first); + + // Nested setting; use subpage ancestors, then append subpage name itself. + std::vector<base::string16> hierarchy_strings = + GenerateAncestorHierarchyStrings(*setting_metadata.primary.second); + hierarchy_strings.push_back( + GetSubpageMetadata(*setting_metadata.primary.second) + .ToSearchResult(kDummyRelevanceScore) + ->result_text); + return hierarchy_strings; +} + +std::vector<base::string16> Hierarchy::GenerateHierarchyStrings( + mojom::Section section) const { + std::vector<base::string16> hierarchy_strings; + hierarchy_strings.push_back( + l10n_util::GetStringUTF16(IDS_INTERNAL_APP_SETTINGS)); + hierarchy_strings.push_back(GetSectionMetadata(section) + .ToSearchResult(kDummyRelevanceScore) + ->result_text); + return hierarchy_strings; +} + +} // namespace settings +} // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.h b/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.h new file mode 100644 index 00000000000..14b915670d4 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/hierarchy.h @@ -0,0 +1,171 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_HIERARCHY_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_HIERARCHY_H_ + +#include <unordered_map> +#include <utility> +#include <vector> + +#include "base/optional.h" +#include "base/strings/string16.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" + +namespace chromeos { +namespace settings { + +class OsSettingsSections; + +// Tracks the OS settings page hierarchy. Settings is composed of a group of +// sections containing subpages and/or settings, and this class provides +// metadata for where these subpages/settings reside and what localized strings +// are used to describe them. +// +// A subpage can either be a direct child of a section or can be a nested +// subpage, meaning that its parent is another subpage. +// +// A setting can either be embedded as a direct child of a section or can be +// a child of a subpage. Additionally, some settings appear in multiple places. +// For example, the Wi-Fi on/off toggle appears in both the top-level Network +// section as well as the Wi-Fi subpage. In cases like this, we consider the +// "primary" location as the more-targeted one - in this example, the Wi-Fi +// subpage is the primary location of the toggle since it is more specific to +// Wi-Fi, and the alternate location is the one embedded in the Network section. +class Hierarchy { + public: + explicit Hierarchy(const OsSettingsSections* sections); + Hierarchy(const Hierarchy& other) = delete; + Hierarchy& operator=(const Hierarchy& other) = delete; + virtual ~Hierarchy(); + + class SectionMetadata { + public: + SectionMetadata(mojom::Section section, const Hierarchy* hierarchy); + ~SectionMetadata(); + + // Whether the only contents of the section is a link to a subpage. + bool only_contains_link_to_subpage; + + // Generates a search result for this section, using the canonical search + // tag as the search result text. |relevance_score| must be passed by the + // client, since this result is being created manually instead of via query + // matching. + mojom::SearchResultPtr ToSearchResult(double relevance_score) const; + + private: + mojom::Section section_; + const Hierarchy* hierarchy_; + }; + + class SubpageMetadata { + public: + SubpageMetadata(int name_message_id, + mojom::Section section, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters, + const Hierarchy* hierarchy); + ~SubpageMetadata(); + + // The section in which the subpage appears. + mojom::Section section; + + // The parent subpage, if applicable. Only applies to nested subpages. + base::Optional<mojom::Subpage> parent_subpage; + + // Generates a search result for this subpage, using the canonical search + // tag as the search result text. |relevance_score| must be passed by the + // client, since this result is being created manually instead of via query + // matching. + mojom::SearchResultPtr ToSearchResult(double relevance_score) const; + + private: + mojom::Subpage subpage_; + + // Message ID corresponding to the localized string used to describe this + // subpage. + int name_message_id_; + + // Icon used for this subpage. + mojom::SearchResultIcon icon_; + + // Default rank; used to order returned results. + mojom::SearchResultDefaultRank default_rank_; + + // Static URL path, which may need to be modified via + // |modify_url_callback_|. + std::string unmodified_url_path_with_parameters_; + + const Hierarchy* hierarchy_; + }; + + // The location of a setting, which includes its section and, if applicable, + // its subpage. Some settings are embedded directly into the section and have + // no associated subpage. + using SettingLocation = + std::pair<mojom::Section, base::Optional<mojom::Subpage>>; + + struct SettingMetadata { + explicit SettingMetadata(mojom::Section primary_section); + ~SettingMetadata(); + + // The primary location, as described above. + SettingLocation primary; + + // Alternate locations, as described above. Empty if the setting has no + // alternate location. + std::vector<SettingLocation> alternates; + }; + + const SectionMetadata& GetSectionMetadata(mojom::Section section) const; + const SubpageMetadata& GetSubpageMetadata(mojom::Subpage subpage) const; + const SettingMetadata& GetSettingMetadata(mojom::Setting setting) const; + + // Generates a list of names of the ancestor sections/subpages for |subpage|. + // The list contains the Settings app name, the section name and, if + // applicable, parent subpage names. Names returned in this list are all + // localized string16s which can be displayed in the UI (e.g., as + // breadcrumbs). + // + // Example 1 - Wi-Fi Networks subpage (no parent subpage): + // ["Settings", "Network"] + // Example 2 - External storage (has parent subpage): + // ["Settings", "Device", "Storage management"] + std::vector<base::string16> GenerateAncestorHierarchyStrings( + mojom::Subpage subpage) const; + + // Same as above, but for settings. + std::vector<base::string16> GenerateAncestorHierarchyStrings( + mojom::Setting setting) const; + + protected: + std::unordered_map<mojom::Section, SectionMetadata> section_map_; + std::unordered_map<mojom::Subpage, SubpageMetadata> subpage_map_; + std::unordered_map<mojom::Setting, SettingMetadata> setting_map_; + + private: + class PerSectionHierarchyGenerator; + + // Generates an array with the Settings app name and |section|'s name. + std::vector<base::string16> GenerateHierarchyStrings( + mojom::Section section) const; + + virtual std::string ModifySearchResultUrl( + mojom::Section section, + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const; + + const OsSettingsSections* sections_; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_HIERARCHY_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.cc index c21b0002fb8..69248ed127e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.cc @@ -8,11 +8,13 @@ #include "ash/public/cpp/network_config_service.h" #include "base/bind.h" #include "base/no_destructor.h" +#include "base/strings/stringprintf.h" #include "chrome/browser/ui/webui/chromeos/network_element_localized_strings_provider.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/internet_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "chrome/browser/ui/webui/webui_util.h" +#include "chrome/common/chrome_features.h" #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/chromium_strings.h" @@ -32,7 +34,7 @@ const std::vector<SearchConcept>& GetNetworkSearchConcepts() { {IDS_OS_SETTINGS_TAG_NETWORK_SETTINGS, mojom::kNetworkSectionPath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSection, {.section = mojom::Section::kNetwork}, {IDS_OS_SETTINGS_TAG_NETWORK_SETTINGS_ALT1, SearchConcept::kAltTagEnd}}, @@ -57,7 +59,7 @@ const std::vector<SearchConcept>& GetEthernetConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY, mojom::kEthernetDetailsSubpagePath, mojom::SearchResultIcon::kEthernet, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kEthernetAutoConfigureIp}, {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY_ALT1, @@ -66,7 +68,7 @@ const std::vector<SearchConcept>& GetEthernetConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_NAME_SERVERS, mojom::kEthernetDetailsSubpagePath, mojom::SearchResultIcon::kEthernet, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kEthernetDns}, {IDS_OS_SETTINGS_TAG_NAME_SERVERS_ALT1, @@ -74,7 +76,7 @@ const std::vector<SearchConcept>& GetEthernetConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_PROXY, mojom::kEthernetDetailsSubpagePath, mojom::SearchResultIcon::kEthernet, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kEthernetProxy}, {IDS_OS_SETTINGS_TAG_PROXY_ALT1, IDS_OS_SETTINGS_TAG_PROXY_ALT2, @@ -92,12 +94,11 @@ const std::vector<SearchConcept>& GetWifiSearchConcepts() { mojom::SearchResultDefaultRank::kHigh, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kWifiNetworks}, - {IDS_OS_SETTINGS_TAG_WIFI_ALT1, IDS_OS_SETTINGS_TAG_WIFI_ALT2, - SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_WIFI_ALT1, SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_KNOWN_NETWORKS, mojom::kKnownNetworksSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kKnownNetworks}, {IDS_OS_SETTINGS_TAG_KNOWN_NETWORKS_ALT1, @@ -115,6 +116,12 @@ const std::vector<SearchConcept>& GetWifiOnSearchConcepts() { mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kWifiOnOff}, {IDS_OS_SETTINGS_TAG_WIFI_TURN_OFF_ALT1, SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_ADD_WIFI, + mojom::kWifiNetworksSubpagePath, + mojom::SearchResultIcon::kWifi, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kWifiAddNetwork}}, }); return *tags; } @@ -143,17 +150,11 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_PREFER_WIFI_NETWORK, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kPreferWifiNetwork}, {IDS_OS_SETTINGS_TAG_PREFER_WIFI_NETWORK_ALT1, SearchConcept::kAltTagEnd}}, - {IDS_OS_SETTINGS_TAG_WIFI_CONFIGURE, - mojom::kWifiDetailsSubpagePath, - mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kConfigureWifi}}, {IDS_OS_SETTINGS_TAG_FORGET_WIFI, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, @@ -163,7 +164,7 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kWifiAutoConfigureIp}, {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY_ALT1, @@ -172,7 +173,7 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_NAME_SERVERS, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kWifiDns}, {IDS_OS_SETTINGS_TAG_NAME_SERVERS_ALT1, @@ -180,7 +181,7 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_PROXY, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kWifiProxy}, {IDS_OS_SETTINGS_TAG_PROXY_ALT1, IDS_OS_SETTINGS_TAG_PROXY_ALT2, @@ -189,7 +190,7 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_AUTO_CONNECT_NETWORK, mojom::kWifiDetailsSubpagePath, mojom::SearchResultIcon::kWifi, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kWifiAutoConnectToNetwork}, {IDS_OS_SETTINGS_TAG_AUTO_CONNECT_NETWORK_ALT1, @@ -198,6 +199,18 @@ const std::vector<SearchConcept>& GetWifiConnectedSearchConcepts() { return *tags; } +const std::vector<SearchConcept>& GetWifiMeteredSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_SETTINGS_INTERNET_NETWORK_METERED, + mojom::kWifiDetailsSubpagePath, + mojom::SearchResultIcon::kWifi, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kWifiMetered}}, + }); + return *tags; +} + const std::vector<SearchConcept>& GetCellularSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_CELLULAR, @@ -207,14 +220,7 @@ const std::vector<SearchConcept>& GetCellularSearchConcepts() { mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kCellularDetails}, {IDS_OS_SETTINGS_TAG_CELLULAR_ALT1, IDS_OS_SETTINGS_TAG_CELLULAR_ALT2, - IDS_OS_SETTINGS_TAG_CELLULAR_ALT3, IDS_OS_SETTINGS_TAG_CELLULAR_ALT4, - SearchConcept::kAltTagEnd}}, - }); - return *tags; -} - -const std::vector<SearchConcept>& GetCellularOnSearchConcepts() { - static const base::NoDestructor<std::vector<SearchConcept>> tags({ + IDS_OS_SETTINGS_TAG_CELLULAR_ALT3, SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_CELLULAR_SIM_LOCK, mojom::kCellularDetailsSubpagePath, mojom::SearchResultIcon::kCellular, @@ -228,6 +234,18 @@ const std::vector<SearchConcept>& GetCellularOnSearchConcepts() { mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kCellularRoaming}}, + {IDS_OS_SETTINGS_TAG_CELLULAR_APN, + mojom::kCellularDetailsSubpagePath, + mojom::SearchResultIcon::kCellular, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kCellularApn}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetCellularOnSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_CELLULAR_TURN_OFF, mojom::kNetworkSectionPath, mojom::SearchResultIcon::kCellular, @@ -235,12 +253,6 @@ const std::vector<SearchConcept>& GetCellularOnSearchConcepts() { mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kMobileOnOff}, {IDS_OS_SETTINGS_TAG_CELLULAR_TURN_OFF_ALT1, SearchConcept::kAltTagEnd}}, - {IDS_OS_SETTINGS_TAG_CELLULAR_APN, - mojom::kCellularDetailsSubpagePath, - mojom::SearchResultIcon::kCellular, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kCellularApn}}, }); return *tags; } @@ -269,7 +281,7 @@ const std::vector<SearchConcept>& GetCellularConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY, mojom::kCellularDetailsSubpagePath, mojom::SearchResultIcon::kCellular, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kCellularAutoConfigureIp}, {IDS_OS_SETTINGS_TAG_CONFIGURE_IP_AUTOMATICALLY_ALT1, @@ -278,7 +290,7 @@ const std::vector<SearchConcept>& GetCellularConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_NAME_SERVERS, mojom::kCellularDetailsSubpagePath, mojom::SearchResultIcon::kCellular, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kCellularDns}, {IDS_OS_SETTINGS_TAG_NAME_SERVERS_ALT1, @@ -286,7 +298,7 @@ const std::vector<SearchConcept>& GetCellularConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_PROXY, mojom::kCellularDetailsSubpagePath, mojom::SearchResultIcon::kCellular, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kCellularProxy}, {IDS_OS_SETTINGS_TAG_PROXY_ALT1, IDS_OS_SETTINGS_TAG_PROXY_ALT2, @@ -295,7 +307,7 @@ const std::vector<SearchConcept>& GetCellularConnectedSearchConcepts() { {IDS_OS_SETTINGS_TAG_AUTO_CONNECT_NETWORK, mojom::kCellularDetailsSubpagePath, mojom::SearchResultIcon::kCellular, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kCellularAutoConnectToNetwork}, {IDS_OS_SETTINGS_TAG_AUTO_CONNECT_NETWORK_ALT1, @@ -304,6 +316,18 @@ const std::vector<SearchConcept>& GetCellularConnectedSearchConcepts() { return *tags; } +const std::vector<SearchConcept>& GetCellularMeteredSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_SETTINGS_INTERNET_NETWORK_METERED, + mojom::kCellularDetailsSubpagePath, + mojom::SearchResultIcon::kCellular, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kCellularMetered}}, + }); + return *tags; +} + const std::vector<SearchConcept>& GetInstantTetheringSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_INSTANT_MOBILE_NETWORKS, @@ -374,6 +398,52 @@ const std::vector<SearchConcept>& GetVpnConnectedSearchConcepts() { return *tags; } +const std::vector<mojom::Setting>& GetEthernetDetailsSettings() { + static const base::NoDestructor<std::vector<mojom::Setting>> settings({ + mojom::Setting::kConfigureEthernet, + mojom::Setting::kEthernetAutoConfigureIp, + mojom::Setting::kEthernetDns, + mojom::Setting::kEthernetProxy, + }); + return *settings; +} + +const std::vector<mojom::Setting>& GetWifiDetailsSettings() { + static const base::NoDestructor<std::vector<mojom::Setting>> settings({ + mojom::Setting::kDisconnectWifiNetwork, + mojom::Setting::kPreferWifiNetwork, + mojom::Setting::kForgetWifiNetwork, + mojom::Setting::kWifiAutoConfigureIp, + mojom::Setting::kWifiDns, + mojom::Setting::kWifiProxy, + mojom::Setting::kWifiAutoConnectToNetwork, + mojom::Setting::kWifiMetered, + }); + return *settings; +} + +const std::vector<mojom::Setting>& GetCellularDetailsSettings() { + static const base::NoDestructor<std::vector<mojom::Setting>> settings({ + mojom::Setting::kCellularSimLock, + mojom::Setting::kCellularRoaming, + mojom::Setting::kCellularApn, + mojom::Setting::kDisconnectCellularNetwork, + mojom::Setting::kCellularAutoConfigureIp, + mojom::Setting::kCellularDns, + mojom::Setting::kCellularProxy, + mojom::Setting::kCellularAutoConnectToNetwork, + mojom::Setting::kCellularMetered, + }); + return *settings; +} + +const std::vector<mojom::Setting>& GetTetherDetailsSettings() { + static const base::NoDestructor<std::vector<mojom::Setting>> settings({ + mojom::Setting::kDisconnectTetherNetwork, + }); + return *settings; +} + bool IsConnected(network_config::mojom::ConnectionStateType connection_state) { return connection_state == network_config::mojom::ConnectionStateType::kOnline || @@ -381,13 +451,49 @@ bool IsConnected(network_config::mojom::ConnectionStateType connection_state) { network_config::mojom::ConnectionStateType::kConnected; } +bool IsPartOfDetailsSubpage(mojom::SearchResultType type, + OsSettingsIdentifier id, + mojom::Subpage details_subpage) { + switch (type) { + case mojom::SearchResultType::kSection: + // Applies to a section, not a details subpage. + return false; + + case mojom::SearchResultType::kSubpage: + return id.subpage == details_subpage; + + case mojom::SearchResultType::kSetting: { + const mojom::Setting& setting = id.setting; + switch (details_subpage) { + case mojom::Subpage::kEthernetDetails: + return base::Contains(GetEthernetDetailsSettings(), setting); + case mojom::Subpage::kWifiDetails: + return base::Contains(GetWifiDetailsSettings(), setting); + case mojom::Subpage::kCellularDetails: + return base::Contains(GetCellularDetailsSettings(), setting); + case mojom::Subpage::kTetherDetails: + return base::Contains(GetTetherDetailsSettings(), setting); + default: + return false; + } + } + } +} + +std::string GetDetailsSubpageUrl(const std::string& url_to_modify, + const std::string& guid) { + return base::StringPrintf("%s?guid=%s", url_to_modify.c_str(), guid.c_str()); +} + } // namespace InternetSection::InternetSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + // General network search tags are always added. - registry()->AddSearchTags(GetNetworkSearchConcepts()); + updater.AddSearchTags(GetNetworkSearchConcepts()); // Receive updates when devices (e.g., Ethernet, Wi-Fi) go on/offline. ash::GetNetworkConfigService( @@ -396,7 +502,7 @@ InternetSection::InternetSection(Profile* profile, // Fetch initial list of devices and active networks. FetchDeviceList(); - FetchActiveNetworks(); + FetchNetworkList(); } InternetSection::~InternetSection() = default; @@ -414,6 +520,7 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"internetConfigName", IDS_SETTINGS_INTERNET_CONFIG_NAME}, {"internetDetailPageTitle", IDS_SETTINGS_INTERNET_DETAIL}, {"internetDeviceEnabling", IDS_SETTINGS_INTERNET_DEVICE_ENABLING}, + {"internetDeviceDisabling", IDS_SETTINGS_INTERNET_DEVICE_DISABLING}, {"internetDeviceInitializing", IDS_SETTINGS_INTERNET_DEVICE_INITIALIZING}, {"internetJoinType", IDS_SETTINGS_INTERNET_JOIN_TYPE}, {"internetKnownNetworksPageTitle", IDS_SETTINGS_INTERNET_KNOWN_NETWORKS}, @@ -461,6 +568,7 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"networkConnectNotAllowed", IDS_SETTINGS_INTERNET_CONNECT_NOT_ALLOWED}, {"networkIPAddress", IDS_SETTINGS_INTERNET_NETWORK_IP_ADDRESS}, {"networkIPConfigAuto", IDS_SETTINGS_INTERNET_NETWORK_IP_CONFIG_AUTO}, + {"networkMetered", IDS_SETTINGS_INTERNET_NETWORK_METERED}, {"networkNameserversLearnMore", IDS_LEARN_MORE}, {"networkPrefer", IDS_SETTINGS_INTERNET_NETWORK_PREFER}, {"networkPrimaryUserControlled", @@ -477,6 +585,8 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"networkSectionProxyExpandA11yLabel", IDS_SETTINGS_INTERNET_NETWORK_SECTION_PROXY_ACCESSIBILITY_LABEL}, {"networkShared", IDS_SETTINGS_INTERNET_NETWORK_SHARED}, + {"networkSharedOwner", IDS_SETTINGS_INTERNET_NETWORK_SHARED_OWNER}, + {"networkSharedNotOwner", IDS_SETTINGS_INTERNET_NETWORK_SHARED_NOT_OWNER}, {"networkVpnBuiltin", IDS_NETWORK_TYPE_VPN_BUILTIN}, {"networkOutOfRange", IDS_SETTINGS_INTERNET_WIFI_NETWORK_OUT_OF_RANGE}, {"cellularContactSpecificCarrier", @@ -535,14 +645,17 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) { html_source->AddBoolean("showTechnologyBadge", !ash::features::IsSeparateNetworkIconsEnabled()); + html_source->AddBoolean( + "showMeteredToggle", + base::FeatureList::IsEnabled(features::kMeteredShowToggle)); html_source->AddString("networkGoogleNameserversLearnMoreUrl", chrome::kGoogleNameserversLearnMoreURL); html_source->AddString( - "networkSyncedShared", + "networkNotSynced", l10n_util::GetStringFUTF16( - IDS_SETTINGS_INTERNET_NETWORK_SYNCED_SHARED, + IDS_SETTINGS_INTERNET_NETWORK_NOT_SYNCED, GetHelpUrlWithBoard(chrome::kWifiSyncLearnMoreURL))); html_source->AddString( "networkSyncedUser", @@ -550,6 +663,11 @@ void InternetSection::AddLoadTimeData(content::WebUIDataSource* html_source) { IDS_SETTINGS_INTERNET_NETWORK_SYNCED_USER, GetHelpUrlWithBoard(chrome::kWifiSyncLearnMoreURL))); html_source->AddString( + "networkSyncedDevice", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_INTERNET_NETWORK_SYNCED_DEVICE, + GetHelpUrlWithBoard(chrome::kWifiSyncLearnMoreURL))); + html_source->AddString( "internetNoNetworksMobileData", l10n_util::GetStringFUTF16( IDS_SETTINGS_INTERNET_LOOKING_FOR_MOBILE_NETWORK, @@ -560,13 +678,140 @@ void InternetSection::AddHandlers(content::WebUI* web_ui) { web_ui->AddMessageHandler(std::make_unique<InternetHandler>(profile())); } +int InternetSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_INTERNET; +} + +mojom::Section InternetSection::GetSection() const { + return mojom::Section::kNetwork; +} + +mojom::SearchResultIcon InternetSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kWifi; +} + +std::string InternetSection::GetSectionPath() const { + return mojom::kNetworkSectionPath; +} + +void InternetSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // Ethernet details. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_INTERNET_ETHERNET_DETAILS, + mojom::Subpage::kEthernetDetails, + mojom::SearchResultIcon::kEthernet, + mojom::SearchResultDefaultRank::kMedium, + mojom::kEthernetDetailsSubpagePath); + RegisterNestedSettingBulk(mojom::Subpage::kEthernetDetails, + GetEthernetDetailsSettings(), generator); + + // Wi-Fi networks. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_INTERNET_WIFI_NETWORKS, mojom::Subpage::kWifiNetworks, + mojom::SearchResultIcon::kWifi, mojom::SearchResultDefaultRank::kMedium, + mojom::kWifiNetworksSubpagePath); + static constexpr mojom::Setting kWifiNetworksSettings[] = { + mojom::Setting::kWifiOnOff, + mojom::Setting::kWifiAddNetwork, + }; + RegisterNestedSettingBulk(mojom::Subpage::kWifiNetworks, + kWifiNetworksSettings, generator); + generator->RegisterTopLevelAltSetting(mojom::Setting::kWifiOnOff); + + // Wi-Fi details. + generator->RegisterNestedSubpage( + IDS_SETTINGS_INTERNET_WIFI_DETAILS, mojom::Subpage::kWifiDetails, + mojom::Subpage::kWifiNetworks, mojom::SearchResultIcon::kWifi, + mojom::SearchResultDefaultRank::kMedium, mojom::kWifiDetailsSubpagePath); + RegisterNestedSettingBulk(mojom::Subpage::kWifiDetails, + GetWifiDetailsSettings(), generator); + + // Known networks. + generator->RegisterNestedSubpage( + IDS_SETTINGS_INTERNET_KNOWN_NETWORKS, mojom::Subpage::kKnownNetworks, + mojom::Subpage::kWifiNetworks, mojom::SearchResultIcon::kWifi, + mojom::SearchResultDefaultRank::kMedium, + mojom::kKnownNetworksSubpagePath); + generator->RegisterNestedAltSetting(mojom::Setting::kPreferWifiNetwork, + mojom::Subpage::kKnownNetworks); + generator->RegisterNestedAltSetting(mojom::Setting::kForgetWifiNetwork, + mojom::Subpage::kKnownNetworks); + + // Mobile data. If Instant Tethering is available, a mobile data subpage is + // available which lists both Cellular and Instant Tethering networks. If + // Instant Tethering is not available, there is no mobile data subpage. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_INTERNET_MOBILE_DATA_NETWORKS, + mojom::Subpage::kMobileDataNetworks, + mojom::SearchResultIcon::kCellular, + mojom::SearchResultDefaultRank::kMedium, + mojom::kMobileDataNetworksSubpagePath); + static constexpr mojom::Setting kMobileDataNetworksSettings[] = { + mojom::Setting::kMobileOnOff, + mojom::Setting::kInstantTetheringOnOff, + }; + RegisterNestedSettingBulk(mojom::Subpage::kMobileDataNetworks, + kMobileDataNetworksSettings, generator); + generator->RegisterTopLevelAltSetting(mojom::Setting::kMobileOnOff); + + // Cellular details. Cellular details are considered a child of the mobile + // data subpage. However, note that if Instant Tethering is not available, + // clicking on "Mobile data" at the Network section navigates users directly + // to the cellular details page and skips over the mobile data subpage. + generator->RegisterNestedSubpage( + IDS_SETTINGS_INTERNET_CELLULAR_DETAILS, mojom::Subpage::kCellularDetails, + mojom::Subpage::kMobileDataNetworks, mojom::SearchResultIcon::kCellular, + mojom::SearchResultDefaultRank::kMedium, + mojom::kCellularDetailsSubpagePath); + RegisterNestedSettingBulk(mojom::Subpage::kCellularDetails, + GetCellularDetailsSettings(), generator); + + // Instant Tethering. Although this is a multi-device feature, its UI resides + // in the network section. + generator->RegisterNestedSubpage( + IDS_SETTINGS_INTERNET_INSTANT_TETHERING_DETAILS, + mojom::Subpage::kTetherDetails, mojom::Subpage::kMobileDataNetworks, + mojom::SearchResultIcon::kInstantTethering, + mojom::SearchResultDefaultRank::kMedium, + mojom::kTetherDetailsSubpagePath); + RegisterNestedSettingBulk(mojom::Subpage::kTetherDetails, + GetTetherDetailsSettings(), generator); + + // VPN. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_INTERNET_VPN_DETAILS, mojom::Subpage::kVpnDetails, + mojom::SearchResultIcon::kWifi, mojom::SearchResultDefaultRank::kMedium, + mojom::kVpnDetailsSubpagePath); +} + +std::string InternetSection::ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const { + if (IsPartOfDetailsSubpage(type, id, mojom::Subpage::kEthernetDetails)) + return GetDetailsSubpageUrl(url_to_modify, *connected_ethernet_guid_); + + if (IsPartOfDetailsSubpage(type, id, mojom::Subpage::kWifiDetails)) + return GetDetailsSubpageUrl(url_to_modify, *connected_wifi_guid_); + + if (IsPartOfDetailsSubpage(type, id, mojom::Subpage::kCellularDetails)) + return GetDetailsSubpageUrl(url_to_modify, *cellular_guid_); + + if (IsPartOfDetailsSubpage(type, id, mojom::Subpage::kTetherDetails)) + return GetDetailsSubpageUrl(url_to_modify, *connected_tether_guid_); + + if (IsPartOfDetailsSubpage(type, id, mojom::Subpage::kVpnDetails)) + return GetDetailsSubpageUrl(url_to_modify, *connected_vpn_guid_); + + // URL does not need to be modified; use default implementation. + return OsSettingsSection::ModifySearchResultUrl(type, id, url_to_modify); +} + void InternetSection::OnDeviceStateListChanged() { FetchDeviceList(); } void InternetSection::OnActiveNetworksChanged( std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) { - OnActiveNetworks(std::move(networks)); + FetchNetworkList(); } void InternetSection::FetchDeviceList() { @@ -579,40 +824,43 @@ void InternetSection::OnDeviceList( using network_config::mojom::DeviceStateType; using network_config::mojom::NetworkType; - registry()->RemoveSearchTags(GetWifiSearchConcepts()); - registry()->RemoveSearchTags(GetWifiOnSearchConcepts()); - registry()->RemoveSearchTags(GetWifiOffSearchConcepts()); - registry()->RemoveSearchTags(GetCellularSearchConcepts()); - registry()->RemoveSearchTags(GetCellularOnSearchConcepts()); - registry()->RemoveSearchTags(GetCellularOffSearchConcepts()); - registry()->RemoveSearchTags(GetInstantTetheringSearchConcepts()); - registry()->RemoveSearchTags(GetInstantTetheringOnSearchConcepts()); - registry()->RemoveSearchTags(GetInstantTetheringOffSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + updater.RemoveSearchTags(GetWifiSearchConcepts()); + updater.RemoveSearchTags(GetWifiOnSearchConcepts()); + updater.RemoveSearchTags(GetWifiOffSearchConcepts()); + updater.RemoveSearchTags(GetCellularOnSearchConcepts()); + updater.RemoveSearchTags(GetCellularOffSearchConcepts()); + updater.RemoveSearchTags(GetInstantTetheringSearchConcepts()); + updater.RemoveSearchTags(GetInstantTetheringOnSearchConcepts()); + updater.RemoveSearchTags(GetInstantTetheringOffSearchConcepts()); for (const auto& device : devices) { switch (device->type) { case NetworkType::kWiFi: - registry()->AddSearchTags(GetWifiSearchConcepts()); + updater.AddSearchTags(GetWifiSearchConcepts()); if (device->device_state == DeviceStateType::kEnabled) - registry()->AddSearchTags(GetWifiOnSearchConcepts()); + updater.AddSearchTags(GetWifiOnSearchConcepts()); else if (device->device_state == DeviceStateType::kDisabled) - registry()->AddSearchTags(GetWifiOffSearchConcepts()); + updater.AddSearchTags(GetWifiOffSearchConcepts()); break; case NetworkType::kCellular: - registry()->AddSearchTags(GetCellularSearchConcepts()); + // Note: Cellular search concepts all point to the cellular details + // page, which is only available if a cellular network exists. This + // check is in OnNetworkList(). if (device->device_state == DeviceStateType::kEnabled) - registry()->AddSearchTags(GetCellularOnSearchConcepts()); + updater.AddSearchTags(GetCellularOnSearchConcepts()); else if (device->device_state == DeviceStateType::kDisabled) - registry()->AddSearchTags(GetCellularOffSearchConcepts()); + updater.AddSearchTags(GetCellularOffSearchConcepts()); break; case NetworkType::kTether: - registry()->AddSearchTags(GetInstantTetheringSearchConcepts()); + updater.AddSearchTags(GetInstantTetheringSearchConcepts()); if (device->device_state == DeviceStateType::kEnabled) - registry()->AddSearchTags(GetInstantTetheringOnSearchConcepts()); + updater.AddSearchTags(GetInstantTetheringOnSearchConcepts()); else if (device->device_state == DeviceStateType::kDisabled) - registry()->AddSearchTags(GetInstantTetheringOffSearchConcepts()); + updater.AddSearchTags(GetInstantTetheringOffSearchConcepts()); break; default: @@ -623,48 +871,76 @@ void InternetSection::OnDeviceList( } } -void InternetSection::FetchActiveNetworks() { +void InternetSection::FetchNetworkList() { cros_network_config_->GetNetworkStateList( network_config::mojom::NetworkFilter::New( - network_config::mojom::FilterType::kActive, + network_config::mojom::FilterType::kVisible, network_config::mojom::NetworkType::kAll, network_config::mojom::kNoLimit), - base::Bind(&InternetSection::OnActiveNetworks, base::Unretained(this))); + base::Bind(&InternetSection::OnNetworkList, base::Unretained(this))); } -void InternetSection::OnActiveNetworks( +void InternetSection::OnNetworkList( std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks) { using network_config::mojom::NetworkType; - registry()->RemoveSearchTags(GetEthernetConnectedSearchConcepts()); - registry()->RemoveSearchTags(GetWifiConnectedSearchConcepts()); - registry()->RemoveSearchTags(GetCellularConnectedSearchConcepts()); - registry()->RemoveSearchTags(GetInstantTetheringConnectedSearchConcepts()); - registry()->RemoveSearchTags(GetVpnConnectedSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + updater.RemoveSearchTags(GetEthernetConnectedSearchConcepts()); + updater.RemoveSearchTags(GetWifiConnectedSearchConcepts()); + updater.RemoveSearchTags(GetWifiMeteredSearchConcepts()); + updater.RemoveSearchTags(GetCellularSearchConcepts()); + updater.RemoveSearchTags(GetCellularConnectedSearchConcepts()); + updater.RemoveSearchTags(GetCellularMeteredSearchConcepts()); + updater.RemoveSearchTags(GetInstantTetheringConnectedSearchConcepts()); + updater.RemoveSearchTags(GetVpnConnectedSearchConcepts()); + + cellular_guid_.reset(); + + connected_ethernet_guid_.reset(); + connected_wifi_guid_.reset(); + connected_tether_guid_.reset(); + connected_vpn_guid_.reset(); for (const auto& network : networks) { + // Special case: Some cellular search functionality is available even if the + // network is not connected. + if (network->type == NetworkType::kCellular) { + cellular_guid_ = network->guid; + updater.AddSearchTags(GetCellularSearchConcepts()); + } + if (!IsConnected(network->connection_state)) continue; switch (network->type) { case NetworkType::kEthernet: - registry()->AddSearchTags(GetEthernetConnectedSearchConcepts()); + connected_ethernet_guid_ = network->guid; + updater.AddSearchTags(GetEthernetConnectedSearchConcepts()); break; case NetworkType::kWiFi: - registry()->AddSearchTags(GetWifiConnectedSearchConcepts()); + connected_wifi_guid_ = network->guid; + updater.AddSearchTags(GetWifiConnectedSearchConcepts()); + if (base::FeatureList::IsEnabled(features::kMeteredShowToggle)) + updater.AddSearchTags(GetWifiMeteredSearchConcepts()); break; case NetworkType::kCellular: - registry()->AddSearchTags(GetCellularConnectedSearchConcepts()); + // Note: GUID is set above. + updater.AddSearchTags(GetCellularConnectedSearchConcepts()); + if (base::FeatureList::IsEnabled(features::kMeteredShowToggle)) + updater.AddSearchTags(GetCellularMeteredSearchConcepts()); break; case NetworkType::kTether: - registry()->AddSearchTags(GetInstantTetheringConnectedSearchConcepts()); + connected_tether_guid_ = network->guid; + updater.AddSearchTags(GetInstantTetheringConnectedSearchConcepts()); break; case NetworkType::kVPN: - registry()->AddSearchTags(GetVpnConnectedSearchConcepts()); + connected_vpn_guid_ = network->guid; + updater.AddSearchTags(GetVpnConnectedSearchConcepts()); break; default: diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.h index f467529b810..cbe35474631 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/internet_section.h @@ -5,8 +5,10 @@ #ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_SECTION_H_ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_INTERNET_SECTION_H_ +#include <string> #include <vector> +#include "base/optional.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" #include "chromeos/services/network_config/public/mojom/cros_network_config.mojom.h" #include "mojo/public/cpp/bindings/receiver.h" @@ -34,6 +36,15 @@ class InternetSection // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; + std::string ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const override; // network_config::mojom::CrosNetworkConfigObserver: void OnActiveNetworksChanged( @@ -51,10 +62,19 @@ class InternetSection void OnDeviceList( std::vector<network_config::mojom::DeviceStatePropertiesPtr> devices); - void FetchActiveNetworks(); - void OnActiveNetworks( + void FetchNetworkList(); + void OnNetworkList( std::vector<network_config::mojom::NetworkStatePropertiesPtr> networks); + // Null if no cellular network exists. + base::Optional<std::string> cellular_guid_; + + // Note: If not connected, the below fields are null. + base::Optional<std::string> connected_ethernet_guid_; + base::Optional<std::string> connected_wifi_guid_; + base::Optional<std::string> connected_tether_guid_; + base::Optional<std::string> connected_vpn_guid_; + mojo::Receiver<network_config::mojom::CrosNetworkConfigObserver> receiver_{ this}; mojo::Remote<network_config::mojom::CrosNetworkConfig> cros_network_config_; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.cc index 0007f1a6a8b..78321bd8c64 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.cc @@ -7,6 +7,7 @@ #include "base/feature_list.h" #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_features_util.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "chrome/browser/ui/webui/settings/languages_handler.h" @@ -14,6 +15,8 @@ #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" +#include "chromeos/constants/chromeos_pref_names.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" @@ -57,7 +60,7 @@ const std::vector<SearchConcept>& GetLanguagesSearchConcepts() { const std::vector<SearchConcept>& GetSmartInputsSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_LANGUAGES_SMART_INPUTS, - mojom::kSmartInputsSubagePath, + mojom::kSmartInputsSubpagePath, mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, @@ -69,7 +72,7 @@ const std::vector<SearchConcept>& GetSmartInputsSearchConcepts() { const std::vector<SearchConcept>& GetAssistivePersonalInfoSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_LANGUAGES_PERSONAL_INFORMATION_SUGGESTIONS, - mojom::kSmartInputsSubagePath, + mojom::kSmartInputsSubpagePath, mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, @@ -78,13 +81,26 @@ const std::vector<SearchConcept>& GetAssistivePersonalInfoSearchConcepts() { return *tags; } +const std::vector<SearchConcept>& GetEmojiSuggestionSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_LANGUAGES_EMOJI_SUGGESTIONS, + mojom::kSmartInputsSubpagePath, + mojom::SearchResultIcon::kGlobe, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kShowEmojiSuggestions}}, + }); + return *tags; +} + bool IsAssistivePersonalInfoAllowed() { return !features::IsGuestModeActive() && base::FeatureList::IsEnabled( ::chromeos::features::kAssistPersonalInfo); } -void AddSmartInputsStrings(content::WebUIDataSource* html_source) { +void AddSmartInputsStrings(content::WebUIDataSource* html_source, + bool is_emoji_suggestion_allowed) { static constexpr webui::LocalizedString kLocalizedStrings[] = { {"smartInputsTitle", IDS_SETTINGS_SMART_INPUTS_TITLE}, {"personalInfoSuggestionTitle", @@ -94,11 +110,71 @@ void AddSmartInputsStrings(content::WebUIDataSource* html_source) { {"showPersonalInfoSuggestion", IDS_SETTINGS_SMART_INPUTS_SHOW_PERSONAL_INFO}, {"managePersonalInfo", IDS_SETTINGS_SMART_INPUTS_MANAGE_PERSONAL_INFO}, + {"emojiSuggestionTitle", + IDS_SETTINGS_SMART_INPUTS_EMOJI_SUGGESTION_TITLE}, + {"emojiSuggestionDescription", + IDS_SETTINGS_SMART_INPUTS_EMOJI_SUGGESTION_DESCRIPTION}, + {"showEmojiSuggestion", IDS_SETTINGS_SMART_INPUTS_SHOW_EMOJI_SUGGESTION}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); html_source->AddBoolean("allowAssistivePersonalInfo", IsAssistivePersonalInfoAllowed()); + html_source->AddBoolean("allowEmojiSuggestion", is_emoji_suggestion_allowed); +} + +void AddInputMethodOptionsStrings(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"inputMethodOptionsBasicSectionTitle", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_BASIC}, + {"inputMethodOptionsAdvancedSectionTitle", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ADVANCED}, + {"inputMethodOptionsPhysicalKeyboardSectionTitle", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PHYSICAL_KEYBOARD}, + {"inputMethodOptionsVirtualKeyboardSectionTitle", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_VIRTUAL_KEYBOARD}, + {"inputMethodOptionsEnableDoubleSpacePeriod", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ENABLE_DOUBLE_SPACE_PERIOD}, + {"inputMethodOptionsEnableGestureTyping", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ENABLE_GESTURE_TYPING}, + {"inputMethodOptionsEnablePrediction", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ENABLE_PREDICTION}, + {"inputMethodOptionsEnableSoundOnKeypress", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ENABLE_SOUND_ON_KEYPRESS}, + {"inputMethodOptionsEnableCapitalization", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_ENABLE_CAPITALIZATION}, + {"inputMethodOptionsAutoCorrection", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_AUTO_CORRECTION}, + {"inputMethodOptionsXkbLayout", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_XKB_LAYOUT}, + {"inputMethodOptionsEditUserDict", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_EDIT_USER_DICT}, + {"inputMethodOptionsPinyinChinesePunctuation", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_CHINESE_PUNCTUATION}, + {"inputMethodOptionsPinyinDefaultChinese", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_DEFAULT_CHINESE}, + {"inputMethodOptionsPinyinEnableFuzzy", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_ENABLE_FUZZY}, + {"inputMethodOptionsPinyinEnableLowerPaging", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_ENABLE_LOWER_PAGING}, + {"inputMethodOptionsPinyinEnableUpperPaging", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_ENABLE_UPPER_PAGING}, + {"inputMethodOptionsPinyinFullWidthCharacter", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_PINYIN_FULL_WIDTH_CHARACTER}, + {"inputMethodOptionsAutoCorrectionOff", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_AUTO_CORRECTION_OFF}, + {"inputMethodOptionsAutoCorrectionModest", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_AUTO_CORRECTION_MODEST}, + {"inputMethodOptionsAutoCorrectionAggressive", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_AUTO_CORRECTION_AGGRESSIVE}, + {"inputMethodOptionsUsKeyboard", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_KEYBOARD_US}, + {"inputMethodOptionsDvorakKeyboard", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_KEYBOARD_DVORAK}, + {"inputMethodOptionsColemakKeyboard", + IDS_SETTINGS_INPUT_METHOD_OPTIONS_KEYBOARD_COLEMAK}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); } } // namespace @@ -106,11 +182,15 @@ void AddSmartInputsStrings(content::WebUIDataSource* html_source) { LanguagesSection::LanguagesSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetLanguagesSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetLanguagesSearchConcepts()); - if (IsAssistivePersonalInfoAllowed()) { - registry()->AddSearchTags(GetSmartInputsSearchConcepts()); - registry()->AddSearchTags(GetAssistivePersonalInfoSearchConcepts()); + if (IsAssistivePersonalInfoAllowed() || IsEmojiSuggestionAllowed()) { + updater.AddSearchTags(GetSmartInputsSearchConcepts()); + if (IsAssistivePersonalInfoAllowed()) + updater.AddSearchTags(GetAssistivePersonalInfoSearchConcepts()); + if (IsEmojiSuggestionAllowed()) + updater.AddSearchTags(GetEmojiSuggestionSearchConcepts()); } } @@ -149,11 +229,15 @@ void LanguagesSection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"moveUp", IDS_SETTINGS_LANGUAGES_LANGUAGES_LIST_MOVE_UP}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); - AddSmartInputsStrings(html_source); + AddSmartInputsStrings(html_source, IsEmojiSuggestionAllowed()); + AddInputMethodOptionsStrings(html_source); html_source->AddString( "languagesLearnMoreURL", base::ASCIIToUTF16(chrome::kLanguageSettingsLearnMoreUrl)); + html_source->AddBoolean("imeOptionsInSettings", + base::FeatureList::IsEnabled( + ::chromeos::features::kImeOptionsInSettings)); } void LanguagesSection::AddHandlers(content::WebUI* web_ui) { @@ -161,5 +245,71 @@ void LanguagesSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<::settings::LanguagesHandler>(profile())); } +int LanguagesSection::GetSectionNameMessageId() const { + return IDS_OS_SETTINGS_LANGUAGES_AND_INPUT_PAGE_TITLE; +} + +mojom::Section LanguagesSection::GetSection() const { + return mojom::Section::kLanguagesAndInput; +} + +mojom::SearchResultIcon LanguagesSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kGlobe; +} + +std::string LanguagesSection::GetSectionPath() const { + return mojom::kLanguagesAndInputSectionPath; +} + +bool LanguagesSection::IsEmojiSuggestionAllowed() const { + return base::FeatureList::IsEnabled( + ::chromeos::features::kEmojiSuggestAddition) && + profile()->GetPrefs()->GetBoolean( + ::chromeos::prefs::kEmojiSuggestionEnterpriseAllowed); +} + +void LanguagesSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // Languages and input details. + generator->RegisterTopLevelSubpage( + IDS_OS_SETTINGS_LANGUAGES_AND_INPUT_PAGE_TITLE, + mojom::Subpage::kLanguagesAndInputDetails, + mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, + mojom::kLanguagesAndInputDetailsSubpagePath); + static constexpr mojom::Setting kLanguagesAndInputDetailsSettings[] = { + mojom::Setting::kAddLanguage, + mojom::Setting::kShowInputOptionsInShelf, + }; + RegisterNestedSettingBulk(mojom::Subpage::kLanguagesAndInputDetails, + kLanguagesAndInputDetailsSettings, generator); + + // Manage input methods. + generator->RegisterNestedSubpage( + IDS_SETTINGS_LANGUAGES_MANAGE_INPUT_METHODS_TITLE, + mojom::Subpage::kManageInputMethods, + mojom::Subpage::kLanguagesAndInputDetails, + mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, + mojom::kManageInputMethodsSubpagePath); + + // Input method options. + generator->RegisterNestedSubpage( + IDS_SETTINGS_LANGUAGES_INPUT_METHOD_OPTIONS_TITLE, + mojom::Subpage::kInputMethodOptions, + mojom::Subpage::kLanguagesAndInputDetails, + mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, + mojom::kInputMethodOptionsSubpagePath); + + // Smart inputs. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_SMART_INPUTS_TITLE, mojom::Subpage::kSmartInputs, + mojom::SearchResultIcon::kGlobe, mojom::SearchResultDefaultRank::kMedium, + mojom::kSmartInputsSubpagePath); + static constexpr mojom::Setting kSmartInputsFeaturesSettings[] = { + mojom::Setting::kShowPersonalInformationSuggestions, + mojom::Setting::kShowEmojiSuggestions, + }; + RegisterNestedSettingBulk(mojom::Subpage::kSmartInputs, + kSmartInputsFeaturesSettings, generator); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.h index d8a742babc2..9236455754d 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/languages_section.h @@ -28,6 +28,13 @@ class LanguagesSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; + + bool IsEmojiSuggestionAllowed() const; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.cc index 9afd5882393..0868f9c866a 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.cc @@ -39,8 +39,6 @@ void AddSearchInSettingsStrings(content::WebUIDataSource* html_source) { {"searchNoResults", IDS_SEARCH_NO_RESULTS}, {"searchResults", IDS_SEARCH_RESULTS}, {"searchResultSelected", IDS_OS_SEARCH_RESULT_ROW_A11Y_RESULT_SELECTED}, - {"searchResultsOne", IDS_OS_SEARCH_BOX_A11Y_ONE_RESULT}, - {"searchResultsNumber", IDS_OS_SEARCH_BOX_A11Y_RESULT_COUNT}, // TODO(dpapad): IDS_DOWNLOAD_CLEAR_SEARCH and IDS_HISTORY_CLEAR_SEARCH // are identical, merge them to one and re-use here. {"clearSearch", IDS_DOWNLOAD_CLEAR_SEARCH}, @@ -98,6 +96,10 @@ void MainSection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"settings", IDS_SETTINGS_SETTINGS}, {"settingsAltPageTitle", IDS_SETTINGS_ALT_PAGE_TITLE}, {"subpageArrowRoleDescription", IDS_SETTINGS_SUBPAGE_BUTTON}, + {"subpageBackButtonAriaLabel", + IDS_SETTINGS_SUBPAGE_BACK_BUTTON_ARIA_LABEL}, + {"subpageBackButtonAriaRoleDescription", + IDS_SETTINGS_SUBPAGE_BACK_BUTTON_ARIA_ROLE_DESCRIPTION}, {"notValidWebAddress", IDS_SETTINGS_NOT_VALID_WEB_ADDRESS}, {"notValidWebAddressForContentType", IDS_SETTINGS_NOT_VALID_WEB_ADDRESS_FOR_CONTENT_TYPE}, @@ -143,11 +145,6 @@ void MainSection::AddLoadTimeData(content::WebUIDataSource* html_source) { html_source->AddResourcePath("search/search.mojom-lite.js", IDR_OS_SETTINGS_SEARCH_MOJOM_LITE_JS); - html_source->AddString("browserSettingsBannerText", - l10n_util::GetStringFUTF16( - IDS_SETTINGS_BROWSER_SETTINGS_BANNER, - base::ASCIIToUTF16(chrome::kChromeUISettingsURL))); - AddSearchInSettingsStrings(html_source); AddChromeOSUserStrings(html_source); @@ -162,6 +159,32 @@ void MainSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<::settings::BrowserLifetimeHandler>()); } +int MainSection::GetSectionNameMessageId() const { + NOTIMPLEMENTED(); + return 0; +} + +mojom::Section MainSection::GetSection() const { + NOTIMPLEMENTED(); + return mojom::Section::kMinValue; +} + +mojom::SearchResultIcon MainSection::GetSectionIcon() const { + NOTIMPLEMENTED(); + return mojom::SearchResultIcon::kMinValue; +} + +std::string MainSection::GetSectionPath() const { + NOTIMPLEMENTED(); + return std::string(); +} + +void MainSection::RegisterHierarchy(HierarchyGenerator* generator) const { + // MainSection is a container for common resources/functionality shared + // between sections and does not have its own subpages/settings. + NOTIMPLEMENTED(); +} + void MainSection::AddChromeOSUserStrings( content::WebUIDataSource* html_source) { const user_manager::User* user = diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.h index 7f8d9de7d5c..fab6b9bfdc3 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/main_section.h @@ -26,6 +26,11 @@ class MainSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; void AddChromeOSUserStrings(content::WebUIDataSource* html_source); }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc index 7ce85eaf234..da38e8b2896 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.cc @@ -4,19 +4,25 @@ #include "chrome/browser/ui/webui/settings/chromeos/multidevice_section.h" +#include "base/feature_list.h" #include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_features.h" #include "chrome/browser/chromeos/android_sms/android_sms_service.h" +#include "chrome/browser/nearby_sharing/nearby_sharing_prefs.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/settings/chromeos/multidevice_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" +#include "chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h" #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/url_constants.h" #include "chrome/common/webui_url_constants.h" #include "chrome/grit/generated_resources.h" +#include "chromeos/constants/chromeos_features.h" #include "chromeos/services/multidevice_setup/public/cpp/prefs.h" #include "chromeos/services/multidevice_setup/public/cpp/url_provider.h" #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" @@ -27,8 +33,22 @@ namespace chromeos { namespace settings { namespace { -const std::vector<SearchConcept>& GetMultiDeviceSearchConcepts() { +const std::vector<SearchConcept>& GetMultiDeviceOptedInSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK_OPTIONS, + mojom::kSmartLockSubpagePath, + mojom::SearchResultIcon::kLock, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSubpage, + {.subpage = mojom::Subpage::kSmartLock}}, + {IDS_OS_SETTINGS_TAG_MULTIDEVICE_FORGET, + mojom::kMultiDeviceFeaturesSubpagePath, + mojom::SearchResultIcon::kPhone, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kForgetPhone}, + {IDS_OS_SETTINGS_TAG_MULTIDEVICE_FORGET_ALT1, + SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_MULTIDEVICE_MESSAGES, mojom::kMultiDeviceFeaturesSubpagePath, mojom::SearchResultIcon::kMessages, @@ -40,54 +60,78 @@ const std::vector<SearchConcept>& GetMultiDeviceSearchConcepts() { {IDS_OS_SETTINGS_TAG_MULTIDEVICE, mojom::kMultiDeviceFeaturesSubpagePath, mojom::SearchResultIcon::kPhone, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kMultiDeviceFeatures}, - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_ALT1, - IDS_OS_SETTINGS_TAG_MULTIDEVICE_ALT2, - IDS_OS_SETTINGS_TAG_MULTIDEVICE_ALT3, SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_MULTIDEVICE_ALT1, SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK, mojom::kMultiDeviceFeaturesSubpagePath, mojom::SearchResultIcon::kLock, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, - {.subpage = mojom::Subpage::kSmartLock}, - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK_ALT1, - SearchConcept::kAltTagEnd}}, + {.subpage = mojom::Subpage::kSmartLock}}, }); return *tags; } -const std::vector<SearchConcept>& GetMultiDeviceOptedInSearchConcepts() { +const std::vector<SearchConcept>& GetMultiDeviceOptedOutSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags([] { + // Special-case: The "set up" search tag also includes the names of the + // multi-device features as a way to increase discoverability of these + // features. + SearchConcept set_up_concept{ + IDS_OS_SETTINGS_TAG_MULTIDEVICE_SET_UP, + mojom::kMultiDeviceSectionPath, + mojom::SearchResultIcon::kPhone, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kSetUpMultiDevice}, + {IDS_OS_SETTINGS_TAG_MULTIDEVICE, + IDS_OS_SETTINGS_TAG_MULTIDEVICE_MESSAGES, + IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK, SearchConcept::kAltTagEnd}, + }; + + // If Instant Tethering is available, also include that in the list. + if (base::FeatureList::IsEnabled(features::kInstantTethering)) { + set_up_concept.alt_tag_ids[3] = IDS_OS_SETTINGS_TAG_INSTANT_TETHERING; + set_up_concept.alt_tag_ids[4] = SearchConcept::kAltTagEnd; + } + + return std::vector<SearchConcept>{set_up_concept}; + }()); + return *tags; +} + +const std::vector<SearchConcept>& GetNearbyShareOnSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK_OPTIONS, - mojom::kSmartLockSubpagePath, - mojom::SearchResultIcon::kLock, + {IDS_OS_SETTINGS_TAG_MULTIDEVICE_NEARBY_SHARE, + mojom::kNearbyShareSubpagePath, + mojom::SearchResultIcon::kNearbyShare, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, - {.subpage = mojom::Subpage::kSmartLock}, - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SMART_LOCK_OPTIONS_ALT1, - SearchConcept::kAltTagEnd}}, - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_FORGET, - mojom::kMultiDeviceFeaturesSubpagePath, - mojom::SearchResultIcon::kPhone, + {.subpage = mojom::Subpage::kNearbyShare}}, + {IDS_OS_SETTINGS_TAG_NEARBY_SHARE_TURN_OFF, + mojom::kNearbyShareSubpagePath, + mojom::SearchResultIcon::kNearbyShare, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kForgetPhone}, - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_FORGET_ALT1, + {.setting = mojom::Setting::kNearbyShareOnOff}, + {IDS_OS_SETTINGS_TAG_NEARBY_SHARE_TURN_OFF_ALT1, SearchConcept::kAltTagEnd}}, }); return *tags; } -const std::vector<SearchConcept>& GetMultiDeviceOptedOutSearchConcepts() { +const std::vector<SearchConcept>& GetNearbyShareOffSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - {IDS_OS_SETTINGS_TAG_MULTIDEVICE_SET_UP, + {IDS_OS_SETTINGS_TAG_NEARBY_SHARE_TURN_ON, mojom::kMultiDeviceSectionPath, - mojom::SearchResultIcon::kPhone, + mojom::SearchResultIcon::kNearbyShare, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kSetUpMultiDevice}}, + {.setting = mojom::Setting::kNearbyShareOnOff}, + {IDS_OS_SETTINGS_TAG_NEARBY_SHARE_TURN_ON_ALT1, + SearchConcept::kAltTagEnd}}, }); return *tags; } @@ -121,13 +165,21 @@ MultiDeviceSection::MultiDeviceSection( multidevice_setup_client_(multidevice_setup_client), android_sms_service_(android_sms_service), pref_service_(pref_service) { + if (base::FeatureList::IsEnabled(::features::kNearbySharing)) { + pref_change_registrar_.Init(pref_service_); + pref_change_registrar_.Add( + ::prefs::kNearbySharingEnabledPrefName, + base::BindRepeating(&MultiDeviceSection::OnNearbySharingEnabledChanged, + base::Unretained(this))); + OnNearbySharingEnabledChanged(); + } + // Note: |multidevice_setup_client_| is null when multi-device features are // prohibited by policy. if (!multidevice_setup_client_) return; multidevice_setup_client_->AddObserver(this); - registry()->AddSearchTags(GetMultiDeviceSearchConcepts()); OnHostStatusChanged(multidevice_setup_client_->GetHostStatus()); } @@ -214,6 +266,7 @@ void MultiDeviceSection::AddLoadTimeData( GetHelpUrlWithBoard(chrome::kEasyUnlockLearnMoreUrl))); AddEasyUnlockStrings(html_source); + ::settings::AddNearbyShareData(html_source); } void MultiDeviceSection::AddHandlers(content::WebUI* web_ui) { @@ -231,15 +284,96 @@ void MultiDeviceSection::AddHandlers(content::WebUI* web_ui) { : nullptr)); } +int MultiDeviceSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_MULTIDEVICE; +} + +mojom::Section MultiDeviceSection::GetSection() const { + return mojom::Section::kMultiDevice; +} + +mojom::SearchResultIcon MultiDeviceSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kPhone; +} + +std::string MultiDeviceSection::GetSectionPath() const { + return mojom::kMultiDeviceSectionPath; +} + +void MultiDeviceSection::RegisterHierarchy( + HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kSetUpMultiDevice); + generator->RegisterTopLevelSetting(mojom::Setting::kVerifyMultiDeviceSetup); + + // MultiDevice features. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_MULTIDEVICE, mojom::Subpage::kMultiDeviceFeatures, + mojom::SearchResultIcon::kPhone, mojom::SearchResultDefaultRank::kMedium, + mojom::kMultiDeviceFeaturesSubpagePath); + static constexpr mojom::Setting kMultiDeviceFeaturesSettings[] = { + mojom::Setting::kMultiDeviceOnOff, + mojom::Setting::kMessagesSetUp, + mojom::Setting::kMessagesOnOff, + mojom::Setting::kForgetPhone, + }; + RegisterNestedSettingBulk(mojom::Subpage::kMultiDeviceFeatures, + kMultiDeviceFeaturesSettings, generator); + generator->RegisterTopLevelAltSetting(mojom::Setting::kMultiDeviceOnOff); + // Note: Instant Tethering is part of the Network section, but it has an + // alternate setting within the MultiDevice section. + generator->RegisterNestedAltSetting(mojom::Setting::kInstantTetheringOnOff, + mojom::Subpage::kMultiDeviceFeatures); + + // Smart Lock. + generator->RegisterNestedSubpage( + IDS_SETTINGS_EASY_UNLOCK_SECTION_TITLE, mojom::Subpage::kSmartLock, + mojom::Subpage::kMultiDeviceFeatures, mojom::SearchResultIcon::kLock, + mojom::SearchResultDefaultRank::kMedium, mojom::kSmartLockSubpagePath); + static constexpr mojom::Setting kSmartLockSettings[] = { + mojom::Setting::kSmartLockOnOff, + mojom::Setting::kSmartLockUnlockOrSignIn, + }; + RegisterNestedSettingBulk(mojom::Subpage::kSmartLock, kSmartLockSettings, + generator); + generator->RegisterNestedAltSetting(mojom::Setting::kSmartLockOnOff, + mojom::Subpage::kMultiDeviceFeatures); + + // Nearby Share, registered regardless of the flag. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_NEARBY_SHARE_TITLE, mojom::Subpage::kNearbyShare, + mojom::SearchResultIcon::kNearbyShare, + mojom::SearchResultDefaultRank::kMedium, mojom::kNearbyShareSubpagePath); + static constexpr mojom::Setting kNearbyShareSettings[] = { + mojom::Setting::kNearbyShareOnOff, + }; + RegisterNestedSettingBulk(mojom::Subpage::kNearbyShare, kNearbyShareSettings, + generator); + generator->RegisterTopLevelAltSetting(mojom::Setting::kNearbyShareOnOff); +} + void MultiDeviceSection::OnHostStatusChanged( const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice& host_status_with_device) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (IsOptedIn(host_status_with_device.first)) { - registry()->RemoveSearchTags(GetMultiDeviceOptedOutSearchConcepts()); - registry()->AddSearchTags(GetMultiDeviceOptedInSearchConcepts()); + updater.RemoveSearchTags(GetMultiDeviceOptedOutSearchConcepts()); + updater.AddSearchTags(GetMultiDeviceOptedInSearchConcepts()); + } else { + updater.RemoveSearchTags(GetMultiDeviceOptedInSearchConcepts()); + updater.AddSearchTags(GetMultiDeviceOptedOutSearchConcepts()); + } +} + +void MultiDeviceSection::OnNearbySharingEnabledChanged() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + + if (pref_service_->GetBoolean(::prefs::kNearbySharingEnabledPrefName)) { + updater.RemoveSearchTags(GetNearbyShareOffSearchConcepts()); + updater.AddSearchTags(GetNearbyShareOnSearchConcepts()); } else { - registry()->RemoveSearchTags(GetMultiDeviceOptedInSearchConcepts()); - registry()->AddSearchTags(GetMultiDeviceOptedOutSearchConcepts()); + updater.RemoveSearchTags(GetNearbyShareOnSearchConcepts()); + updater.AddSearchTags(GetNearbyShareOffSearchConcepts()); } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h index d35711a2c9d..2c608c9e03c 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/multidevice_section.h @@ -7,6 +7,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" #include "chromeos/services/multidevice_setup/public/cpp/multidevice_setup_client.h" +#include "components/prefs/pref_change_registrar.h" class PrefService; @@ -43,15 +44,24 @@ class MultiDeviceSection // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // multidevice_setup::MultiDeviceSetupClient::Observer: void OnHostStatusChanged( const multidevice_setup::MultiDeviceSetupClient::HostStatusWithDevice& host_status_with_device) override; + // Nearby Share enabled pref change observer. + void OnNearbySharingEnabledChanged(); + multidevice_setup::MultiDeviceSetupClient* multidevice_setup_client_; android_sms::AndroidSmsService* android_sms_service_; PrefService* pref_service_; + PrefChangeRegistrar pref_change_registrar_; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h new file mode 100644 index 00000000000..df287c3a928 --- /dev/null +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_IDENTIFIER_H_ +#define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_IDENTIFIER_H_ + +#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h" + +namespace chromeos { +namespace settings { + +// Uniquely identifies a settings item (section, subpage, or setting). +union OsSettingsIdentifier { + mojom::Section section; + mojom::Subpage subpage; + mojom::Setting setting; +}; + +} // namespace settings +} // namespace chromeos + +#endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_OS_SETTINGS_IDENTIFIER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc index ce568062640..95cd5692e5c 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.cc @@ -7,6 +7,7 @@ #include "base/bind.h" #include "base/feature_list.h" #include "chrome/browser/chromeos/local_search_service/local_search_service.h" +#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" @@ -40,10 +41,12 @@ OsSettingsManager::OsSettingsManager( arc_app_list_prefs, identity_manager, android_sms_service, - printers_manager)) { + printers_manager)), + hierarchy_(std::make_unique<Hierarchy>(sections_.get())) { if (base::FeatureList::IsEnabled(features::kNewOsSettingsSearch)) { search_handler_ = std::make_unique<SearchHandler>( - search_tag_registry_.get(), local_search_service); + search_tag_registry_.get(), sections_.get(), hierarchy_.get(), + local_search_service); } } @@ -64,6 +67,7 @@ void OsSettingsManager::Shutdown() { // Note: These must be deleted in the opposite order of their creation to // prevent against UAF violations. search_handler_.reset(); + hierarchy_.reset(); sections_.reset(); search_tag_registry_.reset(); } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h index b60dbcbd181..78a9008a482 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h @@ -46,6 +46,7 @@ class MultiDeviceSetupClient; namespace settings { +class Hierarchy; class OsSettingsSections; class SearchHandler; class SearchTagRegistry; @@ -104,14 +105,17 @@ class OsSettingsManager : public KeyedService { // Note: Returns null when the kNewOsSettingsSearch flag is disabled. SearchHandler* search_handler() { return search_handler_.get(); } + const Hierarchy* hierarchy() const { return hierarchy_.get(); } + private: - FRIEND_TEST_ALL_PREFIXES(OsSettingsManagerTest, Sections); + FRIEND_TEST_ALL_PREFIXES(OsSettingsManagerTest, Initialization); // KeyedService: void Shutdown() override; std::unique_ptr<SearchTagRegistry> search_tag_registry_; std::unique_ptr<OsSettingsSections> sections_; + std::unique_ptr<Hierarchy> hierarchy_; std::unique_ptr<SearchHandler> search_handler_; }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc index a238deaf5fb..71e5abc2b2f 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.cc @@ -9,6 +9,7 @@ #include "chrome/browser/chromeos/local_search_service/local_search_service_factory.h" #include "chrome/browser/chromeos/multidevice_setup/multidevice_setup_client_factory.h" #include "chrome/browser/chromeos/printing/cups_printers_manager_factory.h" +#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/signin/identity_manager_factory.h" @@ -53,13 +54,23 @@ OsSettingsManagerFactory::~OsSettingsManagerFactory() = default; KeyedService* OsSettingsManagerFactory::BuildServiceInstanceFor( content::BrowserContext* context) const { Profile* profile = Profile::FromBrowserContext(context); + + // Edge case: KerberosCredentialsManagerFactory::Get(profile) returns the + // instance associated with the primary profile, even if |profile| is not the + // primary profile, which can cause issues in multi-profile environments. + // Only call Get() if the profile is primary; see https://crbug.com/1103140. + KerberosCredentialsManager* kerberos_credentials_manager = + ProfileHelper::IsPrimaryProfile(profile) + ? KerberosCredentialsManagerFactory::Get(profile) + : nullptr; + return new OsSettingsManager( profile, local_search_service::LocalSearchServiceFactory::GetForProfile(profile), multidevice_setup::MultiDeviceSetupClientFactory::GetForProfile(profile), ProfileSyncServiceFactory::GetForProfile(profile), SupervisedUserServiceFactory::GetForProfile(profile), - KerberosCredentialsManagerFactory::Get(profile), + kerberos_credentials_manager, ArcAppListPrefsFactory::GetForBrowserContext(profile), IdentityManagerFactory::GetForProfile(profile), android_sms::AndroidSmsServiceFactory::GetForBrowserContext(profile), diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_unittest.cc index ec50ed54481..dd20db6da77 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_manager_unittest.cc @@ -4,9 +4,12 @@ #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager.h" +#include "base/metrics/histogram_base.h" #include "base/no_destructor.h" +#include "base/test/metrics/histogram_enum_reader.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/constants_util.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_manager_factory.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" #include "chrome/test/base/testing_browser_process.h" @@ -40,12 +43,55 @@ class OsSettingsManagerTest : public testing::Test { OsSettingsManager* manager_; }; -TEST_F(OsSettingsManagerTest, Sections) { - // For each mojom::Section value, there should be an associated - // OsSettingsSection class registered. +TEST_F(OsSettingsManagerTest, Initialization) { + base::Optional<base::HistogramEnumEntryMap> sections_enum_entry_map = + base::ReadEnumFromEnumsXml("OsSettingsSection"); + ASSERT_TRUE(sections_enum_entry_map); for (const auto& section : constants::AllSections()) { + // For each mojom::Section value, there should be an associated + // OsSettingsSection class registered. EXPECT_TRUE(manager_->sections_->GetSection(section)) << "No OsSettingsSection instance created for " << section << "."; + + // Each mojom::Section should be registered in the hierarchy. + const Hierarchy::SectionMetadata& metadata = + manager_->hierarchy_->GetSectionMetadata(section); + + // Only the "About Chrome OS" section contains only a link to a subpage. + EXPECT_EQ(metadata.only_contains_link_to_subpage, + section == mojom::Section::kAboutChromeOs); + + EXPECT_TRUE( + base::Contains(*sections_enum_entry_map, static_cast<int32_t>(section))) + << "Missing OsSettingsSection enums.xml entry for " << section; + } + + base::Optional<base::HistogramEnumEntryMap> subpages_enum_entry_map = + base::ReadEnumFromEnumsXml("OsSettingsSubpage"); + ASSERT_TRUE(subpages_enum_entry_map); + for (const auto& subpage : constants::AllSubpages()) { + // Each mojom::Subpage should be registered in the hierarchy. Note that + // GetSubpageMetadata() internally CHECK()s that the metadata exists before + // returning it. + manager_->hierarchy_->GetSubpageMetadata(subpage); + + EXPECT_TRUE( + base::Contains(*subpages_enum_entry_map, static_cast<int32_t>(subpage))) + << "Missing OsSettingsSubpage enums.xml entry for " << subpage; + } + + base::Optional<base::HistogramEnumEntryMap> settings_enum_entry_map = + base::ReadEnumFromEnumsXml("OsSetting"); + ASSERT_TRUE(settings_enum_entry_map); + for (const auto& setting : constants::AllSettings()) { + // Each mojom::Setting should be registered in the hierarchy. Note that + // GetSettingMetadata() internally CHECK()s that the metadata exists before + // returning it. + manager_->hierarchy_->GetSettingMetadata(setting); + + EXPECT_TRUE( + base::Contains(*settings_enum_entry_map, static_cast<int32_t>(setting))) + << "Missing OsSetting enums.xml entry for " << setting; } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc index af60b362b5f..02bdc340841 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.cc @@ -7,6 +7,8 @@ #include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "base/system/sys_info.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util.h" namespace chromeos { namespace settings { @@ -18,6 +20,15 @@ base::string16 OsSettingsSection::GetHelpUrlWithBoard( "&b=" + base::SysInfo::GetLsbReleaseBoard()); } +// static +void OsSettingsSection::RegisterNestedSettingBulk( + mojom::Subpage subpage, + const base::span<const mojom::Setting>& settings, + HierarchyGenerator* generator) { + for (const auto& setting : settings) + generator->RegisterNestedSetting(setting, subpage); +} + OsSettingsSection::~OsSettingsSection() = default; OsSettingsSection::OsSettingsSection(Profile* profile, @@ -27,5 +38,33 @@ OsSettingsSection::OsSettingsSection(Profile* profile, DCHECK(search_tag_registry); } +OsSettingsSection::OsSettingsSection() = default; + +std::string OsSettingsSection::ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const { + // Default case for static URLs which do not need to be modified. + return url_to_modify; +} + +mojom::SearchResultPtr OsSettingsSection::GenerateSectionSearchResult( + double relevance_score) const { + return mojom::SearchResult::New( + /*result_text=*/l10n_util::GetStringUTF16(GetSectionNameMessageId()), + /*canonical_result_text=*/ + l10n_util::GetStringUTF16(GetSectionNameMessageId()), + ModifySearchResultUrl(mojom::SearchResultType::kSection, + {.section = GetSection()}, GetSectionPath()), + GetSectionIcon(), relevance_score, + std::vector<base::string16>{ + l10n_util::GetStringUTF16(IDS_INTERNAL_APP_SETTINGS), + l10n_util::GetStringUTF16(GetSectionNameMessageId())}, + mojom::SearchResultDefaultRank::kMedium, + /*was_generated_from_text_match=*/false, + mojom::SearchResultType::kSection, + mojom::SearchResultIdentifier::NewSection(GetSection())); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h index 3e8b5b23bcd..b4406ff4c3e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_section.h @@ -8,7 +8,11 @@ #include <string> #include <vector> +#include "base/containers/span.h" #include "base/strings/string16.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" class Profile; @@ -38,6 +42,57 @@ class SearchTagRegistry; // (i.e., browser to JS IPC mechanisms) to the page. class OsSettingsSection { public: + // Used to construct a hierarchy of this section (i.e., which settings and + // subpages are part of this section). See RegisterHierarchy() below. + // + // Note on primary vs. alternate setting locations: Some settings are located + // in multiple spots of settings. For example, the Wi-Fi on/off toggle appears + // in both the top-level Network section as well as the Wi-Fi subpage. In + // cases like this, we consider the "primary" location as the more-targeted + // one - in this example, the Wi-Fi subpage is the primary location of the + // toggle since it is more specific to Wi-Fi, and the alternate location is + // the one embedded in the Network section. + class HierarchyGenerator { + public: + virtual ~HierarchyGenerator() = default; + + // Registers a subpage whose parent is this section. + virtual void RegisterTopLevelSubpage( + int name_message_id, + mojom::Subpage subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters) = 0; + + // Registers a subpage whose paernt is another subpage in this section. + virtual void RegisterNestedSubpage( + int name_message_id, + mojom::Subpage subpage, + mojom::Subpage parent_subpage, + mojom::SearchResultIcon icon, + mojom::SearchResultDefaultRank default_rank, + const std::string& url_path_with_parameters) = 0; + + // Registers a setting embedded directly in the section (i.e., not within a + // subpage). This functions is for primary locations (see above). + virtual void RegisterTopLevelSetting(mojom::Setting setting) = 0; + + // Registers a setting embedded within a subpage in this section. This + // function is for primary locations (see above). + virtual void RegisterNestedSetting(mojom::Setting setting, + mojom::Subpage subpage) = 0; + + // Register an alternate location for a setting embedded directly in the + // section (i.e., not within a subpage). This function is for alternate + // locations (see above). + virtual void RegisterTopLevelAltSetting(mojom::Setting setting) = 0; + + // Registers a setting embedded within a subpage in this section. This + // function is for alternate locations (see above). + virtual void RegisterNestedAltSetting(mojom::Setting setting, + mojom::Subpage subpage) = 0; + }; + virtual ~OsSettingsSection(); OsSettingsSection(const OsSettingsSection& other) = delete; @@ -51,12 +106,51 @@ class OsSettingsSection { // derived type requires one or more handlers for this section. virtual void AddHandlers(content::WebUI* web_ui) {} + // Provides the message ID for the name of this section. + virtual int GetSectionNameMessageId() const = 0; + + // Provides the Section enum for this section. + virtual mojom::Section GetSection() const = 0; + + // Provides the icon for this section. + virtual mojom::SearchResultIcon GetSectionIcon() const = 0; + + // Provides the path for this section. + virtual std::string GetSectionPath() const = 0; + + // Registers the subpages and/or settings which reside in this section. + virtual void RegisterHierarchy(HierarchyGenerator* generator) const = 0; + + // Modifies a URL to be used by settings search. Some URLs require dynamic + // content (e.g., network detail settings use the GUID of the network as a URL + // parameter to route to details for a specific network). By default, this + // function simply returns |url_to_modify|, which provides functionality for + // static URLs. + virtual std::string ModifySearchResultUrl( + mojom::SearchResultType type, + OsSettingsIdentifier id, + const std::string& url_to_modify) const; + + // Generates a search result corresponding to this section. |relevance_score| + // must be passed by the client, since this result is being created manually + // instead of via query matching. + mojom::SearchResultPtr GenerateSectionSearchResult( + double relevance_score) const; + protected: static base::string16 GetHelpUrlWithBoard(const std::string& original_url); + static void RegisterNestedSettingBulk( + mojom::Subpage, + const base::span<const mojom::Setting>& settings, + HierarchyGenerator* generator); OsSettingsSection(Profile* profile, SearchTagRegistry* search_tag_registry); + // Used by tests. + OsSettingsSection(); + Profile* profile() { return profile_; } + const Profile* profile() const { return profile_; } SearchTagRegistry* registry() { return search_tag_registry_; } private: diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc index 6239b2466c5..334128e8509 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.cc @@ -4,6 +4,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" +#include "build/branding_buildflags.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/webui/settings/chromeos/about_section.h" #include "chrome/browser/ui/webui/settings/chromeos/accessibility_section.h" @@ -125,16 +126,26 @@ OsSettingsSections::OsSettingsSections( sections_map_[mojom::Section::kReset] = reset_section.get(); sections_.push_back(std::move(reset_section)); +#if BUILDFLAG(GOOGLE_CHROME_BRANDING) + auto about_section = std::make_unique<AboutSection>( + profile, search_tag_registry, profile->GetPrefs()); +#else auto about_section = std::make_unique<AboutSection>(profile, search_tag_registry); +#endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) sections_map_[mojom::Section::kAboutChromeOs] = about_section.get(); sections_.push_back(std::move(about_section)); } +OsSettingsSections::OsSettingsSections() = default; + OsSettingsSections::~OsSettingsSections() = default; -OsSettingsSection* OsSettingsSections::GetSection(mojom::Section section) { - return sections_map_[section]; +const OsSettingsSection* OsSettingsSections::GetSection( + mojom::Section section) const { + const auto it = sections_map_.find(section); + CHECK(it != sections_map_.end()); + return it->second; } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h index 44ddc68a34d..d1526481464 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h @@ -54,15 +54,18 @@ class OsSettingsSections { CupsPrintersManager* printers_manager); OsSettingsSections(const OsSettingsSections& other) = delete; OsSettingsSections& operator=(const OsSettingsSections& other) = delete; - ~OsSettingsSections(); + virtual ~OsSettingsSections(); - OsSettingsSection* GetSection(mojom::Section section); + const OsSettingsSection* GetSection(mojom::Section section) const; std::vector<std::unique_ptr<OsSettingsSection>>& sections() { return sections_; } - private: + protected: + // Used by tests. + OsSettingsSections(); + std::unordered_map<mojom::Section, OsSettingsSection*> sections_map_; std::vector<std::unique_ptr<OsSettingsSection>> sections_; }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc index 97f2eeab8a9..8bddd59808d 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/os_settings_ui.cc @@ -68,6 +68,10 @@ OSSettingsUI::OSSettingsUI(content::WebUI* web_ui) html_source->AddResourcePath("chromeos/lazy_load.html", IDR_OS_SETTINGS_LAZY_LOAD_VULCANIZED_HTML); html_source->SetDefaultResource(IDR_OS_SETTINGS_VULCANIZED_HTML); + html_source->AddResourcePath("chromeos/os_settings_v3.html", + IDR_OS_SETTINGS_OS_SETTINGS_V3_HTML); + html_source->AddResourcePath("chromeos/os_settings.js", + IDR_OS_SETTINGS_SETTINGS_ROLLUP_JS); #else webui::SetupWebUIDataSource( html_source, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc index 273e9456b69..fb9df81bbf4 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/parental_controls_handler.cc @@ -14,10 +14,10 @@ #include "chrome/browser/ui/browser_navigator.h" #include "chrome/browser/ui/browser_navigator_params.h" #include "chrome/browser/ui/webui/chromeos/add_supervision/add_supervision_ui.h" -#include "chrome/services/app_service/public/cpp/app_registry_cache.h" -#include "chrome/services/app_service/public/cpp/app_update.h" -#include "chrome/services/app_service/public/mojom/types.mojom.h" #include "chromeos/constants/chromeos_features.h" +#include "components/services/app_service/public/cpp/app_registry_cache.h" +#include "components/services/app_service/public/cpp/app_update.h" +#include "components/services/app_service/public/mojom/types.mojom.h" #include "ui/base/page_transition_types.h" #include "ui/base/window_open_disposition.h" #include "ui/display/types/display_constants.h" diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.cc index 45486f9eed4..e5bb33b3244 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.cc @@ -47,6 +47,7 @@ #include "components/user_manager/user_manager.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_data_source.h" +#include "services/network/public/mojom/content_security_policy.mojom.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" #include "ui/chromeos/resources/grit/ui_chromeos_resources.h" @@ -68,7 +69,9 @@ const std::vector<SearchConcept>& GetPeopleSearchConcepts() { mojom::SearchResultIcon::kLock, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kChangeAuthPin}}, + {.setting = mojom::Setting::kChangeAuthPin}, + {IDS_OS_SETTINGS_TAG_LOCK_SCREEN_PIN_OR_PASSWORD_ALT1, + SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_USERNAMES_AND_PHOTOS, mojom::kManageOtherPeopleSubpagePath, mojom::SearchResultIcon::kAvatar, @@ -110,12 +113,6 @@ const std::vector<SearchConcept>& GetPeopleSearchConcepts() { {.setting = mojom::Setting::kLockScreen}, {IDS_OS_SETTINGS_TAG_LOCK_SCREEN_WHEN_WAKING_ALT1, SearchConcept::kAltTagEnd}}, - {IDS_OS_SETTINGS_TAG_SYNC, - mojom::kSyncSubpagePath, - mojom::SearchResultIcon::kSync, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSubpage, - {.subpage = mojom::Subpage::kSync}}, {IDS_OS_SETTINGS_TAG_PEOPLE_ACCOUNTS_REMOVE, mojom::kMyAccountsSubpagePath, mojom::SearchResultIcon::kAvatar, @@ -145,27 +142,83 @@ const std::vector<SearchConcept>& GetPeopleSearchConcepts() { return *tags; } -const std::vector<SearchConcept>& GetSyncOnSearchConcepts() { +const std::vector<SearchConcept>& GetNonSplitSyncSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_SYNC_AND_GOOGLE_SERVICES, + mojom::kSyncDeprecatedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSubpage, + {.subpage = mojom::Subpage::kSyncDeprecated}}, + {IDS_OS_SETTINGS_TAG_SYNC_MANAGEMENT, + mojom::kSyncDeprecatedAdvancedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSubpage, + {.subpage = mojom::Subpage::kSyncDeprecatedAdvanced}}, + {IDS_OS_SETTINGS_TAG_SYNC_ENCRYPTION_OPTIONS, + mojom::kSyncDeprecatedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kNonSplitSyncEncryptionOptions}, + {IDS_OS_SETTINGS_TAG_SYNC_ENCRYPTION_OPTIONS_ALT1, + SearchConcept::kAltTagEnd}}, + {IDS_OS_SETTINGS_TAG_AUTOCOMPLETE_SEARCHES_AND_URLS, + mojom::kSyncDeprecatedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kAutocompleteSearchesAndUrls}}, + {IDS_OS_SETTINGS_TAG_MAKE_SEARCHES_AND_BROWSER_BETTER, + mojom::kSyncDeprecatedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kMakeSearchesAndBrowsingBetter}}, + {IDS_OS_SETTINGS_TAG_GOOGLE_DRIVE_SEARCH_SUGGESTIONS, + mojom::kSyncDeprecatedSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kGoogleDriveSearchSuggestions}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetSplitSyncSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_SYNC, + mojom::kSyncSubpagePath, + mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultType::kSubpage, + {.subpage = mojom::Subpage::kSync}}, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetSplitSyncOnSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_SYNC_TURN_OFF, mojom::kSyncSubpagePath, mojom::SearchResultIcon::kSync, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kSyncOnOff}, + {.setting = mojom::Setting::kSplitSyncOnOff}, {IDS_OS_SETTINGS_TAG_SYNC_TURN_OFF_ALT1, SearchConcept::kAltTagEnd}}, }); return *tags; } -const std::vector<SearchConcept>& GetSyncOffSearchConcepts() { +const std::vector<SearchConcept>& GetSplitSyncOffSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_SYNC_TURN_ON, mojom::kSyncSubpagePath, mojom::SearchResultIcon::kSync, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kSyncOnOff}, + {.setting = mojom::Setting::kSplitSyncOnOff}, {IDS_OS_SETTINGS_TAG_SYNC_TURN_ON_ALT1, SearchConcept::kAltTagEnd}}, }); return *tags; @@ -176,25 +229,25 @@ const std::vector<SearchConcept>& GetKerberosSearchConcepts() { {IDS_OS_SETTINGS_TAG_KERBEROS_ADD, mojom::kKerberosSubpagePath, mojom::SearchResultIcon::kAvatar, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kAddKerberosTicket}}, {IDS_OS_SETTINGS_TAG_KERBEROS_REMOVE, mojom::kKerberosSubpagePath, mojom::SearchResultIcon::kAvatar, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kRemoveKerberosTicket}}, {IDS_OS_SETTINGS_TAG_KERBEROS, mojom::kKerberosSubpagePath, mojom::SearchResultIcon::kAvatar, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kKerberos}}, {IDS_OS_SETTINGS_TAG_KERBEROS_ACTIVE, mojom::kKerberosSubpagePath, mojom::SearchResultIcon::kAvatar, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kSetActiveKerberosTicket}}, }); @@ -204,19 +257,19 @@ const std::vector<SearchConcept>& GetKerberosSearchConcepts() { const std::vector<SearchConcept>& GetFingerprintSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_FINGERPRINT_REMOVE, - mojom::kFingerprintSubpathPath, + mojom::kFingerprintSubpagePath, mojom::SearchResultIcon::kFingerprint, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kRemoveFingerprint}}, {IDS_OS_SETTINGS_TAG_FINGERPRINT_ADD, - mojom::kFingerprintSubpathPath, + mojom::kFingerprintSubpagePath, mojom::SearchResultIcon::kFingerprint, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kAddFingerprint}}, {IDS_OS_SETTINGS_TAG_FINGERPRINT, - mojom::kFingerprintSubpathPath, + mojom::kFingerprintSubpagePath, mojom::SearchResultIcon::kFingerprint, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, @@ -243,7 +296,11 @@ void AddAccountManagerPageStrings(content::WebUIDataSource* html_source) { static constexpr webui::LocalizedString kLocalizedStrings[] = { {"accountManagerDescription", IDS_SETTINGS_ACCOUNT_MANAGER_DESCRIPTION}, {"accountManagerChildDescription", - IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION_2}, + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_DESCRIPTION}, + {"accountManagerChildFirstMessage", + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_FIRST_MESSAGE}, + {"accountManagerChildSecondMessage", + IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_SECOND_MESSAGE}, {"accountListHeader", IDS_SETTINGS_ACCOUNT_MANAGER_LIST_HEADER}, {"accountManagerPrimaryAccountTooltip", IDS_SETTINGS_ACCOUNT_MANAGER_PRIMARY_ACCOUNT_TOOLTIP}, @@ -431,7 +488,8 @@ void AddFingerprintStrings(content::WebUIDataSource* html_source, // lottie animations, this update has to be performed manually. As the usage // increases, set this as the default so manual override is no longer // required. - html_source->OverrideContentSecurityPolicyWorkerSrc( + html_source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::WorkerSrc, "worker-src blob: 'self';"); html_source->AddResourcePath("finger_print.json", IDR_LOGIN_FINGER_PRINT_TABLET_ANIMATION); @@ -531,8 +589,8 @@ void AddSyncControlsStrings(content::WebUIDataSource* html_source) { html_source->AddBoolean("splitSettingsSyncEnabled", chromeos::features::IsSplitSettingsSyncEnabled()); - html_source->AddBoolean("splitSyncConsent", - chromeos::features::IsSplitSyncConsentEnabled()); + html_source->AddBoolean("useBrowserSyncConsent", + chromeos::features::ShouldUseBrowserSyncConsent()); html_source->AddBoolean( "syncSetupFriendlySettings", base::FeatureList::IsEnabled(::features::kSyncSetupFriendlySettings)); @@ -581,6 +639,7 @@ void AddParentalControlStrings(content::WebUIDataSource* html_source, bool is_child = user_manager::UserManager::Get()->IsLoggedInAsChildUser(); html_source->AddBoolean("isChild", is_child); + base::string16 tooltip; if (is_child) { std::string custodian = supervised_user_service->GetCustodianName(); std::string second_custodian = @@ -596,9 +655,10 @@ void AddParentalControlStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_ACCOUNT_MANAGER_CHILD_MANAGED_BY_TWO_PARENTS_TOOLTIP, base::UTF8ToUTF16(custodian), base::UTF8ToUTF16(second_custodian)); } - html_source->AddString("accountManagerPrimaryAccountChildManagedTooltip", - child_managed_tooltip); + tooltip = child_managed_tooltip; } + html_source->AddString("accountManagerPrimaryAccountChildManagedTooltip", + tooltip); } } // namespace @@ -621,7 +681,8 @@ PeopleSection::PeopleSection( if (features::IsGuestModeActive()) return; - registry()->AddSearchTags(GetPeopleSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetPeopleSearchConcepts()); if (kerberos_credentials_manager_) { // Kerberos search tags are added/removed dynamically. @@ -629,28 +690,34 @@ PeopleSection::PeopleSection( OnKerberosEnabledStateChanged(); } - if (sync_service_ && chromeos::features::IsSplitSettingsSyncEnabled()) { - // Sync search tags are added/removed dynamically. - sync_service_->AddObserver(this); - OnStateChanged(sync_service_); + if (chromeos::features::IsSplitSettingsSyncEnabled()) { + if (sync_service_) { + updater.AddSearchTags(GetSplitSyncSearchConcepts()); + + // Sync search tags are added/removed dynamically. + sync_service_->AddObserver(this); + OnStateChanged(sync_service_); + } + } else { + updater.AddSearchTags(GetNonSplitSyncSearchConcepts()); } // Parental control search tags are added if necessary and do not update // dynamically during a user session. if (features::ShouldShowParentalControlSettings(profile)) - registry()->AddSearchTags(GetParentalSearchConcepts()); + updater.AddSearchTags(GetParentalSearchConcepts()); // Fingerprint search tags are added if necessary and do not update // dynamically during a user session. if (AreFingerprintSettingsAllowed()) - registry()->AddSearchTags(GetFingerprintSearchConcepts()); + updater.AddSearchTags(GetFingerprintSearchConcepts()); } PeopleSection::~PeopleSection() { if (kerberos_credentials_manager_) kerberos_credentials_manager_->RemoveObserver(this); - if (sync_service_ && chromeos::features::IsSplitSettingsSyncEnabled()) + if (chromeos::features::IsSplitSettingsSyncEnabled() && sync_service_) sync_service_->RemoveObserver(this); } @@ -678,7 +745,7 @@ void PeopleSection::AddLoadTimeData(content::WebUIDataSource* html_source) { html_source->AddBoolean("isAccountManagerEnabled", chromeos::IsAccountManagerAvailable(profile())); - if (chromeos::features::IsSplitSyncConsentEnabled()) { + if (chromeos::features::ShouldUseBrowserSyncConsent()) { static constexpr webui::LocalizedString kTurnOffStrings[] = { {"syncDisconnect", IDS_SETTINGS_PEOPLE_SYNC_TURN_OFF}, {"syncDisconnectTitle", @@ -785,24 +852,144 @@ void PeopleSection::AddHandlers(content::WebUI* web_ui) { } } +int PeopleSection::GetSectionNameMessageId() const { + return IDS_OS_SETTINGS_PEOPLE; +} + +mojom::Section PeopleSection::GetSection() const { + return mojom::Section::kPeople; +} + +mojom::SearchResultIcon PeopleSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kAvatar; +} + +std::string PeopleSection::GetSectionPath() const { + return mojom::kPeopleSectionPath; +} + +void PeopleSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kSetUpParentalControls); + + // My accounts. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_ACCOUNT_MANAGER_PAGE_TITLE, mojom::Subpage::kMyAccounts, + mojom::SearchResultIcon::kAvatar, mojom::SearchResultDefaultRank::kMedium, + mojom::kMyAccountsSubpagePath); + static constexpr mojom::Setting kMyAccountsSettings[] = { + mojom::Setting::kAddAccount, + mojom::Setting::kRemoveAccount, + }; + RegisterNestedSettingBulk(mojom::Subpage::kMyAccounts, kMyAccountsSettings, + generator); + + // Combined browser/OS sync. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_SYNC_SYNC_AND_NON_PERSONALIZED_SERVICES, + mojom::Subpage::kSyncDeprecated, mojom::SearchResultIcon::kSync, + mojom::SearchResultDefaultRank::kMedium, + mojom::kSyncDeprecatedSubpagePath); + static constexpr mojom::Setting kSyncDeprecatedSettings[] = { + mojom::Setting::kNonSplitSyncEncryptionOptions, + mojom::Setting::kAutocompleteSearchesAndUrls, + mojom::Setting::kMakeSearchesAndBrowsingBetter, + mojom::Setting::kGoogleDriveSearchSuggestions, + }; + RegisterNestedSettingBulk(mojom::Subpage::kSyncDeprecated, + kSyncDeprecatedSettings, generator); + generator->RegisterNestedSubpage( + IDS_SETTINGS_SYNC_ADVANCED_PAGE_TITLE, + mojom::Subpage::kSyncDeprecatedAdvanced, mojom::Subpage::kSyncDeprecated, + mojom::SearchResultIcon::kSync, mojom::SearchResultDefaultRank::kMedium, + mojom::kSyncDeprecatedAdvancedSubpagePath); + + // OS sync. + generator->RegisterTopLevelSubpage( + IDS_OS_SETTINGS_SYNC_PAGE_TITLE, mojom::Subpage::kSync, + mojom::SearchResultIcon::kSync, mojom::SearchResultDefaultRank::kMedium, + mojom::kSyncSubpagePath); + generator->RegisterNestedSetting(mojom::Setting::kSplitSyncOnOff, + mojom::Subpage::kSync); + + // Security and sign-in. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_TITLE_LOGIN_LOCK, + mojom::Subpage::kSecurityAndSignIn, mojom::SearchResultIcon::kLock, + mojom::SearchResultDefaultRank::kMedium, + mojom::kSecurityAndSignInSubpagePath); + static constexpr mojom::Setting kSecurityAndSignInSettings[] = { + mojom::Setting::kLockScreen, + mojom::Setting::kChangeAuthPin, + }; + RegisterNestedSettingBulk(mojom::Subpage::kSecurityAndSignIn, + kSecurityAndSignInSettings, generator); + + // Fingerprint. + generator->RegisterNestedSubpage( + IDS_SETTINGS_PEOPLE_LOCK_SCREEN_FINGERPRINT_SUBPAGE_TITLE, + mojom::Subpage::kFingerprint, mojom::Subpage::kSecurityAndSignIn, + mojom::SearchResultIcon::kFingerprint, + mojom::SearchResultDefaultRank::kMedium, mojom::kFingerprintSubpagePath); + static constexpr mojom::Setting kFingerprintSettings[] = { + mojom::Setting::kAddFingerprint, + mojom::Setting::kRemoveFingerprint, + }; + RegisterNestedSettingBulk(mojom::Subpage::kFingerprint, kFingerprintSettings, + generator); + + // Manage other people. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_PEOPLE_MANAGE_OTHER_PEOPLE, + mojom::Subpage::kManageOtherPeople, + mojom::SearchResultIcon::kAvatar, + mojom::SearchResultDefaultRank::kMedium, + mojom::kManageOtherPeopleSubpagePath); + static constexpr mojom::Setting kManageOtherPeopleSettings[] = { + mojom::Setting::kGuestBrowsing, + mojom::Setting::kShowUsernamesAndPhotosAtSignIn, + mojom::Setting::kRestrictSignIn, + mojom::Setting::kAddToUserWhitelist, + mojom::Setting::kRemoveFromUserWhitelist, + }; + RegisterNestedSettingBulk(mojom::Subpage::kManageOtherPeople, + kManageOtherPeopleSettings, generator); + + // Kerberos. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_KERBEROS_ACCOUNTS_PAGE_TITLE, mojom::Subpage::kKerberos, + mojom::SearchResultIcon::kAvatar, mojom::SearchResultDefaultRank::kMedium, + mojom::kKerberosSubpagePath); + static constexpr mojom::Setting kKerberosSettings[] = { + mojom::Setting::kAddKerberosTicket, + mojom::Setting::kRemoveKerberosTicket, + mojom::Setting::kSetActiveKerberosTicket, + }; + RegisterNestedSettingBulk(mojom::Subpage::kKerberos, kKerberosSettings, + generator); +} + void PeopleSection::OnStateChanged(syncer::SyncService* sync_service) { DCHECK(chromeos::features::IsSplitSettingsSyncEnabled()); DCHECK_EQ(sync_service, sync_service_); + + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (sync_service_->IsEngineInitialized() && sync_service_->GetUserSettings()->IsOsSyncFeatureEnabled()) { - registry()->AddSearchTags(GetSyncOnSearchConcepts()); - registry()->RemoveSearchTags(GetSyncOffSearchConcepts()); + updater.AddSearchTags(GetSplitSyncOnSearchConcepts()); + updater.RemoveSearchTags(GetSplitSyncOffSearchConcepts()); } else { - registry()->RemoveSearchTags(GetSyncOnSearchConcepts()); - registry()->AddSearchTags(GetSyncOffSearchConcepts()); + updater.RemoveSearchTags(GetSplitSyncOnSearchConcepts()); + updater.AddSearchTags(GetSplitSyncOffSearchConcepts()); } } void PeopleSection::OnKerberosEnabledStateChanged() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (kerberos_credentials_manager_->IsKerberosEnabled()) - registry()->AddSearchTags(GetKerberosSearchConcepts()); + updater.AddSearchTags(GetKerberosSearchConcepts()); else - registry()->RemoveSearchTags(GetKerberosSearchConcepts()); + updater.RemoveSearchTags(GetKerberosSearchConcepts()); } void PeopleSection::AddKerberosAccountsPageStrings( @@ -833,8 +1020,10 @@ void PeopleSection::AddKerberosAccountsPageStrings( // Toggles the Chrome OS Kerberos Accounts submenu in the People section. // Note that the handler is also dependent on this pref. - html_source->AddBoolean("isKerberosEnabled", - kerberos_credentials_manager_->IsKerberosEnabled()); + html_source->AddBoolean( + "isKerberosEnabled", + kerberos_credentials_manager_ != nullptr && + kerberos_credentials_manager_->IsKerberosEnabled()); PrefService* local_state = g_browser_process->local_state(); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.h index d10c6805d22..51e8a4b3ea8 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/people_section.h @@ -54,6 +54,11 @@ class PeopleSection : public OsSettingsSection, // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // syncer::SyncServiceObserver: void OnStateChanged(syncer::SyncService* sync_service) override; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc index e7b9d760bc5..dfbbaf17853 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.cc @@ -48,7 +48,7 @@ const std::vector<SearchConcept>& GetPersonalizationSearchConcepts() { {IDS_OS_SETTINGS_TAG_CHANGE_DEVICE_ACCOUNT_IMAGE, mojom::kChangePictureSubpagePath, mojom::SearchResultIcon::kAvatar, - mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kChangePicture}, {IDS_OS_SETTINGS_TAG_CHANGE_DEVICE_ACCOUNT_IMAGE_ALT1, @@ -60,6 +60,7 @@ const std::vector<SearchConcept>& GetPersonalizationSearchConcepts() { return *tags; } +// TODO(b/159766700): Add search concepts for |kAmbientModePhotosSubpagePath|. const std::vector<SearchConcept>& GetAmbientModeSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_AMBIENT_MODE, @@ -122,10 +123,11 @@ PersonalizationSection::PersonalizationSection( if (features::IsGuestModeActive()) return; - registry()->AddSearchTags(GetPersonalizationSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetPersonalizationSearchConcepts()); if (IsAmbientModeAllowed()) { - registry()->AddSearchTags(GetAmbientModeSearchConcepts()); + updater.AddSearchTags(GetAmbientModeSearchConcepts()); pref_change_registrar_.Init(pref_service_); pref_change_registrar_.Add( @@ -197,13 +199,69 @@ void PersonalizationSection::AddHandlers(content::WebUI* web_ui) { } } +int PersonalizationSection::GetSectionNameMessageId() const { + return IDS_OS_SETTINGS_PERSONALIZATION; +} + +mojom::Section PersonalizationSection::GetSection() const { + return mojom::Section::kPersonalization; +} + +mojom::SearchResultIcon PersonalizationSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kPaintbrush; +} + +std::string PersonalizationSection::GetSectionPath() const { + return mojom::kPersonalizationSectionPath; +} + +void PersonalizationSection::RegisterHierarchy( + HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kOpenWallpaper); + + // Change picture. + generator->RegisterTopLevelSubpage( + IDS_OS_SETTINGS_CHANGE_PICTURE_TITLE, mojom::Subpage::kChangePicture, + mojom::SearchResultIcon::kAvatar, mojom::SearchResultDefaultRank::kMedium, + mojom::kChangePictureSubpagePath); + generator->RegisterNestedSetting(mojom::Setting::kChangeDeviceAccountImage, + mojom::Subpage::kChangePicture); + + // Ambient mode. + generator->RegisterTopLevelSubpage( + IDS_OS_SETTINGS_AMBIENT_MODE_TITLE, mojom::Subpage::kAmbientMode, + mojom::SearchResultIcon::kWallpaper, + mojom::SearchResultDefaultRank::kMedium, mojom::kAmbientModeSubpagePath); + static constexpr mojom::Setting kAmbientModeSettings[] = { + mojom::Setting::kAmbientModeOnOff, + mojom::Setting::kAmbientModeSource, + }; + RegisterNestedSettingBulk(mojom::Subpage::kAmbientMode, kAmbientModeSettings, + generator); + + // Note: The subpage name in the UI is updated dynamically based on the topic + // source. + // TODO(b/159766700): Create a string for the page title and strings for the + // search. + generator->RegisterNestedSubpage( + IDS_OS_SETTINGS_AMBIENT_MODE_TITLE, mojom::Subpage::kAmbientModePhotos, + mojom::Subpage::kAmbientMode, mojom::SearchResultIcon::kWallpaper, + mojom::SearchResultDefaultRank::kMedium, + mojom::kAmbientModePhotosSubpagePath); + generator->RegisterNestedSetting( + mojom::Setting::kAmbientModeUpdatePhotosContainers, + mojom::Subpage::kAmbientModePhotos); +} + void PersonalizationSection::OnAmbientModeEnabledStateChanged() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + if (pref_service_->GetBoolean(ash::ambient::prefs::kAmbientModeEnabled)) { - registry()->AddSearchTags(GetAmbientModeOnSearchConcepts()); - registry()->RemoveSearchTags(GetAmbientModeOffSearchConcepts()); + updater.AddSearchTags(GetAmbientModeOnSearchConcepts()); + updater.RemoveSearchTags(GetAmbientModeOffSearchConcepts()); } else { - registry()->RemoveSearchTags(GetAmbientModeOnSearchConcepts()); - registry()->AddSearchTags(GetAmbientModeOffSearchConcepts()); + updater.RemoveSearchTags(GetAmbientModeOnSearchConcepts()); + updater.AddSearchTags(GetAmbientModeOffSearchConcepts()); } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.h index b50653c34e6..f704a53d971 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/personalization_section.h @@ -33,6 +33,11 @@ class PersonalizationSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // ash::AmbientModeService::Observer: void OnAmbientModeEnabledStateChanged(); diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc index 715782b0b11..38bf2e61ebc 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.cc @@ -27,11 +27,24 @@ void PluginVmHandler::RegisterMessages() { "getPluginVmSharedPathsDisplayText", base::BindRepeating( &PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText, - weak_ptr_factory_.GetWeakPtr())); + base::Unretained(this))); web_ui()->RegisterMessageCallback( "removePluginVmSharedPath", base::BindRepeating(&PluginVmHandler::HandleRemovePluginVmSharedPath, - weak_ptr_factory_.GetWeakPtr())); + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "wouldPermissionChangeRequireRelaunch", + base::BindRepeating( + &PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "setPluginVmPermission", + base::BindRepeating(&PluginVmHandler::HandleSetPluginVmPermission, + base::Unretained(this))); + web_ui()->RegisterMessageCallback( + "relaunchPluginVm", + base::BindRepeating(&PluginVmHandler::HandleRelaunchPluginVm, + base::Unretained(this))); } void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText( @@ -68,5 +81,42 @@ void PluginVmHandler::HandleRemovePluginVmSharedPath( path)); } +void PluginVmHandler::HandleWouldPermissionChangeRequireRelaunch( + const base::ListValue* args) { + AllowJavascript(); + CHECK_EQ(3U, args->GetSize()); + std::string callback_id = args->GetList()[0].GetString(); + plugin_vm::PermissionType permission_type = + static_cast<plugin_vm::PermissionType>(args->GetList()[1].GetInt()); + DCHECK(permission_type == plugin_vm::PermissionType::kCamera || + permission_type == plugin_vm::PermissionType::kMicrophone); + plugin_vm::PluginVmManager* manager = + plugin_vm::PluginVmManagerFactory::GetForProfile(profile_); + bool current_value = manager->GetPermission(permission_type); + bool proposed_value = args->GetList()[2].GetBool(); + bool requires_relaunch = proposed_value != current_value && + manager->IsRelaunchNeededForNewPermissions(); + + ResolveJavascriptCallback(base::Value(callback_id), + base::Value(requires_relaunch)); +} + +void PluginVmHandler::HandleSetPluginVmPermission(const base::ListValue* args) { + CHECK_EQ(2U, args->GetSize()); + plugin_vm::PermissionType permission_type = + static_cast<plugin_vm::PermissionType>(args->GetList()[0].GetInt()); + bool proposed_value = args->GetList()[1].GetBool(); + DCHECK(permission_type == plugin_vm::PermissionType::kCamera || + permission_type == plugin_vm::PermissionType::kMicrophone); + plugin_vm::PluginVmManagerFactory::GetForProfile(profile_)->SetPermission( + permission_type, proposed_value); +} + +void PluginVmHandler::HandleRelaunchPluginVm(const base::ListValue* args) { + CHECK_EQ(0U, args->GetList().size()); + plugin_vm::PluginVmManagerFactory::GetForProfile(profile_) + ->RelaunchPluginVm(); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h index d57b34da1db..e1d77e24424 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/plugin_vm_handler.h @@ -8,6 +8,8 @@ #include <vector> #include "base/memory/weak_ptr.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager.h" +#include "chrome/browser/chromeos/plugin_vm/plugin_vm_manager_factory.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" class Profile; @@ -32,8 +34,16 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler { void HandleGetPluginVmSharedPathsDisplayText(const base::ListValue* args); // Remove a specified path from being shared. void HandleRemovePluginVmSharedPath(const base::ListValue* args); + // Checks if Plugin VM would need to be relaunched if the proposed changes are + // made. + void HandleWouldPermissionChangeRequireRelaunch(const base::ListValue* args); + // Sets the specified permission to the value proposed. + void HandleSetPluginVmPermission(const base::ListValue* args); + // Relaunches Plugin VM. + void HandleRelaunchPluginVm(const base::ListValue* args); Profile* profile_; + // weak_ptr_factory_ should always be last member. base::WeakPtrFactory<PluginVmHandler> weak_ptr_factory_{this}; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.cc index 3415a894f61..168efe1d2d6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.cc @@ -28,10 +28,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { mojom::SearchResultIcon::kPrinter, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAddPrinter}, - {IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT1, - IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT2, - SearchConcept::kAltTagEnd}}, + {.setting = mojom::Setting::kAddPrinter}}, {IDS_OS_SETTINGS_TAG_PRINTING_SAVED_PRINTERS, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, @@ -41,7 +38,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { {IDS_OS_SETTINGS_TAG_PRINTING, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kPrintingDetails}, {IDS_OS_SETTINGS_TAG_PRINTING_ALT1, IDS_OS_SETTINGS_TAG_PRINTING_ALT2, @@ -76,9 +73,10 @@ PrintingSection::PrintingSection(Profile* profile, CupsPrintersManager* printers_manager) : OsSettingsSection(profile, search_tag_registry), printers_manager_(printers_manager) { - registry()->AddSearchTags(GetPrintingSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetPrintingSearchConcepts()); if (IsPrintManagementEnabled()) - registry()->AddSearchTags(GetPrintingManagementSearchConcepts()); + updater.AddSearchTags(GetPrintingManagementSearchConcepts()); } PrintingSection::~PrintingSection() = default; @@ -246,5 +244,38 @@ void PrintingSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<CupsPrintersHandler>(profile(), printers_manager_)); } +int PrintingSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_PRINTING; +} + +mojom::Section PrintingSection::GetSection() const { + return mojom::Section::kPrinting; +} + +mojom::SearchResultIcon PrintingSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kPrinter; +} + +std::string PrintingSection::GetSectionPath() const { + return mojom::kPrintingSectionPath; +} + +void PrintingSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kPrintJobs); + + // Printing details. + generator->RegisterTopLevelSubpage(IDS_SETTINGS_PRINTING_CUPS_PRINTERS, + mojom::Subpage::kPrintingDetails, + mojom::SearchResultIcon::kPrinter, + mojom::SearchResultDefaultRank::kMedium, + mojom::kPrintingDetailsSubpagePath); + static constexpr mojom::Setting kPrintingDetailsSettings[] = { + mojom::Setting::kAddPrinter, + mojom::Setting::kSavedPrinters, + }; + RegisterNestedSettingBulk(mojom::Subpage::kPrintingDetails, + kPrintingDetailsSettings, generator); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.h index b231aeec420..ddef65c546e 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/printing_section.h @@ -31,6 +31,11 @@ class PrintingSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; CupsPrintersManager* printers_manager_; }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc index 94e70f481b3..85c6dea6016 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.cc @@ -13,6 +13,7 @@ #include "chrome/common/chrome_features.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" +#include "chromeos/constants/chromeos_features.h" #include "content/public/browser/web_ui_data_source.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" @@ -32,7 +33,7 @@ const std::vector<SearchConcept>& GetPrivacySearchConcepts() { {IDS_OS_SETTINGS_TAG_PRIVACY, mojom::kPrivacyAndSecuritySectionPath, mojom::SearchResultIcon::kShield, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSection, {.section = mojom::Section::kPrivacyAndSecurity}}, {IDS_OS_SETTINGS_TAG_PRIVACY_WIFI_SLEEP, @@ -68,9 +69,10 @@ const std::vector<SearchConcept>& GetPrivacyGoogleChromeSearchConcepts() { PrivacySection::PrivacySection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetPrivacySearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetPrivacySearchConcepts()); #if BUILDFLAG(GOOGLE_CHROME_BRANDING) - registry()->AddSearchTags(GetPrivacyGoogleChromeSearchConcepts()); + updater.AddSearchTags(GetPrivacyGoogleChromeSearchConcepts()); #endif // BUILDFLAG(GOOGLE_CHROME_BRANDING) } @@ -84,6 +86,9 @@ void PrivacySection::AddLoadTimeData(content::WebUIDataSource* html_source) { {"wakeOnWifi", IDS_SETTINGS_WAKE_ON_WIFI_DESCRIPTION}, {"enableContentProtectionAttestation", IDS_SETTINGS_ENABLE_CONTENT_PROTECTION_ATTESTATION}, + {"enableSuggestedContent", IDS_SETTINGS_ENABLE_SUGGESTED_CONTENT_TITLE}, + {"enableSuggestedContentDesc", + IDS_SETTINGS_ENABLE_SUGGESTED_CONTENT_DESC}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); @@ -91,10 +96,40 @@ void PrivacySection::AddLoadTimeData(content::WebUIDataSource* html_source) { "privacySettingsRedesignEnabled", base::FeatureList::IsEnabled(::features::kPrivacySettingsRedesign)); + html_source->AddBoolean("suggestedContentToggleEnabled", + base::FeatureList::IsEnabled( + ::chromeos::features::kSuggestedContentToggle)); + + html_source->AddString("suggestedContentLearnMoreURL", + chrome::kSuggestedContentLearnMoreURL); + html_source->AddString("syncAndGoogleServicesLearnMoreURL", chrome::kSyncAndGoogleServicesLearnMoreURL); ::settings::AddPersonalizationOptionsStrings(html_source); } +int PrivacySection::GetSectionNameMessageId() const { + return IDS_SETTINGS_PRIVACY; +} + +mojom::Section PrivacySection::GetSection() const { + return mojom::Section::kPrivacyAndSecurity; +} + +mojom::SearchResultIcon PrivacySection::GetSectionIcon() const { + return mojom::SearchResultIcon::kShield; +} + +std::string PrivacySection::GetSectionPath() const { + return mojom::kPrivacyAndSecuritySectionPath; +} + +void PrivacySection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kVerifiedAccess); + generator->RegisterTopLevelSetting(mojom::Setting::kKeepWifiOnDuringSleep); + generator->RegisterTopLevelSetting( + mojom::Setting::kUsageStatsAndCrashReports); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.h index 3c5ba842681..4acbc9d465c 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/privacy_section.h @@ -26,6 +26,11 @@ class PrivacySection : public OsSettingsSection { private: // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.cc index 1275fb0e188..21216088ddc 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.cc @@ -56,8 +56,9 @@ bool IsPowerwashAllowed() { ResetSection::ResetSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); if (IsPowerwashAllowed()) - registry()->AddSearchTags(GetResetSearchConcepts()); + updater.AddSearchTags(GetResetSearchConcepts()); } ResetSection::~ResetSection() = default; @@ -94,5 +95,25 @@ void ResetSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<::settings::ResetSettingsHandler>(profile())); } +int ResetSection::GetSectionNameMessageId() const { + return IDS_SETTINGS_RESET; +} + +mojom::Section ResetSection::GetSection() const { + return mojom::Section::kReset; +} + +mojom::SearchResultIcon ResetSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kReset; +} + +std::string ResetSection::GetSectionPath() const { + return mojom::kResetSectionPath; +} + +void ResetSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kPowerwash); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.h index ee99e0554c7..90287737e66 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/reset_section.h @@ -28,6 +28,11 @@ class ResetSection : public OsSettingsSection { // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom index 4be5d33c332..d63ae03274c 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search.mojom @@ -30,6 +30,22 @@ enum SearchResultDefaultRank { kLow, }; +// Behavior used when determining whether to return parent results for a query. +// Results are returned based on whether text for those results matches the +// user's query, but we also support returning a result for the parent of the +// original result. In this context, a "parent" refers to a section or subpage +// which contains a child subpage or setting. For example, +// Subpage::kWifiNetworks is a subpage whose parent is Section::kNetwork, and +// Setting::kWifiOnOff is a setting whose parent is Subpage::kWifiNetworks. +enum ParentResultBehavior { + // Returns parent results as long as the number of maximum results is not + // exceeded. + kAllowParentResults, + + // Does not return parent results. + kDoNotIncludeParentResults, +}; + // Identifier for the result; each result describes one section, subpage, or // setting. union SearchResultIdentifier { @@ -44,6 +60,12 @@ struct SearchResult { // directly (i.e., not an ID but rather the actual text). mojo_base.mojom.String16 result_text; + // String for the "canonical" version of this result. Some search results use + // alternate text (e.g., "Monitor" instead of "Display"). Note that it is + // often the case that |result_text| and |canonical_result_text| are the same + // string. + mojo_base.mojom.String16 canonical_result_text; + // The URL path containing the relevant setting, which may or may not contain // URL parameters. For example, the Wi-Fi list settings page is // chrome://os-settings/networks?type=WiFi, so the field would be @@ -57,29 +79,57 @@ struct SearchResult { // string match. double relevance_score; - // List of names of the sections/subpages for this result, which may contain - // names of sections and/or subpages. Names are all localized String16s, ready - // to be displayed directly (e.g., as breadcrumbs). + // List of names of the ancestor sections/subpages for this result. The list + // contains the Settings app name and, if applicable, the ancestor section and + // subpage names. Names are all localized String16s which can be displayed in + // the UI (e.g., as breadcrumbs). // - // Example 1 - Wi-Fi subpage: ["Settings", "Network", "Wi-Fi"] - // Example 2 - External storage: ["Settings", "Device", "Storage management", - // "External storage preferences"] + // Example 1 - Wi-Fi subpage: ["Settings", "Network"] + // Example 2 - External storage: ["Settings", "Device", "Storage management"] array<mojo_base.mojom.String16> settings_page_hierarchy; // Default ranking, which is used to break ties when searching for results. SearchResultDefaultRank default_rank; + // True if this result was generated due to a text match; this field can be + // false if it was constructed due to a ParentResultBehavior. + bool was_generated_from_text_match; + // The type and identifier for this search result. The value of the |type| // field indicates the union member used by |id|. SearchResultType type; SearchResultIdentifier id; }; +// Used to observe changes to search results. +interface SearchResultsObserver { + // Called when the availability of one or more search results has changed. In + // this context, "availability" refers to whether a search result can be + // returned based on the user's current state. E.g., "Cellular" results are + // only shown if the device has an attached modem, so this function would be + // called whenever the user plugs in or unplugs a USB modem. Clients can use + // this function to ensure that they do not show "stale" results which are no + // longer actionable by the user. + OnSearchResultAvailabilityChanged(); +}; + // Provides settings search results. Implemented in the browser process; // intended to be called from settings JS and Launcher C++. interface SearchHandler { // Searches settings for the given query and returns a list of results, sorted // from most relevant to least relevant. An empty array indicates no relevant // results. - Search(mojo_base.mojom.String16 query) => (array<SearchResult> results); + // + // This function returns an array with a maximum size of |max_num_results|, + // but the array may contain fewer elements if there are fewer results. + // Clients should never pass a value of 0 for |max_num_results|, since that + // would return an empty result array. + Search(mojo_base.mojom.String16 query, + uint32 max_num_results, + ParentResultBehavior parent_result_behavior) => + (array<SearchResult> results); + + // Adds an observer of search results. Disconnected observers are discarded; + // to stop observing, close the connection. + Observe(pending_remote<SearchResultsObserver> observer); }; diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h index b3763fb0fd3..639dc470ce3 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_concept.h @@ -7,6 +7,7 @@ #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/setting.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_identifier.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h" @@ -58,7 +59,7 @@ struct SearchConcept { // The type and identifier for this search result. The value of the |type| // field indicates the union member used by |id|. mojom::SearchResultType type; - Identifier id; + OsSettingsIdentifier id; // Alternate message IDs (from os_settings_search_tag_strings.grdp) // corresponding to this concept. These IDs refer to messages which represent diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc index 47c0052bbf0..1bde04057b2 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.cc @@ -4,12 +4,14 @@ #include "chrome/browser/ui/webui/settings/chromeos/search/search_handler.h" +#include <algorithm> + #include "base/strings/string_number_conversions.h" -#include "base/strings/utf_string_conversions.h" #include "chrome/browser/chromeos/local_search_service/local_search_service.h" +#include "chrome/browser/ui/webui/settings/chromeos/hierarchy.h" +#include "chrome/browser/ui/webui/settings/chromeos/os_settings_sections.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom.h" -#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "chrome/grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" @@ -17,30 +19,42 @@ namespace chromeos { namespace settings { namespace { -const int32_t kLocalSearchServiceMaxResults = 10; +bool ContainsSectionResult(const std::vector<mojom::SearchResultPtr>& results, + mojom::Section section) { + return std::find_if( + results.begin(), results.end(), [section](const auto& result) { + return result->type == mojom::SearchResultType::kSection && + section == result->id->get_section(); + }) != results.end(); +} -// TODO(https://crbug.com/1071700): Delete this function. -std::vector<base::string16> GenerateDummySettingsHierarchy( - const char* url_path_with_parameters) { - std::vector<base::string16> hierarchy; - hierarchy.push_back(l10n_util::GetStringUTF16(IDS_INTERNAL_APP_SETTINGS)); - hierarchy.push_back(base::ASCIIToUTF16(url_path_with_parameters)); - return hierarchy; +bool ContainsSubpageResult(const std::vector<mojom::SearchResultPtr>& results, + mojom::Subpage subpage) { + return std::find_if( + results.begin(), results.end(), [subpage](const auto& result) { + return result->type == mojom::SearchResultType::kSubpage && + subpage == result->id->get_subpage(); + }) != results.end(); } } // namespace -// static -const size_t SearchHandler::kNumMaxResults = 5; - SearchHandler::SearchHandler( SearchTagRegistry* search_tag_registry, + OsSettingsSections* sections, + Hierarchy* hierarchy, local_search_service::LocalSearchService* local_search_service) : search_tag_registry_(search_tag_registry), + sections_(sections), + hierarchy_(hierarchy), index_(local_search_service->GetIndex( - local_search_service::IndexId::kCrosSettings)) {} + local_search_service::IndexId::kCrosSettings)) { + search_tag_registry_->AddObserver(this); +} -SearchHandler::~SearchHandler() = default; +SearchHandler::~SearchHandler() { + search_tag_registry_->RemoveObserver(this); +} void SearchHandler::BindInterface( mojo::PendingReceiver<mojom::SearchHandler> pending_receiver) { @@ -48,10 +62,19 @@ void SearchHandler::BindInterface( } std::vector<mojom::SearchResultPtr> SearchHandler::Search( - const base::string16& query) { + const base::string16& query, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior) { + // Search for 5x the maximum set of results. If there are many matches for + // a query, it may be the case that |index_| returns some matches with higher + // SearchResultDefaultRank values later in the list. Requesting up to 5x the + // maximum number ensures that such results will be returned and can be ranked + // accordingly when sorted. + uint32_t max_local_search_service_results = 5 * max_num_results; + std::vector<local_search_service::Result> local_search_service_results; local_search_service::ResponseStatus response_status = index_->Find( - query, kLocalSearchServiceMaxResults, &local_search_service_results); + query, max_local_search_service_results, &local_search_service_results); if (response_status != local_search_service::ResponseStatus::kSuccess) { LOG(ERROR) << "Cannot search; LocalSearchService returned " @@ -60,41 +83,149 @@ std::vector<mojom::SearchResultPtr> SearchHandler::Search( return {}; } - return GenerateSearchResultsArray(local_search_service_results); + return GenerateSearchResultsArray(local_search_service_results, + max_num_results, parent_result_behavior); } void SearchHandler::Search(const base::string16& query, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior, SearchCallback callback) { - std::move(callback).Run(Search(query)); + std::move(callback).Run( + Search(query, max_num_results, parent_result_behavior)); +} + +void SearchHandler::Observe( + mojo::PendingRemote<mojom::SearchResultsObserver> observer) { + observers_.Add(std::move(observer)); +} + +void SearchHandler::OnRegistryUpdated() { + for (auto& observer : observers_) + observer->OnSearchResultAvailabilityChanged(); } std::vector<mojom::SearchResultPtr> SearchHandler::GenerateSearchResultsArray( const std::vector<local_search_service::Result>& - local_search_service_results) { + local_search_service_results, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior) const { std::vector<mojom::SearchResultPtr> search_results; for (const auto& result : local_search_service_results) { mojom::SearchResultPtr result_ptr = ResultToSearchResult(result); if (result_ptr) search_results.push_back(std::move(result_ptr)); + } - // Limit the number of results to return. - if (search_results.size() == kNumMaxResults) - break; + std::sort(search_results.begin(), search_results.end(), CompareSearchResults); + + // Now that the results have been sorted, limit the size of to + // |max_num_results|. + search_results.resize( + std::min(static_cast<size_t>(max_num_results), search_results.size())); + + if (parent_result_behavior == + mojom::ParentResultBehavior::kAllowParentResults) { + AddParentResults(max_num_results, &search_results); } return search_results; } -mojom::SearchResultPtr SearchHandler::ResultToSearchResult( - const local_search_service::Result& result) { - int message_id; +void SearchHandler::AddParentResults( + uint32_t max_num_results, + std::vector<mojom::SearchResultPtr>* search_results) const { + auto it = search_results->begin(); + while (search_results->size() < max_num_results && + it != search_results->end()) { + const mojom::SearchResultPtr& result = *it; + switch (result->type) { + case mojom::SearchResultType::kSection: + // Sections have no parents; nothing to do. + break; - // The result's ID is expected to be a stringified int. - if (!base::StringToInt(result.id, &message_id)) - return nullptr; + case mojom::SearchResultType::kSubpage: { + const Hierarchy::SubpageMetadata& metadata = + hierarchy_->GetSubpageMetadata(result->id->get_subpage()); + + // Nested subpage. + if (metadata.parent_subpage) { + it = AddSubpageResultIfPossible(it, *metadata.parent_subpage, + result->relevance_score, + search_results); + break; + } + + // Top-level subpage. + it = AddSectionResultIfPossible(it, result, metadata.section, + search_results); + break; + } + + case mojom::SearchResultType::kSetting: { + const Hierarchy::SettingMetadata& metadata = + hierarchy_->GetSettingMetadata(result->id->get_setting()); + + // Nested setting. + if (metadata.primary.second) { + it = AddSubpageResultIfPossible(it, *metadata.primary.second, + result->relevance_score, + search_results); + break; + } + + // Top-level setting. + it = AddSectionResultIfPossible(it, result, metadata.primary.first, + search_results); + break; + } + } + ++it; + } +} + +std::vector<mojom::SearchResultPtr>::iterator +SearchHandler::AddSectionResultIfPossible( + const std::vector<mojom::SearchResultPtr>::iterator& curr_position, + const mojom::SearchResultPtr& child_result, + mojom::Section section, + std::vector<mojom::SearchResultPtr>* results) const { + // If |results| already includes |section|, do not add it again. + if (ContainsSectionResult(*results, section)) + return curr_position; + + mojom::SearchResultPtr section_result = + hierarchy_->GetSectionMetadata(section).ToSearchResult( + child_result->relevance_score); + + // Don't add a result for a parent section if it has the exact same text as + // the child result, since this results in a broken-looking UI. + if (section_result->result_text == child_result->result_text) + return curr_position; + + return results->insert(curr_position + 1, std::move(section_result)); +} + +std::vector<mojom::SearchResultPtr>::iterator +SearchHandler::AddSubpageResultIfPossible( + const std::vector<mojom::SearchResultPtr>::iterator& curr_position, + mojom::Subpage subpage, + double relevance_score, + std::vector<mojom::SearchResultPtr>* results) const { + // If |results| already includes |subpage|, do not add it again. + if (ContainsSubpageResult(*results, subpage)) + return curr_position; + + return results->insert( + curr_position + 1, + hierarchy_->GetSubpageMetadata(subpage).ToSearchResult(relevance_score)); +} + +mojom::SearchResultPtr SearchHandler::ResultToSearchResult( + const local_search_service::Result& result) const { const SearchConcept* concept = - search_tag_registry_->GetCanonicalTagMetadata(message_id); + search_tag_registry_->GetTagMetadata(result.id); // If the concept was not registered, no metadata is available. This can occur // if the search tag was dynamically unregistered during the asynchronous @@ -102,29 +233,85 @@ mojom::SearchResultPtr SearchHandler::ResultToSearchResult( if (!concept) return nullptr; + // |result| is expected to have one position, whose ID is a stringified int. + DCHECK_EQ(1u, result.positions.size()); + int content_id; + if (!base::StringToInt(result.positions[0].content_id, &content_id)) + return nullptr; + + std::string url; mojom::SearchResultIdentifierPtr result_id; + std::vector<base::string16> hierarchy_strings; switch (concept->type) { - case mojom::SearchResultType::kSection: - result_id = - mojom::SearchResultIdentifier::NewSection(concept->id.section); + case mojom::SearchResultType::kSection: { + mojom::Section section = concept->id.section; + url = GetModifiedUrl(*concept, section); + result_id = mojom::SearchResultIdentifier::NewSection(section); + hierarchy_strings.push_back( + l10n_util::GetStringUTF16(IDS_INTERNAL_APP_SETTINGS)); break; - case mojom::SearchResultType::kSubpage: - result_id = - mojom::SearchResultIdentifier::NewSubpage(concept->id.subpage); + } + case mojom::SearchResultType::kSubpage: { + mojom::Subpage subpage = concept->id.subpage; + url = GetModifiedUrl(*concept, + hierarchy_->GetSubpageMetadata(subpage).section); + result_id = mojom::SearchResultIdentifier::NewSubpage(subpage); + hierarchy_strings = hierarchy_->GenerateAncestorHierarchyStrings(subpage); break; - case mojom::SearchResultType::kSetting: - result_id = - mojom::SearchResultIdentifier::NewSetting(concept->id.setting); + } + case mojom::SearchResultType::kSetting: { + mojom::Setting setting = concept->id.setting; + url = GetModifiedUrl( + *concept, hierarchy_->GetSettingMetadata(setting).primary.first); + result_id = mojom::SearchResultIdentifier::NewSetting(setting); + hierarchy_strings = hierarchy_->GenerateAncestorHierarchyStrings(setting); break; + } } - // TODO(https://crbug.com/1071700): Generate real hierarchy instead of using - // GenerateDummySettingsHierarchy(). return mojom::SearchResult::New( - l10n_util::GetStringUTF16(message_id), concept->url_path_with_parameters, - concept->icon, result.score, - GenerateDummySettingsHierarchy(concept->url_path_with_parameters), - concept->default_rank, concept->type, std::move(result_id)); + /*result_text=*/l10n_util::GetStringUTF16(content_id), + /*canonical_result_text=*/ + l10n_util::GetStringUTF16(concept->canonical_message_id), url, + concept->icon, result.score, hierarchy_strings, concept->default_rank, + /*was_generated_from_text_match=*/true, concept->type, + std::move(result_id)); +} + +std::string SearchHandler::GetModifiedUrl(const SearchConcept& concept, + mojom::Section section) const { + return sections_->GetSection(section)->ModifySearchResultUrl( + concept.type, concept.id, concept.url_path_with_parameters); +} + +// static +bool SearchHandler::CompareSearchResults(const mojom::SearchResultPtr& first, + const mojom::SearchResultPtr& second) { + // Compute the difference between the results' default rankings. Note that + // kHigh is declared before kMedium which is declared before kLow, so a + // negative value indicates that |first| is ranked higher than |second| and a + // positive value indicates that |second| is ranked higher than |first|. + int32_t default_rank_diff = static_cast<int32_t>(first->default_rank) - + static_cast<int32_t>(second->default_rank); + if (default_rank_diff < 0) + return true; + if (default_rank_diff > 0) + return false; + + // At this point, the default ranks are equal, so compare relevance scores. A + // higher relevance score indicates a better text match, so the reverse is + // true this time. + if (first->relevance_score > second->relevance_score) + return true; + if (first->relevance_score < second->relevance_score) + return false; + + // Default rank and relevance scores are equal, so prefer the result which is + // higher on the hierarchy. kSection is declared before kSubpage which is + // declared before kSetting, so follow the same pattern from default ranks + // above. Note that if the types are equal, this will return false, which + // induces a strict weak ordering. + return static_cast<int32_t>(first->type) < static_cast<int32_t>(second->type); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h index 8247cccaa9d..2425c2b56ac 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler.h @@ -7,12 +7,16 @@ #include <vector> +#include "base/gtest_prod_util.h" #include "base/optional.h" #include "chrome/browser/chromeos/local_search_service/index.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/pending_remote.h" #include "mojo/public/cpp/bindings/receiver_set.h" #include "mojo/public/cpp/bindings/remote.h" +#include "mojo/public/cpp/bindings/remote_set.h" namespace local_search_service { class LocalSearchService; @@ -21,7 +25,9 @@ class LocalSearchService; namespace chromeos { namespace settings { -class SearchTagRegistry; +class Hierarchy; +class OsSettingsSections; +struct SearchConcept; // Handles search queries for Chrome OS settings. Search() is expected to be // invoked by the settings UI as well as the the Launcher search UI. Search @@ -29,14 +35,13 @@ class SearchTagRegistry; // indexed in the LocalSearchService and cross-referencing results with // SearchTagRegistry. // -// SearchHandler returns at most |kNumMaxResults| results; searches which do not -// provide any matches result in an empty results array. -class SearchHandler : public mojom::SearchHandler { +// Searches which do not provide any matches result in an empty results array. +class SearchHandler : public mojom::SearchHandler, + public SearchTagRegistry::Observer { public: - // Maximum number of results returned by a Search() call. - static const size_t kNumMaxResults; - SearchHandler(SearchTagRegistry* search_tag_registry, + OsSettingsSections* sections, + Hierarchy* hierarchy, local_search_service::LocalSearchService* local_search_service); ~SearchHandler() override; @@ -47,23 +52,64 @@ class SearchHandler : public mojom::SearchHandler { mojo::PendingReceiver<mojom::SearchHandler> pending_receiver); // Synchronous search implementation (for in-process clients). - std::vector<mojom::SearchResultPtr> Search(const base::string16& query); + std::vector<mojom::SearchResultPtr> Search( + const base::string16& query, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior); // mojom::SearchHandler: - void Search(const base::string16& query, SearchCallback callback) override; + void Search(const base::string16& query, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior, + SearchCallback callback) override; + void Observe( + mojo::PendingRemote<mojom::SearchResultsObserver> observer) override; private: + FRIEND_TEST_ALL_PREFIXES(SearchHandlerTest, CompareSearchResults); + + // SearchTagRegistry::Observer: + void OnRegistryUpdated() override; + std::vector<mojom::SearchResultPtr> GenerateSearchResultsArray( const std::vector<local_search_service::Result>& - local_search_service_results); + local_search_service_results, + uint32_t max_num_results, + mojom::ParentResultBehavior parent_result_behavior) const; + + void AddParentResults( + uint32_t max_num_results, + std::vector<mojom::SearchResultPtr>* search_results) const; + + std::vector<mojom::SearchResultPtr>::iterator AddSectionResultIfPossible( + const std::vector<mojom::SearchResultPtr>::iterator& position, + const mojom::SearchResultPtr& child_result, + mojom::Section section, + std::vector<mojom::SearchResultPtr>* results) const; + + std::vector<mojom::SearchResultPtr>::iterator AddSubpageResultIfPossible( + const std::vector<mojom::SearchResultPtr>::iterator& position, + mojom::Subpage subpage, + double relevance_score, + std::vector<mojom::SearchResultPtr>* results) const; + mojom::SearchResultPtr ResultToSearchResult( - const local_search_service::Result& result); + const local_search_service::Result& result) const; + std::string GetModifiedUrl(const SearchConcept& concept, + mojom::Section section) const; + + // Returns true if |first| should be ranked before |second|. + static bool CompareSearchResults(const mojom::SearchResultPtr& first, + const mojom::SearchResultPtr& second); SearchTagRegistry* search_tag_registry_; + OsSettingsSections* sections_; + Hierarchy* hierarchy_; local_search_service::Index* index_; - // Note: Expected to have multiple clients, so a ReceiverSet is used. + // Note: Expected to have multiple clients, so ReceiverSet/RemoteSet are used. mojo::ReceiverSet<mojom::SearchHandler> receivers_; + mojo::RemoteSet<mojom::SearchResultsObserver> observers_; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc index f2c9d42af8d..d0ecd2baef6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_handler_unittest.cc @@ -10,17 +10,41 @@ #include "base/test/task_environment.h" #include "chrome/browser/chromeos/local_search_service/local_search_service.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" +#include "chrome/browser/ui/webui/settings/chromeos/fake_hierarchy.h" +#include "chrome/browser/ui/webui/settings/chromeos/fake_os_settings_sections.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search.mojom-test-utils.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "chrome/grit/generated_resources.h" #include "chromeos/constants/chromeos_features.h" #include "mojo/public/cpp/bindings/remote.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/l10n/l10n_util.h" namespace chromeos { namespace settings { namespace { +class FakeObserver : public mojom::SearchResultsObserver { + public: + FakeObserver() = default; + ~FakeObserver() override = default; + + mojo::PendingRemote<mojom::SearchResultsObserver> GenerateRemote() { + mojo::PendingRemote<mojom::SearchResultsObserver> remote; + receiver_.Bind(remote.InitWithNewPipeAndPassReceiver()); + return remote; + } + + size_t num_calls() const { return num_calls_; } + + private: + // mojom::SearchResultsObserver: + void OnSearchResultAvailabilityChanged() override { ++num_calls_; } + + size_t num_calls_ = 0; + mojo::Receiver<mojom::SearchResultsObserver> receiver_{this}; +}; + // Note: Copied from printing_section.cc but does not need to stay in sync with // it. const std::vector<SearchConcept>& GetPrintingSearchConcepts() { @@ -30,10 +54,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { mojom::SearchResultIcon::kPrinter, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAddPrinter}, - {IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT1, - IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT2, - SearchConcept::kAltTagEnd}}, + {.setting = mojom::Setting::kAddPrinter}}, {IDS_OS_SETTINGS_TAG_PRINTING_SAVED_PRINTERS, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, @@ -43,7 +64,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { {IDS_OS_SETTINGS_TAG_PRINTING, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kPrintingDetails}, {IDS_OS_SETTINGS_TAG_PRINTING_ALT1, IDS_OS_SETTINGS_TAG_PRINTING_ALT2, @@ -52,13 +73,30 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { return *tags; } +// Creates a result with some default values. +mojom::SearchResultPtr CreateDummyResult() { + return mojom::SearchResult::New( + /*result_text=*/base::string16(), + /*canonical_result_text=*/base::string16(), /*url=*/"", + mojom::SearchResultIcon::kPrinter, /*relevance_score=*/0.5, + /*hierarchy_strings=*/std::vector<base::string16>(), + mojom::SearchResultDefaultRank::kMedium, + /*was_generated_from_text_match=*/false, + mojom::SearchResultType::kSection, + mojom::SearchResultIdentifier::NewSection(mojom::Section::kPrinting)); +} + } // namespace class SearchHandlerTest : public testing::Test { protected: SearchHandlerTest() : search_tag_registry_(&local_search_service_), - handler_(&search_tag_registry_, &local_search_service_) {} + fake_hierarchy_(&fake_sections_), + handler_(&search_tag_registry_, + &fake_sections_, + &fake_hierarchy_, + &local_search_service_) {} ~SearchHandlerTest() override = default; // testing::Test: @@ -66,37 +104,209 @@ class SearchHandlerTest : public testing::Test { scoped_feature_list_.InitAndEnableFeature( chromeos::features::kNewOsSettingsSearch); handler_.BindInterface(handler_remote_.BindNewPipeAndPassReceiver()); + + fake_hierarchy_.AddSubpageMetadata( + IDS_SETTINGS_PRINTING_CUPS_PRINTERS, mojom::Section::kPrinting, + mojom::Subpage::kPrintingDetails, mojom::SearchResultIcon::kPrinter, + mojom::SearchResultDefaultRank::kMedium, + mojom::kPrintingDetailsSubpagePath); + fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting, + mojom::Setting::kAddPrinter); + fake_hierarchy_.AddSettingMetadata(mojom::Section::kPrinting, + mojom::Setting::kSavedPrinters); + + handler_remote_->Observe(observer_.GenerateRemote()); + handler_remote_.FlushForTesting(); + } + + void AddSearchTags(const std::vector<SearchConcept>& search_tags) { + SearchTagRegistry::ScopedTagUpdater updater = + search_tag_registry_.StartUpdate(); + updater.AddSearchTags(search_tags); + } + + void RemoveSearchTags(const std::vector<SearchConcept>& search_tags) { + SearchTagRegistry::ScopedTagUpdater updater = + search_tag_registry_.StartUpdate(); + updater.RemoveSearchTags(search_tags); } base::test::TaskEnvironment task_environment_; base::test::ScopedFeatureList scoped_feature_list_; local_search_service::LocalSearchService local_search_service_; SearchTagRegistry search_tag_registry_; + FakeOsSettingsSections fake_sections_; + FakeHierarchy fake_hierarchy_; SearchHandler handler_; mojo::Remote<mojom::SearchHandler> handler_remote_; + FakeObserver observer_; }; TEST_F(SearchHandlerTest, AddAndRemove) { - // Add printing search tags to registry and search for "Printing". - search_tag_registry_.AddSearchTags(GetPrintingSearchConcepts()); + // Add printing search tags to registry and search for "Print". + AddSearchTags(GetPrintingSearchConcepts()); + handler_remote_.FlushForTesting(); + EXPECT_EQ(1u, observer_.num_calls()); + std::vector<mojom::SearchResultPtr> search_results; + + // 3 results should be available for a "Print" query. mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) - .Search(base::ASCIIToUTF16("Printing"), &search_results); + .Search(base::ASCIIToUTF16("Print"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); + EXPECT_EQ(search_results.size(), 3u); - // Multiple results should be available. - EXPECT_GT(search_results.size(), 0u); + // Limit results to 1 max and ensure that only 1 result is returned. + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("Print"), + /*max_num_results=*/1u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); + EXPECT_EQ(search_results.size(), 1u); // Search for a query which should return no results. mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) - .Search(base::ASCIIToUTF16("QueryWithNoResults"), &search_results); + .Search(base::ASCIIToUTF16("QueryWithNoResults"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); EXPECT_TRUE(search_results.empty()); // Remove printing search tags to registry and verify that no results are // returned for "Printing". - search_tag_registry_.RemoveSearchTags(GetPrintingSearchConcepts()); + RemoveSearchTags(GetPrintingSearchConcepts()); mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) - .Search(base::ASCIIToUTF16("Printing"), &search_results); + .Search(base::ASCIIToUTF16("Print"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); EXPECT_TRUE(search_results.empty()); + EXPECT_EQ(2u, observer_.num_calls()); +} + +TEST_F(SearchHandlerTest, UrlModification) { + // Add printing search tags to registry and search for "Saved". + AddSearchTags(GetPrintingSearchConcepts()); + std::vector<mojom::SearchResultPtr> search_results; + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("Saved"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); + + // Only the "saved printers" item should be returned. + EXPECT_EQ(search_results.size(), 1u); + + // The URL should have bee modified according to the FakeOsSettingSection + // scheme. + EXPECT_EQ( + std::string("Section::kPrinting::") + mojom::kPrintingDetailsSubpagePath, + search_results[0]->url_path_with_parameters); +} + +TEST_F(SearchHandlerTest, AltTagMatch) { + // Add printing search tags to registry. + AddSearchTags(GetPrintingSearchConcepts()); + std::vector<mojom::SearchResultPtr> search_results; + + // Search for "CUPS". The IDS_OS_SETTINGS_TAG_PRINTING result has an alternate + // tag "CUPS" (referring to the Unix printing protocol), so we should receive + // one match. + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("CUPS"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kDoNotIncludeParentResults, + &search_results); + EXPECT_EQ(search_results.size(), 1u); + + // Verify the result text and canonical restult text. + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_OS_SETTINGS_TAG_PRINTING_ALT2), + search_results[0]->result_text); + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_OS_SETTINGS_TAG_PRINTING), + search_results[0]->canonical_result_text); +} + +TEST_F(SearchHandlerTest, AllowParentResult) { + // Add printing search tags to registry. + AddSearchTags(GetPrintingSearchConcepts()); + std::vector<mojom::SearchResultPtr> search_results; + + // Search for "Saved", which should only apply to the "saved printers" item. + // Pass the kAllowParentResults flag, which should also cause its parent + // subpage item to be returned. + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("Saved"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kAllowParentResults, + &search_results); + EXPECT_EQ(search_results.size(), 2u); + EXPECT_FALSE(search_results[1]->was_generated_from_text_match); +} + +TEST_F(SearchHandlerTest, DefaultRank) { + // Add printing search tags to registry. + AddSearchTags(GetPrintingSearchConcepts()); + std::vector<mojom::SearchResultPtr> search_results; + + // Search for "Print". Only the IDS_OS_SETTINGS_TAG_PRINTING result + // contains the word "Printing", but the other results have the similar word + // "Printer". Thus, "Printing" has a higher relevance score. + mojom::SearchHandlerAsyncWaiter(handler_remote_.get()) + .Search(base::ASCIIToUTF16("Print"), + /*max_num_results=*/3u, + mojom::ParentResultBehavior::kAllowParentResults, + &search_results); + EXPECT_EQ(search_results.size(), 3u); + + // Since the IDS_OS_SETTINGS_TAG_PRINTING result has a default rank of kLow, + // it should be the *last* result returned even though it has a higher + // relevance score. + EXPECT_EQ(l10n_util::GetStringUTF16(IDS_OS_SETTINGS_TAG_PRINTING), + search_results[2]->result_text); +} + +// Regression test for https://crbug.com/1090184. +TEST_F(SearchHandlerTest, CompareSearchResults) { + // Create two equal dummy results. + mojom::SearchResultPtr a = CreateDummyResult(); + mojom::SearchResultPtr b = CreateDummyResult(); + + // CompareSearchResults() returns whether |a| < |b|; since they are equal, it + // should return false regardless of the order of parameters. + EXPECT_FALSE(SearchHandler::CompareSearchResults(a, b)); + EXPECT_FALSE(SearchHandler::CompareSearchResults(b, a)); + + // Differ only on default rank. + a = CreateDummyResult(); + a->default_rank = mojom::SearchResultDefaultRank::kLow; + b = CreateDummyResult(); + b->default_rank = mojom::SearchResultDefaultRank::kHigh; + + // Comparison value should differ. + EXPECT_NE(SearchHandler::CompareSearchResults(b, a), + SearchHandler::CompareSearchResults(a, b)); + + // Differ only on relevance score. + a = CreateDummyResult(); + a->relevance_score = 0; + b = CreateDummyResult(); + b->relevance_score = 1; + + // Comparison value should differ. + EXPECT_NE(SearchHandler::CompareSearchResults(b, a), + SearchHandler::CompareSearchResults(a, b)); + + // Differ only on type. + a = CreateDummyResult(); + a->type = mojom::SearchResultType::kSection; + b = CreateDummyResult(); + b->type = mojom::SearchResultType::kSubpage; + + // Comparison value should differ. + EXPECT_NE(SearchHandler::CompareSearchResults(b, a), + SearchHandler::CompareSearchResults(a, b)); } } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom index 5f5b099b176..6be31452dc6 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_result_icon.mojom @@ -30,6 +30,7 @@ enum SearchResultIcon { kMagnifyingGlass, kMessages, kMouse, + kNearbyShare, kPaintbrush, kPenguin, kPhone, diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc index e3b1b3a7b24..618022b76ba 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.cc @@ -4,9 +4,11 @@ #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" +#include <algorithm> +#include <sstream> + #include "base/feature_list.h" #include "base/strings/string_number_conversions.h" -#include "chrome/browser/chromeos/local_search_service/index.h" #include "chrome/browser/chromeos/local_search_service/local_search_service.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" #include "chromeos/constants/chromeos_features.h" @@ -16,36 +18,85 @@ namespace chromeos { namespace settings { namespace { -std::vector<local_search_service::Data> ConceptVectorToDataVector( - const std::vector<SearchConcept>& search_tags) { - std::vector<local_search_service::Data> data_list; - - for (const auto& concept : search_tags) { - std::vector<base::string16> search_tags; - - // Add the canonical tag. - search_tags.push_back( - l10n_util::GetStringUTF16(concept.canonical_message_id)); - - // Add all alternate tags. - for (size_t i = 0; i < SearchConcept::kMaxAltTagsPerConcept; ++i) { - int curr_alt_tag = concept.alt_tag_ids[i]; - if (curr_alt_tag == SearchConcept::kAltTagEnd) - break; - search_tags.push_back(l10n_util::GetStringUTF16(curr_alt_tag)); - } +std::vector<int> GetMessageIds(const SearchConcept* concept) { + // Start with only the canonical ID. + std::vector<int> alt_tag_message_ids{concept->canonical_message_id}; - // Note: A stringified version of the canonical tag message ID is used as - // the identifier for this search data. - data_list.emplace_back(base::NumberToString(concept.canonical_message_id), - search_tags); + // Add alternate IDs, if they exist. + for (size_t i = 0; i < SearchConcept::kMaxAltTagsPerConcept; ++i) { + int curr_alt_tag_message_id = concept->alt_tag_ids[i]; + if (curr_alt_tag_message_id == SearchConcept::kAltTagEnd) + break; + alt_tag_message_ids.push_back(curr_alt_tag_message_id); } - return data_list; + return alt_tag_message_ids; } } // namespace +SearchTagRegistry::ScopedTagUpdater::ScopedTagUpdater( + SearchTagRegistry* registry) + : registry_(registry) {} + +SearchTagRegistry::ScopedTagUpdater::ScopedTagUpdater(ScopedTagUpdater&&) = + default; + +SearchTagRegistry::ScopedTagUpdater::~ScopedTagUpdater() { + std::vector<const SearchConcept*> pending_adds; + std::vector<const SearchConcept*> pending_removals; + + for (const auto& map_entry : pending_updates_) { + const std::string& result_id = map_entry.first; + const SearchConcept* concept = map_entry.second.first; + bool is_pending_add = map_entry.second.second; + + // If tag metadata is present for this tag, it has already been added and is + // present in LocalSearchService. + bool is_concept_already_added = + registry_->GetTagMetadata(result_id) != nullptr; + + // Only add concepts which are intended to be added and have not yet been + // added; only remove concepts which are intended to be removed and have + // already been added. + if (is_pending_add && !is_concept_already_added) + pending_adds.push_back(concept); + if (!is_pending_add && is_concept_already_added) + pending_removals.push_back(concept); + } + + if (!pending_adds.empty()) + registry_->AddSearchTags(pending_adds); + if (!pending_removals.empty()) + registry_->RemoveSearchTags(pending_removals); +} + +void SearchTagRegistry::ScopedTagUpdater::AddSearchTags( + const std::vector<SearchConcept>& search_tags) { + ProcessPendingSearchTags(search_tags, /*is_pending_add=*/true); +} + +void SearchTagRegistry::ScopedTagUpdater::RemoveSearchTags( + const std::vector<SearchConcept>& search_tags) { + ProcessPendingSearchTags(search_tags, /*is_pending_add=*/false); +} + +void SearchTagRegistry::ScopedTagUpdater::ProcessPendingSearchTags( + const std::vector<SearchConcept>& search_tags, + bool is_pending_add) { + for (const auto& concept : search_tags) { + std::string result_id = ToResultId(concept); + auto it = pending_updates_.find(result_id); + if (it == pending_updates_.end()) { + pending_updates_.emplace(std::piecewise_construct, + std::forward_as_tuple(result_id), + std::forward_as_tuple(&concept, is_pending_add)); + } else { + it->second.second = is_pending_add; + } + } +} + SearchTagRegistry::SearchTagRegistry( local_search_service::LocalSearchService* local_search_service) : index_(local_search_service->GetIndex( @@ -53,8 +104,20 @@ SearchTagRegistry::SearchTagRegistry( SearchTagRegistry::~SearchTagRegistry() = default; +void SearchTagRegistry::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void SearchTagRegistry::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +SearchTagRegistry::ScopedTagUpdater SearchTagRegistry::StartUpdate() { + return ScopedTagUpdater(this); +} + void SearchTagRegistry::AddSearchTags( - const std::vector<SearchConcept>& search_tags) { + const std::vector<const SearchConcept*>& search_tags) { if (!base::FeatureList::IsEnabled(features::kNewOsSettingsSearch)) return; @@ -63,31 +126,82 @@ void SearchTagRegistry::AddSearchTags( // Add each concept to the map. Note that it is safe to take the address of // each concept because all concepts are allocated via static // base::NoDestructor objects in the Get*SearchConcepts() helper functions. - for (const auto& concept : search_tags) - canonical_id_to_metadata_map_[concept.canonical_message_id] = &concept; + for (const auto* concept : search_tags) + result_id_to_metadata_list_map_[ToResultId(*concept)] = concept; + + NotifyRegistryUpdated(); } void SearchTagRegistry::RemoveSearchTags( - const std::vector<SearchConcept>& search_tags) { + const std::vector<const SearchConcept*>& search_tags) { if (!base::FeatureList::IsEnabled(features::kNewOsSettingsSearch)) return; - std::vector<std::string> ids; - for (const auto& concept : search_tags) { - canonical_id_to_metadata_map_.erase(concept.canonical_message_id); - ids.push_back(base::NumberToString(concept.canonical_message_id)); + std::vector<std::string> data_ids; + for (const auto* concept : search_tags) { + std::string result_id = ToResultId(*concept); + result_id_to_metadata_list_map_.erase(result_id); + data_ids.push_back(std::move(result_id)); } - index_->Delete(ids); + index_->Delete(data_ids); + + NotifyRegistryUpdated(); } -const SearchConcept* SearchTagRegistry::GetCanonicalTagMetadata( - int canonical_message_id) const { - const auto it = canonical_id_to_metadata_map_.find(canonical_message_id); - if (it == canonical_id_to_metadata_map_.end()) +const SearchConcept* SearchTagRegistry::GetTagMetadata( + const std::string& result_id) const { + const auto it = result_id_to_metadata_list_map_.find(result_id); + if (it == result_id_to_metadata_list_map_.end()) return nullptr; return it->second; } +// static +std::string SearchTagRegistry::ToResultId(const SearchConcept& concept) { + std::stringstream ss; + switch (concept.type) { + case mojom::SearchResultType::kSection: + ss << concept.id.section; + break; + case mojom::SearchResultType::kSubpage: + ss << concept.id.subpage; + break; + case mojom::SearchResultType::kSetting: + ss << concept.id.setting; + break; + } + ss << "," << concept.canonical_message_id; + return ss.str(); +} + +std::vector<local_search_service::Data> +SearchTagRegistry::ConceptVectorToDataVector( + const std::vector<const SearchConcept*>& search_tags) { + std::vector<local_search_service::Data> data_list; + + for (const auto* concept : search_tags) { + // Create a list of Content objects, which use the stringified version of + // message IDs as identifiers. + std::vector<local_search_service::Content> content_list; + for (int message_id : GetMessageIds(concept)) { + content_list.emplace_back( + /*id=*/base::NumberToString(message_id), + /*content=*/l10n_util::GetStringUTF16(message_id)); + } + + // Compute an identifier for this result; the same ID format it used in + // GetTagMetadata(). + data_list.emplace_back(ToResultId(*concept), std::move(content_list)); + } + + return data_list; +} + +void SearchTagRegistry::NotifyRegistryUpdated() { + for (auto& observer : observer_list_) + observer.OnRegistryUpdated(); +} + } // namespace settings } // namespace chromeos diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h index c1c7a947f87..abd1f83b1fa 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h @@ -6,8 +6,13 @@ #define CHROME_BROWSER_UI_WEBUI_SETTINGS_CHROMEOS_SEARCH_SEARCH_TAG_REGISTRY_H_ #include <unordered_map> +#include <utility> #include <vector> +#include "base/gtest_prod_util.h" +#include "base/observer_list.h" +#include "base/observer_list_types.h" +#include "chrome/browser/chromeos/local_search_service/index.h" #include "chrome/browser/ui/webui/settings/chromeos/os_settings_section.h" namespace local_search_service { @@ -21,27 +26,82 @@ namespace settings { struct SearchConcept; // Processes all registered search tags by adding/removing them from -// LocalSearchService and providing metadata via GetCanonicalTagMetadata(). +// LocalSearchService and providing metadata via GetTagMetadata(). class SearchTagRegistry { public: + class Observer : public base::CheckedObserver { + public: + ~Observer() override = default; + virtual void OnRegistryUpdated() = 0; + }; + + class ScopedTagUpdater { + public: + ScopedTagUpdater(ScopedTagUpdater&&); + ScopedTagUpdater(const ScopedTagUpdater&) = delete; + ScopedTagUpdater& operator=(const ScopedTagUpdater&) = delete; + ~ScopedTagUpdater(); + + void AddSearchTags(const std::vector<SearchConcept>& search_tags); + void RemoveSearchTags(const std::vector<SearchConcept>& search_tags); + + private: + friend class SearchTagRegistry; + + explicit ScopedTagUpdater(SearchTagRegistry* registry); + + void ProcessPendingSearchTags(const std::vector<SearchConcept>& search_tags, + bool is_pending_add); + + SearchTagRegistry* registry_; + + // A SearchConcept along with a bool of the pending update state. If the + // bool is true, the concept should be added; if the bool is false, the + // concept should be removed. + using ConceptWithShouldAddBool = std::pair<const SearchConcept*, bool>; + std::unordered_map<std::string, ConceptWithShouldAddBool> pending_updates_; + }; + SearchTagRegistry( local_search_service::LocalSearchService* local_search_service); SearchTagRegistry(const SearchTagRegistry& other) = delete; SearchTagRegistry& operator=(const SearchTagRegistry& other) = delete; virtual ~SearchTagRegistry(); - void AddSearchTags(const std::vector<SearchConcept>& search_tags); - void RemoveSearchTags(const std::vector<SearchConcept>& search_tags); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); - // Returns the tag metadata associated with |canonical_message_id|, which must - // be one of the canonical IDS_OS_SETTINGS_TAG_* identifiers used for a search - // tag. If no metadata is available or if |canonical_message_id| instead - // refers to an alternate tag's ID, null is returned. - const SearchConcept* GetCanonicalTagMetadata(int canonical_message_id) const; + // Starts a tag update, which allows clients to add/remove search tags. The + // ScopedTagUpdater object is returned by value and only updates tags when it + // goes out of scope, so clients should not hold onto it outside the scope of + // a function. + ScopedTagUpdater StartUpdate(); + // Returns the tag metadata associated with |result_id|, which is the ID + // returned by the LocalSearchService. If no metadata is available, null is + // returned. + const SearchConcept* GetTagMetadata(const std::string& result_id) const; private: + FRIEND_TEST_ALL_PREFIXES(SearchTagRegistryTest, AddAndRemove); + + static std::string ToResultId(const SearchConcept& concept); + + void AddSearchTags(const std::vector<const SearchConcept*>& search_tags); + void RemoveSearchTags(const std::vector<const SearchConcept*>& search_tags); + + std::vector<local_search_service::Data> ConceptVectorToDataVector( + const std::vector<const SearchConcept*>& search_tags); + void NotifyRegistryUpdated(); + + // Index used by the LocalSearchService for string matching. local_search_service::Index* index_; - std::unordered_map<int, const SearchConcept*> canonical_id_to_metadata_map_; + + // In-memory cache of all results which have been added to the + // LocalSearchService. Contents are kept in sync with |index_|. + std::unordered_map<std::string, const SearchConcept*> + result_id_to_metadata_list_map_; + + base::ObserverList<Observer> observer_list_; }; } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc index 6eff74b2583..1aebe00ce35 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry_unittest.cc @@ -5,19 +5,31 @@ #include "chrome/browser/ui/webui/settings/chromeos/search/search_tag_registry.h" #include "base/no_destructor.h" -#include "base/test/scoped_feature_list.h" #include "chrome/browser/chromeos/local_search_service/index.h" #include "chrome/browser/chromeos/local_search_service/local_search_service.h" #include "chrome/browser/ui/webui/settings/chromeos/constants/routes.mojom.h" #include "chrome/browser/ui/webui/settings/chromeos/search/search_concept.h" #include "chrome/grit/generated_resources.h" -#include "chromeos/constants/chromeos_features.h" #include "testing/gtest/include/gtest/gtest.h" namespace chromeos { namespace settings { namespace { +class FakeObserver : public SearchTagRegistry::Observer { + public: + FakeObserver() = default; + ~FakeObserver() override = default; + + size_t num_calls() const { return num_calls_; } + + private: + // SearchTagRegistry::Observer: + void OnRegistryUpdated() override { ++num_calls_; } + + size_t num_calls_ = 0; +}; + // Note: Copied from printing_section.cc but does not need to stay in sync with // it. const std::vector<SearchConcept>& GetPrintingSearchConcepts() { @@ -27,10 +39,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { mojom::SearchResultIcon::kPrinter, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAddPrinter}, - {IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT1, - IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER_ALT2, - SearchConcept::kAltTagEnd}}, + {.setting = mojom::Setting::kAddPrinter}}, {IDS_OS_SETTINGS_TAG_PRINTING_SAVED_PRINTERS, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, @@ -40,7 +49,7 @@ const std::vector<SearchConcept>& GetPrintingSearchConcepts() { {IDS_OS_SETTINGS_TAG_PRINTING, mojom::kPrintingDetailsSubpagePath, mojom::SearchResultIcon::kPrinter, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kPrintingDetails}, {IDS_OS_SETTINGS_TAG_PRINTING_ALT1, IDS_OS_SETTINGS_TAG_PRINTING_ALT2, @@ -58,37 +67,61 @@ class SearchTagRegistryTest : public testing::Test { // testing::Test: void SetUp() override { - scoped_feature_list_.InitAndEnableFeature( - chromeos::features::kNewOsSettingsSearch); + search_tag_registry_.AddObserver(&observer_); index_ = local_search_service_.GetIndex( local_search_service::IndexId::kCrosSettings); } - base::test::ScopedFeatureList scoped_feature_list_; + void TearDown() override { search_tag_registry_.RemoveObserver(&observer_); } + local_search_service::LocalSearchService local_search_service_; SearchTagRegistry search_tag_registry_; + FakeObserver observer_; local_search_service::Index* index_; }; TEST_F(SearchTagRegistryTest, AddAndRemove) { // Add search tags; size of the index should increase. - search_tag_registry_.AddSearchTags(GetPrintingSearchConcepts()); + { + SearchTagRegistry::ScopedTagUpdater updater = + search_tag_registry_.StartUpdate(); + updater.AddSearchTags(GetPrintingSearchConcepts()); + + // Nothing should have happened yet, since |updater| has not gone out of + // scope. + EXPECT_EQ(0u, index_->GetSize()); + EXPECT_EQ(0u, observer_.num_calls()); + } + // Now that it went out of scope, the update should have occurred. EXPECT_EQ(3u, index_->GetSize()); + EXPECT_EQ(1u, observer_.num_calls()); - // Tags added should be available via GetCanonicalTagMetadata(). + std::string first_tag_id = + SearchTagRegistry::ToResultId(GetPrintingSearchConcepts()[0]); + + // Tags added should be available via GetTagMetadata(). const SearchConcept* add_printer_concept = - search_tag_registry_.GetCanonicalTagMetadata( - IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER); + search_tag_registry_.GetTagMetadata(first_tag_id); ASSERT_TRUE(add_printer_concept); EXPECT_EQ(mojom::Setting::kAddPrinter, add_printer_concept->id.setting); // Remove search tag; size should go back to 0. - search_tag_registry_.RemoveSearchTags(GetPrintingSearchConcepts()); + { + SearchTagRegistry::ScopedTagUpdater updater = + search_tag_registry_.StartUpdate(); + updater.RemoveSearchTags(GetPrintingSearchConcepts()); + + // Tags should not have been removed yet, since |updater| has not gone out + // of scope. + EXPECT_EQ(3u, index_->GetSize()); + EXPECT_EQ(1u, observer_.num_calls()); + } + // Now that it went out of scope, the update should have occurred. EXPECT_EQ(0u, index_->GetSize()); + EXPECT_EQ(2u, observer_.num_calls()); - // The tag should no longer be accessible via GetCanonicalTagMetadata(). - add_printer_concept = search_tag_registry_.GetCanonicalTagMetadata( - IDS_OS_SETTINGS_TAG_PRINTING_ADD_PRINTER); + // The tag should no longer be accessible via GetTagMetadata(). + add_printer_concept = search_tag_registry_.GetTagMetadata(first_tag_id); ASSERT_FALSE(add_printer_concept); } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.cc b/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.cc index 958071d8716..6f8763df333 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.cc +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.cc @@ -9,6 +9,7 @@ #include "ash/public/cpp/assistant/assistant_state.h" #include "base/no_destructor.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "chrome/browser/chromeos/assistant/assistant_util.h" #include "chrome/browser/profiles/profile.h" @@ -19,10 +20,14 @@ #include "chrome/browser/ui/webui/webui_util.h" #include "chrome/common/url_constants.h" #include "chrome/grit/generated_resources.h" +#include "chromeos/components/quick_answers/quick_answers_client.h" #include "chromeos/constants/chromeos_features.h" #include "chromeos/services/assistant/public/cpp/assistant_prefs.h" #include "chromeos/services/assistant/public/cpp/features.h" +#include "components/language/core/browser/pref_names.h" +#include "components/prefs/pref_service.h" #include "content/public/browser/web_ui_data_source.h" +#include "third_party/icu/source/common/unicode/locid.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/webui/web_ui_util.h" #include "ui/chromeos/devicetype_utils.h" @@ -48,60 +53,45 @@ const std::vector<SearchConcept>& GetAssistantSearchConcepts() { {IDS_OS_SETTINGS_TAG_ASSISTANT, mojom::kAssistantSubpagePath, mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kHigh, + mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSubpage, {.subpage = mojom::Subpage::kAssistant}}, - {IDS_OS_SETTINGS_TAG_ASSISTANT_QUICK_ANSWERS, + }); + return *tags; +} + +const std::vector<SearchConcept>& GetAssistantOnSearchConcepts() { + static const base::NoDestructor<std::vector<SearchConcept>> tags({ + {IDS_OS_SETTINGS_TAG_ASSISTANT_TURN_OFF, mojom::kAssistantSubpagePath, mojom::SearchResultIcon::kAssistant, mojom::SearchResultDefaultRank::kMedium, mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAssistantQuickAnswers}}, + {.setting = mojom::Setting::kAssistantOnOff}, + {IDS_OS_SETTINGS_TAG_ASSISTANT_TURN_OFF_ALT1, + SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_ASSISTANT_PREFERRED_INPUT, mojom::kAssistantSubpagePath, mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kAssistantVoiceInput}}, - {IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE, - mojom::kAssistantSubpagePath, - mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAssistantOkGoogle}, - {IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE_ALT1, - IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE_ALT2, - SearchConcept::kAltTagEnd}}, {IDS_OS_SETTINGS_TAG_ASSISTANT_NOTIFICATIONS, mojom::kAssistantSubpagePath, mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kAssistantNotifications}}, {IDS_OS_SETTINGS_TAG_ASSISTANT_RELATED_INFO, mojom::kAssistantSubpagePath, mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kMedium, + mojom::SearchResultDefaultRank::kLow, mojom::SearchResultType::kSetting, {.setting = mojom::Setting::kAssistantRelatedInfo}}, }); return *tags; } -const std::vector<SearchConcept>& GetAssistantOnSearchConcepts() { - static const base::NoDestructor<std::vector<SearchConcept>> tags({ - {IDS_OS_SETTINGS_TAG_ASSISTANT_TURN_OFF, - mojom::kAssistantSubpagePath, - mojom::SearchResultIcon::kAssistant, - mojom::SearchResultDefaultRank::kMedium, - mojom::SearchResultType::kSetting, - {.setting = mojom::Setting::kAssistantOnOff}, - {IDS_OS_SETTINGS_TAG_ASSISTANT_TURN_OFF_ALT1, - SearchConcept::kAltTagEnd}}, - }); - return *tags; -} - const std::vector<SearchConcept>& GetAssistantOffSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ {IDS_OS_SETTINGS_TAG_ASSISTANT_TURN_ON, @@ -117,21 +107,39 @@ const std::vector<SearchConcept>& GetAssistantOffSearchConcepts() { const std::vector<SearchConcept>& GetAssistantQuickAnswersSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Assistant Quick Answers" search concepts. + {IDS_OS_SETTINGS_TAG_ASSISTANT_QUICK_ANSWERS, + mojom::kAssistantSubpagePath, + mojom::SearchResultIcon::kAssistant, + mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kAssistantQuickAnswers}}, }); return *tags; } const std::vector<SearchConcept>& GetAssistantHotwordDspSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Assistant hotword DSP" search concepts. + {IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE, + mojom::kAssistantSubpagePath, + mojom::SearchResultIcon::kAssistant, + mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kAssistantOkGoogle}, + {IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE_ALT1, + IDS_OS_SETTINGS_TAG_ASSISTANT_OK_GOOGLE_ALT2, + SearchConcept::kAltTagEnd}}, }); return *tags; } const std::vector<SearchConcept>& GetAssistantVoiceMatchSearchConcepts() { static const base::NoDestructor<std::vector<SearchConcept>> tags({ - // TODO(khorimoto): Add "Assistant hotword DSP" search concepts. + {IDS_OS_SETTINGS_TAG_ASSISTANT_TRAIN_VOICE_MODEL, + mojom::kAssistantSubpagePath, + mojom::SearchResultIcon::kAssistant, + mojom::SearchResultDefaultRank::kLow, + mojom::SearchResultType::kSetting, + {.setting = mojom::Setting::kTrainAssistantVoiceModel}}, }); return *tags; } @@ -140,10 +148,6 @@ bool IsVoiceMatchAllowed() { return !assistant::features::IsVoiceMatchDisabled(); } -bool AreQuickAnswersAllowed() { - return features::IsQuickAnswersSettingToggleEnabled(); -} - void AddGoogleAssistantStrings(content::WebUIDataSource* html_source) { static constexpr webui::LocalizedString kLocalizedStrings[] = { {"googleAssistantPageTitle", IDS_SETTINGS_GOOGLE_ASSISTANT}, @@ -186,7 +190,6 @@ void AddGoogleAssistantStrings(content::WebUIDataSource* html_source) { html_source->AddBoolean("hotwordDspAvailable", IsHotwordDspAvailable()); html_source->AddBoolean("voiceMatchDisabled", !IsVoiceMatchAllowed()); - html_source->AddBoolean("quickAnswersAvailable", AreQuickAnswersAllowed()); } } // namespace @@ -194,11 +197,12 @@ void AddGoogleAssistantStrings(content::WebUIDataSource* html_source) { SearchSection::SearchSection(Profile* profile, SearchTagRegistry* search_tag_registry) : OsSettingsSection(profile, search_tag_registry) { - registry()->AddSearchTags(GetSearchPageSearchConcepts()); + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + updater.AddSearchTags(GetSearchPageSearchConcepts()); ash::AssistantState* assistant_state = ash::AssistantState::Get(); if (IsAssistantAllowed() && assistant_state) { - registry()->AddSearchTags(GetAssistantSearchConcepts()); + updater.AddSearchTags(GetAssistantSearchConcepts()); assistant_state->AddObserver(this); UpdateAssistantSearchTags(); @@ -226,6 +230,7 @@ void SearchSection::AddLoadTimeData(content::WebUIDataSource* html_source) { const bool is_assistant_allowed = IsAssistantAllowed(); html_source->AddBoolean("isAssistantAllowed", is_assistant_allowed); + html_source->AddBoolean("quickAnswersAvailable", IsQuickAnswersAllowed()); html_source->AddLocalizedString("osSearchPageTitle", is_assistant_allowed ? IDS_SETTINGS_SEARCH_AND_ASSISTANT @@ -248,6 +253,44 @@ void SearchSection::AddHandlers(content::WebUI* web_ui) { std::make_unique<chromeos::settings::GoogleAssistantHandler>()); } +int SearchSection::GetSectionNameMessageId() const { + return IsAssistantAllowed() ? IDS_SETTINGS_SEARCH_AND_ASSISTANT + : IDS_SETTINGS_SEARCH; +} + +mojom::Section SearchSection::GetSection() const { + return mojom::Section::kSearchAndAssistant; +} + +mojom::SearchResultIcon SearchSection::GetSectionIcon() const { + return mojom::SearchResultIcon::kMagnifyingGlass; +} + +std::string SearchSection::GetSectionPath() const { + return mojom::kSearchAndAssistantSectionPath; +} + +void SearchSection::RegisterHierarchy(HierarchyGenerator* generator) const { + generator->RegisterTopLevelSetting(mojom::Setting::kPreferredSearchEngine); + + // Assistant. + generator->RegisterTopLevelSubpage( + IDS_SETTINGS_GOOGLE_ASSISTANT, mojom::Subpage::kAssistant, + mojom::SearchResultIcon::kAssistant, + mojom::SearchResultDefaultRank::kMedium, mojom::kAssistantSubpagePath); + static constexpr mojom::Setting kAssistantSettings[] = { + mojom::Setting::kAssistantOnOff, + mojom::Setting::kAssistantRelatedInfo, + mojom::Setting::kAssistantQuickAnswers, + mojom::Setting::kAssistantOkGoogle, + mojom::Setting::kAssistantNotifications, + mojom::Setting::kAssistantVoiceInput, + mojom::Setting::kTrainAssistantVoiceModel, + }; + RegisterNestedSettingBulk(mojom::Subpage::kAssistant, kAssistantSettings, + generator); +} + void SearchSection::OnAssistantConsentStatusChanged(int consent_status) { UpdateAssistantSearchTags(); } @@ -264,19 +307,40 @@ void SearchSection::OnAssistantHotwordEnabled(bool enabled) { UpdateAssistantSearchTags(); } -bool SearchSection::IsAssistantAllowed() { +bool SearchSection::IsAssistantAllowed() const { // NOTE: This will be false when the flag is disabled. return ::assistant::IsAssistantAllowedForProfile(profile()) == chromeos::assistant::AssistantAllowedState::ALLOWED; } +bool SearchSection::IsQuickAnswersAllowed() const { + if (!features::IsQuickAnswersSettingToggleEnabled()) + return false; + + const PrefService* prefs = profile()->GetPrefs(); + std::string pref_locale = + prefs->GetString(language::prefs::kApplicationLocale); + // Also accept runtime locale which maybe an approximation of user's pref + // locale. + const std::string kRuntimeLocale = icu::Locale::getDefault().getName(); + + base::ReplaceChars(pref_locale, "-", "_", &pref_locale); + if (!::chromeos::quick_answers::QuickAnswersClient:: + IsQuickAnswersAllowedForLocale(pref_locale, kRuntimeLocale)) + return false; + + return true; +} + void SearchSection::UpdateAssistantSearchTags() { + SearchTagRegistry::ScopedTagUpdater updater = registry()->StartUpdate(); + // Start without any Assistant search concepts, then add if needed below. - registry()->RemoveSearchTags(GetAssistantOnSearchConcepts()); - registry()->RemoveSearchTags(GetAssistantOffSearchConcepts()); - registry()->RemoveSearchTags(GetAssistantQuickAnswersSearchConcepts()); - registry()->RemoveSearchTags(GetAssistantHotwordDspSearchConcepts()); - registry()->RemoveSearchTags(GetAssistantVoiceMatchSearchConcepts()); + updater.RemoveSearchTags(GetAssistantOnSearchConcepts()); + updater.RemoveSearchTags(GetAssistantOffSearchConcepts()); + updater.RemoveSearchTags(GetAssistantQuickAnswersSearchConcepts()); + updater.RemoveSearchTags(GetAssistantHotwordDspSearchConcepts()); + updater.RemoveSearchTags(GetAssistantVoiceMatchSearchConcepts()); ash::AssistantState* assistant_state = ash::AssistantState::Get(); @@ -284,26 +348,26 @@ void SearchSection::UpdateAssistantSearchTags() { // off, none of the sub-features are enabled. if (!assistant_state->settings_enabled() || !assistant_state->settings_enabled().value()) { - registry()->AddSearchTags(GetAssistantOffSearchConcepts()); + updater.AddSearchTags(GetAssistantOffSearchConcepts()); return; } - registry()->AddSearchTags(GetAssistantOnSearchConcepts()); + updater.AddSearchTags(GetAssistantOnSearchConcepts()); - if (AreQuickAnswersAllowed() && assistant_state->context_enabled() && + if (IsQuickAnswersAllowed() && assistant_state->context_enabled() && assistant_state->context_enabled().value()) { - registry()->AddSearchTags(GetAssistantQuickAnswersSearchConcepts()); + updater.AddSearchTags(GetAssistantQuickAnswersSearchConcepts()); } if (IsHotwordDspAvailable()) - registry()->AddSearchTags(GetAssistantHotwordDspSearchConcepts()); + updater.AddSearchTags(GetAssistantHotwordDspSearchConcepts()); if (IsVoiceMatchAllowed() && assistant_state->hotword_enabled() && assistant_state->hotword_enabled().value() && assistant_state->consent_status() && assistant_state->consent_status().value() == assistant::prefs::ConsentStatus::kActivityControlAccepted) { - registry()->AddSearchTags(GetAssistantVoiceMatchSearchConcepts()); + updater.AddSearchTags(GetAssistantVoiceMatchSearchConcepts()); } } diff --git a/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.h b/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.h index d53c8d0e1ff..1de2bc61d26 100644 --- a/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.h +++ b/chromium/chrome/browser/ui/webui/settings/chromeos/search_section.h @@ -30,6 +30,11 @@ class SearchSection : public OsSettingsSection, // OsSettingsSection: void AddLoadTimeData(content::WebUIDataSource* html_source) override; void AddHandlers(content::WebUI* web_ui) override; + int GetSectionNameMessageId() const override; + mojom::Section GetSection() const override; + mojom::SearchResultIcon GetSectionIcon() const override; + std::string GetSectionPath() const override; + void RegisterHierarchy(HierarchyGenerator* generator) const override; // ash::AssistantStateObserver: void OnAssistantConsentStatusChanged(int consent_status) override; @@ -37,7 +42,8 @@ class SearchSection : public OsSettingsSection, void OnAssistantSettingsEnabled(bool enabled) override; void OnAssistantHotwordEnabled(bool enabled) override; - bool IsAssistantAllowed(); + bool IsAssistantAllowed() const; + bool IsQuickAnswersAllowed() const; void UpdateAssistantSearchTags(); }; diff --git a/chromium/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc b/chromium/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc index e2f4c5719c7..b3fd20ec805 100644 --- a/chromium/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc +++ b/chromium/chrome/browser/ui/webui/settings/custom_home_pages_table_model.cc @@ -45,9 +45,8 @@ struct CustomHomePagesTableModel::Entry { CustomHomePagesTableModel::CustomHomePagesTableModel(Profile* profile) : profile_(profile), - observer_(NULL), - num_outstanding_title_lookups_(0) { -} + observer_(nullptr), + num_outstanding_title_lookups_(0) {} CustomHomePagesTableModel::~CustomHomePagesTableModel() { } diff --git a/chromium/chrome/browser/ui/webui/settings/recent_site_settings_helper_unittest.cc b/chromium/chrome/browser/ui/webui/settings/recent_site_settings_helper_unittest.cc index 7984c581713..8d765b17cbe 100644 --- a/chromium/chrome/browser/ui/webui/settings/recent_site_settings_helper_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/recent_site_settings_helper_unittest.cc @@ -20,10 +20,8 @@ namespace { ContentSetting kBlocked = ContentSetting::CONTENT_SETTING_BLOCK; ContentSetting kAllowed = ContentSetting::CONTENT_SETTING_ALLOW; ContentSetting kDefault = ContentSetting::CONTENT_SETTING_DEFAULT; -site_settings::SiteSettingSource kEmbargo = - site_settings::SiteSettingSource::kEmbargo; -site_settings::SiteSettingSource kPreference = - site_settings::SiteSettingSource::kPreference; +SiteSettingSource kEmbargo = site_settings::SiteSettingSource::kEmbargo; +SiteSettingSource kPreference = site_settings::SiteSettingSource::kPreference; ContentSettingsType kNotifications = ContentSettingsType::NOTIFICATIONS; ContentSettingsType kPlugins = ContentSettingsType::PLUGINS; ContentSettingsType kPopups = ContentSettingsType::POPUPS; diff --git a/chromium/chrome/browser/ui/webui/settings/safety_check_handler.cc b/chromium/chrome/browser/ui/webui/settings/safety_check_handler.cc index fabe2039537..81877f13bcf 100644 --- a/chromium/chrome/browser/ui/webui/settings/safety_check_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/safety_check_handler.cc @@ -455,6 +455,9 @@ base::string16 SafetyCheckHandler::GetStringForPasswords( case PasswordsStatus::kError: return l10n_util::GetStringUTF16( IDS_SETTINGS_CHECK_PASSWORDS_ERROR_GENERIC); + case PasswordsStatus::kFeatureUnavailable: + return l10n_util::GetStringUTF16( + IDS_SETTINGS_SAFETY_CHECK_PASSWORDS_FEATURE_UNAVAILABLE); } } @@ -571,6 +574,7 @@ void SafetyCheckHandler::DetermineIfNoPasswordsOrSafe( void SafetyCheckHandler::OnVersionUpdaterResult(VersionUpdater::Status status, int progress, bool rollback, + bool powerwash, const std::string& version, int64_t update_size, const base::string16& message) { @@ -632,6 +636,9 @@ void SafetyCheckHandler::OnStateChanged( Done(0), Total(0)); break; case BulkLeakCheckService::State::kTokenRequestFailure: + OnPasswordsCheckResult(PasswordsStatus::kFeatureUnavailable, + Compromised(0), Done(0), Total(0)); + break; case BulkLeakCheckService::State::kHashingFailure: case BulkLeakCheckService::State::kServiceError: OnPasswordsCheckResult(PasswordsStatus::kError, Compromised(0), Done(0), diff --git a/chromium/chrome/browser/ui/webui/settings/safety_check_handler.h b/chromium/chrome/browser/ui/webui/settings/safety_check_handler.h index 0c73407ae34..8e6d9336183 100644 --- a/chromium/chrome/browser/ui/webui/settings/safety_check_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/safety_check_handler.h @@ -30,7 +30,7 @@ // software. class SafetyCheckHandler : public settings::SettingsPageUIHandler, - public password_manager::BulkLeakCheckService::Observer, + public password_manager::BulkLeakCheckServiceInterface::Observer, public safety_check::SafetyCheck::SafetyCheckHandlerInterface { public: // The following enum represent the state of the safety check parent @@ -70,8 +70,9 @@ class SafetyCheckHandler kSignedOut = 5, kQuotaLimit = 6, kError = 7, + kFeatureUnavailable = 8, // New enum values must go above here. - kMaxValue = kError, + kMaxValue = kFeatureUnavailable, }; enum class ExtensionsStatus { kChecking = 0, @@ -192,6 +193,7 @@ class SafetyCheckHandler void OnVersionUpdaterResult(VersionUpdater::Status status, int progress, bool rollback, + bool powerwash, const std::string& version, int64_t update_size, const base::string16& message); @@ -235,12 +237,12 @@ class SafetyCheckHandler std::unique_ptr<safety_check::UpdateCheckHelper> update_helper_; std::unique_ptr<VersionUpdater> version_updater_; - password_manager::BulkLeakCheckService* leak_service_ = nullptr; + password_manager::BulkLeakCheckServiceInterface* leak_service_ = nullptr; extensions::PasswordsPrivateDelegate* passwords_delegate_ = nullptr; extensions::ExtensionPrefs* extension_prefs_ = nullptr; extensions::ExtensionServiceInterface* extension_service_ = nullptr; - ScopedObserver<password_manager::BulkLeakCheckService, - password_manager::BulkLeakCheckService::Observer> + ScopedObserver<password_manager::BulkLeakCheckServiceInterface, + password_manager::BulkLeakCheckServiceInterface::Observer> observed_leak_check_{this}; base::WeakPtrFactory<SafetyCheckHandler> weak_ptr_factory_{this}; }; diff --git a/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc index c6e18dd2c36..9a790eea198 100644 --- a/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/safety_check_handler_unittest.cc @@ -793,6 +793,25 @@ TEST_F(SafetyCheckHandlerTest, CheckPasswords_Error) { SafetyCheckHandler::PasswordsStatus::kError, 1); } +TEST_F(SafetyCheckHandlerTest, CheckPasswords_FeatureUnavailable) { + safety_check_->PerformSafetyCheck(); + EXPECT_TRUE(test_passwords_delegate_.StartPasswordCheckTriggered()); + static_cast<password_manager::BulkLeakCheckService::Observer*>( + safety_check_.get()) + ->OnStateChanged( + password_manager::BulkLeakCheckService::State::kTokenRequestFailure); + const base::DictionaryValue* event = + GetSafetyCheckStatusChangedWithDataIfExists( + kPasswords, + static_cast<int>( + SafetyCheckHandler::PasswordsStatus::kFeatureUnavailable)); + ASSERT_TRUE(event); + VerifyDisplayString(event, "Password check is not available in Chromium"); + histogram_tester_.ExpectBucketCount( + "Settings.SafetyCheck.PasswordsResult", + SafetyCheckHandler::PasswordsStatus::kFeatureUnavailable, 1); +} + TEST_F(SafetyCheckHandlerTest, CheckPasswords_RunningOneCompromised) { test_passwords_delegate_.SetNumCompromisedCredentials(1); safety_check_->PerformSafetyCheck(); diff --git a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc index 8335deec5b6..831c737b3fa 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler.cc @@ -254,8 +254,8 @@ void ClearBrowsingDataHandler::HandleClearBrowsingData( if (!prefs->GetBoolean(prefs::kClearPluginLSODataEnabled)) site_data_mask &= ~ChromeBrowsingDataRemoverDelegate::DATA_TYPE_PLUGIN_DATA; - int remove_mask = 0; - int origin_mask = 0; + uint64_t remove_mask = 0; + uint64_t origin_mask = 0; std::vector<BrowsingDataType> data_type_vector; const base::ListValue* data_type_list = nullptr; CHECK(args->GetList(1, &data_type_list)); diff --git a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_browsertest.cc index 98dca3b412e..28c96790a03 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_browsertest.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_clear_browsing_data_handler_browsertest.cc @@ -7,6 +7,7 @@ #include "base/values.h" #include "chrome/browser/ui/web_applications/web_app_controller_browsertest.h" +#include "chrome/browser/web_applications/test/web_app_test.h" #include "chrome/common/webui_url_constants.h" #include "chrome/test/base/in_process_browser_test.h" #include "chrome/test/base/ui_test_utils.h" @@ -98,13 +99,10 @@ IN_PROC_BROWSER_TEST_P(ClearBrowsingDataHandlerBrowserTest, GetInstalledApps) { ASSERT_EQ(url.host(), *(installed_app.FindStringKey("registerableDomain"))); } -INSTANTIATE_TEST_SUITE_P( - All, - ClearBrowsingDataHandlerBrowserTest, - ::testing::Values( - web_app::ControllerType::kHostedAppController, - web_app::ControllerType::kUnifiedControllerWithBookmarkApp, - web_app::ControllerType::kUnifiedControllerWithWebApp), - web_app::ControllerTypeParamToString); +INSTANTIATE_TEST_SUITE_P(All, + ClearBrowsingDataHandlerBrowserTest, + ::testing::Values(web_app::ProviderType::kBookmarkApps, + web_app::ProviderType::kWebApps), + web_app::ProviderTypeParamToString); } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc index eff2694ecfd..ccb1ccca58c 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc @@ -99,8 +99,9 @@ #include "components/prefs/pref_service.h" #include "components/user_manager/user_manager.h" #include "ui/chromeos/devicetype_utils.h" -#else +#else // !defined(OS_CHROMEOS) #include "chrome/browser/ui/webui/settings/system_handler.h" +#include "ui/accessibility/accessibility_features.h" #endif #if defined(OS_WIN) @@ -156,6 +157,9 @@ void AddCommonStrings(content::WebUIDataSource* html_source, Profile* profile) { {"settings", IDS_SETTINGS_SETTINGS}, {"settingsAltPageTitle", IDS_SETTINGS_ALT_PAGE_TITLE}, {"subpageArrowRoleDescription", IDS_SETTINGS_SUBPAGE_BUTTON}, + {"subpageBackButtonAriaLabel", IDS_SETTINGS_SUBPAGE_BACK_BUTTON_ARIA_LABEL}, + {"subpageBackButtonAriaRoleDescription", + IDS_SETTINGS_SUBPAGE_BACK_BUTTON_ARIA_ROLE_DESCRIPTION}, {"notValid", IDS_SETTINGS_NOT_VALID}, {"notValidWebAddress", IDS_SETTINGS_NOT_VALID_WEB_ADDRESS}, {"notValidWebAddressForContentType", @@ -203,6 +207,9 @@ void AddA11yStrings(content::WebUIDataSource* html_source) { {"manageAccessibilityFeatures", IDS_SETTINGS_ACCESSIBILITY_MANAGE_ACCESSIBILITY_FEATURES}, {"androidAppsManageAppLinks", IDS_SETTINGS_ANDROID_APPS_MANAGE_APP_LINKS}, +#else // !defined(OS_CHROMEOS) + {"focusHighlightLabel", + IDS_SETTINGS_ACCESSIBILITY_FOCUS_HIGHLIGHT_DESCRIPTION}, #endif }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); @@ -215,6 +222,12 @@ void AddA11yStrings(content::WebUIDataSource* html_source) { "showExperimentalA11yLabels", base::FeatureList::IsEnabled(features::kExperimentalAccessibilityLabels)); +#if !defined(OS_CHROMEOS) + html_source->AddBoolean( + "showFocusHighlightOption", + base::FeatureList::IsEnabled(features::kAccessibilityFocusHighlight)); +#endif + html_source->AddBoolean("enableLiveCaption", base::FeatureList::IsEnabled(media::kLiveCaption)); @@ -460,7 +473,7 @@ void AddChromeCleanupStrings(content::WebUIDataSource* html_source) { IDS_SETTINGS_RESET_CLEANUP_EXPLANATION_CURRENTLY_REMOVING}, {"chromeCleanupExplanationScanError", IDS_SETTINGS_RESET_CLEANUP_EXPLANATION_SCAN_ERROR}, - {"chromeCleanupFindButtonLable", + {"chromeCleanupFindButtonLabel", IDS_SETTINGS_RESET_CLEANUP_FIND_BUTTON_LABEL}, {"chromeCleanupLinkShowItems", IDS_SETTINGS_RESET_CLEANUP_LINK_SHOW_FILES}, @@ -769,6 +782,7 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, static constexpr webui::LocalizedString kLocalizedStrings[] = { {"autofillPageTitle", IDS_SETTINGS_AUTOFILL}, {"passwords", IDS_SETTINGS_PASSWORDS}, + {"passwordsDevice", IDS_SETTINGS_DEVICE_PASSWORDS}, {"checkPasswords", IDS_SETTINGS_CHECK_PASSWORDS}, {"checkPasswordsCanceled", IDS_SETTINGS_CHECK_PASSWORDS_CANCELED}, {"checkedPasswords", IDS_SETTINGS_CHECKED_PASSWORDS}, @@ -817,8 +831,7 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_COMPROMISED_EDIT_PASSWORD_APP}, {"alreadyChangedPasswordLink", IDS_SETTINGS_COMPROMISED_ALREADY_CHANGED_PASSWORD}, - {"editDisclaimerTitle", - IDS_SETTINGS_COMPROMISED_EDIT_DISCLAIMER_TITLE}, + {"editDisclaimerTitle", IDS_SETTINGS_COMPROMISED_EDIT_DISCLAIMER_TITLE}, {"editDisclaimerDescription", IDS_SETTINGS_COMPROMISED_EDIT_DISCLAIMER_DESCRIPTION}, {"creditCards", IDS_AUTOFILL_PAYMENT_METHODS}, @@ -846,6 +859,8 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, {"creditCardType", IDS_SETTINGS_AUTOFILL_CREDIT_CARD_TYPE_COLUMN_LABEL}, {"creditCardExpiration", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_DATE}, {"creditCardName", IDS_SETTINGS_NAME_ON_CREDIT_CARD}, + {"creditCardNickname", IDS_SETTINGS_CREDIT_CARD_NICKNAME}, + {"creditCardNicknameInvalid", IDS_SETTINGS_CREDIT_CARD_NICKNAME_INVALID}, {"creditCardNumber", IDS_SETTINGS_CREDIT_CARD_NUMBER}, {"creditCardExpirationMonth", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_MONTH}, {"creditCardExpirationYear", IDS_SETTINGS_CREDIT_CARD_EXPIRATION_YEAR}, @@ -874,6 +889,10 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_PASSWORDS_LEAK_DETECTION_SIGNED_OUT_ENABLED_DESC}, {"savedPasswordsHeading", IDS_SETTINGS_PASSWORDS_SAVED_HEADING}, {"passwordExceptionsHeading", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_HEADING}, + {"deviceOnlyPasswordsHeading", + IDS_SETTINGS_DEVICE_PASSWORDS_ON_DEVICE_ONLY_HEADING}, + {"deviceAndAccountPasswordsHeading", + IDS_SETTINGS_DEVICE_PASSWORDS_ON_DEVICE_AND_ACCOUNT_HEADING}, {"deletePasswordException", IDS_SETTINGS_PASSWORDS_DELETE_EXCEPTION}, {"removePassword", IDS_SETTINGS_PASSWORD_REMOVE}, {"searchPasswords", IDS_SETTINGS_PASSWORD_SEARCH}, @@ -882,6 +901,10 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, {"passwordDetailsTitle", IDS_SETTINGS_PASSWORDS_VIEW_DETAILS_TITLE}, {"passwordViewDetails", IDS_SETTINGS_PASSWORD_DETAILS}, {"copyPassword", IDS_SETTINGS_PASSWORD_COPY}, + {"passwordStoredOnDevice", IDS_SETTINGS_PASSWORD_STORED_ON_DEVICE}, + {"passwordStoredInAccount", IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT}, + {"passwordStoredInAccountAndOnDevice", + IDS_SETTINGS_PASSWORD_STORED_IN_ACCOUNT_AND_ON_DEVICE}, {"editPasswordWebsiteLabel", IDS_SETTINGS_PASSWORDS_WEBSITE}, {"editPasswordUsernameLabel", IDS_SETTINGS_PASSWORDS_USERNAME}, {"editPasswordPasswordLabel", IDS_SETTINGS_PASSWORDS_PASSWORD}, @@ -895,11 +918,38 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, {"optOutAccountStorageLabel", IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_LABEL}, {"undoRemovePassword", IDS_SETTINGS_PASSWORD_UNDO}, + {"movePasswordToAccount", IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT}, {"passwordDeleted", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD}, {"passwordDeletedFromDevice", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_DEVICE}, {"passwordDeletedFromAccount", IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT}, + {"passwordDeletedFromAccountAndDevice", + IDS_SETTINGS_PASSWORD_DELETED_PASSWORD_FROM_ACCOUNT_AND_DEVICE}, + {"passwordMoveToAccountDialogTitle", + IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_TITLE}, + {"passwordMoveToAccountDialogBody", + IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_BODY}, + {"passwordMoveToAccountDialogMoveButtonText", + IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_MOVE_BUTTON_TEXT}, + {"passwordMoveToAccountDialogCancelButtonText", + IDS_SETTINGS_PASSWORD_MOVE_TO_ACCOUNT_DIALOG_CANCEL_BUTTON_TEXT}, + {"passwordRemoveDialogTitle", IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_TITLE}, + {"passwordRemoveDialogBody", IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_BODY}, + {"passwordRemoveDialogRemoveButtonText", + IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_REMOVE_BUTTON_TEXT}, + {"passwordRemoveDialogCancelButtonText", + IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_CANCEL_BUTTON_TEXT}, + {"passwordRemoveDialogFromAccountCheckboxLabel", + IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_FROM_ACCOUNT_CHECKBOX_LABEL}, + {"passwordRemoveDialogFromDeviceCheckboxLabel", + IDS_SETTINGS_PASSWORD_REMOVE_DIALOG_FROM_DEVICE_CHECKBOX_LABEL}, + {"devicePasswordsLinkLabelSingular", + IDS_SETTINGS_DEVICE_PASSWORDS_LINK_LABEL_SINGULAR}, + {"devicePasswordsLinkLabelPlural", + IDS_SETTINGS_DEVICE_PASSWORDS_LINK_LABEL_PLURAL}, + {"devicePasswordsLinkSubLabel", + IDS_SETTINGS_DEVICE_PASSWORDS_LINK_SUB_LABEL}, {"passwordRowMoreActionsButton", IDS_SETTINGS_PASSWORD_ROW_MORE_ACTIONS}, {"passwordRowFederatedMoreActionsButton", IDS_SETTINGS_PASSWORD_ROW_FEDERATED_MORE_ACTIONS}, @@ -916,6 +966,8 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TIP_ENOUGH_SPACE}, {"exportPasswordsFailTipsAnotherFolder", IDS_SETTINGS_PASSWORDS_EXPORTING_FAILURE_TIP_ANOTHER_FOLDER}, + {"managePasswordsPlaintext", + IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS_PLAINTEXT}, {"savedToThisDeviceOnly", IDS_SETTINGS_PAYMENTS_SAVED_TO_THIS_DEVICE_ONLY}}; @@ -928,6 +980,16 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_PASSWORDS_MANAGE_PASSWORDS, base::UTF8ToUTF16(google_password_manager_url.spec()))); html_source->AddString( + "optInAccountStorageBody", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_PASSWORDS_OPT_IN_ACCOUNT_STORAGE_BODY, + base::UTF8ToUTF16(google_password_manager_url.spec()))); + html_source->AddString( + "optOutAccountStorageBody", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_PASSWORDS_OPT_OUT_ACCOUNT_STORAGE_BODY, + base::UTF8ToUTF16(google_password_manager_url.spec()))); + html_source->AddString( "checkPasswordsErrorQuotaGoogleAccount", l10n_util::GetStringFUTF16( IDS_SETTINGS_CHECK_PASSWORDS_ERROR_QUOTA_LIMIT_GOOGLE_ACCOUNT, @@ -951,6 +1013,11 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, autofill::payments::GetManageInstrumentsUrl().spec()); html_source->AddString("paymentMethodsLearnMoreURL", chrome::kPaymentMethodsLearnMoreURL); + html_source->AddString( + "siteSettingsFlashWildcardsUnsupported", + l10n_util::GetStringFUTF16( + IDS_SETTINGS_SITE_SETTINGS_FLASH_WILDCARD_UNSUPPORTED, + base::UTF8ToUTF16(chrome::kChromeFlashRoadmapURL))); bool is_guest_mode = false; #if defined(OS_CHROMEOS) @@ -982,6 +1049,10 @@ void AddAutofillStrings(content::WebUIDataSource* html_source, base::FeatureList::IsEnabled( autofill::features::kAutofillSaveAndFillVPA)); + html_source->AddBoolean( + "nicknameManagementEnabled", + base::FeatureList::IsEnabled( + autofill::features::kAutofillEnableCardNicknameManagement)); AddLocalizedStringsBulk(html_source, kLocalizedStrings); } @@ -989,15 +1060,15 @@ void AddSignOutDialogStrings(content::WebUIDataSource* html_source, Profile* profile) { #if defined(OS_CHROMEOS) bool is_dice_enabled = false; - bool is_split_sync_consent_enabled = - chromeos::features::IsSplitSyncConsentEnabled(); + bool use_browser_sync_consent = + chromeos::features::ShouldUseBrowserSyncConsent(); #else bool is_dice_enabled = AccountConsistencyModeManager::IsDiceEnabledForProfile(profile); - bool is_split_sync_consent_enabled = false; + bool use_browser_sync_consent = false; #endif - if (is_split_sync_consent_enabled || is_dice_enabled) { + if (use_browser_sync_consent || is_dice_enabled) { static constexpr webui::LocalizedString kTurnOffStrings[] = { {"syncDisconnect", IDS_SETTINGS_PEOPLE_SYNC_TURN_OFF}, {"syncDisconnectTitle", @@ -1136,6 +1207,10 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source, static constexpr webui::LocalizedString kLocalizedStrings[] = { {"privacyPageTitle", IDS_SETTINGS_PRIVACY}, {"privacyPageMore", IDS_SETTINGS_PRIVACY_MORE}, + // TODO(crbug.com/1116608): This seemingly unrelated string resource is + // used because it already has a localized version available in M85, so + // we can ship this label in M85 on short notice. + {"discardedPerSiteSetting", IDS_POLICY_LABEL_IGNORED}, {"doNotTrack", IDS_SETTINGS_ENABLE_DO_NOT_TRACK}, {"doNotTrackDialogTitle", IDS_SETTINGS_ENABLE_DO_NOT_TRACK_DIALOG_TITLE}, // TODO(crbug.com/1062607): This string is no longer used. Remove. @@ -1293,6 +1368,10 @@ void AddPrivacyStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_PRIMARY_LABEL}, {"safetyCheckExtensionsButtonAriaLabel", IDS_SETTINGS_SAFETY_CHECK_EXTENSIONS_BUTTON_ARIA_LABEL}, + {"safetyCheckChromeCleanerPrimaryLabel", + IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_PRIMARY_LABEL}, + {"safetyCheckChromeCleanerButtonAriaLabel", + IDS_SETTINGS_SAFETY_CHECK_CHROME_CLEANER_BUTTON_ARIA_LABEL}, }; AddLocalizedStringsBulk(html_source, kLocalizedStrings); @@ -1401,8 +1480,6 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source, Profile* profile) { static constexpr webui::LocalizedString kLocalizedStrings[] = { {"addSite", IDS_SETTINGS_ADD_SITE}, - {"addSiteExceptionPlaceholder", - IDS_SETTINGS_ADD_SITE_EXCEPTION_PLACEHOLDER}, {"addSiteTitle", IDS_SETTINGS_ADD_SITE_TITLE}, #if defined(OS_CHROMEOS) {"androidSmsNote", IDS_SETTINGS_ANDROID_SMS_NOTE}, @@ -1710,6 +1787,7 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source, {"siteSettingsSourcePolicyAsk", IDS_PAGE_INFO_PERMISSION_ASK_BY_POLICY}, {"siteSettingsAdsBlockNotBlacklistedSingular", IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_NOT_BLACKLISTED_SINGULAR}, + {"siteSettingsAllowlisted", IDS_SETTINGS_SITE_SETTINGS_ALLOWLISTED}, {"siteSettingsAdsBlockBlacklistedSingular", IDS_SETTINGS_SITE_SETTINGS_ADS_BLOCK_BLACKLISTED_SINGULAR}, {"siteSettingsSourceDrmDisabled", @@ -1853,6 +1931,8 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source, IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT}, {"siteSettingsWindowPlacementAsk", IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK}, + {"siteSettingsWindowPlacementAskRecommended", + IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_ASK_RECOMMENDED}, {"siteSettingsWindowPlacementBlock", IDS_SETTINGS_SITE_SETTINGS_WINDOW_PLACEMENT_BLOCK}, }; @@ -1928,9 +2008,8 @@ void AddSiteSettingsStrings(content::WebUIDataSource* html_source, base::FeatureList::IsEnabled( features::kWebBluetoothNewPermissionsBackend)); - html_source->AddBoolean( - "enableWebXrContentSetting", - base::FeatureList::IsEnabled(features::kWebXrPermissionsApi)); + // The exception placeholder should not be translated. See crbug.com/1095878. + html_source->AddString("addSiteExceptionPlaceholder", "[*.]example.com"); } #if !defined(OS_CHROMEOS) diff --git a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc index 83923229636..a06c62efb33 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_manage_profile_handler.cc @@ -11,7 +11,6 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" -#include "base/value_conversions.h" #include "base/values.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/gaia_info_update_service.h" diff --git a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc index b0818ee920f..85887e9be9a 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.cc @@ -8,7 +8,6 @@ #include <utility> #include "base/bind.h" -#include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/net/secure_dns_config.h" @@ -24,10 +23,12 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" #include "net/dns/public/dns_over_https_server_config.h" -#include "net/dns/public/doh_provider_list.h" +#include "net/dns/public/doh_provider_entry.h" #include "net/dns/public/util.h" #include "ui/base/l10n/l10n_util.h" +namespace secure_dns = chrome_browser_net::secure_dns; + namespace settings { namespace { @@ -57,11 +58,7 @@ std::unique_ptr<base::DictionaryValue> CreateSecureDnsSettingDict() { } // namespace -SecureDnsHandler::SecureDnsHandler() - : network_context_getter_( - base::BindRepeating(&SecureDnsHandler::GetNetworkContext, - base::Unretained(this))) {} - +SecureDnsHandler::SecureDnsHandler() = default; SecureDnsHandler::~SecureDnsHandler() = default; void SecureDnsHandler::RegisterMessages() { @@ -111,38 +108,14 @@ void SecureDnsHandler::OnJavascriptDisallowed() { pref_registrar_.RemoveAll(); } -base::Value SecureDnsHandler::GetSecureDnsResolverListForCountry( - int country_id, - const std::vector<net::DohProviderEntry>& providers) { - std::vector<std::string> disabled_providers = - SplitString(features::kDnsOverHttpsDisabledProvidersParam.Get(), ",", - base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); - +base::Value SecureDnsHandler::GetSecureDnsResolverList() { base::Value resolvers(base::Value::Type::LIST); - resolver_histogram_map_.clear(); - // Add all non-disabled resolvers that should be displayed in |country_id|. - for (const auto& entry : providers) { - if (base::Contains(disabled_providers, entry.provider)) - continue; - - if (entry.display_globally || - std::find_if( - entry.display_countries.begin(), entry.display_countries.end(), - [&country_id](const std::string& country_code) { - return country_codes::CountryCharsToCountryID( - country_code[0], country_code[1]) == country_id; - }) != entry.display_countries.end()) { - DCHECK(!entry.ui_name.empty()); - DCHECK(!entry.privacy_policy.empty()); - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetKey("name", base::Value(entry.ui_name)); - dict.SetKey("value", base::Value(entry.dns_over_https_template)); - dict.SetKey("policy", base::Value(entry.privacy_policy)); - resolvers.Append(std::move(dict)); - DCHECK(entry.provider_id_for_histogram.has_value()); - resolver_histogram_map_.insert({entry.dns_over_https_template, - entry.provider_id_for_histogram.value()}); - } + for (const auto* entry : providers_) { + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetStringKey("name", entry->ui_name); + dict.SetStringKey("value", entry->dns_over_https_template); + dict.SetStringKey("policy", entry->privacy_policy); + resolvers.Append(std::move(dict)); } // Randomize the order of the resolvers. @@ -150,13 +123,10 @@ base::Value SecureDnsHandler::GetSecureDnsResolverListForCountry( // Add a custom option to the front of the list base::Value custom(base::Value::Type::DICTIONARY); - custom.SetKey("name", - base::Value(l10n_util::GetStringUTF8(IDS_SETTINGS_CUSTOM))); - custom.SetKey("value", base::Value("custom")); - custom.SetKey("policy", base::Value(std::string())); + custom.SetStringKey("name", l10n_util::GetStringUTF8(IDS_SETTINGS_CUSTOM)); + custom.SetStringKey("value", std::string()); // Empty value means custom. + custom.SetStringKey("policy", std::string()); resolvers.Insert(resolvers.GetList().begin(), std::move(custom)); - resolver_histogram_map_.insert( - {"custom", net::DohProviderIdForHistogram::kCustom}); return resolvers; } @@ -176,15 +146,18 @@ network::mojom::NetworkContext* SecureDnsHandler::GetNetworkContext() { ->GetNetworkContext(); } +void SecureDnsHandler::SetProvidersForTesting( + net::DohProviderEntry::List providers) { + providers_ = std::move(providers); +} + void SecureDnsHandler::HandleGetSecureDnsResolverList( const base::ListValue* args) { AllowJavascript(); std::string callback_id = args->GetList()[0].GetString(); - ResolveJavascriptCallback( - base::Value(callback_id), - GetSecureDnsResolverListForCountry(country_codes::GetCurrentCountryID(), - net::GetDohProviderList())); + ResolveJavascriptCallback(base::Value(callback_id), + GetSecureDnsResolverList()); } void SecureDnsHandler::HandleGetSecureDnsSetting(const base::ListValue* args) { @@ -203,14 +176,12 @@ void SecureDnsHandler::HandleParseCustomDnsEntry(const base::ListValue* args) { // Return all templates in the entry, or none if they are not all valid. base::Value templates(base::Value::Type::LIST); - if (chrome_browser_net::secure_dns::IsValidGroup(custom_entry)) { - for (base::StringPiece t : - chrome_browser_net::secure_dns::SplitGroup(custom_entry)) { + if (secure_dns::IsValidGroup(custom_entry)) { + for (base::StringPiece t : secure_dns::SplitGroup(custom_entry)) { templates.Append(t); } } - UMA_HISTOGRAM_BOOLEAN("Net.DNS.UI.ValidationAttemptSuccess", - !templates.GetList().empty()); + secure_dns::UpdateValidationHistogram(!templates.GetList().empty()); ResolveJavascriptCallback(*callback_id, templates); } @@ -236,7 +207,7 @@ void SecureDnsHandler::HandleProbeCustomDnsTemplate( overrides.attempts = 1; overrides.randomize_ports = false; overrides.secure_dns_mode = net::DnsConfig::SecureDnsMode::SECURE; - chrome_browser_net::secure_dns::ApplyTemplate(&overrides, server_template); + secure_dns::ApplyTemplate(&overrides, server_template); DCHECK(!runner_); runner_ = std::make_unique<chrome_browser_net::DnsProbeRunner>( overrides, network_context_getter_); @@ -251,29 +222,15 @@ void SecureDnsHandler::HandleRecordUserDropdownInteraction( std::string new_provider; CHECK(args->GetString(0, &old_provider)); CHECK(args->GetString(1, &new_provider)); - DCHECK(resolver_histogram_map_.find(old_provider) != - resolver_histogram_map_.end()); - DCHECK(resolver_histogram_map_.find(new_provider) != - resolver_histogram_map_.end()); - for (auto& pair : resolver_histogram_map_) { - if (pair.first == old_provider) { - UMA_HISTOGRAM_ENUMERATION("Net.DNS.UI.DropdownSelectionEvent.Unselected", - pair.second); - } else if (pair.first == new_provider) { - UMA_HISTOGRAM_ENUMERATION("Net.DNS.UI.DropdownSelectionEvent.Selected", - pair.second); - } else { - UMA_HISTOGRAM_ENUMERATION("Net.DNS.UI.DropdownSelectionEvent.Ignored", - pair.second); - } - } + + secure_dns::UpdateDropdownHistograms(providers_, old_provider, new_provider); } void SecureDnsHandler::OnProbeComplete() { bool success = runner_->result() == chrome_browser_net::DnsProbeRunner::CORRECT; runner_.reset(); - UMA_HISTOGRAM_BOOLEAN("Net.DNS.UI.ProbeAttemptSuccess", success); + secure_dns::UpdateProbeHistogram(success); ResolveJavascriptCallback(base::Value(probe_callback_id_), base::Value(success)); probe_callback_id_.clear(); @@ -284,4 +241,12 @@ void SecureDnsHandler::SendSecureDnsSettingUpdatesToJavascript() { *CreateSecureDnsSettingDict()); } +// static +net::DohProviderEntry::List SecureDnsHandler::GetFilteredProviders() { + const auto local_providers = secure_dns::ProvidersForCountry( + net::DohProviderEntry::GetList(), country_codes::GetCurrentCountryID()); + return secure_dns::RemoveDisabledProviders( + local_providers, secure_dns::GetDisabledProviders()); +} + } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.h b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.h index 9f7622ae20d..dddaea2127a 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler.h @@ -9,12 +9,13 @@ #include <string> #include <vector> +#include "base/bind.h" #include "base/macros.h" #include "base/values.h" #include "chrome/browser/net/dns_probe_runner.h" #include "chrome/browser/ui/webui/settings/settings_page_ui_handler.h" #include "components/prefs/pref_change_registrar.h" -#include "net/dns/public/doh_provider_list.h" +#include "net/dns/public/doh_provider_entry.h" #include "services/network/public/cpp/resolve_host_client_base.h" #include "services/network/public/mojom/network_context.mojom.h" @@ -35,13 +36,15 @@ class SecureDnsHandler : public SettingsPageUIHandler { // as a dictionary with the following keys: "name" (the text to display in the // UI), "value" (the DoH template for this provider), and "policy" (the URL of // the provider's privacy policy). - base::Value GetSecureDnsResolverListForCountry( - int country_id, - const std::vector<net::DohProviderEntry>& providers); + base::Value GetSecureDnsResolverList(); void SetNetworkContextForTesting( network::mojom::NetworkContext* network_context); + // DohProviderEntry cannot be copied, so the entries referenced in |providers| + // must be long-lived. + void SetProvidersForTesting(net::DohProviderEntry::List providers); + protected: // Retrieves all pre-approved secure resolvers and returns them to WebUI. void HandleGetSecureDnsResolverList(const base::ListValue* args); @@ -66,10 +69,14 @@ class SecureDnsHandler : public SettingsPageUIHandler { network::mojom::NetworkContext* GetNetworkContext(); void OnProbeComplete(); - std::map<std::string, net::DohProviderIdForHistogram> resolver_histogram_map_; + static net::DohProviderEntry::List GetFilteredProviders(); + + net::DohProviderEntry::List providers_ = GetFilteredProviders(); std::unique_ptr<chrome_browser_net::DnsProbeRunner> runner_; chrome_browser_net::DnsProbeRunner::NetworkContextGetter - network_context_getter_; + network_context_getter_ = + base::BindRepeating(&SecureDnsHandler::GetNetworkContext, + base::Unretained(this)); // ID of the Javascript callback for the current pending probe, or "" if // there is no probe currently in progress. std::string probe_callback_id_; diff --git a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc index 3ece9212741..81aa1024fc1 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_secure_dns_handler_browsertest.cc @@ -27,7 +27,6 @@ #include "base/win/win_util.h" #endif -using net::DohProviderEntry; using testing::_; using testing::IsEmpty; using testing::Return; @@ -43,47 +42,38 @@ constexpr char kRecordUserDropdownInteraction[] = "recordUserDropdownInteraction"; constexpr char kWebUiFunctionName[] = "webUiCallbackName"; -const std::vector<DohProviderEntry>& GetDohProviderListForTesting() { - static const base::NoDestructor<std::vector<DohProviderEntry>> test_providers{ - { - DohProviderEntry( - "Provider_Global1", net::DohProviderIdForHistogram(-1), - {} /*ip_strs */, {} /* dot_hostnames */, - "https://global1.provider/dns-query{?dns}", - "Global Provider 1" /* ui_name */, - "https://global1.provider/privacy_policy/" /* privacy_policy */, - true /* display_globally */, {} /* display_countries */), - DohProviderEntry( - "Provider_NoDisplay", net::DohProviderIdForHistogram(-2), - {} /*ip_strs */, {} /* dot_hostnames */, - "https://nodisplay.provider/dns-query{?dns}", - "No Display Provider" /* ui_name */, - "https://nodisplay.provider/privacy_policy/" /* privacy_policy */, - false /* display_globally */, {} /* display_countries */), - DohProviderEntry( - "Provider_EE_FR", net::DohProviderIdForHistogram(-3), - {} /*ip_strs */, {} /* dot_hostnames */, - "https://ee.fr.provider/dns-query{?dns}", - "EE/FR Provider" /* ui_name */, - "https://ee.fr.provider/privacy_policy/" /* privacy_policy */, - false /* display_globally */, - {"EE", "FR"} /* display_countries */), - DohProviderEntry( - "Provider_FR", net::DohProviderIdForHistogram(-4), - {} /*ip_strs */, {} /* dot_hostnames */, - "https://fr.provider/dns-query{?dns}", - "FR Provider" /* ui_name */, - "https://fr.provider/privacy_policy/" /* privacy_policy */, - false /* display_globally */, {"FR"} /* display_countries */), - DohProviderEntry( - "Provider_Global2", net::DohProviderIdForHistogram(-5), - {} /*ip_strs */, {} /* dot_hostnames */, - "https://global2.provider/dns-query{?dns}", - "Global Provider 2" /* ui_name */, - "https://global2.provider/privacy_policy/" /* privacy_policy */, - true /* display_globally */, {} /* display_countries */), - }}; - return *test_providers; +net::DohProviderEntry::List GetDohProviderListForTesting() { + static const auto global1 = net::DohProviderEntry::ConstructForTesting( + "Provider_Global1", net::DohProviderIdForHistogram(-1), {} /*ip_strs */, + {} /* dot_hostnames */, "https://global1.provider/dns-query{?dns}", + "Global Provider 1" /* ui_name */, + "https://global1.provider/privacy_policy/" /* privacy_policy */, + true /* display_globally */, {} /* display_countries */); + static const auto no_display = net::DohProviderEntry::ConstructForTesting( + "Provider_NoDisplay", net::DohProviderIdForHistogram(-2), {} /*ip_strs */, + {} /* dot_hostnames */, "https://nodisplay.provider/dns-query{?dns}", + "No Display Provider" /* ui_name */, + "https://nodisplay.provider/privacy_policy/" /* privacy_policy */, + false /* display_globally */, {} /* display_countries */); + static const auto ee_fr = net::DohProviderEntry::ConstructForTesting( + "Provider_EE_FR", net::DohProviderIdForHistogram(-3), {} /*ip_strs */, + {} /* dot_hostnames */, "https://ee.fr.provider/dns-query{?dns}", + "EE/FR Provider" /* ui_name */, + "https://ee.fr.provider/privacy_policy/" /* privacy_policy */, + false /* display_globally */, {"EE", "FR"} /* display_countries */); + static const auto fr = net::DohProviderEntry::ConstructForTesting( + "Provider_FR", net::DohProviderIdForHistogram(-4), {} /*ip_strs */, + {} /* dot_hostnames */, "https://fr.provider/dns-query{?dns}", + "FR Provider" /* ui_name */, + "https://fr.provider/privacy_policy/" /* privacy_policy */, + false /* display_globally */, {"FR"} /* display_countries */); + static const auto global2 = net::DohProviderEntry::ConstructForTesting( + "Provider_Global2", net::DohProviderIdForHistogram(-5), {} /*ip_strs */, + {} /* dot_hostnames */, "https://global2.provider/dns-query{?dns}", + "Global Provider 2" /* ui_name */, + "https://global2.provider/privacy_policy/" /* privacy_policy */, + true /* display_globally */, {} /* display_countries */); + return {&global1, &no_display, &ee_fr, &fr, &global2}; } bool FindDropdownItem(const base::Value& resolvers, @@ -315,116 +305,40 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownList) { // Check results. base::Value::ConstListView resolver_list = call_data.arg3()->GetList(); ASSERT_GE(resolver_list.size(), 1U); - EXPECT_EQ("custom", resolver_list[0].FindKey("value")->GetString()); + EXPECT_TRUE(resolver_list[0].FindKey("value")->GetString().empty()); } -IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownListForCountry) { - // The 'EE' list should start with the custom entry, followed by the two - // global providers and the 'EE' provider in some random order. - base::Value resolver_list = handler_->GetSecureDnsResolverListForCountry( - country_codes::CountryCharsToCountryID('E', 'E'), - GetDohProviderListForTesting()); - EXPECT_EQ(4u, resolver_list.GetList().size()); - EXPECT_EQ("custom", resolver_list.GetList()[0].FindKey("value")->GetString()); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 1", - "https://global1.provider/dns-query{?dns}", - "https://global1.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 2", - "https://global2.provider/dns-query{?dns}", - "https://global2.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "EE/FR Provider", - "https://ee.fr.provider/dns-query{?dns}", - "https://ee.fr.provider/privacy_policy/")); - - // The 'FR' list should start with the custom entry, followed by the two - // global providers and the two 'FR' providers in some random order. - resolver_list = handler_->GetSecureDnsResolverListForCountry( - country_codes::CountryCharsToCountryID('F', 'R'), - GetDohProviderListForTesting()); - EXPECT_EQ(5u, resolver_list.GetList().size()); - EXPECT_EQ("custom", resolver_list.GetList()[0].FindKey("value")->GetString()); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 1", - "https://global1.provider/dns-query{?dns}", - "https://global1.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 2", - "https://global2.provider/dns-query{?dns}", - "https://global2.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "EE/FR Provider", - "https://ee.fr.provider/dns-query{?dns}", - "https://ee.fr.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "FR Provider", - "https://fr.provider/dns-query{?dns}", - "https://fr.provider/privacy_policy/")); - - // The 'CA' list should start with the custom entry, followed by the two - // global providers. - resolver_list = handler_->GetSecureDnsResolverListForCountry( - country_codes::CountryCharsToCountryID('C', 'A'), - GetDohProviderListForTesting()); - EXPECT_EQ(3u, resolver_list.GetList().size()); - EXPECT_EQ("custom", resolver_list.GetList()[0].FindKey("value")->GetString()); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 1", - "https://global1.provider/dns-query{?dns}", - "https://global1.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 2", - "https://global2.provider/dns-query{?dns}", - "https://global2.provider/privacy_policy/")); +IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownListContents) { + const auto entries = GetDohProviderListForTesting(); + handler_->SetProvidersForTesting(entries); + const base::Value resolver_list = handler_->GetSecureDnsResolverList(); + + EXPECT_EQ(entries.size() + 1, resolver_list.GetList().size()); + EXPECT_TRUE(resolver_list.GetList()[0].FindKey("value")->GetString().empty()); + for (const auto* entry : entries) { + EXPECT_TRUE(FindDropdownItem(resolver_list, entry->ui_name, + entry->dns_over_https_template, + entry->privacy_policy)); + } } IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, DropdownListChange) { - // Populate the map for recording dropdown change metrics. - base::Value resolver_list = handler_->GetSecureDnsResolverListForCountry( - country_codes::CountryCharsToCountryID('E', 'E'), - GetDohProviderListForTesting()); - EXPECT_EQ(4u, resolver_list.GetList().size()); + handler_->SetProvidersForTesting(GetDohProviderListForTesting()); base::HistogramTester histograms; base::ListValue args; - args.AppendString("custom" /* old_provider */); + args.AppendString(std::string() /* old_provider */); args.AppendString( "https://global1.provider/dns-query{?dns}" /* new_provider */); web_ui_.HandleReceivedMessage(kRecordUserDropdownInteraction, &args); - const std::string uma_base("Net.DNS.UI.DropdownSelectionEvent"); - histograms.ExpectTotalCount(uma_base + ".Ignored", 2u); - histograms.ExpectTotalCount(uma_base + ".Selected", 1u); - histograms.ExpectTotalCount(uma_base + ".Unselected", 1u); + const std::string kUmaBase = "Net.DNS.UI.DropdownSelectionEvent"; + histograms.ExpectTotalCount(kUmaBase + ".Ignored", 4u); + histograms.ExpectTotalCount(kUmaBase + ".Selected", 1u); + histograms.ExpectTotalCount(kUmaBase + ".Unselected", 1u); } -class SecureDnsHandlerTestWithDisabledProviders : public SecureDnsHandlerTest { - protected: - SecureDnsHandlerTestWithDisabledProviders() { - scoped_features_.InitAndEnableFeatureWithParameters( - features::kDnsOverHttps, - {{"DisabledProviders", - "Provider_Global2, , Provider_EE_FR,Unexpected"}}); - } - - private: - base::test::ScopedFeatureList scoped_features_; - - DISALLOW_COPY_AND_ASSIGN(SecureDnsHandlerTestWithDisabledProviders); -}; - -IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTestWithDisabledProviders, - DropdownListDisabledProviders) { - // The 'FR' list should start with the custom entry, followed by the two - // global providers and the two 'FR' providers in some random order. - base::Value resolver_list = handler_->GetSecureDnsResolverListForCountry( - country_codes::CountryCharsToCountryID('F', 'R'), - GetDohProviderListForTesting()); - EXPECT_EQ(3u, resolver_list.GetList().size()); - EXPECT_EQ("custom", resolver_list.GetList()[0].FindKey("value")->GetString()); - EXPECT_TRUE(FindDropdownItem(resolver_list, "Global Provider 1", - "https://global1.provider/dns-query{?dns}", - "https://global1.provider/privacy_policy/")); - EXPECT_TRUE(FindDropdownItem(resolver_list, "FR Provider", - "https://fr.provider/dns-query{?dns}", - "https://fr.provider/privacy_policy/")); -} - -IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTestWithDisabledProviders, - SecureDnsTemplates) { +IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, SecureDnsTemplates) { std::string good_post_template = "https://foo.test/"; std::string good_get_template = "https://bar.test/dns-query{?dns}"; std::string bad_template = "dns-query{?dns}"; @@ -433,6 +347,8 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTestWithDisabledProviders, std::vector<std::string> secure_dns_templates; int management_mode; PrefService* local_state = g_browser_process->local_state(); + local_state->SetString(prefs::kDnsOverHttpsMode, + SecureDnsConfig::kModeAutomatic); local_state->SetString(prefs::kDnsOverHttpsTemplates, good_post_template); EXPECT_TRUE(GetLastSettingsChangedMessage( &secure_dns_mode, &secure_dns_templates, &management_mode)); @@ -458,15 +374,6 @@ IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTestWithDisabledProviders, &secure_dns_mode, &secure_dns_templates, &management_mode)); EXPECT_EQ(1u, secure_dns_templates.size()); EXPECT_EQ(good_post_template, secure_dns_templates[0]); - - // Should still return a provider that was disabled. - local_state->SetString(prefs::kDnsOverHttpsTemplates, - "https://global2.provider/dns-query{?dns}"); - EXPECT_TRUE(GetLastSettingsChangedMessage( - &secure_dns_mode, &secure_dns_templates, &management_mode)); - EXPECT_EQ(1u, secure_dns_templates.size()); - EXPECT_EQ("https://global2.provider/dns-query{?dns}", - secure_dns_templates[0]); } IN_PROC_BROWSER_TEST_F(SecureDnsHandlerTest, TemplateValid) { diff --git a/chromium/chrome/browser/ui/webui/settings/settings_ui.cc b/chromium/chrome/browser/ui/webui/settings/settings_ui.cc index 0462890255d..9e29823de46 100644 --- a/chromium/chrome/browser/ui/webui/settings/settings_ui.cc +++ b/chromium/chrome/browser/ui/webui/settings/settings_ui.cc @@ -285,11 +285,17 @@ SettingsUI::SettingsUI(content::WebUI* web_ui) "syncSetupFriendlySettings", base::FeatureList::IsEnabled(features::kSyncSetupFriendlySettings)); +#if defined(OS_WIN) + html_source->AddBoolean( + "safetyCheckChromeCleanerChildEnabled", + base::FeatureList::IsEnabled(features::kSafetyCheckChromeCleanerChild)); +#endif + #if defined(OS_CHROMEOS) html_source->AddBoolean("splitSettingsSyncEnabled", chromeos::features::IsSplitSettingsSyncEnabled()); - html_source->AddBoolean("splitSyncConsent", - chromeos::features::IsSplitSyncConsentEnabled()); + html_source->AddBoolean("useBrowserSyncConsent", + chromeos::features::ShouldUseBrowserSyncConsent()); html_source->AddBoolean( "userCannotManuallyEnterPassword", diff --git a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc index 20abed8007e..ae20e5716f0 100644 --- a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc +++ b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.cc @@ -8,6 +8,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/system/sys_info.h" #include "build/build_config.h" +#include "chrome/browser/browser_features.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process_platform_part.h" #include "chrome/browser/chromeos/profiles/profile_helper.h" @@ -242,4 +243,15 @@ void AddSyncPageStrings(content::WebUIDataSource* html_source) { } } +void AddNearbyShareData(content::WebUIDataSource* html_source) { + static constexpr webui::LocalizedString kLocalizedStrings[] = { + {"nearbyShareTitle", IDS_SETTINGS_NEARBY_SHARE_TITLE}, + }; + AddLocalizedStringsBulk(html_source, kLocalizedStrings); + + html_source->AddBoolean( + "nearbySharingFeatureFlag", + base::FeatureList::IsEnabled(features::kNearbySharing)); +} + } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h index 33dc5c453d4..73333c7fef5 100644 --- a/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h +++ b/chromium/chrome/browser/ui/webui/settings/shared_settings_localized_strings_provider.h @@ -31,6 +31,9 @@ void AddPasswordPromptDialogStrings(content::WebUIDataSource* html_source); // Adds strings used by the <settings-sync-page> element. void AddSyncPageStrings(content::WebUIDataSource* html_source); +// Adds load time data used by the <settings-nearby-share-subpage>. +void AddNearbyShareData(content::WebUIDataSource* html_source); + } // namespace settings #endif // CHROME_BROWSER_UI_WEBUI_SETTINGS_SHARED_SETTINGS_LOCALIZED_STRINGS_PROVIDER_H_ diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc index e195fcbd936..bcbafdd8e31 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.cc @@ -95,14 +95,6 @@ constexpr char kPlaceholder[] = "placeholder"; // These values are persisted to logs. Entries should not be renumbered and // numeric values should never be reused. -enum class AllSitesAction { - kLoadPage = 0, - kResetPermissions = 1, - kClearData = 2, - kEnterSiteDetails = 3, - kMaxValue = kEnterSiteDetails, -}; - enum class AllSitesAction2 { kLoadPage = 0, kResetSiteGroupPermissions = 1, @@ -418,11 +410,6 @@ void SiteSettingsHandler::RegisterMessages() { base::BindRepeating(&SiteSettingsHandler::HandleGetAllSites, base::Unretained(this))); web_ui()->RegisterMessageCallback( - "getCookieControlsManagedState", - base::BindRepeating( - &SiteSettingsHandler::HandleGetCookieControlsManagedState, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( "getCookieSettingDescription", base::BindRepeating( &SiteSettingsHandler::HandleGetCookieSettingDescription, @@ -817,34 +804,6 @@ void SiteSettingsHandler::HandleGetAllSites(const base::ListValue* args) { ResolveJavascriptCallback(base::Value(callback_id), result); } -void SiteSettingsHandler::HandleGetCookieControlsManagedState( - const base::ListValue* args) { - AllowJavascript(); - CHECK_EQ(1U, args->GetList().size()); - std::string callback_id = args->GetList()[0].GetString(); - - auto managed_states = site_settings::GetCookieControlsManagedState(profile_); - - base::Value result(base::Value::Type::DICTIONARY); - result.SetKey( - site_settings::kAllowAll, - site_settings::GetValueForManagedState(managed_states.allow_all)); - result.SetKey(site_settings::kBlockThirdPartyIncognito, - site_settings::GetValueForManagedState( - managed_states.block_third_party_incognito)); - result.SetKey( - site_settings::kBlockThirdParty, - site_settings::GetValueForManagedState(managed_states.block_third_party)); - result.SetKey( - site_settings::kBlockAll, - site_settings::GetValueForManagedState(managed_states.block_all)); - result.SetKey( - site_settings::kSessionOnly, - site_settings::GetValueForManagedState(managed_states.session_only)); - - ResolveJavascriptCallback(base::Value(callback_id), result); -} - void SiteSettingsHandler::HandleGetCookieSettingDescription( const base::ListValue* args) { AllowJavascript(); diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h index 0b34a324727..90c8106441b 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler.h @@ -128,11 +128,13 @@ class SiteSettingsHandler FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ZoomLevels); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleClearEtldPlus1DataAndCookies); - FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, CookieControlsManagedState); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, CookieSettingDescription); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, HandleGetFormattedBytes); FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, NotificationPermissionRevokeUkm); + FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, ExcludeWebUISchemesInLists); + FRIEND_TEST_ALL_PREFIXES(SiteSettingsHandlerTest, + IncludeWebUISchemesInGetOriginPermissions); // Creates the CookiesTreeModel if necessary. void EnsureCookiesTreeModelCreated(); @@ -171,10 +173,6 @@ class SiteSettingsHandler // the front end when fetching finished. void HandleGetAllSites(const base::ListValue* args); - // Returns whether each of the cookie controls is managed and if so what - // the source of that management is. - void HandleGetCookieControlsManagedState(const base::ListValue* args); - // Returns a string for display describing the current cookie settings. void HandleGetCookieSettingDescription(const base::ListValue* args); diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc index ee654373208..95b114fb5dc 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_handler_unittest.cc @@ -45,8 +45,6 @@ #include "components/content_settings/core/common/content_settings_types.h" #include "components/content_settings/core/common/features.h" #include "components/content_settings/core/common/pref_names.h" -#include "components/content_settings/core/test/content_settings_mock_provider.h" -#include "components/content_settings/core/test/content_settings_test_utils.h" #include "components/history/core/browser/history_service.h" #include "components/infobars/core/infobar.h" #include "components/permissions/chooser_context_base.h" @@ -67,6 +65,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/text/bytes_formatting.h" +#include "ui/webui/webui_allowlist.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/users/mock_user_manager.h" @@ -424,9 +423,10 @@ class SiteSettingsHandlerTest : public testing::Test { } virtual void DestroyIncognitoProfile() { - profile_->SetOffTheRecordProfile(nullptr); - ASSERT_FALSE(profile_->HasOffTheRecordProfile()); - incognito_profile_ = nullptr; + if (incognito_profile_) { + profile_->DestroyOffTheRecordProfile(incognito_profile_); + incognito_profile_ = nullptr; + } } // TODO(https://crbug.com/835712): Currently only set up the cookies and local @@ -1877,6 +1877,133 @@ TEST_F(SiteSettingsHandlerTest, BlockAutoplay_Update) { EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kBlockAutoplayEnabled)); } +TEST_F(SiteSettingsHandlerTest, ExcludeWebUISchemesInLists) { + const ContentSettingsType content_settings_type = + ContentSettingsType::NOTIFICATIONS; + // Register WebUIAllowlist auto-granted permissions. + const url::Origin kWebUIOrigins[] = { + url::Origin::Create(GURL("chrome://test")), + url::Origin::Create(GURL("chrome-untrusted://test")), + url::Origin::Create(GURL("devtools://devtools")), + }; + + WebUIAllowlist* allowlist = WebUIAllowlist::GetOrCreate(profile()); + for (const url::Origin& origin : kWebUIOrigins) + allowlist->RegisterAutoGrantedPermission(origin, content_settings_type); + + // Verify the auto-granted permissions are registered, and they are indeed + // provided by WebUIAllowlist. + HostContentSettingsMap* map = + HostContentSettingsMapFactory::GetForProfile(profile()); + content_settings::SettingInfo info; + std::unique_ptr<base::Value> value = map->GetWebsiteSetting( + kWebUIOrigins[0].GetURL(), kWebUIOrigins[0].GetURL(), + content_settings_type, std::string(), &info); + EXPECT_EQ(CONTENT_SETTING_ALLOW, value->GetInt()); + EXPECT_EQ(content_settings::SETTING_SOURCE_ALLOWLIST, info.source); + + // Register an ordinary website permission. + const GURL kWebUrl = GURL("https://example.com"); + map->SetContentSettingDefaultScope(kWebUrl, kWebUrl, content_settings_type, + std::string(), CONTENT_SETTING_ALLOW); + EXPECT_EQ(CONTENT_SETTING_ALLOW, + map->GetContentSetting(kWebUrl, kWebUrl, content_settings_type, + std::string())); + + // GetAllSites() only returns website exceptions. + { + base::ListValue get_all_sites_args; + get_all_sites_args.AppendString(kCallbackId); + base::Value category_list(base::Value::Type::LIST); + category_list.Append(kNotifications); + get_all_sites_args.Append(std::move(category_list)); + + handler()->HandleGetAllSites(&get_all_sites_args); + + const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); + base::Value::ConstListView site_groups = data.arg3()->GetList(); + EXPECT_EQ(1UL, site_groups.size()); + + const std::string etld_plus1_string = + site_groups[0].FindKey("etldPlus1")->GetString(); + EXPECT_EQ("example.com", etld_plus1_string); + base::Value::ConstListView origin_list = + site_groups[0].FindKey("origins")->GetList(); + EXPECT_EQ(1UL, origin_list.size()); + EXPECT_EQ(kWebUrl.spec(), origin_list[0].FindKey("origin")->GetString()); + } + + // GetExceptionList() only returns website exceptions. + { + base::ListValue get_exception_list_args; + get_exception_list_args.AppendString(kCallbackId); + get_exception_list_args.AppendString(kNotifications); + + handler()->HandleGetExceptionList(&get_exception_list_args); + + const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); + base::Value::ConstListView exception_list = data.arg3()->GetList(); + EXPECT_EQ(1UL, exception_list.size()); + EXPECT_EQ("https://example.com:443", + exception_list[0].FindKey("origin")->GetString()); + } + + // GetRecentSitePermissions() only returns website exceptions. + { + base::ListValue get_recent_permissions_args; + get_recent_permissions_args.AppendString(kCallbackId); + base::Value category_list(base::Value::Type::LIST); + category_list.Append(kNotifications); + get_recent_permissions_args.Append(std::move(category_list)); + get_recent_permissions_args.Append(3); + + handler()->HandleGetRecentSitePermissions(&get_recent_permissions_args); + + const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); + base::Value::ConstListView recent_permission_list = data.arg3()->GetList(); + EXPECT_EQ(1UL, recent_permission_list.size()); + EXPECT_EQ(kWebUrl.spec(), + recent_permission_list[0].FindKey("origin")->GetString()); + } +} + +// GetOriginPermissions() returns the allowlisted exception. We explicitly +// return this, so developers can easily test things (e.g. by navigating to +// chrome://settings/content/siteDetails?site=chrome://example). +TEST_F(SiteSettingsHandlerTest, IncludeWebUISchemesInGetOriginPermissions) { + const ContentSettingsType content_settings_type = + ContentSettingsType::NOTIFICATIONS; + + // Register WebUIAllowlist auto-granted permissions. + const url::Origin kWebUIOrigins[] = { + url::Origin::Create(GURL("chrome://test")), + url::Origin::Create(GURL("chrome-untrusted://test")), + url::Origin::Create(GURL("devtools://devtools")), + }; + + WebUIAllowlist* allowlist = WebUIAllowlist::GetOrCreate(profile()); + for (const url::Origin& origin : kWebUIOrigins) + allowlist->RegisterAutoGrantedPermission(origin, content_settings_type); + + for (const url::Origin& origin : kWebUIOrigins) { + base::ListValue get_origin_permissions_args; + get_origin_permissions_args.AppendString(kCallbackId); + get_origin_permissions_args.AppendString(origin.GetURL().spec()); + auto category_list = std::make_unique<base::ListValue>(); + category_list->AppendString(kNotifications); + get_origin_permissions_args.Append(std::move(category_list)); + + handler()->HandleGetOriginPermissions(&get_origin_permissions_args); + const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); + const base::Value::ConstListView exception_list = data.arg3()->GetList(); + EXPECT_EQ(1UL, exception_list.size()); + + EXPECT_EQ(origin.GetURL().spec(), + exception_list[0].FindKey("origin")->GetString()); + EXPECT_EQ("allowlist", exception_list[0].FindKey("source")->GetString()); + } +} + namespace { constexpr char kUsbPolicySetting[] = R"( @@ -1907,6 +2034,9 @@ GURL ChromiumUrl() { GURL GoogleUrl() { return GURL("https://google.com"); } +GURL WebUIUrl() { + return GURL("chrome://test"); +} } // namespace @@ -1954,6 +2084,7 @@ class SiteSettingsHandlerChooserExceptionTest : public SiteSettingsHandlerTest { const auto kAndroidOrigin = url::Origin::Create(AndroidUrl()); const auto kChromiumOrigin = url::Origin::Create(ChromiumUrl()); const auto kGoogleOrigin = url::Origin::Create(GoogleUrl()); + const auto kWebUIOrigin = url::Origin::Create(WebUIUrl()); // Add the user granted permissions for testing. // These two persistent device permissions should be lumped together with @@ -1964,6 +2095,8 @@ class SiteSettingsHandlerChooserExceptionTest : public SiteSettingsHandlerTest { *persistent_device_info_); chooser_context->GrantDevicePermission(kAndroidOrigin, kChromiumOrigin, *persistent_device_info_); + chooser_context->GrantDevicePermission(kWebUIOrigin, kWebUIOrigin, + *persistent_device_info_); chooser_context->GrantDevicePermission(kAndroidOrigin, kAndroidOrigin, *ephemeral_device_info_); chooser_context->GrantDevicePermission(kAndroidOrigin, kAndroidOrigin, @@ -2118,6 +2251,11 @@ TEST_F(SiteSettingsHandlerChooserExceptionTest, const base::Value& exceptions = GetChooserExceptionListFromWebUiCallData( kUsbChooserGroupName, /*expected_total_calls=*/1u); EXPECT_EQ(exceptions.GetList().size(), 5u); + + // Don't include WebUI schemes. + const std::string kWebUIOriginStr = WebUIUrl().GetOrigin().spec(); + EXPECT_FALSE(ChooserExceptionContainsSiteException( + exceptions, "Gizmo", kWebUIOriginStr, kWebUIOriginStr)); } TEST_F(SiteSettingsHandlerChooserExceptionTest, @@ -2338,61 +2476,6 @@ TEST_F(SiteSettingsHandlerTest, HandleClearEtldPlus1DataAndCookies) { EXPECT_EQ(0U, storage_and_cookie_list->GetSize()); } -TEST_F(SiteSettingsHandlerTest, CookieControlsManagedState) { - // Test that the handler correctly wraps the helper result. Helper with - // extensive logic is tested in site_settings_helper_unittest.cc. - const std::string kNone = "none"; - const std::string kDevicePolicy = "devicePolicy"; - const std::vector<std::string> kControlNames = { - "allowAll", "blockAll", "blockThirdParty", "blockThirdPartyIncognito", - "sessionOnly"}; - - // Check that the default cookie control state is handled correctly. - base::ListValue get_args; - get_args.AppendString(kCallbackId); - handler()->HandleGetCookieControlsManagedState(&get_args); - { - const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); - EXPECT_EQ("cr.webUIResponse", data.function_name()); - EXPECT_EQ(kCallbackId, data.arg1()->GetString()); - ASSERT_TRUE(data.arg2()->GetBool()); - for (const auto& control_name : kControlNames) { - auto* control_state = data.arg3()->FindPath(control_name); - ASSERT_FALSE(control_state->FindKey("disabled")->GetBool()); - ASSERT_EQ(kNone, control_state->FindKey("indicator")->GetString()); - } - } - - // Check that a fully managed cookie state is handled correctly. - HostContentSettingsMap* map = - HostContentSettingsMapFactory::GetForProfile(profile()); - auto provider = std::make_unique<content_settings::MockProvider>(); - provider->SetWebsiteSetting( - ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), - ContentSettingsType::COOKIES, std::string(), - std::make_unique<base::Value>(CONTENT_SETTING_ALLOW)); - content_settings::TestUtils::OverrideProvider( - map, std::move(provider), HostContentSettingsMap::POLICY_PROVIDER); - sync_preferences::TestingPrefServiceSyncable* pref_service = - profile()->GetTestingPrefService(); - pref_service->SetManagedPref(prefs::kBlockThirdPartyCookies, - std::make_unique<base::Value>(true)); - - handler()->HandleGetCookieControlsManagedState(&get_args); - { - const content::TestWebUI::CallData& data = *web_ui()->call_data().back(); - EXPECT_EQ("cr.webUIResponse", data.function_name()); - EXPECT_EQ(kCallbackId, data.arg1()->GetString()); - ASSERT_TRUE(data.arg2()->GetBool()); - for (const auto& control_name : kControlNames) { - auto* control_state = data.arg3()->FindPath(control_name); - ASSERT_TRUE(control_state->FindKey("disabled")->GetBool()); - ASSERT_EQ(kDevicePolicy, - control_state->FindKey("indicator")->GetString()); - } - } -} - TEST_F(SiteSettingsHandlerTest, CookieSettingDescription) { const auto kBlocked = [](int num) { return l10n_util::GetPluralStringFUTF8( diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc index 5dfef5ee030..28addc32434 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.cc @@ -10,8 +10,11 @@ #include <string> #include "base/feature_list.h" +#include "base/no_destructor.h" #include "base/stl_util.h" +#include "base/strings/strcat.h" #include "base/strings/string16.h" +#include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/values.h" #include "chrome/browser/bluetooth/bluetooth_chooser_context.h" @@ -26,12 +29,10 @@ #include "chrome/browser/usb/usb_chooser_context.h" #include "chrome/browser/usb/usb_chooser_context_factory.h" #include "chrome/common/pref_names.h" -#include "components/content_settings/core/browser/cookie_settings.h" #include "components/content_settings/core/browser/host_content_settings_map.h" #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings_pattern.h" #include "components/content_settings/core/common/content_settings_utils.h" -#include "components/content_settings/core/common/pref_names.h" #include "components/permissions/chooser_context_base.h" #include "components/permissions/permission_decision_auto_blocker.h" #include "components/permissions/permission_manager.h" @@ -42,9 +43,11 @@ #include "components/subresource_filter/core/browser/subresource_filter_features.h" #include "components/url_formatter/url_formatter.h" #include "content/public/common/content_features.h" +#include "content/public/common/url_utils.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/constants.h" #include "url/origin.h" +#include "url/url_constants.h" namespace site_settings { @@ -158,6 +161,7 @@ struct SiteSettingSourceStringMapping { }; const SiteSettingSourceStringMapping kSiteSettingSourceStringMapping[] = { + {SiteSettingSource::kAllowlist, "allowlist"}, {SiteSettingSource::kAdsFilterBlacklist, "ads-filter-blacklist"}, {SiteSettingSource::kDefault, "default"}, {SiteSettingSource::kDrmDisabled, "drm-disabled"}, @@ -195,38 +199,49 @@ static_assert(base::size(kPolicyIndicatorTypeStringMapping) == "kPolicyIndicatorStringMapping should have " "PolicyIndicatorType::kNumIndicators elements"); +const std::string& GetDevtoolsPatternPrefix() { + static const base::NoDestructor<std::string> kDevtoolsPatternPrefix( + base::StrCat({content_settings::kChromeDevToolsScheme, + url::kStandardSchemeSeparator})); + return *kDevtoolsPatternPrefix; +} + // Retrieves the corresponding string, according to the following precedence // order from highest to lowest priority: -// 1. Kill-switch. -// 2. Insecure origins (some permissions are denied to insecure origins). -// 3. Enterprise policy. -// 4. Extensions. -// 5. Activated for ads filtering (for Ads ContentSettingsType only). -// 6. DRM disabled (for CrOS's Protected Content ContentSettingsType only). -// 7. User-set per-origin setting. -// 8. Embargo. -// 9. User-set patterns. -// 10. User-set global default for a ContentSettingsType. -// 11. Chrome's built-in default. +// 1. Allowlisted WebUI content setting. +// 2. Kill-switch. +// 3. Insecure origins (some permissions are denied to insecure origins). +// 4. Enterprise policy. +// 5. Extensions. +// 6. Activated for ads filtering (for Ads ContentSettingsType only). +// 7. DRM disabled (for CrOS's Protected Content ContentSettingsType only). +// 8. User-set per-origin setting. +// 9. Embargo. +// 10. User-set patterns. +// 11. User-set global default for a ContentSettingsType. +// 12. Chrome's built-in default. SiteSettingSource CalculateSiteSettingSource( Profile* profile, const ContentSettingsType content_type, const GURL& origin, const content_settings::SettingInfo& info, const permissions::PermissionResult result) { + if (info.source == content_settings::SETTING_SOURCE_ALLOWLIST) + return SiteSettingSource::kAllowlist; // Source #1. + if (result.source == permissions::PermissionStatusSource::KILL_SWITCH) - return SiteSettingSource::kKillSwitch; // Source #1. + return SiteSettingSource::kKillSwitch; // Source #2. if (result.source == permissions::PermissionStatusSource::INSECURE_ORIGIN) - return SiteSettingSource::kInsecureOrigin; // Source #2. + return SiteSettingSource::kInsecureOrigin; // Source #3. if (info.source == content_settings::SETTING_SOURCE_POLICY || info.source == content_settings::SETTING_SOURCE_SUPERVISED) { - return SiteSettingSource::kPolicy; // Source #3. + return SiteSettingSource::kPolicy; // Source #4. } if (info.source == content_settings::SETTING_SOURCE_EXTENSION) - return SiteSettingSource::kExtension; // Source #4. + return SiteSettingSource::kExtension; // Source #5. if (content_type == ContentSettingsType::ADS && base::FeatureList::IsEnabled( @@ -236,14 +251,14 @@ SiteSettingSource CalculateSiteSettingSource( if (map->GetWebsiteSetting(origin, GURL(), ContentSettingsType::ADS_DATA, /*resource_identifier=*/std::string(), /*setting_info=*/nullptr) != nullptr) { - return SiteSettingSource::kAdsFilterBlacklist; // Source #5. + return SiteSettingSource::kAdsFilterBlacklist; // Source #6. } } // Protected Content will be blocked if the |kEnableDRM| pref is off. if (content_type == ContentSettingsType::PROTECTED_MEDIA_IDENTIFIER && !profile->GetPrefs()->GetBoolean(prefs::kEnableDRM)) { - return SiteSettingSource::kDrmDisabled; // Source #6. + return SiteSettingSource::kDrmDisabled; // Source #7. } DCHECK_NE(content_settings::SETTING_SOURCE_NONE, info.source); @@ -252,18 +267,18 @@ SiteSettingSource CalculateSiteSettingSource( permissions::PermissionStatusSource::MULTIPLE_DISMISSALS || result.source == permissions::PermissionStatusSource::MULTIPLE_IGNORES) { - return SiteSettingSource::kEmbargo; // Source #8. + return SiteSettingSource::kEmbargo; // Source #9. } if (info.primary_pattern == ContentSettingsPattern::Wildcard() && info.secondary_pattern == ContentSettingsPattern::Wildcard()) { - return SiteSettingSource::kDefault; // Source #10, #11. + return SiteSettingSource::kDefault; // Source #11, #12. } - // Source #7, #9. When #7 is the source, |result.source| won't - // be set to any of the source #7 enum values, as PermissionManager is - // aware of the difference between these two sources internally. The - // subtlety here should go away when PermissionManager can handle all - // content settings and all possible sources. + // Source #8, #10. When #8 is the source, |result.source| won't be set to + // any of the source #8 enum values, as PermissionManager is aware of the + // difference between these two sources internally. The subtlety here should + // go away when PermissionManager can handle all content settings and all + // possible sources. return SiteSettingSource::kPreference; } @@ -286,6 +301,16 @@ bool PatternAppliesToSingleOrigin(const ContentSettingPatternSource& pattern) { return true; } +bool PatternAppliesToWebUISchemes(const ContentSettingPatternSource& pattern) { + return pattern.primary_pattern.GetScheme() == + ContentSettingsPattern::SchemeType::SCHEME_CHROME || + pattern.primary_pattern.GetScheme() == + ContentSettingsPattern::SchemeType::SCHEME_CHROMEUNTRUSTED || + base::StartsWith(pattern.primary_pattern.ToString(), + GetDevtoolsPatternPrefix(), + base::CompareCase::INSENSITIVE_ASCII); +} + // Retrieves the source of a chooser exception as a string. This method uses the // CalculateSiteSettingSource method above to calculate the correct string to // use. @@ -344,31 +369,12 @@ const ChooserTypeNameEntry kChooserTypeGroupNames[] = { {&GetHidChooserContext, kHidChooserDataGroupType}, {&GetBluetoothChooserContext, kBluetoothChooserDataGroupType}}; -PolicyIndicatorType GetPolicyIndicatorFromSettingSource( - content_settings::SettingSource setting_source) { - switch (setting_source) { - case content_settings::SETTING_SOURCE_POLICY: - return PolicyIndicatorType::kDevicePolicy; - case content_settings::SETTING_SOURCE_SUPERVISED: - return PolicyIndicatorType::kParent; - case content_settings::SETTING_SOURCE_EXTENSION: - return PolicyIndicatorType::kExtension; - case content_settings::SETTING_SOURCE_USER: - case content_settings::SETTING_SOURCE_ALLOWLIST: - case content_settings::SETTING_SOURCE_NONE: - return PolicyIndicatorType::kNone; - case content_settings::SETTING_SOURCE_INSTALLED_WEBAPP: - NOTREACHED(); - return PolicyIndicatorType::kNone; - } -} - } // namespace bool HasRegisteredGroupName(ContentSettingsType type) { for (size_t i = 0; i < base::size(kContentSettingsTypeGroupNames); ++i) { if (type == kContentSettingsTypeGroupNames[i].type && - kContentSettingsTypeGroupNames[i].name != nullptr) { + kContentSettingsTypeGroupNames[i].name) { return true; } } @@ -389,7 +395,7 @@ std::string ContentSettingsTypeToGroupName(ContentSettingsType type) { for (size_t i = 0; i < base::size(kContentSettingsTypeGroupNames); ++i) { if (type == kContentSettingsTypeGroupNames[i].type) { const char* name = kContentSettingsTypeGroupNames[i].name; - if (name != nullptr) + if (name) return name; break; } @@ -455,7 +461,9 @@ std::unique_ptr<base::DictionaryValue> GetExceptionForPage( const std::string& display_name, const ContentSetting& setting, const std::string& provider_name, - bool incognito) { + bool incognito, + bool is_embargoed, + bool is_discarded) { auto exception = std::make_unique<base::DictionaryValue>(); exception->SetString(kOrigin, pattern.ToString()); exception->SetString(kDisplayName, display_name); @@ -471,6 +479,8 @@ std::unique_ptr<base::DictionaryValue> GetExceptionForPage( exception->SetString(kSetting, setting_string); exception->SetString(kSource, provider_name); exception->SetBoolean(kIncognito, incognito); + exception->SetBoolean(kIsEmbargoed, is_embargoed); + exception->SetBoolean(kIsDiscarded, is_discarded); return exception; } @@ -539,6 +549,11 @@ void GetExceptionsForContentType( map->GetSettingsForOneType(type, std::string(), &all_settings); + // Will return only regular settings for a regular profile and only incognito + // settings for an incognito Profile. + ContentSettingsForOneType discarded_settings; + map->GetDiscardedSettingsForOneType(type, std::string(), &discarded_settings); + // Group settings by primary_pattern. AllPatternsSettings all_patterns_settings; for (const auto& setting : all_settings) { @@ -556,6 +571,11 @@ void GetExceptionsForContentType( if (map->IsOffTheRecord() && !setting.incognito) continue; + // Don't add WebUI settings. + if (PatternAppliesToWebUISchemes(setting)) { + continue; + } + all_patterns_settings[std::make_pair( setting.primary_pattern, setting.source)][setting.secondary_pattern] = setting.GetContentSetting(); @@ -569,6 +589,8 @@ void GetExceptionsForContentType( permissions::PermissionsClient::Get()->GetPermissionDecisionAutoBlocker( profile); + std::set<ContentSettingsPattern> origins_under_embargo; + for (const auto& setting : embargo_settings) { // Off-the-record HostContentSettingsMap contains incognito content // settings as well as normal content settings. Here, we use the @@ -582,6 +604,7 @@ void GetExceptionsForContentType( if (auto_blocker ->GetEmbargoResult(GURL(setting.primary_pattern.ToString()), type) .content_setting == CONTENT_SETTING_BLOCK) { + origins_under_embargo.insert(setting.primary_pattern); all_patterns_settings[std::make_pair( setting.primary_pattern, setting.source)][setting.secondary_pattern] = CONTENT_SETTING_BLOCK; @@ -620,9 +643,10 @@ void GetExceptionsForContentType( parent == one_settings.end() ? CONTENT_SETTING_DEFAULT : parent->second; const ContentSettingsPattern& secondary_pattern = parent == one_settings.end() ? primary_pattern : parent->first; - this_provider_exceptions.push_back( - GetExceptionForPage(primary_pattern, secondary_pattern, display_name, - parent_setting, source, incognito)); + this_provider_exceptions.push_back(GetExceptionForPage( + primary_pattern, secondary_pattern, display_name, parent_setting, + source, incognito, + base::Contains(origins_under_embargo, primary_pattern))); // Add the "children" for any embedded settings. for (auto j = one_settings.begin(); j != one_settings.end(); ++j) { @@ -631,9 +655,9 @@ void GetExceptionsForContentType( continue; ContentSetting content_setting = j->second; - this_provider_exceptions.push_back( - GetExceptionForPage(primary_pattern, j->first, display_name, - content_setting, source, incognito)); + this_provider_exceptions.push_back(GetExceptionForPage( + primary_pattern, j->first, display_name, content_setting, source, + incognito, base::Contains(origins_under_embargo, primary_pattern))); } } @@ -653,6 +677,15 @@ void GetExceptionsForContentType( for (auto& exception : one_provider_exceptions) exceptions->Append(std::move(exception)); } + + for (auto& discarded_rule : discarded_settings) { + exceptions->Append(GetExceptionForPage( + discarded_rule.primary_pattern, discarded_rule.secondary_pattern, + GetDisplayNameForPattern(discarded_rule.primary_pattern, + extension_registry), + discarded_rule.GetContentSetting(), discarded_rule.source, incognito, + false /*is_embargoed*/, true /*is_discarded*/)); + } } void GetContentCategorySetting(const HostContentSettingsMap* map, @@ -713,7 +746,8 @@ std::vector<ContentSettingPatternSource> GetSiteExceptionsForContentType( map->GetSettingsForOneType(content_type, std::string(), &entries); entries.erase(std::remove_if(entries.begin(), entries.end(), [](const ContentSettingPatternSource& e) { - return !PatternAppliesToSingleOrigin(e); + return !PatternAppliesToSingleOrigin(e) || + PatternAppliesToWebUISchemes(e); }), entries.end()); return entries; @@ -864,6 +898,10 @@ base::Value GetChooserExceptionListFromProfile( std::map<std::pair<base::string16, base::Value>, ChooserExceptionDetails> all_chooser_objects; for (const auto& object : objects) { + // Don't include WebUI settings. + if (content::HasWebUIScheme(object->requesting_origin)) + continue; + base::string16 name = chooser_context->GetObjectDisplayName(object->value); auto& chooser_exception_details = all_chooser_objects[std::make_pair(name, object->value.Clone())]; @@ -893,121 +931,6 @@ base::Value GetChooserExceptionListFromProfile( return exceptions; } -CookieControlsManagedState GetCookieControlsManagedState(Profile* profile) { - // Setup a default unmanaged state that is updated based on the actual - // managed state. - CookieControlsManagedState managed_state; - - HostContentSettingsMap* map = - HostContentSettingsMapFactory::GetForProfile(profile); - std::string content_setting_provider; - auto content_setting = map->GetDefaultContentSetting( - ContentSettingsType::COOKIES, &content_setting_provider); - auto content_setting_source = - HostContentSettingsMap::GetSettingSourceFromProviderName( - content_setting_provider); - bool content_setting_enforced = - content_setting_source != - content_settings::SettingSource::SETTING_SOURCE_USER; - - // Both the content setting and the block_third_party preference can - // be controlled via policy. - const PrefService::Preference* block_third_party_pref = - profile->GetPrefs()->FindPreference(prefs::kBlockThirdPartyCookies); - bool block_third_party_on = block_third_party_pref->GetValue()->GetBool(); - bool block_third_party_enforced = !block_third_party_pref->IsUserModifiable(); - // IsRecommended() cannot be used as we care if a recommended value exists at - // all, even if a user has overwritten it. - bool block_third_party_recommended = - (block_third_party_pref && block_third_party_pref->GetRecommendedValue()); - bool block_third_party_recommended_on = - block_third_party_recommended && - block_third_party_pref->GetRecommendedValue()->GetBool(); - - // kCookieControlsMode == kOn should imply block_third_party is on. - auto control_mode = static_cast<content_settings::CookieControlsMode>( - profile->GetPrefs()->GetInteger(prefs::kCookieControlsMode)); - DCHECK(control_mode != - content_settings::CookieControlsMode::kBlockThirdParty || - block_third_party_on); - - // Get indicators representing each settings source. These may or may not - // be used depending on the determined managed state. - auto content_setting_source_indicator = - GetPolicyIndicatorFromSettingSource(content_setting_source); - auto block_third_party_source_indicator = - GetPolicyIndicatorFromPref(block_third_party_pref); - - if (!content_setting_enforced && !block_third_party_enforced && - !block_third_party_recommended) { - // No cookie controls are managed or recommended. - return managed_state; - } - - if (content_setting_enforced) { - // Block and session only managed state only depend on the content setting. - managed_state.block_all = {/*disabled*/ true, - content_setting_source_indicator}; - managed_state.session_only = {/*disabled*/ true, - content_setting_source_indicator}; - } - - if (content_setting_enforced && content_setting == CONTENT_SETTING_BLOCK) { - // All remaining controls are managed by the content setting source. - managed_state.allow_all = {/*disabled*/ true, - content_setting_source_indicator}; - managed_state.block_third_party_incognito = { - /*disabled*/ true, content_setting_source_indicator}; - managed_state.block_third_party = {/*disabled*/ true, - content_setting_source_indicator}; - return managed_state; - } - if (content_setting_enforced && block_third_party_enforced) { - // All remaining controls are managed by the block_third_party source. - managed_state.allow_all = {/*disabled*/ true, - block_third_party_source_indicator}; - managed_state.block_third_party_incognito = { - /*disabled*/ true, block_third_party_source_indicator}; - managed_state.block_third_party = {/*disabled*/ true, - block_third_party_source_indicator}; - return managed_state; - } - DCHECK(!content_setting_enforced || - content_setting == CONTENT_SETTING_ALLOW || - content_setting == CONTENT_SETTING_SESSION_ONLY); - DCHECK(!content_setting_enforced || !block_third_party_enforced); - // At this stage the content setting is not enforcing a BLOCK state. Given - // this, allow and block_third_party are still valid choices that do not - // contradict the content setting. They can thus be controlled or recommended - // by the block_third_party preference. - if (block_third_party_enforced) { - DCHECK(!content_setting_enforced); - managed_state.block_third_party_incognito = { - true, block_third_party_source_indicator}; - if (block_third_party_on) { - managed_state.allow_all = {/*disabled*/ true, - block_third_party_source_indicator}; - } else { - managed_state.block_third_party = {/*disabled*/ true, - block_third_party_source_indicator}; - } - return managed_state; - } - if (block_third_party_recommended) { - if (block_third_party_recommended_on) { - managed_state.block_third_party = {/*disabled*/ false, - block_third_party_source_indicator}; - } else { - managed_state.allow_all = {/*disabled*/ false, - block_third_party_source_indicator}; - } - return managed_state; - } - DCHECK(content_setting_enforced && !block_third_party_enforced && - !block_third_party_recommended); - return managed_state; -} - std::string PolicyIndicatorTypeToString(const PolicyIndicatorType type) { return kPolicyIndicatorTypeStringMapping[static_cast<int>(type)] .indicator_str; diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h index b018f7c63a3..8ac89c83c11 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper.h @@ -53,11 +53,6 @@ typedef std::map<std::pair<ContentSettingsPattern, std::string>, using ChooserExceptionDetails = std::map<std::pair<GURL, std::string>, std::set<std::pair<GURL, bool>>>; -constexpr char kAllowAll[] = "allowAll"; -constexpr char kBlockThirdPartyIncognito[] = "blockThirdPartyIncognito"; -constexpr char kBlockThirdParty[] = "blockThirdParty"; -constexpr char kBlockAll[] = "blockAll"; -constexpr char kSessionOnly[] = "sessionOnly"; constexpr char kChooserType[] = "chooserType"; constexpr char kDisplayName[] = "displayName"; constexpr char kEmbeddingOrigin[] = "embeddingOrigin"; @@ -72,8 +67,11 @@ constexpr char kSites[] = "sites"; constexpr char kPolicyIndicator[] = "indicator"; constexpr char kSource[] = "source"; constexpr char kType[] = "type"; +constexpr char kIsEmbargoed[] = "isEmbargoed"; +constexpr char kIsDiscarded[] = "isDiscarded"; enum class SiteSettingSource { + kAllowlist, kAdsFilterBlacklist, kDefault, kDrmDisabled, @@ -108,15 +106,6 @@ struct ManagedState { PolicyIndicatorType indicator = PolicyIndicatorType::kNone; }; -// Represents the manage states for all of the cookie controls. -struct CookieControlsManagedState { - ManagedState allow_all; - ManagedState block_third_party_incognito; - ManagedState block_third_party; - ManagedState block_all; - ManagedState session_only; -}; - // Returns whether a group name has been registered for the given type. bool HasRegisteredGroupName(ContentSettingsType type); @@ -141,7 +130,9 @@ std::unique_ptr<base::DictionaryValue> GetExceptionForPage( const std::string& display_name, const ContentSetting& setting, const std::string& provider_name, - bool incognito); + bool incognito, + bool is_embargoed = false, + bool is_discarded = false); // Helper function to construct a dictionary for a hosted app exception. void AddExceptionForHostedApp(const std::string& url_pattern, @@ -225,9 +216,6 @@ base::Value GetChooserExceptionListFromProfile( Profile* profile, const ChooserTypeNameEntry& chooser_type); -// Returns the cookie controls manage state for a given profile. -CookieControlsManagedState GetCookieControlsManagedState(Profile* profile); - // Concerts a PolicyIndicatorType to its string identifier. std::string PolicyIndicatorTypeToString(const PolicyIndicatorType type); diff --git a/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc b/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc index 2a08d0797ee..6b11de46fbe 100644 --- a/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc +++ b/chromium/chrome/browser/ui/webui/settings/site_settings_helper_unittest.cc @@ -23,7 +23,6 @@ #include "components/permissions/chooser_context_base.h" #include "components/permissions/permission_decision_auto_blocker.h" #include "components/prefs/pref_service.h" -#include "components/sync_preferences/testing_pref_service_syncable.h" #include "content/public/test/browser_task_environment.h" #include "extensions/browser/extension_registry.h" #include "mojo/public/cpp/bindings/pending_remote.h" @@ -75,6 +74,47 @@ class SiteSettingsHelperTest : public testing::Test { content::BrowserTaskEnvironment task_environment_; }; +TEST_F(SiteSettingsHelperTest, ExceptionListWithEmbargoedAndBlockedOrigins) { + TestingProfile profile; + + constexpr char kOriginToEmbargo[] = "https://embargoed.co.uk:443"; + auto* auto_blocker = + PermissionDecisionAutoBlockerFactory::GetForProfile(&profile); + for (size_t i = 0; i < 3; ++i) { + auto_blocker->RecordDismissAndEmbargo(GURL(kOriginToEmbargo), + kContentTypeNotifications, false); + } + + constexpr char kOriginToBlock[] = "https://www.blocked.com:443"; + auto* map = HostContentSettingsMapFactory::GetForProfile(&profile); + map->SetContentSettingDefaultScope(GURL(kOriginToBlock), GURL(kOriginToBlock), + kContentTypeNotifications, std::string(), + CONTENT_SETTING_BLOCK); + + base::ListValue exceptions; + site_settings::GetExceptionsForContentType(kContentTypeNotifications, + &profile, + /*extension_registry=*/nullptr, + /*web_ui=*/nullptr, + /*incognito=*/false, &exceptions); + + // |exceptions| size should be 2. One blocked and one embargoed origins. + ASSERT_EQ(2U, exceptions.GetSize()); + base::Value* value = nullptr; + // Get last added origin. + exceptions.Get(0, &value); + base::Value* is_embargoed = value->FindKey(site_settings::kIsEmbargoed); + ASSERT_NE(nullptr, is_embargoed); + // Last added origin is blocked, |embargo| key should be false. + EXPECT_FALSE(is_embargoed->GetBool()); + + // Get embargoed origin. + exceptions.Get(1, &value); + is_embargoed = value->FindKey(site_settings::kIsEmbargoed); + ASSERT_NE(nullptr, is_embargoed); + EXPECT_TRUE(is_embargoed->GetBool()); +} + TEST_F(SiteSettingsHelperTest, ExceptionListShowsIncognitoEmbargoed) { TestingProfile profile; constexpr char kOriginToBlock[] = "https://www.blocked.com:443"; @@ -816,302 +856,4 @@ TEST_F(SiteSettingsHelperChooserExceptionTest, } } -namespace { - -// All of the possible managed states for a boolean preference that can be -// both enforced and recommended. -enum class PrefSetting { - kEnforcedOff, - kEnforcedOn, - kRecommendedOff, - kRecommendedOn, - kNotSet, -}; - -// Possible preference sources supported by TestingPrefService. -// TODO(crbug.com/1063281): Extend TestingPrefService to support prefs set for -// supervised users. -enum class PrefSource { - kExtension, - kDevicePolicy, - kRecommended, - kNone, -}; - -// Represents a set of settings, preferences and the associated expected -// CookieControlsManagedState. -struct CookiesManagedStateTestCase { - ContentSetting default_content_setting; - content_settings::SettingSource default_content_setting_source; - PrefSetting block_third_party; - PrefSource block_third_party_source; - CookieControlsManagedState expected_result; -}; - -const std::vector<CookiesManagedStateTestCase> test_cases = { - {CONTENT_SETTING_DEFAULT, - content_settings::SETTING_SOURCE_NONE, - PrefSetting::kEnforcedOff, - PrefSource::kExtension, - {{false, PolicyIndicatorType::kNone}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}}}, - {CONTENT_SETTING_DEFAULT, - content_settings::SETTING_SOURCE_NONE, - PrefSetting::kEnforcedOn, - PrefSource::kDevicePolicy, - {{true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}}}, - {CONTENT_SETTING_DEFAULT, - content_settings::SETTING_SOURCE_NONE, - PrefSetting::kRecommendedOff, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kRecommended}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}}}, - {CONTENT_SETTING_DEFAULT, - content_settings::SETTING_SOURCE_NONE, - PrefSetting::kRecommendedOn, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kRecommended}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}}}, - {CONTENT_SETTING_DEFAULT, - content_settings::SETTING_SOURCE_NONE, - PrefSetting::kNotSet, - PrefSource::kNone, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}}}, - {CONTENT_SETTING_ALLOW, - content_settings::SETTING_SOURCE_POLICY, - PrefSetting::kEnforcedOff, - PrefSource::kExtension, - {{true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}}}, - {CONTENT_SETTING_ALLOW, - content_settings::SETTING_SOURCE_EXTENSION, - PrefSetting::kEnforcedOn, - PrefSource::kDevicePolicy, - {{true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}}}, - {CONTENT_SETTING_ALLOW, - content_settings::SETTING_SOURCE_SUPERVISED, - PrefSetting::kRecommendedOff, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kRecommended}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}}}, - {CONTENT_SETTING_ALLOW, - content_settings::SETTING_SOURCE_POLICY, - PrefSetting::kRecommendedOn, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kRecommended}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}}}, - {CONTENT_SETTING_ALLOW, - content_settings::SETTING_SOURCE_EXTENSION, - PrefSetting::kNotSet, - PrefSource::kNone, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}}}, - {CONTENT_SETTING_BLOCK, - content_settings::SETTING_SOURCE_SUPERVISED, - PrefSetting::kEnforcedOff, - PrefSource::kDevicePolicy, - {{true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}}}, - {CONTENT_SETTING_BLOCK, - content_settings::SETTING_SOURCE_POLICY, - PrefSetting::kEnforcedOn, - PrefSource::kExtension, - {{true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}}}, - {CONTENT_SETTING_BLOCK, - content_settings::SETTING_SOURCE_EXTENSION, - PrefSetting::kRecommendedOff, - PrefSource::kRecommended, - {{true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}}}, - {CONTENT_SETTING_BLOCK, - content_settings::SETTING_SOURCE_SUPERVISED, - PrefSetting::kRecommendedOn, - PrefSource::kRecommended, - {{true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}}}, - {CONTENT_SETTING_BLOCK, - content_settings::SETTING_SOURCE_POLICY, - PrefSetting::kNotSet, - PrefSource::kNone, - {{true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}}}, - {CONTENT_SETTING_SESSION_ONLY, - content_settings::SETTING_SOURCE_EXTENSION, - PrefSetting::kEnforcedOff, - PrefSource::kDevicePolicy, - {{true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}}}, - {CONTENT_SETTING_SESSION_ONLY, - content_settings::SETTING_SOURCE_SUPERVISED, - PrefSetting::kEnforcedOn, - PrefSource::kExtension, - {{true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}}}, - {CONTENT_SETTING_SESSION_ONLY, - content_settings::SETTING_SOURCE_POLICY, - PrefSetting::kRecommendedOff, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kRecommended}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {true, PolicyIndicatorType::kDevicePolicy}, - {true, PolicyIndicatorType::kDevicePolicy}}}, - {CONTENT_SETTING_SESSION_ONLY, - content_settings::SETTING_SOURCE_EXTENSION, - PrefSetting::kRecommendedOn, - PrefSource::kRecommended, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kRecommended}, - {true, PolicyIndicatorType::kExtension}, - {true, PolicyIndicatorType::kExtension}}}, - {CONTENT_SETTING_SESSION_ONLY, - content_settings::SETTING_SOURCE_SUPERVISED, - PrefSetting::kNotSet, - PrefSource::kNone, - {{false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {false, PolicyIndicatorType::kNone}, - {true, PolicyIndicatorType::kParent}, - {true, PolicyIndicatorType::kParent}}}}; - -void SetupTestConditions(HostContentSettingsMap* map, - sync_preferences::TestingPrefServiceSyncable* prefs, - const CookiesManagedStateTestCase& test_case) { - if (test_case.default_content_setting != CONTENT_SETTING_DEFAULT) { - auto provider = std::make_unique<content_settings::MockProvider>(); - provider->SetWebsiteSetting( - ContentSettingsPattern::Wildcard(), ContentSettingsPattern::Wildcard(), - ContentSettingsType::COOKIES, std::string(), - std::make_unique<base::Value>(test_case.default_content_setting)); - HostContentSettingsMap::ProviderType provider_type; - switch (test_case.default_content_setting_source) { - case content_settings::SETTING_SOURCE_POLICY: - provider_type = HostContentSettingsMap::POLICY_PROVIDER; - break; - case content_settings::SETTING_SOURCE_EXTENSION: - provider_type = HostContentSettingsMap::CUSTOM_EXTENSION_PROVIDER; - break; - case content_settings::SETTING_SOURCE_SUPERVISED: - provider_type = HostContentSettingsMap::SUPERVISED_PROVIDER; - break; - case content_settings::SETTING_SOURCE_NONE: - default: - provider_type = HostContentSettingsMap::DEFAULT_PROVIDER; - } - content_settings::TestUtils::OverrideProvider(map, std::move(provider), - provider_type); - } - - if (test_case.block_third_party != PrefSetting::kNotSet) { - bool third_party_value = - test_case.block_third_party == PrefSetting::kRecommendedOn || - test_case.block_third_party == PrefSetting::kEnforcedOn; - if (test_case.block_third_party_source == PrefSource::kExtension) { - prefs->SetExtensionPref(prefs::kBlockThirdPartyCookies, - std::make_unique<base::Value>(third_party_value)); - } else if (test_case.block_third_party_source == - PrefSource::kDevicePolicy) { - prefs->SetManagedPref(prefs::kBlockThirdPartyCookies, - std::make_unique<base::Value>(third_party_value)); - } else if (test_case.block_third_party_source == PrefSource::kRecommended) { - prefs->SetRecommendedPref( - prefs::kBlockThirdPartyCookies, - std::make_unique<base::Value>(third_party_value)); - } - } -} - -void AssertManagedCookieStateEqual(const CookieControlsManagedState& a, - const CookieControlsManagedState b) { - ASSERT_EQ(a.allow_all.disabled, b.allow_all.disabled); - ASSERT_EQ(a.allow_all.indicator, b.allow_all.indicator); - ASSERT_EQ(a.block_third_party_incognito.disabled, - b.block_third_party_incognito.disabled); - ASSERT_EQ(a.block_third_party_incognito.indicator, - b.block_third_party_incognito.indicator); - ASSERT_EQ(a.block_third_party.disabled, b.block_third_party.disabled); - ASSERT_EQ(a.block_third_party.indicator, b.block_third_party.indicator); - ASSERT_EQ(a.block_all.disabled, b.block_all.disabled); - ASSERT_EQ(a.block_all.indicator, b.block_all.indicator); - ASSERT_EQ(a.session_only.disabled, b.session_only.disabled); - ASSERT_EQ(a.session_only.indicator, b.session_only.indicator); -} - -TEST_F(SiteSettingsHelperTest, CookiesManagedState) { - for (auto test_case : test_cases) { - TestingProfile profile; - HostContentSettingsMap* map = - HostContentSettingsMapFactory::GetForProfile(&profile); - sync_preferences::TestingPrefServiceSyncable* prefs = - profile.GetTestingPrefService(); - testing::Message scope_message; - scope_message << "Content Setting:" << test_case.default_content_setting - << " Block Third Party:" - << static_cast<int>(test_case.block_third_party); - SCOPED_TRACE(scope_message); - SetupTestConditions(map, prefs, test_case); - AssertManagedCookieStateEqual( - site_settings::GetCookieControlsManagedState(&profile), - test_case.expected_result); - } -} - -} // namespace - } // namespace site_settings diff --git a/chromium/chrome/browser/ui/webui/settings/tts_handler.cc b/chromium/chrome/browser/ui/webui/settings/tts_handler.cc index f17e4b3a742..abf485deba1 100644 --- a/chromium/chrome/browser/ui/webui/settings/tts_handler.cc +++ b/chromium/chrome/browser/ui/webui/settings/tts_handler.cc @@ -28,7 +28,7 @@ namespace settings { TtsHandler::TtsHandler() {} TtsHandler::~TtsHandler() { - content::TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); + RemoveTtsControllerDelegates(); } void TtsHandler::HandleGetAllTtsVoiceData(const base::ListValue* args) { @@ -176,8 +176,7 @@ void TtsHandler::OnJavascriptAllowed() { } void TtsHandler::OnJavascriptDisallowed() { - content::TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); - content::TtsController::GetInstance()->RemoveUtteranceEventDelegate(this); + RemoveTtsControllerDelegates(); } int TtsHandler::GetVoiceLangMatchScore(const content::VoiceData* voice, @@ -205,4 +204,9 @@ void TtsHandler::OnTtsEngineAwake(bool success) { OnVoicesChanged(); } +void TtsHandler::RemoveTtsControllerDelegates() { + content::TtsController::GetInstance()->RemoveVoicesChangedDelegate(this); + content::TtsController::GetInstance()->RemoveUtteranceEventDelegate(this); +} + } // namespace settings diff --git a/chromium/chrome/browser/ui/webui/settings/tts_handler.h b/chromium/chrome/browser/ui/webui/settings/tts_handler.h index a839dee3c0b..fa1820a32ba 100644 --- a/chromium/chrome/browser/ui/webui/settings/tts_handler.h +++ b/chromium/chrome/browser/ui/webui/settings/tts_handler.h @@ -45,6 +45,7 @@ class TtsHandler : public SettingsPageUIHandler, void OnTtsEngineAwake(bool success); int GetVoiceLangMatchScore(const content::VoiceData* voice, const std::string& app_locale); + void RemoveTtsControllerDelegates(); base::WeakPtrFactory<TtsHandler> weak_factory_{this}; |