diff --git a/build/patches/Content-settings-infrastructure.patch b/build/patches/Content-settings-infrastructure.patch
index 3d018e67..f8a8826a 100644
--- a/build/patches/Content-settings-infrastructure.patch
+++ b/build/patches/Content-settings-infrastructure.patch
@@ -14,25 +14,37 @@ Require: bromite-build-utils.patch
.../ChromeSiteSettingsDelegate.java | 17 +-
.../installable/installed_webapp_bridge.cc | 2 +-
.../settings/privacy_page/privacy_page.html | 1 +
- .../settings/privacy_page/privacy_page.ts | 44 ++-
+ .../settings/privacy_page/privacy_page.ts | 77 +++-
chrome/browser/resources/settings/route.ts | 2 +
- .../settings_category_default_radio_group.ts | 7 +
+ ...settings_category_default_radio_group.html | 12 +
+ .../settings_category_default_radio_group.ts | 22 +-
.../settings/site_settings/site_details.html | 1 +
- .../settings/site_settings/site_details.ts | 16 +-
- .../site_settings_page/site_settings_page.ts | 28 +-
- .../site_settings_page_util.ts | 55 ++++
+ .../settings/site_settings/site_details.ts | 33 +-
+ .../site_details_permission.html | 8 +-
+ .../site_settings/site_details_permission.ts | 44 ++
+ .../site_settings/site_settings_mixin.ts | 18 +
+ .../site_settings_page/site_settings_page.ts | 30 +-
+ .../site_settings_page_util.ts | 55 +++
chrome/browser/ui/BUILD.gn | 1 +
- .../views/page_info/page_info_view_factory.cc | 13 +
- .../settings_localized_strings_provider.cc | 39 +++
+ .../controls/rich_controls_container_view.h | 7 +
+ .../ui/views/page_info/page_info_main_view.cc | 27 +-
+ .../views/page_info/page_info_view_factory.cc | 19 +
+ .../views/page_info/page_info_view_factory.h | 3 +
+ .../page_info/permission_toggle_row_view.cc | 163 ++++++-
+ .../page_info/permission_toggle_row_view.h | 18 +-
+ .../settings_localized_strings_provider.cc | 54 +++
.../ui/webui/settings/site_settings_helper.cc | 34 +-
+ .../browser_ui/settings/android/BUILD.gn | 1 +
+ .../preference_spinner_single_widget.xml | 92 ++++
+ .../settings/SpinnerPreference.java | 24 +
.../browser_ui/site_settings/android/BUILD.gn | 18 +
.../site_settings/AllSiteSettings.java | 5 +-
- .../BromiteCustomContentSetting.java | 139 ++++++++
- .../BromiteCustomContentSettingImpl.java | 307 ++++++++++++++++++
- ...tomTriStateSiteSettingsPreferenceImpl.java | 24 ++
+ .../BromiteCustomContentSetting.java | 155 +++++++
+ .../BromiteCustomContentSettingImpl.java | 409 ++++++++++++++++++
+ ...tomTriStateSiteSettingsPreferenceImpl.java | 24 +
.../ContentSettingsResources.java | 14 +-
.../site_settings/SingleCategorySettings.java | 50 ++-
- .../site_settings/SingleWebsiteSettings.java | 26 +-
+ .../site_settings/SingleWebsiteSettings.java | 58 ++-
.../site_settings/SiteSettings.java | 14 +-
.../site_settings/SiteSettingsCategory.java | 18 +-
.../site_settings/SiteSettingsDelegate.java | 2 +
@@ -41,7 +53,7 @@ Require: bromite-build-utils.patch
.../WebsitePermissionsFetcher.java | 4 +-
.../WebsitePreferenceBridge.java | 3 +-
.../impl/include_all_directory.java | 1 +
- ...miteCustomContentSettingImplBase.java.tmpl | 48 +++
+ ...miteCustomContentSettingImplBase.java.tmpl | 48 ++
.../bromite_content_settings/placeholder.txt | 1 +
components/content_settings/android/BUILD.gn | 1 +
.../content_settings/core/browser/BUILD.gn | 7 +
@@ -49,7 +61,7 @@ Require: bromite-build-utils.patch
.../core/browser/content_settings_registry.cc | 2 +
.../core/browser/content_settings_uma_util.cc | 3 +-
.../core/browser/content_settings_utils.cc | 12 +
- .../core/browser/website_settings_info.h | 35 ++
+ .../core/browser/website_settings_info.h | 38 ++
.../core/browser/website_settings_registry.cc | 6 +
.../core/browser/website_settings_registry.h | 1 +
.../content_settings/core/common/BUILD.gn | 7 +
@@ -58,7 +70,7 @@ Require: bromite-build-utils.patch
.../core/common/content_settings.h | 10 +
.../core/common/content_settings.mojom | 6 +
.../common/content_settings_mojom_traits.cc | 12 +-
- .../common/content_settings_mojom_traits.h | 24 ++
+ .../common/content_settings_mojom_traits.h | 24 +
.../common/content_settings_param_traits.h | 2 +-
.../core/common/content_settings_types.h | 4 +-
.../renderer/content_settings_agent_impl.cc | 33 ++
@@ -72,7 +84,8 @@ Require: bromite-build-utils.patch
.../platform/web_content_settings_client.h | 5 +
.../execution_context/execution_context.cc | 16 +
.../execution_context/execution_context.h | 5 +
- 61 files changed, 1194 insertions(+), 45 deletions(-)
+ 73 files changed, 1821 insertions(+), 76 deletions(-)
+ create mode 100644 components/browser_ui/settings/android/java/res/layout/preference_spinner_single_widget.xml
create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSetting.java
create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSettingImpl.java
create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomTriStateSiteSettingsPreferenceImpl.java
@@ -147,9 +160,37 @@ diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
--- a/chrome/browser/resources/settings/privacy_page/privacy_page.ts
+++ b/chrome/browser/resources/settings/privacy_page/privacy_page.ts
-@@ -71,7 +71,49 @@ export class SettingsPrivacyPageElement extends SettingsPrivacyPageElementBase {
+@@ -47,6 +47,7 @@ import {SiteSettingsPrefsBrowserProxy, SiteSettingsPrefsBrowserProxyImpl} from '
+
+ import {PrivacyGuideAvailabilityMixin} from './privacy_guide/privacy_guide_availability_mixin.js';
+ import {getTemplate} from './privacy_page.html.js';
++import { SettingsCategoryDefaultRadioGroupElement } from '../site_settings/settings_category_default_radio_group.js';
+
+ interface BlockAutoplayStatus {
+ enabled: boolean;
+@@ -70,8 +71,82 @@ export class SettingsPrivacyPageElement extends SettingsPrivacyPageElementBase {
+ return 'settings-privacy-page';
}
++ static getSettingTemplate(template: HTMLTemplateElement,
++ name: string) : SettingsCategoryDefaultRadioGroupElement | undefined {
++ let value : SettingsCategoryDefaultRadioGroupElement | undefined = undefined;
++ let page : any = template.content!.querySelector("settings-animated-pages");
++
++ page.querySelectorAll("template")!.forEach(
++ (subTemplate: HTMLTemplateElement) => {
++ subTemplate.content.querySelectorAll("settings-category-default-radio-group").forEach(
++ (radio: any) => {
++ let setting = radio.getAttribute("category")
++ .replace("[[contentSettingsTypesEnum_.", "")
++ .replace("]]", "");
++ if ((ContentSettingsTypes as any)[setting] === name)
++ value = radio;
++ });
++ });
++ return value;
++ }
++
static get template() {
- return getTemplate();
+ let template = getTemplate();
@@ -160,36 +201,49 @@ diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.ts b/ch
+ let name = obj["name"];
+ let tag_name = obj["tag_ui"]; if (!tag_name) tag_name = obj["name"];
+
-+ let template = document.createElement("settings-subpage");
-+ template.setAttribute("page-title", loadTimeData.getString(`brSiteSettings${name}`));
-+ template.setAttribute("search-title", loadTimeData.getString("siteSettingsAllSitesSearch"));
-+ template.setAttribute("search-term", "{{searchFilter_}}");
++ let existingSetting = this.getSettingTemplate(template, name);
++ if (existingSetting) {
++ if (loadTimeData.valueExists(`brSiteSettings${name}Ask`)) {
++ existingSetting.setAttribute("ask-option-label", loadTimeData.getString(`brSiteSettings${name}Ask`));
++ existingSetting.setAttribute("ask-option-icon", `br-settings:${name}-off`);
++ }
++ continue;
++ }
++
++ let subpage = document.createElement("settings-subpage");
++ subpage.setAttribute("page-title", loadTimeData.getString(`brSiteSettings${name}`));
++ subpage.setAttribute("search-title", loadTimeData.getString("siteSettingsAllSitesSearch"));
++ subpage.setAttribute("search-term", "{{searchFilter_}}");
+
+ let divElement = document.createElement("div");
+ divElement.setAttribute("class", "content-settings-header secondary");
+ divElement.innerText = loadTimeData.getString(`brSiteSettings${name}Description`);
-+ template.appendChild(divElement);
++ subpage.appendChild(divElement);
+
-+ let radioGroup = document.createElement("settings-category-default-radio-group");
++ let radioGroup = existingSetting ?? document.createElement("settings-category-default-radio-group");
+ radioGroup.setAttribute("category", name);
+ radioGroup.setAttribute("allow-option-label", loadTimeData.getString(`brSiteSettings${name}Allowed`));
+ radioGroup.setAttribute("allow-option-icon", `br-settings:${name}`);
+ radioGroup.setAttribute("block-option-label", loadTimeData.getString(`brSiteSettings${name}Blocked`));
+ radioGroup.setAttribute("block-option-icon", `br-settings:${name}-off`);
-+ template.appendChild(radioGroup);
++ if (loadTimeData.valueExists(`brSiteSettings${name}Ask`)) {
++ radioGroup.setAttribute("ask-option-label", loadTimeData.getString(`brSiteSettings${name}Ask`));
++ radioGroup.setAttribute("ask-option-icon", `br-settings:${name}-off`);
++ }
++ subpage.appendChild(radioGroup);
+
+ let exceptions = document.createElement("category-setting-exceptions");
+ exceptions.setAttribute("category", name);
+ exceptions.setAttribute("allow-header", loadTimeData.getString(`brSiteSettings${name}AllowedExceptions`));
+ exceptions.setAttribute("block-header", loadTimeData.getString(`brSiteSettings${name}BlockedExceptions`));
+ exceptions.setAttribute("search-filter", "[[searchFilter_]]");
-+ template.appendChild(exceptions);
++ subpage.appendChild(exceptions);
+
+ let tag = document.createElement("template");
+ tag.setAttribute("is", "dom-if");
+ tag.setAttribute("route-path", `/content/${tag_name}`);
+ tag.setAttribute("no-search", "");
-+ tag.content.appendChild(template);
++ tag.content.appendChild(subpage);
+
+ content.parentElement!.insertBefore(tag, content);
+ }
@@ -217,23 +271,101 @@ diff --git a/chrome/browser/resources/settings/route.ts b/chrome/browser/resourc
}
/**
+diff --git a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.html b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.html
+--- a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.html
++++ b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.html
+@@ -53,5 +53,17 @@
+ icon="[[blockOptionIcon]]"
+ no-collapse>
+
++
++
+
+
diff --git a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
--- a/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
+++ b/chrome/browser/resources/settings/site_settings/settings_category_default_radio_group.ts
-@@ -161,6 +161,13 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+@@ -29,6 +29,7 @@ import {ContentSettingProvider, DefaultContentSetting} from './site_settings_pre
+ export enum SiteContentRadioSetting {
+ DISABLED = 0,
+ ENABLED = 1,
++ ASK = 2,
+ }
+
+ export interface SettingsCategoryDefaultRadioGroupElement {
+@@ -76,6 +77,10 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+ blockOptionSubLabel: String,
+ blockOptionIcon: String,
+
++ askOptionLabel: String,
++ askOptionSubLabel: String,
++ askOptionIcon: String,
++
+ siteContentRadioSettingEnum_: {
+ type: Object,
+ value: SiteContentRadioSetting,
+@@ -111,6 +116,9 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+ blockOptionLabel: string;
+ blockOptionSubLabel: string;
+ blockOptionIcon: string;
++ askOptionLabel: string;
++ askOptionSubLabel: string;
++ askOptionIcon: string;
+ private pref_: chrome.settingsPrivate.PrefObject;
+
+ override ready() {
+@@ -121,6 +129,13 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+ (category: ContentSettingsTypes) => this.onCategoryChanged_(category));
+ }
+
++ private showAskSetting_(
++ category: ContentSettingsTypes): boolean {
++ let obj = this.getSettingData(category);
++ if (obj) return obj["allowed_ask"] === "1";
++ return false;
++ }
++
+ private getAllowOptionForCategory_(): ContentSetting {
+ switch (this.category) {
+ case ContentSettingsTypes.ADS:
+@@ -161,6 +176,8 @@ export class SettingsCategoryDefaultRadioGroupElement extends
// "Ask" vs "Blocked".
return ContentSetting.ASK;
default:
-+ for (let index=0; index < loadTimeData.getInteger("br_cs_count"); index++) {
-+ let obj = JSON.parse(loadTimeData.getString("br_cs_" + index));
-+ let name = obj["name"];
-+ if (this.category == name) {
-+ return ContentSetting.ALLOW;
-+ }
-+ }
++ let obj = this.getSettingData(this.category);
++ if (obj) return ContentSetting.ALLOW;
assertNotReached('Invalid category: ' + this.category);
}
}
+@@ -185,6 +202,7 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+ /** @type {!ContentSetting} */ (this.getAllowOptionForCategory_());
+ this.browserProxy.setDefaultValueForContentType(
+ this.category,
++ this.pref_.value === SiteContentRadioSetting.ASK ? "ask" :
+ this.categoryEnabled_ ? allowOption : ContentSetting.BLOCK);
+ }
+
+@@ -216,7 +234,9 @@ export class SettingsCategoryDefaultRadioGroupElement extends
+ }
+
+ const enabled = this.computeIsSettingEnabled(update.setting);
+- const prefValue = enabled ? SiteContentRadioSetting.ENABLED :
++ const ask = this.computeIsSettingAsk(update.setting);
++ const prefValue = ask ? SiteContentRadioSetting.ASK :
++ enabled ? SiteContentRadioSetting.ENABLED :
+ SiteContentRadioSetting.DISABLED;
+
+ this.set('pref_.value', prefValue);
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html
--- a/chrome/browser/resources/settings/site_settings/site_details.html
+++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -246,9 +378,24 @@ diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b
diff --git a/chrome/browser/resources/settings/site_settings/site_details.ts b/chrome/browser/resources/settings/site_settings/site_details.ts
--- a/chrome/browser/resources/settings/site_settings/site_details.ts
+++ b/chrome/browser/resources/settings/site_settings/site_details.ts
-@@ -61,7 +61,21 @@ export class SiteDetailsElement extends SiteDetailsElementBase {
+@@ -60,8 +60,39 @@ export class SiteDetailsElement extends SiteDetailsElementBase {
+ return 'site-details';
}
++ static getSettingTemplate(template: HTMLTemplateElement,
++ name: string) : SiteDetailsPermissionElement | undefined {
++ let value : SiteDetailsPermissionElement | undefined = undefined;
++ template.content.querySelectorAll("site-details-permission").forEach(
++ (element: any) => {
++ let setting = element.getAttribute("category")
++ .replace("[[contentSettingsTypesEnum_.", "")
++ .replace("]]", "");
++ if ((ContentSettingsTypes as any)[setting] === name)
++ value = element;
++ });
++ return value;
++ }
++
static get template() {
- return getTemplate();
+ let template = getTemplate();
@@ -258,6 +405,9 @@ diff --git a/chrome/browser/resources/settings/site_settings/site_details.ts b/c
+ let obj = JSON.parse(loadTimeData.getString("br_cs_" + index));
+ let name = obj["name"];
+
++ if (this.getSettingTemplate(template, name))
++ continue;
++
+ let tag = document.createElement("site-details-permission");
+ tag.setAttribute("category", name);
+ tag.setAttribute("icon", `br-settings:${name}`);
@@ -269,6 +419,163 @@ diff --git a/chrome/browser/resources/settings/site_settings/site_details.ts b/c
}
static get properties() {
+diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.html b/chrome/browser/resources/settings/site_settings/site_details_permission.html
+--- a/chrome/browser/resources/settings/site_settings/site_details_permission.html
++++ b/chrome/browser/resources/settings/site_settings/site_details_permission.html
+@@ -49,7 +49,9 @@
+
+
+
+
+
+diff --git a/chrome/browser/resources/settings/site_settings/site_details_permission.ts b/chrome/browser/resources/settings/site_settings/site_details_permission.ts
+--- a/chrome/browser/resources/settings/site_settings/site_details_permission.ts
++++ b/chrome/browser/resources/settings/site_settings/site_details_permission.ts
+@@ -20,6 +20,7 @@ import {WebUiListenerMixin} from 'chrome://resources/cr_elements/web_ui_listener
+ import {assert, assertNotReached} from 'chrome://resources/js/assert.js';
+ import {sanitizeInnerHtml} from 'chrome://resources/js/parse_html_subset.js';
+ import {PolymerElement} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
++import {loadTimeData} from '../i18n_setup.js';
+
+ import {ChooserType, ContentSetting, ContentSettingsTypes, SiteSettingSource} from './constants.js';
+ import {getTemplate} from './site_details_permission.html.js';
+@@ -267,15 +268,28 @@ export class SiteDetailsPermissionElement extends
+ return '';
+ }
+
++ let obj = this.getSettingData(category);
+ if (defaultSetting === ContentSetting.ASK ||
+ defaultSetting === ContentSetting.IMPORTANT_CONTENT) {
++ if (obj) {
++ let v = this.askSettingString_(category, "");
++ if (v !== "") return "(Default) " + v;
++ }
+ return this.i18n('siteSettingsActionAskDefault');
+ } else if (defaultSetting === ContentSetting.ALLOW) {
++ if (obj) {
++ let v = this.allowSettingString_(category, "");
++ if (v !== "") return "(Default) " + v;
++ }
+ if (this.useCustomSoundLabels_(category) && useAutomaticLabel) {
+ return this.i18n('siteSettingsActionAutomaticDefault');
+ }
+ return this.i18n('siteSettingsActionAllowDefault');
+ } else if (defaultSetting === ContentSetting.BLOCK) {
++ if (obj) {
++ let v = this.blockSettingString_(category, "", "");
++ if (v !== "") return "(Default) " + v;
++ }
+ if (this.useCustomSoundLabels_(category)) {
+ return this.i18n('siteSettingsActionMuteDefault');
+ }
+@@ -294,12 +308,40 @@ export class SiteDetailsPermissionElement extends
+ private blockSettingString_(
+ category: ContentSettingsTypes, blockString: string,
+ muteString: string): string {
++ let obj = this.getSettingData(category);
++ if (obj) {
++ let propertyName = "brSiteSettings" + obj["name"] + "Blocked";
++ if (loadTimeData.valueExists(propertyName))
++ return loadTimeData.getString(propertyName);
++ }
+ if (this.useCustomSoundLabels_(category)) {
+ return muteString;
+ }
+ return blockString;
+ }
+
++ private allowSettingString_(
++ category: ContentSettingsTypes, defaultString: string): string {
++ let obj = this.getSettingData(category);
++ if (obj) {
++ let propertyName = "brSiteSettings" + obj["name"] + "Allowed";
++ if (loadTimeData.valueExists(propertyName))
++ return loadTimeData.getString(propertyName);
++ }
++ return defaultString;
++ }
++
++ private askSettingString_(
++ category: ContentSettingsTypes, defaultString: string): string {
++ let obj = this.getSettingData(category);
++ if (obj) {
++ let propertyName = "brSiteSettings" + obj["name"] + "Ask";
++ if (loadTimeData.valueExists(propertyName))
++ return loadTimeData.getString(propertyName);
++ }
++ return defaultString;
++ }
++
+ /**
+ * @return true if |this| should be hidden.
+ */
+@@ -385,6 +427,8 @@ export class SiteDetailsPermissionElement extends
+ private showAskSetting_(
+ category: ContentSettingsTypes, setting: ContentSetting,
+ source: SiteSettingSource): boolean {
++ let obj = this.getSettingData(category);
++ if (obj) return obj["allowed_ask"] === "1";
+ // For chooser-based permissions 'ask' takes the place of 'allow'.
+ if (category === ContentSettingsTypes.SERIAL_PORTS ||
+ category === ContentSettingsTypes.USB_DEVICES ||
+diff --git a/chrome/browser/resources/settings/site_settings/site_settings_mixin.ts b/chrome/browser/resources/settings/site_settings/site_settings_mixin.ts
+--- a/chrome/browser/resources/settings/site_settings/site_settings_mixin.ts
++++ b/chrome/browser/resources/settings/site_settings/site_settings_mixin.ts
+@@ -11,6 +11,7 @@ import {dedupingMixin, PolymerElement} from 'chrome://resources/polymer/v3_0/pol
+
+ import {ContentSetting,ContentSettingsTypes, SiteSettingSource} from './constants.js';
+ import {RawSiteException,SiteException,SiteSettingsPrefsBrowserProxy,SiteSettingsPrefsBrowserProxyImpl} from './site_settings_prefs_browser_proxy.js';
++import {loadTimeData} from '../i18n_setup.js';
+ // clang-format on
+
+ type Constructor = new (...args: any[]) => T;
+@@ -91,6 +92,21 @@ export const SiteSettingsMixin = dedupingMixin(
+ return setting !== ContentSetting.BLOCK;
+ }
+
++ computeIsSettingAsk(setting: ContentSetting): boolean {
++ return setting === ContentSetting.ASK;
++ }
++
++ getSettingData(category: ContentSettingsTypes): any {
++ for (let index=0; index < loadTimeData.getInteger("br_cs_count"); index++) {
++ let obj = JSON.parse(loadTimeData.getString("br_cs_" + index));
++ let name = obj["name"];
++ if (name == category) {
++ return obj;
++ }
++ }
++ return undefined;
++ }
++
+ /**
+ * Converts a string origin/pattern to a URL.
+ * @param originOrPattern The origin/pattern to convert to URL.
+@@ -171,6 +187,8 @@ export interface SiteSettingsMixinInterface {
+ browserProxy: SiteSettingsPrefsBrowserProxy;
+ category: ContentSettingsTypes;
+ computeIsSettingEnabled(setting: string): boolean;
++ computeIsSettingAsk(setting: string): boolean;
++ getSettingData(category: ContentSettingsTypes): any;
+ originRepresentation(origin: string): string;
+ toUrl(originOrPattern: string): URL|null;
+ expandSiteException(exception: RawSiteException): SiteException;
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
--- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
+++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.ts
@@ -281,7 +588,7 @@ diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_
{
route: routes.SITE_SETTINGS_ADS,
id: Id.ADS,
-@@ -371,10 +371,34 @@ function getCategoryItemMap(): Map {
+@@ -371,10 +371,36 @@ function getCategoryItemMap(): Map {
disabledLabel: 'trackingProtectionLinkRowSubLabel',
});
}
@@ -289,14 +596,16 @@ diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_
+ let obj = JSON.parse(loadTimeData.getString("br_cs_" + index));
+ let name = obj["name"];
+
-+ categoryList.push({
-+ route: (routes as any)[`SITE_SETTINGS_${name.toUpperCase()}`],
-+ id: name,
-+ label: `brSiteSettings${name}`,
-+ icon: `br-settings:${name}`,
-+ enabledLabel: `brSiteSettings${name}Allowed`,
-+ disabledLabel: `brSiteSettings${name}Blocked`,
-+ });
++ if (!categoryList.find(x=> x.id == name)) {
++ categoryList.push({
++ route: (routes as any)[`SITE_SETTINGS_${name.toUpperCase()}`],
++ id: name,
++ label: `brSiteSettings${name}`,
++ icon: `br-settings:${name}`,
++ enabledLabel: `brSiteSettings${name}Allowed`,
++ disabledLabel: `brSiteSettings${name}Blocked`,
++ });
++ }
+ }
+
categoryItemMap = new Map(categoryList.map(item => [item.id, item]));
@@ -316,7 +625,7 @@ diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_
function buildItemListFromIds(orderedIdList: ContentSettingsTypes[]):
CategoryListItem[] {
const map = getCategoryItemMap();
-@@ -465,7 +489,7 @@ export class SettingsSiteSettingsPageElement extends
+@@ -465,7 +491,7 @@ export class SettingsSiteSettingsPageElement extends
Id.IMAGES,
Id.POPUPS,
]),
@@ -408,6 +717,92 @@ diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
"//components/enterprise/common:files_scan_data",
"//components/feedback/proto",
"//components/headless/policy",
+diff --git a/chrome/browser/ui/views/controls/rich_controls_container_view.h b/chrome/browser/ui/views/controls/rich_controls_container_view.h
+--- a/chrome/browser/ui/views/controls/rich_controls_container_view.h
++++ b/chrome/browser/ui/views/controls/rich_controls_container_view.h
+@@ -42,6 +42,13 @@ class RichControlsContainerView : public views::FlexLayoutView {
+ return AddChildView(std::move(control_view));
+ }
+
++ template
++ T* AddControlUnderLabel(std::unique_ptr control_view) {
++ control_view->SetProperty(views::kInternalPaddingKey,
++ control_view->GetInsets());
++ return labels_wrapper_->AddChildView(std::move(control_view));
++ }
++
+ int GetFirstLineHeight();
+ gfx::Size FlexRule(const views::View* view,
+ const views::SizeBounds& maximum_size) const;
+diff --git a/chrome/browser/ui/views/page_info/page_info_main_view.cc b/chrome/browser/ui/views/page_info/page_info_main_view.cc
+--- a/chrome/browser/ui/views/page_info/page_info_main_view.cc
++++ b/chrome/browser/ui/views/page_info/page_info_main_view.cc
+@@ -49,6 +49,7 @@
+ #include "ui/views/controls/separator.h"
+ #include "ui/views/layout/box_layout.h"
+ #include "ui/views/layout/flex_layout.h"
++#include "ui/views/layout/table_layout.h"
+ #include "ui/views/view_class_properties.h"
+
+ #if BUILDFLAG(FULL_SAFE_BROWSING)
+@@ -243,8 +244,20 @@ void PageInfoMainView::SetPermissionInfo(
+ scroll_view->SetDrawOverflowIndicator(false);
+ auto* content_view =
+ scroll_view->SetContents(std::make_unique());
+- content_view->SetLayoutManager(std::make_unique())
+- ->SetOrientation(views::LayoutOrientation::kVertical);
++
++ views::TableLayout* table_layout =
++ content_view->SetLayoutManager(std::make_unique());
++ table_layout->AddColumn(
++ views::LayoutAlignment::kStretch, views::LayoutAlignment::kStretch,
++ views::TableLayout::kFixedSize,
++ views::TableLayout::ColumnSize::kFixed,
++ /*fixed_width=*/250, /*min_width=*/0)
++ .AddColumn(
++ views::LayoutAlignment::kStretch, views::LayoutAlignment::kStretch,
++ views::TableLayout::kFixedSize,
++ views::TableLayout::ColumnSize::kFixed,
++ /*fixed_width=*/250, /*min_width=*/0)
++ .AddRows(/*n=*/1, views::TableLayout::kFixedSize);
+ content_view->SetID(PageInfoViewFactory::VIEW_ID_PAGE_INFO_PERMISSION_VIEW);
+ content_view->SetProperty(views::kElementIdentifierKey,
+ kPermissionsElementId);
+@@ -259,17 +272,24 @@ void PageInfoMainView::SetPermissionInfo(
+ }
+ }
+
++ int count = 0;
+ for (const auto& permission : permission_info_list) {
++ if (++count % 2 == 0)
++ table_layout->AddRows(/*n=*/1, views::TableLayout::kFixedSize);
+ auto* toggle_row =
+ content_view->AddChildView(std::make_unique(
+ ui_delegate_, navigation_handler_, permission, should_show_spacer));
+ toggle_row->AddObserver(this);
++ toggle_row->SetProperty(views::kMarginsKey,
++ gfx::Insets::TLBR(0, 0, 0, 0));
+ toggle_row->SetProperty(views::kCrossAxisAlignmentKey,
+ views::LayoutAlignment::kStretch);
+ toggle_rows_.push_back(std::move(toggle_row));
+ }
+
+ for (auto& object : chosen_object_info_list) {
++ if (++count % 2 == 0)
++ table_layout->AddRows(/*n=*/1, views::TableLayout::kFixedSize);
+ // The view takes ownership of the object info.
+ auto object_view = std::make_unique(
+ std::move(object),
+@@ -280,6 +300,9 @@ void PageInfoMainView::SetPermissionInfo(
+ content_view->AddChildView(std::move(object_view)));
+ }
+
++ if (++count % 2 == 0)
++ content_view->AddChildView(std::make_unique());
++
+ const int controls_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric(
+ views::DISTANCE_RELATED_CONTROL_VERTICAL);
+ reset_button_ = content_view->AddChildView(
diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
--- a/chrome/browser/ui/views/page_info/page_info_view_factory.cc
+++ b/chrome/browser/ui/views/page_info/page_info_view_factory.cc
@@ -420,7 +815,21 @@ diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrom
#include "components/page_info/core/features.h"
#include "components/page_info/core/proto/about_this_site_metadata.pb.h"
#include "components/page_info/page_info.h"
-@@ -507,6 +509,17 @@ const ui::ImageModel PageInfoViewFactory::GetPermissionIcon(
+@@ -253,7 +255,13 @@ const ui::ImageModel PageInfoViewFactory::GetPermissionIcon(
+ ContentSetting setting = info.setting == CONTENT_SETTING_DEFAULT
+ ? info.default_setting
+ : info.setting;
++ return GetPermissionIcon(info, setting);
++}
+
++// static
++const ui::ImageModel PageInfoViewFactory::GetPermissionIcon(
++ const PageInfo::PermissionInfo& info,
++ ContentSetting setting) {
+ // For guard content settings and Automatic Picture-in-Picture, ASK is treated
+ // as an "on" state.
+ const bool show_blocked_badge =
+@@ -507,6 +515,17 @@ const ui::ImageModel PageInfoViewFactory::GetPermissionIcon(
icon = &vector_icons::kPictureInPictureIcon;
break;
default:
@@ -438,6 +847,287 @@ diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.cc b/chrom
// All other |ContentSettingsType|s do not have icons on desktop or are
// not shown in the Page Info bubble.
NOTREACHED_NORETURN();
+diff --git a/chrome/browser/ui/views/page_info/page_info_view_factory.h b/chrome/browser/ui/views/page_info/page_info_view_factory.h
+--- a/chrome/browser/ui/views/page_info/page_info_view_factory.h
++++ b/chrome/browser/ui/views/page_info/page_info_view_factory.h
+@@ -83,6 +83,9 @@ class PageInfoViewFactory {
+ static const ui::ImageModel GetPermissionIcon(
+ const PageInfo::PermissionInfo& info);
+
++ static const ui::ImageModel GetPermissionIcon(
++ const PageInfo::PermissionInfo& info, ContentSetting setting);
++
+ // Returns the icon for the given object |info|.
+ static const ui::ImageModel GetChosenObjectIcon(
+ const PageInfoUI::ChosenObjectInfo& info,
+diff --git a/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc b/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc
+--- a/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc
++++ b/chrome/browser/ui/views/page_info/permission_toggle_row_view.cc
+@@ -35,6 +35,30 @@
+ #include "ui/views/controls/label.h"
+ #include "ui/views/style/typography.h"
+ #include "ui/views/view_class_properties.h"
++#include "components/content_settings/core/browser/content_settings_registry.h"
++
++namespace {
++ bool IsAllowed(const PageInfo::PermissionInfo& permission) {
++ if (permission.setting == CONTENT_SETTING_DEFAULT)
++ return permission.default_setting == CONTENT_SETTING_ALLOW;
++ else
++ return permission.setting == CONTENT_SETTING_ALLOW;
++ }
++
++ bool IsBlocked(const PageInfo::PermissionInfo& permission) {
++ if (permission.setting == CONTENT_SETTING_DEFAULT)
++ return permission.default_setting == CONTENT_SETTING_BLOCK;
++ else
++ return permission.setting == CONTENT_SETTING_BLOCK;
++ }
++
++ bool IsAsk(const PageInfo::PermissionInfo& permission) {
++ if (permission.setting == CONTENT_SETTING_DEFAULT)
++ return permission.default_setting == CONTENT_SETTING_ASK;
++ else
++ return permission.setting == CONTENT_SETTING_ASK;
++ }
++}
+
+ PermissionToggleRowView::PermissionToggleRowView(
+ ChromePageInfoUiDelegate* delegate,
+@@ -48,16 +72,12 @@ PermissionToggleRowView::PermissionToggleRowView(
+ // instead of adding it as the only child.
+ SetUseDefaultFillLayout(true);
+ row_view_ = AddChildView(std::make_unique());
++ row_view_->SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
+
+ std::u16string toggle_accessible_name =
+ PageInfoUI::PermissionTypeToUIString(permission.type);
+ row_view_->SetTitle(toggle_accessible_name);
+
+- // Add extra details as sublabel.
+- std::u16string detail = delegate->GetPermissionDetail(permission.type);
+- if (!detail.empty())
+- row_view_->AddSecondaryLabel(detail);
+-
+ if (permission.requesting_origin.has_value()) {
+ std::u16string requesting_origin_string;
+ switch (permission.type) {
+@@ -115,6 +135,90 @@ void PermissionToggleRowView::PermissionChanged() {
+ }
+ }
+
++void PermissionToggleRowView::OnShowOptionsMenu() {
++ sources_menu_runner_.reset();
++
++ ContentSettingsType type = permission_.type;
++ const content_settings::WebsiteSettingsInfo* setting_info =
++ content_settings::WebsiteSettingsRegistry::GetInstance()->Get(type);
++
++ sources_menu_model_ = std::make_unique(this);
++ if (setting_info->allowed_ui() != 0) {
++ sources_menu_model_->AddCheckItemWithStringId(1, setting_info->allowed_ui());
++ } else {
++ sources_menu_model_->AddCheckItem(1, u"Enabled");
++ }
++ sources_menu_model_->SetIcon(sources_menu_model_->GetItemCount() - 1,
++ PageInfoViewFactory::GetPermissionIcon(permission_, CONTENT_SETTING_DEFAULT));
++
++ auto* content_setting_registry =
++ content_settings::ContentSettingsRegistry::GetInstance();
++ auto* content_setting = content_setting_registry->Get(type);
++ if (content_setting->IsSettingValid(CONTENT_SETTING_ASK)) {
++ if (setting_info->ask_ui() != 0) {
++ sources_menu_model_->AddCheckItemWithStringId(3, setting_info->ask_ui());
++ } else {
++ sources_menu_model_->AddCheckItem(3, u"Ask");
++ }
++ sources_menu_model_->SetIcon(sources_menu_model_->GetItemCount() - 1,
++ PageInfoViewFactory::GetPermissionIcon(permission_, CONTENT_SETTING_ASK));
++ }
++
++ if (setting_info->blocked_ui() != 0) {
++ sources_menu_model_->AddCheckItemWithStringId(2, setting_info->blocked_ui());
++ } else {
++ sources_menu_model_->AddCheckItem(2, u"Disabled");
++ }
++ sources_menu_model_->SetIcon(sources_menu_model_->GetItemCount() - 1,
++ PageInfoViewFactory::GetPermissionIcon(permission_, CONTENT_SETTING_BLOCK));
++
++ sources_menu_model_->AddSeparator(ui::NORMAL_SEPARATOR);
++ sources_menu_model_->AddCheckItem(4, u"Default");
++
++ //sources_menu_model_->ActivatedAt(0);
++
++ // const gfx::Point point = choose_button_->GetMenuPosition();
++ // const gfx::Point origin(point.x() - width(), point.y() - height());
++
++ sources_menu_runner_ = std::make_unique(
++ sources_menu_model_.get(), views::MenuRunner::COMBOBOX);
++ sources_menu_runner_->RunMenuAt(
++ choose_button_->GetWidget(), nullptr,
++ choose_button_->GetAnchorBoundsInScreen(),
++ views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE);
++}
++
++bool PermissionToggleRowView::IsCommandIdChecked(int command_id) const {
++ if (permission_.setting == CONTENT_SETTING_DEFAULT && command_id == 4) {
++ return true;
++ } else if (IsAllowed(permission_)) {
++ return command_id == 1;
++ } else if (IsBlocked(permission_)) {
++ return command_id == 2;
++ } else if (IsAsk(permission_)) {
++ return command_id == 3;
++ }
++ return false;
++}
++
++void PermissionToggleRowView::ExecuteCommand(int command_id, int event_flags) {
++ switch(command_id) {
++ case 1:
++ permission_.setting = CONTENT_SETTING_ALLOW;
++ break;
++ case 2:
++ permission_.setting = CONTENT_SETTING_BLOCK;
++ break;
++ case 3:
++ permission_.setting = CONTENT_SETTING_ASK;
++ break;
++ case 4:
++ permission_.setting = CONTENT_SETTING_DEFAULT;
++ break;
++ }
++ PermissionChanged();
++}
++
+ void PermissionToggleRowView::OnToggleButtonPressed() {
+ PageInfoUI::ToggleBetweenAllowAndBlock(permission_);
+ PermissionChanged();
+@@ -123,24 +227,19 @@ void PermissionToggleRowView::OnToggleButtonPressed() {
+ void PermissionToggleRowView::InitForUserSource(
+ bool should_show_spacer_view,
+ const std::u16string& toggle_accessible_name) {
+- const int icon_label_spacing = ChromeLayoutProvider::Get()->GetDistanceMetric(
+- views::DISTANCE_RELATED_LABEL_HORIZONTAL);
+-
+- auto toggle_button = std::make_unique(
+- base::BindRepeating(&PermissionToggleRowView::OnToggleButtonPressed,
+- base::Unretained(this)));
++ auto toggle_button = std::make_unique(
++ base::BindRepeating(&PermissionToggleRowView::OnShowOptionsMenu,
++ base::Unretained(this)),
++ u"");
++ toggle_button->SetStyle(ui::ButtonStyle::kText);
++ toggle_button->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+ toggle_button->SetID(
+ PageInfoViewFactory::VIEW_ID_PERMISSION_TOGGLE_ROW_TOGGLE_BUTTON);
+- toggle_button->SetPreferredSize(
+- gfx::Size(toggle_button->GetPreferredSize().width(),
+- row_view_->GetFirstLineHeight()));
+- toggle_button->SetProperty(views::kMarginsKey,
+- gfx::Insets::VH(0, icon_label_spacing));
+ toggle_button->SetTooltipText(PageInfoUI::PermissionTooltipUiString(
+ permission_.type, permission_.requesting_origin));
+ toggle_button->SetAccessibleName(toggle_accessible_name);
+
+- toggle_button_ = row_view_->AddControl(std::move(toggle_button));
++ choose_button_ = row_view_->AddControlUnderLabel(std::move(toggle_button));
+
+ const int icon_size = GetLayoutConstant(PAGE_INFO_ICON_SIZE);
+ // TODO(crbug.com/1011533): Remove separate handling of
+@@ -187,9 +286,6 @@ void PermissionToggleRowView::InitForUserSource(
+ auto spacer_view = std::make_unique();
+ spacer_view->SetPreferredSize(gfx::Size(icon_size, icon_size));
+ spacer_view_ = row_view_->AddControl(std::move(spacer_view));
+- } else {
+- toggle_button_->SetProperty(
+- views::kMarginsKey, gfx::Insets::TLBR(0, icon_label_spacing, 0, 0));
+ }
+ }
+ }
+@@ -245,6 +341,33 @@ void PermissionToggleRowView::UpdateUiOnPermissionChanged() {
+ state_label_ = row_view_->AddSecondaryLabel(state_text);
+ }
+ }
++
++ if (choose_button_) {
++ ContentSettingsType type = permission_.type;
++ const content_settings::WebsiteSettingsInfo* setting_info =
++ content_settings::WebsiteSettingsRegistry::GetInstance()->Get(type);
++
++ std::u16string caption;
++ if (IsAllowed(permission_)) {
++ if (setting_info->allowed_ui() != 0)
++ caption = l10n_util::GetStringUTF16(setting_info->allowed_ui());
++ else
++ caption = u"Allowed";
++ } else if (IsBlocked(permission_)) {
++ if (setting_info->blocked_ui() != 0)
++ caption = l10n_util::GetStringUTF16(setting_info->blocked_ui());
++ else
++ caption = u"Blocked";
++ } else if (IsAsk(permission_)) {
++ if (setting_info->ask_ui() != 0)
++ caption = l10n_util::GetStringUTF16(setting_info->ask_ui());
++ else
++ caption = u"Ask";
++ } else {
++ caption = u"Unknown";
++ }
++ choose_button_->SetText(caption);
++ }
+ }
+
+ void PermissionToggleRowView::ResetPermission() {
+diff --git a/chrome/browser/ui/views/page_info/permission_toggle_row_view.h b/chrome/browser/ui/views/page_info/permission_toggle_row_view.h
+--- a/chrome/browser/ui/views/page_info/permission_toggle_row_view.h
++++ b/chrome/browser/ui/views/page_info/permission_toggle_row_view.h
+@@ -14,6 +14,11 @@
+ #include "ui/base/metadata/metadata_header_macros.h"
+ #include "ui/views/view.h"
+
++#include "components/content_settings/core/browser/website_settings_registry.h"
++#include "chrome/browser/ui/views/controls/md_text_button_with_down_arrow.h"
++#include "ui/base/models/simple_menu_model.h"
++#include "ui/views/controls/menu/menu_runner.h"
++
+ class ChromePageInfoUiDelegate;
+ class PageInfoNavigationHandler;
+
+@@ -29,7 +34,8 @@ class PageInfoBubbleViewTestApi;
+ // A view that shows a permission that a site is able to access, and
+ // allows the user to control via toggle whether that access is granted. Has a
+ // button that opens a subpage with more controls.
+-class PermissionToggleRowView : public views::View {
++class PermissionToggleRowView : public views::View,
++ public ui::SimpleMenuModel::Delegate {
+ public:
+ METADATA_HEADER(PermissionToggleRowView);
+ PermissionToggleRowView(ChromePageInfoUiDelegate* delegate,
+@@ -52,6 +58,12 @@ class PermissionToggleRowView : public views::View {
+ private:
+ friend class test::PageInfoBubbleViewTestApi;
+
++ void OnShowOptionsMenu();
++
++ // ui::SimpleMenuModel::Delegate overrides:
++ bool IsCommandIdChecked(int command_id) const override;
++ void ExecuteCommand(int command_id, int event_flags) override;
++
+ void OnToggleButtonPressed();
+ void InitForUserSource(bool should_show_spacer_view,
+ const std::u16string& toggle_accessible_name);
+@@ -65,6 +77,10 @@ class PermissionToggleRowView : public views::View {
+ raw_ptr toggle_button_ = nullptr;
+ raw_ptr spacer_view_ = nullptr;
+
++ raw_ptr choose_button_ = nullptr;
++ std::unique_ptr sources_menu_model_;
++ std::unique_ptr sources_menu_runner_;
++
+ raw_ptr delegate_ = nullptr;
+ raw_ptr navigation_handler_ =
+ nullptr;
diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
--- a/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/settings_localized_strings_provider.cc
@@ -449,34 +1139,47 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide
#include "build/branding_buildflags.h"
#include "build/build_config.h"
#include "build/buildflag.h"
-@@ -72,6 +73,8 @@
+@@ -72,6 +73,9 @@
#include "components/google/core/common/google_util.h"
#include "components/history/core/common/pref_names.h"
#include "components/omnibox/common/omnibox_features.h"
++#include "components/content_settings/core/browser/content_settings_registry.h"
+#include "components/content_settings/core/browser/website_settings_info.h"
+#include "components/content_settings/core/browser/website_settings_registry.h"
#include "components/password_manager/core/browser/leak_detection_dialog_utils.h"
#include "components/password_manager/core/browser/manage_passwords_referrer.h"
#include "components/password_manager/core/common/password_manager_features.h"
-@@ -3628,6 +3631,42 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source,
+@@ -3628,6 +3632,56 @@ void AddLocalizedStrings(content::WebUIDataSource* html_source,
policy_indicator::AddLocalizedStrings(html_source);
AddSecurityKeysStrings(html_source);
+ content_settings::WebsiteSettingsRegistry* website_settings =
+ content_settings::WebsiteSettingsRegistry::GetInstance();
++ auto* content_setting_registry =
++ content_settings::ContentSettingsRegistry::GetInstance();
+ int index = 0;
+ for (const content_settings::WebsiteSettingsInfo* info : *website_settings) {
+ if (info->desktop_ui()) {
++ auto* content_setting = content_setting_registry->Get(info->type());
+ std::string name = info->name();
+ std::string prefix = "brSiteSettings" + name;
+
-+ html_source->AddLocalizedString(prefix, info->title_ui());
-+ html_source->AddLocalizedString(prefix + "Description", info->description_ui());
-+ html_source->AddLocalizedString(prefix + "Allowed", info->allowed_ui());
-+ html_source->AddLocalizedString(prefix + "Blocked", info->blocked_ui());
-+ html_source->AddLocalizedString(prefix + "AllowedExceptions", info->allowed_exceptions_ui());
-+ html_source->AddLocalizedString(prefix + "BlockedExceptions", info->blocked_exceptions_ui());
-+ html_source->AddLocalizedString(prefix + "MidSentence", info->mid_sentence_ui());
++ if (info->title_ui())
++ html_source->AddLocalizedString(prefix, info->title_ui());
++ if (info->description_ui())
++ html_source->AddLocalizedString(prefix + "Description", info->description_ui());
++ if (info->allowed_ui())
++ html_source->AddLocalizedString(prefix + "Allowed", info->allowed_ui());
++ if (info->blocked_ui())
++ html_source->AddLocalizedString(prefix + "Blocked", info->blocked_ui());
++ if (info->ask_ui())
++ html_source->AddLocalizedString(prefix + "Ask", info->ask_ui());
++ if (info->allowed_exceptions_ui())
++ html_source->AddLocalizedString(prefix + "AllowedExceptions", info->allowed_exceptions_ui());
++ if (info->blocked_exceptions_ui())
++ html_source->AddLocalizedString(prefix + "BlockedExceptions", info->blocked_exceptions_ui());
++ if (info->mid_sentence_ui())
++ html_source->AddLocalizedString(prefix + "MidSentence", info->mid_sentence_ui());
+
+ base::Value::Dict dict;
+ dict.Set("name", name);
@@ -486,6 +1189,8 @@ diff --git a/chrome/browser/ui/webui/settings/settings_localized_strings_provide
+ info->initial_default_value().GetInt() == (int)CONTENT_SETTING_ALLOW ? "allow" :
+ info->initial_default_value().GetInt() == (int)CONTENT_SETTING_BLOCK ? "block" :
+ "ask");
++ dict.Set("allowed_ask",
++ content_setting->IsSettingValid(CONTENT_SETTING_ASK) ? "1" : "0");
+
+ std::string json_string;
+ base::JSONWriter::WriteWithOptions(
@@ -584,6 +1289,169 @@ diff --git a/chrome/browser/ui/webui/settings/site_settings_helper.cc b/chrome/b
initialized = true;
}
+diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
+--- a/components/browser_ui/settings/android/BUILD.gn
++++ b/components/browser_ui/settings/android/BUILD.gn
+@@ -82,6 +82,7 @@ android_resources("java_resources") {
+ "java/res/layout/managed_disclaimer_preference_for_radio_groups.xml",
+ "java/res/layout/preference_chrome_image_view.xml",
+ "java/res/layout/preference_spinner.xml",
++ "java/res/layout/preference_spinner_single_widget.xml",
+ "java/res/layout/preference_spinner_single_line.xml",
+ "java/res/layout/preference_spinner_single_line_item.xml",
+ "java/res/layout/settings_action_bar_shadow.xml",
+diff --git a/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_widget.xml b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_widget.xml
+new file mode 100644
+--- /dev/null
++++ b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_widget.xml
+@@ -0,0 +1,92 @@
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
++
+diff --git a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
+--- a/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
++++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
+@@ -23,6 +23,7 @@ public class SpinnerPreference extends Preference {
+ private ArrayAdapter