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 @@ -503,6 +503,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.viewport_segments == b.viewport_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 @@ -4063,6 +4063,7 @@ }, { name: "opacity", + computed_style_custom_functions: ["setter","getter"], property_methods: ["ParseSingleValue", "CSSValueFromComputedStyleInternal"], interpolable: true, compositable: true, @@ -4070,7 +4071,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 @@ -196,7 +196,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 @@ -1971,7 +1971,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 @@ -1070,7 +1070,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 @@ -1504,7 +1504,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; @@ -1529,7 +1534,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; @@ -1730,7 +1740,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 { @@ -2315,6 +2326,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 @@ -2371,6 +2399,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 @@ -1563,6 +1563,10 @@ void LocalFrame::RestoreScrollOffsets() { saved_scroll_offsets_ = nullptr; } +void LocalFrame::SetPageZoomFactorBaseValue(float factor) { + page_zoom_factor_base_value_ = factor; +} + void LocalFrame::SetLayoutZoomFactor(float factor) { SetLayoutAndTextZoomFactors(factor, text_zoom_factor_); } @@ -1744,12 +1748,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 *= LayoutZoomFactor(); + // with_zoom_factor is default true + if (with_zoom_factor) + ratio *= LayoutZoomFactor(); + else + ratio = layout_zoom_factor_; return ratio; } @@ -3343,6 +3351,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 @@ -431,13 +431,14 @@ class CORE_EXPORT LocalFrame final // for embedded frames the effect of the CSS "zoom" property applied to the // embedding element (e.g.