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"
16 #include <nsIWidget.h>
17 #include <nsIBaseWindow.h>
18 #include <nsServiceManagerUtils.h>
19 #include "nsIXULAppInfo.h"
20 #include "nsIJumpListBuilder.h"
21 #include "nsUXThemeData.h"
24 #include "TaskbarTabPreview.h"
25 #include "TaskbarWindowPreview.h"
26 #include "JumpListBuilder.h"
27 #include "nsWidgetsCID.h"
28 #include "nsPIDOMWindow.h"
29 #include "nsAppDirectoryServiceDefs.h"
30 #include "mozilla/Preferences.h"
31 #include "nsAppRunner.h"
32 #include "nsXREDirProvider.h"
34 #include <propvarutil.h>
38 static NS_DEFINE_CID(kJumpListBuilderCID
, NS_WIN_JUMPLISTBUILDER_CID
);
42 HWND
GetHWNDFromDocShell(nsIDocShell
* aShell
) {
43 nsCOMPtr
<nsIBaseWindow
> baseWindow(
44 do_QueryInterface(reinterpret_cast<nsISupports
*>(aShell
)));
46 if (!baseWindow
) return nullptr;
48 nsCOMPtr
<nsIWidget
> widget
;
49 baseWindow
->GetMainWidget(getter_AddRefs(widget
));
51 return widget
? (HWND
)widget
->GetNativeData(NS_NATIVE_WINDOW
) : nullptr;
54 HWND
GetHWNDFromDOMWindow(mozIDOMWindow
* dw
) {
55 nsCOMPtr
<nsIWidget
> widget
;
57 if (!dw
) return nullptr;
59 nsCOMPtr
<nsPIDOMWindowInner
> window
= nsPIDOMWindowInner::From(dw
);
60 return GetHWNDFromDocShell(window
->GetDocShell());
63 nsresult
SetWindowAppUserModelProp(mozIDOMWindow
* aParent
,
64 const nsString
& aIdentifier
) {
65 NS_ENSURE_ARG_POINTER(aParent
);
67 if (aIdentifier
.IsEmpty()) return NS_ERROR_INVALID_ARG
;
69 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDOMWindow(aParent
), GA_ROOT
);
71 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
73 RefPtr
<IPropertyStore
> pPropStore
;
74 if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND
, IID_IPropertyStore
,
75 getter_AddRefs(pPropStore
)))) {
76 return NS_ERROR_INVALID_ARG
;
80 if (FAILED(InitPropVariantFromString(aIdentifier
.get(), &pv
))) {
81 return NS_ERROR_UNEXPECTED
;
85 if (FAILED(pPropStore
->SetValue(PKEY_AppUserModel_ID
, pv
)) ||
86 FAILED(pPropStore
->Commit())) {
87 rv
= NS_ERROR_FAILURE
;
90 PropVariantClear(&pv
);
95 ///////////////////////////////////////////////////////////////////////////////
96 // default nsITaskbarPreviewController
98 class DefaultController final
: public nsITaskbarPreviewController
{
99 ~DefaultController() {}
103 explicit DefaultController(HWND hWnd
) : mWnd(hWnd
) {}
106 NS_DECL_NSITASKBARPREVIEWCONTROLLER
110 DefaultController::GetWidth(uint32_t* aWidth
) {
112 ::GetClientRect(mWnd
, &r
);
118 DefaultController::GetHeight(uint32_t* aHeight
) {
120 ::GetClientRect(mWnd
, &r
);
126 DefaultController::GetThumbnailAspectRatio(float* aThumbnailAspectRatio
) {
127 uint32_t width
, height
;
130 if (!height
) height
= 1;
132 *aThumbnailAspectRatio
= width
/ float(height
);
137 DefaultController::RequestThumbnail(nsITaskbarPreviewCallback
* aCallback
,
138 uint32_t width
, uint32_t height
) {
143 DefaultController::RequestPreview(nsITaskbarPreviewCallback
* aCallback
) {
148 DefaultController::OnClose(void) {
149 MOZ_ASSERT_UNREACHABLE(
150 "OnClose should not be called for "
151 "TaskbarWindowPreviews");
156 DefaultController::OnActivate(bool* rAcceptActivation
) {
157 *rAcceptActivation
= true;
158 MOZ_ASSERT_UNREACHABLE(
159 "OnActivate should not be called for "
160 "TaskbarWindowPreviews");
165 DefaultController::OnClick(nsITaskbarPreviewButton
* button
) { return NS_OK
; }
167 NS_IMPL_ISUPPORTS(DefaultController
, nsITaskbarPreviewController
)
173 ///////////////////////////////////////////////////////////////////////////////
176 NS_IMPL_ISUPPORTS(WinTaskbar
, nsIWinTaskbar
)
178 bool WinTaskbar::Initialize() {
179 if (mTaskbar
) return true;
181 ::CoInitialize(nullptr);
183 ::CoCreateInstance(CLSID_TaskbarList
, nullptr, CLSCTX_INPROC_SERVER
,
184 IID_ITaskbarList4
, (void**)&mTaskbar
);
185 if (FAILED(hr
)) return false;
187 hr
= mTaskbar
->HrInit();
189 // This may fail with shell extensions like blackbox installed.
190 NS_WARNING("Unable to initialize taskbar");
191 NS_RELEASE(mTaskbar
);
197 WinTaskbar::WinTaskbar() : mTaskbar(nullptr) {}
199 WinTaskbar::~WinTaskbar() {
200 if (mTaskbar
) { // match successful Initialize() call
201 NS_RELEASE(mTaskbar
);
207 bool WinTaskbar::GenerateAppUserModelID(nsAString
& aAppUserModelId
,
208 bool aPrivateBrowsing
) {
209 // If marked as such in prefs, use a hash of the profile path for the id
210 // instead of the install path hash setup by the installer.
211 bool useProfile
= Preferences::GetBool("taskbar.grouping.useprofile", false);
213 nsCOMPtr
<nsIFile
> profileDir
;
214 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR
,
215 getter_AddRefs(profileDir
));
217 if (profileDir
&& NS_SUCCEEDED(profileDir
->Exists(&exists
)) && exists
) {
219 if (NS_SUCCEEDED(profileDir
->GetPersistentDescriptor(path
))) {
221 id
.AppendInt(HashString(path
));
223 aAppUserModelId
.Assign(id
);
230 // The default value is set by the installer and is stored in the registry
231 // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
232 // hash generation operation fails, the installer will not store a value in
233 // the registry or set ids on shortcuts. A lack of an id can also occur for
235 nsCOMPtr
<nsIXULAppInfo
> appInfo
=
236 do_GetService("@mozilla.org/xre/app-info;1");
238 if (appInfo
&& NS_SUCCEEDED(appInfo
->GetName(appName
))) {
240 regKey
.AssignLiteral("Software\\Mozilla\\");
241 AppendASCIItoUTF16(appName
, regKey
);
242 regKey
.AppendLiteral("\\TaskBarIDs");
244 WCHAR path
[MAX_PATH
];
245 if (GetModuleFileNameW(nullptr, path
, MAX_PATH
)) {
246 wchar_t* slash
= wcsrchr(path
, '\\');
247 if (!slash
) return false;
248 *slash
= '\0'; // no trailing slash
250 // The hash is short, but users may customize this, so use a respectable
253 if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE
, regKey
.get(), path
, buf
,
255 aAppUserModelId
.Assign(buf
);
256 } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER
, regKey
.get(), path
,
258 aAppUserModelId
.Assign(buf
);
263 // If we haven't found an ID yet then use the install hash. In xpcshell tests
264 // the directory provider may not have been initialized so bypass in this
266 if (aAppUserModelId
.IsEmpty() && gDirServiceProvider
) {
267 gDirServiceProvider
->GetInstallHash(aAppUserModelId
);
270 if (aPrivateBrowsing
) {
271 aAppUserModelId
.AppendLiteral(";PrivateBrowsingAUMID");
274 return !aAppUserModelId
.IsEmpty();
278 bool WinTaskbar::GetAppUserModelID(nsAString
& aAppUserModelId
,
279 bool aPrivateBrowsing
) {
280 // If an ID has already been set then use that.
282 if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id
))) {
283 aAppUserModelId
.Assign(id
);
287 return GenerateAppUserModelID(aAppUserModelId
, aPrivateBrowsing
);
291 WinTaskbar::GetDefaultGroupId(nsAString
& aDefaultGroupId
) {
292 if (!GetAppUserModelID(aDefaultGroupId
)) return NS_ERROR_UNEXPECTED
;
298 WinTaskbar::GetDefaultPrivateGroupId(nsAString
& aDefaultPrivateGroupId
) {
299 if (!GetAppUserModelID(aDefaultPrivateGroupId
, true))
300 return NS_ERROR_UNEXPECTED
;
305 // (static) Called from AppShell
306 bool WinTaskbar::RegisterAppUserModelID() {
308 if (!GetAppUserModelID(uid
)) return false;
310 return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid
.get()));
314 WinTaskbar::GetAvailable(bool* aAvailable
) {
315 // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
316 // installed. Initialize early to return available=false in those cases.
317 *aAvailable
= Initialize();
323 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell
* shell
,
324 nsITaskbarPreviewController
* controller
,
325 nsITaskbarTabPreview
** _retval
) {
326 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
328 NS_ENSURE_ARG_POINTER(shell
);
329 NS_ENSURE_ARG_POINTER(controller
);
331 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDocShell(shell
), GA_ROOT
);
333 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
335 RefPtr
<TaskbarTabPreview
> preview(
336 new TaskbarTabPreview(mTaskbar
, controller
, toplevelHWND
, shell
));
337 if (!preview
) return NS_ERROR_OUT_OF_MEMORY
;
339 nsresult rv
= preview
->Init();
344 preview
.forget(_retval
);
350 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell
* shell
,
351 nsITaskbarWindowPreview
** _retval
) {
352 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
354 NS_ENSURE_ARG_POINTER(shell
);
356 HWND toplevelHWND
= ::GetAncestor(GetHWNDFromDocShell(shell
), GA_ROOT
);
358 if (!toplevelHWND
) return NS_ERROR_INVALID_ARG
;
360 nsWindow
* window
= WinUtils::GetNSWindowPtr(toplevelHWND
);
362 if (!window
) return NS_ERROR_FAILURE
;
364 nsCOMPtr
<nsITaskbarWindowPreview
> preview
= window
->GetTaskbarPreview();
366 RefPtr
<DefaultController
> defaultController
=
367 new DefaultController(toplevelHWND
);
369 TaskbarWindowPreview
* previewRaw
= new TaskbarWindowPreview(
370 mTaskbar
, defaultController
, toplevelHWND
, shell
);
372 return NS_ERROR_OUT_OF_MEMORY
;
375 preview
= previewRaw
;
377 nsresult rv
= previewRaw
->Init();
381 window
->SetTaskbarPreview(preview
);
384 preview
.forget(_retval
);
390 WinTaskbar::GetTaskbarProgress(nsIDocShell
* shell
,
391 nsITaskbarProgress
** _retval
) {
392 nsCOMPtr
<nsITaskbarWindowPreview
> preview
;
393 nsresult rv
= GetTaskbarWindowPreview(shell
, getter_AddRefs(preview
));
394 NS_ENSURE_SUCCESS(rv
, rv
);
396 return CallQueryInterface(preview
, _retval
);
400 WinTaskbar::GetOverlayIconController(
401 nsIDocShell
* shell
, nsITaskbarOverlayIconController
** _retval
) {
402 nsCOMPtr
<nsITaskbarWindowPreview
> preview
;
403 nsresult rv
= GetTaskbarWindowPreview(shell
, getter_AddRefs(preview
));
404 NS_ENSURE_SUCCESS(rv
, rv
);
406 return CallQueryInterface(preview
, _retval
);
410 WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing
,
411 nsIJumpListBuilder
** aJumpListBuilder
) {
414 if (JumpListBuilder::sBuildingList
) return NS_ERROR_ALREADY_INITIALIZED
;
416 nsCOMPtr
<nsIJumpListBuilder
> builder
=
417 do_CreateInstance(kJumpListBuilderCID
, &rv
);
418 if (NS_FAILED(rv
)) return NS_ERROR_UNEXPECTED
;
420 NS_IF_ADDREF(*aJumpListBuilder
= builder
);
423 GenerateAppUserModelID(aumid
, aPrivateBrowsing
);
424 builder
->SetAppUserModelID(aumid
);
430 WinTaskbar::SetGroupIdForWindow(mozIDOMWindow
* aParent
,
431 const nsAString
& aIdentifier
) {
432 return SetWindowAppUserModelProp(aParent
, nsString(aIdentifier
));
436 WinTaskbar::PrepareFullScreen(void* aHWND
, bool aFullScreen
) {
437 if (!Initialize()) return NS_ERROR_NOT_AVAILABLE
;
439 NS_ENSURE_ARG_POINTER(aHWND
);
441 if (!::IsWindow((HWND
)aHWND
)) return NS_ERROR_INVALID_ARG
;
443 HRESULT hr
= mTaskbar
->MarkFullscreenWindow((HWND
)aHWND
, aFullScreen
);
445 return NS_ERROR_UNEXPECTED
;
451 } // namespace widget
452 } // namespace mozilla