#include "ui/views/win/hwnd_message_handler.h"
#include <tchar.h>
#include <dwmapi.h>
#include <oleacc.h>
#include <shellapi.h>
#include <wrl/client.h>
#include <algorithm>
#include <utility>
#include "base/auto_reset.h"
#include "base/compiler_specific.h"
#include "base/containers/heap_array.h"
#include "base/debug/gdi_debug_util_win.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/notimplemented.h"
#include "base/strings/string_util.h"
#include "base/strings/string_util_win.h"
#include "base/strings/stringprintf.h"
#include "base/task/current_thread.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/win/dark_mode_support.h"
#include "base/win/scoped_gdi_object.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "services/tracing/public/cpp/perfetto/macros.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/chrome_window_handle_event_info.pbzero.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/accessibility/accessibility_features.h"
#include "ui/accessibility/platform/ax_fragment_root_win.h"
#include "ui/accessibility/platform/ax_platform.h"
#include "ui/accessibility/platform/ax_platform_node_win.h"
#include "ui/accessibility/platform/ax_system_caret_win.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/text_input_type.h"
#include "ui/base/mojom/window_show_state.mojom.h"
#include "ui/base/ui_base_features.h"
#include "ui/base/view_prop.h"
#include "ui/base/win/hwnd_metrics.h"
#include "ui/base/win/internal_constants.h"
#include "ui/base/win/lock_state.h"
#include "ui/base/win/mouse_wheel_util.h"
#include "ui/base/win/session_change_observer.h"
#include "ui/base/win/touch_input.h"
#include "ui/base/win/win_cursor.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/display/win/dpi.h"
#include "ui/display/win/screen_win.h"
#include "ui/events/event.h"
#include "ui/events/event_constants.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion_win.h"
#include "ui/events/types/event_type.h"
#include "ui/events/win/system_event_state_lookup.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/geometry/resize_utils.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/path_win.h"
#include "ui/gfx/win/hwnd_util.h"
#include "ui/gfx/win/icon_util.h"
#include "ui/gfx/win/rendering_window_manager.h"
#include "ui/latency/latency_info.h"
#include "ui/native_theme/native_theme_win.h"
#include "ui/views/views_delegate.h"
#include "ui/views/widget/widget_hwnd_utils.h"
#include "ui/views/win/fullscreen_handler.h"
#include "ui/views/win/hwnd_message_handler_delegate.h"
#include "ui/views/win/hwnd_message_handler_headless.h"
#include "ui/views/win/hwnd_util.h"
#include "ui/views/win/pen_event_handler_util.h"
#include "ui/views/win/scoped_fullscreen_visibility.h"
namespace views {
namespace {
class MoveLoopMouseWatcher {
public:
MoveLoopMouseWatcher(base::WeakPtr<HWNDMessageHandler> host,
bool hide_on_escape);
MoveLoopMouseWatcher(const MoveLoopMouseWatcher&) = delete;
MoveLoopMouseWatcher& operator=(const MoveLoopMouseWatcher&) = delete;
~MoveLoopMouseWatcher();
static void UnhookForHost(HWNDMessageHandler* host);
bool got_mouse_up() const { return got_mouse_up_; }
private:
static MoveLoopMouseWatcher* instance_;
static LRESULT CALLBACK MouseHook(int n_code, WPARAM w_param, LPARAM l_param);
static LRESULT CALLBACK KeyHook(int n_code, WPARAM w_param, LPARAM l_param);
void Unhook();
base::WeakPtr<HWNDMessageHandler> host_;
const bool hide_on_escape_;
bool got_mouse_up_ = false;
HHOOK mouse_hook_ = nullptr;
HHOOK key_hook_ = nullptr;
};
MoveLoopMouseWatcher* MoveLoopMouseWatcher::instance_ = nullptr;
MoveLoopMouseWatcher::MoveLoopMouseWatcher(
base::WeakPtr<HWNDMessageHandler> host,
bool hide_on_escape)
: host_(std::move(host)), hide_on_escape_(hide_on_escape) {
if (instance_) {
instance_->Unhook();
}
mouse_hook_ =
SetWindowsHookEx(WH_MOUSE, &MouseHook, nullptr, GetCurrentThreadId());
if (mouse_hook_) {
instance_ = this;
key_hook_ =
SetWindowsHookEx(WH_KEYBOARD, &KeyHook, nullptr, GetCurrentThreadId());
}
if (instance_ != this) {
got_mouse_up_ = true;
}
}
MoveLoopMouseWatcher::~MoveLoopMouseWatcher() {
Unhook();
}
void MoveLoopMouseWatcher::UnhookForHost(HWNDMessageHandler* host) {
if (instance_ && instance_->host_.get() == host) {
instance_->Unhook();
}
}
void MoveLoopMouseWatcher::Unhook() {
if (instance_ != this) {
return;
}
DCHECK(mouse_hook_);
UnhookWindowsHookEx(mouse_hook_);
if (key_hook_) {
UnhookWindowsHookEx(key_hook_);
}
key_hook_ = nullptr;
mouse_hook_ = nullptr;
instance_ = nullptr;
}
LRESULT CALLBACK MoveLoopMouseWatcher::MouseHook(int n_code,
WPARAM w_param,
LPARAM l_param) {
DCHECK(instance_);
if (n_code == HC_ACTION && w_param == WM_LBUTTONUP) {
instance_->got_mouse_up_ = true;
}
return CallNextHookEx(instance_->mouse_hook_, n_code, w_param, l_param);
}
LRESULT CALLBACK MoveLoopMouseWatcher::KeyHook(int n_code,
WPARAM w_param,
LPARAM l_param) {
if (n_code == HC_ACTION && w_param == VK_ESCAPE) {
int value = TRUE;
DwmSetWindowAttribute(instance_->host_->hwnd(),
DWMWA_TRANSITIONS_FORCEDISABLED, &value,
sizeof(value));
if (instance_->hide_on_escape_) {
instance_->host_->Hide();
}
}
return CallNextHookEx(instance_->key_hook_, n_code, w_param, l_param);
}
BOOL CALLBACK EnumChildWindowsForRedraw(HWND hwnd, LPARAM lparam) {
DWORD process_id;
GetWindowThreadProcessId(hwnd, &process_id);
UINT flags = RDW_INVALIDATE | RDW_NOCHILDREN | RDW_FRAME;
if (process_id == GetCurrentProcessId()) {
flags |= RDW_UPDATENOW;
}
RedrawWindow(hwnd, nullptr, nullptr, flags);
return TRUE;
}
void EnableMenuItemByCommand(HMENU menu, UINT command, bool enabled) {
UINT flags = MF_BYCOMMAND | (enabled ? MF_ENABLED : MF_DISABLED | MF_GRAYED);
EnableMenuItem(menu, command, flags);
}
constexpr int kAutoHideTaskbarThicknessPx = 2;
ui::EventType GetTouchEventType(POINTER_FLAGS pointer_flags) {
if (pointer_flags & POINTER_FLAG_DOWN) {
return ui::EventType::kTouchPressed;
}
if (pointer_flags & POINTER_FLAG_UPDATE) {
return ui::EventType::kTouchMoved;
}
if (pointer_flags & POINTER_FLAG_UP) {
return ui::EventType::kTouchReleased;
}
return ui::EventType::kTouchMoved;
}
bool IsHitTestOnResizeHandle(LRESULT hittest) {
return hittest == HTRIGHT || hittest == HTLEFT || hittest == HTTOP ||
hittest == HTBOTTOM || hittest == HTTOPLEFT || hittest == HTTOPRIGHT ||
hittest == HTBOTTOMLEFT || hittest == HTBOTTOMRIGHT;
}
gfx::ResizeEdge GetWindowResizeEdge(UINT param) {
switch (param) {
case WMSZ_BOTTOM:
return gfx::ResizeEdge::kBottom;
case WMSZ_TOP:
return gfx::ResizeEdge::kTop;
case WMSZ_LEFT:
return gfx::ResizeEdge::kLeft;
case WMSZ_RIGHT:
return gfx::ResizeEdge::kRight;
case WMSZ_TOPLEFT:
return gfx::ResizeEdge::kTopLeft;
case WMSZ_TOPRIGHT:
return gfx::ResizeEdge::kTopRight;
case WMSZ_BOTTOMLEFT:
return gfx::ResizeEdge::kBottomLeft;
case WMSZ_BOTTOMRIGHT:
return gfx::ResizeEdge::kBottomRight;
default:
NOTREACHED();
}
}
int GetFlagsFromRawInputMessage(RAWINPUT* input) {
int flags = ui::EF_NONE;
if (input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_1_DOWN) {
flags |= ui::EF_LEFT_MOUSE_BUTTON;
}
if (input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_2_DOWN) {
flags |= ui::EF_RIGHT_MOUSE_BUTTON;
}
if (input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_3_DOWN) {
flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
}
if (input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_4_DOWN) {
flags |= ui::EF_BACK_MOUSE_BUTTON;
}
if (input->data.mouse.usButtonFlags & RI_MOUSE_BUTTON_5_DOWN) {
flags |= ui::EF_FORWARD_MOUSE_BUTTON;
}
return ui::GetModifiersFromKeyState() | flags;
}
using WindowOwnerMap = base::flat_map<HWND, std::vector<HWND>>;
BOOL CALLBACK EnumOwnedWindowsProc(HWND hwnd, LPARAM l_param) {
HWND owner = ::GetWindow(hwnd, GW_OWNER);
if (owner) {
WindowOwnerMap* window_owner_map =
reinterpret_cast<WindowOwnerMap*>(l_param);
(*window_owner_map)[owner].push_back(hwnd);
}
return TRUE;
}
constexpr auto kTouchDownContextResetTimeout = base::Milliseconds(500);
constexpr int kSynthesizedMouseMessagesTimeDifference = 500;
}
class HWNDMessageHandler::ScopedRedrawLock {
public:
explicit ScopedRedrawLock(HWNDMessageHandler* owner)
: owner_(owner),
hwnd_(owner_->hwnd()),
should_lock_(owner_->IsVisible() && !owner->HasChildRenderingWindow() &&
::IsWindow(hwnd_) && !owner_->IsHeadless() &&
(!(GetWindowLong(hwnd_, GWL_STYLE) & WS_CAPTION))) {
if (should_lock_) {
owner_->LockUpdates();
}
}
ScopedRedrawLock(const ScopedRedrawLock&) = delete;
ScopedRedrawLock& operator=(const ScopedRedrawLock&) = delete;
~ScopedRedrawLock() {
if (!cancel_unlock_ && should_lock_ && ::IsWindow(hwnd_)) {
owner_->UnlockUpdates();
}
}
void CancelUnlockOperation() { cancel_unlock_ = true; }
private:
raw_ptr<HWNDMessageHandler> owner_;
HWND hwnd_;
bool cancel_unlock_ = false;
const bool should_lock_;
};
base::LazyInstance<HWNDMessageHandler::FullscreenWindowMonitorMap>::
DestructorAtExit HWNDMessageHandler::fullscreen_monitor_map_ =
LAZY_INSTANCE_INITIALIZER;
LONG HWNDMessageHandler::last_touch_or_pen_message_time_ = 0;
bool HWNDMessageHandler::is_pen_active_in_client_area_ = false;
bool HWNDMessageHandler::handle_pen_events_in_client_area_ = true;
std::unique_ptr<HWNDMessageHandler> HWNDMessageHandler::Create(
HWNDMessageHandlerDelegate* delegate,
const std::string& debugging_id) {
HWNDMessageHandler* message_handler =
display::Screen::Get()->IsHeadless()
? new HWNDMessageHandlerHeadless(delegate, debugging_id)
: new HWNDMessageHandler(delegate, debugging_id);
return base::WrapUnique(message_handler);
}
HWNDMessageHandler::HWNDMessageHandler(HWNDMessageHandlerDelegate* delegate,
const std::string& debugging_id)
: WindowImpl(debugging_id),
delegate_(delegate),
fullscreen_handler_(new FullscreenHandler),
waiting_for_close_now_(false),
restored_enabled_(false),
current_cursor_(base::MakeRefCounted<ui::WinCursor>()),
dpi_(0),
called_enable_non_client_dpi_scaling_(false),
active_mouse_tracking_flags_(0),
is_right_mouse_pressed_on_caption_(false),
lock_updates_count_(0),
ignore_window_pos_changes_(false),
last_monitor_(nullptr),
is_first_nccalc_(true),
menu_depth_(0),
id_generator_(0),
pen_processor_(&id_generator_, true),
user_resize_detector_(delegate),
touch_down_contexts_(0),
last_mouse_hwheel_time_(0),
dwm_transition_desired_(false),
sent_window_size_changing_(false),
left_button_down_on_caption_(false),
background_fullscreen_hack_(false) {}
HWNDMessageHandler::~HWNDMessageHandler() {
MoveLoopMouseWatcher::UnhookForHost(this);
ClearUserData();
}
void HWNDMessageHandler::Init(HWND parent, const gfx::Rect& bounds) {
TRACE_EVENT0("views", "HWNDMessageHandler::Init");
GetMonitorAndRects(bounds.ToRECT(), &last_monitor_, &last_monitor_rect_,
&last_work_area_);
initial_bounds_valid_ = !bounds.IsEmpty();
WindowImpl::Init(parent, bounds);
InitExtras();
}
void HWNDMessageHandler::InitModalType(ui::mojom::ModalType modal_type) {
if (modal_type != ui::mojom::ModalType::kWindow) {
return;
}
HWND start = ::GetWindow(hwnd(), GW_OWNER);
while (start) {
::EnableWindow(start, FALSE);
start = ::GetParent(start);
}
}
void HWNDMessageHandler::Close() {
if (!IsWindow(hwnd())) {
return;
}
Hide();
RestoreEnabledIfNecessary();
base::win::EnableFlicks(hwnd());
if (!waiting_for_close_now_) {
waiting_for_close_now_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&HWNDMessageHandler::CloseNow,
msg_handler_weak_factory_.GetWeakPtr()));
}
}
void HWNDMessageHandler::CloseNow() {
waiting_for_close_now_ = false;
if (IsWindow(hwnd())) {
DestroyWindow(hwnd());
}
}
gfx::Rect HWNDMessageHandler::GetWindowBoundsInScreen() const {
RECT r;
GetWindowRect(hwnd(), &r);
return gfx::Rect(r);
}
gfx::Rect HWNDMessageHandler::GetClientAreaBoundsInScreen() const {
RECT r;
GetClientRect(hwnd(), &r);
POINT point = {r.left, r.top};
ClientToScreen(hwnd(), &point);
return gfx::Rect(point.x, point.y, r.right - r.left, r.bottom - r.top);
}
gfx::Rect HWNDMessageHandler::GetRestoredBounds() const {
if (IsFullscreen()) {
return fullscreen_handler_->GetRestoreBounds();
}
gfx::Rect bounds;
GetWindowPlacement(&bounds, nullptr);
return bounds;
}
gfx::Rect HWNDMessageHandler::GetClientAreaBounds() const {
if (IsMinimized()) {
return gfx::Rect();
}
if (delegate_->WidgetSizeIsClientSize()) {
return GetClientAreaBoundsInScreen();
}
return GetWindowBoundsInScreen();
}
void HWNDMessageHandler::GetWindowPlacement(
gfx::Rect* bounds,
ui::mojom::WindowShowState* show_state) const {
WINDOWPLACEMENT wp;
wp.length = sizeof(wp);
bool succeeded = !!::GetWindowPlacement(hwnd(), &wp);
DCHECK(succeeded);
if (bounds != nullptr) {
if (wp.showCmd == SW_SHOWNORMAL) {
succeeded = GetWindowRect(hwnd(), &wp.rcNormalPosition) != 0;
DCHECK(succeeded);
*bounds = gfx::Rect(wp.rcNormalPosition);
} else {
MONITORINFO mi;
mi.cbSize = sizeof(mi);
succeeded =
GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST),
&mi) != 0;
DCHECK(succeeded);
*bounds = gfx::Rect(wp.rcNormalPosition);
bounds->Offset(mi.rcWork.left - mi.rcMonitor.left,
mi.rcWork.top - mi.rcMonitor.top);
}
}
if (show_state) {
if (wp.showCmd == SW_SHOWMAXIMIZED) {
*show_state = ui::mojom::WindowShowState::kMaximized;
} else if (wp.showCmd == SW_SHOWMINIMIZED) {
*show_state = ui::mojom::WindowShowState::kMinimized;
} else {
*show_state = ui::mojom::WindowShowState::kNormal;
}
}
}
void HWNDMessageHandler::SetBounds(const gfx::Rect& bounds_in_pixels,
bool force_size_changed) {
background_fullscreen_hack_ = false;
SetBoundsInternal(bounds_in_pixels, force_size_changed);
}
void HWNDMessageHandler::SetParentOrOwner(HWND new_parent) {
LONG style = GetWindowLong(hwnd(), GWL_STYLE);
if (style & WS_CHILD) {
DCHECK(new_parent);
SetParent(hwnd(), new_parent);
} else {
SetWindowLongPtr(hwnd(), GWLP_HWNDPARENT,
reinterpret_cast<LONG_PTR>(new_parent));
}
}
std::vector<HWND> HWNDMessageHandler::GetOwnedWindows() {
std::vector<HWND> owned_windows;
WindowOwnerMap window_owner_map;
::EnumThreadWindows(GetCurrentThreadId(), &EnumOwnedWindowsProc,
reinterpret_cast<LPARAM>(&window_owner_map));
if (window_owner_map.find(hwnd()) == window_owner_map.end()) {
return owned_windows;
}
base::queue<HWND> to_process;
to_process.push(hwnd());
while (!to_process.empty()) {
HWND current_owner = to_process.front();
to_process.pop();
auto it = window_owner_map.find(current_owner);
if (it != window_owner_map.end()) {
for (HWND owned_child : it->second) {
if (IsWindow(owned_child)) {
owned_windows.push_back(owned_child);
to_process.push(owned_child);
}
}
}
}
return owned_windows;
}
void HWNDMessageHandler::SetDwmFrameExtension(DwmFrameState state) {
if (!delegate_->HasFrame() && !is_translucent_) {
MARGINS m = {0, 0, 0, 0};
if (state == DwmFrameState::kOn && !IsMaximized()) {
m = {0, 0, 1, 0};
}
DwmExtendFrameIntoClientArea(hwnd(), &m);
}
}
void HWNDMessageHandler::SetSize(const gfx::Size& size) {
SetWindowPos(hwnd(), nullptr, 0, 0, size.width(), size.height(),
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
}
void HWNDMessageHandler::CenterWindow(const gfx::Size& size) {
HWND parent = GetParent(hwnd());
if (!IsWindow(parent)) {
parent = ::GetWindow(hwnd(), GW_OWNER);
}
gfx::CenterAndSizeWindow(parent, hwnd(), size);
}
void HWNDMessageHandler::SetRegion(HRGN region) {
custom_window_region_.reset(region);
ResetWindowRegion(true, true);
}
void HWNDMessageHandler::StackAbove(HWND other_hwnd) {
DCHECK(other_hwnd);
HWND next_window = ::GetNextWindow(other_hwnd, GW_HWNDPREV);
base::AutoReset<bool> auto_reset(&ignore_window_pos_changes_, true);
::SetWindowPos(hwnd(), next_window ? next_window : HWND_TOP, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
void HWNDMessageHandler::StackAtTop() {
base::AutoReset<bool> auto_reset(&ignore_window_pos_changes_, true);
::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
void HWNDMessageHandler::Show(ui::mojom::WindowShowState show_state,
const gfx::Rect& pixel_restore_bounds) {
TRACE_EVENT0("views", "HWNDMessageHandler::Show");
int native_show_state;
if (show_state == ui::mojom::WindowShowState::kMaximized &&
!pixel_restore_bounds.IsEmpty()) {
WINDOWPLACEMENT placement = {0};
placement.length = sizeof(WINDOWPLACEMENT);
placement.showCmd = SW_SHOWMAXIMIZED;
placement.rcNormalPosition = pixel_restore_bounds.ToRECT();
SetWindowPlacement(hwnd(), &placement);
native_show_state = SW_SHOWMAXIMIZED;
} else {
const bool is_maximized_or_arranged =
IsMaximized() || IsWindowArranged(hwnd());
switch (show_state) {
case ui::mojom::WindowShowState::kInactive:
native_show_state =
is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE;
break;
case ui::mojom::WindowShowState::kMaximized:
native_show_state = SW_SHOWMAXIMIZED;
break;
case ui::mojom::WindowShowState::kMinimized:
native_show_state = SW_SHOWMINIMIZED;
break;
case ui::mojom::WindowShowState::kNormal:
if ((GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) ||
(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
native_show_state =
is_maximized_or_arranged ? SW_SHOWNA : SW_SHOWNOACTIVATE;
} else {
native_show_state =
is_maximized_or_arranged ? SW_SHOW : SW_SHOWNORMAL;
}
break;
case ui::mojom::WindowShowState::kFullscreen:
native_show_state = SW_SHOWNORMAL;
SetFullscreen(true, display::kInvalidDisplayId);
break;
default:
native_show_state = delegate_->GetInitialShowState();
break;
}
ShowWindow(hwnd(), native_show_state);
if (native_show_state == SW_HIDE) {
native_show_state = SW_SHOWNORMAL;
ShowWindow(hwnd(), native_show_state);
}
}
if (native_show_state == SW_SHOWNORMAL ||
native_show_state == SW_SHOWMAXIMIZED) {
Activate();
}
if (!delegate_->HandleInitialFocus(show_state)) {
SetInitialFocus();
}
}
void HWNDMessageHandler::Hide() {
if (IsWindow(hwnd())) {
SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0,
SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE |
SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER);
}
}
void HWNDMessageHandler::Maximize() {
if (IsFullscreen()) {
SetFullscreen(false, display::kInvalidDisplayId);
}
ExecuteSystemMenuCommand(SC_MAXIMIZE);
}
void HWNDMessageHandler::Minimize() {
ExecuteSystemMenuCommand(SC_MINIMIZE);
delegate_->HandleNativeBlur(nullptr);
}
void HWNDMessageHandler::Restore() {
if (IsFullscreen()) {
SetFullscreen(false, display::kInvalidDisplayId);
} else {
ExecuteSystemMenuCommand(SC_RESTORE);
}
}
void HWNDMessageHandler::Activate() {
if (IsMinimized()) {
base::AutoReset<bool> restoring_activate(¬ify_restore_on_activate_,
true);
::ShowWindow(hwnd(), SW_RESTORE);
}
::SetWindowPos(hwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
SetForegroundWindow(hwnd());
}
void HWNDMessageHandler::Deactivate() {
HWND next_hwnd = ::GetNextWindow(hwnd(), GW_HWNDNEXT);
while (next_hwnd) {
if (::IsWindowVisible(next_hwnd)) {
::SetForegroundWindow(next_hwnd);
return;
}
next_hwnd = ::GetNextWindow(next_hwnd, GW_HWNDNEXT);
}
}
void HWNDMessageHandler::SetAlwaysOnTop(bool on_top) {
::SetWindowPos(hwnd(), on_top ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
bool HWNDMessageHandler::IsVisible() const {
return !!::IsWindowVisible(hwnd());
}
bool HWNDMessageHandler::IsActive() const {
return ::GetActiveWindow() == ::GetAncestor(hwnd(), GA_ROOT);
}
bool HWNDMessageHandler::IsMinimized() const {
return !!::IsIconic(hwnd());
}
bool HWNDMessageHandler::IsMaximized() const {
return !!::IsZoomed(hwnd()) && !IsFullscreen();
}
bool HWNDMessageHandler::IsFullscreen() const {
return fullscreen_handler_->fullscreen();
}
bool HWNDMessageHandler::IsAlwaysOnTop() const {
return (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TOPMOST) != 0;
}
bool HWNDMessageHandler::IsHeadless() const {
return false;
}
bool HWNDMessageHandler::RunMoveLoop(const gfx::Vector2d& drag_offset,
bool hide_on_escape) {
ReleaseCapture();
MoveLoopMouseWatcher watcher(msg_handler_weak_factory_.GetWeakPtr(),
hide_on_escape);
base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
SendMessage(hwnd(), WM_SYSCOMMAND, SC_MOVE | 0x0002,
static_cast<LPARAM>(GetMessagePos()));
return watcher.got_mouse_up();
}
void HWNDMessageHandler::EndMoveLoop() {
SendMessage(hwnd(), WM_CANCELMODE, 0, 0);
}
void HWNDMessageHandler::SendFrameChanged() {
SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE |
SWP_NOOWNERZORDER | SWP_NOREPOSITION | SWP_NOSENDCHANGING |
SWP_NOSIZE | SWP_NOZORDER);
}
void HWNDMessageHandler::FlashFrame(bool flash) {
FLASHWINFO fwi;
fwi.cbSize = sizeof(fwi);
fwi.hwnd = hwnd();
if (flash) {
fwi.dwFlags = custom_window_region_.is_valid() ? FLASHW_TRAY : FLASHW_ALL;
fwi.uCount = 4;
fwi.dwTimeout = 0;
} else {
fwi.dwFlags = FLASHW_STOP;
}
FlashWindowEx(&fwi);
}
void HWNDMessageHandler::ClearNativeFocus() {
::SetFocus(hwnd());
}
void HWNDMessageHandler::SetCapture() {
DCHECK(!HasCapture()) << " release capture error = "
<< logging::SystemErrorCodeToString(
release_capture_errno_);
::SetCapture(hwnd());
}
void HWNDMessageHandler::ReleaseCapture() {
if (HasCapture() && !::ReleaseCapture()) {
release_capture_errno_ = ::GetLastError();
} else {
release_capture_errno_ = 0;
}
}
bool HWNDMessageHandler::HasCapture() const {
return ::GetCapture() == hwnd();
}
FullscreenHandler* HWNDMessageHandler::fullscreen_handler() {
return fullscreen_handler_.get();
}
void HWNDMessageHandler::SetVisibilityChangedAnimationsEnabled(bool enabled) {
int dwm_value = enabled ? FALSE : TRUE;
DwmSetWindowAttribute(hwnd(), DWMWA_TRANSITIONS_FORCEDISABLED, &dwm_value,
sizeof(dwm_value));
}
bool HWNDMessageHandler::SetTitle(const std::u16string& title) {
std::wstring current_title;
auto len_with_null = static_cast<size_t>(GetWindowTextLength(hwnd())) + 1;
if (len_with_null == 1 && title.length() == 0) {
return false;
}
if (len_with_null - 1 == title.length() &&
GetWindowText(hwnd(), base::WriteInto(¤t_title, len_with_null),
len_with_null) &&
current_title == base::AsWStringView(title)) {
return false;
}
SetWindowText(hwnd(), base::as_wcstr(title));
return true;
}
void HWNDMessageHandler::SetCursor(scoped_refptr<ui::WinCursor> cursor) {
DCHECK(cursor);
TRACE_EVENT1("ui,input", "HWNDMessageHandler::SetCursor", "cursor",
static_cast<const void*>(cursor->hcursor()));
::SetCursor(cursor->hcursor());
current_cursor_ = cursor;
}
void HWNDMessageHandler::FrameTypeChanged() {
needs_dwm_frame_clear_ = true;
if (!custom_window_region_.is_valid() && IsFrameSystemDrawn()) {
dwm_transition_desired_ = true;
}
if (!dwm_transition_desired_ || !IsFullscreen()) {
PerformDwmTransition();
}
}
void HWNDMessageHandler::PaintAsActiveChanged() {
if (!delegate_->HasNonClientView() || !delegate_->CanActivate() ||
!delegate_->HasFrame() ||
(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN)) {
return;
}
DefWindowProcWithRedrawLock(WM_NCACTIVATE, delegate_->ShouldPaintAsActive(),
0);
}
void HWNDMessageHandler::SetWindowIcons(const gfx::ImageSkia& window_icon,
const gfx::ImageSkia& app_icon) {
if (!window_icon.isNull()) {
base::win::ScopedGDIObject<HICON> previous_icon = std::move(window_icon_);
window_icon_ = IconUtil::CreateHICONFromSkBitmap(*window_icon.bitmap());
SendMessage(hwnd(), WM_SETICON, ICON_SMALL,
reinterpret_cast<LPARAM>(window_icon_.get()));
}
if (!app_icon.isNull()) {
base::win::ScopedGDIObject<HICON> previous_icon = std::move(app_icon_);
app_icon_ = IconUtil::CreateHICONFromSkBitmap(*app_icon.bitmap());
SendMessage(hwnd(), WM_SETICON, ICON_BIG,
reinterpret_cast<LPARAM>(app_icon_.get()));
}
}
void HWNDMessageHandler::SetFullscreen(bool fullscreen,
int64_t target_display_id) {
HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY);
FullscreenWindowMonitorMap::iterator iter =
fullscreen_monitor_map_.Get().find(monitor);
if (iter != fullscreen_monitor_map_.Get().end()) {
fullscreen_monitor_map_.Get().erase(iter);
}
background_fullscreen_hack_ = false;
auto ref = msg_handler_weak_factory_.GetWeakPtr();
fullscreen_handler()->SetFullscreen(fullscreen, target_display_id);
if (!ref) {
return;
}
if (fullscreen) {
HMONITOR new_monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY);
(fullscreen_monitor_map_.Get())[new_monitor] = this;
}
if (!fullscreen && dwm_transition_desired_) {
PerformDwmTransition();
}
}
void HWNDMessageHandler::SetAspectRatio(float aspect_ratio,
const gfx::Size& excluded_margin) {
DCHECK_GT(aspect_ratio, 0.0f);
aspect_ratio_ = aspect_ratio;
excluded_margin_dip_ = excluded_margin;
RECT window_rect;
if (GetWindowRect(hwnd(), &window_rect)) {
gfx::Rect rect(window_rect);
SizeWindowToAspectRatio(WMSZ_BOTTOMRIGHT, &rect);
SetBoundsInternal(rect, false);
}
}
void HWNDMessageHandler::SizeConstraintsChanged() {
LONG style = GetWindowLong(hwnd(), GWL_STYLE);
const bool had_caption_on_init = window_style() & WS_CAPTION;
const bool can_resize = !is_translucent_ && delegate_->CanResize();
const bool can_maximize = can_resize && delegate_->CanMaximize();
auto set_style_func = [&style](LONG bit, bool should_set) {
if (should_set) {
style |= bit;
} else {
style &= ~bit;
}
};
set_style_func(WS_THICKFRAME, can_resize);
set_style_func(WS_CAPTION,
(can_resize | had_caption_on_init) &&
!(style & static_cast<LONG>(WS_POPUP | WS_CHILD)));
set_style_func(WS_MAXIMIZEBOX, can_maximize);
set_style_func(WS_MINIMIZEBOX, delegate_->CanMinimize());
SetWindowLong(hwnd(), GWL_STYLE, style);
SendFrameChanged();
}
void HWNDMessageHandler::UseDefaultHandlerForPenEventsUntilPenUp() {
handle_pen_events_in_client_area_ = false;
}
bool HWNDMessageHandler::HasChildRenderingWindow() {
return gfx::RenderingWindowManager::GetInstance()->HasValidChildWindow(
hwnd());
}
void HWNDMessageHandler::set_is_translucent(bool is_translucent) {
is_translucent_ = is_translucent;
}
bool HWNDMessageHandler::is_translucent() const {
return is_translucent_;
}
std::unique_ptr<aura::ScopedEnableUnadjustedMouseEvents>
HWNDMessageHandler::RegisterUnadjustedMouseEvent() {
std::unique_ptr<ScopedEnableUnadjustedMouseEventsWin> scoped_enable =
ScopedEnableUnadjustedMouseEventsWin::StartMonitor(this);
DCHECK(using_wm_input_);
return scoped_enable;
}
void HWNDMessageHandler::set_using_wm_input(bool using_wm_input) {
using_wm_input_ = using_wm_input;
}
bool HWNDMessageHandler::using_wm_input() const {
return using_wm_input_;
}
HICON HWNDMessageHandler::GetDefaultWindowIcon() const {
return ViewsDelegate::GetInstance()->GetDefaultWindowIcon();
}
HICON HWNDMessageHandler::GetSmallWindowIcon() const {
return ViewsDelegate::GetInstance()->GetSmallWindowIcon();
}
LRESULT HWNDMessageHandler::OnWndProc(UINT message,
WPARAM w_param,
LPARAM l_param) {
TRACE_EVENT("ui,toplevel", "HWNDMessageHandler::OnWndProc",
[&](perfetto::EventContext ctx) {
perfetto::protos::pbzero::ChromeWindowHandleEventInfo* args =
ctx.event()->set_chrome_window_handle_event_info();
args->set_message_id(message);
args->set_hwnd_ptr(
static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hwnd())));
});
HWND window = hwnd();
LRESULT result = 0;
if (delegate_ &&
delegate_->PreHandleMSG(message, w_param, l_param, &result)) {
return result;
}
const BOOL old_msg_handled = msg_handled_;
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
const BOOL processed =
_ProcessWindowMessage(window, message, w_param, l_param, result, 0);
if (!ref) {
return 0;
}
msg_handled_ = old_msg_handled;
if (!processed) {
result = DefWindowProc(window, message, w_param, l_param);
if (!ref || !::IsWindow(window)) {
return result;
}
}
if (delegate_) {
delegate_->PostHandleMSG(message, w_param, l_param);
if (message == WM_NCDESTROY) {
RestoreEnabledIfNecessary();
delegate_->HandleDestroyed();
}
}
if (message == WM_ACTIVATE && IsTopLevelWindow(window)) {
PostProcessActivateMessage(LOWORD(w_param), !!HIWORD(w_param),
reinterpret_cast<HWND>(l_param));
}
return result;
}
void HWNDMessageHandler::OnFocus() {}
void HWNDMessageHandler::OnBlur() {}
void HWNDMessageHandler::OnCaretBoundsChanged(
const ui::TextInputClient* client) {
if (!ax_system_caret_) {
ax_system_caret_ = std::make_unique<ui::AXSystemCaretWin>(hwnd());
}
if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) {
ax_system_caret_->Hide();
return;
}
const gfx::Rect dip_caret_bounds(client->GetCaretBounds());
gfx::Rect caret_bounds =
display::win::GetScreenWin()->DIPToScreenRect(hwnd(), dip_caret_bounds);
caret_bounds.set_width(1);
ax_system_caret_->MoveCaretTo(caret_bounds);
}
void HWNDMessageHandler::OnTextInputStateChanged(
const ui::TextInputClient* client) {
if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) {
OnCaretBoundsChanged(client);
}
}
void HWNDMessageHandler::OnInputMethodDestroyed(
const ui::InputMethod* input_method) {
DestroyAXSystemCaret();
}
LRESULT HWNDMessageHandler::HandleMouseMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = HandleMouseEventInternal(message, w_param, l_param, false);
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandleKeyboardMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = 0;
if ((message == WM_CHAR) || (message == WM_SYSCHAR)) {
ret = OnImeMessages(message, w_param, l_param);
} else {
ret = OnKeyEvent(message, w_param, l_param);
}
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandleTouchMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnTouchEvent(message, w_param, l_param);
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandlePointerMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnPointerEvent(message, w_param, l_param);
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandleInputMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnInputEvent(message, w_param, l_param);
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandleScrollMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnScrollMessage(message, w_param, l_param);
*handled = !ref.get() || msg_handled_;
return ret;
}
LRESULT HWNDMessageHandler::HandleNcHitTestMessage(unsigned int message,
WPARAM w_param,
LPARAM l_param,
bool* handled) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT ret = OnNCHitTest(
gfx::Point(CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)));
*handled = !ref.get() || msg_handled_;
return ret;
}
void HWNDMessageHandler::ApplyPinchZoomScale(float scale) {
POINT cursor_pos = GetCursorPos();
ScreenToClient(hwnd(), &cursor_pos);
ui::GestureEventDetails event_details(ui::EventType::kGesturePinchUpdate);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
event_details.set_scale(scale);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
void HWNDMessageHandler::ApplyPinchZoomBegin() {
POINT cursor_pos = GetCursorPos();
ScreenToClient(hwnd(), &cursor_pos);
ui::GestureEventDetails event_details(ui::EventType::kGesturePinchBegin);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
void HWNDMessageHandler::ApplyPinchZoomEnd() {
POINT cursor_pos = GetCursorPos();
ScreenToClient(hwnd(), &cursor_pos);
ui::GestureEventDetails event_details(ui::EventType::kGesturePinchEnd);
event_details.set_device_type(ui::GestureDeviceType::DEVICE_TOUCHPAD);
ui::GestureEvent event(cursor_pos.x, cursor_pos.y, ui::EF_NONE,
base::TimeTicks::Now(), event_details);
delegate_->HandleGestureEvent(&event);
}
void HWNDMessageHandler::ApplyPanGestureEvent(
int scroll_x,
int scroll_y,
ui::EventMomentumPhase momentum_phase,
ui::ScrollEventPhase phase) {
gfx::Vector2d offset{scroll_x, scroll_y};
POINT root_location = GetCursorPos();
POINT location = {root_location.x, root_location.y};
ScreenToClient(hwnd(), &location);
gfx::Point cursor_location(location);
gfx::Point cursor_root_location(root_location);
int modifiers = ui::GetModifiersFromKeyState();
ui::ScrollEvent event(ui::EventType::kScroll, cursor_location,
ui::EventTimeForNow(), modifiers, scroll_x, scroll_y,
scroll_x, scroll_y, 2, momentum_phase, phase);
delegate_->HandleScrollEvent(&event);
}
void HWNDMessageHandler::ApplyPanGestureScroll(int scroll_x, int scroll_y) {
ApplyPanGestureEvent(scroll_x, scroll_y, ui::EventMomentumPhase::NONE,
ui::ScrollEventPhase::kUpdate);
}
void HWNDMessageHandler::ApplyPanGestureFling(int scroll_x, int scroll_y) {
ApplyPanGestureEvent(scroll_x, scroll_y,
ui::EventMomentumPhase::INERTIAL_UPDATE,
ui::ScrollEventPhase::kNone);
}
void HWNDMessageHandler::ApplyPanGestureScrollBegin(int scroll_x,
int scroll_y) {
ApplyPanGestureEvent(scroll_x, scroll_y, ui::EventMomentumPhase::NONE,
ui::ScrollEventPhase::kBegan);
}
void HWNDMessageHandler::ApplyPanGestureScrollEnd(bool transitioning_to_pinch) {
ApplyPanGestureEvent(0, 0,
transitioning_to_pinch ? ui::EventMomentumPhase::BLOCKED
: ui::EventMomentumPhase::NONE,
ui::ScrollEventPhase::kEnd);
}
void HWNDMessageHandler::ApplyPanGestureFlingBegin() {
ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::BEGAN,
ui::ScrollEventPhase::kNone);
}
void HWNDMessageHandler::ApplyPanGestureFlingEnd() {
ApplyPanGestureEvent(0, 0, ui::EventMomentumPhase::END,
ui::ScrollEventPhase::kNone);
}
gfx::NativeViewAccessible HWNDMessageHandler::GetChildOfAXFragmentRoot() {
return delegate_->GetNativeViewAccessible();
}
gfx::NativeViewAccessible HWNDMessageHandler::GetParentOfAXFragmentRoot() {
if (!features::IsAccessibilityWinAXFragmentRootParentEnabled()) {
return nullptr;
}
return delegate_->GetParentNativeViewAccessible();
}
bool HWNDMessageHandler::IsAXFragmentRootAControlElement() {
return true;
}
void HWNDMessageHandler::InitExtras() {
if (!called_enable_non_client_dpi_scaling_ && delegate_->HasFrame()) {
using EnableChildWindowDpiMessagePtr = LRESULT(WINAPI*)(HWND, BOOL);
static const auto enable_child_window_dpi_message_func =
reinterpret_cast<EnableChildWindowDpiMessagePtr>(
base::win::GetUser32FunctionPointer("EnableChildWindowDpiMessage"));
if (enable_child_window_dpi_message_func) {
enable_child_window_dpi_message_func(hwnd(), TRUE);
}
}
prop_window_target_ = std::make_unique<ui::ViewProp>(
hwnd(), ui::WindowEventTarget::kWin32InputEventTarget,
static_cast<ui::WindowEventTarget*>(this));
DCHECK(delegate_->GetHWNDMessageDelegateInputMethod());
observation_.Observe(delegate_->GetHWNDMessageDelegateInputMethod());
if (::ui::AXPlatform::GetInstance().IsUiaProviderEnabled()) {
ax_fragment_root_ = std::make_unique<ui::AXFragmentRootWin>(hwnd(), this);
}
base::win::DisableFlicks(hwnd());
}
int HWNDMessageHandler::GetAppbarAutohideEdges(HMONITOR monitor) {
autohide_factory_.InvalidateWeakPtrs();
return ViewsDelegate::GetInstance()->GetAppbarAutohideEdges(
monitor, base::BindOnce(&HWNDMessageHandler::OnAppbarAutohideEdgesChanged,
autohide_factory_.GetWeakPtr()));
}
void HWNDMessageHandler::OnAppbarAutohideEdgesChanged() {
RECT client;
GetWindowRect(hwnd(), &client);
SetWindowPos(hwnd(), nullptr, client.left, client.top,
client.right - client.left, client.bottom - client.top,
SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE);
}
void HWNDMessageHandler::SetInitialFocus() {
if (!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_TRANSPARENT) &&
!(GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE)) {
if (IsHeadless()) {
delegate_->HandleNativeFocus(0);
} else {
::SetFocus(hwnd());
}
}
}
void HWNDMessageHandler::PostProcessActivateMessage(
int activation_state,
bool minimized,
HWND window_gaining_or_losing_activation) {
DCHECK(IsTopLevelWindow(hwnd()));
if (::IsChild(hwnd(), window_gaining_or_losing_activation) &&
gfx::GetClassName(window_gaining_or_losing_activation) ==
std::wstring(ui::kLegacyRenderWidgetHostHwnd)) {
return;
}
const bool active = activation_state != WA_INACTIVE && !minimized;
if (notify_restore_on_activate_) {
notify_restore_on_activate_ = false;
delegate_->HandleWindowMinimizedOrRestored(true);
last_size_param_ = SIZE_RESTORED;
}
if (delegate_->CanActivate()) {
delegate_->HandleActivationChanged(active);
}
if (!::IsWindow(window_gaining_or_losing_activation)) {
window_gaining_or_losing_activation = ::GetForegroundWindow();
}
if (!active) {
if (IsFullscreen() && ::IsWindow(window_gaining_or_losing_activation)) {
HMONITOR active_window_monitor = MonitorFromWindow(
window_gaining_or_losing_activation, MONITOR_DEFAULTTOPRIMARY);
HMONITOR fullscreen_window_monitor =
MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY);
if (active_window_monitor == fullscreen_window_monitor) {
OnBackgroundFullscreen();
}
}
} else if (background_fullscreen_hack_) {
DCHECK(IsFullscreen());
MONITORINFO monitor_info = {sizeof(monitor_info)};
GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY),
&monitor_info);
SetBoundsInternal(gfx::Rect(monitor_info.rcMonitor), false);
fullscreen_handler()->MarkFullscreen(true);
background_fullscreen_hack_ = false;
} else {
CheckAndHandleBackgroundFullscreenOnMonitor(
window_gaining_or_losing_activation);
}
}
void HWNDMessageHandler::RestoreEnabledIfNecessary() {
if (delegate_->IsModal() && !restored_enabled_) {
restored_enabled_ = true;
HWND start = ::GetWindow(hwnd(), GW_OWNER);
while (start) {
::EnableWindow(start, TRUE);
start = ::GetParent(start);
}
}
}
void HWNDMessageHandler::ExecuteSystemMenuCommand(int command) {
if (command) {
SendMessage(hwnd(), WM_SYSCOMMAND, static_cast<WPARAM>(command), 0);
}
}
void HWNDMessageHandler::TrackMouseEvents(DWORD mouse_tracking_flags) {
if (active_mouse_tracking_flags_ == 0 || mouse_tracking_flags & TME_CANCEL) {
if (mouse_tracking_flags & TME_CANCEL) {
active_mouse_tracking_flags_ = 0;
} else {
active_mouse_tracking_flags_ = mouse_tracking_flags;
}
TRACKMOUSEEVENT tme;
tme.cbSize = sizeof(tme);
tme.dwFlags = mouse_tracking_flags;
tme.hwndTrack = hwnd();
tme.dwHoverTime = 0;
TrackMouseEvent(&tme);
} else if (mouse_tracking_flags != active_mouse_tracking_flags_) {
TrackMouseEvents(active_mouse_tracking_flags_ | TME_CANCEL);
TrackMouseEvents(mouse_tracking_flags);
}
}
void HWNDMessageHandler::ClientAreaSizeChanged() {
if ((background_fullscreen_hack_ && !sent_window_size_changing_) ||
IsHeadless()) {
return;
}
auto ref = msg_handler_weak_factory_.GetWeakPtr();
delegate_->HandleClientSizeChanged(GetClientAreaBounds().size());
if (!ref) {
return;
}
current_window_size_message_++;
sent_window_size_changing_ = false;
}
bool HWNDMessageHandler::GetClientAreaInsets(gfx::Insets* insets,
HMONITOR monitor) const {
int frame_thickness = ui::GetResizableFrameThicknessFromMonitorInPixels(
monitor, GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION);
if (delegate_->GetClientAreaInsets(insets, frame_thickness)) {
return true;
}
DCHECK(insets->IsEmpty());
if (!delegate_->HasNonClientView() || HasSystemFrame()) {
return false;
}
if (IsMaximized()) {
if (!delegate_->HasFrame()) {
frame_thickness -= 1;
}
*insets = gfx::Insets(frame_thickness);
return true;
}
*insets = gfx::Insets();
return true;
}
void HWNDMessageHandler::ResetWindowRegion(bool force, bool redraw) {
if (!is_translucent_ && !custom_window_region_.is_valid() &&
(IsFrameSystemDrawn() || !delegate_->HasNonClientView())) {
if (force) {
SetWindowRgn(hwnd(), nullptr, redraw);
}
return;
}
base::win::ScopedGDIObject<HRGN> current_rgn(CreateRectRgn(0, 0, 0, 0));
GetWindowRgn(hwnd(), current_rgn.get());
RECT window_rect;
GetWindowRect(hwnd(), &window_rect);
base::win::ScopedGDIObject<HRGN> new_region;
if (custom_window_region_.is_valid()) {
new_region.reset(CreateRectRgn(0, 0, 0, 0));
CombineRgn(new_region.get(), custom_window_region_.get(), nullptr,
RGN_COPY);
} else if (IsMaximized()) {
HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof mi;
GetMonitorInfo(monitor, &mi);
RECT work_rect = mi.rcWork;
OffsetRect(&work_rect, static_cast<int>(-window_rect.left),
static_cast<int>(-window_rect.top));
new_region.reset(CreateRectRgnIndirect(&work_rect));
} else {
SkPath window_mask;
delegate_->GetWindowMask(gfx::Size(window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top),
&window_mask);
if (!window_mask.isEmpty()) {
new_region.reset(gfx::CreateHRGNFromSkPath(window_mask));
}
}
const bool has_current_region = current_rgn != nullptr;
const bool has_new_region = new_region != nullptr;
if (has_current_region != has_new_region ||
(has_current_region && !EqualRgn(current_rgn.get(), new_region.get()))) {
SetWindowRgn(hwnd(), new_region.release(), redraw);
}
}
LRESULT HWNDMessageHandler::DefWindowProcWithRedrawLock(UINT message,
WPARAM w_param,
LPARAM l_param) {
ScopedRedrawLock lock(this);
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
LRESULT result = DefWindowProc(hwnd(), message, w_param, l_param);
if (!ref) {
lock.CancelUnlockOperation();
}
return result;
}
void HWNDMessageHandler::LockUpdates() {
if (++lock_updates_count_ == 1) {
SetWindowLong(hwnd(), GWL_STYLE,
GetWindowLong(hwnd(), GWL_STYLE) & ~WS_VISIBLE);
}
}
void HWNDMessageHandler::UnlockUpdates() {
if (--lock_updates_count_ <= 0) {
SetWindowLong(hwnd(), GWL_STYLE,
GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
lock_updates_count_ = 0;
}
}
void HWNDMessageHandler::ForceRedrawWindow(int attempts) {
if (ui::IsWorkstationLocked()) {
if (--attempts <= 0) {
return;
}
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::ForceRedrawWindow,
msg_handler_weak_factory_.GetWeakPtr(), attempts),
base::Milliseconds(500));
return;
}
InvalidateRect(hwnd(), nullptr, FALSE);
}
bool HWNDMessageHandler::IsFrameSystemDrawn() const {
FrameMode frame_mode = delegate_->GetFrameMode();
return frame_mode == FrameMode::SYSTEM_DRAWN ||
frame_mode == FrameMode::SYSTEM_DRAWN_NO_CONTROLS;
}
bool HWNDMessageHandler::HasSystemFrame() const {
return delegate_->HasFrame() && IsFrameSystemDrawn();
}
void HWNDMessageHandler::OnActivateApp(BOOL active, DWORD thread_id) {
if (delegate_->HasNonClientView() && !active &&
thread_id != GetCurrentThreadId()) {
if (HasSystemFrame()) {
DefWindowProcWithRedrawLock(WM_NCACTIVATE, FALSE, 0);
}
}
}
BOOL HWNDMessageHandler::OnAppCommand(HWND window,
int command,
WORD device,
WORD keystate) {
BOOL handled = !!delegate_->HandleAppCommand(command);
SetMsgHandled(handled);
return handled;
}
void HWNDMessageHandler::OnCancelMode() {
delegate_->HandleCancelMode();
SetMsgHandled(FALSE);
}
void HWNDMessageHandler::OnCaptureChanged(HWND window) {
delegate_->HandleCaptureLost();
}
void HWNDMessageHandler::OnClose() {
delegate_->HandleClose();
}
void HWNDMessageHandler::OnCommand(UINT notification_code,
int command,
HWND window) {
if (notification_code > 1 || delegate_->HandleAppCommand(command)) {
SetMsgHandled(FALSE);
}
}
LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) {
if (is_translucent_) {
MARGINS margins = {-1, -1, -1, -1};
DwmExtendFrameIntoClientArea(hwnd(), &margins);
::SetProp(hwnd(), ui::kWindowTranslucent, reinterpret_cast<HANDLE>(1));
}
if (base::win::GetVersion() >= base::win::Version::WIN11 &&
use_rounded_corner_) {
DWM_WINDOW_CORNER_PREFERENCE corner_pref = DWMWCP_ROUND;
DwmSetWindowAttribute(hwnd(), DWMWA_WINDOW_CORNER_PREFERENCE, &corner_pref,
sizeof(corner_pref));
}
fullscreen_handler_->set_hwnd(hwnd());
base::win::AllowDarkModeForWindow(hwnd(), true);
SendMessage(hwnd(), WM_CHANGEUISTATE, MAKELPARAM(UIS_CLEAR, UISF_HIDEFOCUS),
0);
if (!delegate_->HasFrame()) {
SetWindowLong(hwnd(), GWL_STYLE,
GetWindowLong(hwnd(), GWL_STYLE) & ~WS_CAPTION);
SendFrameChanged();
}
GetSystemMenu(hwnd(), false);
ClientAreaSizeChanged();
delegate_->HandleCreate();
session_change_observer_ =
std::make_unique<ui::SessionChangeObserver>(base::BindRepeating(
&HWNDMessageHandler::OnSessionChange, base::Unretained(this)));
if (initial_bounds_valid_) {
dpi_ = display::win::GetScreenWin()->GetDPIForHWND(hwnd());
}
may_service_accessibility_requests_ = true;
return 0;
}
void HWNDMessageHandler::OnDestroy() {
may_service_accessibility_requests_ = false;
::RemoveProp(hwnd(), ui::kWindowTranslucent);
session_change_observer_.reset();
delegate_->HandleDestroying();
auto& map = fullscreen_monitor_map_.Get();
const auto i = std::ranges::find(
map, this, &FullscreenWindowMonitorMap::value_type::second);
if (i != map.end()) {
map.erase(i);
}
if (auto& ax_platform = ui::AXPlatform::GetInstance();
ax_platform.HasServicedUiaClients()) {
if (ax_platform.IsUiaProviderEnabled() &&
base::FeatureList::IsEnabled(features::kUiaDisconnectRootProviders)) {
::UiaDisconnectProvider(ax_fragment_root_->GetProvider());
}
::UiaReturnRawElementProvider(hwnd(), 0, 0, nullptr);
}
}
void HWNDMessageHandler::OnDisplayChange(UINT bits_per_pixel,
const gfx::Size& screen_size) {
TRACE_EVENT0("ui", "HWNDMessageHandler::OnDisplayChange");
display::win::GetScreenWin()->UpdateDisplayInfosIfNeeded();
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleDisplayChange();
if (!ref) {
return;
}
SendFrameChanged();
}
LRESULT HWNDMessageHandler::OnDpiChanged(UINT msg,
WPARAM w_param,
LPARAM l_param) {
if (LOWORD(w_param) != HIWORD(w_param)) {
NOTIMPLEMENTED() << "Received non-square scaling factors";
}
TRACE_EVENT("ui", "HWNDMessageHandler::OnDpiChanged",
[&](perfetto::EventContext ctx) {
perfetto::protos::pbzero::ChromeWindowHandleEventInfo* args =
ctx.event()->set_chrome_window_handle_event_info();
args->set_dpi(LOWORD(w_param));
});
int dpi;
float scaling_factor;
if (display::Display::HasForceDeviceScaleFactor()) {
scaling_factor = display::Display::GetForcedDeviceScaleFactor();
dpi = display::win::GetDPIFromScalingFactor(scaling_factor);
} else {
dpi = LOWORD(w_param);
scaling_factor = display::win::GetScreenWin()->GetScaleFactorForDPI(dpi);
}
if (dpi_ == 0) {
dpi_ = dpi;
return 0;
}
dpi_ = dpi;
display::win::GetScreenWin()->UpdateDisplayInfos();
SetBoundsInternal(gfx::Rect(*reinterpret_cast<RECT*>(l_param)), false);
delegate_->HandleWindowScaleFactorChanged(scaling_factor);
return 0;
}
void HWNDMessageHandler::OnEnterMenuLoop(BOOL from_track_popup_menu) {
if (menu_depth_++ == 0) {
delegate_->HandleMenuLoop(true);
}
}
void HWNDMessageHandler::OnEnterSizeMove() {
user_resize_detector_.OnEnterSizeMove();
delegate_->HandleBeginWMSizeMove();
SetMsgHandled(FALSE);
}
LRESULT HWNDMessageHandler::OnEraseBkgnd(HDC dc) {
gfx::Insets insets;
if (delegate_->GetDwmFrameInsetsInPixels(&insets) && !insets.IsEmpty() &&
needs_dwm_frame_clear_) {
needs_dwm_frame_clear_ = false;
RECT client_rect;
GetClientRect(hwnd(), &client_rect);
base::win::ScopedGDIObject<HBRUSH> brush(CreateSolidBrush(0));
RECT rect = {0, 0, client_rect.right, insets.top()};
FillRect(dc, &rect, brush.get());
}
return 1;
}
void HWNDMessageHandler::OnExitMenuLoop(BOOL is_shortcut_menu) {
if (--menu_depth_ == 0) {
delegate_->HandleMenuLoop(false);
}
DCHECK_GE(0, menu_depth_);
}
void HWNDMessageHandler::OnExitSizeMove() {
user_resize_detector_.OnExitSizeMove();
delegate_->HandleEndWMSizeMove();
SetMsgHandled(FALSE);
CheckAndHandleBackgroundFullscreenOnMonitor(hwnd());
}
void HWNDMessageHandler::OnGetMinMaxInfo(MINMAXINFO* minmax_info) {
gfx::Size min_window_size;
gfx::Size max_window_size;
delegate_->GetMinMaxSize(&min_window_size, &max_window_size);
min_window_size = delegate_->DIPToScreenSize(min_window_size);
max_window_size = delegate_->DIPToScreenSize(max_window_size);
if (delegate_->WidgetSizeIsClientSize()) {
RECT client_rect, window_rect;
GetClientRect(hwnd(), &client_rect);
GetWindowRect(hwnd(), &window_rect);
CR_DEFLATE_RECT(&window_rect, &client_rect);
min_window_size.Enlarge(window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top);
if (max_window_size.width()) {
max_window_size.Enlarge(window_rect.right - window_rect.left, 0);
}
if (max_window_size.height()) {
max_window_size.Enlarge(0, window_rect.bottom - window_rect.top);
}
}
minmax_info->ptMinTrackSize.x = min_window_size.width();
minmax_info->ptMinTrackSize.y = min_window_size.height();
if (max_window_size.width() || max_window_size.height()) {
if (!max_window_size.width()) {
max_window_size.set_width(GetSystemMetrics(SM_CXMAXTRACK));
}
if (!max_window_size.height()) {
max_window_size.set_height(GetSystemMetrics(SM_CYMAXTRACK));
}
minmax_info->ptMaxTrackSize.x = max_window_size.width();
minmax_info->ptMaxTrackSize.y = max_window_size.height();
}
SetMsgHandled(FALSE);
}
LRESULT HWNDMessageHandler::OnGetObject(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (!may_service_accessibility_requests_) {
return 0;
}
switch (static_cast<LONG>(l_param)) {
case UiaRootObjectId:
if (ui::AXPlatform::GetInstance().IsUiaProviderEnabled()) {
Microsoft::WRL::ComPtr<IRawElementProviderSimple> root;
ax_fragment_root_->GetNativeViewAccessible()->QueryInterface(
IID_PPV_ARGS(&root));
ui::AXPlatform::GetInstance().SetUiaClientServiced(true);
return ::UiaReturnRawElementProvider(hwnd(), w_param, l_param,
root.Get());
}
break;
case OBJID_CLIENT:
if (auto root_accessible = delegate_->GetNativeViewAccessible()) {
return ::LresultFromObject(IID_IAccessible, w_param, root_accessible);
}
break;
case OBJID_CARET:
if (ax_system_caret_ && ::GetFocus() == hwnd()) {
return ::LresultFromObject(IID_IAccessible, w_param,
ax_system_caret_->GetCaret());
}
break;
default:
break;
}
return 0;
}
LRESULT HWNDMessageHandler::OnImeMessages(UINT message,
WPARAM w_param,
LPARAM l_param) {
LRESULT result = 0;
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
const bool msg_handled =
delegate_->HandleIMEMessage(message, w_param, l_param, &result);
if (ref.get()) {
SetMsgHandled(msg_handled);
}
return result;
}
void HWNDMessageHandler::OnInitMenu(HMENU menu) {
bool is_fullscreen = IsFullscreen();
bool is_minimized = IsMinimized();
bool is_maximized = IsMaximized();
bool is_restored = !is_fullscreen && !is_minimized && !is_maximized;
ScopedRedrawLock lock(this);
EnableMenuItemByCommand(
menu, SC_RESTORE,
delegate_->CanResize() && (is_minimized || is_maximized));
EnableMenuItemByCommand(menu, SC_MOVE, is_restored);
EnableMenuItemByCommand(menu, SC_SIZE, delegate_->CanResize() && is_restored);
EnableMenuItemByCommand(
menu, SC_MAXIMIZE,
delegate_->CanMaximize() && !is_fullscreen && !is_maximized);
EnableMenuItemByCommand(menu, SC_MINIMIZE,
delegate_->CanMinimize() && !is_minimized);
if (is_maximized && delegate_->CanResize()) {
::SetMenuDefaultItem(menu, SC_RESTORE, FALSE);
} else if (!is_maximized && delegate_->CanMaximize()) {
::SetMenuDefaultItem(menu, SC_MAXIMIZE, FALSE);
}
}
void HWNDMessageHandler::OnInputLangChange(DWORD character_set,
HKL input_language_id) {
delegate_->HandleInputLanguageChange(character_set, input_language_id);
}
LRESULT HWNDMessageHandler::OnKeyEvent(UINT message,
WPARAM w_param,
LPARAM l_param) {
CHROME_MSG msg = {hwnd(), message, w_param, l_param,
static_cast<DWORD>(GetMessageTime())};
ui::KeyEvent key(msg);
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleKeyEvent(&key);
if (!ref) {
return 0;
}
if (!key.handled()) {
SetMsgHandled(FALSE);
}
return 0;
}
void HWNDMessageHandler::OnKillFocus(HWND focused_window) {
if (!IsHeadless()) {
delegate_->HandleNativeBlur(focused_window);
}
SetMsgHandled(FALSE);
}
LRESULT HWNDMessageHandler::OnMouseActivate(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (touch_down_contexts_) {
return MA_NOACTIVATE;
}
if (::GetProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow)) {
::RemoveProp(hwnd(), ui::kIgnoreTouchMouseActivateForWindow);
return MA_NOACTIVATE;
}
if (delegate_->HasNonClientView()) {
if (delegate_->CanActivate()) {
return MA_ACTIVATE;
}
return MA_NOACTIVATEANDEAT;
}
if (GetWindowLong(hwnd(), GWL_EXSTYLE) & WS_EX_NOACTIVATE) {
return MA_NOACTIVATE;
}
SetMsgHandled(FALSE);
return MA_ACTIVATE;
}
LRESULT HWNDMessageHandler::OnMouseRange(UINT message,
WPARAM w_param,
LPARAM l_param) {
return HandleMouseEventInternal(message, w_param, l_param, true);
}
LRESULT HWNDMessageHandler::OnPointerActivate(UINT message,
WPARAM w_param,
LPARAM l_param) {
POINTER_INPUT_TYPE pointer_type;
if (::GetPointerType(GET_POINTERID_WPARAM(w_param), &pointer_type) &&
pointer_type == PT_TOUCHPAD) {
return PA_NOACTIVATE;
}
SetMsgHandled(FALSE);
return -1;
}
LRESULT HWNDMessageHandler::OnPointerEvent(UINT message,
WPARAM w_param,
LPARAM l_param) {
POINTER_INPUT_TYPE pointer_type;
if (!::GetPointerType(GET_POINTERID_WPARAM(w_param), &pointer_type)) {
SetMsgHandled(FALSE);
return -1;
}
if (pointer_type == PT_PEN &&
(message == WM_NCPOINTERDOWN || message == WM_NCPOINTERUP ||
message == WM_NCPOINTERUPDATE)) {
pointer_type = PT_TOUCH;
}
switch (pointer_type) {
case PT_PEN:
return HandlePointerEventTypePenClient(message, w_param, l_param);
case PT_TOUCH:
return HandlePointerEventTypeTouchOrNonClient(message, w_param, l_param);
default:
break;
}
SetMsgHandled(FALSE);
return -1;
}
LRESULT HWNDMessageHandler::OnInputEvent(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (!using_wm_input_) {
return -1;
}
HRAWINPUT input_handle = reinterpret_cast<HRAWINPUT>(l_param);
UINT size = 0;
UINT result = ::GetRawInputData(input_handle, RID_INPUT, nullptr, &size,
sizeof(RAWINPUTHEADER));
if (result == static_cast<UINT>(-1)) {
PLOG(ERROR) << "GetRawInputData() failed";
return 0;
}
DCHECK_EQ(0u, result);
auto buffer = std::make_unique<uint8_t[]>(size);
RAWINPUT* input = reinterpret_cast<RAWINPUT*>(buffer.get());
result = ::GetRawInputData(input_handle, RID_INPUT, buffer.get(), &size,
sizeof(RAWINPUTHEADER));
if (result == static_cast<UINT>(-1)) {
PLOG(ERROR) << "GetRawInputData() failed";
return 0;
}
DCHECK_EQ(size, result);
if (input->header.dwType == RIM_TYPEMOUSE &&
input->data.mouse.usButtonFlags != RI_MOUSE_WHEEL) {
POINT cursor_pos = {0};
::GetCursorPos(&cursor_pos);
ScreenToClient(hwnd(), &cursor_pos);
ui::MouseEvent event(
ui::EventType::kMouseMoved, gfx::PointF(cursor_pos.x, cursor_pos.y),
gfx::PointF(cursor_pos.x, cursor_pos.y), ui::EventTimeForNow(),
GetFlagsFromRawInputMessage(input), 0);
if (!(input->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)) {
ui::MouseEvent::DispatcherApi(&event).set_movement(
gfx::Vector2dF(input->data.mouse.lLastX, input->data.mouse.lLastY));
}
delegate_->HandleMouseEvent(&event);
}
return ::DefRawInputProc(&input, 1, sizeof(RAWINPUTHEADER));
}
void HWNDMessageHandler::OnMove(const gfx::Point& point) {
delegate_->HandleMove();
SetMsgHandled(FALSE);
}
void HWNDMessageHandler::OnMoving(UINT param, const RECT* new_bounds) {
delegate_->HandleMove();
}
LRESULT HWNDMessageHandler::OnNCActivate(UINT message,
WPARAM w_param,
LPARAM l_param) {
BOOL active = static_cast<BOOL>(LOWORD(w_param));
const bool paint_as_active = delegate_->ShouldPaintAsActive();
if (!delegate_->HasNonClientView()) {
SetMsgHandled(FALSE);
return 0;
}
if (!delegate_->CanActivate()) {
return TRUE;
}
if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
RedrawWindow(hwnd(), nullptr, nullptr,
RDW_NOCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW);
EnumChildWindows(hwnd(), EnumChildWindowsForRedraw, NULL);
}
if (IsVisible()) {
delegate_->SchedulePaint();
}
if (!delegate_->HasFrame() ||
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
SetMsgHandled(TRUE);
return TRUE;
}
return DefWindowProcWithRedrawLock(WM_NCACTIVATE, paint_as_active || active,
0);
}
LRESULT HWNDMessageHandler::OnNCCalcSize(BOOL mode, LPARAM l_param) {
if (is_first_nccalc_) {
is_first_nccalc_ = false;
if (GetWindowLong(hwnd(), GWL_STYLE) & WS_CAPTION) {
SetMsgHandled(FALSE);
return 0;
}
}
RECT* client_rect =
mode ? &(reinterpret_cast<NCCALCSIZE_PARAMS*>(l_param)->rgrc[0])
: reinterpret_cast<RECT*>(l_param);
HMONITOR monitor = MonitorFromWindow(hwnd(), MONITOR_DEFAULTTONULL);
if (!monitor) {
monitor = MonitorFromRect(client_rect, MONITOR_DEFAULTTONULL);
if (!monitor) {
return 0;
}
}
gfx::Insets insets;
bool got_insets = GetClientAreaInsets(&insets, monitor);
if (!got_insets && !IsFullscreen() && !(mode && !delegate_->HasFrame())) {
SetMsgHandled(FALSE);
return 0;
}
client_rect->left += insets.left();
client_rect->top += insets.top();
client_rect->bottom -= insets.bottom();
client_rect->right -= insets.right();
if (IsMaximized()) {
const int autohide_edges = GetAppbarAutohideEdges(monitor);
if (autohide_edges & ViewsDelegate::EDGE_LEFT) {
client_rect->left += kAutoHideTaskbarThicknessPx;
}
if (autohide_edges & ViewsDelegate::EDGE_TOP) {
if (IsFrameSystemDrawn()) {
--client_rect->bottom;
} else {
client_rect->top += kAutoHideTaskbarThicknessPx;
}
}
if (autohide_edges & ViewsDelegate::EDGE_RIGHT) {
client_rect->right -= kAutoHideTaskbarThicknessPx;
}
if (autohide_edges & ViewsDelegate::EDGE_BOTTOM) {
client_rect->bottom -= kAutoHideTaskbarThicknessPx;
}
return 0;
}
if (insets.left() == 0 || insets.top() == 0) {
return 0;
}
return mode ? WVR_REDRAW : 0;
}
LRESULT HWNDMessageHandler::OnNCCreate(LPCREATESTRUCT lpCreateStruct) {
SetMsgHandled(FALSE);
if (delegate_->HasFrame()) {
using EnableNonClientDpiScalingPtr = decltype(::EnableNonClientDpiScaling)*;
static const auto enable_non_client_dpi_scaling_func =
reinterpret_cast<EnableNonClientDpiScalingPtr>(
base::win::GetUser32FunctionPointer("EnableNonClientDpiScaling"));
called_enable_non_client_dpi_scaling_ =
!!(enable_non_client_dpi_scaling_func &&
enable_non_client_dpi_scaling_func(hwnd()));
}
return FALSE;
}
LRESULT HWNDMessageHandler::OnNCHitTest(const gfx::Point& point) {
if (!delegate_->HasNonClientView()) {
SetMsgHandled(FALSE);
return 0;
}
POINT temp = {point.x(), point.y()};
MapWindowPoints(HWND_DESKTOP, hwnd(), &temp, 1);
int component = delegate_->GetNonClientComponent(gfx::Point(temp));
if (component == HTCLIENT) {
return component;
}
if (HasSystemFrame() &&
delegate_->GetFrameMode() != FrameMode::SYSTEM_DRAWN_NO_CONTROLS) {
LRESULT result;
if (DwmDefWindowProc(hwnd(), WM_NCHITTEST, 0,
MAKELPARAM(point.x(), point.y()), &result)) {
return result;
}
}
if (component != HTNOWHERE) {
return component;
}
LRESULT hit_test_code =
DefWindowProc(hwnd(), WM_NCHITTEST, 0, MAKELPARAM(point.x(), point.y()));
return hit_test_code;
}
void HWNDMessageHandler::OnNCPaint(HRGN rgn) {
RECT window_rect;
GetWindowRect(hwnd(), &window_rect);
RECT dirty_region;
if (!rgn || rgn == reinterpret_cast<HRGN>(1)) {
dirty_region.left = 0;
dirty_region.top = 0;
dirty_region.right = window_rect.right - window_rect.left;
dirty_region.bottom = window_rect.bottom - window_rect.top;
} else {
RECT rgn_bounding_box;
GetRgnBox(rgn, &rgn_bounding_box);
if (!IntersectRect(&dirty_region, &rgn_bounding_box, &window_rect)) {
SetMsgHandled(FALSE);
return;
}
OffsetRect(&dirty_region, static_cast<int>(-window_rect.left),
static_cast<int>(-window_rect.top));
}
if (!delegate_->HasNonClientView() || IsFrameSystemDrawn()) {
HDC dc = GetWindowDC(hwnd());
RECT client_rect;
::GetClientRect(hwnd(), &client_rect);
::MapWindowPoints(hwnd(), nullptr, reinterpret_cast<POINT*>(&client_rect),
2);
::OffsetRect(&client_rect, static_cast<int>(-window_rect.left),
static_cast<int>(-window_rect.top));
base::win::ScopedGDIObject<HRGN> base(
::CreateRectRgnIndirect(&dirty_region));
base::win::ScopedGDIObject<HRGN> client(
::CreateRectRgnIndirect(&client_rect));
base::win::ScopedGDIObject<HRGN> nonclient(::CreateRectRgn(0, 0, 0, 0));
::CombineRgn(nonclient.get(), base.get(), client.get(), RGN_DIFF);
::SelectClipRgn(dc, nonclient.get());
HBRUSH brush = CreateSolidBrush(0);
::FillRect(dc, &dirty_region, brush);
::DeleteObject(brush);
::ReleaseDC(hwnd(), dc);
SetMsgHandled(FALSE);
return;
}
gfx::Size root_view_size = delegate_->GetRootViewSize();
if (gfx::Size(window_rect.right - window_rect.left,
window_rect.bottom - window_rect.top) != root_view_size) {
return;
}
delegate_->HandlePaintAccelerated(gfx::Rect(dirty_region));
SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN);
}
LRESULT HWNDMessageHandler::OnNCUAHDrawCaption(UINT message,
WPARAM w_param,
LPARAM l_param) {
SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN);
return 0;
}
LRESULT HWNDMessageHandler::OnNCUAHDrawFrame(UINT message,
WPARAM w_param,
LPARAM l_param) {
SetMsgHandled(delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN);
return 0;
}
void HWNDMessageHandler::OnPaint(HDC dc) {
PAINTSTRUCT ps;
HDC display_dc = BeginPaint(hwnd(), &ps);
if (!display_dc) {
base::debug::CollectGDIUsageAndDie();
}
if (!IsRectEmpty(&ps.rcPaint)) {
HBRUSH brush = delegate_->GetBackgroundPaintBrush();
if (!brush || is_translucent_) {
brush = reinterpret_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
}
if (HasChildRenderingWindow()) {
FillRect(ps.hdc, &ps.rcPaint, brush);
} else if (exposed_pixels_ != gfx::Size()) {
RECT cr;
if (GetClientRect(hwnd(), &cr)) {
const gfx::Size client_area = gfx::Rect(cr).size();
if (exposed_pixels_.height() > 0) {
RECT rect = {0, client_area.height() - exposed_pixels_.height(),
client_area.width(), client_area.height()};
FillRect(ps.hdc, &rect, brush);
}
if (exposed_pixels_.width() > 0) {
RECT rect = {client_area.width() - exposed_pixels_.width(), 0,
client_area.width(),
client_area.height() - exposed_pixels_.height()};
FillRect(ps.hdc, &rect, brush);
}
}
}
delegate_->HandlePaintAccelerated(gfx::Rect(ps.rcPaint));
}
exposed_pixels_ = gfx::Size();
EndPaint(hwnd(), &ps);
}
LRESULT HWNDMessageHandler::OnReflectedMessage(UINT message,
WPARAM w_param,
LPARAM l_param) {
SetMsgHandled(FALSE);
return 0;
}
LRESULT HWNDMessageHandler::OnScrollMessage(UINT message,
WPARAM w_param,
LPARAM l_param) {
CHROME_MSG msg = {hwnd(), message, w_param, l_param,
static_cast<DWORD>(GetMessageTime())};
ui::ScrollEvent event(msg);
delegate_->HandleScrollEvent(&event);
return 0;
}
LRESULT HWNDMessageHandler::OnSetCursor(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (is_pen_active_in_client_area_) {
return 1;
}
wchar_t* cursor = IDC_ARROW;
switch (LOWORD(l_param)) {
case HTSIZE:
cursor = IDC_SIZENWSE;
break;
case HTLEFT:
case HTRIGHT:
cursor = IDC_SIZEWE;
break;
case HTTOP:
case HTBOTTOM:
cursor = IDC_SIZENS;
break;
case HTTOPLEFT:
case HTBOTTOMRIGHT:
cursor = IDC_SIZENWSE;
break;
case HTTOPRIGHT:
case HTBOTTOMLEFT:
cursor = IDC_SIZENESW;
break;
case HTCLIENT:
SetCursor(current_cursor_);
return 1;
case LOWORD(HTERROR): // Use HTERROR's LOWORD value for valid comparison.
SetMsgHandled(FALSE);
break;
default:
// Use the default value, IDC_ARROW.
break;
}
::SetCursor(LoadCursor(nullptr, cursor));
return 1;
}
void HWNDMessageHandler::OnSetFocus(HWND last_focused_window) {
// Headless windows are believed to always have focus, so avoid
// reporting native focus changes.
if (!IsHeadless()) {
delegate_->HandleNativeFocus(last_focused_window);
}
SetMsgHandled(FALSE);
}
LRESULT HWNDMessageHandler::OnSetIcon(UINT size_type, HICON new_icon) {
// Use a ScopedRedrawLock to avoid weird non-client painting.
return DefWindowProcWithRedrawLock(WM_SETICON, size_type,
reinterpret_cast<LPARAM>(new_icon));
}
LRESULT HWNDMessageHandler::OnSetText(const wchar_t* text) {
// Use a ScopedRedrawLock to avoid weird non-client painting.
return DefWindowProcWithRedrawLock(WM_SETTEXT, NULL,
reinterpret_cast<LPARAM>(text));
}
void HWNDMessageHandler::OnSettingChange(UINT flags, const wchar_t* section) {
if (!GetParent(hwnd()) && (flags == SPI_SETWORKAREA)) {
// Fire a dummy SetWindowPos() call, so we'll trip the code in
// OnWindowPosChanging() below that notices work area changes.
::SetWindowPos(hwnd(), nullptr, 0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW |
SWP_NOACTIVATE | SWP_NOOWNERZORDER);
SetMsgHandled(TRUE);
} else {
if (flags == SPI_SETWORKAREA) {
delegate_->HandleWorkAreaChanged();
}
SetMsgHandled(FALSE);
}
if (flags == SPI_SETWORKAREA) {
SendFrameChanged();
}
}
void HWNDMessageHandler::OnSize(UINT param, const gfx::Size& size) {
if (DidMinimizedChange(last_size_param_, param) && IsTopLevelWindow(hwnd())) {
delegate_->HandleWindowMinimizedOrRestored(param != SIZE_MINIMIZED);
}
last_size_param_ = param;
RedrawWindow(hwnd(), nullptr, nullptr, RDW_INVALIDATE | RDW_ALLCHILDREN);
ResetWindowRegion(false, true);
}
void HWNDMessageHandler::OnSizing(UINT param, RECT* rect) {
user_resize_detector_.OnSizing();
if (!aspect_ratio_.has_value()) {
return;
}
if (param < WMSZ_LEFT || param > WMSZ_BOTTOMRIGHT) {
return;
}
gfx::Rect window_rect(*rect);
SizeWindowToAspectRatio(param, &window_rect);
*rect = window_rect.ToRECT();
}
void HWNDMessageHandler::OnSysCommand(UINT notification_code,
const gfx::Point& point) {
static const int sc_mask = 0xFFF0;
if (IsFullscreen() && (((notification_code & sc_mask) == SC_SIZE) ||
((notification_code & sc_mask) == SC_MOVE) ||
((notification_code & sc_mask) == SC_MAXIMIZE))) {
return;
}
const bool window_control_action =
(notification_code & sc_mask) == SC_MINIMIZE ||
(notification_code & sc_mask) == SC_MAXIMIZE ||
(notification_code & sc_mask) == SC_RESTORE;
const bool custom_controls_frame_mode =
delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN_NO_CONTROLS ||
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN;
if (custom_controls_frame_mode && window_control_action) {
delegate_->ResetWindowControls();
}
if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
const bool window_bounds_change =
(notification_code & sc_mask) == SC_MOVE ||
(notification_code & sc_mask) == SC_SIZE;
if (window_bounds_change || window_control_action) {
DestroyAXSystemCaret();
}
if (window_bounds_change && !IsVisible()) {
SetWindowLong(hwnd(), GWL_STYLE,
GetWindowLong(hwnd(), GWL_STYLE) | WS_VISIBLE);
}
}
if ((notification_code & sc_mask) == SC_KEYMENU && point.x() == 0) {
int modifiers = ui::EF_NONE;
if (ui::win::IsShiftPressed()) {
modifiers |= ui::EF_SHIFT_DOWN;
}
if (ui::win::IsCtrlPressed()) {
modifiers |= ui::EF_CONTROL_DOWN;
}
ui::Accelerator accelerator(ui::KeyboardCodeForWindowsKeyCode(VK_MENU),
modifiers);
delegate_->HandleAccelerator(accelerator);
return;
}
if (delegate_->HandleCommand(static_cast<int>(notification_code))) {
return;
}
bool is_mouse_menu = (notification_code & sc_mask) == SC_MOUSEMENU;
if (is_mouse_menu) {
CHECK(!handling_mouse_menu_);
handling_mouse_menu_ = true;
}
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop allow;
DefWindowProc(hwnd(), WM_SYSCOMMAND, notification_code,
MAKELPARAM(point.x(), point.y()));
if (is_mouse_menu && ref) {
handling_mouse_menu_ = false;
}
}
void HWNDMessageHandler::OnThemeChanged() {
ui::NativeThemeWin::CloseHandles();
}
void HWNDMessageHandler::OnTimeChange() {
base::Time::NowFromSystemTime();
}
LRESULT HWNDMessageHandler::OnTouchEvent(UINT message,
WPARAM w_param,
LPARAM l_param) {
CloseTouchInputHandle(reinterpret_cast<HTOUCHINPUT>(l_param));
return 0;
}
void HWNDMessageHandler::OnWindowPosChanging(WINDOWPOS* window_pos) {
TRACE_EVENT0("ui", "HWNDMessageHandler::OnWindowPosChanging");
if (ignore_window_pos_changes_) {
if (!(window_pos->flags & ((IsVisible() ? SWP_HIDEWINDOW : SWP_SHOWWINDOW) |
SWP_FRAMECHANGED)) &&
(window_pos->flags & (SWP_NOZORDER | SWP_NOACTIVATE))) {
window_pos->flags |= SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW;
window_pos->flags &=
static_cast<unsigned int>(~(SWP_SHOWWINDOW | SWP_HIDEWINDOW));
}
} else if (!GetParent(hwnd())) {
RECT window_rect;
const bool have_new_window_rect =
!(window_pos->flags & SWP_NOMOVE) && !(window_pos->flags & SWP_NOSIZE);
if (have_new_window_rect) {
window_rect.left = window_pos->x;
window_rect.top = window_pos->y;
window_rect.right = window_pos->x + window_pos->cx;
window_rect.bottom = window_pos->y + window_pos->cy;
}
HMONITOR monitor;
gfx::Rect monitor_rect, work_area;
if ((have_new_window_rect || GetWindowRect(hwnd(), &window_rect)) &&
GetMonitorAndRects(window_rect, &monitor, &monitor_rect, &work_area)) {
bool work_area_changed = (monitor_rect == last_monitor_rect_) &&
(work_area != last_work_area_);
const bool same_monitor = monitor && (monitor == last_monitor_);
gfx::Rect expected_maximized_bounds = work_area;
if (IsMaximized()) {
gfx::Insets client_area_insets;
if (GetClientAreaInsets(&client_area_insets, monitor)) {
expected_maximized_bounds.Inset(
gfx::ScaleToCeiledInsets(client_area_insets, -1));
}
}
const bool incorrect_maximized_bounds =
IsMaximized() && have_new_window_rect &&
(expected_maximized_bounds.x() != window_pos->x ||
expected_maximized_bounds.y() != window_pos->y ||
expected_maximized_bounds.width() != window_pos->cx ||
expected_maximized_bounds.height() != window_pos->cy);
if (background_fullscreen_hack_ &&
(!(window_pos->flags & SWP_NOSIZE) &&
(monitor_rect.height() - window_pos->cy != 1))) {
background_fullscreen_hack_ = false;
}
const bool fullscreen_without_hack =
IsFullscreen() && !background_fullscreen_hack_;
if (same_monitor &&
(incorrect_maximized_bounds || fullscreen_without_hack ||
work_area_changed) &&
!IsWindowArranged(hwnd())) {
gfx::Rect new_window_rect;
if (IsFullscreen()) {
new_window_rect = monitor_rect;
} else if (IsMaximized()) {
new_window_rect = expected_maximized_bounds;
} else {
new_window_rect = gfx::Rect(window_rect);
new_window_rect.AdjustToFit(work_area);
}
window_pos->x = new_window_rect.x();
window_pos->y = new_window_rect.y();
window_pos->cx = new_window_rect.width();
window_pos->cy = new_window_rect.height();
window_pos->flags &= static_cast<unsigned int>(
~(SWP_NOSIZE | SWP_NOMOVE | SWP_NOREDRAW));
window_pos->flags |= SWP_NOCOPYBITS;
ignore_window_pos_changes_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::StopIgnoringPosChanges,
msg_handler_weak_factory_.GetWeakPtr()));
}
last_monitor_ = monitor;
last_monitor_rect_ = monitor_rect;
last_work_area_ = work_area;
}
}
RECT window_rect;
gfx::Size old_size;
if (GetWindowRect(hwnd(), &window_rect)) {
old_size = gfx::Rect(window_rect).size();
}
gfx::Size new_size = gfx::Size(window_pos->cx, window_pos->cy);
if ((old_size != new_size && !(window_pos->flags & SWP_NOSIZE)) ||
window_pos->flags & SWP_FRAMECHANGED) {
exposed_pixels_ = new_size - old_size;
delegate_->HandleWindowSizeChanging();
sent_window_size_changing_ = true;
PostMessage(hwnd(), WM_WINDOWSIZINGFINISHED, ++current_window_size_message_,
0);
if (is_translucent_) {
window_pos->flags |= SWP_NOCOPYBITS;
}
} else {
exposed_pixels_ = gfx::Size();
}
if (ScopedFullscreenVisibility::IsHiddenForFullscreen(hwnd())) {
window_pos->flags &= static_cast<unsigned int>(~SWP_SHOWWINDOW);
}
if (window_pos->flags & SWP_HIDEWINDOW) {
SetDwmFrameExtension(DwmFrameState::kOff);
}
SetMsgHandled(FALSE);
}
void HWNDMessageHandler::OnWindowPosChanged(WINDOWPOS* window_pos) {
TRACE_EVENT0("ui", "HWNDMessageHandler::OnWindowPosChanged");
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
if (DidClientAreaSizeChange(window_pos)) {
ClientAreaSizeChanged();
}
if (!ref) {
return;
}
if (window_pos->flags & SWP_FRAMECHANGED) {
SetDwmFrameExtension(DwmFrameState::kOn);
}
if (window_pos->flags & SWP_SHOWWINDOW) {
delegate_->HandleVisibilityChanged(true);
SetDwmFrameExtension(DwmFrameState::kOn);
} else if (window_pos->flags & SWP_HIDEWINDOW) {
delegate_->HandleVisibilityChanged(false);
}
UpdateDwmFrame();
SetMsgHandled(FALSE);
}
LRESULT HWNDMessageHandler::OnWindowSizingFinished(UINT message,
WPARAM w_param,
LPARAM l_param) {
if (current_window_size_message_ != w_param) {
return 0;
}
delegate_->HandleWindowSizeUnchanged();
sent_window_size_changing_ = false;
exposed_pixels_ = gfx::Size();
return 0;
}
void HWNDMessageHandler::OnSessionChange(WPARAM status_code,
const bool* is_current_session) {
if (status_code == WTS_SESSION_UNLOCK) {
ForceRedrawWindow(10);
}
}
void HWNDMessageHandler::HandleTouchEvents(const TouchEvents& touch_events) {
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
for (size_t i = 0; i < touch_events.size() && ref; ++i) {
delegate_->HandleTouchEvent(const_cast<ui::TouchEvent*>(&touch_events[i]));
}
}
void HWNDMessageHandler::ResetTouchDownContext() {
touch_down_contexts_--;
}
LRESULT HWNDMessageHandler::HandleMouseEventInternal(UINT message,
WPARAM w_param,
LPARAM l_param,
bool track_mouse) {
if (is_pen_active_in_client_area_) {
INPUT_MESSAGE_SOURCE input_message_source;
static const auto get_current_input_message_source =
reinterpret_cast<decltype(&::GetCurrentInputMessageSource)>(
base::win::GetUser32FunctionPointer(
"GetCurrentInputMessageSource"));
if (get_current_input_message_source &&
get_current_input_message_source(&input_message_source) &&
input_message_source.deviceType == IMDT_UNAVAILABLE) {
return 0;
}
is_pen_active_in_client_area_ = false;
}
if (message != WM_MOUSEWHEEL && message != WM_MOUSEHWHEEL &&
ui::IsMouseEventFromTouch(message)) {
LRESULT hittest = SendMessage(hwnd(), WM_NCHITTEST, 0, l_param);
if (hittest == HTCAPTION || hittest == HTSYSMENU) {
SetMsgHandled(FALSE);
}
if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN &&
(hittest == HTCLOSE || hittest == HTMINBUTTON ||
hittest == HTMAXBUTTON)) {
SetMsgHandled(FALSE);
}
if (!IsHitTestOnResizeHandle(hittest)) {
return 0;
}
}
if (message == WM_MOUSEHWHEEL) {
last_mouse_hwheel_time_ = ::GetMessageTime();
}
if (message == WM_MOUSEWHEEL &&
::GetMessageTime() == last_mouse_hwheel_time_) {
message = WM_MOUSEHWHEEL;
}
if (message == WM_RBUTTONUP && is_right_mouse_pressed_on_caption_) {
is_right_mouse_pressed_on_caption_ = false;
ReleaseCapture();
POINT screen_point = CR_POINT_INITIALIZER_FROM_LPARAM(l_param);
MapWindowPoints(hwnd(), HWND_DESKTOP, &screen_point, 1);
w_param = static_cast<WPARAM>(SendMessage(
hwnd(), WM_NCHITTEST, 0, MAKELPARAM(screen_point.x, screen_point.y)));
if (w_param == HTCAPTION || w_param == HTSYSMENU) {
ShowSystemMenuAtScreenPixelLocation(hwnd(), gfx::Point(screen_point));
return 0;
}
} else if (message == WM_NCLBUTTONDOWN &&
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
switch (w_param) {
case HTCLOSE:
case HTMINBUTTON:
case HTMAXBUTTON: {
w_param |= ui::win::IsCtrlPressed() ? MK_CONTROL : 0;
w_param |= ui::win::IsShiftPressed() ? MK_SHIFT : 0;
}
}
} else if (message == WM_NCRBUTTONDOWN &&
(w_param == HTCAPTION || w_param == HTSYSMENU)) {
is_right_mouse_pressed_on_caption_ = true;
SetCapture();
}
LONG message_time = GetMessageTime();
CHROME_MSG msg = {hwnd(),
message,
w_param,
l_param,
static_cast<DWORD>(message_time),
{CR_GET_X_LPARAM(l_param), CR_GET_Y_LPARAM(l_param)}};
ui::MouseEvent event(msg);
if (IsSynthesizedMouseMessage(message, message_time, l_param)) {
event.SetFlags(event.flags() | ui::EF_FROM_TOUCH);
}
if (event.type() == ui::EventType::kMouseMoved && !HasCapture() &&
track_mouse) {
TrackMouseEvents((message == WM_NCMOUSEMOVE) ? TME_NONCLIENT | TME_LEAVE
: TME_LEAVE);
} else if (event.type() == ui::EventType::kMouseExited) {
active_mouse_tracking_flags_ = 0;
} else if (event.type() == ui::EventType::kMousewheel) {
ui::MouseWheelEvent mouse_wheel_event(msg);
if (ui::RerouteMouseWheel(hwnd(), w_param, l_param) ||
delegate_->HandleMouseEvent(&mouse_wheel_event)) {
SetMsgHandled(TRUE);
return 0;
} else {
return 1;
}
}
if (using_wm_input_ && (event.type() == ui::EventType::kMouseMoved ||
event.type() == ui::EventType::kMouseDragged)) {
return 0;
}
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
bool handled = false;
if (event.type() == ui::EventType::kMouseDragged) {
constexpr int kMaxDragEventsToIgnore00Move = 6;
POINT point;
point.x = event.x();
point.y = event.y();
::ClientToScreen(hwnd(), &point);
num_drag_events_after_press_++;
if (point.x == 0 && point.y == 0 &&
num_drag_events_after_press_ <= kMaxDragEventsToIgnore00Move) {
POINT cursor_pos;
::GetCursorPos(&cursor_pos);
constexpr int kMinSpuriousDistance = 30;
auto distance = sqrt(pow(static_cast<float>(abs(cursor_pos.x)), 2) +
pow(static_cast<float>(abs(cursor_pos.y)), 2));
if (distance > kMinSpuriousDistance) {
SetMsgHandled(true);
return 0;
}
}
} else if (event.type() == ui::EventType::kMousePressed) {
num_drag_events_after_press_ = 0;
}
if (!handling_mouse_menu_ || message != WM_RBUTTONUP) {
handled = delegate_->HandleMouseEvent(&event);
}
if (!ref.get()) {
return 0;
}
if (!handled && message == WM_NCLBUTTONDOWN && w_param != HTSYSMENU &&
w_param != HTCAPTION &&
delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
DefWindowProcWithRedrawLock(message, w_param, l_param);
handled = true;
}
if (!handled) {
handled = HandleMouseInputForCaption(message, w_param, l_param);
}
if (ref.get()) {
SetMsgHandled(handled);
}
return 0;
}
LRESULT HWNDMessageHandler::HandlePointerEventTypeTouchOrNonClient(
UINT message,
WPARAM w_param,
LPARAM l_param) {
UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
using GetPointerTouchInfoFn = BOOL(WINAPI*)(UINT32, POINTER_TOUCH_INFO*);
POINTER_TOUCH_INFO pointer_touch_info;
static const auto get_pointer_touch_info =
reinterpret_cast<GetPointerTouchInfoFn>(
base::win::GetUser32FunctionPointer("GetPointerTouchInfo"));
if (!get_pointer_touch_info ||
!get_pointer_touch_info(pointer_id, &pointer_touch_info)) {
SetMsgHandled(FALSE);
return -1;
}
TRACE_EVENT1(
"ui", "HWNDMessageHandler::HandlePointerEventTypeTouchOrNonClient",
"POINTER_TOUCH_INFO",
base::StringPrintf(
"pointerId: %" PRIu32 "\npointerFlags: %" PRIu32
"\nptPixelLocationRaw: (%" PRIi64 ", %" PRIi64 ")\npressure: %" PRIu32
"\norientation: %" PRIu32 "\nradiusX: %" PRIi64 "\nradiusY: %" PRIi64,
pointer_touch_info.pointerInfo.pointerId,
pointer_touch_info.pointerInfo.pointerFlags,
pointer_touch_info.pointerInfo.ptPixelLocationRaw.x,
pointer_touch_info.pointerInfo.ptPixelLocationRaw.y,
pointer_touch_info.pressure, pointer_touch_info.orientation,
abs(pointer_touch_info.rcContactRaw.right -
pointer_touch_info.rcContactRaw.left) /
2,
abs(pointer_touch_info.rcContactRaw.bottom -
pointer_touch_info.rcContactRaw.top) /
2));
last_touch_or_pen_message_time_ = ::GetMessageTime();
if (message == WM_POINTERENTER || message == WM_POINTERLEAVE) {
SetMsgHandled(TRUE);
return 0;
}
if (message == WM_POINTERDOWN || message == WM_NCPOINTERDOWN) {
touch_down_contexts_++;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&HWNDMessageHandler::ResetTouchDownContext,
msg_handler_weak_factory_.GetWeakPtr()),
kTouchDownContextResetTimeout);
}
POINTER_INFO pointer_info = pointer_touch_info.pointerInfo;
POINTER_FLAGS pointer_flags = pointer_info.pointerFlags;
if ((message == WM_POINTERUPDATE) &&
!(pointer_flags & POINTER_FLAG_INCONTACT)) {
SetMsgHandled(TRUE);
return 0;
}
POINT client_point = pointer_info.ptPixelLocationRaw;
ScreenToClient(hwnd(), &client_point);
gfx::Point touch_point = gfx::Point(client_point.x, client_point.y);
ui::EventType event_type = GetTouchEventType(pointer_flags);
const base::TimeTicks event_time = ui::EventTimeForNow();
auto mapped_pointer_id =
static_cast<ui::PointerId>(id_generator_.GetGeneratedID(pointer_id));
float pressure = static_cast<float>(pointer_touch_info.pressure) / 1024;
float radius_x =
(pointer_touch_info.rcContact.right - pointer_touch_info.rcContact.left) /
2.0;
float radius_y =
(pointer_touch_info.rcContact.bottom - pointer_touch_info.rcContact.top) /
2.0;
auto rotation_angle = static_cast<int>(pointer_touch_info.orientation);
rotation_angle %= 180;
if (rotation_angle < 0) {
rotation_angle += 180;
}
ui::TouchEvent event(
event_type, touch_point, event_time,
ui::PointerDetails(ui::EventPointerType::kTouch, mapped_pointer_id,
radius_x, radius_y, pressure, rotation_angle),
ui::GetModifiersFromKeyState());
ui::ComputeEventLatencyOSFromPOINTER_INFO(event_type, pointer_info,
event_time);
event.latency()->AddLatencyNumberWithTimestamp(
ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time);
if (event_type == ui::EventType::kTouchReleased) {
id_generator_.ReleaseNumber(pointer_id);
}
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
delegate_->HandleTouchEvent(&event);
if (ref) {
if (delegate_->GetFrameMode() == FrameMode::SYSTEM_DRAWN) {
if (message == WM_POINTERUP) {
event.SetHandled();
}
} else {
const bool on_titlebar =
SendMessage(hwnd(), WM_NCHITTEST, 0, l_param) == HTCAPTION;
if (event_type == ui::EventType::kTouchReleased && !on_titlebar) {
event.SetHandled();
}
}
SetMsgHandled(event.handled());
}
return 0;
}
LRESULT HWNDMessageHandler::HandlePointerEventTypePen(
UINT message,
UINT32 pointer_id,
POINTER_PEN_INFO pointer_pen_info) {
POINT client_point = pointer_pen_info.pointerInfo.ptPixelLocationRaw;
ScreenToClient(hwnd(), &client_point);
gfx::Point point = gfx::Point(client_point.x, client_point.y);
std::unique_ptr<ui::Event> event = pen_processor_.GenerateEvent(
message, pointer_id, pointer_pen_info, point);
base::WeakPtr<HWNDMessageHandler> ref(msg_handler_weak_factory_.GetWeakPtr());
if (event) {
if (event->IsTouchEvent()) {
delegate_->HandleTouchEvent(event->AsTouchEvent());
} else {
CHECK(event->IsMouseEvent());
delegate_->HandleMouseEvent(event->AsMouseEvent());
}
last_touch_or_pen_message_time_ = ::GetMessageTime();
is_pen_active_in_client_area_ = true;
}
if (ref) {
SetMsgHandled(handle_pen_events_in_client_area_);
}
if (message == WM_POINTERUP) {
handle_pen_events_in_client_area_ = true;
}
return 0;
}
LRESULT HWNDMessageHandler::HandlePointerEventTypePenClient(UINT message,
WPARAM w_param,
LPARAM l_param) {
UINT32 pointer_id = GET_POINTERID_WPARAM(w_param);
POINTER_PEN_INFO pointer_pen_info;
if (!GetPointerPenInfo(pointer_id, &pointer_pen_info)) {
SetMsgHandled(FALSE);
return -1;
}
TRACE_EVENT1(
"ui", "HWNDMessageHandler::HandlePointerEventTypePenClient",
"POINTER_PEN_INFO",
base::StringPrintf("pointerId: %" PRIu32 "\npointerFlags: %" PRIu32
"\nptPixelLocationRaw: (%" PRIi64 ", %" PRIi64
")\npressure: %" PRIu32 "\nrotation: %" PRIu32
"\ntiltX: %" PRIi64 "\ntiltY: %" PRIi64,
pointer_pen_info.pointerInfo.pointerId,
pointer_pen_info.pointerInfo.pointerFlags,
pointer_pen_info.pointerInfo.ptPixelLocationRaw.x,
pointer_pen_info.pointerInfo.ptPixelLocationRaw.y,
pointer_pen_info.pressure, pointer_pen_info.rotation,
pointer_pen_info.tiltX, pointer_pen_info.tiltY));
return HandlePointerEventTypePen(message, pointer_id, pointer_pen_info);
}
bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message,
int message_time,
LPARAM l_param) {
if (ui::IsMouseEventFromTouch(message)) {
return true;
}
if (last_touch_or_pen_message_time_ &&
message_time >= last_touch_or_pen_message_time_ &&
((message_time - last_touch_or_pen_message_time_) <=
kSynthesizedMouseMessagesTimeDifference)) {
POINT mouse_location = CR_POINT_INITIALIZER_FROM_LPARAM(l_param);
::ClientToScreen(hwnd(), &mouse_location);
POINT cursor_pos = {0};
::GetCursorPos(&cursor_pos);
return UNSAFE_TODO(memcmp(&cursor_pos, &mouse_location, sizeof(POINT))) ==
0;
}
return false;
}
void HWNDMessageHandler::PerformDwmTransition() {
CHECK(IsFrameSystemDrawn());
dwm_transition_desired_ = false;
delegate_->HandleFrameChanged();
SendFrameChanged();
}
void HWNDMessageHandler::UpdateDwmFrame() {
TRACE_EVENT0("ui", "HWNDMessageHandler::UpdateDwmFrame");
gfx::Insets insets;
if (delegate_->GetDwmFrameInsetsInPixels(&insets)) {
MARGINS margins = {insets.left(), insets.right(), insets.top(),
insets.bottom()};
DwmExtendFrameIntoClientArea(hwnd(), &margins);
}
}
bool HWNDMessageHandler::HandleMouseInputForCaption(unsigned int message,
WPARAM w_param,
LPARAM l_param) {
bool handled = false;
switch (message) {
case WM_NCLBUTTONDOWN: {
if (w_param == HTCAPTION) {
left_button_down_on_caption_ = true;
caption_left_button_click_pos_.set_x(CR_GET_X_LPARAM(l_param));
caption_left_button_click_pos_.set_y(CR_GET_Y_LPARAM(l_param));
handled = true;
}
break;
}
case WM_MOUSEMOVE:
case WM_NCMOUSEMOVE: {
if (!left_button_down_on_caption_) {
break;
}
bool should_handle_pending_ncl_button_down = true;
if (message == WM_NCMOUSEMOVE) {
if (caption_left_button_click_pos_.x() == CR_GET_X_LPARAM(l_param) &&
caption_left_button_click_pos_.y() == CR_GET_Y_LPARAM(l_param)) {
should_handle_pending_ncl_button_down = false;
}
}
if (should_handle_pending_ncl_button_down) {
l_param = MAKELPARAM(caption_left_button_click_pos_.x(),
caption_left_button_click_pos_.y());
left_button_down_on_caption_ = false;
if (delegate_->GetFrameMode() == FrameMode::CUSTOM_DRAWN) {
DefWindowProcWithRedrawLock(WM_NCLBUTTONDOWN, HTCAPTION, l_param);
} else {
DefWindowProc(hwnd(), WM_NCLBUTTONDOWN, HTCAPTION, l_param);
}
}
break;
}
case WM_NCMOUSELEAVE: {
if (HasSystemFrame()) {
handled = DwmDefWindowProc(hwnd(), WM_NCMOUSELEAVE, 0, 0, nullptr) != 0;
}
break;
}
default:
left_button_down_on_caption_ = false;
break;
}
return handled;
}
void HWNDMessageHandler::SetBoundsInternal(const gfx::Rect& bounds_in_pixels,
bool force_size_changed) {
gfx::Size old_size = GetClientAreaBounds().size();
SetWindowPos(hwnd(), nullptr, bounds_in_pixels.x(), bounds_in_pixels.y(),
bounds_in_pixels.width(), bounds_in_pixels.height(),
SWP_NOACTIVATE | SWP_NOZORDER);
if (old_size == bounds_in_pixels.size() && force_size_changed &&
!background_fullscreen_hack_) {
auto ref = msg_handler_weak_factory_.GetWeakPtr();
delegate_->HandleClientSizeChanged(GetClientAreaBounds().size());
if (!ref) {
return;
}
ResetWindowRegion(false, true);
}
}
void HWNDMessageHandler::CheckAndHandleBackgroundFullscreenOnMonitor(
HWND window) {
HMONITOR monitor = MonitorFromWindow(window, MONITOR_DEFAULTTOPRIMARY);
FullscreenWindowMonitorMap::iterator iter =
fullscreen_monitor_map_.Get().find(monitor);
if (iter != fullscreen_monitor_map_.Get().end()) {
DCHECK(iter->second);
if (window != iter->second->hwnd()) {
iter->second->OnBackgroundFullscreen();
}
}
}
void HWNDMessageHandler::OnBackgroundFullscreen() {
DCHECK(IsFullscreen());
MONITORINFO monitor_info = {sizeof(monitor_info)};
GetMonitorInfo(MonitorFromWindow(hwnd(), MONITOR_DEFAULTTOPRIMARY),
&monitor_info);
gfx::Rect shrunk_rect(monitor_info.rcMonitor);
shrunk_rect.set_height(shrunk_rect.height() - 1);
background_fullscreen_hack_ = true;
SetBoundsInternal(shrunk_rect, false);
fullscreen_handler()->MarkFullscreen(false);
}
void HWNDMessageHandler::DestroyAXSystemCaret() {
ax_system_caret_ = nullptr;
}
void HWNDMessageHandler::SizeWindowToAspectRatio(UINT param,
gfx::Rect* window_rect) {
gfx::Size min_window_size;
gfx::Size max_window_size;
delegate_->GetMinMaxSize(&min_window_size, &max_window_size);
min_window_size = delegate_->DIPToScreenSize(min_window_size);
max_window_size = delegate_->DIPToScreenSize(max_window_size);
std::optional<gfx::Size> max_size_param;
if (!max_window_size.IsEmpty()) {
max_size_param = max_window_size;
}
gfx::Size excluded_margin = delegate_->DIPToScreenSize(excluded_margin_dip_);
gfx::SizeRectToAspectRatioWithExcludedMargin(
GetWindowResizeEdge(param), aspect_ratio_.value(), min_window_size,
max_size_param, excluded_margin, *window_rect);
}
POINT HWNDMessageHandler::GetCursorPos() const {
if (mock_cursor_position_.has_value()) {
return mock_cursor_position_.value().ToPOINT();
}
POINT cursor_pos = {};
::GetCursorPos(&cursor_pos);
return cursor_pos;
}
bool HWNDMessageHandler::IsTopLevelWindow(HWND window) {
LONG style = ::GetWindowLong(window, GWL_STYLE);
if (!(style & WS_CHILD)) {
return true;
}
HWND parent = ::GetParent(window);
return !parent || (parent == ::GetDesktopWindow());
}
bool HWNDMessageHandler::GetMonitorAndRects(const RECT& rect,
HMONITOR* monitor,
gfx::Rect* monitor_rect,
gfx::Rect* work_area) {
DCHECK(monitor);
DCHECK(monitor_rect);
DCHECK(work_area);
*monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
if (!*monitor) {
return false;
}
MONITORINFO monitor_info = {0};
monitor_info.cbSize = sizeof(monitor_info);
GetMonitorInfo(*monitor, &monitor_info);
*monitor_rect = gfx::Rect(monitor_info.rcMonitor);
*work_area = gfx::Rect(monitor_info.rcWork);
return true;
}
void UseDefaultHandlerForPenEventsUntilPenUp() {
HWNDMessageHandler::UseDefaultHandlerForPenEventsUntilPenUp();
}
}