Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / widget / windows / WinTaskbar.cpp
blob66854f3ee2ea9331384205c74120edd9017c98d0
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 "nsIWinTaskbar.h"
9 #include "WinTaskbar.h"
10 #include "TaskbarPreview.h"
11 #include "nsITaskbarPreviewController.h"
13 #include "mozilla/RefPtr.h"
14 #include "mozilla/widget/JumpListBuilder.h"
15 #include <nsError.h>
16 #include <nsCOMPtr.h>
17 #include <nsIWidget.h>
18 #include <nsIBaseWindow.h>
19 #include <nsServiceManagerUtils.h>
20 #include "nsIXULAppInfo.h"
21 #include "nsILegacyJumpListBuilder.h"
22 #include "nsUXThemeData.h"
23 #include "nsWindow.h"
24 #include "WinUtils.h"
25 #include "TaskbarTabPreview.h"
26 #include "TaskbarWindowPreview.h"
27 #include "LegacyJumpListBuilder.h"
28 #include "nsWidgetsCID.h"
29 #include "nsPIDOMWindow.h"
30 #include "nsAppDirectoryServiceDefs.h"
31 #include "mozilla/Preferences.h"
32 #include "nsAppRunner.h"
33 #include "nsXREDirProvider.h"
34 #include "mozilla/widget/WinRegistry.h"
35 #include <io.h>
36 #include <propvarutil.h>
37 #include <propkey.h>
38 #include <shellapi.h>
40 static NS_DEFINE_CID(kLegacyJumpListBuilderCID,
41 NS_WIN_LEGACYJUMPLISTBUILDER_CID);
43 namespace {
45 HWND GetHWNDFromDocShell(nsIDocShell* aShell) {
46 nsCOMPtr<nsIBaseWindow> baseWindow(
47 do_QueryInterface(reinterpret_cast<nsISupports*>(aShell)));
49 if (!baseWindow) return nullptr;
51 nsCOMPtr<nsIWidget> widget;
52 baseWindow->GetMainWidget(getter_AddRefs(widget));
54 return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
57 HWND GetHWNDFromDOMWindow(mozIDOMWindow* dw) {
58 nsCOMPtr<nsIWidget> widget;
60 if (!dw) return nullptr;
62 nsCOMPtr<nsPIDOMWindowInner> window = nsPIDOMWindowInner::From(dw);
63 return GetHWNDFromDocShell(window->GetDocShell());
66 nsresult SetWindowAppUserModelProp(mozIDOMWindow* aParent,
67 const nsString& aIdentifier) {
68 NS_ENSURE_ARG_POINTER(aParent);
70 if (aIdentifier.IsEmpty()) return NS_ERROR_INVALID_ARG;
72 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT);
74 if (!toplevelHWND) return NS_ERROR_INVALID_ARG;
76 RefPtr<IPropertyStore> pPropStore;
77 if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND, IID_IPropertyStore,
78 getter_AddRefs(pPropStore)))) {
79 return NS_ERROR_INVALID_ARG;
82 PROPVARIANT pv;
83 if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
84 return NS_ERROR_UNEXPECTED;
87 nsresult rv = NS_OK;
88 if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) ||
89 FAILED(pPropStore->Commit())) {
90 rv = NS_ERROR_FAILURE;
93 PropVariantClear(&pv);
95 return rv;
98 ///////////////////////////////////////////////////////////////////////////////
99 // default nsITaskbarPreviewController
101 class DefaultController final : public nsITaskbarPreviewController {
102 ~DefaultController() {}
103 HWND mWnd;
105 public:
106 explicit DefaultController(HWND hWnd) : mWnd(hWnd) {}
108 NS_DECL_ISUPPORTS
109 NS_DECL_NSITASKBARPREVIEWCONTROLLER
112 NS_IMETHODIMP
113 DefaultController::GetWidth(uint32_t* aWidth) {
114 RECT r;
115 ::GetClientRect(mWnd, &r);
116 *aWidth = r.right;
117 return NS_OK;
120 NS_IMETHODIMP
121 DefaultController::GetHeight(uint32_t* aHeight) {
122 RECT r;
123 ::GetClientRect(mWnd, &r);
124 *aHeight = r.bottom;
125 return NS_OK;
128 NS_IMETHODIMP
129 DefaultController::GetThumbnailAspectRatio(float* aThumbnailAspectRatio) {
130 uint32_t width, height;
131 GetWidth(&width);
132 GetHeight(&height);
133 if (!height) height = 1;
135 *aThumbnailAspectRatio = width / float(height);
136 return NS_OK;
139 NS_IMETHODIMP
140 DefaultController::RequestThumbnail(nsITaskbarPreviewCallback* aCallback,
141 uint32_t width, uint32_t height) {
142 return NS_OK;
145 NS_IMETHODIMP
146 DefaultController::RequestPreview(nsITaskbarPreviewCallback* aCallback) {
147 return NS_OK;
150 NS_IMETHODIMP
151 DefaultController::OnClose(void) {
152 MOZ_ASSERT_UNREACHABLE(
153 "OnClose should not be called for "
154 "TaskbarWindowPreviews");
155 return NS_OK;
158 NS_IMETHODIMP
159 DefaultController::OnActivate(bool* rAcceptActivation) {
160 *rAcceptActivation = true;
161 MOZ_ASSERT_UNREACHABLE(
162 "OnActivate should not be called for "
163 "TaskbarWindowPreviews");
164 return NS_OK;
167 NS_IMETHODIMP
168 DefaultController::OnClick(nsITaskbarPreviewButton* button) { return NS_OK; }
170 NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController)
171 } // namespace
173 namespace mozilla {
174 namespace widget {
176 ///////////////////////////////////////////////////////////////////////////////
177 // nsIWinTaskbar
179 NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar)
181 bool WinTaskbar::Initialize() {
182 if (mTaskbar) return true;
184 ::CoInitialize(nullptr);
185 HRESULT hr =
186 ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER,
187 IID_ITaskbarList4, (void**)&mTaskbar);
188 if (FAILED(hr)) return false;
190 hr = mTaskbar->HrInit();
191 if (FAILED(hr)) {
192 // This may fail with shell extensions like blackbox installed.
193 NS_WARNING("Unable to initialize taskbar");
194 NS_RELEASE(mTaskbar);
195 return false;
197 return true;
200 WinTaskbar::WinTaskbar() : mTaskbar(nullptr) {}
202 WinTaskbar::~WinTaskbar() {
203 if (mTaskbar) { // match successful Initialize() call
204 NS_RELEASE(mTaskbar);
205 ::CoUninitialize();
209 // static
210 bool WinTaskbar::GenerateAppUserModelID(nsAString& aAppUserModelId,
211 bool aPrivateBrowsing) {
212 // If marked as such in prefs, use a hash of the profile path for the id
213 // instead of the install path hash setup by the installer.
214 if (Preferences::GetBool("taskbar.grouping.useprofile", false)) {
215 nsCOMPtr<nsIFile> profileDir;
216 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
217 getter_AddRefs(profileDir));
218 bool exists = false;
219 if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
220 nsAutoCString path;
221 if (NS_SUCCEEDED(profileDir->GetPersistentDescriptor(path))) {
222 nsAutoString id;
223 id.AppendInt(HashString(path));
224 if (!id.IsEmpty()) {
225 aAppUserModelId.Assign(id);
227 if (aPrivateBrowsing) {
228 aAppUserModelId.AppendLiteral(";PrivateBrowsingAUMID");
231 return true;
237 // The default value is set by the installer and is stored in the registry
238 // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
239 // hash generation operation fails, the installer will not store a value in
240 // the registry or set ids on shortcuts. A lack of an id can also occur for
241 // zipped builds.
242 nsCOMPtr<nsIXULAppInfo> appInfo =
243 do_GetService("@mozilla.org/xre/app-info;1");
244 nsCString appName;
245 if (appInfo && NS_SUCCEEDED(appInfo->GetName(appName))) {
246 nsAutoString regKey;
247 regKey.AssignLiteral("Software\\Mozilla\\");
248 AppendASCIItoUTF16(appName, regKey);
249 regKey.AppendLiteral("\\TaskBarIDs");
251 WCHAR path[MAX_PATH];
252 if (GetModuleFileNameW(nullptr, path, MAX_PATH)) {
253 wchar_t* slash = wcsrchr(path, '\\');
254 if (!slash) return false;
255 *slash = '\0'; // no trailing slash
257 nsDependentString pathStr(path);
258 for (auto* rootKey : {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}) {
259 if (auto aumid = WinRegistry::GetString(rootKey, regKey, pathStr)) {
260 aAppUserModelId = std::move(*aumid);
261 break;
267 // If we haven't found an ID yet then use the install hash. In xpcshell tests
268 // the directory provider may not have been initialized so bypass in this
269 // case.
270 if (aAppUserModelId.IsEmpty() && gDirServiceProvider) {
271 gDirServiceProvider->GetInstallHash(aAppUserModelId);
274 if (aPrivateBrowsing) {
275 aAppUserModelId.AppendLiteral(";PrivateBrowsingAUMID");
278 return !aAppUserModelId.IsEmpty();
281 // static
282 bool WinTaskbar::GetAppUserModelID(nsAString& aAppUserModelId,
283 bool aPrivateBrowsing) {
284 // If an ID has already been set then use that.
285 PWSTR id;
286 if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id))) {
287 aAppUserModelId.Assign(id);
288 CoTaskMemFree(id);
291 return GenerateAppUserModelID(aAppUserModelId, aPrivateBrowsing);
294 NS_IMETHODIMP
295 WinTaskbar::GetDefaultGroupId(nsAString& aDefaultGroupId) {
296 if (!GetAppUserModelID(aDefaultGroupId)) return NS_ERROR_UNEXPECTED;
298 return NS_OK;
301 NS_IMETHODIMP
302 WinTaskbar::GetDefaultPrivateGroupId(nsAString& aDefaultPrivateGroupId) {
303 if (!GetAppUserModelID(aDefaultPrivateGroupId, true))
304 return NS_ERROR_UNEXPECTED;
306 return NS_OK;
309 // (static) Called from AppShell
310 bool WinTaskbar::RegisterAppUserModelID() {
311 nsAutoString uid;
312 if (!GetAppUserModelID(uid)) return false;
314 return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid.get()));
317 NS_IMETHODIMP
318 WinTaskbar::GetAvailable(bool* aAvailable) {
319 // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
320 // installed. Initialize early to return available=false in those cases.
321 *aAvailable = Initialize();
323 return NS_OK;
326 NS_IMETHODIMP
327 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell* shell,
328 nsITaskbarPreviewController* controller,
329 nsITaskbarTabPreview** _retval) {
330 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE;
332 NS_ENSURE_ARG_POINTER(shell);
333 NS_ENSURE_ARG_POINTER(controller);
335 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
337 if (!toplevelHWND) return NS_ERROR_INVALID_ARG;
339 RefPtr<TaskbarTabPreview> preview(
340 new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell));
341 if (!preview) return NS_ERROR_OUT_OF_MEMORY;
343 nsresult rv = preview->Init();
344 if (NS_FAILED(rv)) {
345 return rv;
348 preview.forget(_retval);
350 return NS_OK;
353 NS_IMETHODIMP
354 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell* shell,
355 nsITaskbarWindowPreview** _retval) {
356 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE;
358 NS_ENSURE_ARG_POINTER(shell);
360 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
362 if (!toplevelHWND) return NS_ERROR_INVALID_ARG;
364 nsWindow* window = WinUtils::GetNSWindowPtr(toplevelHWND);
366 if (!window) return NS_ERROR_FAILURE;
368 nsCOMPtr<nsITaskbarWindowPreview> preview = window->GetTaskbarPreview();
369 if (!preview) {
370 RefPtr<DefaultController> defaultController =
371 new DefaultController(toplevelHWND);
373 TaskbarWindowPreview* previewRaw = new TaskbarWindowPreview(
374 mTaskbar, defaultController, toplevelHWND, shell);
375 if (!previewRaw) {
376 return NS_ERROR_OUT_OF_MEMORY;
379 preview = previewRaw;
381 nsresult rv = previewRaw->Init();
382 if (NS_FAILED(rv)) {
383 return rv;
385 window->SetTaskbarPreview(preview);
388 preview.forget(_retval);
390 return NS_OK;
393 NS_IMETHODIMP
394 WinTaskbar::GetTaskbarProgress(nsIDocShell* shell,
395 nsITaskbarProgress** _retval) {
396 nsCOMPtr<nsITaskbarWindowPreview> preview;
397 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
398 NS_ENSURE_SUCCESS(rv, rv);
400 return CallQueryInterface(preview, _retval);
403 NS_IMETHODIMP
404 WinTaskbar::GetOverlayIconController(
405 nsIDocShell* shell, nsITaskbarOverlayIconController** _retval) {
406 nsCOMPtr<nsITaskbarWindowPreview> preview;
407 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
408 NS_ENSURE_SUCCESS(rv, rv);
410 return CallQueryInterface(preview, _retval);
413 NS_IMETHODIMP
414 WinTaskbar::CreateLegacyJumpListBuilder(
415 bool aPrivateBrowsing, nsILegacyJumpListBuilder** aJumpListBuilder) {
416 nsresult rv;
418 if (LegacyJumpListBuilder::sBuildingList) return NS_ERROR_ALREADY_INITIALIZED;
420 nsCOMPtr<nsILegacyJumpListBuilder> builder =
421 do_CreateInstance(kLegacyJumpListBuilderCID, &rv);
422 if (NS_FAILED(rv)) return NS_ERROR_UNEXPECTED;
424 NS_IF_ADDREF(*aJumpListBuilder = builder);
426 nsAutoString aumid;
427 GenerateAppUserModelID(aumid, aPrivateBrowsing);
428 builder->SetAppUserModelID(aumid);
430 return NS_OK;
433 NS_IMETHODIMP
434 WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing,
435 nsIJumpListBuilder** aJumpListBuilder) {
436 nsAutoString aumid;
437 GenerateAppUserModelID(aumid, aPrivateBrowsing);
439 nsCOMPtr<nsIJumpListBuilder> builder = new JumpListBuilder(aumid);
440 if (!builder) {
441 return NS_ERROR_UNEXPECTED;
444 NS_IF_ADDREF(*aJumpListBuilder = builder);
445 return NS_OK;
448 NS_IMETHODIMP
449 WinTaskbar::GetGroupIdForWindow(mozIDOMWindow* aParent,
450 nsAString& aIdentifier) {
451 NS_ENSURE_ARG_POINTER(aParent);
452 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT);
453 if (!toplevelHWND) return NS_ERROR_INVALID_ARG;
454 RefPtr<IPropertyStore> pPropStore;
455 if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND, IID_IPropertyStore,
456 getter_AddRefs(pPropStore)))) {
457 return NS_ERROR_INVALID_ARG;
459 PROPVARIANT pv;
460 PropVariantInit(&pv);
461 auto cleanupPropVariant = MakeScopeExit([&] { PropVariantClear(&pv); });
462 if (FAILED(pPropStore->GetValue(PKEY_AppUserModel_ID, &pv))) {
463 return NS_ERROR_FAILURE;
465 if (pv.vt != VT_LPWSTR) {
466 // This can happen when there is no window specific group ID set
467 // It's not an error case so we have to check for empty strings
468 // returned from the function.
469 return NS_OK;
471 aIdentifier.Assign(char16ptr_t(pv.pwszVal));
472 return NS_OK;
475 NS_IMETHODIMP
476 WinTaskbar::SetGroupIdForWindow(mozIDOMWindow* aParent,
477 const nsAString& aIdentifier) {
478 return SetWindowAppUserModelProp(aParent, nsString(aIdentifier));
481 NS_IMETHODIMP
482 WinTaskbar::PrepareFullScreen(void* aHWND, bool aFullScreen) {
483 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE;
485 NS_ENSURE_ARG_POINTER(aHWND);
487 if (!::IsWindow((HWND)aHWND)) return NS_ERROR_INVALID_ARG;
489 HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen);
490 if (FAILED(hr)) {
491 return NS_ERROR_UNEXPECTED;
494 return NS_OK;
497 } // namespace widget
498 } // namespace mozilla