Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / windows / TaskbarWindowPreview.cpp
blob212278be4a6935d239ce34919c4ef5c0355d2bcc
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 "mozilla/ArrayUtils.h"
10 #include <nsITaskbarPreviewController.h>
11 #include "TaskbarWindowPreview.h"
12 #include "WindowHook.h"
13 #include "nsUXThemeData.h"
14 #include "TaskbarPreviewButton.h"
15 #include "nsWindow.h"
16 #include "nsWindowGfx.h"
18 namespace mozilla {
19 namespace widget {
21 namespace {
22 bool WindowHookProc(void* aContext, HWND hWnd, UINT nMsg, WPARAM wParam,
23 LPARAM lParam, LRESULT* aResult) {
24 TaskbarWindowPreview* preview =
25 reinterpret_cast<TaskbarWindowPreview*>(aContext);
26 *aResult = preview->WndProc(nMsg, wParam, lParam);
27 return true;
29 } // namespace
31 NS_IMPL_ISUPPORTS(TaskbarWindowPreview, nsITaskbarWindowPreview,
32 nsITaskbarProgress, nsITaskbarOverlayIconController,
33 nsISupportsWeakReference)
35 /**
36 * These correspond directly to the states defined in nsITaskbarProgress.idl, so
37 * they should be kept in sync.
39 static TBPFLAG sNativeStates[] = {TBPF_NOPROGRESS, TBPF_INDETERMINATE,
40 TBPF_NORMAL, TBPF_ERROR, TBPF_PAUSED};
42 TaskbarWindowPreview::TaskbarWindowPreview(
43 ITaskbarList4* aTaskbar, nsITaskbarPreviewController* aController,
44 HWND aHWND, nsIDocShell* aShell)
45 : TaskbarPreview(aTaskbar, aController, aHWND, aShell),
46 mCustomDrawing(false),
47 mHaveButtons(false),
48 mState(TBPF_NOPROGRESS),
49 mCurrentValue(0),
50 mMaxValue(0),
51 mOverlayIcon(nullptr) {
52 // Window previews are visible by default
53 (void)SetVisible(true);
55 memset(mThumbButtons, 0, sizeof mThumbButtons);
56 for (int32_t i = 0; i < nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS; i++) {
57 mThumbButtons[i].dwMask = THB_FLAGS | THB_ICON | THB_TOOLTIP;
58 mThumbButtons[i].iId = i;
59 mThumbButtons[i].dwFlags = THBF_HIDDEN;
63 TaskbarWindowPreview::~TaskbarWindowPreview() {
64 if (mOverlayIcon) {
65 ::DestroyIcon(mOverlayIcon);
66 mOverlayIcon = nullptr;
69 // We need to clean up a hook associated with the "this" pointer.
70 SetVisible(false);
72 if (IsWindowAvailable()) {
73 DetachFromNSWindow();
74 } else {
75 mWnd = nullptr;
79 nsresult TaskbarWindowPreview::Init() {
80 nsresult rv = TaskbarPreview::Init();
81 if (NS_FAILED(rv)) {
82 return rv;
85 if (CanMakeTaskbarCalls()) {
86 return NS_OK;
89 WindowHook* hook = GetWindowHook();
90 if (!hook) {
91 return NS_ERROR_NOT_AVAILABLE;
94 return hook->AddMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
95 TaskbarWindowHook, this);
98 nsresult TaskbarWindowPreview::ShowActive(bool active) {
99 return FAILED(mTaskbar->ActivateTab(active ? mWnd : nullptr))
100 ? NS_ERROR_FAILURE
101 : NS_OK;
104 HWND& TaskbarWindowPreview::PreviewWindow() { return mWnd; }
106 nsresult TaskbarWindowPreview::GetButton(uint32_t index,
107 nsITaskbarPreviewButton** _retVal) {
108 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
109 return NS_ERROR_INVALID_ARG;
111 nsCOMPtr<nsITaskbarPreviewButton> button(
112 do_QueryReferent(mWeakButtons[index]));
114 if (!button) {
115 // Lost reference
116 button = new TaskbarPreviewButton(this, index);
117 if (!button) {
118 return NS_ERROR_OUT_OF_MEMORY;
120 mWeakButtons[index] = do_GetWeakReference(button);
123 if (!mHaveButtons) {
124 mHaveButtons = true;
126 WindowHook* hook = GetWindowHook();
127 if (!hook) {
128 return NS_ERROR_NOT_AVAILABLE;
130 (void)hook->AddHook(WM_COMMAND, WindowHookProc, this);
132 if (mVisible && FAILED(mTaskbar->ThumbBarAddButtons(
133 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS,
134 mThumbButtons))) {
135 return NS_ERROR_FAILURE;
138 button.forget(_retVal);
139 return NS_OK;
142 NS_IMETHODIMP
143 TaskbarWindowPreview::SetEnableCustomDrawing(bool aEnable) {
144 if (aEnable == mCustomDrawing) return NS_OK;
146 WindowHook* hook = GetWindowHook();
147 if (!hook) {
148 return NS_ERROR_NOT_AVAILABLE;
151 mCustomDrawing = aEnable;
152 TaskbarPreview::EnableCustomDrawing(mWnd, aEnable);
154 if (aEnable) {
155 (void)hook->AddHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
156 (void)hook->AddHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc,
157 this);
158 } else {
159 (void)hook->RemoveHook(WM_DWMSENDICONICLIVEPREVIEWBITMAP, WindowHookProc,
160 this);
161 (void)hook->RemoveHook(WM_DWMSENDICONICTHUMBNAIL, WindowHookProc, this);
163 return NS_OK;
166 NS_IMETHODIMP
167 TaskbarWindowPreview::GetEnableCustomDrawing(bool* aEnable) {
168 *aEnable = mCustomDrawing;
169 return NS_OK;
172 NS_IMETHODIMP
173 TaskbarWindowPreview::SetProgressState(nsTaskbarProgressState aState,
174 uint64_t aCurrentValue,
175 uint64_t aMaxValue) {
176 NS_ENSURE_ARG_RANGE(aState, nsTaskbarProgressState(0),
177 nsTaskbarProgressState(ArrayLength(sNativeStates) - 1));
179 TBPFLAG nativeState = sNativeStates[aState];
180 if (nativeState == TBPF_NOPROGRESS || nativeState == TBPF_INDETERMINATE) {
181 NS_ENSURE_TRUE(aCurrentValue == 0, NS_ERROR_INVALID_ARG);
182 NS_ENSURE_TRUE(aMaxValue == 0, NS_ERROR_INVALID_ARG);
185 if (aCurrentValue > aMaxValue) return NS_ERROR_ILLEGAL_VALUE;
187 mState = nativeState;
188 mCurrentValue = aCurrentValue;
189 mMaxValue = aMaxValue;
191 // Only update if we can
192 return CanMakeTaskbarCalls() ? UpdateTaskbarProgress() : NS_OK;
195 NS_IMETHODIMP
196 TaskbarWindowPreview::SetOverlayIcon(imgIContainer* aStatusIcon,
197 const nsAString& aStatusDescription) {
198 nsresult rv;
199 if (aStatusIcon) {
200 // The image shouldn't be animated
201 bool isAnimated;
202 rv = aStatusIcon->GetAnimated(&isAnimated);
203 NS_ENSURE_SUCCESS(rv, rv);
204 NS_ENSURE_FALSE(isAnimated, NS_ERROR_INVALID_ARG);
207 HICON hIcon = nullptr;
208 if (aStatusIcon) {
209 rv = nsWindowGfx::CreateIcon(
210 aStatusIcon, false, LayoutDeviceIntPoint(),
211 nsWindowGfx::GetIconMetrics(nsWindowGfx::kSmallIcon), &hIcon);
212 NS_ENSURE_SUCCESS(rv, rv);
215 if (mOverlayIcon) ::DestroyIcon(mOverlayIcon);
216 mOverlayIcon = hIcon;
217 mIconDescription = aStatusDescription;
219 // Only update if we can
220 return CanMakeTaskbarCalls() ? UpdateOverlayIcon() : NS_OK;
223 nsresult TaskbarWindowPreview::UpdateTaskbarProperties() {
224 if (mHaveButtons) {
225 if (FAILED(mTaskbar->ThumbBarAddButtons(
226 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
227 return NS_ERROR_FAILURE;
229 nsresult rv = UpdateTaskbarProgress();
230 NS_ENSURE_SUCCESS(rv, rv);
231 rv = UpdateOverlayIcon();
232 NS_ENSURE_SUCCESS(rv, rv);
233 return TaskbarPreview::UpdateTaskbarProperties();
236 nsresult TaskbarWindowPreview::UpdateTaskbarProgress() {
237 HRESULT hr = mTaskbar->SetProgressState(mWnd, mState);
238 if (SUCCEEDED(hr) && mState != TBPF_NOPROGRESS &&
239 mState != TBPF_INDETERMINATE)
240 hr = mTaskbar->SetProgressValue(mWnd, mCurrentValue, mMaxValue);
242 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
245 nsresult TaskbarWindowPreview::UpdateOverlayIcon() {
246 HRESULT hr =
247 mTaskbar->SetOverlayIcon(mWnd, mOverlayIcon, mIconDescription.get());
248 return SUCCEEDED(hr) ? NS_OK : NS_ERROR_FAILURE;
251 LRESULT
252 TaskbarWindowPreview::WndProc(UINT nMsg, WPARAM wParam, LPARAM lParam) {
253 RefPtr<TaskbarWindowPreview> kungFuDeathGrip(this);
254 switch (nMsg) {
255 case WM_COMMAND: {
256 uint32_t id = LOWORD(wParam);
257 uint32_t index = id;
258 nsCOMPtr<nsITaskbarPreviewButton> button;
259 nsresult rv = GetButton(index, getter_AddRefs(button));
260 if (NS_SUCCEEDED(rv)) mController->OnClick(button);
262 return 0;
264 return TaskbarPreview::WndProc(nMsg, wParam, lParam);
267 /* static */
268 bool TaskbarWindowPreview::TaskbarWindowHook(void* aContext, HWND hWnd,
269 UINT nMsg, WPARAM wParam,
270 LPARAM lParam, LRESULT* aResult) {
271 NS_ASSERTION(nMsg == nsAppShell::GetTaskbarButtonCreatedMessage(),
272 "Window hook proc called with wrong message");
273 TaskbarWindowPreview* preview =
274 reinterpret_cast<TaskbarWindowPreview*>(aContext);
275 // Now we can make all the calls to mTaskbar
276 preview->UpdateTaskbarProperties();
277 return false;
280 nsresult TaskbarWindowPreview::Enable() {
281 nsresult rv = TaskbarPreview::Enable();
282 NS_ENSURE_SUCCESS(rv, rv);
284 return FAILED(mTaskbar->AddTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
287 nsresult TaskbarWindowPreview::Disable() {
288 nsresult rv = TaskbarPreview::Disable();
289 NS_ENSURE_SUCCESS(rv, rv);
291 return FAILED(mTaskbar->DeleteTab(mWnd)) ? NS_ERROR_FAILURE : NS_OK;
294 void TaskbarWindowPreview::DetachFromNSWindow() {
295 // Remove the hooks we have for drawing
296 SetEnableCustomDrawing(false);
298 if (WindowHook* hook = GetWindowHook()) {
299 (void)hook->RemoveHook(WM_COMMAND, WindowHookProc, this);
300 (void)hook->RemoveMonitor(nsAppShell::GetTaskbarButtonCreatedMessage(),
301 TaskbarWindowHook, this);
303 TaskbarPreview::DetachFromNSWindow();
306 nsresult TaskbarWindowPreview::UpdateButtons() {
307 NS_ASSERTION(mVisible, "UpdateButtons called on invisible preview");
309 if (FAILED(mTaskbar->ThumbBarUpdateButtons(
310 mWnd, nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS, mThumbButtons)))
311 return NS_ERROR_FAILURE;
312 return NS_OK;
315 nsresult TaskbarWindowPreview::UpdateButton(uint32_t index) {
316 if (index >= nsITaskbarWindowPreview::NUM_TOOLBAR_BUTTONS)
317 return NS_ERROR_INVALID_ARG;
318 if (mVisible) {
319 if (FAILED(mTaskbar->ThumbBarUpdateButtons(mWnd, 1, &mThumbButtons[index])))
320 return NS_ERROR_FAILURE;
322 return NS_OK;
325 } // namespace widget
326 } // namespace mozilla