From: uazo Date: Tue, 20 Dec 2022 11:06:42 +0000 Subject: Viewport Protection flag 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 feature flag (default enabled) Require: Content-settings-infrastructure.patch --- .../impl/BromiteViewportContentSetting.java | 94 +++++++++++++++++++ .../bromite_content_settings/viewport.grdp | 27 ++++++ .../bromite_content_settings/viewport.inc | 22 +++++ .../bromite_content_settings/VIEWPORT.inc | 1 + .../guest_view/browser/guest_view_base.cc | 8 ++ .../Viewport-Protection-flag.inc | 10 ++ .../features_cc/Viewport-Protection-flag.inc | 3 + .../features_h/Viewport-Protection-flag.inc | 2 + .../common/widget/device_emulation_params.h | 6 +- .../renderer/core/css/css_properties.json5 | 2 +- .../blink/renderer/core/css/media_values.cc | 2 +- .../core/css/resolver/style_resolver.cc | 7 +- .../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 | 43 ++++++++- .../blink/renderer/core/frame/local_frame.cc | 17 +++- .../blink/renderer/core/frame/local_frame.h | 6 +- .../core/frame/screen_metrics_emulator.cc | 18 +++- .../core/frame/screen_metrics_emulator.h | 14 +++ .../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 | 93 ++++++++++++++++++ third_party/blink/renderer/core/page/page.h | 9 ++ .../renderer/core/style/computed_style.h | 6 ++ 26 files changed, 430 insertions(+), 20 deletions(-) create mode 100644 components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteViewportContentSetting.java create mode 100644 components/browser_ui/strings/bromite_content_settings/viewport.grdp create mode 100644 components/content_settings/core/browser/bromite_content_settings/viewport.inc create mode 100644 components/content_settings/core/common/bromite_content_settings/VIEWPORT.inc create mode 100644 cromite_flags/chrome/browser/about_flags_cc/Viewport-Protection-flag.inc create mode 100644 cromite_flags/third_party/blink/common/features_cc/Viewport-Protection-flag.inc create mode 100644 cromite_flags/third_party/blink/common/features_h/Viewport-Protection-flag.inc diff --git a/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteViewportContentSetting.java b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteViewportContentSetting.java new file mode 100644 --- /dev/null +++ b/components/browser_ui/site_settings/android/java/src/org/chromium/components/browser_ui/site_settings/impl/BromiteViewportContentSetting.java @@ -0,0 +1,94 @@ +/* + 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.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, + /*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, + /*summaryOverrideForScreenReader*/ 0); + } + + @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 int getCategoryDescription() { + return R.string.settings_site_settings_viewport_protection_description; + } + + @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/strings/bromite_content_settings/viewport.grdp b/components/browser_ui/strings/bromite_content_settings/viewport.grdp new file mode 100644 --- /dev/null +++ b/components/browser_ui/strings/bromite_content_settings/viewport.grdp @@ -0,0 +1,27 @@ + + + + Viewport Size Protection + + + Viewport Size Protection + + + Viewport Size Protection + + + Hides the screen size by randomly changing the values for each website + + + Enabled + + + Disabled + + + Viewport Protection is enabled with these websites + + + Not allowed to use Viewport Protection + + diff --git a/components/content_settings/core/browser/bromite_content_settings/viewport.inc b/components/content_settings/core/browser/bromite_content_settings/viewport.inc new file mode 100644 --- /dev/null +++ b/components/content_settings/core/browser/bromite_content_settings/viewport.inc @@ -0,0 +1,22 @@ + Register(ContentSettingsType::VIEWPORT, "viewport", CONTENT_SETTING_ALLOW, + WebsiteSettingsInfo::SYNCABLE, + /*allowlisted_schemes=*/{}, + /*valid_settings=*/{CONTENT_SETTING_ALLOW, + CONTENT_SETTING_BLOCK}, + WebsiteSettingsInfo::TOP_ORIGIN_ONLY_SCOPE, + WebsiteSettingsRegistry::ALL_PLATFORMS, + ContentSettingsInfo::INHERIT_IN_INCOGNITO, + ContentSettingsInfo::EXCEPTIONS_ON_SECURE_AND_INSECURE_ORIGINS); + + content_settings::WebsiteSettingsRegistry::GetInstance() + ->GetMutable(ContentSettingsType::VIEWPORT) + ->set_show_into_info_page() + .set_desktop_ui() + .set_is_renderer_content_setting() + .set_title_ui(IDS_SITE_SETTINGS_TYPE_VIEWPORT) + .set_description_ui(IDS_SETTINGS_SITE_SETTINGS_VIEWPORT_PROTECTION_DESCRIPTION) + .set_allowed_ui(IDS_WEBSITE_SETTINGS_CATEGORY_VIEWPORT_ENABLED) + .set_blocked_ui(IDS_WEBSITE_SETTINGS_CATEGORY_VIEWPORT_DISABLED) + .set_allowed_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_VIEWPORT_PROTECTION_ALLOWED_EXCEPTIONS) + .set_blocked_exceptions_ui(IDS_SETTINGS_SITE_SETTINGS_VIEWPORT_PROTECTION_BLOCKED_EXCEPTIONS) + .set_mid_sentence_ui(IDS_SITE_SETTINGS_TYPE_VIEWPORT_MID_SENTENCE); diff --git a/components/content_settings/core/common/bromite_content_settings/VIEWPORT.inc b/components/content_settings/core/common/bromite_content_settings/VIEWPORT.inc new file mode 100644 --- /dev/null +++ b/components/content_settings/core/common/bromite_content_settings/VIEWPORT.inc @@ -0,0 +1 @@ + VIEWPORT, diff --git a/components/guest_view/browser/guest_view_base.cc b/components/guest_view/browser/guest_view_base.cc --- a/components/guest_view/browser/guest_view_base.cc +++ b/components/guest_view/browser/guest_view_base.cc @@ -502,6 +502,14 @@ void GuestViewBase::AttachToOuterWebContentsFrame( if (attachment_callback) std::move(attachment_callback).Run(); + // Disable viewport protection + // I use this hack, i.e. I reuse the SetPageScale method for my own purposes, + // so that I do not have to enter a new call in mojo and thus edit + // so many files. + // the chromium code never uses negative values, so there is no + // danger of inserting problems + embedder_web_contents->SetPageScale(-999); + // Completing attachment will resume suspended resource loads and then send // queued events. SignalWhenReady(base::BindOnce(&GuestViewBase::DidAttach, diff --git a/cromite_flags/chrome/browser/about_flags_cc/Viewport-Protection-flag.inc b/cromite_flags/chrome/browser/about_flags_cc/Viewport-Protection-flag.inc new file mode 100644 --- /dev/null +++ b/cromite_flags/chrome/browser/about_flags_cc/Viewport-Protection-flag.inc @@ -0,0 +1,10 @@ +#ifdef FLAG_SECTION + + {"viewport-protection", + "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.", kOsAll, + FEATURE_VALUE_TYPE(blink::features::kViewportProtection)}, + +#endif diff --git a/cromite_flags/third_party/blink/common/features_cc/Viewport-Protection-flag.inc b/cromite_flags/third_party/blink/common/features_cc/Viewport-Protection-flag.inc new file mode 100644 --- /dev/null +++ b/cromite_flags/third_party/blink/common/features_cc/Viewport-Protection-flag.inc @@ -0,0 +1,3 @@ +CROMITE_FEATURE(kViewportProtection, + "ViewportProtection", + base::FEATURE_DISABLED_BY_DEFAULT); diff --git a/cromite_flags/third_party/blink/common/features_h/Viewport-Protection-flag.inc b/cromite_flags/third_party/blink/common/features_h/Viewport-Protection-flag.inc new file mode 100644 --- /dev/null +++ b/cromite_flags/third_party/blink/common/features_h/Viewport-Protection-flag.inc @@ -0,0 +1,2 @@ +// Enable blink viewport protection +BLINK_COMMON_EXPORT BASE_DECLARE_FEATURE(kViewportProtection); diff --git a/third_party/blink/public/common/widget/device_emulation_params.h b/third_party/blink/public/common/widget/device_emulation_params.h --- a/third_party/blink/public/common/widget/device_emulation_params.h +++ b/third_party/blink/public/common/widget/device_emulation_params.h @@ -21,6 +21,9 @@ namespace blink { struct DeviceEmulationParams { mojom::EmulatedScreenType screen_type = mojom::EmulatedScreenType::kDesktop; + // Forces screen recalculation the same way as mobile + bool force_mobile_calc = false; + // Emulated screen size. Typically full / physical size of the device screen // in DIP. Empty size means using default value: original one for kDesktop // screen position, equal to |view_size| for kMobile. @@ -78,7 +81,8 @@ inline bool operator==(const DeviceEmulationParams& a, a.viewport_offset == b.viewport_offset && a.viewport_scale == b.viewport_scale && a.window_segments == b.window_segments && - a.device_posture == b.device_posture; + a.device_posture == b.device_posture && + a.force_mobile_calc == b.force_mobile_calc; } inline bool operator!=(const DeviceEmulationParams& a, diff --git a/third_party/blink/renderer/core/css/css_properties.json5 b/third_party/blink/renderer/core/css/css_properties.json5 --- a/third_party/blink/renderer/core/css/css_properties.json5 +++ b/third_party/blink/renderer/core/css/css_properties.json5 @@ -3709,6 +3709,7 @@ }, { name: "opacity", + computed_style_custom_functions: ["setter","getter"], property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"], interpolable: true, compositable: true, @@ -3716,7 +3717,6 @@ field_template: "primitive", default_value: "1.0", type_name: "float", - computed_style_custom_functions: ["setter"], typedom_types: ["Number"], valid_for_first_letter: true, valid_for_first_line: true, 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 @@ -184,7 +184,7 @@ bool MediaValues::CalculateStrictMode(LocalFrame* frame) { } float MediaValues::CalculateDevicePixelRatio(LocalFrame* frame) { - return frame->DevicePixelRatio(); + return frame->DevicePixelRatio(false); } bool MediaValues::CalculateDeviceSupportsHDR(LocalFrame* frame) { 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 @@ -1823,7 +1823,12 @@ ComputedStyleBuilder StyleResolver::InitialStyleBuilderForElement() const { ComputedStyleBuilder builder = CreateComputedStyleBuilder(); builder.SetRtlOrdering(GetDocument().VisuallyOrdered() ? EOrder::kVisual : EOrder::kLogical); - builder.SetZoom(InitialZoom()); + if (GetDocument().GetPage() && GetDocument().GetPage()->IsScreenEmulated()) { + // hides the zoom override to the dom on the html tag + builder.SetZoom(1); + } else { + builder.SetZoom(InitialZoom()); + } builder.SetEffectiveZoom(InitialZoom()); builder.SetInForcedColorsMode(GetDocument().InForcedColorsMode()); builder.SetTapHighlightColor( 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 { @@ -143,8 +144,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 @@ -1050,7 +1050,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 @@ -1485,7 +1485,12 @@ int LocalDOMWindow::outerHeight() const { if (frame->IsInFencedFrameTree()) { return innerHeight(); } - + // If screen is emulated and this frame is remote cross-origin + // return innerHeight + if (frame->GetPage() && frame->GetPage()->IsScreenEmulated() + && frame->IsCrossOriginToOutermostMainFrame()) { + return innerHeight(); + } Page* page = frame->GetPage(); if (!page) return 0; @@ -1510,7 +1515,12 @@ int LocalDOMWindow::outerWidth() const { if (frame->IsInFencedFrameTree()) { return innerWidth(); } - + // If screen is emulated and this frame is remote cross-origin + // return innerWidth + if (frame->GetPage() && frame->GetPage()->IsScreenEmulated() + && frame->IsCrossOriginToOutermostMainFrame()) { + return innerWidth(); + } Page* page = frame->GetPage(); if (!page) return 0; @@ -1711,7 +1721,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 { @@ -2301,6 +2312,23 @@ 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 + bool protection_enabled = base::FeatureList::IsEnabled(features::kViewportProtection); + protection_enabled |= GetFrame()->GetContentSettingsClient()->AllowContentSetting( + ContentSettingsType::VIEWPORT, false); + result.frame->GetPage()->CalculateEmulatedScreenSetting( + To(result.frame), + /*force*/ protection_enabled); + } + // 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 @@ -2357,6 +2385,15 @@ DOMWindow* LocalDOMWindow::openPictureInPictureWindow( DCHECK(result.new_window); result.frame->Navigate(frame_request, WebFrameLoadType::kStandard); + + bool protection_enabled = base::FeatureList::IsEnabled(features::kViewportProtection); + protection_enabled |= GetFrame()->GetContentSettingsClient()->AllowContentSetting( + ContentSettingsType::VIEWPORT, false); + result.frame->GetPage()->CalculateEmulatedScreenSetting( + To(result.frame), + /*force*/ protection_enabled); + LOG(INFO) << "---protection_enabled " << protection_enabled; + LocalDOMWindow* pip_dom_window = To(result.frame->DomWindow()); pip_dom_window->SetIsPictureInPictureWindow(); 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 @@ -1465,6 +1465,10 @@ void LocalFrame::RestoreScrollOffsets() { saved_scroll_offsets_ = nullptr; } +void LocalFrame::SetPageZoomFactorBaseValue(float factor) { + page_zoom_factor_base_value_ = factor; +} + void LocalFrame::SetPageZoomFactor(float factor) { SetPageAndTextZoomFactors(factor, text_zoom_factor_); } @@ -1635,12 +1639,16 @@ 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; } @@ -3191,6 +3199,11 @@ void LocalFrame::SetScaleFactor(float scale_factor) { DCHECK(!GetDocument() || !GetDocument()->Printing()); DCHECK(IsMainFrame()); + if (scale_factor <= -999) { + GetPage()->DisableScreenEmulated(); + return; + } + const PageScaleConstraints& constraints = GetPage()->GetPageScaleConstraintsSet().FinalConstraints(); scale_factor = constraints.ClampToConstraints(scale_factor); 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 @@ -424,13 +424,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. @@ -1050,6 +1051,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 @@ -34,6 +34,7 @@ void ScreenMetricsEmulator::Trace(Visitor* vistor) const { } void ScreenMetricsEmulator::DisableAndApply() { + override_screen_type_ = false; frame_widget_->SetScreenMetricsEmulationParameters(false, emulation_params_); frame_widget_->SetScreenRects(original_view_screen_rect_, original_window_screen_rect_); @@ -48,7 +49,16 @@ void ScreenMetricsEmulator::DisableAndApply() { void ScreenMetricsEmulator::ChangeEmulationParams( const DeviceEmulationParams& params) { + if (!params.force_mobile_calc) { + // user has activated device emulator via devtools + override_screen_type_ = true; + // we need to save requested value + last_screen_type_ = params.screen_type; + } emulation_params_ = params; + if (override_screen_type_) { + emulation_params_.screen_type = last_screen_type_; + } Apply(); } @@ -169,6 +179,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( @@ -197,9 +210,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 @@ -63,6 +63,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(); @@ -79,6 +84,8 @@ class ScreenMetricsEmulator : public GarbageCollected { private: bool emulating_desktop() const { + if (emulation_params_.force_mobile_calc == true) + return false; return emulation_params_.screen_type == mojom::blink::EmulatedScreenType::kDesktop; } @@ -91,6 +98,10 @@ class ScreenMetricsEmulator : public GarbageCollected { // Parameters as passed by `WebFrameWidgetImpl::EnableDeviceEmulation()` DeviceEmulationParams emulation_params_; + // Used to remember the user's choice if devtools are activated + bool override_screen_type_ = false; + mojom::EmulatedScreenType last_screen_type_; + // Original values to restore back after emulation ends. display::ScreenInfos original_screen_infos_; gfx::Size original_widget_size_; @@ -99,6 +110,9 @@ class ScreenMetricsEmulator : public GarbageCollected { gfx::Rect original_window_screen_rect_; std::vector original_root_window_segments_ ALLOW_DISCOURAGED_TYPE( "WebFrameWidgetImpl::SetWindowSegments() uses STL"); + + // 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 @@ -1769,6 +1769,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 @@ -359,7 +359,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 @@ -96,6 +96,12 @@ #include "ui/color/color_provider.h" #include "ui/color/color_provider_utils.h" +#include "base/rand_util.h" +#include "build/build_config.h" +#include "third_party/blink/public/common/widget/device_emulation_params.h" +#include "third_party/blink/renderer/core/exported/web_view_impl.h" +#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h" + namespace blink { namespace { @@ -1037,7 +1043,94 @@ void Page::UpdateAcceleratedCompositingSettings() { } } +void Page::CalculateEmulatedScreenSetting(LocalFrame* frame, bool force) { + bool isEnabled = base::FeatureList::IsEnabled(features::kViewportProtection); + blink::WebContentSettingsClient* settings = frame->GetContentSettingsClient(); + isEnabled |= (settings && settings->AllowContentSetting( + ContentSettingsType::VIEWPORT, /*default_value*/ false)); + String protocol = + frame->GetSecurityContext()->GetSecurityOrigin()->Protocol(); + if (SchemeRegistry::IsWebUIScheme(protocol) || disable_screen_emulated_) { + isEnabled = false; + } + if (isEnabled || 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 with force_mobile_calc + params.force_mobile_calc = true; + params.screen_type = mojom::EmulatedScreenType::kDesktop; + // 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::DisableScreenEmulated() { + disable_screen_emulated_ = true; + GetChromeClient().GetWebView()->DisableDeviceEmulation(); + DeprecatedLocalMainFrame()->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 @@ -486,6 +486,10 @@ class CORE_EXPORT Page final : public GarbageCollected, // the close task will still be processed after the swap. void TakeCloseTaskHandler(Page* old_page); + void CalculateEmulatedScreenSetting(LocalFrame* frame, bool force = false); + bool IsScreenEmulated() { return is_screen_emulated_; } + void DisableScreenEmulated(); + private: friend class ScopedPagePauser; class CloseTaskHandler; @@ -640,6 +644,11 @@ 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; + bool disable_screen_emulated_ = false; + double override_window_scale_factor_ = 0; + double override_zoom_factor_ = 0; + // This tracks the mode that the fenced frame is set to. blink::FencedFrame::DeprecatedFencedFrameMode fenced_frame_mode_ = blink::FencedFrame::DeprecatedFencedFrameMode::kDefault; diff --git a/third_party/blink/renderer/core/style/computed_style.h b/third_party/blink/renderer/core/style/computed_style.h --- a/third_party/blink/renderer/core/style/computed_style.h +++ b/third_party/blink/renderer/core/style/computed_style.h @@ -1931,6 +1931,12 @@ class ComputedStyle final : public ComputedStyleBase { IsRunningBackdropFilterAnimationOnCompositor(); } + float Opacity() const { + float v = OpacityInternal(); + if (v >= 0.991f) v = 1.0f; + return v; + } + // Opacity utility functions. bool HasOpacity() const { return Opacity() < 1.0f; } --