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 Original License: GPL-2.0-or-later - https://spdx.org/licenses/GPL-2.0-or-later.html License: GPL-3.0-only - https://spdx.org/licenses/GPL-3.0-only.html Require: Content-settings-infrastructure.patch --- .../browser_ui/site_settings/android/BUILD.gn | 3 + .../res/layout/time_zone_select_dialog.xml | 36 ++++ ...ezoneoverride_site_settings_preference.xml | 68 ++++++ ...imezoneOverrideSiteSettingsPreference.java | 196 ++++++++++++++++++ .../WebsitePreferenceBridge.java | 10 + ...BromiteTimezoneOverrideContentSetting.java | 157 ++++++++++++++ .../android/website_preference_bridge.cc | 16 ++ .../bromite_content_settings/timezone.grdp | 39 ++++ .../timezone_override.inc | 24 +++ .../browser/content_settings_pref_provider.cc | 16 ++ .../browser/content_settings_pref_provider.h | 4 + .../core/browser/content_settings_utils.cc | 5 + .../core/browser/host_content_settings_map.cc | 8 + .../core/browser/host_content_settings_map.h | 3 + .../bromite_content_settings/TIMEZONE.inc | 2 + .../core/common/content_settings.h | 1 + .../core/common/content_settings.mojom | 1 + .../common/content_settings_mojom_traits.cc | 3 +- .../common/content_settings_mojom_traits.h | 5 + .../content_settings/core/common/pref_names.h | 3 + .../renderer/content_settings_agent_impl.cc | 78 +++++++ .../renderer/content_settings_agent_impl.h | 10 + .../blink/renderer/core/timezone/build.gni | 2 + .../timezone/external_timezone_controller.cc | 40 ++++ .../timezone/external_timezone_controller.h | 34 +++ .../core/timezone/timezone_controller.cc | 11 - .../core/timezone/timezone_controller.h | 12 +- 27 files changed, 770 insertions(+), 17 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 create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteTimezoneOverrideContentSetting.java create mode 100644 components/browser_ui/strings/bromite_content_settings/timezone.grdp create mode 100644 components/content_settings/core/browser/bromite_content_settings/timezone_override.inc create mode 100644 components/content_settings/core/common/bromite_content_settings/TIMEZONE.inc create mode 100644 third_party/blink/renderer/core/timezone/external_timezone_controller.cc create mode 100644 third_party/blink/renderer/core/timezone/external_timezone_controller.h 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 @@ -108,6 +108,7 @@ android_library("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/WebsiteRowPreference.java", + "java/src/org/chromium/components/browser_ui/site_settings/TimezoneOverrideSiteSettingsPreference.java" ] resources_package = "org.chromium.components.browser_ui.site_settings" @@ -290,6 +291,8 @@ android_resources("java_resources") { "java/res/xml/site_settings_preferences.xml", "java/res/xml/storage_access_settings.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/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,196 @@ +/* + 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.ContentSetting; +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 BromiteCustomTriStateSiteSettingsPreferenceImpl, + RadioGroup.OnCheckedChangeListener, + RadioButtonWithEditText.OnTextChangeListener { + private @ContentSetting int mSetting = ContentSetting.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(@ContentSetting int setting, BrowserContextHandle browserContextHandle) { + mSetting = setting; + mBrowserContextHandle = browserContextHandle; + } + + @Override + public @ContentSetting int getCheckedSetting() { + return mSetting; + } + + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + if (mAllowed.isChecked()) { + mSetting = ContentSetting.ALLOW; + } else if (mAsk.isChecked()) { + mSetting = ContentSetting.ASK; + } else if (mBlocked.isChecked()) { + mSetting = ContentSetting.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); + + if (mBrowserContextHandle != null) + 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(@ContentSetting int setting) { + if (setting == ContentSetting.ALLOW) { + return mAllowed; + } else if (setting == ContentSetting.ASK) { + return mAsk; + } else if (setting == ContentSetting.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.ThemeOverlay_BrowserUI_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/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 @@ -571,6 +571,14 @@ public class WebsitePreferenceBridge { WebsitePreferenceBridgeJni.get().resetNotificationsSettingsForTest(browserContextHandle); } + public static String getCustomTimezone(BrowserContextHandle browserContextHandle) { + return WebsitePreferenceBridgeJni.get().getCustomTimezone(browserContextHandle); + } + + public static void setCustomTimezone(BrowserContextHandle browserContextHandle, String custom_timezone) { + WebsitePreferenceBridgeJni.get().setCustomTimezone(browserContextHandle, custom_timezone); + } + @NativeMethods public interface Natives { boolean isNotificationEmbargoedForOrigin( @@ -740,5 +748,7 @@ public class WebsitePreferenceBridge { String toDomainWildcardPattern(String pattern); String toHostOnlyPattern(String pattern); + String getCustomTimezone(BrowserContextHandle browserContextHandle); + void setCustomTimezone(BrowserContextHandle browserContextHandle, String custom_timezone); } } diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteTimezoneOverrideContentSetting.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteTimezoneOverrideContentSetting.java new file mode 100644 --- /dev/null +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteTimezoneOverrideContentSetting.java @@ -0,0 +1,157 @@ +/* + 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.impl; + +import org.chromium.components.browser_ui.site_settings.R; + +import org.chromium.components.browser_ui.site_settings.BromiteCustomContentSetting; +import org.chromium.components.browser_ui.site_settings.ContentSettingsResources; +import org.chromium.components.browser_ui.site_settings.SingleCategorySettings; +import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; +import org.chromium.components.browser_ui.site_settings.TimezoneOverrideSiteSettingsPreference; +import org.chromium.components.browser_ui.site_settings.WebsitePreferenceBridge; +import org.chromium.components.content_settings.ContentSetting; +import org.chromium.components.content_settings.ContentSettingsType; +import org.chromium.content_public.browser.BrowserContextHandle; + +import android.view.View; +import androidx.annotation.Nullable; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import java.util.ArrayList; + +public class BromiteTimezoneOverrideContentSetting extends BromiteCustomContentSetting { + private static final String TIMEOVERRIDE_STATE_TOGGLE_KEY = "timeoverride_state_toggle"; + + public BromiteTimezoneOverrideContentSetting() { + super(/*contentSettingsType*/ ContentSettingsType.TIMEZONE_OVERRIDE, + /*defaultEnabledValue*/ ContentSetting.ALLOW, + /*defaultDisabledValue*/ ContentSetting.BLOCK, + /*allowException*/ true, + /*preferenceKey*/ "timezone_override", + /*profilePrefKey*/ "timezone_override_permission_list"); + } + + @Override + public ContentSettingsResources.ResourceItem getResourceItem() { + return new ContentSettingsResources.ResourceItem( + /*icon*/ R.drawable.web_asset, + /*title*/ R.string.timezone_override_permission_title, + /*defaultEnabledValue*/ getDefaultEnabledValue(), + /*defaultDisabledValue*/ getDefaultDisabledValue(), + /*enabledSummary*/ R.string.website_settings_category_timezone_override_custom, + /*disabledSummary*/ R.string.website_settings_category_timezone_override_random, + /*summaryOverrideForScreenReader*/ 0); + } + + @Override + public int getCategorySummary(@Nullable @ContentSetting int value) { + switch (value) { + case ContentSetting.ALLOW: + return R.string.website_settings_category_timezone_override_allowed; + case ContentSetting.ASK: + return R.string.website_settings_category_timezone_override_custom; + case ContentSetting.BLOCK: + return R.string.website_settings_category_timezone_override_random; + default: + return 0; + } + } + + @Override + public int[] getTriStateSettingDescriptionIDs() { + int[] descriptionIDs = { + R.string.website_settings_category_timezone_override_allowed, // ALLOWED + R.string.website_settings_category_timezone_override_custom, // ASK + R.string.website_settings_category_timezone_override_random}; // BLOCKED + return descriptionIDs; + } + + @Override + public int[] getTriStateSettingIconIDs() { + return new int[] { + R.drawable.web_asset, + R.drawable.web_asset, + R.drawable.sensors_off_24px + }; + } + + @Override + public int getCategoryDescription() { + return R.string.website_settings_timeoverride_info; + } + + @Override + public boolean requiresTriStateContentSetting() { + return true; + } + + @Override + public boolean showOnlyDescriptions() { + return true; + } + + @Override + public int getAddExceptionDialogMessage() { + return R.string.website_settings_category_timezone_override_allowed; + } + + @Override + public @Nullable Boolean considerException(SiteSettingsCategory category, @ContentSetting int value) { + return value != ContentSetting.ALLOW; + } + + @Override + public boolean isOnBlockList(@ContentSetting Integer contentSetting) { + return ContentSetting.ALLOW != contentSetting; + } + + @Override + public boolean isHelpAndFeedbackEnabled() { + return true; + } + + @Override + public String getHelpAndFeedbackActivityUrl() { + return "https://github.com/bromite/bromite/wiki/TimezoneOverride"; + } + + @Override + public void configureGlobalToggles(SiteSettingsCategory category, SingleCategorySettings setting) { + BrowserContextHandle browserContext = setting.getSiteSettingsDelegate().getBrowserContextHandle(); + PreferenceScreen screen = setting.getPreferenceScreen(); + + Preference triStateToggle = screen.findPreference( + SingleCategorySettings.TRI_STATE_TOGGLE_KEY); + int order = triStateToggle.getOrder(); + screen.removePreference(triStateToggle); + + TimezoneOverrideSiteSettingsPreference timeOverrideStatePreference = + new TimezoneOverrideSiteSettingsPreference(setting.getContext(), null); + timeOverrideStatePreference.setKey(SingleCategorySettings.TRI_STATE_TOGGLE_KEY); + screen.addPreference(timeOverrideStatePreference); + timeOverrideStatePreference.setOrder(order); + + timeOverrideStatePreference.setOnPreferenceChangeListener(setting); + @ContentSetting + int value = WebsitePreferenceBridge.getDefaultContentSetting( + browserContext, ContentSettingsType.TIMEZONE_OVERRIDE); + timeOverrideStatePreference.initialize(value, browserContext); + } +} 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 @@ -1319,5 +1319,21 @@ JNI_WebsitePreferenceBridge_ToHostOnlyPattern(JNIEnv* env, return ConvertUTF8ToJavaString(env, host_only_pattern.ToString()); } +static void JNI_WebsitePreferenceBridge_SetCustomTimezone( + JNIEnv* env, + const JavaRef& jbrowser_context_handle, + const JavaRef& custom_timezone) { + std::string new_timezone = ConvertJavaStringToUTF8(env, custom_timezone); + GetHostContentSettingsMap(jbrowser_context_handle)->SetTimezoneOverrideValue(new_timezone); +} + +static base::android::ScopedJavaLocalRef JNI_WebsitePreferenceBridge_GetCustomTimezone( + JNIEnv* env, + const JavaRef& jbrowser_context_handle) { + std::string custom_timezone; + GetHostContentSettingsMap(jbrowser_context_handle)->GetTimezoneOverrideValue(custom_timezone); + return ConvertUTF8ToJavaString(env, custom_timezone); +} + DEFINE_JNI(GeolocationSetting) DEFINE_JNI(WebsitePreferenceBridge) diff --git a/components/browser_ui/strings/bromite_content_settings/timezone.grdp b/components/browser_ui/strings/bromite_content_settings/timezone.grdp new file mode 100644 --- /dev/null +++ b/components/browser_ui/strings/bromite_content_settings/timezone.grdp @@ -0,0 +1,39 @@ + + + + Timezone override + + + 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 + + + Timezone override + + diff --git a/components/content_settings/core/browser/bromite_content_settings/timezone_override.inc b/components/content_settings/core/browser/bromite_content_settings/timezone_override.inc new file mode 100644 --- /dev/null +++ b/components/content_settings/core/browser/bromite_content_settings/timezone_override.inc @@ -0,0 +1,24 @@ + Register(ContentSettingsType::TIMEZONE_OVERRIDE, "timezone-override", CONTENT_SETTING_ALLOW, + WebsiteSettingsInfo::SYNCABLE, + /*allowlisted_schemes=*/{}, + /*valid_settings=*/{CONTENT_SETTING_ALLOW, // use system time + CONTENT_SETTING_ASK, // custom timezone, default UTC + CONTENT_SETTING_BLOCK}, // random + WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, + WebsiteSettingsRegistry::ALL_PLATFORMS, + ContentSettingsInfo::INHERIT_IN_INCOGNITO, + PermissionSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + + content_settings::WebsiteSettingsRegistry::GetInstance() + ->GetMutable(ContentSettingsType::TIMEZONE_OVERRIDE) + ->set_show_into_info_page() + .set_desktop_ui() + .set_is_renderer_content_setting() + .set_title_ui(IDS_SITE_SETTINGS_TIMEZONE_OVERRIDE_TITLE) + .set_description_ui(IDS_WEBSITE_SETTINGS_TIMEOVERRIDE_INFO) + .set_allowed_ui(IDS_WEBSITE_SETTINGS_CATEGORY_TIMEZONE_OVERRIDE_ALLOWED) + .set_blocked_ui(IDS_WEBSITE_SETTINGS_CATEGORY_TIMEZONE_OVERRIDE_RANDOM) + .set_ask_ui(IDS_WEBSITE_SETTINGS_CATEGORY_TIMEZONE_OVERRIDE_CUSTOM) + .set_allowed_exceptions_ui(IDS_WEBSITE_SETTINGS_CATEGORY_TIMEZONE_OVERRIDE_ALLOWED) + .set_blocked_exceptions_ui(IDS_WEBSITE_SETTINGS_CATEGORY_TIMEZONE_OVERRIDE_RANDOM) + .set_mid_sentence_ui(IDS_SITE_SETTINGS_TIMEZONE_OVERRIDE_TITLE); 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 @@ -108,6 +108,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 @@ -196,6 +198,10 @@ PrefProvider::PrefProvider(PrefService* prefs, event_args->set_number_of_exceptions( num_exceptions); // PrefProvider::PrefProvider. }); + + custom_timezone_ = + prefs_->GetString( + prefs::kContentSettingsCustomTimezone); } PrefProvider::~PrefProvider() { @@ -584,4 +590,14 @@ void PrefProvider::SetClockForTesting(const base::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 @@ -83,6 +83,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. @@ -131,6 +134,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_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 @@ -156,6 +156,11 @@ void GetRendererContentSettingRules(const HostContentSettingsMap* map, ContentSettingToValue(CONTENT_SETTING_BLOCK), ProviderType::kNone, map->IsOffTheRecord())); #endif + + // pass custom timezone value to the render process + 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 @@ -762,6 +762,14 @@ void HostContentSettingsMap::SetClockForTesting(const base::Clock* clock) { } } +void HostContentSettingsMap::GetTimezoneOverrideValue(std::string& custom_timezone) const { + GetPrefProvider()->GetPrefTimezoneOverrideValue(custom_timezone); +} + +void HostContentSettingsMap::SetTimezoneOverrideValue(const std::string& custom_timezone) { + GetPrefProvider()->SetPrefTimezoneOverrideValue(custom_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 @@ -424,6 +424,9 @@ class HostContentSettingsMap : public content_settings::Observer, // Returns the current time of the `clock_`. base::Time Now() const { return clock_->Now(); } + void GetTimezoneOverrideValue(std::string& custom_timezone) const; + void SetTimezoneOverrideValue(const std::string& custom_timezone); + private: friend class base::RefCountedThreadSafe; friend class content_settings::TestUtils; diff --git a/components/content_settings/core/common/bromite_content_settings/TIMEZONE.inc b/components/content_settings/core/common/bromite_content_settings/TIMEZONE.inc new file mode 100644 --- /dev/null +++ b/components/content_settings/core/common/bromite_content_settings/TIMEZONE.inc @@ -0,0 +1,2 @@ + // Content setting for timezone customization functionality. + TIMEZONE_OVERRIDE, 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 @@ -131,6 +131,7 @@ struct RendererContentSettingRules { bool operator==(const RendererContentSettingRules& other) const; ContentSettingsForOneType mixed_content_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 @@ -94,4 +94,5 @@ struct ContentSettingPatternSource { struct RendererContentSettingRules { array settings_rules; array mixed_content_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 @@ -129,7 +129,8 @@ bool StructTraitssettings_rules) && - data.ReadMixedContentRules(&out->mixed_content_rules); + data.ReadMixedContentRules(&out->mixed_content_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 @@ -219,6 +219,11 @@ struct StructTraits< return r.mixed_content_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/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 @@ -281,6 +281,9 @@ inline constexpr char kDesktopSiteWindowSettingEnabled[] = "desktop_site.window_setting"; #endif +inline constexpr char kContentSettingsCustomTimezone[] = + "profile.content_settings.custom_timezone"; + } // 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/feature_list.h" #include "base/functional/bind.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" @@ -162,6 +164,7 @@ void ContentSettingsAgentImpl::DidCommitProvisionalLoad( // correctly detect that a piece of content flipped from "not blocked" to // "blocked". ClearBlockedContentSettings(); // do not remove + UpdateOverrides(); blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); if (frame->Parent()) @@ -465,4 +468,79 @@ void ContentSettingsAgentImpl::ClearBlockedContentSettings() { cached_script_permissions_.clear(); } +bool ContentSettingsAgentImpl::UpdateOverrides() { + // Evaluate the content setting rules + ContentSetting setting = CONTENT_SETTING_ALLOW; + + if (content_setting_rules_) { + setting = GetContentSetting( + ContentSettingsType::TIMEZONE_OVERRIDE, setting); + } + 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 + + if (setting == CONTENT_SETTING_ALLOW) { + // system time + if (timezone_override_) { + timezone_override_.reset(); + } + return true; + } + + // https://chromium-review.googlesource.com/c/chromium/src/+/7031664 + if (blink::ExternalTimeZoneController::HasTimeZoneOverride()) { + DLOG(INFO) << "UpdateTimeZoneOverride: already set"; + return false; + } + + /* 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. + */ + std::string timezone_id; + 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 = tzID; + } + delete timezones; + } + } else if (setting == CONTENT_SETTING_ASK) { + if (timezone_override_value.empty()) + timezone_id = "Europe/London"; + else + timezone_id = timezone_override_value; + } + + timezone_override_.reset(); + timezone_override_ = + blink::ExternalTimeZoneController::SetTimeZoneOverride(timezone_id); + if (!timezone_override_) { + DLOG(WARNING) << "UpdateTimeZoneOverride - Invalid timezone id '" + << timezone_id << "'"; + return false; + } else { + DLOG(INFO) + << "UpdateTimeZoneOverride - setting to '" + << timezone_id << "'"; + return true; + } +} + } // 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 @@ -23,6 +23,10 @@ #include "third_party/blink/public/platform/web_content_settings_client.h" #include "url/origin.h" +#include "third_party/blink/renderer/core/timezone/external_timezone_controller.h" +#include "third_party/icu/source/common/unicode/strenum.h" +#include "third_party/icu/source/i18n/unicode/timezone.h" + namespace blink { class WebFrame; class WebURL; @@ -160,6 +164,12 @@ class ContentSettingsAgentImpl std::unique_ptr delegate_; mojo::AssociatedReceiverSet receivers_; + + std::unique_ptr timezone_override_; + + bool UpdateOverrides(); + bool UpdateTimeZoneOverride(ContentSetting setting, const std::string& timezone_override_value); + bool UpdateLocaleOverride(ContentSetting setting); }; } // namespace content_settings diff --git a/third_party/blink/renderer/core/timezone/build.gni b/third_party/blink/renderer/core/timezone/build.gni --- a/third_party/blink/renderer/core/timezone/build.gni +++ b/third_party/blink/renderer/core/timezone/build.gni @@ -5,4 +5,6 @@ blink_core_sources_timezone = [ "timezone_controller.cc", "timezone_controller.h", + "external_timezone_controller.cc", + "external_timezone_controller.h", ] diff --git a/third_party/blink/renderer/core/timezone/external_timezone_controller.cc b/third_party/blink/renderer/core/timezone/external_timezone_controller.cc new file mode 100644 --- /dev/null +++ b/third_party/blink/renderer/core/timezone/external_timezone_controller.cc @@ -0,0 +1,40 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/core/timezone/external_timezone_controller.h" +#include "third_party/blink/renderer/core/timezone/timezone_controller.h" +#include "third_party/blink/renderer/platform/wtf/wtf.h" + +namespace blink { + +ExternalTimeZoneController::ExternalTimeZoneController() { + DCHECK(IsMainThread()); +} + +// static +std::unique_ptr +ExternalTimeZoneController::SetTimeZoneOverride(const std::string& timezone_id) { + DCHECK(IsMainThread()); + auto timezone = String(timezone_id.c_str()); + + auto result = TimeZoneController::SetTimeZoneOverride(timezone); + if (result.status != TimeZoneController::TimeZoneOverrideStatus::kSuccess) + return nullptr; + + result.handle->clear_at_destruction_ = false; + return std::unique_ptr(new TimeZoneOverride()); +} + +// static +bool ExternalTimeZoneController::HasTimeZoneOverride() { + DCHECK(IsMainThread()); + return TimeZoneController::HasTimeZoneOverride(); +} + +// static +void ExternalTimeZoneController::ClearTimeZoneOverride() { + TimeZoneController::ClearTimeZoneOverride(); +} + +} // namespace blink diff --git a/third_party/blink/renderer/core/timezone/external_timezone_controller.h b/third_party/blink/renderer/core/timezone/external_timezone_controller.h new file mode 100644 --- /dev/null +++ b/third_party/blink/renderer/core/timezone/external_timezone_controller.h @@ -0,0 +1,34 @@ +#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_TIMEZONE_EXTERNAL_TIMEZONE_CONTROLLER_H_ +#define THIRD_PARTY_BLINK_RENDERER_CORE_TIMEZONE_EXTERNAL_TIMEZONE_CONTROLLER_H_ + +#include +#include + +#include "third_party/blink/renderer/core/core_export.h" + +namespace blink { + +class CORE_EXPORT ExternalTimeZoneController final { + public: + class TimeZoneOverride { + friend ExternalTimeZoneController; + TimeZoneOverride() = default; + + public: + ~TimeZoneOverride() { ClearTimeZoneOverride(); } + }; + + static std::unique_ptr SetTimeZoneOverride( + const std::string& timezone_id); + + static bool HasTimeZoneOverride(); + + private: + static void ClearTimeZoneOverride(); + + ExternalTimeZoneController(); +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_CORE_TIMEZONE_EXTERNAL_TIMEZONE_CONTROLLER_H_ diff --git a/third_party/blink/renderer/core/timezone/timezone_controller.cc b/third_party/blink/renderer/core/timezone/timezone_controller.cc --- a/third_party/blink/renderer/core/timezone/timezone_controller.cc +++ b/third_party/blink/renderer/core/timezone/timezone_controller.cc @@ -141,8 +141,6 @@ TimeZoneController::TimeZoneOverrideResult TimeZoneController::SetTimeZoneOverride(const String& timezone_id) { DCHECK(!timezone_id.empty()); - base::AutoLock locker(instance().lock_); - if (timezone_id == instance().TimeZoneIdOverride()) { // Do nothing. return {TimeZoneOverrideStatus::kSuccess, nullptr}; @@ -168,20 +166,16 @@ TimeZoneController::SetTimeZoneOverride(const String& timezone_id) { // static bool TimeZoneController::HasTimeZoneOverride() { - instance().lock_.AssertAcquired(); return !instance().override_timezone_id_.empty(); } // static const String& TimeZoneController::TimeZoneIdOverride() { - instance().lock_.AssertAcquired(); return instance().override_timezone_id_; } // static void TimeZoneController::ClearTimeZoneOverride() { - base::AutoLock locker(instance().lock_); - DCHECK(HasTimeZoneOverride()); if (!CanonicalEquals(instance().GetHostTimezoneId(), @@ -197,8 +191,6 @@ void TimeZoneController::ClearTimeZoneOverride() { void TimeZoneController::ChangeTimeZoneOverride(const String& timezone_id) { DCHECK(!timezone_id.empty()); - base::AutoLock locker(instance().lock_); - if (!HasTimeZoneOverride()) { VLOG(1) << "Cannot change if there are no existing timezone override."; return; @@ -243,8 +235,6 @@ bool TimeZoneController::SetIcuTimeZoneAndNotifyV8(const String& timezone_id) { void TimeZoneController::OnTimeZoneChange(const String& timezone_id) { DCHECK(IsMainThread()); - base::AutoLock locker(instance().lock_); - // Remember requested timezone id so we can set it when timezone // override is removed. instance().host_timezone_id_ = timezone_id; @@ -254,7 +244,6 @@ void TimeZoneController::OnTimeZoneChange(const String& timezone_id) { } const String& TimeZoneController::GetHostTimezoneId() { - lock_.AssertAcquired(); if (host_timezone_id_.IsNull()) { CHECK(base::FeatureList::IsEnabled(kLazyBlinkTimezoneInit)); host_timezone_id_ = GetCurrentTimezoneId(); diff --git a/third_party/blink/renderer/core/timezone/timezone_controller.h b/third_party/blink/renderer/core/timezone/timezone_controller.h --- a/third_party/blink/renderer/core/timezone/timezone_controller.h +++ b/third_party/blink/renderer/core/timezone/timezone_controller.h @@ -12,6 +12,7 @@ #include "services/device/public/mojom/time_zone_monitor.mojom-blink.h" #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/blink/renderer/core/timezone/external_timezone_controller.h" namespace blink { @@ -26,6 +27,7 @@ namespace blink { // current host system time zone is assumed. class CORE_EXPORT TimeZoneController final : public device::mojom::blink::TimeZoneMonitorClient { + friend ExternalTimeZoneController; public: ~TimeZoneController() override; @@ -61,7 +63,9 @@ class CORE_EXPORT TimeZoneController final ChangeTimeZoneOverride(timezone_id); } - ~TimeZoneOverride() { ClearTimeZoneOverride(); } + bool clear_at_destruction_ = true; + + ~TimeZoneOverride() { if (clear_at_destruction_) ClearTimeZoneOverride(); } }; struct TimeZoneOverrideResult { @@ -96,10 +100,8 @@ class CORE_EXPORT TimeZoneController final // by Oilpan. mojo::Receiver receiver_{this}; - base::Lock lock_; - - String host_timezone_id_ GUARDED_BY(lock_); - String override_timezone_id_ GUARDED_BY(lock_); + String host_timezone_id_; + String override_timezone_id_; }; } // namespace blink --