Commit 03b56190 authored by David Bienvenu's avatar David Bienvenu Committed by Commit Bot

Native window occlusion tracking for windows.

This implements native window occlusion tracking on windows, under flag/experiment control. It uses WinEvent event hooks and EnumWindows to track and calculate occlusion.

It is not yet hooked up to the code that treats occluded windows differently.

Bug: 813093
Change-Id: Ic9b473b8cad38e7b1653566f28917164bc92e21c
Reviewed-on: https://chromium-review.googlesource.com/c/1140752
Commit-Queue: David Bienvenu <davidbienvenu@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Cr-Commit-Position: refs/heads/master@{#603619}
parent f0c17a60
...@@ -4344,6 +4344,12 @@ const FeatureEntry kFeatureEntries[] = { ...@@ -4344,6 +4344,12 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kEnableHomeLauncherGesturesDescription, kOsCrOS, flag_descriptions::kEnableHomeLauncherGesturesDescription, kOsCrOS,
FEATURE_VALUE_TYPE(app_list_features::kEnableHomeLauncherGestures)}, FEATURE_VALUE_TYPE(app_list_features::kEnableHomeLauncherGestures)},
#endif #endif
#if defined(OS_WIN)
{"calculate-native-win-occlusion",
flag_descriptions::kCalculateNativeWinOcclusionName,
flag_descriptions::kCalculateNativeWinOcclusionDescription, kOsWin,
FEATURE_VALUE_TYPE(features::kCalculateNativeWinOcclusion)},
#endif // OS_WIN
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
{"happiness-tarcking-surveys-for-desktop", {"happiness-tarcking-surveys-for-desktop",
......
...@@ -2858,6 +2858,12 @@ const char kGoogleBrandedContextMenuDescription[] = ...@@ -2858,6 +2858,12 @@ const char kGoogleBrandedContextMenuDescription[] =
#if defined(OS_WIN) #if defined(OS_WIN)
const char kCalculateNativeWinOcclusionName[] =
"Calculate window occlusion on Windows";
const char kCalculateNativeWinOcclusionDescription[] =
"Calculate window occlusion on Windows will be used in the future "
"to throttle and potentially unload foreground tabs in occluded windows";
const char kCloudPrintXpsName[] = "XPS in Google Cloud Print"; const char kCloudPrintXpsName[] = "XPS in Google Cloud Print";
const char kCloudPrintXpsDescription[] = const char kCloudPrintXpsDescription[] =
"XPS enables advanced options for classic printers connected to the Cloud " "XPS enables advanced options for classic printers connected to the Cloud "
......
...@@ -1720,6 +1720,9 @@ extern const char kGoogleBrandedContextMenuDescription[]; ...@@ -1720,6 +1720,9 @@ extern const char kGoogleBrandedContextMenuDescription[];
#if defined(OS_WIN) #if defined(OS_WIN)
extern const char kCalculateNativeWinOcclusionName[];
extern const char kCalculateNativeWinOcclusionDescription[];
extern const char kCloudPrintXpsName[]; extern const char kCloudPrintXpsName[];
extern const char kCloudPrintXpsDescription[]; extern const char kCloudPrintXpsDescription[];
......
...@@ -4803,6 +4803,7 @@ if (!is_android) { ...@@ -4803,6 +4803,7 @@ if (!is_android) {
if (use_aura) { if (use_aura) {
sources += [ "../browser/ui/views/drag_and_drop_interactive_uitest.cc" ] sources += [ "../browser/ui/views/drag_and_drop_interactive_uitest.cc" ]
deps += [ "//ui/aura:aura_interactive_ui_tests" ]
} else { } else {
sources -= [ sources -= [
"base/interactive_test_utils_aura.cc", "base/interactive_test_utils_aura.cc",
......
...@@ -29198,6 +29198,7 @@ from previous Chrome versions. ...@@ -29198,6 +29198,7 @@ from previous Chrome versions.
<int value="-1536293422" label="SharedArrayBuffer:enabled"/> <int value="-1536293422" label="SharedArrayBuffer:enabled"/>
<int value="-1536242739" label="security-chip"/> <int value="-1536242739" label="security-chip"/>
<int value="-1535758690" label="AutoplayIgnoreWebAudio:disabled"/> <int value="-1535758690" label="AutoplayIgnoreWebAudio:disabled"/>
<int value="-1533258008" label="CalculateNativeWinOcclusion:enabled"/>
<int value="-1532035450" label="DragTabsInTabletMode:disabled"/> <int value="-1532035450" label="DragTabsInTabletMode:disabled"/>
<int value="-1532014193" label="disable-encryption-migration"/> <int value="-1532014193" label="disable-encryption-migration"/>
<int value="-1528455406" label="OmniboxPedalSuggestions:enabled"/> <int value="-1528455406" label="OmniboxPedalSuggestions:enabled"/>
...@@ -31092,6 +31093,7 @@ from previous Chrome versions. ...@@ -31092,6 +31093,7 @@ from previous Chrome versions.
<int value="2043321329" label="OfflinePagesPrefetchingUI:disabled"/> <int value="2043321329" label="OfflinePagesPrefetchingUI:disabled"/>
<int value="2056572020" label="EnableUsernameCorrection:disabled"/> <int value="2056572020" label="EnableUsernameCorrection:disabled"/>
<int value="2058283872" label="CCTModuleCache:disabled"/> <int value="2058283872" label="CCTModuleCache:disabled"/>
<int value="2058439723" label="CalculateNativeWinOcclusion:disabled"/>
<int value="2059322877" label="new-avatar-menu"/> <int value="2059322877" label="new-avatar-menu"/>
<int value="2063091429" label="OfflinePagesSharing:enabled"/> <int value="2063091429" label="OfflinePagesSharing:enabled"/>
<int value="2067634730" label="LsdPermissionPrompt:disabled"/> <int value="2067634730" label="LsdPermissionPrompt:disabled"/>
...@@ -138,6 +138,8 @@ jumbo_component("aura") { ...@@ -138,6 +138,8 @@ jumbo_component("aura") {
"mus/window_tree_client_delegate.cc", "mus/window_tree_client_delegate.cc",
"mus/window_tree_host_mus.cc", "mus/window_tree_host_mus.cc",
"mus/window_tree_host_mus_init_params.cc", "mus/window_tree_host_mus_init_params.cc",
"native_window_occlusion_tracker_win.cc",
"native_window_occlusion_tracker_win.h",
"null_window_targeter.cc", "null_window_targeter.cc",
"scoped_keyboard_hook.cc", "scoped_keyboard_hook.cc",
"scoped_simple_keyboard_hook.cc", "scoped_simple_keyboard_hook.cc",
...@@ -155,6 +157,12 @@ jumbo_component("aura") { ...@@ -155,6 +157,12 @@ jumbo_component("aura") {
"window_tree_host_platform.cc", "window_tree_host_platform.cc",
] ]
# aura_interactive_ui_tests needs access to native_window_occlusion_tracker.h.
friend = [
":aura_interactive_ui_tests",
":aura_unittests",
]
defines = [ "AURA_IMPLEMENTATION" ] defines = [ "AURA_IMPLEMENTATION" ]
deps = [ deps = [
...@@ -394,6 +402,10 @@ test("aura_unittests") { ...@@ -394,6 +402,10 @@ test("aura_unittests") {
"window_unittest.cc", "window_unittest.cc",
] ]
if (is_win) {
sources += [ "native_window_occlusion_tracker_unittest.cc" ]
}
deps = [ deps = [
":test_support", ":test_support",
"//base/test:test_support", "//base/test:test_support",
...@@ -424,3 +436,29 @@ test("aura_unittests") { ...@@ -424,3 +436,29 @@ test("aura_unittests") {
"//third_party/mesa_headers", "//third_party/mesa_headers",
] ]
} }
# This target is added as a dependency of browser interactive_ui_tests. It must
# be source_set, otherwise the linker will drop the tests as dead code.
source_set("aura_interactive_ui_tests") {
testonly = true
if (is_win) {
sources = [
"native_window_occlusion_tracker_win_interactive_test.cc",
]
deps = [
":aura",
":test_support",
"//base/test:test_support",
"//net",
"//testing/gtest",
"//ui/base/ime:ime",
"//ui/display:display",
"//ui/gfx",
"//ui/gfx/geometry",
"//ui/gl:test_support",
"//ui/gl/init",
"//ui/views:views",
]
}
}
...@@ -20,6 +20,7 @@ include_rules = [ ...@@ -20,6 +20,7 @@ include_rules = [
"+ui/display", "+ui/display",
"+ui/events", "+ui/events",
"+ui/gfx", "+ui/gfx",
"+ui/gl/test",
"+ui/metro_viewer", # TODO(beng): investigate moving remote_root_window_host "+ui/metro_viewer", # TODO(beng): investigate moving remote_root_window_host
# to ui/metro_viewer. # to ui/metro_viewer.
"+ui/ozone/public", "+ui/ozone/public",
......
// Copyright 2018 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/aura/native_window_occlusion_tracker_win.h"
#include <winuser.h>
#include "base/win/scoped_gdi_object.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/win/window_impl.h"
namespace aura {
// Test wrapper around native window HWND.
class TestNativeWindow : public gfx::WindowImpl {
public:
TestNativeWindow() {}
~TestNativeWindow() override;
private:
// Overridden from gfx::WindowImpl:
BOOL ProcessWindowMessage(HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT& result,
DWORD msg_map_id) override {
return FALSE; // Results in DefWindowProc().
}
DISALLOW_COPY_AND_ASSIGN(TestNativeWindow);
};
TestNativeWindow::~TestNativeWindow() {
if (hwnd())
DestroyWindow(hwnd());
}
// This class currently tests the behavior of
// NativeWindowOcclusionTrackerWin::IsWindowVisibleAndFullyOpaque with hwnds
// with various attributes (e.g., minimized, transparent, etc).
class NativeWindowOcclusionTrackerTest : public test::AuraTestBase {
public:
NativeWindowOcclusionTrackerTest() {}
TestNativeWindow* native_win() { return native_win_.get(); }
HWND CreateNativeWindow(DWORD ex_style) {
native_win_ = std::make_unique<TestNativeWindow>();
native_win_->set_window_style(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN);
native_win_->set_window_ex_style(ex_style);
gfx::Rect bounds(0, 0, 100, 100);
native_win_->Init(nullptr, bounds);
HWND hwnd = native_win_->hwnd();
base::win::ScopedRegion region(CreateRectRgn(0, 0, 0, 0));
if (GetWindowRgn(hwnd, region.get()) == COMPLEXREGION) {
// On Windows 7, the newly created window has a complex region, which
// means it will be ignored during the occlusion calculation. So, force
// it to have a simple region so that we get test coverage on win 7.
RECT bounding_rect;
EXPECT_TRUE(GetWindowRect(hwnd, &bounding_rect));
base::win::ScopedRegion rectangular_region(
CreateRectRgnIndirect(&bounding_rect));
SetWindowRgn(hwnd, rectangular_region.get(), /*redraw=*/TRUE);
}
ShowWindow(hwnd, SW_SHOWNORMAL);
EXPECT_TRUE(UpdateWindow(hwnd));
return hwnd;
}
// Wrapper around IsWindowVisibleAndFullyOpaque so only the test class
// needs to be a friend of NativeWindowOcclusionTrackerWin.
bool CheckWindowVisibleAndFullyOpaque(HWND hwnd, gfx::Rect* win_rect) {
bool ret = NativeWindowOcclusionTrackerWin::IsWindowVisibleAndFullyOpaque(
hwnd, win_rect);
// In general, if IsWindowVisibleAndFullyOpaque returns false, the
// returned rect should not be altered.
if (!ret)
EXPECT_EQ(*win_rect, gfx::Rect(0, 0, 0, 0));
return ret;
}
private:
std::unique_ptr<TestNativeWindow> native_win_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowOcclusionTrackerTest);
};
TEST_F(NativeWindowOcclusionTrackerTest, VisibleOpaqueWindow) {
HWND hwnd = CreateNativeWindow(/*ex_style=*/0);
gfx::Rect returned_rect;
// Normal windows should be visible.
EXPECT_TRUE(CheckWindowVisibleAndFullyOpaque(hwnd, &returned_rect));
// Check that the returned rect == the actual window rect of the hwnd.
RECT win_rect;
ASSERT_TRUE(GetWindowRect(hwnd, &win_rect));
EXPECT_EQ(returned_rect, gfx::Rect(win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, MinimizedWindow) {
HWND hwnd = CreateNativeWindow(/*ex_style=*/0);
gfx::Rect win_rect;
ShowWindow(hwnd, SW_MINIMIZE);
// Minimized windows are not considered visible.
EXPECT_FALSE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, TransparentWindow) {
HWND hwnd = CreateNativeWindow(WS_EX_TRANSPARENT);
gfx::Rect win_rect;
// Transparent windows are not considered visible and opaque.
EXPECT_FALSE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, ToolWindow) {
HWND hwnd = CreateNativeWindow(WS_EX_TOOLWINDOW);
gfx::Rect win_rect;
// Tool windows are not considered visible and opaque.
EXPECT_FALSE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, LayeredAlphaWindow) {
HWND hwnd = CreateNativeWindow(WS_EX_LAYERED);
gfx::Rect win_rect;
BYTE alpha = 1;
DWORD flags = LWA_ALPHA;
COLORREF color_ref = RGB(1, 1, 1);
SetLayeredWindowAttributes(hwnd, color_ref, alpha, flags);
// Layered windows with alpha < 255 are not considered visible and opaque.
EXPECT_FALSE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, LayeredNonAlphaWindow) {
HWND hwnd = CreateNativeWindow(WS_EX_LAYERED);
gfx::Rect win_rect;
BYTE alpha = 1;
DWORD flags = 0;
COLORREF color_ref = RGB(1, 1, 1);
SetLayeredWindowAttributes(hwnd, color_ref, alpha, flags);
// Layered non alpha windows are considered visible and opaque.
EXPECT_TRUE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
TEST_F(NativeWindowOcclusionTrackerTest, ComplexRegionWindow) {
HWND hwnd = CreateNativeWindow(/*ex_style=*/0);
gfx::Rect win_rect;
// Create a region with rounded corners, which should be a complex region.
base::win::ScopedRegion region(CreateRoundRectRgn(1, 1, 100, 100, 5, 5));
SetWindowRgn(hwnd, region.get(), /*redraw=*/TRUE);
// Windows with complex regions are not considered visible and fully opaque.
EXPECT_FALSE(CheckWindowVisibleAndFullyOpaque(hwnd, &win_rect));
}
} // namespace aura
This diff is collapsed.
// Copyright 2018 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_AURA_NATIVE_WINDOW_OCCLUSION_TRACKER_WIN_H_
#define UI_AURA_NATIVE_WINDOW_OCCLUSION_TRACKER_WIN_H_
#include <windows.h>
#include <winuser.h>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/sequenced_task_runner.h"
#include "base/time/time.h"
#include "ui/aura/aura_export.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"
namespace aura {
// This class keeps track of whether any HWNDs are occluding any app windows.
// It notifies the host of any app window whose occlusion state changes. Most
// code should not need to use this; it's an implementation detail.
class AURA_EXPORT NativeWindowOcclusionTrackerWin : public WindowObserver {
public:
static NativeWindowOcclusionTrackerWin* GetOrCreateInstance();
// Enables notifying the host of |window| via SetNativeWindowOcclusionState()
// when the occlusion state has been computed.
void Enable(Window* window);
// Disables notifying the host of |window| via
// OnNativeWindowOcclusionStateChanged() when the occlusion state has been
// computed. It's not neccesary to call this when |window| is deleted because
// OnWindowDestroying calls Disable.
void Disable(Window* window);
// aura::WindowObserver:
void OnWindowVisibilityChanged(Window* window, bool visible) override;
void OnWindowDestroying(Window* window) override;
private:
friend class NativeWindowOcclusionTrackerTest;
// This class computes the occlusion state of the tracked windows.
// It runs on a separate thread, and notifies the main thread of
// the occlusion state of the tracked windows.
class WindowOcclusionCalculator {
public:
WindowOcclusionCalculator(
scoped_refptr<base::SequencedTaskRunner> task_runner,
scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner);
~WindowOcclusionCalculator();
void EnableOcclusionTrackingForWindow(HWND hwnd);
void DisableOcclusionTrackingForWindow(HWND hwnd);
// If a window becomes visible, makes sure event hooks are registered.
void HandleVisibilityChanged(bool visible);
private:
friend class NativeWindowOcclusionTrackerTest;
struct NativeWindowOcclusionState {
// The region of the native window that is not occluded by other windows.
SkRegion unoccluded_region;
// The current occlusion state of the native window. Default to UNKNOWN
// because we do not know the state starting out. More information on
// these states can be found in aura::Window.
aura::Window::OcclusionState occlusion_state =
aura::Window::OcclusionState::UNKNOWN;
};
// Registers event hooks, if not registered.
void MaybeRegisterEventHooks();
// This is the callback registered to get notified of various Windows
// events, like window moving/resizing.
static void CALLBACK EventHookCallback(HWINEVENTHOOK hWinEventHook,
DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild,
DWORD dwEventThread,
DWORD dwmsEventTime);
// EnumWindows callback used to iterate over all hwnds to determine
// occlusion status of all tracked root windows. Also builds up
// |current_pids_with_visible_windows_| and registers event hooks for newly
// discovered processes with visible hwnds.
static BOOL CALLBACK
ComputeNativeWindowOcclusionStatusCallback(HWND hwnd, LPARAM lParam);
// EnumWindows callback used to update the list of process ids with
// visible hwnds, |pids_for_location_change_hook_|.
static BOOL CALLBACK UpdateVisibleWindowProcessIdsCallback(HWND hwnd,
LPARAM lParam);
// Determines which processes owning visible application windows to set the
// EVENT_OBJECT_LOCATIONCHANGE event hook for and stores the pids in
// |pids_for_location_change_hook_|.
void UpdateVisibleWindowProcessIds();
// Computes the native window occlusion status for all tracked root aura
// windows in |root_window_hwnds_occlusion_state_| and notifies them if
// their occlusion status has changed.
void ComputeNativeWindowOcclusionStatus();
// Schedules an occlusion calculation |update_occlusion_delay_| time in the
// future, if one isn't already scheduled.
void ScheduleOcclusionCalculationIfNeeded();
// Registers a global event hook (not per process) for the events in the
// range from |event_min| to |event_max|, inclusive.
void RegisterGlobalEventHook(UINT event_min, UINT event_max);
// Registers the EVENT_OBJECT_LOCATIONCHANGE event hook for the process with
// passed id. The process has one or more visible, opaque windows.
void RegisterEventHookForProcess(DWORD pid);
// Registers/Unregisters the event hooks necessary for occlusion tracking
// via calls to RegisterEventHook. These event hooks are disabled when all
// tracked windows are minimized.
void RegisterEventHooks();
void UnregisterEventHooks();
// EnumWindows callback for occlusion calculation. Returns true to
// continue enumeration, false otherwise. Currently, always returns
// true because this function also updates
// |current_pids_with_visible_windows|, and needs to see all HWNDs.
bool ProcessComputeNativeWindowOcclusionStatusCallback(
HWND hwnd,
base::flat_set<DWORD>* current_pids_with_visible_windows);
// Processes events sent to OcclusionEventHookCallback.
// It generally triggers scheduling of the occlusion calculation, but
// ignores certain events in order to not calculate occlusion more than
// necessary.
void ProcessEventHookCallback(DWORD event,
HWND hwnd,
LONG idObject,
LONG idChild);
// EnumWindows callback for determining which processes to set the
// EVENT_OBJECT_LOCATIONCHANGE event hook for. We set that event hook for
// processes hosting fully visible, opaque windows.
void ProcessUpdateVisibleWindowProcessIdsCallback(HWND hwnd);
// Task runner for our thread.
scoped_refptr<base::SequencedTaskRunner> task_runner_;
// Task runner for the thread that created |this|. UpdateOcclusionState
// task is posted to this task runner.
const scoped_refptr<base::SequencedTaskRunner> ui_thread_task_runner_;
// Map of root app window hwnds and their occlusion state. This contains
// both visible and hidden windows.
base::flat_map<HWND, NativeWindowOcclusionState>
root_window_hwnds_occlusion_state_;
// Values returned by SetWinEventHook are stored so that hooks can be
// unregistered when necessary.
std::vector<HWINEVENTHOOK> global_event_hooks_;
// Map from process id to EVENT_OBJECT_LOCATIONCHANGE event hook.
base::flat_map<DWORD, HWINEVENTHOOK> process_event_hooks_;
// Pids of processes for which the EVENT_OBJECT_LOCATIONCHANGE event hook is
// set. These are the processes hosting windows in
// |visible_and_fully_opaque_windows_|.
base::flat_set<DWORD> pids_for_location_change_hook_;
// Timer to delay occlusion update.
base::OneShotTimer occlusion_update_timer_;
// Used to keep track of whether we're in the middle of getting window move
// events, in order to wait until the window move is complete before
// calculating window occlusion.
bool window_is_moving_ = false;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(WindowOcclusionCalculator);
};
NativeWindowOcclusionTrackerWin();
~NativeWindowOcclusionTrackerWin() override;
// Returns true if we are interested in |hwnd| for purposes of occlusion
// calculation. We are interested in |hwnd| if it is a window that is visible,
// opaque, and bounded. If we are interested in |hwnd|, stores the window
// rectangle in |window_rect|.
static bool IsWindowVisibleAndFullyOpaque(HWND hwnd, gfx::Rect* window_rect);
// Updates root windows occclusion state.
void UpdateOcclusionState(const base::flat_map<HWND, Window::OcclusionState>&
root_window_hwnds_occlusion_state);
// Task runner to call ComputeNativeWindowOcclusionStatus, and to handle
// Windows event notifications, off of the UI thread.
const scoped_refptr<base::SequencedTaskRunner> update_occlusion_task_runner_;
// Map of HWND to root app windows. Maintained on the UI thread, and used
// to send occlusion state notifications to Windows from
// |root_window_hwnds_occlusion_state_|.
base::flat_map<HWND, Window*> hwnd_root_window_map_;
std::unique_ptr<WindowOcclusionCalculator> occlusion_calculator_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowOcclusionTrackerWin);
};
} // namespace aura
#endif // UI_AURA_NATIVE_WINDOW_OCCLUSION_TRACKER_WIN_H_
// Copyright 2018 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/aura/native_window_occlusion_tracker_win.h"
#include <winuser.h>
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/task/task_scheduler/task_scheduler.h"
#include "base/test/scoped_feature_list.h"
#include "base/win/scoped_gdi_object.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/env.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/test_focus_client.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/test/test_window_delegate.h"
#include "ui/aura/test/test_window_parenting_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/aura/window_tree_host_platform.h"
#include "ui/base/ime/input_method_initializer.h"
#include "ui/base/ui_base_features.h"
#include "ui/display/win/dpi.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/win/window_impl.h"
#include "ui/gl/test/gl_surface_test_support.h"
namespace aura {
// This class is used to verify expectations about occlusion state changes by
// adding instances of it as an observer of aura:Windows the tests create and
// checking that they get the expected call(s) to OnOcclusionStateChanged.
// The tests verify that the current state, when idle, is the expected state,
// because the state can be VISIBLE before it reaches the expected state.
class MockWindowTreeHostObserver : public WindowTreeHostObserver {
public:
MockWindowTreeHostObserver() {}
~MockWindowTreeHostObserver() override { EXPECT_FALSE(is_expecting_call()); }
// WindowTreeHostObserver:
void OnOcclusionStateChanged(WindowTreeHost* host,
Window::OcclusionState new_state) override {
EXPECT_NE(new_state, Window::OcclusionState::UNKNOWN);
// Should only get notified when the occlusion state changes.
EXPECT_NE(new_state, cur_state_);
cur_state_ = new_state;
}
void set_expectation(Window::OcclusionState expectation) {
expectation_ = expectation;
}
bool is_expecting_call() const { return expectation_ != cur_state_; }
private:
Window::OcclusionState expectation_ = Window::OcclusionState::UNKNOWN;
Window::OcclusionState cur_state_ = Window::OcclusionState::UNKNOWN;
DISALLOW_COPY_AND_ASSIGN(MockWindowTreeHostObserver);
};
// Test wrapper around native window HWND.
class TestNativeWindow : public gfx::WindowImpl {
public:
TestNativeWindow() {}
~TestNativeWindow() override;
private:
// Overridden from gfx::WindowImpl:
BOOL ProcessWindowMessage(HWND window,
UINT message,
WPARAM w_param,
LPARAM l_param,
LRESULT& result,
DWORD msg_map_id) override {
return FALSE; // Results in DefWindowProc().
}
DISALLOW_COPY_AND_ASSIGN(TestNativeWindow);
};
TestNativeWindow::~TestNativeWindow() {
if (hwnd())
DestroyWindow(hwnd());
}
class NativeWindowOcclusionTrackerTest : public test::AuraTestBase {
public:
NativeWindowOcclusionTrackerTest() {}
void SetUp() override {
if (gl::GetGLImplementation() == gl::kGLImplementationNone)
gl::GLSurfaceTestSupport::InitializeOneOff();
AuraTestBase::SetUp();
ui::InitializeInputMethodForTesting();
display::Screen::SetScreenInstance(test_screen());
scoped_feature_list_.InitAndEnableFeature(
features::kCalculateNativeWinOcclusion);
}
void SetNativeWindowBounds(HWND hwnd, const gfx::Rect& bounds) {
RECT wr = bounds.ToRECT();
AdjustWindowRectEx(&wr, GetWindowLong(hwnd, GWL_STYLE), FALSE,
GetWindowLong(hwnd, GWL_EXSTYLE));
// Make sure to keep the window onscreen, as AdjustWindowRectEx() may have
// moved part of it offscreen.
gfx::Rect window_bounds(wr);
window_bounds.set_x(std::max(0, window_bounds.x()));
window_bounds.set_y(std::max(0, window_bounds.y()));
SetWindowPos(hwnd, HWND_TOP, window_bounds.x(), window_bounds.y(),
window_bounds.width(), window_bounds.height(),
SWP_NOREPOSITION);
EXPECT_TRUE(UpdateWindow(hwnd));
}
HWND CreateNativeWindowWithBounds(const gfx::Rect& bounds) {
native_win_ = std::make_unique<TestNativeWindow>();
native_win_->set_window_style(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN);
native_win_->Init(nullptr, bounds);
HWND hwnd = native_win_->hwnd();
SetNativeWindowBounds(hwnd, bounds);
base::win::ScopedRegion region(CreateRectRgn(0, 0, 0, 0));
if (GetWindowRgn(hwnd, region.get()) == COMPLEXREGION) {
// On Windows 7, the newly created window has a complex region, which
// means it will be ignored during the occlusion calculation. So, force
// it to have a simple region so that we get test coverage on win 7.
RECT bounding_rect;
GetWindowRect(hwnd, &bounding_rect);
base::win::ScopedRegion rectangular_region(
CreateRectRgnIndirect(&bounding_rect));
SetWindowRgn(hwnd, rectangular_region.get(), TRUE);
}
ShowWindow(hwnd, SW_SHOWNORMAL);
EXPECT_TRUE(UpdateWindow(hwnd));
return hwnd;
}
void CreateTrackedAuraWindowWithBounds(MockWindowTreeHostObserver* observer,
gfx::Rect bounds) {
host()->Show();
host()->SetBoundsInPixels(bounds);
host()->AddObserver(observer);
Window* window = CreateNormalWindow(1, host()->window(), nullptr);
window->SetBounds(bounds);
window->env()->GetWindowOcclusionTracker()->Track(window);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<TestNativeWindow> native_win_;
DISALLOW_COPY_AND_ASSIGN(NativeWindowOcclusionTrackerTest);
};
// Simple test completely covering an aura window with a native window.
TEST_F(NativeWindowOcclusionTrackerTest, SimpleOcclusion) {
base::RunLoop run_loop;
std::unique_ptr<MockWindowTreeHostObserver> observer =
std::make_unique<MockWindowTreeHostObserver>();
CreateTrackedAuraWindowWithBounds(observer.get(), gfx::Rect(0, 0, 100, 100));
observer->set_expectation(Window::OcclusionState::OCCLUDED);
CreateNativeWindowWithBounds(gfx::Rect(0, 0, 100, 100));
run_loop.RunUntilIdle();
EXPECT_FALSE(observer->is_expecting_call());
}
// Simple test with an aura window and native window that do not overlap.
TEST_F(NativeWindowOcclusionTrackerTest, SimpleVisible) {
base::RunLoop run_loop;
MockWindowTreeHostObserver observer;
CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
observer.set_expectation(Window::OcclusionState::VISIBLE);
CreateNativeWindowWithBounds(gfx::Rect(200, 0, 100, 100));
run_loop.RunUntilIdle();
EXPECT_FALSE(observer.is_expecting_call());
}
// Simple test with a minimized aura window and native window.
TEST_F(NativeWindowOcclusionTrackerTest, SimpleHidden) {
base::RunLoop run_loop;
MockWindowTreeHostObserver observer;
CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
CreateNativeWindowWithBounds(gfx::Rect(200, 0, 100, 100));
// Minimize the tracked aura window and check that its occlusion state
// is HIDDEN.
::ShowWindow(host()->GetAcceleratedWidget(), SW_MINIMIZE);
observer.set_expectation(Window::OcclusionState::HIDDEN);
run_loop.RunUntilIdle();
EXPECT_FALSE(observer.is_expecting_call());
}
} // namespace aura
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkRegion.h"
#include "ui/aura/env.h" #include "ui/aura/env.h"
#include "ui/aura/window_tracker.h" #include "ui/aura/window_tracker.h"
#include "ui/aura/window_tree_host.h"
#include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/transform.h" #include "ui/gfx/transform.h"
...@@ -430,9 +431,18 @@ void WindowOcclusionTracker::TrackedWindowAddedToRoot(Window* window) { ...@@ -430,9 +431,18 @@ void WindowOcclusionTracker::TrackedWindowAddedToRoot(Window* window) {
DCHECK(root_window); DCHECK(root_window);
RootWindowState& root_window_state = root_windows_[root_window]; RootWindowState& root_window_state = root_windows_[root_window];
++root_window_state.num_tracked_windows; ++root_window_state.num_tracked_windows;
if (root_window_state.num_tracked_windows == 1)
AddObserverToWindowAndDescendants(root_window);
MarkRootWindowAsDirty(&root_window_state); MarkRootWindowAsDirty(&root_window_state);
// It's only useful to track the host if |window| is the first tracked window
// under |root_window|. All windows under the same root have the same host.
if (root_window_state.num_tracked_windows == 1) {
AddObserverToWindowAndDescendants(root_window);
auto* host = root_window->GetHost();
if (host) {
host->AddObserver(this);
host->EnableNativeWindowOcclusionTracking();
}
}
MaybeComputeOcclusion(); MaybeComputeOcclusion();
} }
...@@ -445,6 +455,8 @@ void WindowOcclusionTracker::TrackedWindowRemovedFromRoot(Window* window) { ...@@ -445,6 +455,8 @@ void WindowOcclusionTracker::TrackedWindowRemovedFromRoot(Window* window) {
if (root_window_state_it->second.num_tracked_windows == 0) { if (root_window_state_it->second.num_tracked_windows == 0) {
RemoveObserverFromWindowAndDescendants(root_window); RemoveObserverFromWindowAndDescendants(root_window);
root_windows_.erase(root_window_state_it); root_windows_.erase(root_window_state_it);
root_window->GetHost()->RemoveObserver(this);
root_window->GetHost()->DisableNativeWindowOcclusionTracking();
} }
} }
...@@ -631,4 +643,8 @@ void WindowOcclusionTracker::OnWindowLayerRecreated(Window* window) { ...@@ -631,4 +643,8 @@ void WindowOcclusionTracker::OnWindowLayerRecreated(Window* window) {
} }
} }
void WindowOcclusionTracker::OnOcclusionStateChanged(
WindowTreeHost* host,
aura::Window::OcclusionState new_state) {}
} // namespace aura } // namespace aura
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "ui/aura/aura_export.h" #include "ui/aura/aura_export.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/aura/window_tree_host_observer.h"
#include "ui/compositor/layer_animation_observer.h" #include "ui/compositor/layer_animation_observer.h"
struct SkIRect; struct SkIRect;
...@@ -46,7 +47,8 @@ class Env; ...@@ -46,7 +47,8 @@ class Env;
// Note that an occluded window may be drawn on the screen by window switching // Note that an occluded window may be drawn on the screen by window switching
// features such as "Alt-Tab" or "Overview". // features such as "Alt-Tab" or "Overview".
class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
public WindowObserver { public WindowObserver,
public WindowTreeHostObserver {
public: public:
// Prevents window occlusion state computations within its scope. If an event // Prevents window occlusion state computations within its scope. If an event
// that could cause window occlusion states to change occurs within the scope // that could cause window occlusion states to change occurs within the scope
...@@ -214,6 +216,10 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, ...@@ -214,6 +216,10 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver,
Window* new_root) override; Window* new_root) override;
void OnWindowLayerRecreated(Window* window) override; void OnWindowLayerRecreated(Window* window) override;
// WindowTreeHostObserver
void OnOcclusionStateChanged(WindowTreeHost* host,
Window::OcclusionState new_state) override;
// Windows whose occlusion state is tracked. // Windows whose occlusion state is tracked.
base::flat_map<Window*, Window::OcclusionState> tracked_windows_; base::flat_map<Window*, Window::OcclusionState> tracked_windows_;
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include "base/metrics/histogram_macros.h" #include "base/metrics/histogram_macros.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
#include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/viz/common/features.h" #include "components/viz/common/features.h"
#include "ui/aura/client/capture_client.h" #include "ui/aura/client/capture_client.h"
#include "ui/aura/client/cursor_client.h" #include "ui/aura/client/cursor_client.h"
...@@ -39,6 +40,10 @@ ...@@ -39,6 +40,10 @@
#include "ui/gfx/icc_profile.h" #include "ui/gfx/icc_profile.h"
#include "ui/platform_window/platform_window_init_properties.h" #include "ui/platform_window/platform_window_init_properties.h"
#if defined(OS_WIN)
#include "ui/aura/native_window_occlusion_tracker_win.h"
#endif // OS_WIN
namespace aura { namespace aura {
namespace { namespace {
...@@ -78,6 +83,12 @@ class ScopedLocalSurfaceIdValidator { ...@@ -78,6 +83,12 @@ class ScopedLocalSurfaceIdValidator {
}; };
#endif #endif
#if defined(OS_WIN)
bool IsNativeWindowOcclusionEnabled() {
return base::FeatureList::IsEnabled(features::kCalculateNativeWinOcclusion);
}
#endif // OS_WIN
} // namespace } // namespace
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
...@@ -118,6 +129,10 @@ void WindowTreeHost::RemoveObserver(WindowTreeHostObserver* observer) { ...@@ -118,6 +129,10 @@ void WindowTreeHost::RemoveObserver(WindowTreeHostObserver* observer) {
observers_.RemoveObserver(observer); observers_.RemoveObserver(observer);
} }
bool WindowTreeHost::HasObserver(const WindowTreeHostObserver* observer) const {
return observers_.HasObserver(observer);
}
ui::EventSink* WindowTreeHost::event_sink() { ui::EventSink* WindowTreeHost::event_sink() {
return dispatcher_.get(); return dispatcher_.get();
} }
...@@ -292,11 +307,38 @@ bool WindowTreeHost::ShouldSendKeyEventToIme() { ...@@ -292,11 +307,38 @@ bool WindowTreeHost::ShouldSendKeyEventToIme() {
return true; return true;
} }
void WindowTreeHost::EnableNativeWindowOcclusionTracking() {
#if defined(OS_WIN)
if (IsNativeWindowOcclusionEnabled()) {
NativeWindowOcclusionTrackerWin::GetOrCreateInstance()->Enable(window());
}
#endif // OS_WIN
}
void WindowTreeHost::DisableNativeWindowOcclusionTracking() {
#if defined(OS_WIN)
if (IsNativeWindowOcclusionEnabled()) {
occlusion_state_ = Window::OcclusionState::UNKNOWN;
NativeWindowOcclusionTrackerWin::GetOrCreateInstance()->Disable(window());
}
#endif // OS_WIN
}
void WindowTreeHost::SetNativeWindowOcclusionState(
Window::OcclusionState state) {
if (occlusion_state_ != state) {
occlusion_state_ = state;
for (WindowTreeHostObserver& observer : observers_)
observer.OnOcclusionStateChanged(this, state);
}
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// WindowTreeHost, protected: // WindowTreeHost, protected:
WindowTreeHost::WindowTreeHost(std::unique_ptr<Window> window) WindowTreeHost::WindowTreeHost(std::unique_ptr<Window> window)
: window_(window.release()), // See header for details on ownership. : window_(window.release()), // See header for details on ownership.
occlusion_state_(Window::OcclusionState::UNKNOWN),
last_cursor_(ui::CursorType::kNull), last_cursor_(ui::CursorType::kNull),
input_method_(nullptr), input_method_(nullptr),
owned_input_method_(false), owned_input_method_(false),
......
...@@ -80,6 +80,7 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, ...@@ -80,6 +80,7 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate,
void AddObserver(WindowTreeHostObserver* observer); void AddObserver(WindowTreeHostObserver* observer);
void RemoveObserver(WindowTreeHostObserver* observer); void RemoveObserver(WindowTreeHostObserver* observer);
bool HasObserver(const WindowTreeHostObserver* observer) const;
Window* window() { return window_; } Window* window() { return window_; }
const Window* window() const { return window_; } const Window* window() const { return window_; }
...@@ -229,6 +230,18 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, ...@@ -229,6 +230,18 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate,
// WindowEventDispatcher during event dispatch. // WindowEventDispatcher during event dispatch.
virtual bool ShouldSendKeyEventToIme(); virtual bool ShouldSendKeyEventToIme();
// Enables native window occlusion tracking for the native window this host
// represents.
virtual void EnableNativeWindowOcclusionTracking();
// Disables native window occlusion tracking for the native window this host
// represents.
virtual void DisableNativeWindowOcclusionTracking();
// Remembers the current occlusion state, and if it has changed, notifies
// observers of the change.
virtual void SetNativeWindowOcclusionState(Window::OcclusionState state);
protected: protected:
friend class ScopedKeyboardHook; friend class ScopedKeyboardHook;
friend class TestScreen; // TODO(beng): see if we can remove/consolidate. friend class TestScreen; // TODO(beng): see if we can remove/consolidate.
...@@ -328,6 +341,10 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, ...@@ -328,6 +341,10 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate,
// the end of the dtor). // the end of the dtor).
Window* window_; // Owning. Window* window_; // Owning.
// Keeps track of the occlusion state of the host, and used to send
// notifications to observers when it changes.
Window::OcclusionState occlusion_state_;
base::ObserverList<WindowTreeHostObserver>::Unchecked observers_; base::ObserverList<WindowTreeHostObserver>::Unchecked observers_;
std::unique_ptr<WindowEventDispatcher> dispatcher_; std::unique_ptr<WindowEventDispatcher> dispatcher_;
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define UI_AURA_WINDOW_TREE_HOST_OBSERVER_H_ #define UI_AURA_WINDOW_TREE_HOST_OBSERVER_H_
#include "ui/aura/aura_export.h" #include "ui/aura/aura_export.h"
#include "ui/aura/window.h"
namespace gfx { namespace gfx {
class Point; class Point;
...@@ -29,6 +30,11 @@ class AURA_EXPORT WindowTreeHostObserver { ...@@ -29,6 +30,11 @@ class AURA_EXPORT WindowTreeHostObserver {
// Called when the native window system sends the host request to close. // Called when the native window system sends the host request to close.
virtual void OnHostCloseRequested(WindowTreeHost* host) {} virtual void OnHostCloseRequested(WindowTreeHost* host) {}
// Called when the occlusion status of the native window changes, iff
// occlusion tracking is enabled for a descendant of the root.
virtual void OnOcclusionStateChanged(WindowTreeHost* host,
Window::OcclusionState new_state) {}
protected: protected:
virtual ~WindowTreeHostObserver() {} virtual ~WindowTreeHostObserver() {}
}; };
......
...@@ -12,6 +12,11 @@ ...@@ -12,6 +12,11 @@
namespace features { namespace features {
#if defined(OS_WIN)
// If enabled, calculate native window occlusion - Windows-only.
const base::Feature kCalculateNativeWinOcclusion{
"CalculateNativeWinOcclusion", base::FEATURE_DISABLED_BY_DEFAULT};
#endif // OW_WIN
// If enabled, the emoji picker context menu item may be shown for editable // If enabled, the emoji picker context menu item may be shown for editable
// text areas. // text areas.
const base::Feature kEnableEmojiContextMenu { const base::Feature kEnableEmojiContextMenu {
......
...@@ -32,6 +32,7 @@ UI_BASE_EXPORT bool IsNotificationIndicatorEnabled(); ...@@ -32,6 +32,7 @@ UI_BASE_EXPORT bool IsNotificationIndicatorEnabled();
UI_BASE_EXPORT bool IsUiGpuRasterizationEnabled(); UI_BASE_EXPORT bool IsUiGpuRasterizationEnabled();
#if defined(OS_WIN) #if defined(OS_WIN)
UI_BASE_EXPORT extern const base::Feature kCalculateNativeWinOcclusion;
UI_BASE_EXPORT extern const base::Feature kInputPaneOnScreenKeyboard; UI_BASE_EXPORT extern const base::Feature kInputPaneOnScreenKeyboard;
UI_BASE_EXPORT extern const base::Feature kPointerEventsForTouch; UI_BASE_EXPORT extern const base::Feature kPointerEventsForTouch;
UI_BASE_EXPORT extern const base::Feature kPrecisionTouchpad; UI_BASE_EXPORT extern const base::Feature kPrecisionTouchpad;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "ui/base/class_property.h" #include "ui/base/class_property.h"
#include "ui/base/cursor/cursor_loader_win.h" #include "ui/base/cursor/cursor_loader_win.h"
#include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/win/shell.h" #include "ui/base/win/shell.h"
#include "ui/compositor/paint_context.h" #include "ui/compositor/paint_context.h"
#include "ui/display/win/dpi.h" #include "ui/display/win/dpi.h"
...@@ -28,6 +29,7 @@ ...@@ -28,6 +29,7 @@
#include "ui/gfx/path.h" #include "ui/gfx/path.h"
#include "ui/gfx/path_win.h" #include "ui/gfx/path_win.h"
#include "ui/views/corewm/tooltip_win.h" #include "ui/views/corewm/tooltip_win.h"
#include "ui/views/views_switches.h"
#include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h" #include "ui/views/widget/desktop_aura/desktop_drag_drop_client_win.h"
#include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h" #include "ui/views/widget/desktop_aura/desktop_native_cursor_manager.h"
#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
...@@ -175,7 +177,6 @@ DesktopWindowTreeHostWin::CreateDragDropClient( ...@@ -175,7 +177,6 @@ DesktopWindowTreeHostWin::CreateDragDropClient(
void DesktopWindowTreeHostWin::Close() { void DesktopWindowTreeHostWin::Close() {
content_window()->Hide(); content_window()->Hide();
// TODO(beng): Move this entire branch to DNWA so it can be shared with X11. // TODO(beng): Move this entire branch to DNWA so it can be shared with X11.
if (should_animate_window_close_) { if (should_animate_window_close_) {
pending_close_ = true; pending_close_ = true;
...@@ -880,6 +881,9 @@ void DesktopWindowTreeHostWin::HandleNativeBlur(HWND focused_window) { ...@@ -880,6 +881,9 @@ void DesktopWindowTreeHostWin::HandleNativeBlur(HWND focused_window) {
} }
bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) { bool DesktopWindowTreeHostWin::HandleMouseEvent(ui::MouseEvent* event) {
// TODO(davidbienvenu): Check for getting mouse events for an occluded window
// with either a DCHECK or a stat. Event can cause this object to be deleted
// so look at occlusion state before we do anything with the event.
SendEventToSink(event); SendEventToSink(event);
return event->handled(); return event->handled();
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment