Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / windows / WinTaskbar.cpp
blob67717499bc6617c4901e1cf94783df0fdf02dc52
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 "nsUXThemeData.h"
22 #include "nsWindow.h"
23 #include "WinUtils.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"
33 #include <io.h>
34 #include <propvarutil.h>
35 #include <propkey.h>
36 #include <shellapi.h>
38 namespace {
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;
77 PROPVARIANT pv;
78 if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
79 return NS_ERROR_UNEXPECTED;
82 nsresult rv = NS_OK;
83 if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) ||
84 FAILED(pPropStore->Commit())) {
85 rv = NS_ERROR_FAILURE;
88 PropVariantClear(&pv);
90 return rv;
93 ///////////////////////////////////////////////////////////////////////////////
94 // default nsITaskbarPreviewController
96 class DefaultController final : public nsITaskbarPreviewController {
97 ~DefaultController() {}
98 HWND mWnd;
100 public:
101 explicit DefaultController(HWND hWnd) : mWnd(hWnd) {}
103 NS_DECL_ISUPPORTS
104 NS_DECL_NSITASKBARPREVIEWCONTROLLER
107 NS_IMETHODIMP
108 DefaultController::GetWidth(uint32_t* aWidth) {
109 RECT r;
110 ::GetClientRect(mWnd, &r);
111 *aWidth = r.right;
112 return NS_OK;
115 NS_IMETHODIMP
116 DefaultController::GetHeight(uint32_t* aHeight) {
117 RECT r;
118 ::GetClientRect(mWnd, &r);
119 *aHeight = r.bottom;
120 return NS_OK;
123 NS_IMETHODIMP
124 DefaultController::GetThumbnailAspectRatio(float* aThumbnailAspectRatio) {
125 uint32_t width, height;
126 GetWidth(&width);
127 GetHeight(&height);
128 if (!height) height = 1;
130 *aThumbnailAspectRatio = width / float(height);
131 return NS_OK;
134 NS_IMETHODIMP
135 DefaultController::RequestThumbnail(nsITaskbarPreviewCallback* aCallback,
136 uint32_t width, uint32_t height) {
137 return NS_OK;
140 NS_IMETHODIMP
141 DefaultController::RequestPreview(nsITaskbarPreviewCallback* aCallback) {
142 return NS_OK;
145 NS_IMETHODIMP
146 DefaultController::OnClose(void) {
147 MOZ_ASSERT_UNREACHABLE(
148 "OnClose should not be called for "
149 "TaskbarWindowPreviews");
150 return NS_OK;
153 NS_IMETHODIMP
154 DefaultController::OnActivate(bool* rAcceptActivation) {
155 *rAcceptActivation = true;
156 MOZ_ASSERT_UNREACHABLE(
157 "OnActivate should not be called for "
158 "TaskbarWindowPreviews");
159 return NS_OK;
162 NS_IMETHODIMP
163 DefaultController::OnClick(nsITaskbarPreviewButton* button) { return NS_OK; }
165 NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController)
166 } // namespace
168 namespace mozilla {
169 namespace widget {
171 ///////////////////////////////////////////////////////////////////////////////
172 // nsIWinTaskbar
174 NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar)
176 bool WinTaskbar::Initialize() {
177 if (mTaskbar) return true;
179 ::CoInitialize(nullptr);
180 HRESULT hr =
181 ::CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER,
182 IID_ITaskbarList4, (void**)&mTaskbar);
183 if (FAILED(hr)) return false;
185 hr = mTaskbar->HrInit();
186 if (FAILED(hr)) {
187 // This may fail with shell extensions like blackbox installed.
188 NS_WARNING("Unable to initialize taskbar");
189 NS_RELEASE(mTaskbar);
190 return false;
192 return true;
195 WinTaskbar::WinTaskbar() : mTaskbar(nullptr) {}
197 WinTaskbar::~WinTaskbar() {
198 if (mTaskbar) { // match successful Initialize() call
199 NS_RELEASE(mTaskbar);
200 ::CoUninitialize();
204 // static
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));
213 bool exists = false;
214 if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
215 nsAutoCString path;
216 if (NS_SUCCEEDED(profileDir->GetPersistentDescriptor(path))) {
217 nsAutoString id;
218 id.AppendInt(HashString(path));
219 if (!id.IsEmpty()) {
220 aAppUserModelId.Assign(id);
222 if (aPrivateBrowsing) {
223 aAppUserModelId.AppendLiteral(";PrivateBrowsingAUMID");
226 return true;
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
236 // zipped builds.
237 nsCOMPtr<nsIXULAppInfo> appInfo =
238 do_GetService("@mozilla.org/xre/app-info;1");
239 nsCString appName;
240 if (appInfo && NS_SUCCEEDED(appInfo->GetName(appName))) {
241 nsAutoString regKey;
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);
256 break;
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
264 // case.
265 if (aAppUserModelId.IsEmpty() && gDirServiceProvider) {
266 gDirServiceProvider->GetInstallHash(aAppUserModelId);
269 if (aPrivateBrowsing) {
270 aAppUserModelId.AppendLiteral(";PrivateBrowsingAUMID");
273 return !aAppUserModelId.IsEmpty();
276 // static
277 bool WinTaskbar::GetAppUserModelID(nsAString& aAppUserModelId,
278 bool aPrivateBrowsing) {
279 // If an ID has already been set then use that.
280 PWSTR id;
281 if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id))) {
282 aAppUserModelId.Assign(id);
283 CoTaskMemFree(id);
286 return GenerateAppUserModelID(aAppUserModelId, aPrivateBrowsing);
289 NS_IMETHODIMP
290 WinTaskbar::GetDefaultGroupId(nsAString& aDefaultGroupId) {
291 if (!GetAppUserModelID(aDefaultGroupId)) return NS_ERROR_UNEXPECTED;
293 return NS_OK;
296 NS_IMETHODIMP
297 WinTaskbar::GetDefaultPrivateGroupId(nsAString& aDefaultPrivateGroupId) {
298 if (!GetAppUserModelID(aDefaultPrivateGroupId, true))
299 return NS_ERROR_UNEXPECTED;
301 return NS_OK;
304 // (static) Called from AppShell
305 bool WinTaskbar::RegisterAppUserModelID() {
306 nsAutoString uid;
307 if (!GetAppUserModelID(uid)) return false;
309 return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid.get()));
312 NS_IMETHODIMP
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();
318 return NS_OK;
321 NS_IMETHODIMP
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();
339 if (NS_FAILED(rv)) {
340 return rv;
343 preview.forget(_retval);
345 return NS_OK;
348 NS_IMETHODIMP
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();
364 if (!preview) {
365 RefPtr<DefaultController> defaultController =
366 new DefaultController(toplevelHWND);
368 TaskbarWindowPreview* previewRaw = new TaskbarWindowPreview(
369 mTaskbar, defaultController, toplevelHWND, shell);
370 if (!previewRaw) {
371 return NS_ERROR_OUT_OF_MEMORY;
374 preview = previewRaw;
376 nsresult rv = previewRaw->Init();
377 if (NS_FAILED(rv)) {
378 return rv;
380 window->SetTaskbarPreview(preview);
383 preview.forget(_retval);
385 return NS_OK;
388 NS_IMETHODIMP
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);
398 NS_IMETHODIMP
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);
408 NS_IMETHODIMP
409 WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing,
410 nsIJumpListBuilder** aJumpListBuilder) {
411 nsAutoString aumid;
412 GenerateAppUserModelID(aumid, aPrivateBrowsing);
414 nsCOMPtr<nsIJumpListBuilder> builder = new JumpListBuilder(aumid);
415 if (!builder) {
416 return NS_ERROR_UNEXPECTED;
419 NS_IF_ADDREF(*aJumpListBuilder = builder);
420 return NS_OK;
423 NS_IMETHODIMP
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;
434 PROPVARIANT pv;
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.
444 return NS_OK;
446 aIdentifier.Assign(char16ptr_t(pv.pwszVal));
447 return NS_OK;
450 NS_IMETHODIMP
451 WinTaskbar::SetGroupIdForWindow(mozIDOMWindow* aParent,
452 const nsAString& aIdentifier) {
453 return SetWindowAppUserModelProp(aParent, nsString(aIdentifier));
456 NS_IMETHODIMP
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);
465 if (FAILED(hr)) {
466 return NS_ERROR_UNEXPECTED;
469 return NS_OK;
472 } // namespace widget
473 } // namespace mozilla