Bug 1692971 [wpt PR 27638] - WebKit export of https://bugs.webkit.org/show_bug.cgi...
[gecko.git] / widget / windows / TaskbarTabPreview.cpp
bloba19a6bf12e1d9d240a196988448e875a8737e4a8
1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "TaskbarTabPreview.h"
9 #include "nsWindowGfx.h"
10 #include "nsUXThemeData.h"
11 #include "WinUtils.h"
12 #include <nsITaskbarPreviewController.h>
14 #define TASKBARPREVIEW_HWNDID L"TaskbarTabPreviewHwnd"
16 namespace mozilla {
17 namespace widget {
19 NS_IMPL_ISUPPORTS(TaskbarTabPreview, nsITaskbarTabPreview)
21 const wchar_t* const kWindowClass = L"MozillaTaskbarPreviewClass";
23 TaskbarTabPreview::TaskbarTabPreview(ITaskbarList4* aTaskbar,
24 nsITaskbarPreviewController* aController,
25 HWND aHWND, nsIDocShell* aShell)
26 : TaskbarPreview(aTaskbar, aController, aHWND, aShell),
27 mProxyWindow(nullptr),
28 mIcon(nullptr),
29 mRegistered(false) {}
31 TaskbarTabPreview::~TaskbarTabPreview() {
32 if (mIcon) {
33 ::DestroyIcon(mIcon);
34 mIcon = nullptr;
37 // We need to ensure that proxy window disappears or else Bad Things happen.
38 if (mProxyWindow) Disable();
40 NS_ASSERTION(!mProxyWindow, "Taskbar proxy window was not destroyed!");
42 if (IsWindowAvailable()) {
43 DetachFromNSWindow();
44 } else {
45 mWnd = nullptr;
49 nsresult TaskbarTabPreview::Init() {
50 nsresult rv = TaskbarPreview::Init();
51 if (NS_FAILED(rv)) {
52 return rv;
55 WindowHook* hook = GetWindowHook();
56 if (!hook) {
57 return NS_ERROR_NOT_AVAILABLE;
60 return hook->AddMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
63 nsresult TaskbarTabPreview::ShowActive(bool active) {
64 NS_ASSERTION(mVisible && CanMakeTaskbarCalls(),
65 "ShowActive called on invisible window or before taskbar calls "
66 "can be made for this window");
67 return FAILED(
68 mTaskbar->SetTabActive(active ? mProxyWindow : nullptr, mWnd, 0))
69 ? NS_ERROR_FAILURE
70 : NS_OK;
73 HWND& TaskbarTabPreview::PreviewWindow() { return mProxyWindow; }
75 nativeWindow TaskbarTabPreview::GetHWND() { return mProxyWindow; }
77 void TaskbarTabPreview::EnsureRegistration() {
78 NS_ASSERTION(mVisible && CanMakeTaskbarCalls(),
79 "EnsureRegistration called when it is not safe to do so");
81 (void)UpdateTaskbarProperties();
84 NS_IMETHODIMP
85 TaskbarTabPreview::GetTitle(nsAString& aTitle) {
86 aTitle = mTitle;
87 return NS_OK;
90 NS_IMETHODIMP
91 TaskbarTabPreview::SetTitle(const nsAString& aTitle) {
92 mTitle = aTitle;
93 return mVisible ? UpdateTitle() : NS_OK;
96 NS_IMETHODIMP
97 TaskbarTabPreview::SetIcon(imgIContainer* icon) {
98 HICON hIcon = nullptr;
99 if (icon) {
100 nsresult rv;
101 rv = nsWindowGfx::CreateIcon(
102 icon, false, LayoutDeviceIntPoint(),
103 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
104 NS_ENSURE_SUCCESS(rv, rv);
107 if (mIcon) ::DestroyIcon(mIcon);
108 mIcon = hIcon;
109 mIconImage = icon;
110 return mVisible ? UpdateIcon() : NS_OK;
113 NS_IMETHODIMP
114 TaskbarTabPreview::GetIcon(imgIContainer** icon) {
115 NS_IF_ADDREF(*icon = mIconImage);
116 return NS_OK;
119 NS_IMETHODIMP
120 TaskbarTabPreview::Move(nsITaskbarTabPreview* aNext) {
121 if (aNext == this) return NS_ERROR_INVALID_ARG;
122 mNext = aNext;
123 return CanMakeTaskbarCalls() ? UpdateNext() : NS_OK;
126 nsresult TaskbarTabPreview::UpdateTaskbarProperties() {
127 if (mRegistered) return NS_OK;
129 if (FAILED(mTaskbar->RegisterTab(mProxyWindow, mWnd)))
130 return NS_ERROR_FAILURE;
132 nsresult rv = UpdateNext();
133 NS_ENSURE_SUCCESS(rv, rv);
134 rv = TaskbarPreview::UpdateTaskbarProperties();
135 mRegistered = true;
136 return rv;
139 LRESULT
140 TaskbarTabPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
141 RefPtr<TaskbarTabPreview> kungFuDeathGrip(this);
142 switch (nMsg) {
143 case WM_CREATE:
144 TaskbarPreview::EnableCustomDrawing(mProxyWindow, true);
145 return 0;
146 case WM_CLOSE:
147 mController->OnClose();
148 return 0;
149 case WM_ACTIVATE:
150 if (LOWORD(wParam) == WA_ACTIVE) {
151 // Activate the tab the user selected then restore the main window,
152 // keeping normal/max window state intact.
153 bool activateWindow;
154 nsresult rv = mController->OnActivate(&activateWindow);
155 if (NS_SUCCEEDED(rv) && activateWindow) {
156 nsWindow* win = WinUtils::GetNSWindowPtr(mWnd);
157 if (win) {
158 nsWindow* parent = win->GetTopLevelWindow(true);
159 if (parent) {
160 parent->Show(true);
165 return 0;
166 case WM_GETICON:
167 return (LRESULT)mIcon;
168 case WM_SYSCOMMAND:
169 // Send activation events to the top level window and select the proper
170 // tab through the controller.
171 if (wParam == SC_RESTORE || wParam == SC_MAXIMIZE) {
172 bool activateWindow;
173 nsresult rv = mController->OnActivate(&activateWindow);
174 if (NS_SUCCEEDED(rv) && activateWindow) {
175 // Note, restoring an iconic, maximized window here will only
176 // activate the maximized window. This is not a bug, it's default
177 // windows behavior.
178 ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
180 return 0;
182 // Forward everything else to the top level window. Do not forward
183 // close since that's intended for the tab. When the preview proxy
184 // closes, we'll close the tab above.
185 return wParam == SC_CLOSE
186 ? ::DefWindowProcW(mProxyWindow, WM_SYSCOMMAND, wParam, lParam)
187 : ::SendMessageW(mWnd, WM_SYSCOMMAND, wParam, lParam);
188 return 0;
190 return TaskbarPreview::WndProc(nMsg, wParam, lParam);
193 /* static */
194 LRESULT CALLBACK TaskbarTabPreview::GlobalWndProc(HWND hWnd, UINT nMsg,
195 WPARAM wParam,
196 LPARAM lParam) {
197 TaskbarTabPreview* preview(nullptr);
198 if (nMsg == WM_CREATE) {
199 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lParam);
200 preview = reinterpret_cast<TaskbarTabPreview*>(cs->lpCreateParams);
201 if (!::SetPropW(hWnd, TASKBARPREVIEW_HWNDID, preview))
202 NS_ERROR("Could not associate native window with tab preview");
203 preview->mProxyWindow = hWnd;
204 } else {
205 preview = reinterpret_cast<TaskbarTabPreview*>(
206 ::GetPropW(hWnd, TASKBARPREVIEW_HWNDID));
207 if (nMsg == WM_DESTROY) ::RemovePropW(hWnd, TASKBARPREVIEW_HWNDID);
210 if (preview) return preview->WndProc(nMsg, wParam, lParam);
211 return ::DefWindowProcW(hWnd, nMsg, wParam, lParam);
214 nsresult TaskbarTabPreview::Enable() {
215 WNDCLASSW wc;
216 HINSTANCE module = GetModuleHandle(nullptr);
218 if (!GetClassInfoW(module, kWindowClass, &wc)) {
219 wc.style = 0;
220 wc.lpfnWndProc = GlobalWndProc;
221 wc.cbClsExtra = 0;
222 wc.cbWndExtra = 0;
223 wc.hInstance = module;
224 wc.hIcon = nullptr;
225 wc.hCursor = nullptr;
226 wc.hbrBackground = (HBRUSH) nullptr;
227 wc.lpszMenuName = (LPCWSTR) nullptr;
228 wc.lpszClassName = kWindowClass;
229 RegisterClassW(&wc);
231 ::CreateWindowW(kWindowClass, L"TaskbarPreviewWindow",
232 WS_CAPTION | WS_SYSMENU, 0, 0, 200, 60, nullptr, nullptr,
233 module, this);
234 // GlobalWndProc will set mProxyWindow so that WM_CREATE can have a valid HWND
235 if (!mProxyWindow) return NS_ERROR_INVALID_ARG;
237 UpdateProxyWindowStyle();
239 nsresult rv = TaskbarPreview::Enable();
240 nsresult rvUpdate;
241 rvUpdate = UpdateTitle();
242 if (NS_FAILED(rvUpdate)) rv = rvUpdate;
244 rvUpdate = UpdateIcon();
245 if (NS_FAILED(rvUpdate)) rv = rvUpdate;
247 return rv;
250 nsresult TaskbarTabPreview::Disable() {
251 // TaskbarPreview::Disable assumes that mWnd is valid but this method can be
252 // called when it is null iff the nsWindow has already been destroyed and we
253 // are still visible for some reason during object destruction.
254 if (mWnd) TaskbarPreview::Disable();
256 if (FAILED(mTaskbar->UnregisterTab(mProxyWindow))) return NS_ERROR_FAILURE;
257 mRegistered = false;
259 // TaskbarPreview::WndProc will set mProxyWindow to null
260 if (!DestroyWindow(mProxyWindow)) return NS_ERROR_FAILURE;
261 mProxyWindow = nullptr;
262 return NS_OK;
265 void TaskbarTabPreview::DetachFromNSWindow() {
266 (void)SetVisible(false);
267 if (WindowHook* hook = GetWindowHook()) {
268 hook->RemoveMonitor(WM_WINDOWPOSCHANGED, MainWindowHook, this);
270 TaskbarPreview::DetachFromNSWindow();
273 /* static */
274 bool TaskbarTabPreview::MainWindowHook(void* aContext, HWND hWnd, UINT nMsg,
275 WPARAM wParam, LPARAM lParam,
276 LRESULT* aResult) {
277 if (nMsg == WM_WINDOWPOSCHANGED) {
278 TaskbarTabPreview* preview = reinterpret_cast<TaskbarTabPreview*>(aContext);
279 WINDOWPOS* pos = reinterpret_cast<WINDOWPOS*>(lParam);
280 if (SWP_FRAMECHANGED == (pos->flags & SWP_FRAMECHANGED))
281 preview->UpdateProxyWindowStyle();
282 } else {
283 MOZ_ASSERT_UNREACHABLE(
284 "Style changed hook fired on non-style changed "
285 "message");
287 return false;
290 void TaskbarTabPreview::UpdateProxyWindowStyle() {
291 if (!mProxyWindow) return;
293 DWORD minMaxMask = WS_MINIMIZE | WS_MAXIMIZE;
294 DWORD windowStyle = GetWindowLongW(mWnd, GWL_STYLE);
296 DWORD proxyStyle = GetWindowLongW(mProxyWindow, GWL_STYLE);
297 proxyStyle &= ~minMaxMask;
298 proxyStyle |= windowStyle & minMaxMask;
299 SetWindowLongW(mProxyWindow, GWL_STYLE, proxyStyle);
301 DWORD exStyle =
302 (WS_MAXIMIZE == (windowStyle & WS_MAXIMIZE)) ? WS_EX_TOOLWINDOW : 0;
303 SetWindowLongW(mProxyWindow, GWL_EXSTYLE, exStyle);
306 nsresult TaskbarTabPreview::UpdateTitle() {
307 NS_ASSERTION(mVisible, "UpdateTitle called on invisible preview");
309 if (!::SetWindowTextW(mProxyWindow, mTitle.get())) return NS_ERROR_FAILURE;
310 return NS_OK;
313 nsresult TaskbarTabPreview::UpdateIcon() {
314 NS_ASSERTION(mVisible, "UpdateIcon called on invisible preview");
316 ::SendMessageW(mProxyWindow, WM_SETICON, ICON_SMALL, (LPARAM)mIcon);
318 return NS_OK;
321 nsresult TaskbarTabPreview::UpdateNext() {
322 NS_ASSERTION(CanMakeTaskbarCalls() && mVisible,
323 "UpdateNext called on invisible tab preview");
324 HWND hNext = nullptr;
325 if (mNext) {
326 bool visible;
327 nsresult rv = mNext->GetVisible(&visible);
329 NS_ENSURE_SUCCESS(rv, rv);
331 // Can only move next to enabled previews
332 if (!visible) return NS_ERROR_FAILURE;
334 hNext = (HWND)mNext->GetHWND();
336 // hNext must be registered with the taskbar if the call is to succeed
337 mNext->EnsureRegistration();
339 if (FAILED(mTaskbar->SetTabOrder(mProxyWindow, hNext)))
340 return NS_ERROR_FAILURE;
341 return NS_OK;
344 } // namespace widget
345 } // namespace mozilla