From ce2d35dfe5f205f05437fc603c42bba7edf5ecc2 Mon Sep 17 00:00:00 2001 From: Carmelo Messina Date: Wed, 6 Jul 2022 14:22:41 +0200 Subject: [PATCH] Viewport Protection site setting --- build/patches/Add-Viewport-Protection.patch | 667 ++++++++++++++++++++ 1 file changed, 667 insertions(+) create mode 100644 build/patches/Add-Viewport-Protection.patch diff --git a/build/patches/Add-Viewport-Protection.patch b/build/patches/Add-Viewport-Protection.patch new file mode 100644 index 00000000..99f99ef8 --- /dev/null +++ b/build/patches/Add-Viewport-Protection.patch @@ -0,0 +1,667 @@ +From: uazo +Date: Thu, 30 Jun 2022 12:47:17 +0000 +Subject: Viewport Protection + +--- + .../browser_ui/site_settings/android/BUILD.gn | 3 + + .../BromiteCustomContentSettingImpl.java | 1 + + .../BromiteViewportContentSetting.java | 86 +++++++++++++++++++ + .../site_settings/SiteSettingsCategory.java | 5 +- + .../strings/android/browser_ui_strings.grd | 1 + + .../browser_ui/strings/android/viewport.grdp | 18 ++++ + components/components_strings.grd | 1 + + .../core/browser/content_settings_registry.cc | 14 +++ + .../core/browser/content_settings_utils.cc | 2 + + .../core/common/content_settings.cc | 4 +- + .../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 ++ + .../core/common/content_settings_types.h | 2 + + .../renderer/content_settings_agent_impl.cc | 9 ++ + .../renderer/content_settings_agent_impl.h | 1 + + .../platform/web_content_settings_client.h | 2 + + .../blink/renderer/core/css/media_values.cc | 10 +++ + .../renderer/core/frame/local_frame_view.cc | 3 + + .../blink/renderer/core/frame/screen.cc | 17 +++- + .../renderer/core/frame/visual_viewport.cc | 4 + + .../renderer/core/frame/visual_viewport.h | 5 ++ + .../renderer/core/html/html_meta_element.cc | 28 +++++- + .../renderer/core/loader/frame_loader.cc | 7 +- + third_party/blink/renderer/core/page/page.cc | 8 ++ + third_party/blink/renderer/core/page/page.h | 3 + + .../screen_enumeration/screen_detailed.cc | 15 ++++ + 28 files changed, 247 insertions(+), 12 deletions(-) + create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteViewportContentSetting.java + create mode 100644 components/browser_ui/strings/android/viewport.grdp + +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 +@@ -78,6 +78,9 @@ android_library("java") { + sources += [ + "java/src/org/chromium/components/browser_ui/site_settings/BromiteWebRTCContentSetting.java", + ] ++ sources += [ ++ "java/src/org/chromium/components/browser_ui/site_settings/BromiteViewportContentSetting.java", ++ ] + annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ] + resources_package = "org.chromium.components.browser_ui.site_settings" + deps = [ +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSettingImpl.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSettingImpl.java +--- a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSettingImpl.java ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteCustomContentSettingImpl.java +@@ -45,6 +45,7 @@ public abstract class BromiteCustomContentSettingImpl { + mItemList = new ArrayList(); + mItemList.add(new BromiteWebGLContentSetting()); + mItemList.add(new BromiteWebRTCContentSetting()); ++ mItemList.add(new BromiteViewportContentSetting()); + } + + public static SiteSettingsCategory createFromType( +diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteViewportContentSetting.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteViewportContentSetting.java +new file mode 100644 +--- /dev/null ++++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/BromiteViewportContentSetting.java +@@ -0,0 +1,86 @@ ++/* ++ 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.components.browser_ui.site_settings.ContentSettingsResources; ++import org.chromium.components.browser_ui.site_settings.SiteSettingsCategory; ++import org.chromium.components.content_settings.ContentSettingValues; ++import org.chromium.components.content_settings.ContentSettingsType; ++import org.chromium.content_public.browser.BrowserContextHandle; ++ ++import androidx.annotation.Nullable; ++import androidx.preference.Preference; ++import androidx.preference.PreferenceScreen; ++ ++import java.util.ArrayList; ++ ++public class BromiteViewportContentSetting extends BromiteCustomContentSetting { ++ public BromiteViewportContentSetting() { ++ super(/*contentSettingsType*/ ContentSettingsType.VIEWPORT, ++ /*siteSettingsCategory*/ SiteSettingsCategory.Type.VIEWPORT, ++ /*defaultEnabledValue*/ ContentSettingValues.ALLOW, ++ /*defaultDisabledValue*/ ContentSettingValues.BLOCK, ++ /*allowException*/ true, ++ /*preferenceKey*/ "viewport", ++ /*profilePrefKey*/ "viewport"); ++ } ++ ++ @Override ++ public ContentSettingsResources.ResourceItem getResourceItem() { ++ return new ContentSettingsResources.ResourceItem( ++ /*icon*/ R.drawable.web_asset, ++ /*title*/ R.string.viewport_permission_title, ++ /*defaultEnabledValue*/ getDefaultEnabledValue(), ++ /*defaultDisabledValue*/ getDefaultDisabledValue(), ++ /*enabledSummary*/ R.string.website_settings_category_viewport_enabled, ++ /*disabledSummary*/ R.string.website_settings_category_viewport_disabled); ++ } ++ ++ @Override ++ public int getCategorySummary(@Nullable @ContentSettingValues int value) { ++ switch (value) { ++ case ContentSettingValues.ALLOW: ++ return R.string.website_settings_category_viewport_enabled; ++ case ContentSettingValues.BLOCK: ++ return R.string.website_settings_category_viewport_disabled; ++ default: ++ // this will cause a runtime exception ++ return 0; ++ } ++ } ++ ++ @Override ++ public boolean requiresTriStateContentSetting() { ++ return false; ++ } ++ ++ @Override ++ public boolean showOnlyDescriptions() { ++ return true; ++ } ++ ++ @Override ++ public int getAddExceptionDialogMessage() { ++ return R.string.website_settings_category_viewport_enabled; ++ } ++ ++ @Override ++ public @Nullable Boolean considerException(SiteSettingsCategory category, @ContentSettingValues int value) { ++ return value != ContentSettingValues.BLOCK; ++ } ++} +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 +@@ -44,7 +44,7 @@ public class SiteSettingsCategory { + 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.TIMEZONE_OVERRIDE, Type.AUTOPLAY, Type.JAVASCRIPT_JIT, +- Type.IMAGES, Type.WEBGL, Type.WEBRTC}) ++ Type.IMAGES, Type.WEBGL, Type.WEBRTC, Type.VIEWPORT}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type { + // All updates here must also be reflected in {@link #preferenceKey(int) +@@ -81,10 +81,11 @@ public class SiteSettingsCategory { + int IMAGES = 29; + int WEBGL = 30; + int WEBRTC = 31; ++ int VIEWPORT = 32; + /** + * Number of handled categories used for calculating array sizes. + */ +- int NUM_ENTRIES = 32; ++ int NUM_ENTRIES = 33; + } + + private final BrowserContextHandle mBrowserContextHandle; +diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd +--- a/components/browser_ui/strings/android/browser_ui_strings.grd ++++ b/components/browser_ui/strings/android/browser_ui_strings.grd +@@ -176,6 +176,7 @@ + + + ++ + + + Got it +diff --git a/components/browser_ui/strings/android/viewport.grdp b/components/browser_ui/strings/android/viewport.grdp +new file mode 100644 +--- /dev/null ++++ b/components/browser_ui/strings/android/viewport.grdp +@@ -0,0 +1,18 @@ ++ ++ ++ ++ Viewport Size Protection ++ ++ ++ Viewport Size Protection ++ ++ ++ Viewport Size Protection ++ ++ ++ Enabled ++ ++ ++ Disabled ++ ++ +diff --git a/components/components_strings.grd b/components/components_strings.grd +--- a/components/components_strings.grd ++++ b/components/components_strings.grd +@@ -339,6 +339,7 @@ + + + ++ + + + +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 +@@ -707,6 +707,20 @@ void ContentSettingsRegistry::Init() { + /*show_into_info_page*/ true, + /*permission_type_ui*/ IDS_SITE_SETTINGS_TYPE_WEBRTC, + /*permission_type_ui_mid_sentence*/ IDS_SITE_SETTINGS_TYPE_WEBRTC_MID_SENTENCE); ++ ++ Register(ContentSettingsType::VIEWPORT, "viewport", CONTENT_SETTING_BLOCK, ++ WebsiteSettingsInfo::SYNCABLE, ++ AllowlistedSchemes(), ++ ValidSettings(CONTENT_SETTING_ALLOW, ++ CONTENT_SETTING_BLOCK), ++ WebsiteSettingsInfo::SINGLE_ORIGIN_WITH_EMBEDDED_EXCEPTIONS_SCOPE, ++ WebsiteSettingsRegistry::PLATFORM_ANDROID, ++ ContentSettingsInfo::INHERIT_IN_INCOGNITO, ++ ContentSettingsInfo::PERSISTENT, ++ ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS, ++ /*show_into_info_page*/ true, ++ /*permission_type_ui*/ IDS_SITE_SETTINGS_TYPE_VIEWPORT, ++ /*permission_type_ui_mid_sentence*/ IDS_SITE_SETTINGS_TYPE_VIEWPORT_MID_SENTENCE); + } + + void ContentSettingsRegistry::Register( +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 +@@ -159,6 +159,8 @@ void GetRendererContentSettingRules(const HostContentSettingsMap* map, + &(rules->webgl_rules)); + map->GetSettingsForOneType(ContentSettingsType::WEBRTC, + &(rules->webrtc_rules)); ++ map->GetSettingsForOneType(ContentSettingsType::VIEWPORT, ++ &(rules->viewport_rules)); + } + + bool IsMorePermissive(ContentSetting a, ContentSetting b) { +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 +@@ -206,7 +206,8 @@ bool RendererContentSettingRules::IsRendererContentSetting( + content_type == ContentSettingsType::AUTO_DARK_WEB_CONTENT || + content_type == ContentSettingsType::TIMEZONE_OVERRIDE || + content_type == ContentSettingsType::WEBGL || +- content_type == ContentSettingsType::WEBRTC; ++ content_type == ContentSettingsType::WEBRTC || ++ content_type == ContentSettingsType::VIEWPORT; + } + + void RendererContentSettingRules::FilterRulesByOutermostMainFrameURL( +@@ -220,6 +221,7 @@ void RendererContentSettingRules::FilterRulesByOutermostMainFrameURL( + FilterRulesForType(autoplay_rules, outermost_main_frame_url); + FilterRulesForType(webgl_rules, outermost_main_frame_url); + FilterRulesForType(webrtc_rules, outermost_main_frame_url); ++ FilterRulesForType(viewport_rules, outermost_main_frame_url); + } + + RendererContentSettingRules::RendererContentSettingRules() = default; +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 +@@ -99,6 +99,7 @@ struct RendererContentSettingRules { + std::string timezone_override_value; + ContentSettingsForOneType webgl_rules; + ContentSettingsForOneType webrtc_rules; ++ ContentSettingsForOneType viewport_rules; + }; + + 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 +@@ -83,4 +83,5 @@ struct RendererContentSettingRules { + string timezone_override_value; + array webgl_rules; + array webrtc_rules; ++ array viewport_rules; + }; +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 +@@ -106,7 +106,8 @@ bool StructTraitstimezone_override_rules) && + data.ReadTimezoneOverrideValue(&out->timezone_override_value) && + data.ReadWebglRules(&out->webgl_rules) && +- data.ReadWebrtcRules(&out->webrtc_rules); ++ data.ReadWebrtcRules(&out->webrtc_rules) && ++ data.ReadViewportRules(&out->viewport_rules); + } + + } // 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 +@@ -175,6 +175,11 @@ struct StructTraits< + return r.webrtc_rules; + } + ++ static const std::vector& viewport_rules( ++ const RendererContentSettingRules& r) { ++ return r.viewport_rules; ++ } ++ + 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 +@@ -284,6 +284,8 @@ enum class ContentSettingsType : int32_t { + + WEBRTC, + ++ VIEWPORT, ++ + // Setting to indicate whether browser should allow signing into a website via + // the browser FedCM API. + FEDERATED_IDENTITY_API, +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 +@@ -473,6 +473,15 @@ bool ContentSettingsAgentImpl::AllowWebRTC(bool enabled_per_settings) { + url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()); + } + ++bool ContentSettingsAgentImpl::AllowViewportChange(bool enabled_per_settings) { ++ if (!content_setting_rules_) ++ return false; ++ blink::WebLocalFrame* frame = render_frame()->GetWebFrame(); ++ return CONTENT_SETTING_ALLOW == GetContentSettingFromRules( ++ content_setting_rules_->viewport_rules, ++ url::Origin(frame->GetDocument().GetSecurityOrigin()).GetURL()); ++} ++ + bool ContentSettingsAgentImpl::IsAllowlistedForContentSettings() const { + if (should_allowlist_) + return true; +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 +@@ -96,6 +96,7 @@ class ContentSettingsAgentImpl + bool ShouldAutoupgradeMixedContent() override; + bool AllowWebgl(bool enabled_per_settings) override; + bool AllowWebRTC(bool enabled_per_settings) override; ++ bool AllowViewportChange(bool enabled_per_settings) override; + + bool allow_running_insecure_content() const { + return allow_running_insecure_content_; +diff --git a/third_party/blink/public/platform/web_content_settings_client.h b/third_party/blink/public/platform/web_content_settings_client.h +--- a/third_party/blink/public/platform/web_content_settings_client.h ++++ b/third_party/blink/public/platform/web_content_settings_client.h +@@ -103,6 +103,8 @@ class WebContentSettingsClient { + + virtual bool AllowWebRTC(bool default_value) { return default_value; } + ++ virtual bool AllowViewportChange(bool default_value) { return default_value; } ++ + // Reports that passive mixed content was found at the provided URL. + virtual void PassiveInsecureContentFound(const WebURL&) {} + +diff --git a/third_party/blink/renderer/core/css/media_values.cc b/third_party/blink/renderer/core/css/media_values.cc +--- a/third_party/blink/renderer/core/css/media_values.cc ++++ b/third_party/blink/renderer/core/css/media_values.cc +@@ -193,6 +193,11 @@ int MediaValues::CalculateDeviceWidth(LocalFrame* frame) { + device_width = static_cast( + lroundf(device_width * screen_info.device_scale_factor)); + } ++ float width_override = frame->GetPage()->PageWidthOverride(); ++ if (width_override) { ++ device_width = static_cast( ++ lroundf(device_width * (1.0 - (width_override / 100.0)))); ++ } + return device_width; + } + +@@ -205,6 +210,11 @@ int MediaValues::CalculateDeviceHeight(LocalFrame* frame) { + device_height = static_cast( + lroundf(device_height * screen_info.device_scale_factor)); + } ++ float width_override = frame->GetPage()->PageWidthOverride(); ++ if (width_override) { ++ device_height = static_cast( ++ lroundf(device_height * (1.0 - (width_override / 100.0)))); ++ } + return device_height; + } + +diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc +--- a/third_party/blink/renderer/core/frame/local_frame_view.cc ++++ b/third_party/blink/renderer/core/frame/local_frame_view.cc +@@ -1034,6 +1034,9 @@ gfx::SizeF LocalFrameView::ViewportSizeForMediaQueries() const { + gfx::SizeF viewport_size(layout_size_); + if (!frame_->GetDocument() || !frame_->GetDocument()->Printing()) + viewport_size.Scale(1 / GetFrame().PageZoomFactor()); ++ float width_override = frame_->GetPage()->PageWidthOverride(); ++ if (width_override) ++ viewport_size.Scale(1.0 + (width_override / 100.0)); + return viewport_size; + } + +diff --git a/third_party/blink/renderer/core/frame/screen.cc b/third_party/blink/renderer/core/frame/screen.cc +--- a/third_party/blink/renderer/core/frame/screen.cc ++++ b/third_party/blink/renderer/core/frame/screen.cc +@@ -35,6 +35,7 @@ + #include "third_party/blink/renderer/core/frame/local_frame.h" + #include "third_party/blink/renderer/core/frame/settings.h" + #include "third_party/blink/renderer/core/page/chrome_client.h" ++#include "third_party/blink/renderer/core/page/page.h" + #include "ui/display/screen_info.h" + #include "ui/display/screen_infos.h" + +@@ -42,6 +43,10 @@ namespace blink { + + namespace { + ++float GetScaleOverride(blink::Page* page) { ++ return 1.0 - (page->PageWidthOverride() / 100.0); ++} ++ + } // namespace + + Screen::Screen(LocalDOMWindow* window, int64_t display_id) +@@ -83,9 +88,10 @@ int Screen::height() const { + const display::ScreenInfo& screen_info = GetScreenInfo(); + if (frame->GetSettings()->GetReportScreenSizeInPhysicalPixelsQuirk()) { + return base::ClampRound(screen_info.rect.height() * ++ GetScaleOverride(frame->GetPage()) * + screen_info.device_scale_factor); + } +- return screen_info.rect.height(); ++ return screen_info.rect.height() * GetScaleOverride(frame->GetPage()); + } + + int Screen::width() const { +@@ -95,9 +101,10 @@ int Screen::width() const { + const display::ScreenInfo& screen_info = GetScreenInfo(); + if (frame->GetSettings()->GetReportScreenSizeInPhysicalPixelsQuirk()) { + return base::ClampRound(screen_info.rect.width() * ++ GetScaleOverride(frame->GetPage()) * + screen_info.device_scale_factor); + } +- return screen_info.rect.width(); ++ return screen_info.rect.width() * GetScaleOverride(frame->GetPage()); + } + + unsigned Screen::colorDepth() const { +@@ -141,9 +148,10 @@ int Screen::availHeight() const { + const display::ScreenInfo& screen_info = GetScreenInfo(); + if (frame->GetSettings()->GetReportScreenSizeInPhysicalPixelsQuirk()) { + return base::ClampRound(screen_info.available_rect.height() * ++ GetScaleOverride(frame->GetPage()) * + screen_info.device_scale_factor); + } +- return screen_info.available_rect.height(); ++ return screen_info.available_rect.height() * GetScaleOverride(frame->GetPage()); + } + + int Screen::availWidth() const { +@@ -153,9 +161,10 @@ int Screen::availWidth() const { + const display::ScreenInfo& screen_info = GetScreenInfo(); + if (frame->GetSettings()->GetReportScreenSizeInPhysicalPixelsQuirk()) { + return base::ClampRound(screen_info.available_rect.width() * ++ GetScaleOverride(frame->GetPage()) * + screen_info.device_scale_factor); + } +- return screen_info.available_rect.width(); ++ return screen_info.available_rect.width() * GetScaleOverride(frame->GetPage()); + } + + void Screen::Trace(Visitor* visitor) const { +diff --git a/third_party/blink/renderer/core/frame/visual_viewport.cc b/third_party/blink/renderer/core/frame/visual_viewport.cc +--- a/third_party/blink/renderer/core/frame/visual_viewport.cc ++++ b/third_party/blink/renderer/core/frame/visual_viewport.cc +@@ -467,6 +467,10 @@ void VisualViewport::SetScale(float scale) { + gfx::PointAtOffsetFromOrigin(offset_)); + } + ++void VisualViewport::SetPageWidthOverride(int value) { ++ page_width_override_ = value; ++} ++ + double VisualViewport::OffsetLeft() const { + // Offset{Left|Top} and Width|Height are used by the DOMVisualViewport to + // expose values to JS. We'll only ever ask the visual viewport for these +diff --git a/third_party/blink/renderer/core/frame/visual_viewport.h b/third_party/blink/renderer/core/frame/visual_viewport.h +--- a/third_party/blink/renderer/core/frame/visual_viewport.h ++++ b/third_party/blink/renderer/core/frame/visual_viewport.h +@@ -145,6 +145,9 @@ class CORE_EXPORT VisualViewport : public GarbageCollected, + float Scale() const { return scale_; } + bool IsPinchGestureActive() const { return is_pinch_gesture_active_; } + ++ void SetPageWidthOverride(int); ++ int PageWidthOverride() const { return page_width_override_; } ++ + // Convert the given rect in the main LocalFrameView's coordinates into a rect + // in the viewport. The given and returned rects are in CSS pixels, meaning + // scale isn't applied. +@@ -382,6 +385,8 @@ class CORE_EXPORT VisualViewport : public GarbageCollected, + CompositorElementId elasticity_effect_node_id_; + + bool needs_paint_property_update_; ++ ++ int page_width_override_; + }; + + } // namespace blink +diff --git a/third_party/blink/renderer/core/html/html_meta_element.cc b/third_party/blink/renderer/core/html/html_meta_element.cc +--- a/third_party/blink/renderer/core/html/html_meta_element.cc ++++ b/third_party/blink/renderer/core/html/html_meta_element.cc +@@ -22,6 +22,7 @@ + + #include "third_party/blink/renderer/core/html/html_meta_element.h" + ++#include "base/rand_util.h" + #include "third_party/blink/public/mojom/frame/color_scheme.mojom-blink.h" + #include "third_party/blink/renderer/core/css/style_engine.h" + #include "third_party/blink/renderer/core/dom/document.h" +@@ -565,6 +566,27 @@ void HTMLMetaElement::ProcessContent() { + if (!IsInDocumentTree()) + return; + ++ bool process_default = true; ++ ++ if (GetDocument().GetFrame()) { ++ blink::LocalFrame* frame = GetDocument().GetFrame(); ++ blink::WebContentSettingsClient* settings = frame->GetContentSettingsClient(); ++ blink::Page* page = frame->GetPage(); ++ ++ if (settings->AllowViewportChange(false)) { ++ if (page->PageWidthOverride() == 0) { ++ page->SetPageWidthOverride(base::RandInt(-5, 15)); ++ } ++ ++ float device_width = 1.0 + (page->PageWidthOverride() / 100.0); ++ String newvalue("initial-scale=" + base::NumberToString(device_width)); ++ ProcessViewportContentAttribute(newvalue, ViewportDescription::kViewportMeta); ++ process_default = false; ++ } else { ++ page->SetPageWidthOverride(0); ++ } ++ } ++ + const AtomicString& name_value = FastGetAttribute(html_names::kNameAttr); + if (name_value.IsEmpty()) + return; +@@ -594,8 +616,10 @@ void HTMLMetaElement::ProcessContent() { + return; + + if (EqualIgnoringASCIICase(name_value, "viewport")) { +- ProcessViewportContentAttribute(content_value, +- ViewportDescription::kViewportMeta); ++ if (process_default) { ++ ProcessViewportContentAttribute(content_value, ++ ViewportDescription::kViewportMeta); ++ } + } else if (EqualIgnoringASCIICase(name_value, "referrer") && + GetExecutionContext()) { + UseCounter::Count(&GetDocument(), +diff --git a/third_party/blink/renderer/core/loader/frame_loader.cc b/third_party/blink/renderer/core/loader/frame_loader.cc +--- a/third_party/blink/renderer/core/loader/frame_loader.cc ++++ b/third_party/blink/renderer/core/loader/frame_loader.cc +@@ -371,8 +371,11 @@ void FrameLoader::SaveScrollState() { + history_item->SetVisualViewportScrollOffset( + frame_->GetPage()->GetVisualViewport().VisibleRect().OffsetFromOrigin()); + +- if (frame_->IsMainFrame()) +- history_item->SetPageScaleFactor(frame_->GetPage()->PageScaleFactor()); ++ if (frame_->IsMainFrame()) { ++ int page_width_override = frame_->GetPage()->PageWidthOverride(); ++ if (page_width_override == 0) ++ history_item->SetPageScaleFactor(frame_->GetPage()->PageScaleFactor()); ++ } + + Client()->DidUpdateCurrentHistoryItem(); + } +diff --git a/third_party/blink/renderer/core/page/page.cc b/third_party/blink/renderer/core/page/page.cc +--- a/third_party/blink/renderer/core/page/page.cc ++++ b/third_party/blink/renderer/core/page/page.cc +@@ -507,6 +507,14 @@ float Page::PageScaleFactor() const { + return GetVisualViewport().Scale(); + } + ++void Page::SetPageWidthOverride(float value) { ++ GetVisualViewport().SetPageWidthOverride(value); ++} ++ ++int Page::PageWidthOverride() const { ++ return GetVisualViewport().PageWidthOverride(); ++} ++ + void Page::AllVisitedStateChanged(bool invalidate_visited_link_hashes) { + for (const Page* page : OrdinaryPages()) { + for (Frame* frame = page->main_frame_; frame; +diff --git a/third_party/blink/renderer/core/page/page.h b/third_party/blink/renderer/core/page/page.h +--- a/third_party/blink/renderer/core/page/page.h ++++ b/third_party/blink/renderer/core/page/page.h +@@ -273,6 +273,9 @@ class CORE_EXPORT Page final : public GarbageCollected, + void SetPageScaleFactor(float); + float PageScaleFactor() const; + ++ void SetPageWidthOverride(float); ++ int PageWidthOverride() const; ++ + float InspectorDeviceScaleFactorOverride() const { + return inspector_device_scale_factor_override_; + } +diff --git a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc b/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc +--- a/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc ++++ b/third_party/blink/renderer/modules/screen_enumeration/screen_detailed.cc +@@ -9,12 +9,22 @@ + #include "third_party/blink/renderer/core/frame/local_frame.h" + #include "third_party/blink/renderer/core/frame/settings.h" + #include "third_party/blink/renderer/core/page/chrome_client.h" ++#include "third_party/blink/renderer/core/page/page.h" ++#include "third_party/blink/public/platform/web_content_settings_client.h" + #include "third_party/blink/renderer/platform/wtf/text/string_statics.h" + #include "ui/display/screen_info.h" + #include "ui/display/screen_infos.h" + + namespace blink { + ++namespace { ++ ++float GetScaleOverride(blink::Page* page) { ++ return 1.0 - (page->PageWidthOverride() / 100.0); ++} ++ ++} ++ + ScreenDetailed::ScreenDetailed(LocalDOMWindow* window, + int64_t display_id, + bool label_is_internal, +@@ -87,6 +97,11 @@ bool ScreenDetailed::isInternal() const { + float ScreenDetailed::devicePixelRatio() const { + if (!DomWindow()) + return 0.f; ++ LocalFrame* frame = DomWindow()->GetFrame(); ++ blink::WebContentSettingsClient* settings = frame->GetContentSettingsClient(); ++ blink::Page* page = frame->GetPage(); ++ if (settings->AllowViewportChange(false)) ++ return GetScreenInfo().device_scale_factor * GetScaleOverride(page); + return GetScreenInfo().device_scale_factor; + } + +-- +2.25.1