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 "nsUXThemeData.h"
24 #include "TaskbarTabPreview.h"
25 #include "TaskbarWindowPreview.h"
26 #include "nsWidgetsCID.h"
27 #include "nsPIDOMWindow.h"
28 #include "nsAppDirectoryServiceDefs.h"
29 #include "mozilla/Preferences.h"
30 #include "nsAppRunner.h"
31 #include "nsXREDirProvider.h"
32 #include "mozilla/widget/WinRegistry.h"
34 #include <propvarutil.h>
40 HWND
GetHWNDFromDocShell(nsIDocShell
* aShell
) {
41 nsCOMPtr
<nsIBaseWindow
> baseWindow(
42 do_QueryInterface(reinterpret_cast<nsISupports
*>(aShell
)));
44 if (!baseWindow
) return nullptr;
46 nsCOMPtr
<nsIWidget
> widget
;
47 baseWindow
->GetMainWidget(getter_AddRefs(widget
));
49 return widget
? (HWND
)widget
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
52 HWND
GetHWNDFromDOMWindow(mozIDOMWindow
* dw
) {
53 nsCOMPtr
<nsIWidget
> widget
;
55 if (!dw
) return nullptr;
57 nsCOMPtr
<nsPIDOMWindowInner
> window
= nsPIDOMWindowInner::From(dw
);
58 return GetHWNDFromDocShell(window
->GetDocShell());
61 nsresult
SetWindowAppUserModelProp(mozIDOMWindow
* aParent
,
62 const nsString
& aIdentifier
) {
63 NS_ENSURE_ARG_POINTER(aParent
);
65 if (aIdentifier
.IsEmpty()) return NS_ERROR_INVALID_ARG
;
67 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDOMWindow(aParent
), GA_ROOT
);
69 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
71 RefPtr
<IPropertyStore
> pPropStore
;
72 if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND
, IID_IPropertyStore
,
73 getter_AddRefs(pPropStore
)))) {
74 return NS_ERROR_INVALID_ARG
;
78 if (FAILED(InitPropVariantFromString(aIdentifier
.get(), &pv
))) {
79 return NS_ERROR_UNEXPECTED
;
83 if (FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
)) ||
84 FAILED(pPropStore
->Commit())) {
85 rv
= NS_ERROR_FAILURE
;
88 PropVariantClear(&pv
);
93 ///////////////////////////////////////////////////////////////////////////////
94 // default nsITaskbarPreviewController
96 class DefaultController final
: public nsITaskbarPreviewController
{
97 ~DefaultController() {}
101 explicit DefaultController(HWND hWnd
) : mWnd(hWnd
) {}
104 NS_DECL_NSITASKBARPREVIEWCONTROLLER
108 DefaultController::GetWidth(uint32_t* aWidth
) {
110 ::GetClientRect(mWnd
, &r
);
116 DefaultController::GetHeight(uint32_t* aHeight
) {
118 ::GetClientRect(mWnd
, &r
);
124 DefaultController::GetThumbnailAspectRatio(float* aThumbnailAspectRatio
) {
125 uint32_t width
, height
;
128 if (!height
) height
= 1;
130 *aThumbnailAspectRatio
= width
/ float(height
);
135 DefaultController::RequestThumbnail(nsITaskbarPreviewCallback
* aCallback
,
136 uint32_t width
, uint32_t height
) {
141 DefaultController::RequestPreview(nsITaskbarPreviewCallback
* aCallback
) {
146 DefaultController::OnClose(void) {
147 MOZ_ASSERT_UNREACHABLE(
148 "OnClose should not be called for "
149 "TaskbarWindowPreviews");
154 DefaultController::OnActivate(bool* rAcceptActivation
) {
155 *rAcceptActivation
= true;
156 MOZ_ASSERT_UNREACHABLE(
157 "OnActivate should not be called for "
158 "TaskbarWindowPreviews");
163 DefaultController::OnClick(nsITaskbarPreviewButton
* button
) { return NS_OK
; }
165 NS_IMPL_ISUPPORTS(DefaultController
, nsITaskbarPreviewController
)
171 ///////////////////////////////////////////////////////////////////////////////
174 NS_IMPL_ISUPPORTS(WinTaskbar
, nsIWinTaskbar
)
176 bool WinTaskbar::Initialize() {
177 if (mTaskbar
) return true;
179 ::CoInitialize(nullptr);
181 ::CoCreateInstance(CLSID_TaskbarList
, nullptr, CLSCTX_INPROC_SERVER
,
182 IID_ITaskbarList4
, (void**)&mTaskbar
);
183 if (FAILED(hr
)) return false;
185 hr
= mTaskbar
->HrInit();
187 // This may fail with shell extensions like blackbox installed.
188 NS_WARNING("Unable to initialize taskbar");
189 NS_RELEASE(mTaskbar
);
195 WinTaskbar::WinTaskbar() : mTaskbar(nullptr) {}
197 WinTaskbar::~WinTaskbar() {
198 if (mTaskbar
) { // match successful Initialize() call
199 NS_RELEASE(mTaskbar
);
205 bool WinTaskbar::GenerateAppUserModelID(nsAString
& aAppUserModelId
,
206 bool aPrivateBrowsing
) {
207 // If marked as such in prefs, use a hash of the profile path for the id
208 // instead of the install path hash setup by the installer.
209 if (Preferences::GetBool("taskbar.grouping.useprofile", false)) {
210 nsCOMPtr
<nsIFile
> profileDir
;
211 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
212 getter_AddRefs(profileDir
));
214 if (profileDir
&& NS_SUCCEEDED(profileDir
->Exists(&exists
)) && exists
) {
216 if (NS_SUCCEEDED(profileDir
->GetPersistentDescriptor(path
))) {
218 id
.AppendInt(HashString(path
));
220 aAppUserModelId
.Assign(id
);
222 if (aPrivateBrowsing
) {
223 aAppUserModelId
.AppendLiteral(";PrivateBrowsingAUMID");
232 // The default value is set by the installer and is stored in the registry
233 // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
234 // hash generation operation fails, the installer will not store a value in
235 // the registry or set ids on shortcuts. A lack of an id can also occur for
237 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
238 do_GetService("@mozilla.org/xre/app-info;1");
240 if (appInfo
&& NS_SUCCEEDED(appInfo
->GetName(appName
))) {
242 regKey
.AssignLiteral("Software\\Mozilla\\");
243 AppendASCIItoUTF16(appName
, regKey
);
244 regKey
.AppendLiteral("\\TaskBarIDs");
246 WCHAR path
[MAX_PATH
];
247 if (GetModuleFileNameW(nullptr, path
, MAX_PATH
)) {
248 wchar_t* slash
= wcsrchr(path
, '\\');
249 if (!slash
) return false;
250 *slash
= '\0'; // no trailing slash
252 nsDependentString
pathStr(path
);
253 for (auto* rootKey
: {HKEY_LOCAL_MACHINE
, HKEY_CURRENT_USER
}) {
254 if (auto aumid
= WinRegistry::GetString(rootKey
, regKey
, pathStr
)) {
255 aAppUserModelId
= std::move(*aumid
);
262 // If we haven't found an ID yet then use the install hash. In xpcshell tests
263 // the directory provider may not have been initialized so bypass in this
265 if (aAppUserModelId
.IsEmpty() && gDirServiceProvider
) {
266 gDirServiceProvider
->GetInstallHash(aAppUserModelId
);
269 if (aPrivateBrowsing
) {
270 aAppUserModelId
.AppendLiteral(";PrivateBrowsingAUMID");
273 return !aAppUserModelId
.IsEmpty();
277 bool WinTaskbar::GetAppUserModelID(nsAString
& aAppUserModelId
,
278 bool aPrivateBrowsing
) {
279 // If an ID has already been set then use that.
281 if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id
))) {
282 aAppUserModelId
.Assign(id
);
286 return GenerateAppUserModelID(aAppUserModelId
, aPrivateBrowsing
);
290 WinTaskbar::GetDefaultGroupId(nsAString
& aDefaultGroupId
) {
291 if (!GetAppUserModelID(aDefaultGroupId
)) return NS_ERROR_UNEXPECTED
;
297 WinTaskbar::GetDefaultPrivateGroupId(nsAString
& aDefaultPrivateGroupId
) {
298 if (!GetAppUserModelID(aDefaultPrivateGroupId
, true))
299 return NS_ERROR_UNEXPECTED
;
304 // (static) Called from AppShell
305 bool WinTaskbar::RegisterAppUserModelID() {
307 if (!GetAppUserModelID(uid
)) return false;
309 return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid
.get()));
313 WinTaskbar::GetAvailable(bool* aAvailable
) {
314 // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
315 // installed. Initialize early to return available=false in those cases.
316 *aAvailable
= Initialize();
322 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell
* shell
,
323 nsITaskbarPreviewController
* controller
,
324 nsITaskbarTabPreview
** _retval
) {
325 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
327 NS_ENSURE_ARG_POINTER(shell
);
328 NS_ENSURE_ARG_POINTER(controller
);
330 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDocShell(shell
), GA_ROOT
);
332 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
334 RefPtr
<TaskbarTabPreview
> preview(
335 new TaskbarTabPreview(mTaskbar
, controller
, toplevelHWND
, shell
));
336 if (!preview
) return NS_ERROR_OUT_OF_MEMORY
;
338 nsresult rv
= preview
->Init();
343 preview
.forget(_retval
);
349 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell
* shell
,
350 nsITaskbarWindowPreview
** _retval
) {
351 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
353 NS_ENSURE_ARG_POINTER(shell
);
355 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDocShell(shell
), GA_ROOT
);
357 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
359 nsWindow
* window
= WinUtils::GetNSWindowPtr(toplevelHWND
);
361 if (!window
) return NS_ERROR_FAILURE
;
363 nsCOMPtr
<nsITaskbarWindowPreview
> preview
= window
->GetTaskbarPreview();
365 RefPtr
<DefaultController
> defaultController
=
366 new DefaultController(toplevelHWND
);
368 TaskbarWindowPreview
* previewRaw
= new TaskbarWindowPreview(
369 mTaskbar
, defaultController
, toplevelHWND
, shell
);
371 return NS_ERROR_OUT_OF_MEMORY
;
374 preview
= previewRaw
;
376 nsresult rv
= previewRaw
->Init();
380 window
->SetTaskbarPreview(preview
);
383 preview
.forget(_retval
);
389 WinTaskbar::GetTaskbarProgress(nsIDocShell
* shell
,
390 nsITaskbarProgress
** _retval
) {
391 nsCOMPtr
<nsITaskbarWindowPreview
> preview
;
392 nsresult rv
= GetTaskbarWindowPreview(shell
, getter_AddRefs(preview
));
393 NS_ENSURE_SUCCESS(rv
, rv
);
395 return CallQueryInterface(preview
, _retval
);
399 WinTaskbar::GetOverlayIconController(
400 nsIDocShell
* shell
, nsITaskbarOverlayIconController
** _retval
) {
401 nsCOMPtr
<nsITaskbarWindowPreview
> preview
;
402 nsresult rv
= GetTaskbarWindowPreview(shell
, getter_AddRefs(preview
));
403 NS_ENSURE_SUCCESS(rv
, rv
);
405 return CallQueryInterface(preview
, _retval
);
409 WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing
,
410 nsIJumpListBuilder
** aJumpListBuilder
) {
412 GenerateAppUserModelID(aumid
, aPrivateBrowsing
);
414 nsCOMPtr
<nsIJumpListBuilder
> builder
= new JumpListBuilder(aumid
);
416 return NS_ERROR_UNEXPECTED
;
419 NS_IF_ADDREF(*aJumpListBuilder
= builder
);
424 WinTaskbar::GetGroupIdForWindow(mozIDOMWindow
* aParent
,
425 nsAString
& aIdentifier
) {
426 NS_ENSURE_ARG_POINTER(aParent
);
427 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDOMWindow(aParent
), GA_ROOT
);
428 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
429 RefPtr
<IPropertyStore
> pPropStore
;
430 if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND
, IID_IPropertyStore
,
431 getter_AddRefs(pPropStore
)))) {
432 return NS_ERROR_INVALID_ARG
;
435 PropVariantInit(&pv
);
436 auto cleanupPropVariant
= MakeScopeExit([&] { PropVariantClear(&pv
); });
437 if (FAILED(pPropStore
->GetValue(PKEY_AppUserModel_ID
, &pv
))) {
438 return NS_ERROR_FAILURE
;
440 if (pv
.vt
!= VT_LPWSTR
) {
441 // This can happen when there is no window specific group ID set
442 // It's not an error case so we have to check for empty strings
443 // returned from the function.
446 aIdentifier
.Assign(char16ptr_t(pv
.pwszVal
));
451 WinTaskbar::SetGroupIdForWindow(mozIDOMWindow
* aParent
,
452 const nsAString
& aIdentifier
) {
453 return SetWindowAppUserModelProp(aParent
, nsString(aIdentifier
));
457 WinTaskbar::PrepareFullScreen(void* aHWND
, bool aFullScreen
) {
458 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
460 NS_ENSURE_ARG_POINTER(aHWND
);
462 if (!::IsWindow((HWND
)aHWND
)) return NS_ERROR_INVALID_ARG
;
464 HRESULT hr
= mTaskbar
->MarkFullscreenWindow((HWND
)aHWND
, aFullScreen
);
466 return NS_ERROR_UNEXPECTED
;
472 } // namespace widget
473 } // namespace mozilla