Reland: Window Placement: Support multi-screen info from Mac shim
The original CL (uploaded as PS1) broke compile on iOS:
https://chromium-review.googlesource.com/c/chromium/src/+/2851592
PS3 fixes compile by using a more canonical EXPECT_DCHECK* test macro.
Pluralize display observation/plumbing/caching for Mac renderers:
- Observe multi-display changes from RWHNSViewBridge (shim).
- Plumb multi-display structs: RWHVCocoa (shim) -> RWHVMac (browser).
- Cache multi-display state in RWHVMac & BrowserCompositorMac.
Enable Mac multi-screen APIs via RenderWidgetHostImpl::GetScreenInfos:
- Get RWHI's multi-screen info from RWHV*, like legacy singular info.
- Add RWHVBase::GetDisplays(), to return live info from display::Screen.
- Override GetDisplays() in RWHVMac, to return cached remote shim info.
Avoid incorrect usage of process-local screen info on RWHVMac:
- Remove improper OnSynchronizedDisplayPropertiesChanged override.
- Start/Stop local Screen observation in MigrateNSViewBridge.
Add DisplayList mojo struct & traits; add current id; update API/impl.
Make RWHVMac better conform with RWHVBase screen info patterns:
- Make RWHVBase::UpdateScreenInfo virtual, override in RWHVMac.
- Inline HasDisplayPropertyChanged in RWHVBase::UpdateScreenInfo.
- Inline BrowserCompositorMac::GetRendererScreenInfo in RWHVMac.
Add some explanatory comments and TODOs with associated bugs.
Bug: 1194700, 1169291, 1169312, 1204273, 1207996
Change-Id: I1a304f0df347b5b611608bc5412d81b70abe5999
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2893191
Commit-Queue: Mike Wasserman <msw@chromium.org>
Auto-Submit: Mike Wasserman <msw@chromium.org>
Reviewed-by: Mitsuru Oshima <oshima@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#883300}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
8133aef8d3
commit
11554ff99d
chrome/browser
ui
exclusive_access
window_placement
content
app_shim_remote_cocoa
render_widget_host_ns_view_bridge.hrender_widget_host_ns_view_bridge.mmrender_widget_host_view_cocoa.mm
browser
renderer_host
browser_compositor_view_mac.hbrowser_compositor_view_mac.mmdisplay_util.cc
input
render_widget_host_impl.ccrender_widget_host_input_event_router.ccrender_widget_host_view_base.ccrender_widget_host_view_base.hrender_widget_host_view_child_frame.ccrender_widget_host_view_child_frame.hrender_widget_host_view_event_handler.ccrender_widget_host_view_mac.hrender_widget_host_view_mac.mmscreen_enumeration
web_contents
common
third_party/blink
common
public
ui/display
@ -663,7 +663,6 @@ class ExperimentalFullscreenControllerInteractiveTest
|
||||
// where the window server's async handling of the fullscreen window state may
|
||||
// transition the window into fullscreen on the actual (non-mocked) display
|
||||
// bounds before or after the window bounds checks, yielding flaky results.
|
||||
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
|
||||
#if !BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#define MAYBE_FullscreenOnSecondDisplay DISABLED_FullscreenOnSecondDisplay
|
||||
#else
|
||||
@ -748,7 +747,6 @@ IN_PROC_BROWSER_TEST_F(ExperimentalFullscreenControllerInteractiveTest,
|
||||
// transition the window into fullscreen on the actual (non-mocked) display
|
||||
// bounds before or after the window bounds checks, yielding flaky results.
|
||||
// TODO(msw): Parameterize the maximized state and combine with the test above.
|
||||
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
|
||||
#if !BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#define MAYBE_FullscreenOnSecondDisplayMaximized \
|
||||
DISABLED_FullscreenOnSecondDisplayMaximized
|
||||
@ -838,7 +836,6 @@ IN_PROC_BROWSER_TEST_F(ExperimentalFullscreenControllerInteractiveTest,
|
||||
// where the window server's async handling of the fullscreen window state may
|
||||
// transition the window into fullscreen on the actual (non-mocked) display
|
||||
// bounds before or after the window bounds checks, yielding flaky results.
|
||||
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
|
||||
#if !BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#define MAYBE_FullscreenChangeDisplays DISABLED_FullscreenChangeDisplays
|
||||
#else
|
||||
|
@ -35,7 +35,6 @@ class WindowPlacementTest : public InProcessBrowserTest {
|
||||
|
||||
// TODO(crbug.com/1183791): Disabled on non-ChromeOS because of races with
|
||||
// SetScreenInstance and observers not being notified.
|
||||
// TODO(crbug.com/1194700): Disabled on Mac because of GetScreenInfos staleness.
|
||||
#if !BUILDFLAG(IS_CHROMEOS_ASH)
|
||||
#define MAYBE_OnScreensChangeEvent DISABLED_OnScreensChangeEvent
|
||||
#else
|
||||
|
@ -80,8 +80,9 @@ class RenderWidgetHostNSViewBridge : public mojom::RenderWidgetHostNSView,
|
||||
bool IsPopup() const { return !!popup_window_; }
|
||||
|
||||
// display::DisplayObserver implementation.
|
||||
void OnDisplayMetricsChanged(const display::Display& display,
|
||||
uint32_t metrics) override;
|
||||
void OnDisplayAdded(const display::Display&) override;
|
||||
void OnDisplayRemoved(const display::Display&) override;
|
||||
void OnDisplayMetricsChanged(const display::Display&, uint32_t) override;
|
||||
|
||||
// The NSView used for input and display.
|
||||
base::scoped_nsobject<RenderWidgetHostViewCocoa> cocoa_view_;
|
||||
|
@ -215,9 +215,17 @@ void RenderWidgetHostNSViewBridge::SetShowingContextMenu(bool showing) {
|
||||
[cocoa_view_ setShowingContextMenu:showing];
|
||||
}
|
||||
|
||||
void RenderWidgetHostNSViewBridge::OnDisplayAdded(const display::Display&) {
|
||||
[cocoa_view_ updateScreenProperties];
|
||||
}
|
||||
|
||||
void RenderWidgetHostNSViewBridge::OnDisplayRemoved(const display::Display&) {
|
||||
[cocoa_view_ updateScreenProperties];
|
||||
}
|
||||
|
||||
void RenderWidgetHostNSViewBridge::OnDisplayMetricsChanged(
|
||||
const display::Display& display,
|
||||
uint32_t changed_metrics) {
|
||||
const display::Display&,
|
||||
uint32_t) {
|
||||
// Note that -updateScreenProperties is also be called by the notification
|
||||
// NSWindowDidChangeBackingPropertiesNotification (some of these calls
|
||||
// will be redundant).
|
||||
|
@ -1391,11 +1391,13 @@ void ExtractUnderlines(NSAttributedString* string,
|
||||
if (!enclosingWindow)
|
||||
return;
|
||||
|
||||
display::Screen* screen = display::Screen::GetScreen();
|
||||
// TODO(ccameron): This will call [enclosingWindow screen], which may return
|
||||
// nil. Do that call here to avoid sending bogus display info to the host.
|
||||
display::Display display =
|
||||
display::Screen::GetScreen()->GetDisplayNearestView(self);
|
||||
_host->OnDisplayChanged(display);
|
||||
display::DisplayList display_list(screen->GetAllDisplays(),
|
||||
screen->GetPrimaryDisplay().id(),
|
||||
screen->GetDisplayNearestView(self).id());
|
||||
_host->OnDisplaysChanged(display_list);
|
||||
}
|
||||
|
||||
// This will be called when the NSView's NSWindow moves from one NSScreen to
|
||||
|
@ -14,11 +14,10 @@
|
||||
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
|
||||
#include "components/viz/common/surfaces/scoped_surface_id_allocator.h"
|
||||
#include "content/browser/renderer_host/delegated_frame_host.h"
|
||||
#include "third_party/blink/public/common/widget/screen_info.h"
|
||||
#include "ui/compositor/compositor.h"
|
||||
#include "ui/compositor/compositor_observer.h"
|
||||
#include "ui/compositor/layer_observer.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/display_list.h"
|
||||
#include "ui/gfx/ca_layer_params.h"
|
||||
|
||||
namespace ui {
|
||||
@ -53,7 +52,7 @@ class CONTENT_EXPORT BrowserCompositorMac : public DelegatedFrameHostClient,
|
||||
ui::AcceleratedWidgetMacNSView* accelerated_widget_mac_ns_view,
|
||||
BrowserCompositorMacClient* client,
|
||||
bool render_widget_host_is_hidden,
|
||||
const display::Display& initial_display,
|
||||
const display::DisplayList& initial_display_list,
|
||||
const viz::FrameSinkId& frame_sink_id);
|
||||
~BrowserCompositorMac() override;
|
||||
|
||||
@ -78,7 +77,7 @@ class CONTENT_EXPORT BrowserCompositorMac : public DelegatedFrameHostClient,
|
||||
// true if any properties that need to be communicated to the
|
||||
// RenderWidgetHostImpl have changed.
|
||||
bool UpdateSurfaceFromNSView(const gfx::Size& new_size_dip,
|
||||
const display::Display& new_display);
|
||||
const display::DisplayList& new_display_list);
|
||||
|
||||
// Update the renderer's SurfaceId to reflect |new_size_in_pixels| in
|
||||
// anticipation of the NSView resizing during auto-resize.
|
||||
@ -110,7 +109,7 @@ class CONTENT_EXPORT BrowserCompositorMac : public DelegatedFrameHostClient,
|
||||
}
|
||||
|
||||
const gfx::Size& GetRendererSize() const { return dfh_size_dip_; }
|
||||
void GetRendererScreenInfo(blink::ScreenInfo* screen_info) const;
|
||||
const display::DisplayList& display_list() const { return display_list_; }
|
||||
viz::ScopedSurfaceIdAllocator GetScopedRendererSurfaceIdAllocator(
|
||||
base::OnceCallback<void()> allocation_task);
|
||||
const viz::LocalSurfaceId& GetRendererLocalSurfaceId();
|
||||
@ -189,7 +188,10 @@ class CONTENT_EXPORT BrowserCompositorMac : public DelegatedFrameHostClient,
|
||||
viz::ParentLocalSurfaceIdAllocator dfh_local_surface_id_allocator_;
|
||||
gfx::Size dfh_size_pixels_;
|
||||
gfx::Size dfh_size_dip_;
|
||||
display::Display dfh_display_;
|
||||
|
||||
// Cached info about the displays relevant to the RenderWidgetHostView.
|
||||
// TODO(crbug.com/1169312): Consolidate this cache with that of RWHVBase.
|
||||
display::DisplayList display_list_;
|
||||
|
||||
// This is used to cache the saved frame state to be used for tab switching
|
||||
// metric. In tab switch in MacOS, DelegatedFrameHost::WasShown is called once
|
||||
|
@ -17,7 +17,6 @@
|
||||
#include "components/viz/common/features.h"
|
||||
#include "components/viz/common/surfaces/local_surface_id.h"
|
||||
#include "content/browser/compositor/image_transport_factory.h"
|
||||
#include "content/browser/renderer_host/display_util.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/context_factory.h"
|
||||
#include "third_party/abseil-cpp/absl/types/optional.h"
|
||||
@ -25,7 +24,6 @@
|
||||
#include "ui/accelerated_widget_mac/window_resize_helper_mac.h"
|
||||
#include "ui/base/layout.h"
|
||||
#include "ui/compositor/recyclable_compositor_mac.h"
|
||||
#include "ui/display/screen.h"
|
||||
#include "ui/gfx/geometry/dip_util.h"
|
||||
#include "ui/gfx/geometry/size_conversions.h"
|
||||
|
||||
@ -52,11 +50,11 @@ BrowserCompositorMac::BrowserCompositorMac(
|
||||
ui::AcceleratedWidgetMacNSView* accelerated_widget_mac_ns_view,
|
||||
BrowserCompositorMacClient* client,
|
||||
bool render_widget_host_is_hidden,
|
||||
const display::Display& initial_display,
|
||||
const display::DisplayList& initial_display_list,
|
||||
const viz::FrameSinkId& frame_sink_id)
|
||||
: client_(client),
|
||||
accelerated_widget_mac_ns_view_(accelerated_widget_mac_ns_view),
|
||||
dfh_display_(initial_display),
|
||||
display_list_(initial_display_list),
|
||||
weak_factory_(this) {
|
||||
g_browser_compositors.Get().insert(this);
|
||||
|
||||
@ -118,22 +116,36 @@ void BrowserCompositorMac::SetBackgroundColor(SkColor background_color) {
|
||||
|
||||
bool BrowserCompositorMac::UpdateSurfaceFromNSView(
|
||||
const gfx::Size& new_size_dip,
|
||||
const display::Display& new_display) {
|
||||
if (new_size_dip == dfh_size_dip_ && new_display == dfh_display_)
|
||||
return false;
|
||||
const display::DisplayList& new_display_list) {
|
||||
if (new_size_dip == dfh_size_dip_) {
|
||||
if (new_display_list == display_list_)
|
||||
return false;
|
||||
if (*new_display_list.GetCurrentDisplayIterator() ==
|
||||
*display_list_.GetCurrentDisplayIterator()) {
|
||||
// Another display changed; no SurfaceId updates are needed here, but
|
||||
// returning true instructs the caller to notify its RenderWidgetHostImpl.
|
||||
// That will synchronize visual properties thoughout the frame tree,
|
||||
// updating cached screen info and events exposed by web platform APIs.
|
||||
display_list_ = new_display_list;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_resize = !dfh_size_dip_.IsEmpty() && new_size_dip != dfh_size_dip_;
|
||||
|
||||
bool needs_new_surface_id =
|
||||
new_size_dip != dfh_size_dip_ ||
|
||||
new_display.device_scale_factor() != dfh_display_.device_scale_factor();
|
||||
new_display_list.GetCurrentDisplayIterator()->device_scale_factor() !=
|
||||
display_list_.GetCurrentDisplayIterator()->device_scale_factor();
|
||||
|
||||
dfh_display_ = new_display;
|
||||
display_list_ = new_display_list;
|
||||
dfh_size_dip_ = new_size_dip;
|
||||
const display::Display& display = *display_list_.GetCurrentDisplayIterator();
|
||||
|
||||
// The device scale factor is always an integer, so the result here is also
|
||||
// an integer.
|
||||
dfh_size_pixels_ = gfx::ToRoundedSize(gfx::ConvertSizeToPixels(
|
||||
dfh_size_dip_, dfh_display_.device_scale_factor()));
|
||||
dfh_size_pixels_ = gfx::ToRoundedSize(
|
||||
gfx::ConvertSizeToPixels(dfh_size_dip_, display.device_scale_factor()));
|
||||
root_layer_->SetBounds(gfx::Rect(dfh_size_dip_));
|
||||
|
||||
if (needs_new_surface_id) {
|
||||
@ -145,8 +157,8 @@ bool BrowserCompositorMac::UpdateSurfaceFromNSView(
|
||||
|
||||
if (recyclable_compositor_) {
|
||||
recyclable_compositor_->UpdateSurface(dfh_size_pixels_,
|
||||
dfh_display_.device_scale_factor(),
|
||||
dfh_display_.color_spaces());
|
||||
display.device_scale_factor(),
|
||||
display.color_spaces());
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -159,16 +171,19 @@ void BrowserCompositorMac::UpdateSurfaceFromChild(
|
||||
const viz::LocalSurfaceId& child_local_surface_id) {
|
||||
if (dfh_local_surface_id_allocator_.UpdateFromChild(child_local_surface_id)) {
|
||||
if (auto_resize_enabled) {
|
||||
dfh_display_.set_device_scale_factor(new_device_scale_factor);
|
||||
// TODO(crbug.com/1169312): Update RWHVMac's cached screen info similarly?
|
||||
display::Display display = *display_list_.GetCurrentDisplayIterator();
|
||||
display.set_device_scale_factor(new_device_scale_factor);
|
||||
display_list_.UpdateDisplay(display);
|
||||
// TODO(danakj): We should avoid lossy conversions to integer DIPs.
|
||||
dfh_size_dip_ = gfx::ToFlooredSize(gfx::ConvertSizeToDips(
|
||||
new_size_in_pixels, dfh_display_.device_scale_factor()));
|
||||
new_size_in_pixels, display.device_scale_factor()));
|
||||
dfh_size_pixels_ = new_size_in_pixels;
|
||||
root_layer_->SetBounds(gfx::Rect(dfh_size_dip_));
|
||||
if (recyclable_compositor_) {
|
||||
recyclable_compositor_->UpdateSurface(
|
||||
dfh_size_pixels_, dfh_display_.device_scale_factor(),
|
||||
dfh_display_.color_spaces());
|
||||
recyclable_compositor_->UpdateSurface(dfh_size_pixels_,
|
||||
display.device_scale_factor(),
|
||||
display.color_spaces());
|
||||
}
|
||||
}
|
||||
delegated_frame_host_->EmbedSurface(
|
||||
@ -270,9 +285,11 @@ void BrowserCompositorMac::TransitionToState(State new_state) {
|
||||
recyclable_compositor_ =
|
||||
ui::RecyclableCompositorMacFactory::Get()->CreateCompositor(
|
||||
content::GetContextFactory());
|
||||
const display::Display& display =
|
||||
*display_list_.GetCurrentDisplayIterator();
|
||||
recyclable_compositor_->UpdateSurface(dfh_size_pixels_,
|
||||
dfh_display_.device_scale_factor(),
|
||||
dfh_display_.color_spaces());
|
||||
display.device_scale_factor(),
|
||||
display.color_spaces());
|
||||
recyclable_compositor_->compositor()->SetRootLayer(root_layer_.get());
|
||||
recyclable_compositor_->compositor()->SetBackgroundColor(background_color_);
|
||||
recyclable_compositor_->widget()->SetNSView(
|
||||
@ -330,7 +347,7 @@ void BrowserCompositorMac::OnFrameTokenChanged(
|
||||
}
|
||||
|
||||
float BrowserCompositorMac::GetDeviceScaleFactor() const {
|
||||
return dfh_display_.device_scale_factor();
|
||||
return display_list_.GetCurrentDisplayIterator()->device_scale_factor();
|
||||
}
|
||||
|
||||
void BrowserCompositorMac::InvalidateLocalSurfaceIdOnEviction() {
|
||||
@ -382,14 +399,12 @@ void BrowserCompositorMac::SetParentUiLayer(ui::Layer* new_parent_ui_layer) {
|
||||
}
|
||||
|
||||
bool BrowserCompositorMac::ForceNewSurfaceForTesting() {
|
||||
display::Display new_display(dfh_display_);
|
||||
new_display.set_device_scale_factor(new_display.device_scale_factor() * 2.0f);
|
||||
return UpdateSurfaceFromNSView(dfh_size_dip_, new_display);
|
||||
}
|
||||
|
||||
void BrowserCompositorMac::GetRendererScreenInfo(
|
||||
blink::ScreenInfo* screen_info) const {
|
||||
DisplayUtil::DisplayToScreenInfo(screen_info, dfh_display_);
|
||||
display::DisplayList new_display_list(display_list_);
|
||||
display::Display display = *new_display_list.GetCurrentDisplayIterator();
|
||||
// TODO(crbug.com/1169312): Update RWHVMac's cached screen info similarly?
|
||||
display.set_device_scale_factor(display.device_scale_factor() * 2.0f);
|
||||
new_display_list.UpdateDisplay(display);
|
||||
return UpdateSurfaceFromNSView(dfh_size_dip_, new_display_list);
|
||||
}
|
||||
|
||||
viz::ScopedSurfaceIdAllocator
|
||||
|
@ -48,6 +48,8 @@ void DisplayUtil::DisplayToScreenInfo(blink::ScreenInfo* screen_info,
|
||||
screen_info->orientation_type = GetOrientationTypeForDesktop(display);
|
||||
#endif
|
||||
|
||||
// TODO(crbug.com/1194700 and crbug.com/1182855): Use cross-process screen
|
||||
// info caches, not local-process info, for child frames and Mac's shim.
|
||||
auto* screen = display::Screen::GetScreen();
|
||||
// Some tests are run with no Screen initialized.
|
||||
screen_info->is_extended = screen && screen->GetNumDisplays() > 1;
|
||||
|
@ -926,7 +926,7 @@ IN_PROC_BROWSER_TEST_P(
|
||||
EXPECT_EQ(ui::TouchSelectionController::INACTIVE,
|
||||
rwhva->selection_controller()->active_status());
|
||||
EXPECT_FALSE(ui::TouchSelectionMenuRunner::GetInstance()->IsRunning());
|
||||
EXPECT_EQ(2.f, rwhva->current_device_scale_factor());
|
||||
EXPECT_EQ(2.f, rwhva->GetCurrentDeviceScaleFactor());
|
||||
EXPECT_EQ(gfx::RectF(),
|
||||
rwhva->selection_controller()->GetVisibleRectBetweenBounds());
|
||||
|
||||
|
@ -2031,14 +2031,6 @@ blink::ScreenInfos RenderWidgetHostImpl::GetScreenInfos() {
|
||||
blink::ScreenInfo current_screen_info;
|
||||
GetScreenInfo(¤t_screen_info);
|
||||
|
||||
// TODO(enne): RenderWidgetHostViewMac caches the ScreenInfo and chooses
|
||||
// not to change it during resizes. This means that the RWHV::GetScreenInfo
|
||||
// returned might be stale wrt GetAllDisplays() below. Fix this.
|
||||
// For now, just return the legacy screen info for mac.
|
||||
#if defined(OS_MAC)
|
||||
return blink::ScreenInfos(current_screen_info);
|
||||
#else
|
||||
|
||||
// If this widget has not been connected to a view yet (or has been
|
||||
// disconnected), the display code may be using a fake primary display.
|
||||
// In these cases, temporarily return the legacy screen info until
|
||||
@ -2056,12 +2048,10 @@ blink::ScreenInfos RenderWidgetHostImpl::GetScreenInfos() {
|
||||
return blink::ScreenInfos(current_screen_info);
|
||||
}
|
||||
|
||||
display::Screen* screen = display::Screen::GetScreen();
|
||||
if (!screen) {
|
||||
return blink::ScreenInfos(current_screen_info);
|
||||
}
|
||||
|
||||
const std::vector<display::Display>& displays = screen->GetAllDisplays();
|
||||
// Get displays from RenderWidgetHostView, not directly from display::Screen.
|
||||
// This helps maintain consistency with the legacy singular GetScreenInfo().
|
||||
// For example, Mac may cache display info from a remote process NSWindow.
|
||||
const std::vector<display::Display>& displays = view_->GetDisplays();
|
||||
|
||||
// Just return the legacy singular ScreenInfo, if its id is invalid or if the
|
||||
// display::Screen is not initialized; each of which occurs in various tests.
|
||||
@ -2073,9 +2063,8 @@ blink::ScreenInfos RenderWidgetHostImpl::GetScreenInfos() {
|
||||
return blink::ScreenInfos(current_screen_info);
|
||||
}
|
||||
|
||||
// If we get here, we are asserting that the current display as reported
|
||||
// by the RenderWidgetHostView is inside of GetAllDisplays().
|
||||
|
||||
// Build multi-screen info from the displays returned by RenderWidgetHostView,
|
||||
// ensure its legacy singular screen info struct is included in this set.
|
||||
blink::ScreenInfos result;
|
||||
bool current_display_added = false;
|
||||
for (const auto& display : displays) {
|
||||
@ -2122,7 +2111,6 @@ blink::ScreenInfos RenderWidgetHostImpl::GetScreenInfos() {
|
||||
|
||||
// Fall back to legacy screen info, if we are in a bad state.
|
||||
return blink::ScreenInfos(current_screen_info);
|
||||
#endif
|
||||
}
|
||||
|
||||
void RenderWidgetHostImpl::GetSnapshotFromBrowser(
|
||||
|
@ -1940,7 +1940,7 @@ void RenderWidgetHostInputEventRouter::SetCursor(const WebCursor& cursor) {
|
||||
return;
|
||||
|
||||
last_device_scale_factor_ =
|
||||
last_mouse_move_root_view_->current_device_scale_factor();
|
||||
last_mouse_move_root_view_->GetCurrentDeviceScaleFactor();
|
||||
if (auto* cursor_manager = last_mouse_move_root_view_->GetCursorManager()) {
|
||||
for (auto it : owner_map_) {
|
||||
if (it.second)
|
||||
|
@ -501,40 +501,73 @@ void RenderWidgetHostViewBase::ProcessAckedTouchEvent(
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
const std::vector<display::Display>& RenderWidgetHostViewBase::GetDisplays()
|
||||
const {
|
||||
// Get the latest info directly from display::Screen, like GetScreenInfo().
|
||||
// TODO(crbug.com/1169312): Unify display info caching and change detection.
|
||||
if (auto* screen = display::Screen::GetScreen())
|
||||
return screen->GetAllDisplays();
|
||||
static const base::NoDestructor<std::vector<display::Display>> kEmptyDisplays;
|
||||
return *kEmptyDisplays;
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewBase::UpdateScreenInfo(gfx::NativeView view) {
|
||||
if (host() && host()->delegate())
|
||||
host()->delegate()->SendScreenRects();
|
||||
|
||||
// TODO(crbug.com/1169312): Unify display info caching and change detection.
|
||||
display::Display::Rotation old_display_rotation = current_display_.rotation();
|
||||
if (HasDisplayPropertyChanged(view) && host()) {
|
||||
OnSynchronizedDisplayPropertiesChanged(old_display_rotation !=
|
||||
current_display_.rotation());
|
||||
host()->NotifyScreenInfoChanged();
|
||||
bool has_display_property_changed = true;
|
||||
bool has_rotation_changed = false;
|
||||
auto* screen = display::Screen::GetScreen();
|
||||
display::DisplayList new_display_list(
|
||||
screen->GetAllDisplays(), screen->GetPrimaryDisplay().id(),
|
||||
screen->GetDisplayNearestView(view).id());
|
||||
if (!display_list_.displays().empty()) {
|
||||
const display::Display& current_display =
|
||||
*display_list_.GetCurrentDisplayIterator();
|
||||
const display::Display& new_display =
|
||||
*new_display_list.GetCurrentDisplayIterator();
|
||||
// Proposed multi-screen APIs expose the current display's status as the
|
||||
// primary display, and whether it is one of several extended displays, so
|
||||
// those changes should also be surfaced via RenderWidgetHostImpl.
|
||||
const bool current_display_is_primary =
|
||||
display_list_.primary_id() == current_display.id();
|
||||
const bool current_display_is_extended =
|
||||
display_list_.displays().size() > 1;
|
||||
const bool new_display_is_primary =
|
||||
new_display_list.primary_id() == new_display.id();
|
||||
const bool new_display_is_extended = new_display_list.displays().size() > 1;
|
||||
has_display_property_changed =
|
||||
current_display.id() != new_display.id() ||
|
||||
current_display.bounds() != new_display.bounds() ||
|
||||
current_display.work_area() != new_display.work_area() ||
|
||||
current_display.device_scale_factor() !=
|
||||
new_display.device_scale_factor() ||
|
||||
current_display.rotation() != new_display.rotation() ||
|
||||
current_display.color_spaces() != new_display.color_spaces() ||
|
||||
current_display.IsInternal() != new_display.IsInternal() ||
|
||||
current_display_is_primary != new_display_is_primary ||
|
||||
current_display_is_extended != new_display_is_extended;
|
||||
has_rotation_changed = current_display.rotation() != new_display.rotation();
|
||||
}
|
||||
|
||||
if (has_display_property_changed) {
|
||||
display_list_ = new_display_list;
|
||||
|
||||
// Notify the associated RenderWidgetHostImpl when screen info has changed.
|
||||
// That will synchronize visual properties needed for frame tree rendering
|
||||
// and for web platform APIs that expose screen and window info and events.
|
||||
if (host()) {
|
||||
OnSynchronizedDisplayPropertiesChanged(has_rotation_changed);
|
||||
host()->NotifyScreenInfoChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderWidgetHostViewBase::HasDisplayPropertyChanged(gfx::NativeView view) {
|
||||
auto* screen = display::Screen::GetScreen();
|
||||
auto display = screen->GetDisplayNearestView(view);
|
||||
bool display_is_extended = screen->GetNumDisplays() > 1;
|
||||
bool display_is_primary = screen->GetPrimaryDisplay().id() == display.id();
|
||||
if (current_display_.id() == display.id() &&
|
||||
current_display_.bounds() == display.bounds() &&
|
||||
current_display_.work_area() == display.work_area() &&
|
||||
current_display_.device_scale_factor() == display.device_scale_factor() &&
|
||||
current_display_.rotation() == display.rotation() &&
|
||||
current_display_.color_spaces() == display.color_spaces() &&
|
||||
current_display_.IsInternal() == display.IsInternal() &&
|
||||
current_display_is_extended_ == display_is_extended &&
|
||||
current_display_is_primary_ == display_is_primary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
current_display_ = display;
|
||||
current_display_is_extended_ = display_is_extended;
|
||||
current_display_is_primary_ = display_is_primary;
|
||||
return true;
|
||||
float RenderWidgetHostViewBase::GetCurrentDeviceScaleFactor() const {
|
||||
if (display_list_.displays().empty())
|
||||
return display::Display().device_scale_factor();
|
||||
return display_list_.GetCurrentDisplayIterator()->device_scale_factor();
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewBase::DidUnregisterFromTextInputManager(
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include "ui/base/ime/text_input_mode.h"
|
||||
#include "ui/base/ime/text_input_type.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/display_list.h"
|
||||
#include "ui/events/event_constants.h"
|
||||
#include "ui/gfx/geometry/rect.h"
|
||||
#include "ui/gfx/native_widget_types.h"
|
||||
@ -101,10 +102,6 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
|
||||
RenderWidgetHostViewBase(const RenderWidgetHostViewBase&) = delete;
|
||||
RenderWidgetHostViewBase& operator=(const RenderWidgetHostViewBase&) = delete;
|
||||
|
||||
float current_device_scale_factor() const {
|
||||
return current_display_.device_scale_factor();
|
||||
}
|
||||
|
||||
// Returns the focused RenderWidgetHost inside this |view|'s RWH.
|
||||
RenderWidgetHostImpl* GetFocusedWidget() const;
|
||||
|
||||
@ -174,11 +171,14 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
|
||||
|
||||
virtual void SendInitialPropertiesIfNeeded() {}
|
||||
|
||||
// Called when screen information or native widget bounds change.
|
||||
void UpdateScreenInfo(gfx::NativeView view);
|
||||
// Get display info known to this view; must be consistent with GetScreenInfo.
|
||||
virtual const std::vector<display::Display>& GetDisplays() const;
|
||||
|
||||
// Updates cached screen information and returns whether it has changed.
|
||||
bool HasDisplayPropertyChanged(gfx::NativeView view);
|
||||
// Called when screen information or native widget bounds change.
|
||||
virtual void UpdateScreenInfo(gfx::NativeView view);
|
||||
|
||||
// Get the device scale factor of the associated display.
|
||||
float GetCurrentDeviceScaleFactor() const;
|
||||
|
||||
// Called by the TextInputManager to notify the view about being removed from
|
||||
// the list of registered views, i.e., TextInputManager is no longer tracking
|
||||
@ -574,10 +574,6 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
|
||||
|
||||
virtual bool HasFallbackSurface() const;
|
||||
|
||||
void set_current_device_scale_factor(float scale) {
|
||||
current_display_.set_device_scale_factor(scale);
|
||||
}
|
||||
|
||||
// The model object. Access is protected to allow access to
|
||||
// RenderWidgetHostViewChildFrame.
|
||||
RenderWidgetHostImpl* host_;
|
||||
@ -585,6 +581,9 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
|
||||
// Whether this view is a frame or a popup.
|
||||
WidgetType widget_type_ = WidgetType::kFrame;
|
||||
|
||||
// Cached information about the renderer's display environment.
|
||||
display::DisplayList display_list_;
|
||||
|
||||
// Indicates whether keyboard lock is active for this view.
|
||||
bool keyboard_locked_ = false;
|
||||
|
||||
@ -650,11 +649,6 @@ class CONTENT_EXPORT RenderWidgetHostViewBase : public RenderWidgetHostView {
|
||||
return view_stopped_flinging_for_test_;
|
||||
}
|
||||
|
||||
// Cached information about the renderer's display environment.
|
||||
display::Display current_display_;
|
||||
bool current_display_is_extended_ = false;
|
||||
bool current_display_is_primary_ = false;
|
||||
|
||||
base::ObserverList<RenderWidgetHostViewBaseObserver>::Unchecked observers_;
|
||||
|
||||
absl::optional<blink::WebGestureEvent> pending_touchpad_pinch_begin_;
|
||||
|
@ -47,22 +47,22 @@ namespace content {
|
||||
// static
|
||||
RenderWidgetHostViewChildFrame* RenderWidgetHostViewChildFrame::Create(
|
||||
RenderWidgetHost* widget,
|
||||
const blink::ScreenInfo& screen_info) {
|
||||
const blink::ScreenInfo& parent_screen_info) {
|
||||
RenderWidgetHostViewChildFrame* view =
|
||||
new RenderWidgetHostViewChildFrame(widget, screen_info);
|
||||
new RenderWidgetHostViewChildFrame(widget, parent_screen_info);
|
||||
view->Init();
|
||||
return view;
|
||||
}
|
||||
|
||||
RenderWidgetHostViewChildFrame::RenderWidgetHostViewChildFrame(
|
||||
RenderWidgetHost* widget_host,
|
||||
const blink::ScreenInfo& screen_info)
|
||||
const blink::ScreenInfo& parent_screen_info)
|
||||
: RenderWidgetHostViewBase(widget_host),
|
||||
frame_sink_id_(
|
||||
base::checked_cast<uint32_t>(widget_host->GetProcess()->GetID()),
|
||||
base::checked_cast<uint32_t>(widget_host->GetRoutingID())),
|
||||
frame_connector_(nullptr),
|
||||
screen_info_(screen_info) {
|
||||
parent_screen_info_(parent_screen_info) {
|
||||
GetHostFrameSinkManager()->RegisterFrameSinkId(
|
||||
frame_sink_id_, this, viz::ReportFirstSurfaceActivation::kNo);
|
||||
GetHostFrameSinkManager()->SetFrameSinkDebugLabel(
|
||||
@ -131,8 +131,17 @@ void RenderWidgetHostViewChildFrame::SetFrameConnector(
|
||||
SetParentFrameSinkId(parent_view->GetFrameSinkId());
|
||||
}
|
||||
|
||||
set_current_device_scale_factor(
|
||||
// Initialize a display struct as needed, to cache the scale factor.
|
||||
// TODO(crbug.com/1182855): Use the parent_view's entire display::DisplayList.
|
||||
if (display_list_.displays().empty()) {
|
||||
display_list_ = display::DisplayList(
|
||||
{display::Display(display::kDefaultDisplayId)},
|
||||
display::kDefaultDisplayId, display::kDefaultDisplayId);
|
||||
}
|
||||
display::Display current_display = *display_list_.GetCurrentDisplayIterator();
|
||||
current_display.set_device_scale_factor(
|
||||
frame_connector_->screen_info().device_scale_factor);
|
||||
display_list_.UpdateDisplay(current_display);
|
||||
|
||||
auto* root_view = frame_connector_->GetRootRenderWidgetHostView();
|
||||
if (root_view) {
|
||||
@ -840,7 +849,7 @@ void RenderWidgetHostViewChildFrame::
|
||||
const cc::RenderFrameMetadata& metadata =
|
||||
host()->render_frame_metadata_provider()->LastRenderFrameMetadata();
|
||||
selection_controller_client_->UpdateSelectionBoundsIfNeeded(
|
||||
metadata.selection, current_device_scale_factor());
|
||||
metadata.selection, GetCurrentDeviceScaleFactor());
|
||||
}
|
||||
}
|
||||
|
||||
@ -908,9 +917,11 @@ RenderWidgetHostViewChildFrame::FilterInputEvent(
|
||||
|
||||
void RenderWidgetHostViewChildFrame::GetScreenInfo(
|
||||
blink::ScreenInfo* screen_info) {
|
||||
// TODO(crbug.com/1182855): Propagate screen infos from the parent on changes
|
||||
// and on connection init; avoid lazily updating the local cache like this.
|
||||
if (frame_connector_)
|
||||
screen_info_ = frame_connector_->screen_info();
|
||||
*screen_info = screen_info_;
|
||||
parent_screen_info_ = frame_connector_->screen_info();
|
||||
*screen_info = parent_screen_info_;
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewChildFrame::EnableAutoResize(
|
||||
|
@ -59,9 +59,10 @@ class CONTENT_EXPORT RenderWidgetHostViewChildFrame
|
||||
public RenderFrameMetadataProvider::Observer,
|
||||
public viz::HostFrameSinkClient {
|
||||
public:
|
||||
// TODO(crbug.com/1182855): Pass multi-screen info from the parent.
|
||||
static RenderWidgetHostViewChildFrame* Create(
|
||||
RenderWidgetHost* widget,
|
||||
const blink::ScreenInfo& screen_info);
|
||||
const blink::ScreenInfo& parent_screen_info);
|
||||
|
||||
void SetFrameConnector(CrossProcessFrameConnector* frame_connector);
|
||||
|
||||
@ -214,8 +215,9 @@ class CONTENT_EXPORT RenderWidgetHostViewChildFrame
|
||||
FRIEND_TEST_ALL_PREFIXES(RenderWidgetHostViewChildFrameTest,
|
||||
ForwardsBeginFrameAcks);
|
||||
|
||||
explicit RenderWidgetHostViewChildFrame(RenderWidgetHost* widget,
|
||||
const blink::ScreenInfo& screen_info);
|
||||
// TODO(crbug.com/1182855): Pass multi-screen info from the parent.
|
||||
RenderWidgetHostViewChildFrame(RenderWidgetHost* widget,
|
||||
const blink::ScreenInfo& parent_screen_info);
|
||||
void Init();
|
||||
|
||||
// Sets |parent_frame_sink_id_| and registers frame sink hierarchy. If the
|
||||
@ -308,7 +310,7 @@ class CONTENT_EXPORT RenderWidgetHostViewChildFrame
|
||||
// created, to be used before this view is connected to its FrameDelegate.
|
||||
// This is kept up to date anytime GetScreenInfo() is called and we have
|
||||
// a FrameDelegate.
|
||||
blink::ScreenInfo screen_info_;
|
||||
blink::ScreenInfo parent_screen_info_;
|
||||
|
||||
base::WeakPtrFactory<RenderWidgetHostViewChildFrame> weak_factory_{this};
|
||||
DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewChildFrame);
|
||||
|
@ -959,7 +959,7 @@ bool RenderWidgetHostViewEventHandler::MatchesSynthesizedMovePosition(
|
||||
const blink::WebMouseEvent& event) {
|
||||
if (event.GetType() == blink::WebInputEvent::Type::kMouseMove &&
|
||||
synthetic_move_position_.has_value()) {
|
||||
if (IsFractionalScaleFactor(host_view_->current_device_scale_factor())) {
|
||||
if (IsFractionalScaleFactor(host_view_->GetCurrentDeviceScaleFactor())) {
|
||||
// For fractional scale factors, the conversion from pixels to dip and
|
||||
// vice versa could result in off by 1 or 2 errors which hurts us because
|
||||
// the artificial move to center event cause the cursor to bounce around
|
||||
|
@ -148,10 +148,11 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
|
||||
gfx::NativeViewAccessible AccessibilityGetNativeViewAccessibleForWindow()
|
||||
override;
|
||||
absl::optional<SkColor> GetBackgroundColor() override;
|
||||
void OnSynchronizedDisplayPropertiesChanged(bool rotation) override;
|
||||
|
||||
void TransformPointToRootSurface(gfx::PointF* point) override;
|
||||
gfx::Rect GetBoundsInRootWindow() override;
|
||||
const std::vector<display::Display>& GetDisplays() const override;
|
||||
void UpdateScreenInfo(gfx::NativeView view) override;
|
||||
viz::ScopedSurfaceIdAllocator DidUpdateVisualProperties(
|
||||
const cc::RenderFrameMetadata& metadata) override;
|
||||
void DidNavigate() override;
|
||||
@ -296,11 +297,6 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
|
||||
.max_time_between_phase_ended_and_momentum_phase_began();
|
||||
}
|
||||
|
||||
// Update the size, scale factor, color profile, vsync parameters, and any
|
||||
// other properties of the NSView or its NSScreen. Propagate these to the
|
||||
// RenderWidgetHostImpl as well.
|
||||
void UpdateNSViewAndDisplayProperties();
|
||||
|
||||
// RenderWidgetHostNSViewHostHelper implementation.
|
||||
id GetRootBrowserAccessibilityElement() override;
|
||||
id GetFocusedBrowserAccessibilityElement() override;
|
||||
@ -334,7 +330,7 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
|
||||
bool attached_to_window) override;
|
||||
void OnWindowFrameInScreenChanged(
|
||||
const gfx::Rect& window_frame_in_screen_dip) override;
|
||||
void OnDisplayChanged(const display::Display& display) override;
|
||||
void OnDisplaysChanged(const display::DisplayList& display_list) override;
|
||||
void BeginKeyboardEvent() override;
|
||||
void EndKeyboardEvent() override;
|
||||
void ForwardKeyboardEventWithCommands(
|
||||
@ -566,9 +562,6 @@ class CONTENT_EXPORT RenderWidgetHostViewMac
|
||||
// (where the origin is the upper-left corner of Screen::GetPrimaryDisplay).
|
||||
gfx::Rect window_frame_in_screen_dip_;
|
||||
|
||||
// Cached copy of the display information pushed to us from the NSView.
|
||||
display::Display display_;
|
||||
|
||||
// Whether or not the NSView's NSWindow is the key window.
|
||||
bool is_window_key_ = false;
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#import "content/browser/accessibility/browser_accessibility_mac.h"
|
||||
#include "content/browser/accessibility/browser_accessibility_manager_mac.h"
|
||||
#include "content/browser/renderer_host/cursor_manager.h"
|
||||
#include "content/browser/renderer_host/display_util.h"
|
||||
#include "content/browser/renderer_host/input/motion_event_web.h"
|
||||
#import "content/browser/renderer_host/input/synthetic_gesture_target_mac.h"
|
||||
#include "content/browser/renderer_host/input/web_input_event_builders_mac.h"
|
||||
@ -187,13 +188,15 @@ RenderWidgetHostViewMac::RenderWidgetHostViewMac(RenderWidgetHost* widget)
|
||||
// Guess that the initial screen we will be on is the screen of the current
|
||||
// window (since that's the best guess that we have, and is usually right).
|
||||
// https://crbug.com/357443
|
||||
display_ =
|
||||
display::Screen::GetScreen()->GetDisplayNearestWindow([NSApp keyWindow]);
|
||||
display::Screen* screen = display::Screen::GetScreen();
|
||||
display_list_ = display::DisplayList(
|
||||
screen->GetAllDisplays(), screen->GetPrimaryDisplay().id(),
|
||||
screen->GetDisplayNearestWindow([NSApp keyWindow]).id());
|
||||
|
||||
viz::FrameSinkId frame_sink_id = host()->GetFrameSinkId();
|
||||
|
||||
browser_compositor_ = std::make_unique<BrowserCompositorMac>(
|
||||
this, this, host()->is_hidden(), display_, frame_sink_id);
|
||||
this, this, host()->is_hidden(), display_list_, frame_sink_id);
|
||||
DCHECK(![GetInProcessNSView() window]);
|
||||
|
||||
host()->SetView(this);
|
||||
@ -254,6 +257,10 @@ void RenderWidgetHostViewMac::MigrateNSViewBridge(
|
||||
// If no host is specified, then use the locally hosted NSView.
|
||||
if (!remote_cocoa_application) {
|
||||
ns_view_ = in_process_ns_view_bridge_.get();
|
||||
// Observe local Screen info, to correspond with the locally hosted NSView.
|
||||
// TODO(crbug.com/1204273): Maybe recreate `in_process_ns_view_bridge_`?
|
||||
display::Screen::GetScreen()->RemoveObserver(
|
||||
in_process_ns_view_bridge_.get());
|
||||
return;
|
||||
}
|
||||
|
||||
@ -276,6 +283,12 @@ void RenderWidgetHostViewMac::MigrateNSViewBridge(
|
||||
|
||||
ns_view_ = remote_ns_view_.get();
|
||||
|
||||
// End local display::Screen observation via `in_process_ns_view_bridge_`;
|
||||
// the remote NSWindow's display::Screen information will be sent by Mojo.
|
||||
// TODO(crbug.com/1204273): Maybe just destroy `in_process_ns_view_bridge_`?
|
||||
display::Screen::GetScreen()->RemoveObserver(
|
||||
in_process_ns_view_bridge_.get());
|
||||
|
||||
// Popup windows will specify an invalid |parent_ns_view_id|, because popups
|
||||
// have their own NSWindows (of which they are the content NSView).
|
||||
if (parent_ns_view_id != remote_cocoa::kInvalidNSViewId)
|
||||
@ -397,45 +410,21 @@ RenderWidgetHostImpl* RenderWidgetHostViewMac::GetWidgetForIme() {
|
||||
return GetActiveWidget();
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::UpdateNSViewAndDisplayProperties() {
|
||||
display_link_ = ui::DisplayLinkMac::GetForDisplay(display_.id());
|
||||
if (!display_link_) {
|
||||
// Note that on some headless systems, the display link will fail to be
|
||||
// created, so this should not be a fatal error.
|
||||
LOG(ERROR) << "Failed to create display link.";
|
||||
}
|
||||
|
||||
// During auto-resize it is the responsibility of the caller to ensure that
|
||||
// the NSView and RenderWidgetHostImpl are kept in sync.
|
||||
if (host()->auto_resize_enabled())
|
||||
return;
|
||||
|
||||
if (host()->delegate())
|
||||
host()->delegate()->SendScreenRects();
|
||||
else
|
||||
host()->SendScreenRects();
|
||||
|
||||
// RenderWidgetHostImpl will query BrowserCompositorMac for the dimensions
|
||||
// to send to the renderer, so it is required that BrowserCompositorMac be
|
||||
// updated first. Only notify RenderWidgetHostImpl of the update if any
|
||||
// properties it will query have changed.
|
||||
if (browser_compositor_->UpdateSurfaceFromNSView(
|
||||
view_bounds_in_window_dip_.size(), display_)) {
|
||||
host()->NotifyScreenInfoChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::GetScreenInfo(blink::ScreenInfo* screen_info) {
|
||||
browser_compositor_->GetRendererScreenInfo(screen_info);
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::OnSynchronizedDisplayPropertiesChanged(
|
||||
bool rotation) {
|
||||
// Update cached screen information when the current display changes.
|
||||
const auto& display =
|
||||
display::Screen::GetScreen()->GetDisplayNearestWindow([NSApp keyWindow]);
|
||||
if (display != display_)
|
||||
OnDisplayChanged(display);
|
||||
const display::DisplayList& displays = browser_compositor_->display_list();
|
||||
DisplayUtil::DisplayToScreenInfo(screen_info,
|
||||
*displays.GetCurrentDisplayIterator());
|
||||
// Recalculate some ScreenInfo properties from the cached screen info, which
|
||||
// may originate from a remote process that hosts the associated NSWindow.
|
||||
// DisplayToScreenInfo derives some properties from the latest display::Screen
|
||||
// info observed directly in this process, which may be intermittently
|
||||
// out-of-sync with remote info. Also, BrowserCompositorMac and
|
||||
// RenderWidgetHostViewMac do not update their cached screen info during
|
||||
// auto-resize.
|
||||
// TODO(crbug.com/1194700): Pass cached remote process screen info to
|
||||
// DisplayToScreenInfo; it should not use local process info internally.
|
||||
screen_info->is_extended = displays.displays().size() > 1;
|
||||
screen_info->is_primary = screen_info->display_id == displays.primary_id();
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::Show() {
|
||||
@ -744,6 +733,50 @@ void RenderWidgetHostViewMac::UpdateTooltip(
|
||||
SetTooltipText(tooltip_text);
|
||||
}
|
||||
|
||||
const std::vector<display::Display>& RenderWidgetHostViewMac::GetDisplays()
|
||||
const {
|
||||
// Return cached screen info, which may originate from a remote process that
|
||||
// hosts the associated NSWindow. The latest display::Screen info observed
|
||||
// directly in this process may be intermittently out-of-sync with that info.
|
||||
// Also, BrowserCompositorMac and RenderWidgetHostViewMac do not update their
|
||||
// cached screen info during auto-resize.
|
||||
return browser_compositor_->display_list().displays();
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::UpdateScreenInfo(gfx::NativeView view) {
|
||||
// Update the size, scale factor, color profile, vsync parameters, and any
|
||||
// other properties of the NSView or pertinent NSScreens. Propagate these to
|
||||
// the RenderWidgetHostImpl as well.
|
||||
|
||||
display_link_ = ui::DisplayLinkMac::GetForDisplay(display_list_.current_id());
|
||||
if (!display_link_) {
|
||||
// Note that on some headless systems, the display link will fail to be
|
||||
// created, so this should not be a fatal error.
|
||||
LOG(ERROR) << "Failed to create display link.";
|
||||
}
|
||||
|
||||
// During auto-resize it is the responsibility of the caller to ensure that
|
||||
// the NSView and RenderWidgetHostImpl are kept in sync.
|
||||
if (host()->auto_resize_enabled())
|
||||
return;
|
||||
|
||||
if (host()->delegate())
|
||||
host()->delegate()->SendScreenRects();
|
||||
else
|
||||
host()->SendScreenRects();
|
||||
|
||||
// TODO(crbug.com/1169312): Unify display info caching and change detection.
|
||||
// Notify the associated RenderWidgetHostImpl when screen info has changed.
|
||||
// That will synchronize visual properties needed for frame tree rendering
|
||||
// and for web platform APIs that expose screen and window info and events.
|
||||
// RenderWidgetHostImpl will query BrowserCompositorMac for the dimensions
|
||||
// to send to the renderer, so BrowserCompositorMac must be updated first.
|
||||
if (browser_compositor_->UpdateSurfaceFromNSView(
|
||||
view_bounds_in_window_dip_.size(), display_list_)) {
|
||||
host()->NotifyScreenInfoChanged();
|
||||
}
|
||||
}
|
||||
|
||||
viz::ScopedSurfaceIdAllocator
|
||||
RenderWidgetHostViewMac::DidUpdateVisualProperties(
|
||||
const cc::RenderFrameMetadata& metadata) {
|
||||
@ -868,10 +901,13 @@ void RenderWidgetHostViewMac::CopyFromSurface(
|
||||
->GetDelegatedFrameHost()
|
||||
->GetWeakPtr();
|
||||
}
|
||||
// TODO(crbug.com/1169321): Resolve potential differences between display info
|
||||
// caches in RenderWidgetHostViewMac and BrowserCompositorMac.
|
||||
RenderWidgetHostViewBase::CopyMainAndPopupFromSurface(
|
||||
host()->GetWeakPtr(),
|
||||
browser_compositor_->GetDelegatedFrameHost()->GetWeakPtr(), popup_host,
|
||||
popup_frame_host, src_subrect, dst_size, display_.device_scale_factor(),
|
||||
popup_frame_host, src_subrect, dst_size,
|
||||
display_list_.GetCurrentDisplayIterator()->device_scale_factor(),
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
@ -1510,7 +1546,7 @@ void RenderWidgetHostViewMac::OnBoundsInWindowChanged(
|
||||
}
|
||||
|
||||
if (view_size_changed)
|
||||
UpdateNSViewAndDisplayProperties();
|
||||
UpdateScreenInfo(GetNativeView());
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::OnWindowFrameInScreenChanged(
|
||||
@ -1525,11 +1561,16 @@ void RenderWidgetHostViewMac::OnWindowFrameInScreenChanged(
|
||||
host()->SendScreenRects();
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::OnDisplayChanged(
|
||||
const display::Display& display) {
|
||||
display_ = display;
|
||||
// TODO(crbug.com/1169291): Unify per-platform DisplayObserver instances.
|
||||
UpdateNSViewAndDisplayProperties();
|
||||
void RenderWidgetHostViewMac::OnDisplaysChanged(
|
||||
const display::DisplayList& display_list) {
|
||||
// Cache this screen info, which may originate from a remote process that
|
||||
// hosts the associated NSWindow. The latest display::Screen info observed
|
||||
// directly in this process may be intermittently out-of-sync with that info.
|
||||
// Also, BrowserCompositorMac and RenderWidgetHostViewMac do not update their
|
||||
// cached screen info during auto-resize.
|
||||
// TODO(crbug.com/1169291): Unify screen info plumbing, caching, etc.
|
||||
display_list_ = display_list;
|
||||
UpdateScreenInfo(GetNativeView());
|
||||
}
|
||||
|
||||
void RenderWidgetHostViewMac::BeginKeyboardEvent() {
|
||||
|
@ -120,6 +120,8 @@ IN_PROC_BROWSER_TEST_F(ScreenEnumerationTest, DISABLED_GetScreensBasic) {
|
||||
EXPECT_EQ(GetExpectedScreens(), base::Value::AsListValue(result.value));
|
||||
}
|
||||
|
||||
// TODO(crbug.com/1205676): Remove this test in favor of IsExtendedBasic.
|
||||
// window.isMultiScreen() is deprecated in favor of screen.isExtended.
|
||||
IN_PROC_BROWSER_TEST_F(ScreenEnumerationTest, IsMultiScreenBasic) {
|
||||
ASSERT_TRUE(NavigateToURL(shell(), GetTestUrl(nullptr, "empty.html")));
|
||||
ASSERT_EQ(true, EvalJs(shell(), "'isMultiScreen' in self"));
|
||||
@ -191,6 +193,8 @@ IN_PROC_BROWSER_TEST_F(FakeScreenEnumerationTest, MAYBE_GetScreensFaked) {
|
||||
EXPECT_EQ(GetExpectedScreens(), base::Value::AsListValue(result.value));
|
||||
}
|
||||
|
||||
// TODO(crbug.com/1205676): Remove this test in favor of IsExtendedFaked.
|
||||
// window.isMultiScreen() is deprecated in favor of screen.isExtended.
|
||||
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
|
||||
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
|
||||
#if defined(OS_ANDROID) || defined(OS_WIN)
|
||||
@ -233,50 +237,51 @@ IN_PROC_BROWSER_TEST_F(FakeScreenEnumerationTest, MAYBE_IsExtendedFaked) {
|
||||
// TODO(crbug.com/1042990): Windows crashes static casting to ScreenWin.
|
||||
// TODO(crbug.com/1042990): Android requires a GetDisplayNearestView overload.
|
||||
#if defined(OS_ANDROID) || defined(OS_WIN)
|
||||
#define MAYBE_OnScreensChangeNoPermission DISABLED_OnScreensChangeNoPermission
|
||||
#define MAYBE_ScreenOnchangeNoPermission DISABLED_ScreenOnchangeNoPermission
|
||||
#else
|
||||
#define MAYBE_OnScreensChangeNoPermission OnScreensChangeNoPermission
|
||||
#define MAYBE_ScreenOnchangeNoPermission ScreenOnchangeNoPermission
|
||||
#endif
|
||||
// Sites with no permission only get an event if isMultiScreen() changes.
|
||||
// Sites with no permission only get an event if screen.isExtended changes.
|
||||
// TODO(crbug.com/1119974): Need content_browsertests permission controls.
|
||||
IN_PROC_BROWSER_TEST_F(FakeScreenEnumerationTest,
|
||||
MAYBE_OnScreensChangeNoPermission) {
|
||||
MAYBE_ScreenOnchangeNoPermission) {
|
||||
ASSERT_TRUE(NavigateToURL(test_shell(), GetTestUrl(nullptr, "empty.html")));
|
||||
ASSERT_EQ(true, EvalJs(test_shell(), "'onscreenschange' in self"));
|
||||
constexpr char kSetOnScreensChange[] = R"(
|
||||
onscreenschange = function() { ++document.title; };
|
||||
ASSERT_EQ(true, EvalJs(test_shell(), "'onchange' in screen"));
|
||||
constexpr char kSetScreenOnchange[] = R"(
|
||||
window.screen.onchange = function() { ++document.title; };
|
||||
document.title = 0;
|
||||
)";
|
||||
EXPECT_EQ(0, EvalJs(test_shell(), kSetOnScreensChange));
|
||||
EXPECT_EQ(0, EvalJs(test_shell(), kSetScreenOnchange));
|
||||
EXPECT_EQ(false, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("0", EvalJs(test_shell(), "document.title"));
|
||||
|
||||
// isMultiScreen() changes from false to true here, so an event is sent.
|
||||
EXPECT_EQ(false, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
// screen.isExtended changes from false to true here, so an event is sent.
|
||||
EXPECT_EQ(false, EvalJs(test_shell(), "screen.isExtended"));
|
||||
screen()->display_list().AddDisplay({1, gfx::Rect(100, 100, 801, 802)},
|
||||
display::DisplayList::Type::NOT_PRIMARY);
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
|
||||
|
||||
// isMultiScreen() remains unchanged, so no event is sent.
|
||||
// screen.isExtended remains unchanged, so no event is sent.
|
||||
screen()->display_list().AddDisplay({2, gfx::Rect(901, 100, 801, 802)},
|
||||
display::DisplayList::Type::NOT_PRIMARY);
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
|
||||
|
||||
// isMultiScreen() remains unchanged, so no event is sent.
|
||||
// screen.isExtended remains unchanged, so no event is sent.
|
||||
EXPECT_NE(0u, screen()->display_list().UpdateDisplay(
|
||||
{2, gfx::Rect(902, 100, 801, 802)}));
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
|
||||
|
||||
// isMultiScreen() remains unchanged, so no event is sent.
|
||||
// screen.isExtended remains unchanged, so no event is sent.
|
||||
screen()->display_list().RemoveDisplay(2);
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
EXPECT_EQ(true, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("1", EvalJs(test_shell(), "document.title"));
|
||||
|
||||
// isMultiScreen() changes from true to false here, so an event is sent.
|
||||
// screen.isExtended changes from true to false here, so an event is sent.
|
||||
screen()->display_list().RemoveDisplay(1);
|
||||
EXPECT_EQ(false, EvalJs(test_shell(), kIsMultiScreenScript));
|
||||
EXPECT_EQ(false, EvalJs(test_shell(), "screen.isExtended"));
|
||||
EXPECT_EQ("2", EvalJs(test_shell(), "document.title"));
|
||||
}
|
||||
|
||||
|
@ -1355,12 +1355,20 @@ void WebContentsImpl::OnScreensChange(bool is_multi_screen_changed) {
|
||||
OPTIONAL_TRACE_EVENT1("content", "WebContentsImpl::OnScreensChange",
|
||||
"is_multi_screen_changed", is_multi_screen_changed);
|
||||
// Allow fullscreen requests shortly after user-generated screens changes.
|
||||
// TODO(crbug.com/1169291): Mac should not activate this on local process
|
||||
// display::Screen signals, but via RenderWidgetHostViewMac screen updates.
|
||||
transient_allow_fullscreen_.Activate();
|
||||
|
||||
// Mac display info may originate from a remote process hosting the NSWindow;
|
||||
// this local process display::Screen signal should not trigger updates.
|
||||
// TODO(crbug.com/1169291): Unify screen info plumbing, caching, etc.
|
||||
#if !defined(OS_MAC)
|
||||
// Send |is_multi_screen_changed| events to all visible frames, but limit
|
||||
// other events to frames with the Window Placement permission. This obviates
|
||||
// the most pressing need for sites to poll isMultiScreen(), which is exposed
|
||||
// without explicit permission, while also protecting privacy.
|
||||
// TODO(crbug.com/1109989): Postpone events; refine utility/privacy balance.
|
||||
// TODO(crbug.com/1205676): Remove this deprecated window.screenschange code.
|
||||
for (FrameTreeNode* node : frame_tree_.Nodes()) {
|
||||
RenderFrameHostImpl* rfh = node->current_frame_host();
|
||||
if ((is_multi_screen_changed &&
|
||||
@ -1378,6 +1386,7 @@ void WebContentsImpl::OnScreensChange(bool is_multi_screen_changed) {
|
||||
GetRenderViewHost()->GetWidget()->GetView()) {
|
||||
view->UpdateScreenInfo(view->GetNativeView());
|
||||
}
|
||||
#endif // !OS_MAC
|
||||
}
|
||||
|
||||
void WebContentsImpl::OnScreenOrientationChange() {
|
||||
|
@ -10,7 +10,7 @@ import "third_party/blink/public/mojom/webshare/share_error.mojom";
|
||||
import "ui/base/mojom/attributed_string.mojom";
|
||||
import "ui/base/cursor/mojom/cursor.mojom";
|
||||
import "ui/base/ime/mojom/ime_types.mojom";
|
||||
import "ui/display/mojom/display.mojom";
|
||||
import "ui/display/mojom/display_list.mojom";
|
||||
import "ui/events/mojom/event.mojom";
|
||||
import "ui/gfx/geometry/mojom/geometry.mojom";
|
||||
import "ui/gfx/mojom/ca_layer_params.mojom";
|
||||
@ -139,8 +139,8 @@ interface RenderWidgetHostNSViewHost {
|
||||
OnWindowFrameInScreenChanged(
|
||||
gfx.mojom.Rect window_frame_in_screen_dip);
|
||||
|
||||
// Indicate changes to the NSView's NSScreen's properties.
|
||||
OnDisplayChanged(display.mojom.Display display);
|
||||
// Indicate changes to the NSScreen hosting the NSView, or other NSScreens.
|
||||
OnDisplaysChanged(display.mojom.DisplayList display_list);
|
||||
|
||||
// Indicate the begin and end block of a keyboard event. The beginning of this
|
||||
// block will record the active RenderWidgetHost, and will forward all
|
||||
|
@ -27,9 +27,8 @@ ScreenInfo& ScreenInfos::mutable_current() {
|
||||
}
|
||||
|
||||
const ScreenInfo& ScreenInfos::current() const {
|
||||
const auto& current_screen_info = base::ranges::find_if(
|
||||
screen_infos,
|
||||
[&](const ScreenInfo& s) { return s.display_id == current_display_id; });
|
||||
const auto& current_screen_info = base::ranges::find(
|
||||
screen_infos, current_display_id, &ScreenInfo::display_id);
|
||||
CHECK(current_screen_info != screen_infos.end());
|
||||
return *current_screen_info;
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
namespace mojo {
|
||||
|
||||
// static
|
||||
bool StructTraits<blink::mojom::ScreenInfosDataView, blink::ScreenInfos>::Read(
|
||||
blink::mojom::ScreenInfosDataView data,
|
||||
blink::ScreenInfos* out) {
|
||||
|
@ -38,6 +38,8 @@
|
||||
|
||||
namespace blink {
|
||||
|
||||
// This structure roughly parallels display::Display. It may be desirable to
|
||||
// deprecate derived counterparts of ui/display types; see crbug.com/1208469.
|
||||
struct BLINK_COMMON_EXPORT ScreenInfo {
|
||||
// Device scale factor. Specifies the ratio between physical and logical
|
||||
// pixels.
|
||||
|
@ -12,6 +12,8 @@ namespace blink {
|
||||
|
||||
// Information about a set of screens that are relevant to a particular widget.
|
||||
// This includes an id for the screen currently showing the widget.
|
||||
// This structure roughly parallels display::DisplayList. It may be desirable to
|
||||
// deprecate derived counterparts of ui/display types; see crbug.com/1208469.
|
||||
struct BLINK_COMMON_EXPORT ScreenInfos {
|
||||
ScreenInfos();
|
||||
explicit ScreenInfos(const ScreenInfo& screen_info);
|
||||
|
@ -9,7 +9,10 @@ import "ui/gfx/geometry/mojom/geometry.mojom";
|
||||
import "ui/gfx/mojom/display_color_spaces.mojom";
|
||||
|
||||
// Information about the screen on which a WidgetBase is being displayed. This
|
||||
// is the content counterpart to blink::ScreenInfo.
|
||||
// is the content counterpart to blink::ScreenInfo, and it roughly parallels
|
||||
// display.mojom.Display. It may be desirable to deprecate derived counterparts
|
||||
// of ui/display types, but doing so is complicated by widespread use and legacy
|
||||
// quirks around blink::ScreenInfo.
|
||||
struct ScreenInfo {
|
||||
// Device scale factor. Specifies the ratio between physical and logical
|
||||
// pixels.
|
||||
|
@ -8,6 +8,9 @@ import "third_party/blink/public/mojom/widget/screen_info.mojom";
|
||||
|
||||
// Information about a set of screens that are relevant to a particular widget.
|
||||
// This includes an id for the screen currently showing the widget.
|
||||
// This structure roughly parallels display.mojom.DisplayList. It may be
|
||||
// desirable to deprecate derived counterparts of ui/display types, but doing so
|
||||
// is complicated by widespread use and legacy quirks around blink::ScreenInfo.
|
||||
struct ScreenInfos {
|
||||
// The array of ScreenInfo objects for the set of relevant screens.
|
||||
array<ScreenInfo> screen_infos;
|
||||
|
@ -200,6 +200,7 @@ test("display_unittests") {
|
||||
"manager/display_manager_utilities_unittest.cc",
|
||||
"manager/json_converter_unittest.cc",
|
||||
"manager/managed_display_info_unittest.cc",
|
||||
"mojom/display_list_mojom_traits_unittest.cc",
|
||||
"mojom/display_mojom_traits_unittest.cc",
|
||||
"screen_unittest.cc",
|
||||
"unified_desktop_utils_unittests.cc",
|
||||
@ -238,6 +239,7 @@ test("display_unittests") {
|
||||
"//build:chromeos_buildflags",
|
||||
"//cc/base",
|
||||
"//mojo/core/test:run_all_unittests",
|
||||
"//mojo/public/cpp/test_support:test_utils",
|
||||
"//testing/gmock",
|
||||
"//testing/gtest",
|
||||
"//ui/display/fake",
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
#include "ui/display/display_list.h"
|
||||
|
||||
#include "base/containers/flat_set.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "build/build_config.h"
|
||||
#include "ui/display/display_observer.h"
|
||||
|
||||
namespace display {
|
||||
@ -13,6 +15,43 @@ DisplayList::DisplayList() = default;
|
||||
|
||||
DisplayList::~DisplayList() = default;
|
||||
|
||||
DisplayList::DisplayList(const Displays& displays,
|
||||
int64_t primary_id,
|
||||
int64_t current_id)
|
||||
: displays_(displays), primary_id_(primary_id), current_id_(current_id) {
|
||||
#if defined(OS_FUCHSIA)
|
||||
// TODO(crbug.com/1207996): Resolve ScenicScreen's lack of primary display.
|
||||
if (!displays_.empty() && primary_id_ == kInvalidDisplayId)
|
||||
primary_id_ = displays_[0].id();
|
||||
#endif // OS_FUCHSIA
|
||||
DCHECK(observers_.empty());
|
||||
DCHECK(IsValid());
|
||||
}
|
||||
|
||||
DisplayList::DisplayList(const DisplayList& other)
|
||||
: displays_(other.displays_),
|
||||
primary_id_(other.primary_id_),
|
||||
current_id_(other.current_id_) {
|
||||
DCHECK(other.observers_.empty());
|
||||
DCHECK(observers_.empty());
|
||||
DCHECK(IsValid());
|
||||
}
|
||||
|
||||
DisplayList& DisplayList::operator=(const DisplayList& other) {
|
||||
displays_ = other.displays_;
|
||||
primary_id_ = other.primary_id_;
|
||||
current_id_ = other.current_id_;
|
||||
DCHECK(other.observers_.empty());
|
||||
DCHECK(observers_.empty());
|
||||
DCHECK(IsValid());
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DisplayList::operator==(const DisplayList& other) const {
|
||||
return displays_ == other.displays_ && primary_id_ == other.primary_id_ &&
|
||||
current_id_ == other.current_id_;
|
||||
}
|
||||
|
||||
void DisplayList::AddObserver(DisplayObserver* observer) {
|
||||
observers_.AddObserver(observer);
|
||||
}
|
||||
@ -23,18 +62,17 @@ void DisplayList::RemoveObserver(DisplayObserver* observer) {
|
||||
|
||||
DisplayList::Displays::const_iterator DisplayList::FindDisplayById(
|
||||
int64_t id) const {
|
||||
for (auto iter = displays_.begin(); iter != displays_.end(); ++iter) {
|
||||
if (iter->id() == id)
|
||||
return iter;
|
||||
}
|
||||
return displays_.end();
|
||||
return base::ranges::find(displays_, id, &Display::id);
|
||||
}
|
||||
|
||||
DisplayList::Displays::const_iterator DisplayList::GetPrimaryDisplayIterator()
|
||||
const {
|
||||
return primary_display_index_ == -1
|
||||
? displays_.end()
|
||||
: displays_.begin() + primary_display_index_;
|
||||
return FindDisplayById(primary_id_);
|
||||
}
|
||||
|
||||
DisplayList::Displays::const_iterator DisplayList::GetCurrentDisplayIterator()
|
||||
const {
|
||||
return FindDisplayById(current_id_);
|
||||
}
|
||||
|
||||
void DisplayList::AddOrUpdateDisplay(const Display& display, Type type) {
|
||||
@ -54,10 +92,10 @@ uint32_t DisplayList::UpdateDisplay(const Display& display, Type type) {
|
||||
|
||||
Display* local_display = &(*iter);
|
||||
uint32_t changed_values = 0;
|
||||
if (type == Type::PRIMARY &&
|
||||
static_cast<int>(iter - displays_.begin()) !=
|
||||
static_cast<int>(GetPrimaryDisplayIterator() - displays_.begin())) {
|
||||
primary_display_index_ = static_cast<int>(iter - displays_.begin());
|
||||
// TODO(crbug.com/1207996): Guard against removal of the primary designation.
|
||||
// DCHECK(type == Type::PRIMARY || local_display->id() != primary_id_);
|
||||
if (type == Type::PRIMARY && local_display->id() != primary_id_) {
|
||||
primary_id_ = local_display->id();
|
||||
// ash::DisplayManager only notifies for the Display gaining primary, not
|
||||
// the one losing it.
|
||||
changed_values |= DisplayObserver::DISPLAY_METRIC_PRIMARY;
|
||||
@ -95,51 +133,77 @@ uint32_t DisplayList::UpdateDisplay(const Display& display, Type type) {
|
||||
}
|
||||
for (DisplayObserver& observer : observers_)
|
||||
observer.OnDisplayMetricsChanged(*local_display, changed_values);
|
||||
DCHECK(IsValid());
|
||||
return changed_values;
|
||||
}
|
||||
|
||||
void DisplayList::AddDisplay(const Display& display, Type type) {
|
||||
DCHECK(displays_.end() == FindDisplayByIdInternal(display.id()));
|
||||
DCHECK(displays_.end() == FindDisplayById(display.id()));
|
||||
displays_.push_back(display);
|
||||
if (type == Type::PRIMARY)
|
||||
primary_display_index_ = static_cast<int>(displays_.size()) - 1;
|
||||
// The first display added should always be designated as the primary display.
|
||||
// Displays added later can take on the primary designation as appropriate.
|
||||
if (type == Type::PRIMARY || displays_.size() == 1)
|
||||
primary_id_ = display.id();
|
||||
for (DisplayObserver& observer : observers_)
|
||||
observer.OnDisplayAdded(display);
|
||||
DCHECK(IsValid());
|
||||
}
|
||||
|
||||
void DisplayList::RemoveDisplay(int64_t id) {
|
||||
auto iter = FindDisplayByIdInternal(id);
|
||||
DCHECK(displays_.end() != iter);
|
||||
if (primary_display_index_ == static_cast<int>(iter - displays_.begin())) {
|
||||
if (id == primary_id_) {
|
||||
// The primary display can only be removed if it is the last display.
|
||||
// Users must choose a new primary before removing an old primary display.
|
||||
DCHECK_EQ(1u, displays_.size());
|
||||
primary_display_index_ = -1;
|
||||
} else if (primary_display_index_ >
|
||||
static_cast<int>(iter - displays_.begin())) {
|
||||
primary_display_index_--;
|
||||
primary_id_ = kInvalidDisplayId;
|
||||
}
|
||||
if (id == current_id_) {
|
||||
// The current display can only be removed if it is the last display.
|
||||
// Users must choose a new current before removing an old current display.
|
||||
DCHECK_EQ(1u, displays_.size());
|
||||
current_id_ = kInvalidDisplayId;
|
||||
}
|
||||
const Display display = *iter;
|
||||
displays_.erase(iter);
|
||||
for (DisplayObserver& observer : observers_)
|
||||
observer.OnDisplayRemoved(display);
|
||||
DCHECK(IsValid());
|
||||
}
|
||||
|
||||
bool DisplayList::IsValid() const {
|
||||
// The primary and current ids must be invalid when `displays_` is empty.
|
||||
if (displays_.empty())
|
||||
return primary_id_ == kInvalidDisplayId && current_id_ == kInvalidDisplayId;
|
||||
|
||||
// Ensure ids are unique.
|
||||
base::flat_set<int64_t> display_ids;
|
||||
display_ids.reserve(displays_.size());
|
||||
for (const auto& display : displays_) {
|
||||
if (!display_ids.insert(display.id()).second)
|
||||
return false;
|
||||
}
|
||||
|
||||
// The primary id must correspond to a `displays_` entry.
|
||||
if (GetPrimaryDisplayIterator() == displays_.end())
|
||||
return false;
|
||||
|
||||
// The current id may be invalid, or must correspond to a `displays_` entry.
|
||||
if (current_id_ != kInvalidDisplayId &&
|
||||
GetCurrentDisplayIterator() == displays_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
DisplayList::Type DisplayList::GetTypeByDisplayId(int64_t display_id) const {
|
||||
if (primary_display_index_ == -1)
|
||||
return Type::NOT_PRIMARY;
|
||||
return (displays_[primary_display_index_].id() == display_id
|
||||
? Type::PRIMARY
|
||||
: Type::NOT_PRIMARY);
|
||||
return display_id == primary_id_ ? Type::PRIMARY : Type::NOT_PRIMARY;
|
||||
}
|
||||
|
||||
DisplayList::Displays::iterator DisplayList::FindDisplayByIdInternal(
|
||||
int64_t id) {
|
||||
for (auto iter = displays_.begin(); iter != displays_.end(); ++iter) {
|
||||
if (iter->id() == id)
|
||||
return iter;
|
||||
}
|
||||
return displays_.end();
|
||||
return base::ranges::find(displays_, id, &Display::id);
|
||||
}
|
||||
|
||||
} // namespace display
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/observer_list.h"
|
||||
@ -16,8 +15,6 @@
|
||||
|
||||
namespace display {
|
||||
|
||||
class Display;
|
||||
class DisplayList;
|
||||
class DisplayObserver;
|
||||
|
||||
// Maintains an ordered list of Displays as well as operations to add, remove
|
||||
@ -35,14 +32,24 @@ class DISPLAY_EXPORT DisplayList {
|
||||
DisplayList();
|
||||
~DisplayList();
|
||||
|
||||
// WARNING: The copy constructor and assignment operator do not copy nor move
|
||||
// observers; also, the comparison operator does not compare observers.
|
||||
DisplayList(const Displays& displays, int64_t primary_id, int64_t current_id);
|
||||
DisplayList(const DisplayList& other);
|
||||
DisplayList& operator=(const DisplayList& other);
|
||||
bool operator==(const DisplayList& other) const;
|
||||
|
||||
void AddObserver(DisplayObserver* observer);
|
||||
void RemoveObserver(DisplayObserver* observer);
|
||||
|
||||
const Displays& displays() const { return displays_; }
|
||||
int64_t primary_id() const { return primary_id_; }
|
||||
int64_t current_id() const { return current_id_; }
|
||||
|
||||
Displays::const_iterator FindDisplayById(int64_t id) const;
|
||||
|
||||
Displays::const_iterator GetPrimaryDisplayIterator() const;
|
||||
Displays::const_iterator GetCurrentDisplayIterator() const;
|
||||
|
||||
void AddOrUpdateDisplay(const Display& display, Type type);
|
||||
|
||||
@ -62,6 +69,9 @@ class DISPLAY_EXPORT DisplayList {
|
||||
// Removes the Display with the specified id.
|
||||
void RemoveDisplay(int64_t id);
|
||||
|
||||
// Checks expectations around DisplayList validity.
|
||||
bool IsValid() const;
|
||||
|
||||
base::ObserverList<DisplayObserver>* observers() { return &observers_; }
|
||||
|
||||
private:
|
||||
@ -69,11 +79,19 @@ class DISPLAY_EXPORT DisplayList {
|
||||
|
||||
Displays::iterator FindDisplayByIdInternal(int64_t id);
|
||||
|
||||
// The list of displays tracked by the display::Screen or other client.
|
||||
std::vector<Display> displays_;
|
||||
int primary_display_index_ = -1;
|
||||
// The id of the primary Display in `displays_` for the display::Screen.
|
||||
int64_t primary_id_ = kInvalidDisplayId;
|
||||
// The id of the current Display in `displays_`, for some client's context.
|
||||
// This is used when DisplayList needs to track which display a client is on,
|
||||
// typically for a cached DisplayList owned by the client window itself. This
|
||||
// should be kInvalidDisplayId for the DisplayList owned by display::Screen,
|
||||
// which represents the system-wide state and needs to work for all clients.
|
||||
// This member is included in this structure to maintain consistency with some
|
||||
// list of cached displays, perhaps that's not ideal. See crbug.com/1207996.
|
||||
int64_t current_id_ = kInvalidDisplayId;
|
||||
base::ObserverList<DisplayObserver> observers_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DisplayList);
|
||||
};
|
||||
|
||||
} // namespace display
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/test/gtest_util.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/display/display.h"
|
||||
#include "ui/display/display_observer.h"
|
||||
@ -107,5 +108,64 @@ TEST(DisplayListTest, AddUpdateRemove) {
|
||||
EXPECT_EQ(3, display_list.GetPrimaryDisplayIterator()->id());
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, EmptyIsValid) {
|
||||
DisplayList display_list;
|
||||
EXPECT_TRUE(display_list.IsValid());
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, FirstDisplayAddedIsForcedToBePrimary) {
|
||||
DisplayList display_list;
|
||||
display_list.AddDisplay(Display(1), DisplayList::Type::NOT_PRIMARY);
|
||||
EXPECT_EQ(1, display_list.primary_id());
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, SinglePrimaryDisplayNoCurrentIdIsValid) {
|
||||
DisplayList display_list({Display(1)}, /*primary_id=*/1,
|
||||
/*current_id=*/kInvalidDisplayId);
|
||||
EXPECT_TRUE(display_list.IsValid());
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, SinglePrimaryDisplayWithCurrentIdIsValid) {
|
||||
DisplayList display_list({Display(1)}, /*primary_id=*/1, /*current_id=*/1);
|
||||
EXPECT_TRUE(display_list.IsValid());
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, PrimaryMustBeInvalidWhenEmpty) {
|
||||
// `primary_id` must be kInvalidDisplayId if `displays` is empty.
|
||||
EXPECT_DCHECK_DEATH(EXPECT_FALSE(
|
||||
DisplayList({}, /*primary_id=*/1, /*current_id=*/kInvalidDisplayId)
|
||||
.IsValid()));
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, CurrentMustBeInvalidWhenEmpty) {
|
||||
// `primary_id` must be kInvalidDisplayId if `displays` is empty.
|
||||
EXPECT_DCHECK_DEATH(
|
||||
EXPECT_FALSE(DisplayList({}, /*primary_id=*/kInvalidDisplayId,
|
||||
/*current_id=*/1)
|
||||
.IsValid()));
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, PrimaryIdMustBePresent) {
|
||||
// `primary_id` must match an element of `displays`.
|
||||
EXPECT_DCHECK_DEATH(EXPECT_FALSE(DisplayList({Display(1)}, /*primary_id=*/2,
|
||||
/*current_id=*/1)
|
||||
.IsValid()));
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, CurrentIdMustBePresent) {
|
||||
// `current_id` must match an element of `displays`.
|
||||
EXPECT_DCHECK_DEATH(EXPECT_FALSE(DisplayList({Display(1)}, /*primary_id=*/1,
|
||||
/*current_id=*/2)
|
||||
.IsValid()));
|
||||
}
|
||||
|
||||
TEST(DisplayListTest, DisplaysIdsMustBeUnique) {
|
||||
// `displays` must use unique id values.
|
||||
EXPECT_DCHECK_DEATH(EXPECT_FALSE(DisplayList({Display(1), Display(1)},
|
||||
/*primary_id=*/1,
|
||||
/*current_id=*/1)
|
||||
.IsValid()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace display
|
||||
|
@ -12,6 +12,7 @@ mojom("mojom") {
|
||||
"display_configuration_params.mojom",
|
||||
"display_constants.mojom",
|
||||
"display_layout.mojom",
|
||||
"display_list.mojom",
|
||||
"display_mode.mojom",
|
||||
"display_snapshot.mojom",
|
||||
"gamma_ramp_rgb_entry.mojom",
|
||||
@ -151,6 +152,17 @@ mojom("mojom") {
|
||||
traits_public_deps = [ "//ui/display" ]
|
||||
traits_deps = [ "//ui/gfx/geometry" ]
|
||||
},
|
||||
{
|
||||
types = [
|
||||
{
|
||||
mojom = "display.mojom.DisplayList"
|
||||
cpp = "::display::DisplayList"
|
||||
},
|
||||
]
|
||||
traits_sources = [ "display_list_mojom_traits.cc" ]
|
||||
traits_headers = [ "display_list_mojom_traits.h" ]
|
||||
traits_public_deps = [ ":shared_mojom_traits" ]
|
||||
},
|
||||
]
|
||||
|
||||
cpp_typemaps += shared_cpp_typemaps
|
||||
|
20
ui/display/mojom/display_list.mojom
Normal file
20
ui/display/mojom/display_list.mojom
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
module display.mojom;
|
||||
|
||||
import "ui/display/mojom/display.mojom";
|
||||
|
||||
// A vector of Displays and indices of notable entries.
|
||||
// Corresponds to display::DisplayList and roughly parallels blink::ScreenInfos.
|
||||
// WARNING: This does not convey observers held by display::DisplayList.
|
||||
struct DisplayList {
|
||||
// The array of Display objects for the set of relevant displays.
|
||||
array<Display> displays;
|
||||
|
||||
// The id of the primary Display in `displays` for the aggregate Screen.
|
||||
int64 primary_id;
|
||||
// The id of the current Display in `displays` for a context (e.g. a window).
|
||||
int64 current_id;
|
||||
};
|
21
ui/display/mojom/display_list_mojom_traits.cc
Normal file
21
ui/display/mojom/display_list_mojom_traits.cc
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ui/display/mojom/display_list_mojom_traits.h"
|
||||
|
||||
#include "ui/display/mojom/display.mojom.h"
|
||||
|
||||
namespace mojo {
|
||||
|
||||
// static
|
||||
bool StructTraits<display::mojom::DisplayListDataView, display::DisplayList>::
|
||||
Read(display::mojom::DisplayListDataView data, display::DisplayList* out) {
|
||||
std::vector<display::Display> displays;
|
||||
if (!data.ReadDisplays(&displays))
|
||||
return false;
|
||||
*out = display::DisplayList(displays, data.primary_id(), data.current_id());
|
||||
return out->IsValid();
|
||||
}
|
||||
|
||||
} // namespace mojo
|
41
ui/display/mojom/display_list_mojom_traits.h
Normal file
41
ui/display/mojom/display_list_mojom_traits.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef UI_DISPLAY_MOJOM_DISPLAY_LIST_MOJOM_TRAITS_H_
|
||||
#define UI_DISPLAY_MOJOM_DISPLAY_LIST_MOJOM_TRAITS_H_
|
||||
|
||||
#include "ui/display/display_list.h"
|
||||
#include "ui/display/mojom/display_list.mojom-shared.h"
|
||||
|
||||
namespace display {
|
||||
class Display;
|
||||
} // namespace display
|
||||
|
||||
namespace mojo {
|
||||
|
||||
template <>
|
||||
struct StructTraits<display::mojom::DisplayListDataView, display::DisplayList> {
|
||||
static const std::vector<display::Display>& displays(
|
||||
const display::DisplayList& r) {
|
||||
DCHECK(r.IsValid());
|
||||
return r.displays();
|
||||
}
|
||||
|
||||
static int64_t primary_id(const display::DisplayList& r) {
|
||||
DCHECK(r.IsValid());
|
||||
return r.primary_id();
|
||||
}
|
||||
|
||||
static int64_t current_id(const display::DisplayList& r) {
|
||||
DCHECK(r.IsValid());
|
||||
return r.current_id();
|
||||
}
|
||||
|
||||
static bool Read(display::mojom::DisplayListDataView r,
|
||||
display::DisplayList* out);
|
||||
};
|
||||
|
||||
} // namespace mojo
|
||||
|
||||
#endif // UI_DISPLAY_MOJOM_DISPLAY_LIST_MOJOM_TRAITS_H_
|
101
ui/display/mojom/display_list_mojom_traits_unittest.cc
Normal file
101
ui/display/mojom/display_list_mojom_traits_unittest.cc
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright 2021 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "ui/display/mojom/display_list_mojom_traits.h"
|
||||
|
||||
#include "base/test/gtest_util.h"
|
||||
#include "mojo/public/cpp/test_support/test_utils.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/display/display_list.h"
|
||||
#include "ui/display/mojom/display_list.mojom.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using mojo::test::SerializeAndDeserialize;
|
||||
|
||||
TEST(StructTraitsTest, DisplayListRoundtripEmpty) {
|
||||
display::DisplayList i, o;
|
||||
ASSERT_TRUE(SerializeAndDeserialize<display::mojom::DisplayList>(i, o));
|
||||
EXPECT_EQ(i, o);
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListRoundtripValid) {
|
||||
display::DisplayList i, o;
|
||||
i = display::DisplayList({display::Display(1)}, /*primary_id=*/1,
|
||||
/*current_id=*/1);
|
||||
EXPECT_EQ(i.primary_id(), i.displays()[0].id());
|
||||
EXPECT_EQ(i.current_id(), i.displays()[0].id());
|
||||
ASSERT_TRUE(SerializeAndDeserialize<display::mojom::DisplayList>(i, o));
|
||||
EXPECT_EQ(i, o);
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListPrimaryMustBeInvalidWhenEmpty) {
|
||||
// `primary_id` must be kInvalidDisplayId if `displays` is empty.
|
||||
display::mojom::DisplayListPtr i = display::mojom::DisplayList::New();
|
||||
display::DisplayList o;
|
||||
i->displays = {};
|
||||
i->primary_id = 1;
|
||||
i->current_id = display::kInvalidDisplayId;
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SerializeAndDeserialize<display::mojom::DisplayList>(i, o);
|
||||
EXPECT_FALSE(o.IsValid());
|
||||
});
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListCurrentMustBeInvalidWhenEmpty) {
|
||||
// `current_id` must be kInvalidDisplayId if `displays` is empty.
|
||||
display::mojom::DisplayListPtr i = display::mojom::DisplayList::New();
|
||||
display::DisplayList o;
|
||||
i->displays = {};
|
||||
i->primary_id = display::kInvalidDisplayId;
|
||||
i->current_id = 1;
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SerializeAndDeserialize<display::mojom::DisplayList>(i, o);
|
||||
EXPECT_FALSE(o.IsValid());
|
||||
});
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListPrimaryIdMustBePresent) {
|
||||
// `primary_id` must match an element of `displays`.
|
||||
display::mojom::DisplayListPtr i = display::mojom::DisplayList::New();
|
||||
display::DisplayList o;
|
||||
i->displays = {display::Display(1)};
|
||||
i->primary_id = 2;
|
||||
i->current_id = display::kInvalidDisplayId;
|
||||
EXPECT_NE(i->primary_id, i->displays[0].id());
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SerializeAndDeserialize<display::mojom::DisplayList>(i, o);
|
||||
EXPECT_FALSE(o.IsValid());
|
||||
});
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListCurrentIdMustBePresent) {
|
||||
// `current_id` must match an element of `displays`.
|
||||
display::mojom::DisplayListPtr i = display::mojom::DisplayList::New();
|
||||
display::DisplayList o;
|
||||
i->displays = {display::Display(1)};
|
||||
i->primary_id = 1;
|
||||
i->current_id = 2;
|
||||
EXPECT_NE(i->current_id, i->displays[0].id());
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SerializeAndDeserialize<display::mojom::DisplayList>(i, o);
|
||||
EXPECT_FALSE(o.IsValid());
|
||||
});
|
||||
}
|
||||
|
||||
TEST(StructTraitsTest, DisplayListDisplaysIdsMustBeUnique) {
|
||||
// `displays` must use unique id values.
|
||||
display::mojom::DisplayListPtr i = display::mojom::DisplayList::New();
|
||||
display::DisplayList o;
|
||||
i->displays = {display::Display(1), display::Display(1)};
|
||||
i->primary_id = 1;
|
||||
i->current_id = 1;
|
||||
EXPECT_EQ(i->displays[0].id(), i->displays[1].id());
|
||||
EXPECT_DCHECK_DEATH({
|
||||
SerializeAndDeserialize<display::mojom::DisplayList>(i, o);
|
||||
EXPECT_FALSE(o.IsValid());
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace
|
Reference in New Issue
Block a user