From: uazo Date: Wed, 30 Sep 2020 07:40:01 +0000 Subject: Timezone customization Allow specifying a custom timezone, or using a random one. See also: https://github.com/bromite/bromite/wiki/TimezoneOverride --- .../ChromeSiteSettingsDelegate.java | 16 ++ .../browser_ui/site_settings/android/BUILD.gn | 3 + .../res/layout/time_zone_select_dialog.xml | 36 ++++ ...ezoneoverride_site_settings_preference.xml | 68 ++++++ .../res/xml/site_settings_preferences.xml | 4 + .../java/res/xml/website_preferences.xml | 9 + .../ContentSettingsResources.java | 35 +++- .../site_settings/SingleCategorySettings.java | 77 ++++++- .../site_settings/SingleWebsiteSettings.java | 8 +- .../site_settings/SiteSettings.java | 2 +- .../site_settings/SiteSettingsCategory.java | 9 +- .../site_settings/SiteSettingsDelegate.java | 2 + ...imezoneOverrideSiteSettingsPreference.java | 193 ++++++++++++++++++ .../browser_ui/site_settings/Website.java | 10 + .../WebsitePermissionsFetcher.java | 3 + .../WebsitePreferenceBridge.java | 12 ++ .../android/website_preference_bridge.cc | 16 ++ .../strings/android/site_settings.grdp | 35 ++++ .../browser/content_settings_pref_provider.cc | 16 ++ .../browser/content_settings_pref_provider.h | 4 + .../core/browser/content_settings_registry.cc | 12 ++ .../core/browser/content_settings_utils.cc | 7 + .../core/browser/host_content_settings_map.cc | 8 + .../core/browser/host_content_settings_map.h | 3 + .../core/common/content_settings.cc | 3 +- .../core/common/content_settings.h | 2 + .../core/common/content_settings.mojom | 2 + .../common/content_settings_mojom_traits.cc | 4 +- .../common/content_settings_mojom_traits.h | 10 + .../core/common/content_settings_types.h | 3 + .../core/common/pref_names.cc | 3 + .../content_settings/core/common/pref_names.h | 2 + .../renderer/content_settings_agent_impl.cc | 89 ++++++++ .../renderer/content_settings_agent_impl.h | 4 + .../WebLayerSiteSettingsDelegate.java | 3 + 35 files changed, 698 insertions(+), 15 deletions(-) create mode 100755 components/browser_ui/site_settings/android/java/res/layout/time_zone_select_dialog.xml create mode 100755 components/browser_ui/site_settings/android/java/res/layout/timezoneoverride_site_settings_preference.xml create mode 100755 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java --- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsDelegate.java @@ -43,6 +43,10 @@ import org.chromium.url.GURL; import java.util.Set; +import android.content.Intent; +import android.provider.Browser; +import android.net.Uri; + /** * A SiteSettingsDelegate instance that contains Chrome-specific Site Settings logic. */ @@ -257,4 +261,16 @@ public class ChromeSiteSettingsDelegate implements SiteSettingsDelegate { mPrivacySandboxController.dismissSnackbar(); } } + + // open wiki page for documentation about the timezone override feature + @Override + public void launchTimeZoneOverrideHelpAndFeedbackActivity(Activity currentActivity) { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/bromite/bromite/wiki/TimezoneOverride")); + // Let Chromium know that this intent is from Chromium, so that it does not close the app when + // the user presses 'back' button. + intent.putExtra(Browser.EXTRA_APPLICATION_ID, currentActivity.getPackageName()); + intent.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); + intent.setPackage(currentActivity.getPackageName()); + currentActivity.startActivity(intent); + } } diff --git a/components/browser_ui/site_settings/android/BUILD.gn b/components/browser_ui/site_settings/android/BUILD.gn --- a/components/browser_ui/site_settings/android/BUILD.gn +++ b/components/browser_ui/site_settings/android/BUILD.gn @@ -66,6 +66,7 @@ android_library("java") { "java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java", "java/src/org/chromium/components/browser_ui/site_settings/WebsitePreference.java", "java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java", + "java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java" ] annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] resources_package = "org.chromium.components.browser_ui.site_settings" @@ -162,6 +163,8 @@ android_resources("java_resources") { "java/res/xml/single_website_preferences.xml", "java/res/xml/site_settings_preferences.xml", "java/res/xml/website_preferences.xml", + "java/res/layout/timezoneoverride_site_settings_preference.xml", + "java/res/layout/time_zone_select_dialog.xml", ] deps = [ diff --git a/components/browser_ui/site_settings/android/java/res/layout/time_zone_select_dialog.xml b/components/browser_ui/site_settings/android/java/res/layout/time_zone_select_dialog.xml new file mode 100755 --- /dev/null +++ b/components/browser_ui/site_settings/android/java/res/layout/time_zone_select_dialog.xml @@ -0,0 +1,36 @@ + + + + + + + + + + \ No newline at end of file diff --git a/components/browser_ui/site_settings/android/java/res/layout/timezoneoverride_site_settings_preference.xml b/components/browser_ui/site_settings/android/java/res/layout/timezoneoverride_site_settings_preference.xml new file mode 100755 --- /dev/null +++ b/components/browser_ui/site_settings/android/java/res/layout/timezoneoverride_site_settings_preference.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + diff --git a/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml --- a/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml +++ b/components/browser_ui/site_settings/android/java/res/xml/site_settings_preferences.xml @@ -41,6 +41,10 @@ + + + + + + + + 0; return getString(resource); @@ -629,6 +650,13 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment if (mCategory.showSites(SiteSettingsCategory.Type.COOKIES) && mRequiresFourStateSetting) { setting = cookieSettingsExceptionShouldBlock() ? ContentSettingValues.BLOCK : ContentSettingValues.ALLOW; + } else if (mRequiresTriStateSetting) { + setting = WebsitePreferenceBridge.getDefaultContentSetting(browserContextHandle, mCategory.getContentSettingsType()); + if (setting == ContentSettingValues.ALLOW) { + setting = ContentSettingValues.BLOCK; + } else { + setting = ContentSettingValues.ALLOW; + } } else { setting = (WebsitePreferenceBridge.isCategoryEnabled( browserContextHandle, mCategory.getContentSettingsType())) @@ -685,6 +713,8 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment && !WebsitePreferenceBridge.isCategoryEnabled( browserContextHandle, ContentSettingsType.AUTOMATIC_DOWNLOADS)) { allowSpecifyingExceptions = true; + } else if (mCategory.showSites(SiteSettingsCategory.Type.TIMEZONE_OVERRIDE)) { + allowSpecifyingExceptions = true; } if (allowSpecifyingExceptions) { getPreferenceScreen().addPreference(new AddExceptionPreference(getStyledContext(), @@ -856,7 +886,14 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment TriStateSiteSettingsPreference triStateToggle = (TriStateSiteSettingsPreference) getPreferenceScreen().findPreference( TRI_STATE_TOGGLE_KEY); - return (triStateToggle.getCheckedSetting() == ContentSettingValues.BLOCK); + if (triStateToggle != null) + return (triStateToggle.getCheckedSetting() == ContentSettingValues.BLOCK); + + TimezoneOverrideSiteSettingsPreference timeOverrideStatePreference = + (TimezoneOverrideSiteSettingsPreference) getPreferenceScreen().findPreference( + TIMEOVERRIDE_STATE_TOGGLE_KEY); + if (timeOverrideStatePreference != null) + return (timeOverrideStatePreference.getCheckedSetting() != ContentSettingValues.ALLOW); } else if (mRequiresFourStateSetting) { FourStateCookieSettingsPreference fourStateCookieToggle = (FourStateCookieSettingsPreference) getPreferenceScreen().findPreference( @@ -885,6 +922,9 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment (FourStateCookieSettingsPreference) screen.findPreference( FOUR_STATE_COOKIE_TOGGLE_KEY); // TODO(crbug.com/1104836): Remove the old third-party cookie blocking UI + TimezoneOverrideSiteSettingsPreference timeOverrideStatePreference = + (TimezoneOverrideSiteSettingsPreference) screen.findPreference( + TIMEOVERRIDE_STATE_TOGGLE_KEY); Preference notificationsVibrate = screen.findPreference(NOTIFICATIONS_VIBRATE_TOGGLE_KEY); Preference notificationsQuietUi = screen.findPreference(NOTIFICATIONS_QUIET_UI_TOGGLE_KEY); Preference explainProtectedMediaKey = screen.findPreference(EXPLAIN_PROTECTED_MEDIA_KEY); @@ -896,20 +936,32 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment if (mRequiresTriStateSetting) { screen.removePreference(binaryToggle); screen.removePreference(fourStateCookieToggle); - configureTriStateToggle(triStateToggle, contentType); + if (mCategory.showSites(SiteSettingsCategory.Type.TIMEZONE_OVERRIDE)) { + screen.removePreference(triStateToggle); + configureTimeOverrideStateToggle(timeOverrideStatePreference); + } + else { + screen.removePreference(timeOverrideStatePreference); + configureTriStateToggle(triStateToggle, contentType); + } } else if (mRequiresFourStateSetting) { screen.removePreference(binaryToggle); screen.removePreference(triStateToggle); + screen.removePreference(timeOverrideStatePreference); configureFourStateCookieToggle(fourStateCookieToggle); } else { screen.removePreference(triStateToggle); screen.removePreference(fourStateCookieToggle); + screen.removePreference(timeOverrideStatePreference); configureBinaryToggle(binaryToggle, contentType); } if (!mCategory.showSites(SiteSettingsCategory.Type.COOKIES)) { screen.removePreference(screen.findPreference(COOKIE_INFO_TEXT_KEY)); } + if (!(mCategory.showSites(SiteSettingsCategory.Type.TIMEZONE_OVERRIDE))) { + screen.removePreference(screen.findPreference(TIMEOVERRIDE_INFO_TEXT)); + } if (permissionBlockedByOs) { maybeShowOsWarning(screen); @@ -1025,6 +1077,15 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment triStateToggle.initialize(setting, descriptionIds); } + private void configureTimeOverrideStateToggle( + TimezoneOverrideSiteSettingsPreference timeOverrideStateToggle) { + timeOverrideStateToggle.setOnPreferenceChangeListener(this); + @ContentSettingValues + int setting = WebsitePreferenceBridge.getDefaultContentSetting( + getSiteSettingsDelegate().getBrowserContextHandle(), ContentSettingsType.TIMEZONE_OVERRIDE); + timeOverrideStateToggle.initialize(setting, getSiteSettingsDelegate().getBrowserContextHandle()); + } + private void configureBinaryToggle(ChromeSwitchPreference binaryToggle, int contentType) { binaryToggle.setOnPreferenceChangeListener(this); binaryToggle.setTitle(ContentSettingsResources.getTitle(contentType)); @@ -1108,9 +1169,11 @@ public class SingleCategorySettings extends SiteSettingsPreferenceFragment CharSequence[] descriptions = new String[2]; descriptions[0] = - getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.ALLOW)); + getString(ContentSettingsResources.getSiteSummary(contentSettingsType, + ContentSettingValues.ALLOW)); descriptions[1] = - getString(ContentSettingsResources.getSiteSummary(ContentSettingValues.BLOCK)); + getString(ContentSettingsResources.getSiteSummary(contentSettingsType, + ContentSettingValues.BLOCK)); return new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog) .setPositiveButton(R.string.cancel, null) diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SingleWebsiteSettings.java @@ -132,6 +132,8 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment return "popup_permission_list"; case ContentSettingsType.SOUND: return "sound_permission_list"; + case ContentSettingsType.TIMEZONE_OVERRIDE: + return "timezone_override_permission_list"; case ContentSettingsType.AR: return "ar_permission_list"; case ContentSettingsType.MEDIASTREAM_CAMERA: @@ -884,11 +886,13 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment if (value == null) return; setUpPreferenceCommon(preference, value); + int content_type = getContentSettingsTypeFromPreferenceKey(preference.getKey()); + ChromeSwitchPreference switchPreference = (ChromeSwitchPreference) preference; switchPreference.setChecked(value == ContentSettingValues.ALLOW); switchPreference.setSummary(isEmbargoed ? getString(R.string.automatically_blocked) - : getString(ContentSettingsResources.getCategorySummary(value))); + : getString(ContentSettingsResources.getCategorySummary(content_type, value))); switchPreference.setOnPreferenceChangeListener(this); @ContentSettingsType int contentType = getContentSettingsTypeFromPreferenceKey(preference.getKey()); @@ -1080,7 +1084,7 @@ public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment } mSite.setContentSetting(browserContextHandle, type, permission); - preference.setSummary(getString(ContentSettingsResources.getCategorySummary(permission))); + preference.setSummary(getString(ContentSettingsResources.getCategorySummary(type, permission))); preference.setIcon(getContentSettingsIcon(type, permission)); if (mWebsiteSettingsObserver != null) { diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettings.java @@ -117,7 +117,7 @@ public class SiteSettings } else if (Type.AUTO_DARK_WEB_CONTENT == prefCategory) { p.setSummary(ContentSettingsResources.getAutoDarkWebContentListSummary(checked)); } else if (requiresTriStateSetting) { - p.setSummary(ContentSettingsResources.getCategorySummary(setting)); + p.setSummary(ContentSettingsResources.getCategorySummary(contentType, setting)); } else { p.setSummary(ContentSettingsResources.getCategorySummary(contentType, checked)); } diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsCategory.java @@ -43,7 +43,7 @@ public class SiteSettingsCategory { Type.JAVASCRIPT, Type.MICROPHONE, Type.NFC, Type.NOTIFICATIONS, Type.POPUPS, Type.PROTECTED_MEDIA, Type.SENSORS, Type.SOUND, Type.USB, Type.VIRTUAL_REALITY, Type.USE_STORAGE, Type.AUTO_DARK_WEB_CONTENT, Type.REQUEST_DESKTOP_SITE, - Type.FEDERATED_IDENTITY_API}) + Type.FEDERATED_IDENTITY_API, Type.TIMEZONE_OVERRIDE}) @Retention(RetentionPolicy.SOURCE) public @interface Type { // All updates here must also be reflected in {@link #preferenceKey(int) @@ -74,10 +74,11 @@ public class SiteSettingsCategory { int AUTO_DARK_WEB_CONTENT = 23; int REQUEST_DESKTOP_SITE = 24; int FEDERATED_IDENTITY_API = 25; + int TIMEZONE_OVERRIDE = 26; /** * Number of handled categories used for calculating array sizes. */ - int NUM_ENTRIES = 26; + int NUM_ENTRIES = 27; } private final BrowserContextHandle mBrowserContextHandle; @@ -202,6 +203,8 @@ public class SiteSettingsCategory { return ContentSettingsType.USB_GUARD; case Type.VIRTUAL_REALITY: return ContentSettingsType.VR; + case Type.TIMEZONE_OVERRIDE: + return ContentSettingsType.TIMEZONE_OVERRIDE; // case Type.ALL_SITES // case Type.USE_STORAGE default: @@ -282,6 +285,8 @@ public class SiteSettingsCategory { return "use_storage"; case Type.VIRTUAL_REALITY: return "virtual_reality"; + case Type.TIMEZONE_OVERRIDE: + return "timezone_override"; default: assert false; return ""; diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/SiteSettingsDelegate.java @@ -124,4 +124,6 @@ public interface SiteSettingsDelegate { * Dismisses the Privacy Sandbox snackbar, if active. */ void dismissPrivacySandboxSnackbar(); + + void launchTimeZoneOverrideHelpAndFeedbackActivity(Activity currentActivity); } diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java new file mode 100755 --- /dev/null +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java @@ -0,0 +1,193 @@ +/* + This file is part of Bromite. + + Bromite is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Bromite is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Bromite. If not, see . +*/ + +package org.chromium.components.browser_ui.site_settings; + +import org.chromium.base.Log; +import android.content.Context; +import android.content.Intent; +import android.content.DialogInterface; +import android.view.View; +import android.widget.RadioGroup; +import android.widget.Button; +import android.widget.TextView; +import android.util.AttributeSet; +import android.widget.ArrayAdapter; +import android.widget.ListView; +import android.view.LayoutInflater; +import android.widget.AdapterView; +import android.graphics.Color; + +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; +import androidx.appcompat.app.AlertDialog; + +import org.chromium.content_public.browser.BrowserContextHandle; +import org.chromium.components.content_settings.ContentSettingValues; +import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; +import org.chromium.components.browser_ui.widget.RadioButtonWithDescription; +import org.chromium.components.browser_ui.widget.RadioButtonWithEditText; +import org.chromium.components.browser_ui.widget.TintedDrawable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.TimeZone; + +/** + * TimezoneOverride Preference for SiteSettings. + */ +public class TimezoneOverrideSiteSettingsPreference + extends Preference implements RadioGroup.OnCheckedChangeListener, + RadioButtonWithEditText.OnTextChangeListener { + private @ContentSettingValues int mSetting = ContentSettingValues.DEFAULT; + private RadioButtonWithDescription mAllowed; + private RadioButtonWithEditText mAsk; + private RadioButtonWithDescription mBlocked; + private RadioGroup mRadioGroup; + private TextView mSelectButton; + + private String currentSelected; + + private BrowserContextHandle mBrowserContextHandle; + + public TimezoneOverrideSiteSettingsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + setLayoutResource(R.layout.timezoneoverride_site_settings_preference); + setSelectable(false); + } + + public void initialize(@ContentSettingValues int setting, BrowserContextHandle browserContextHandle) { + mSetting = setting; + mBrowserContextHandle = browserContextHandle; + } + + public @ContentSettingValues int getCheckedSetting() { + return mSetting; + } + + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + if (mAllowed.isChecked()) { + mSetting = ContentSettingValues.ALLOW; + } else if (mAsk.isChecked()) { + mSetting = ContentSettingValues.ASK; + } else if (mBlocked.isChecked()) { + mSetting = ContentSettingValues.BLOCK; + } + + callChangeListener(mSetting); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + mAllowed = (RadioButtonWithDescription) holder.findViewById(R.id.allowed); + mAsk = (RadioButtonWithEditText) holder.findViewById(R.id.ask); + mBlocked = (RadioButtonWithDescription) holder.findViewById(R.id.blocked); + mRadioGroup = (RadioGroup) holder.findViewById(R.id.radio_button_layout); + mRadioGroup.setOnCheckedChangeListener(this); + + mAsk.setPrimaryText(WebsitePreferenceBridge.getCustomTimezone(mBrowserContextHandle)); + mAsk.addTextChangeListener(this); + + ListView listView = (ListView)holder.findViewById(R.id.listView); + + mSelectButton = (TextView) holder.findViewById(R.id.select_button); + mSelectButton.setOnClickListener(view -> { + showSelectTimeZoneDialog(); + }); + + RadioButtonWithDescription radioButton = findRadioButton(mSetting); + if (radioButton != null) radioButton.setChecked(true); + } + + private RadioButtonWithDescription findRadioButton(@ContentSettingValues int setting) { + if (setting == ContentSettingValues.ALLOW) { + return mAllowed; + } else if (setting == ContentSettingValues.ASK) { + return mAsk; + } else if (setting == ContentSettingValues.BLOCK) { + return mBlocked; + } else { + return null; + } + } + + public void onTextChanged(CharSequence newText) { + WebsitePreferenceBridge.setCustomTimezone(mBrowserContextHandle, newText.toString()); + } + + private void showSelectTimeZoneDialog() { + LayoutInflater inflater = + (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.time_zone_select_dialog, null); + + ListView listView = view.findViewById(R.id.listView); + ArrayList timezones = new ArrayList<>(Arrays.asList(TimeZone.getAvailableIDs())); + ArrayAdapter adapter = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1, android.R.id.text1, timezones); + listView.setAdapter(adapter); + + currentSelected = String.valueOf(mAsk.getPrimaryText()); + listView.post(new Runnable() + { + public void run() + { + for (int j = 0; j < timezones.size(); j++) { + if (currentSelected.equals(timezones.get(j))) { + listView.requestFocusFromTouch(); + listView.setSelection(j); + adapter.notifyDataSetChanged(); + break; + } + } + } + }); + + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView adapterView, View view, int i, long l) { + currentSelected = timezones.get(i); + listView.setSelected(true); + } + }); + + DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int button) { + if (button == AlertDialog.BUTTON_POSITIVE) { + mAsk.setPrimaryText(currentSelected); + } else { + dialog.dismiss(); + } + } + }; + + AlertDialog.Builder alert = + new AlertDialog.Builder(getContext(), R.style.Theme_Chromium_AlertDialog); + AlertDialog alertDialog = + alert.setTitle(R.string.website_settings_select_dialog_title) + .setView(view) + .setPositiveButton( + R.string.website_settings_select_dialog_button, onClickListener) + .setNegativeButton(R.string.cancel, onClickListener) + .create(); + alertDialog.getDelegate().setHandleNativeActionModesEnabled(false); + alertDialog.show(); + } +} diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/Website.java @@ -229,6 +229,16 @@ public final class Website implements Serializable { } else { RecordUserAction.record("SoundContentSetting.UnmuteBy.SiteSettings"); } + } else if (type == ContentSettingsType.TIMEZONE_OVERRIDE) { + // It is possible to set the permission without having an existing exception, + // because we can show the ALLOW state even when this permission is set to the + // default. In that case, just set an exception now to ALLOW to enable changing the + // permission. + if (exception == null) { + exception = new ContentSettingException( + ContentSettingsType.TIMEZONE_OVERRIDE, getAddress().getHost(), value, ""); + setContentSettingException(type, exception); + } } // We want to call setContentSetting even after explicitly setting // mContentSettingException above because this will trigger the actual change diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePermissionsFetcher.java @@ -68,6 +68,7 @@ public class WebsitePermissionsFetcher { case ContentSettingsType.POPUPS: case ContentSettingsType.REQUEST_DESKTOP_SITE: case ContentSettingsType.SOUND: + case ContentSettingsType.TIMEZONE_OVERRIDE: return WebsitePermissionsType.CONTENT_SETTING_EXCEPTION; case ContentSettingsType.AR: case ContentSettingsType.CLIPBOARD_READ_WRITE: @@ -148,6 +149,8 @@ public class WebsitePermissionsFetcher { for (@ContentSettingsType int type = 0; type < ContentSettingsType.NUM_TYPES; type++) { addFetcherForContentSettingsType(queue, type); } + queue.add(new ExceptionInfoFetcher(ContentSettingsType.TIMEZONE_OVERRIDE)); + queue.add(new PermissionsAvailableCallbackRunner(callback)); queue.next(); } diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java --- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/WebsitePreferenceBridge.java @@ -221,6 +221,8 @@ public class WebsitePreferenceBridge { switch (contentSettingsType) { case ContentSettingsType.PROTECTED_MEDIA_IDENTIFIER: return true; + case ContentSettingsType.TIMEZONE_OVERRIDE: + return true; default: return false; } @@ -351,6 +353,14 @@ public class WebsitePreferenceBridge { contentSettingType, primaryPattern, secondaryPattern, setting); } + public static String getCustomTimezone(BrowserContextHandle browserContextHandle) { + return WebsitePreferenceBridgeJni.get().getCustomTimezone(browserContextHandle); + } + + public static void setCustomTimezone(BrowserContextHandle browserContextHandle, String timezone) { + WebsitePreferenceBridgeJni.get().setCustomTimezone(browserContextHandle, timezone); + } + @NativeMethods public interface Natives { boolean isNotificationEmbargoedForOrigin( @@ -412,5 +422,7 @@ public class WebsitePreferenceBridge { boolean isContentSettingManagedByCustodian( BrowserContextHandle browserContextHandle, int contentSettingType); boolean getLocationAllowedByPolicy(BrowserContextHandle browserContextHandle); + String getCustomTimezone(BrowserContextHandle browserContextHandle); + void setCustomTimezone(BrowserContextHandle browserContextHandle, String timezone); } } diff --git a/components/browser_ui/site_settings/android/website_preference_bridge.cc b/components/browser_ui/site_settings/android/website_preference_bridge.cc --- a/components/browser_ui/site_settings/android/website_preference_bridge.cc +++ b/components/browser_ui/site_settings/android/website_preference_bridge.cc @@ -900,3 +900,19 @@ static jboolean JNI_WebsitePreferenceBridge_GetLocationAllowedByPolicy( ->GetDefaultContentSetting(ContentSettingsType::GEOLOCATION, nullptr) == CONTENT_SETTING_ALLOW; } + +static void JNI_WebsitePreferenceBridge_SetCustomTimezone( + JNIEnv* env, + const JavaParamRef& jbrowser_context_handle, + const JavaParamRef& timezone) { + std::string new_timezone = ConvertJavaStringToUTF8(env, timezone); + GetHostContentSettingsMap(jbrowser_context_handle)->SetTimezoneOverrideValue(new_timezone); +} + +static base::android::ScopedJavaLocalRef JNI_WebsitePreferenceBridge_GetCustomTimezone( + JNIEnv* env, + const JavaParamRef& jbrowser_context_handle) { + std::string timezone; + GetHostContentSettingsMap(jbrowser_context_handle)->GetTimezoneOverrideValue(timezone); + return ConvertUTF8ToJavaString(env, timezone); +} diff --git a/components/browser_ui/strings/android/site_settings.grdp b/components/browser_ui/strings/android/site_settings.grdp --- a/components/browser_ui/strings/android/site_settings.grdp +++ b/components/browser_ui/strings/android/site_settings.grdp @@ -75,6 +75,9 @@ Dark theme for sites + + Timezone override + Desktop site @@ -497,6 +500,38 @@ Block sites from playing protected content + + + Override timezone with a custom or random one, or use the system timezone + + + None (use system timezone) + + + Random + + + System timezone + + + Custom timezone + + + Specify a custom timezone (default UTC) + + + Random (for each page) + + + Choose Timezone... + + + Choose Timezone + + + Select + + diff --git a/components/content_settings/core/browser/content_settings_pref_provider.cc b/components/content_settings/core/browser/content_settings_pref_provider.cc --- a/components/content_settings/core/browser/content_settings_pref_provider.cc +++ b/components/content_settings/core/browser/content_settings_pref_provider.cc @@ -84,6 +84,8 @@ void PrefProvider::RegisterProfilePrefs( info->GetPrefRegistrationFlags()); } + registry->RegisterStringPref(prefs::kContentSettingsCustomTimezone, std::string()); + // Obsolete prefs ---------------------------------------------------------- // These prefs have been removed, but need to be registered so they can @@ -169,6 +171,10 @@ PrefProvider::PrefProvider(PrefService* prefs, event_args->set_number_of_exceptions( num_exceptions); // PrefProvider::PrefProvider. }); + + custom_timezone_ = + prefs_->GetString( + prefs::kContentSettingsCustomTimezone); } PrefProvider::~PrefProvider() { @@ -299,4 +305,14 @@ void PrefProvider::SetClockForTesting(base::Clock* clock) { clock_ = clock; } +void PrefProvider::GetPrefTimezoneOverrideValue(std::string& timezone) const { + timezone = custom_timezone_; +} + +void PrefProvider::SetPrefTimezoneOverrideValue(const std::string& timezone) { + prefs_->SetString( + prefs::kContentSettingsCustomTimezone, timezone); + custom_timezone_ = timezone; +} + } // namespace content_settings diff --git a/components/content_settings/core/browser/content_settings_pref_provider.h b/components/content_settings/core/browser/content_settings_pref_provider.h --- a/components/content_settings/core/browser/content_settings_pref_provider.h +++ b/components/content_settings/core/browser/content_settings_pref_provider.h @@ -67,6 +67,9 @@ class PrefProvider : public UserModifiableProvider { ContentSettingsPref* GetPref(ContentSettingsType type) const; + void GetPrefTimezoneOverrideValue(std::string& timezone) const; + void SetPrefTimezoneOverrideValue(const std::string& timezone); + private: friend class DeadlockCheckerObserver; // For testing. @@ -99,6 +102,7 @@ class PrefProvider : public UserModifiableProvider { base::ThreadChecker thread_checker_; raw_ptr clock_; + std::string custom_timezone_; }; } // namespace content_settings diff --git a/components/content_settings/core/browser/content_settings_registry.cc b/components/content_settings/core/browser/content_settings_registry.cc --- a/components/content_settings/core/browser/content_settings_registry.cc +++ b/components/content_settings/core/browser/content_settings_registry.cc @@ -645,6 +645,18 @@ void ContentSettingsRegistry::Init() { ContentSettingsInfo::PERSISTENT, ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + Register(ContentSettingsType::TIMEZONE_OVERRIDE, "timezone-override", CONTENT_SETTING_ALLOW, + WebsiteSettingsInfo::SYNCABLE, + AllowlistedSchemes(kChromeUIScheme, kChromeDevToolsScheme), + ValidSettings(CONTENT_SETTING_ALLOW, // use system time + CONTENT_SETTING_ASK, // custom timezone, default UTC + CONTENT_SETTING_BLOCK), // random + WebsiteSettingsInfo::SINGLE_ORIGIN_WITH_EMBEDDED_EXCEPTIONS_SCOPE, + WebsiteSettingsRegistry::PLATFORM_ANDROID, + ContentSettingsInfo::INHERIT_IN_INCOGNITO, + ContentSettingsInfo::PERSISTENT, + ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + Register(ContentSettingsType::FEDERATED_IDENTITY_API, "webid-api", CONTENT_SETTING_ALLOW, WebsiteSettingsInfo::UNSYNCABLE, AllowlistedSchemes(), diff --git a/components/content_settings/core/browser/content_settings_utils.cc b/components/content_settings/core/browser/content_settings_utils.cc --- a/components/content_settings/core/browser/content_settings_utils.cc +++ b/components/content_settings/core/browser/content_settings_utils.cc @@ -150,6 +150,13 @@ void GetRendererContentSettingRules(const HostContentSettingsMap* map, &(rules->script_rules)); map->GetSettingsForOneType(ContentSettingsType::POPUPS, &(rules->popup_redirect_rules)); + + // pass custom timezone rules and value to the render process + map->GetSettingsForOneType(ContentSettingsType::TIMEZONE_OVERRIDE, + &(rules->timezone_override_rules)); + std::string timezone; + map->GetTimezoneOverrideValue(timezone); + rules->timezone_override_value = timezone; } bool IsMorePermissive(ContentSetting a, ContentSetting b) { diff --git a/components/content_settings/core/browser/host_content_settings_map.cc b/components/content_settings/core/browser/host_content_settings_map.cc --- a/components/content_settings/core/browser/host_content_settings_map.cc +++ b/components/content_settings/core/browser/host_content_settings_map.cc @@ -599,6 +599,14 @@ void HostContentSettingsMap::SetClockForTesting(base::Clock* clock) { provider->SetClockForTesting(clock); } +void HostContentSettingsMap::GetTimezoneOverrideValue(std::string& timezone) const { + GetPrefProvider()->GetPrefTimezoneOverrideValue(timezone); +} + +void HostContentSettingsMap::SetTimezoneOverrideValue(const std::string& timezone) { + GetPrefProvider()->SetPrefTimezoneOverrideValue(timezone); +} + void HostContentSettingsMap::RecordExceptionMetrics() { auto* content_setting_registry = content_settings::ContentSettingsRegistry::GetInstance(); diff --git a/components/content_settings/core/browser/host_content_settings_map.h b/components/content_settings/core/browser/host_content_settings_map.h --- a/components/content_settings/core/browser/host_content_settings_map.h +++ b/components/content_settings/core/browser/host_content_settings_map.h @@ -334,6 +334,9 @@ class HostContentSettingsMap : public content_settings::Observer, allow_invalid_secondary_pattern_for_testing_ = allow; } + void GetTimezoneOverrideValue(std::string& timezone) const; + void SetTimezoneOverrideValue(const std::string& timezone); + private: friend class base::RefCountedThreadSafe; friend class content_settings::TestUtils; diff --git a/components/content_settings/core/common/content_settings.cc b/components/content_settings/core/common/content_settings.cc --- a/components/content_settings/core/common/content_settings.cc +++ b/components/content_settings/core/common/content_settings.cc @@ -190,7 +190,8 @@ bool RendererContentSettingRules::IsRendererContentSetting( content_type == ContentSettingsType::CLIENT_HINTS || content_type == ContentSettingsType::POPUPS || content_type == ContentSettingsType::MIXEDSCRIPT || - content_type == ContentSettingsType::AUTO_DARK_WEB_CONTENT; + content_type == ContentSettingsType::AUTO_DARK_WEB_CONTENT || + content_type == ContentSettingsType::TIMEZONE_OVERRIDE; } RendererContentSettingRules::RendererContentSettingRules() {} diff --git a/components/content_settings/core/common/content_settings.h b/components/content_settings/core/common/content_settings.h --- a/components/content_settings/core/common/content_settings.h +++ b/components/content_settings/core/common/content_settings.h @@ -80,6 +80,8 @@ struct RendererContentSettingRules { ContentSettingsForOneType popup_redirect_rules; ContentSettingsForOneType mixed_content_rules; ContentSettingsForOneType auto_dark_content_rules; + ContentSettingsForOneType timezone_override_rules; + std::string timezone_override_value; }; namespace content_settings { diff --git a/components/content_settings/core/common/content_settings.mojom b/components/content_settings/core/common/content_settings.mojom --- a/components/content_settings/core/common/content_settings.mojom +++ b/components/content_settings/core/common/content_settings.mojom @@ -78,4 +78,6 @@ struct RendererContentSettingRules { array popup_redirect_rules; array mixed_content_rules; array auto_dark_content_rules; + array timezone_override_rules; + string timezone_override_value; }; diff --git a/components/content_settings/core/common/content_settings_mojom_traits.cc b/components/content_settings/core/common/content_settings_mojom_traits.cc --- a/components/content_settings/core/common/content_settings_mojom_traits.cc +++ b/components/content_settings/core/common/content_settings_mojom_traits.cc @@ -101,7 +101,9 @@ bool StructTraitsscript_rules) && data.ReadPopupRedirectRules(&out->popup_redirect_rules) && data.ReadMixedContentRules(&out->mixed_content_rules) && - data.ReadAutoDarkContentRules(&out->auto_dark_content_rules); + data.ReadAutoDarkContentRules(&out->auto_dark_content_rules) && + data.ReadTimezoneOverrideRules(&out->timezone_override_rules) && + data.ReadTimezoneOverrideValue(&out->timezone_override_value); } } // namespace mojo diff --git a/components/content_settings/core/common/content_settings_mojom_traits.h b/components/content_settings/core/common/content_settings_mojom_traits.h --- a/components/content_settings/core/common/content_settings_mojom_traits.h +++ b/components/content_settings/core/common/content_settings_mojom_traits.h @@ -150,6 +150,16 @@ struct StructTraits< return r.auto_dark_content_rules; } + static const std::vector& timezone_override_rules( + const RendererContentSettingRules& r) { + return r.timezone_override_rules; + } + + static const std::string& timezone_override_value( + const RendererContentSettingRules& r) { + return r.timezone_override_value; + } + static bool Read( content_settings::mojom::RendererContentSettingRulesDataView data, RendererContentSettingRules* out); diff --git a/components/content_settings/core/common/content_settings_types.h b/components/content_settings/core/common/content_settings_types.h --- a/components/content_settings/core/common/content_settings_types.h +++ b/components/content_settings/core/common/content_settings_types.h @@ -226,6 +226,9 @@ enum class ContentSettingsType : int32_t { // by the File System Access API. FILE_SYSTEM_LAST_PICKED_DIRECTORY, + // Content setting for timezone customization functionality. + TIMEZONE_OVERRIDE, + // Controls access to the getDisplayMedia API when {preferCurrentTab: true} // is specified. // TODO(crbug.com/1150788): Also apply this when getDisplayMedia() is called diff --git a/components/content_settings/core/common/pref_names.cc b/components/content_settings/core/common/pref_names.cc --- a/components/content_settings/core/common/pref_names.cc +++ b/components/content_settings/core/common/pref_names.cc @@ -157,4 +157,7 @@ const char kQuietNotificationPermissionUiDisabledTime[] = const char kNotificationsVibrateEnabled[] = "notifications.vibrate_enabled"; #endif +const char kContentSettingsCustomTimezone[] = + "profile.content_settings.custom_timezone"; + } // namespace prefs diff --git a/components/content_settings/core/common/pref_names.h b/components/content_settings/core/common/pref_names.h --- a/components/content_settings/core/common/pref_names.h +++ b/components/content_settings/core/common/pref_names.h @@ -83,6 +83,8 @@ extern const char kQuietNotificationPermissionUiDisabledTime[]; extern const char kNotificationsVibrateEnabled[]; #endif +extern const char kContentSettingsCustomTimezone[]; + } // namespace prefs #endif // COMPONENTS_CONTENT_SETTINGS_CORE_COMMON_PREF_NAMES_H_ diff --git a/components/content_settings/renderer/content_settings_agent_impl.cc b/components/content_settings/renderer/content_settings_agent_impl.cc --- a/components/content_settings/renderer/content_settings_agent_impl.cc +++ b/components/content_settings/renderer/content_settings_agent_impl.cc @@ -8,8 +8,10 @@ #include "base/bind.h" #include "base/feature_list.h" +#include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/strings/string_number_conversions.h" +#include "base/rand_util.h" #include "components/content_settings/core/common/content_settings.h" #include "components/content_settings/core/common/content_settings.mojom.h" #include "components/content_settings/core/common/content_settings_pattern.h" @@ -29,6 +31,10 @@ #include "third_party/blink/public/web/web_local_frame.h" #include "third_party/blink/public/web/web_local_frame_client.h" #include "third_party/blink/public/web/web_view.h" +#include "third_party/blink/renderer/core/inspector/locale_controller.h" +#include "third_party/blink/renderer/core/timezone/timezone_controller.h" +#include "third_party/icu/source/common/unicode/strenum.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" #include "url/gurl.h" #include "url/origin.h" #include "url/url_constants.h" @@ -41,6 +47,8 @@ using blink::WebString; using blink::WebURL; using blink::WebView; +std::unique_ptr timezone_override_; + namespace content_settings { namespace { @@ -357,6 +365,10 @@ bool ContentSettingsAgentImpl::AllowScript(bool enabled_per_settings) { allow = allow || IsAllowlistedForContentSettings(); cached_script_permissions_[frame] = allow; + + if (allow) + UpdateOverrides(); + return allow; } @@ -487,4 +499,81 @@ bool ContentSettingsAgentImpl::IsAllowlistedForContentSettings() const { return false; } +bool ContentSettingsAgentImpl::UpdateOverrides() { + // Evaluate the content setting rules + ContentSetting setting = CONTENT_SETTING_ALLOW; + + if (content_setting_rules_) { + blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); + + setting = GetContentSettingFromRules( + content_setting_rules_->timezone_override_rules, frame, + url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()); + } + return UpdateTimeZoneOverride( + setting, content_setting_rules_->timezone_override_value); + //&& UpdateLocaleOverride(setting); +} + +bool ContentSettingsAgentImpl::UpdateTimeZoneOverride( + ContentSetting setting, + const std::string& timezone_override_value) { + // base/i18n/icu_util.cc # 329 + + /* timezone_id: third_party/icu/source/i18n/timezone.cpp + We first try to lookup the zone ID in our system list. If this + * fails, we try to parse it as a custom string GMT[+-]hh:mm. If + * all else fails, we return GMT, which is probably not what the + * user wants, but at least is a functioning TimeZone object. + */ + String timezone_id; + + if (setting == CONTENT_SETTING_ALLOW) { + // system time + if (timezone_override_) { + timezone_override_.reset(); + } + return true; + } else if (setting == CONTENT_SETTING_BLOCK) { + // timezone random + UErrorCode ec = U_ZERO_ERROR; + int32_t rawOffset = base::RandInt(-12, 11) * 3600 * 1000; + icu::StringEnumeration* timezones = icu::TimeZone::createEnumeration( + rawOffset); // Obtain timezones by GMT timezone offset + if (timezones) { + const char* tzID; + int32_t length; + if ((tzID = timezones->next(&length, ec)) != NULL) { + timezone_id = String(tzID); + } + delete timezones; + } + } else if (setting == CONTENT_SETTING_ASK) { + if (timezone_override_value.empty()) + timezone_id = "Europe/London"; + else + timezone_id = String(timezone_override_value.c_str()); + } + + if (blink::TimeZoneController::HasTimeZoneOverride() == false) { + timezone_override_.reset(); + timezone_override_ = + blink::TimeZoneController::SetTimeZoneOverride(timezone_id); + if (!timezone_override_) { + LOG(WARNING) << "UpdateTimeZoneOverride - Invalid timezone id '" + << timezone_id << "'"; + return false; + } else { + LOG(INFO) + << "UpdateTimeZoneOverride - setting to '" + << timezone_id << "'"; + return true; + } + } else { + LOG(INFO) + << "UpdateTimeZoneOverride: already set"; + return false; + } +} + } // namespace content_settings diff --git a/components/content_settings/renderer/content_settings_agent_impl.h b/components/content_settings/renderer/content_settings_agent_impl.h --- a/components/content_settings/renderer/content_settings_agent_impl.h +++ b/components/content_settings/renderer/content_settings_agent_impl.h @@ -183,6 +183,10 @@ class ContentSettingsAgentImpl std::unique_ptr delegate_; mojo::AssociatedReceiverSet receivers_; + + bool UpdateOverrides(); + bool UpdateTimeZoneOverride(ContentSetting setting, const std::string& timezone_override_value); + bool UpdateLocaleOverride(ContentSetting setting); }; } // namespace content_settings diff --git a/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java b/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java --- a/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java +++ b/weblayer/browser/java/org/chromium/weblayer_private/settings/WebLayerSiteSettingsDelegate.java @@ -137,4 +137,7 @@ public class WebLayerSiteSettingsDelegate @Override public void dismissPrivacySandboxSnackbar() {} + + @Override + public void launchTimeZoneOverrideHelpAndFeedbackActivity(Activity currentActivity) {} } -- 2.25.1