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"
17 #include <nsIWidget.h>
18 #include <nsIBaseWindow.h>
19 #include <nsServiceManagerUtils.h>
20 #include "nsIXULAppInfo.h"
21 #include "nsILegacyJumpListBuilder.h"
22 #include "nsUXThemeData.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"
36 #include <propvarutil.h>
40 static NS_DEFINE_CID(kLegacyJumpListBuilderCID
,
41 NS_WIN_LEGACYJUMPLISTBUILDER_CID
);
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
;
83 if (FAILED(InitPropVariantFromString(aIdentifier
.get(), &pv
))) {
84 return NS_ERROR_UNEXPECTED
;
88 if (FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
)) ||
89 FAILED(pPropStore
->Commit())) {
90 rv
= NS_ERROR_FAILURE
;
93 PropVariantClear(&pv
);
98 ///////////////////////////////////////////////////////////////////////////////
99 // default nsITaskbarPreviewController
101 class DefaultController final
: public nsITaskbarPreviewController
{
102 ~DefaultController() {}
106 explicit DefaultController(HWND hWnd
) : mWnd(hWnd
) {}
109 NS_DECL_NSITASKBARPREVIEWCONTROLLER
113 DefaultController::GetWidth(uint32_t* aWidth
) {
115 ::GetClientRect(mWnd
, &r
);
121 DefaultController::GetHeight(uint32_t* aHeight
) {
123 ::GetClientRect(mWnd
, &r
);
129 DefaultController::GetThumbnailAspectRatio(float* aThumbnailAspectRatio
) {
130 uint32_t width
, height
;
133 if (!height
) height
= 1;
135 *aThumbnailAspectRatio
= width
/ float(height
);
140 DefaultController::RequestThumbnail(nsITaskbarPreviewCallback
* aCallback
,
141 uint32_t width
, uint32_t height
) {
146 DefaultController::RequestPreview(nsITaskbarPreviewCallback
* aCallback
) {
151 DefaultController::OnClose(void) {
152 MOZ_ASSERT_UNREACHABLE(
153 "OnClose should not be called for "
154 "TaskbarWindowPreviews");
159 DefaultController::OnActivate(bool* rAcceptActivation
) {
160 *rAcceptActivation
= true;
161 MOZ_ASSERT_UNREACHABLE(
162 "OnActivate should not be called for "
163 "TaskbarWindowPreviews");
168 DefaultController::OnClick(nsITaskbarPreviewButton
* button
) { return NS_OK
; }
170 NS_IMPL_ISUPPORTS(DefaultController
, nsITaskbarPreviewController
)
176 ///////////////////////////////////////////////////////////////////////////////
179 NS_IMPL_ISUPPORTS(WinTaskbar
, nsIWinTaskbar
)
181 bool WinTaskbar::Initialize() {
182 if (mTaskbar
) return true;
184 ::CoInitialize(nullptr);
186 ::CoCreateInstance(CLSID_TaskbarList
, nullptr, CLSCTX_INPROC_SERVER
,
187 IID_ITaskbarList4
, (void**)&mTaskbar
);
188 if (FAILED(hr
)) return false;
190 hr
= mTaskbar
->HrInit();
192 // This may fail with shell extensions like blackbox installed.
193 NS_WARNING("Unable to initialize taskbar");
194 NS_RELEASE(mTaskbar
);
200 WinTaskbar::WinTaskbar() : mTaskbar(nullptr) {}
202 WinTaskbar::~WinTaskbar() {
203 if (mTaskbar
) { // match successful Initialize() call
204 NS_RELEASE(mTaskbar
);
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
));
219 if (profileDir
&& NS_SUCCEEDED(profileDir
->Exists(&exists
)) && exists
) {
221 if (NS_SUCCEEDED(profileDir
->GetPersistentDescriptor(path
))) {
223 id
.AppendInt(HashString(path
));
225 aAppUserModelId
.Assign(id
);
227 if (aPrivateBrowsing
) {
228 aAppUserModelId
.AppendLiteral(";PrivateBrowsingAUMID");
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
242 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
243 do_GetService("@mozilla.org/xre/app-info;1");
245 if (appInfo
&& NS_SUCCEEDED(appInfo
->GetName(appName
))) {
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
);
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
270 if (aAppUserModelId
.IsEmpty() && gDirServiceProvider
) {
271 gDirServiceProvider
->GetInstallHash(aAppUserModelId
);
274 if (aPrivateBrowsing
) {
275 aAppUserModelId
.AppendLiteral(";PrivateBrowsingAUMID");
278 return !aAppUserModelId
.IsEmpty();
282 bool WinTaskbar::GetAppUserModelID(nsAString
& aAppUserModelId
,
283 bool aPrivateBrowsing
) {
284 // If an ID has already been set then use that.
286 if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id
))) {
287 aAppUserModelId
.Assign(id
);
291 return GenerateAppUserModelID(aAppUserModelId
, aPrivateBrowsing
);
295 WinTaskbar::GetDefaultGroupId(nsAString
& aDefaultGroupId
) {
296 if (!GetAppUserModelID(aDefaultGroupId
)) return NS_ERROR_UNEXPECTED
;
302 WinTaskbar::GetDefaultPrivateGroupId(nsAString
& aDefaultPrivateGroupId
) {
303 if (!GetAppUserModelID(aDefaultPrivateGroupId
, true))
304 return NS_ERROR_UNEXPECTED
;
309 // (static) Called from AppShell
310 bool WinTaskbar::RegisterAppUserModelID() {
312 if (!GetAppUserModelID(uid
)) return false;
314 return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid
.get()));
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();
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();
348 preview
.forget(_retval
);
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();
370 RefPtr
<DefaultController
> defaultController
=
371 new DefaultController(toplevelHWND
);
373 TaskbarWindowPreview
* previewRaw
= new TaskbarWindowPreview(
374 mTaskbar
, defaultController
, toplevelHWND
, shell
);
376 return NS_ERROR_OUT_OF_MEMORY
;
379 preview
= previewRaw
;
381 nsresult rv
= previewRaw
->Init();
385 window
->SetTaskbarPreview(preview
);
388 preview
.forget(_retval
);
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
);
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
);
414 WinTaskbar::CreateLegacyJumpListBuilder(
415 bool aPrivateBrowsing
, nsILegacyJumpListBuilder
** aJumpListBuilder
) {
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
);
427 GenerateAppUserModelID(aumid
, aPrivateBrowsing
);
428 builder
->SetAppUserModelID(aumid
);
434 WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing
,
435 nsIJumpListBuilder
** aJumpListBuilder
) {
437 GenerateAppUserModelID(aumid
, aPrivateBrowsing
);
439 nsCOMPtr
<nsIJumpListBuilder
> builder
= new JumpListBuilder(aumid
);
441 return NS_ERROR_UNEXPECTED
;
444 NS_IF_ADDREF(*aJumpListBuilder
= builder
);
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
;
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.
471 aIdentifier
.Assign(char16ptr_t(pv
.pwszVal
));
476 WinTaskbar::SetGroupIdForWindow(mozIDOMWindow
* aParent
,
477 const nsAString
& aIdentifier
) {
478 return SetWindowAppUserModelProp(aParent
, nsString(aIdentifier
));
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
);
491 return NS_ERROR_UNEXPECTED
;
497 } // namespace widget
498 } // namespace mozilla