From: uazo Date: Fri, 26 Aug 2022 13:15:43 +0000 Subject: Viewport Protection Scale the viewport and the screen by a random factor to prevent coordinate-based fingerprinting scripts. The factor is changed at each change of origin. The feature is controlled by a site setting (default disabled) --- .../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 + .../core/css/resolver/style_resolver.cc | 10 ++- .../blink/renderer/core/events/mouse_event.h | 19 +++- .../renderer/core/events/pointer_event.h | 11 +++ .../renderer/core/exported/web_view_impl.cc | 2 +- .../renderer/core/frame/local_dom_window.cc | 27 +++++- .../blink/renderer/core/frame/local_frame.cc | 12 ++- .../blink/renderer/core/frame/local_frame.h | 6 +- .../core/frame/screen_metrics_emulator.cc | 8 +- .../core/frame/screen_metrics_emulator.h | 8 ++ .../core/frame/web_frame_widget_impl.cc | 8 ++ .../core/frame/web_remote_frame_impl.cc | 3 +- .../blink/renderer/core/input/touch.cc | 17 +++- third_party/blink/renderer/core/page/page.cc | 74 ++++++++++++++++ third_party/blink/renderer/core/page/page.h | 7 ++ 32 files changed, 351 insertions(+), 20 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 @@ -88,6 +88,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 @@ -340,6 +340,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 @@ -710,6 +710,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::TOP_ORIGIN_ONLY_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 @@ -208,7 +208,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( @@ -222,6 +223,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 @@ -467,6 +467,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 @@ -101,6 +101,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/resolver/style_resolver.cc b/third_party/blink/renderer/core/css/resolver/style_resolver.cc --- a/third_party/blink/renderer/core/css/resolver/style_resolver.cc +++ b/third_party/blink/renderer/core/css/resolver/style_resolver.cc @@ -1499,8 +1499,14 @@ scoped_refptr StyleResolver::InitialStyleForElement() const { initial_style->SetRtlOrdering( GetDocument().VisuallyOrdered() ? EOrder::kVisual : EOrder::kLogical); - initial_style->SetZoom(InitialZoom()); - initial_style->SetEffectiveZoom(initial_style->Zoom()); + if (GetDocument().GetPage() && GetDocument().GetPage()->IsScreenEmulated()) { + // hides the zoom override to the dom on the html tag + initial_style->SetZoom(1); + initial_style->SetEffectiveZoom(InitialZoom()); + } else { + initial_style->SetZoom(InitialZoom()); + initial_style->SetEffectiveZoom(initial_style->Zoom()); + } initial_style->SetInForcedColorsMode(GetDocument().InForcedColorsMode()); initial_style->SetTapHighlightColor( ComputedStyleInitialValues::InitialTapHighlightColor()); diff --git a/third_party/blink/renderer/core/events/mouse_event.h b/third_party/blink/renderer/core/events/mouse_event.h --- a/third_party/blink/renderer/core/events/mouse_event.h +++ b/third_party/blink/renderer/core/events/mouse_event.h @@ -31,6 +31,7 @@ #include "third_party/blink/renderer/core/dom/events/simulated_click_options.h" #include "third_party/blink/renderer/core/events/ui_event_with_key_state.h" #include "third_party/blink/renderer/platform/wtf/casting.h" +#include "third_party/blink/renderer/core/page/page.h" namespace blink { @@ -141,8 +142,22 @@ class CORE_EXPORT MouseEvent : public UIEventWithKeyState { // Note that these values are adjusted to counter the effects of zoom, so that // values exposed via DOM APIs are invariant under zooming. - virtual double screenX() const { return std::floor(screen_x_); } - virtual double screenY() const { return std::floor(screen_y_); } + virtual double screenX() const { + if (view() && view()->GetFrame() && + view()->GetFrame()->GetPage() && + view()->GetFrame()->GetPage()->IsScreenEmulated()) { + return std::floor(page_x_); + } + return std::floor(screen_x_); + } + virtual double screenY() const { + if (view() && view()->GetFrame() && + view()->GetFrame()->GetPage() && + view()->GetFrame()->GetPage()->IsScreenEmulated()) { + return std::floor(page_y_); + } + return std::floor(screen_y_); + } virtual double clientX() const { return std::floor(client_x_); } virtual double clientY() const { return std::floor(client_y_); } diff --git a/third_party/blink/renderer/core/events/pointer_event.h b/third_party/blink/renderer/core/events/pointer_event.h --- a/third_party/blink/renderer/core/events/pointer_event.h +++ b/third_party/blink/renderer/core/events/pointer_event.h @@ -9,6 +9,7 @@ #include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/events/mouse_event.h" #include "third_party/blink/renderer/platform/wtf/casting.h" +#include "third_party/blink/renderer/core/page/page.h" namespace blink { @@ -57,11 +58,21 @@ class CORE_EXPORT PointerEvent : public MouseEvent { double screenX() const override { if (ShouldHaveIntegerCoordinates()) return MouseEvent::screenX(); + if (view() && view()->GetFrame() && + view()->GetFrame()->GetPage() && + view()->GetFrame()->GetPage()->IsScreenEmulated()) { + return page_x_; + } return screen_x_; } double screenY() const override { if (ShouldHaveIntegerCoordinates()) return MouseEvent::screenY(); + if (view() && view()->GetFrame() && + view()->GetFrame()->GetPage() && + view()->GetFrame()->GetPage()->IsScreenEmulated()) { + return page_y_; + } return screen_y_; } double clientX() const override { diff --git a/third_party/blink/renderer/core/exported/web_view_impl.cc b/third_party/blink/renderer/core/exported/web_view_impl.cc --- a/third_party/blink/renderer/core/exported/web_view_impl.cc +++ b/third_party/blink/renderer/core/exported/web_view_impl.cc @@ -1026,7 +1026,7 @@ WebPagePopupImpl* WebViewImpl::OpenPagePopup(PagePopupClient* client) { page_popup_ = WebPagePopupImpl::Create( std::move(popup_widget_host), std::move(widget_host), std::move(widget_receiver), this, agent_group_scheduler, - opener_widget->GetOriginalScreenInfos(), client); + opener_widget->GetScreenInfos(), client); EnablePopupMouseWheelEventListener(web_opener_frame->LocalRoot()); return page_popup_.get(); } diff --git a/third_party/blink/renderer/core/frame/local_dom_window.cc b/third_party/blink/renderer/core/frame/local_dom_window.cc --- a/third_party/blink/renderer/core/frame/local_dom_window.cc +++ b/third_party/blink/renderer/core/frame/local_dom_window.cc @@ -1395,6 +1395,11 @@ int LocalDOMWindow::outerHeight() const { if (!page) return 0; + // If screen is emulated and this frame is remote cross-origin + // return innerHeight + if (page->IsScreenEmulated() && frame->IsCrossOriginToOutermostMainFrame()) + return innerHeight(); + ChromeClient& chrome_client = page->GetChromeClient(); if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk()) { return static_cast( @@ -1420,6 +1425,11 @@ int LocalDOMWindow::outerWidth() const { if (!page) return 0; + // If screen is emulated and this frame is remote cross-origin + // return innerWidth + if (page->IsScreenEmulated() && frame->IsCrossOriginToOutermostMainFrame()) + return innerWidth(); + ChromeClient& chrome_client = page->GetChromeClient(); if (page->GetSettings().GetReportScreenSizeInPhysicalPixelsQuirk()) { return static_cast( @@ -1607,7 +1617,8 @@ double LocalDOMWindow::devicePixelRatio() const { if (!GetFrame()) return 0.0; - return GetFrame()->DevicePixelRatio(); + // never send the zoom factor override value + return GetFrame()->DevicePixelRatio(/*with_zoom_factor*/false); } void LocalDOMWindow::scrollBy(double x, double y) const { @@ -2149,6 +2160,20 @@ DOMWindow* LocalDOMWindow::open(v8::Isolate* isolate, if (!completed_url.IsEmpty() || result.new_window) result.frame->Navigate(frame_request, WebFrameLoadType::kStandard); + if (result.frame->IsLocalFrame()) { + // we need to use opener setting when opening a iframe without url + // (as "about:blank") to force emulated screen + // since result.frame.GetContentSettingsClient()->AllowViewportChange() + // in the Page::DidCommitLoad() event returns false for these urls + // + // prevent this js code: + // var w = window.open() + // var not_emulated_screen_info = w.screen + result.frame->GetPage()->CalculateEmulatedScreenSetting( + To(result.frame), + /*force*/ GetFrame()->GetContentSettingsClient()->AllowViewportChange(false)); + } + // TODO(japhet): window-open-noopener.html?_top and several tests in // html/browsers/windows/browsing-context-names/ appear to require that // the special case target names (_top, _parent, _self) ignore opener diff --git a/third_party/blink/renderer/core/frame/local_frame.cc b/third_party/blink/renderer/core/frame/local_frame.cc --- a/third_party/blink/renderer/core/frame/local_frame.cc +++ b/third_party/blink/renderer/core/frame/local_frame.cc @@ -1275,6 +1275,10 @@ gfx::SizeF LocalFrame::ResizePageRectsKeepingRatio( return gfx::SizeF(result_width, result_height); } +void LocalFrame::SetPageZoomFactorBaseValue(float factor) { + page_zoom_factor_base_value_ = factor; +} + void LocalFrame::SetPageZoomFactor(float factor) { SetPageAndTextZoomFactors(factor, text_zoom_factor_); } @@ -1414,12 +1418,16 @@ device::mojom::blink::DevicePostureType LocalFrame::GetDevicePosture() { return mojo_handler_->GetDevicePosture(); } -double LocalFrame::DevicePixelRatio() const { +double LocalFrame::DevicePixelRatio(bool with_zoom_factor) const { if (!page_) return 0; double ratio = page_->InspectorDeviceScaleFactorOverride(); - ratio *= PageZoomFactor(); + // with_zoom_factor is default true + if (with_zoom_factor) + ratio *= PageZoomFactor(); + else + ratio = page_zoom_factor_; return ratio; } diff --git a/third_party/blink/renderer/core/frame/local_frame.h b/third_party/blink/renderer/core/frame/local_frame.h --- a/third_party/blink/renderer/core/frame/local_frame.h +++ b/third_party/blink/renderer/core/frame/local_frame.h @@ -373,13 +373,14 @@ class CORE_EXPORT LocalFrame final void SetInViewSourceMode(bool = true); void SetPageZoomFactor(float); - float PageZoomFactor() const { return page_zoom_factor_; } + void SetPageZoomFactorBaseValue(float factor); + float PageZoomFactor() const { return page_zoom_factor_ + page_zoom_factor_base_value_; } void SetTextZoomFactor(float); float TextZoomFactor() const { return text_zoom_factor_; } void SetPageAndTextZoomFactors(float page_zoom_factor, float text_zoom_factor); - double DevicePixelRatio() const; + double DevicePixelRatio(bool with_zoom_factor = true) const; // Informs the local root's document and its local descendant subtree that a // media query value changed. @@ -904,6 +905,7 @@ class CORE_EXPORT LocalFrame final unsigned hidden_ : 1; float page_zoom_factor_; + float page_zoom_factor_base_value_ = 0; float text_zoom_factor_; Member probe_sink_; diff --git a/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc b/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc --- a/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc +++ b/third_party/blink/renderer/core/frame/screen_metrics_emulator.cc @@ -163,6 +163,9 @@ void ScreenMetricsEmulator::Apply() { frame_widget_->SetScreenInfoAndSize(emulated_screen_infos, /*widget_size=*/widget_size, /*visible_viewport_size=*/widget_size); + + // save emulated window size + window_size_ = window_size; } void ScreenMetricsEmulator::UpdateVisualProperties( @@ -191,9 +194,8 @@ void ScreenMetricsEmulator::OnUpdateScreenRects( const gfx::Rect& window_screen_rect) { original_view_screen_rect_ = view_screen_rect; original_window_screen_rect_ = window_screen_rect; - if (emulating_desktop()) { - Apply(); - } + // needed as we need browser ui size + Apply(); } } // namespace blink diff --git a/third_party/blink/renderer/core/frame/screen_metrics_emulator.h b/third_party/blink/renderer/core/frame/screen_metrics_emulator.h --- a/third_party/blink/renderer/core/frame/screen_metrics_emulator.h +++ b/third_party/blink/renderer/core/frame/screen_metrics_emulator.h @@ -61,6 +61,11 @@ class ScreenMetricsEmulator : public GarbageCollected { // Emulated position of the main frame widget (aka view) rect. gfx::Point ViewRectOrigin(); + // Get emulated window size + const gfx::Size& ViewWindowSize() const { + return window_size_; + } + // Disables emulation and applies non-emulated values to the // WebFrameWidgetImpl. Call this before destroying the ScreenMetricsEmulator. void DisableAndApply(); @@ -96,6 +101,9 @@ class ScreenMetricsEmulator : public GarbageCollected { gfx::Rect original_view_screen_rect_; gfx::Rect original_window_screen_rect_; std::vector original_root_window_segments_; + + // Actual size after apply + gfx::Size window_size_; }; } // namespace blink diff --git a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc --- a/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc +++ b/third_party/blink/renderer/core/frame/web_frame_widget_impl.cc @@ -1581,6 +1581,14 @@ void WebFrameWidgetImpl::ApplyVisualPropertiesSizing( if (auto* device_emulator = DeviceEmulator()) { device_emulator->UpdateVisualProperties(visual_properties); + // Shink the view according to browsercontrols + size_ = widget_base_->DIPsToCeiledBlinkSpace( + device_emulator->ViewWindowSize()); + View()->ResizeWithBrowserControls( + size_.value(), + widget_base_->DIPsToCeiledBlinkSpace( + widget_base_->VisibleViewportSizeInDIPs()), + visual_properties.browser_controls_params); return; } diff --git a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc --- a/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc +++ b/third_party/blink/renderer/core/frame/web_remote_frame_impl.cc @@ -348,7 +348,8 @@ void WebRemoteFrameImpl::InitializeFrameVisualProperties( visual_properties.page_scale_factor = ancestor_widget->PageScaleInMainFrame(); visual_properties.is_pinch_gesture_active = ancestor_widget->PinchGestureActiveInMainFrame(); - visual_properties.screen_infos = ancestor_widget->GetOriginalScreenInfos(); + // for a cross-site iframe, set the actual (original or emulated) screen infos + visual_properties.screen_infos = ancestor_widget->GetScreenInfos(); visual_properties.visible_viewport_size = ancestor_widget->VisibleViewportSizeInDIPs(); const WebVector& window_segments = diff --git a/third_party/blink/renderer/core/input/touch.cc b/third_party/blink/renderer/core/input/touch.cc --- a/third_party/blink/renderer/core/input/touch.cc +++ b/third_party/blink/renderer/core/input/touch.cc @@ -30,6 +30,7 @@ #include "third_party/blink/renderer/core/frame/local_frame_view.h" #include "third_party/blink/renderer/core/paint/paint_layer_scrollable_area.h" #include "ui/gfx/geometry/point_f.h" +#include "third_party/blink/renderer/core/page/page.h" namespace blink { @@ -75,7 +76,13 @@ Touch::Touch(LocalFrame* frame, radius_(radius), rotation_angle_(rotation_angle), force_(force), - absolute_location_(PageToAbsolute(frame, page_pos)) {} + absolute_location_(PageToAbsolute(frame, page_pos)) { + if (frame->GetPage() && frame->GetPage()->IsScreenEmulated()) { + // use page_pos instead of screen_pos + screen_pos_.set_x(page_pos_.x()); + screen_pos_.set_y(page_pos_.y()); + } + } Touch::Touch(EventTarget* target, int identifier, @@ -105,7 +112,13 @@ Touch::Touch(LocalFrame* frame, const TouchInit* initializer) radius_(initializer->radiusX(), initializer->radiusY()), rotation_angle_(initializer->rotationAngle()), force_(initializer->force()), - absolute_location_(PageToAbsolute(frame, page_pos_)) {} + absolute_location_(PageToAbsolute(frame, page_pos_)) { + if (frame->GetPage() && frame->GetPage()->IsScreenEmulated()) { + // use page_pos instead of screen_pos + screen_pos_.set_x(page_pos_.x()); + screen_pos_.set_y(page_pos_.y()); + } + } Touch* Touch::CloneWithNewTarget(EventTarget* event_target) const { return MakeGarbageCollected( 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 @@ -92,6 +92,9 @@ #include "third_party/blink/renderer/platform/scheduler/public/agent_group_scheduler.h" #include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h" #include "third_party/skia/include/core/SkColor.h" +#include "base/rand_util.h" +#include "third_party/blink/public/common/widget/device_emulation_params.h" +#include "third_party/blink/renderer/core/exported/web_view_impl.h" namespace blink { @@ -881,7 +884,78 @@ void Page::UpdateAcceleratedCompositingSettings() { } } +void Page::CalculateEmulatedScreenSetting(LocalFrame* frame, bool force) { + blink::WebContentSettingsClient* settings = frame->GetContentSettingsClient(); + if ( (settings && settings->AllowViewportChange(false)) || force) { + // this is the maximum (and minimum) value which in percentage + // corresponds to +- 0.03% + // more or less 3-6 pixels according to the resolution 300-600px + // little enough not to change the page view the user is used to, + // but enough to change all bounds, especially those in floating point + const int max_range = 300; + + // only for the local main frame + // the other local frames use the values from main + // while the remote ones do not communicate the values to the parent + // (and they will be local main frame in their page context) + if (main_frame_ == frame) { + // set the scale factor + double scale_factor = 0; + if (override_window_scale_factor_ != 0) { + scale_factor = override_window_scale_factor_; + } else { + // we allow the increase or decrease of the screen size (and view) + scale_factor = 1.0 + base::RandInt(-max_range, max_range) / 10000.0; + } + + // save the value, so a same domain navigation will reuse same value + override_window_scale_factor_ = scale_factor; + + // we divide the value in half: half for the screen and the view, + // which then the latter will be scaled again by the zoom + double half_random = (scale_factor - 1.0) / 2.0; + + // set emulation params + DeviceEmulationParams params; + // the screen size is changed to match the widget size for mobile emulation + params.screen_type = mojom::EmulatedScreenType::kMobile; + // scale the widget size (and the screen size) by half_random scale factor + params.scale = 1 / (1.0 + half_random); + + GetChromeClient().GetWebView()->EnableDeviceEmulation(params); + + // set zoom factor + // the zoom factor is used by all the functions that manage the bounds, + // which is multiplied by the values in pixels when computed + // we do not modify the actual value but only the one used internally + // it becomes the base value used as the zoom property of the css, but + // it does not appear on the dom (which always remains 1.0) + double zoom_factor = 0; + if (override_zoom_factor_ != 0) { + zoom_factor = override_zoom_factor_; + } else { + // we only allow the page size to decrease, otherwise the scroll + // bars would not be visible + zoom_factor = base::RandInt(0, max_range/2) / 10000.0; + } + + // save the value, so a same domain navigation will reuse same value + override_zoom_factor_ = zoom_factor; + + frame->SetPageZoomFactorBaseValue(zoom_factor); + } + is_screen_emulated = true; + } else { + if (is_screen_emulated && main_frame_ == frame) { + GetChromeClient().GetWebView()->DisableDeviceEmulation(); + frame->SetPageZoomFactorBaseValue(0); + } + is_screen_emulated = false; + } +} + void Page::DidCommitLoad(LocalFrame* frame) { + CalculateEmulatedScreenSetting(frame); if (main_frame_ == frame) { GetConsoleMessageStorage().Clear(); GetInspectorIssueStorage().Clear(); 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 @@ -410,6 +410,9 @@ class CORE_EXPORT Page final : public GarbageCollected, } mojom::blink::FencedFrameMode FencedFrameMode() { return fenced_frame_mode_; } + void CalculateEmulatedScreenSetting(LocalFrame* frame, bool force = false); + bool IsScreenEmulated() { return is_screen_emulated; } + private: friend class ScopedPagePauser; @@ -539,6 +542,10 @@ class CORE_EXPORT Page final : public GarbageCollected, // browser side FrameTree has the FrameTree::Type of kFencedFrame. bool is_fenced_frame_tree_ = false; + bool is_screen_emulated = false; + double override_window_scale_factor_ = 0; + double override_zoom_factor_ = 0; + // If the page is hosted inside an MPArch fenced frame, this tracks the // mode that the fenced frame is set to. This will always be set to kDefault // for the ShadowDOM implementation of fenced frames. -- 2.25.1