widget/windows/WinTaskbar.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:fda041ae078e
1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 *
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/. */
7
8 #include "WinTaskbar.h"
9 #include "TaskbarPreview.h"
10 #include <nsITaskbarPreviewController.h>
11
12 #include <nsError.h>
13 #include <nsCOMPtr.h>
14 #include <nsIWidget.h>
15 #include <nsIBaseWindow.h>
16 #include <nsIObserverService.h>
17 #include <nsServiceManagerUtils.h>
18 #include <nsAutoPtr.h>
19 #include "nsIXULAppInfo.h"
20 #include "nsIJumpListBuilder.h"
21 #include "nsUXThemeData.h"
22 #include "nsWindow.h"
23 #include "WinUtils.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 "mozilla/WindowsVersion.h"
32 #include <io.h>
33 #include <propvarutil.h>
34 #include <propkey.h>
35 #include <shellapi.h>
36
37 const wchar_t kShellLibraryName[] = L"shell32.dll";
38
39 static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID);
40
41 namespace {
42
43 HWND
44 GetHWNDFromDocShell(nsIDocShell *aShell) {
45 nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(reinterpret_cast<nsISupports*>(aShell)));
46
47 if (!baseWindow)
48 return nullptr;
49
50 nsCOMPtr<nsIWidget> widget;
51 baseWindow->GetMainWidget(getter_AddRefs(widget));
52
53 return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
54 }
55
56 HWND
57 GetHWNDFromDOMWindow(nsIDOMWindow *dw) {
58 nsCOMPtr<nsIWidget> widget;
59
60 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(dw);
61 if (!window)
62 return nullptr;
63
64 return GetHWNDFromDocShell(window->GetDocShell());
65 }
66
67 nsresult
68 SetWindowAppUserModelProp(nsIDOMWindow *aParent,
69 const nsString & aIdentifier) {
70 NS_ENSURE_ARG_POINTER(aParent);
71
72 if (aIdentifier.IsEmpty())
73 return NS_ERROR_INVALID_ARG;
74
75 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT);
76
77 if (!toplevelHWND)
78 return NS_ERROR_INVALID_ARG;
79
80 typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr)
81 (HWND hwnd, REFIID riid, void** ppv);
82 SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr;
83
84 HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
85 funcGetProStore = (SHGetPropertyStoreForWindowPtr)
86 GetProcAddress(hDLL, "SHGetPropertyStoreForWindow");
87
88 if (!funcGetProStore) {
89 FreeLibrary(hDLL);
90 return NS_ERROR_NO_INTERFACE;
91 }
92
93 IPropertyStore* pPropStore;
94 if (FAILED(funcGetProStore(toplevelHWND,
95 IID_PPV_ARGS(&pPropStore)))) {
96 FreeLibrary(hDLL);
97 return NS_ERROR_INVALID_ARG;
98 }
99
100 PROPVARIANT pv;
101 if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
102 pPropStore->Release();
103 FreeLibrary(hDLL);
104 return NS_ERROR_UNEXPECTED;
105 }
106
107 nsresult rv = NS_OK;
108 if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) ||
109 FAILED(pPropStore->Commit())) {
110 rv = NS_ERROR_FAILURE;
111 }
112
113 PropVariantClear(&pv);
114 pPropStore->Release();
115 FreeLibrary(hDLL);
116
117 return rv;
118 }
119
120 ///////////////////////////////////////////////////////////////////////////////
121 // default nsITaskbarPreviewController
122
123 class DefaultController MOZ_FINAL : public nsITaskbarPreviewController
124 {
125 HWND mWnd;
126 public:
127 DefaultController(HWND hWnd)
128 : mWnd(hWnd)
129 {
130 }
131
132 NS_DECL_ISUPPORTS
133 NS_DECL_NSITASKBARPREVIEWCONTROLLER
134 };
135
136 NS_IMETHODIMP
137 DefaultController::GetWidth(uint32_t *aWidth)
138 {
139 RECT r;
140 ::GetClientRect(mWnd, &r);
141 *aWidth = r.right;
142 return NS_OK;
143 }
144
145 NS_IMETHODIMP
146 DefaultController::GetHeight(uint32_t *aHeight)
147 {
148 RECT r;
149 ::GetClientRect(mWnd, &r);
150 *aHeight = r.bottom;
151 return NS_OK;
152 }
153
154 NS_IMETHODIMP
155 DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) {
156 uint32_t width, height;
157 GetWidth(&width);
158 GetHeight(&height);
159 if (!height)
160 height = 1;
161
162 *aThumbnailAspectRatio = width/float(height);
163 return NS_OK;
164 }
165
166 NS_IMETHODIMP
167 DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) {
168 *rDrawFrame = true;
169 return NS_OK;
170 }
171
172 NS_IMETHODIMP
173 DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) {
174 *rDrawFrame = false;
175 return NS_OK;
176 }
177
178 NS_IMETHODIMP
179 DefaultController::OnClose(void) {
180 NS_NOTREACHED("OnClose should not be called for TaskbarWindowPreviews");
181 return NS_OK;
182 }
183
184 NS_IMETHODIMP
185 DefaultController::OnActivate(bool *rAcceptActivation) {
186 *rAcceptActivation = true;
187 NS_NOTREACHED("OnActivate should not be called for TaskbarWindowPreviews");
188 return NS_OK;
189 }
190
191 NS_IMETHODIMP
192 DefaultController::OnClick(nsITaskbarPreviewButton *button) {
193 return NS_OK;
194 }
195
196 NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController)
197 }
198
199 namespace mozilla {
200 namespace widget {
201
202 ///////////////////////////////////////////////////////////////////////////////
203 // nsIWinTaskbar
204
205 NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar)
206
207 bool
208 WinTaskbar::Initialize() {
209 if (mTaskbar)
210 return true;
211
212 ::CoInitialize(nullptr);
213 HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList,
214 nullptr,
215 CLSCTX_INPROC_SERVER,
216 IID_ITaskbarList4,
217 (void**)&mTaskbar);
218 if (FAILED(hr))
219 return false;
220
221 hr = mTaskbar->HrInit();
222 if (FAILED(hr)) {
223 // This may fail with shell extensions like blackbox installed.
224 NS_WARNING("Unable to initialize taskbar");
225 NS_RELEASE(mTaskbar);
226 return false;
227 }
228 return true;
229 }
230
231 WinTaskbar::WinTaskbar()
232 : mTaskbar(nullptr) {
233 }
234
235 WinTaskbar::~WinTaskbar() {
236 if (mTaskbar) { // match successful Initialize() call
237 NS_RELEASE(mTaskbar);
238 ::CoUninitialize();
239 }
240 }
241
242 // static
243 bool
244 WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) {
245 // For win8 metro builds, we can't set this. The value is static
246 // for the app.
247 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
248 return false;
249 }
250 // If marked as such in prefs, use a hash of the profile path for the id
251 // instead of the install path hash setup by the installer.
252 bool useProfile =
253 Preferences::GetBool("taskbar.grouping.useprofile", false);
254 if (useProfile) {
255 nsCOMPtr<nsIFile> profileDir;
256 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
257 getter_AddRefs(profileDir));
258 bool exists = false;
259 if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
260 nsAutoCString path;
261 if (NS_SUCCEEDED(profileDir->GetNativePath(path))) {
262 nsAutoString id;
263 id.AppendInt(HashString(path));
264 if (!id.IsEmpty()) {
265 aDefaultGroupId.Assign(id);
266 return true;
267 }
268 }
269 }
270 }
271
272 // The default value is set by the installer and is stored in the registry
273 // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
274 // hash generation operation fails, the installer will not store a value in
275 // the registry or set ids on shortcuts. A lack of an id can also occur for
276 // zipped builds. We skip setting the global id in this case as well.
277 nsCOMPtr<nsIXULAppInfo> appInfo =
278 do_GetService("@mozilla.org/xre/app-info;1");
279 if (!appInfo)
280 return false;
281
282 nsCString appName;
283 if (NS_FAILED(appInfo->GetName(appName))) {
284 // We just won't register then, let Windows handle it.
285 return false;
286 }
287
288 nsAutoString regKey;
289 regKey.AssignLiteral("Software\\Mozilla\\");
290 AppendASCIItoUTF16(appName, regKey);
291 regKey.AppendLiteral("\\TaskBarIDs");
292
293 WCHAR path[MAX_PATH];
294 if (GetModuleFileNameW(nullptr, path, MAX_PATH)) {
295 wchar_t* slash = wcsrchr(path, '\\');
296 if (!slash)
297 return false;
298 *slash = '\0'; // no trailing slash
299
300 // The hash is short, but users may customize this, so use a respectable
301 // string buffer.
302 wchar_t buf[256];
303 if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE,
304 regKey.get(),
305 path,
306 buf,
307 sizeof buf)) {
308 aDefaultGroupId.Assign(buf);
309 } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER,
310 regKey.get(),
311 path,
312 buf,
313 sizeof buf)) {
314 aDefaultGroupId.Assign(buf);
315 }
316 }
317
318 return !aDefaultGroupId.IsEmpty();
319
320 return true;
321 }
322
323 /* readonly attribute AString defaultGroupId; */
324 NS_IMETHODIMP
325 WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) {
326 if (!GetAppUserModelID(aDefaultGroupId))
327 return NS_ERROR_UNEXPECTED;
328
329 return NS_OK;
330 }
331
332 // (static) Called from AppShell
333 bool
334 WinTaskbar::RegisterAppUserModelID() {
335 if (!IsWin7OrLater())
336 return false;
337
338 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
339 return false;
340 }
341
342 SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
343 bool retVal = false;
344
345 nsAutoString uid;
346 if (!GetAppUserModelID(uid))
347 return false;
348
349 HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
350
351 funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
352 GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
353
354 if (!funcAppUserModelID) {
355 ::FreeLibrary(hDLL);
356 return false;
357 }
358
359 if (SUCCEEDED(funcAppUserModelID(uid.get())))
360 retVal = true;
361
362 if (hDLL)
363 ::FreeLibrary(hDLL);
364
365 return retVal;
366 }
367
368 NS_IMETHODIMP
369 WinTaskbar::GetAvailable(bool *aAvailable) {
370 // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
371 // installed. Initialize early to return available=false in those cases.
372 *aAvailable = IsWin7OrLater() && Initialize();
373
374 return NS_OK;
375 }
376
377 NS_IMETHODIMP
378 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell *shell, nsITaskbarPreviewController *controller, nsITaskbarTabPreview **_retval) {
379 if (!Initialize())
380 return NS_ERROR_NOT_AVAILABLE;
381
382 NS_ENSURE_ARG_POINTER(shell);
383 NS_ENSURE_ARG_POINTER(controller);
384
385 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
386
387 if (!toplevelHWND)
388 return NS_ERROR_INVALID_ARG;
389
390 nsRefPtr<TaskbarTabPreview> preview(new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell));
391 if (!preview)
392 return NS_ERROR_OUT_OF_MEMORY;
393
394 preview.forget(_retval);
395
396 return NS_OK;
397 }
398
399 NS_IMETHODIMP
400 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell *shell, nsITaskbarWindowPreview **_retval) {
401 if (!Initialize())
402 return NS_ERROR_NOT_AVAILABLE;
403
404 NS_ENSURE_ARG_POINTER(shell);
405
406 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
407
408 if (!toplevelHWND)
409 return NS_ERROR_INVALID_ARG;
410
411 nsWindow *window = WinUtils::GetNSWindowPtr(toplevelHWND);
412
413 if (!window)
414 return NS_ERROR_FAILURE;
415
416 nsCOMPtr<nsITaskbarWindowPreview> preview = window->GetTaskbarPreview();
417 if (!preview) {
418 nsRefPtr<DefaultController> defaultController = new DefaultController(toplevelHWND);
419 preview = new TaskbarWindowPreview(mTaskbar, defaultController, toplevelHWND, shell);
420 if (!preview)
421 return NS_ERROR_OUT_OF_MEMORY;
422 window->SetTaskbarPreview(preview);
423 }
424
425 preview.forget(_retval);
426
427 return NS_OK;
428 }
429
430 NS_IMETHODIMP
431 WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) {
432 nsCOMPtr<nsITaskbarWindowPreview> preview;
433 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
434 NS_ENSURE_SUCCESS(rv, rv);
435
436 return CallQueryInterface(preview, _retval);
437 }
438
439 NS_IMETHODIMP
440 WinTaskbar::GetOverlayIconController(nsIDocShell *shell,
441 nsITaskbarOverlayIconController **_retval) {
442 nsCOMPtr<nsITaskbarWindowPreview> preview;
443 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
444 NS_ENSURE_SUCCESS(rv, rv);
445
446 return CallQueryInterface(preview, _retval);
447 }
448
449 /* nsIJumpListBuilder createJumpListBuilder(); */
450 NS_IMETHODIMP
451 WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) {
452 nsresult rv;
453
454 if (JumpListBuilder::sBuildingList)
455 return NS_ERROR_ALREADY_INITIALIZED;
456
457 nsCOMPtr<nsIJumpListBuilder> builder =
458 do_CreateInstance(kJumpListBuilderCID, &rv);
459 if (NS_FAILED(rv))
460 return NS_ERROR_UNEXPECTED;
461
462 NS_IF_ADDREF(*aJumpListBuilder = builder);
463
464 return NS_OK;
465 }
466
467 /* void setGroupIdForWindow (in nsIDOMWindow aParent, in AString aIdentifier); */
468 NS_IMETHODIMP
469 WinTaskbar::SetGroupIdForWindow(nsIDOMWindow *aParent,
470 const nsAString & aIdentifier) {
471 return SetWindowAppUserModelProp(aParent, nsString(aIdentifier));
472 }
473
474 /* void prepareFullScreen(in nsIDOMWindow aWindow, in boolean aFullScreen); */
475 NS_IMETHODIMP
476 WinTaskbar::PrepareFullScreen(nsIDOMWindow *aWindow, bool aFullScreen) {
477 NS_ENSURE_ARG_POINTER(aWindow);
478
479 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aWindow), GA_ROOT);
480 if (!toplevelHWND)
481 return NS_ERROR_INVALID_ARG;
482
483 return PrepareFullScreenHWND(toplevelHWND, aFullScreen);
484 }
485
486 /* void prepareFullScreen(in voidPtr aWindow, in boolean aFullScreen); */
487 NS_IMETHODIMP
488 WinTaskbar::PrepareFullScreenHWND(void *aHWND, bool aFullScreen) {
489 if (!Initialize())
490 return NS_ERROR_NOT_AVAILABLE;
491
492 NS_ENSURE_ARG_POINTER(aHWND);
493
494 if (!::IsWindow((HWND)aHWND))
495 return NS_ERROR_INVALID_ARG;
496
497 HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen);
498 if (FAILED(hr)) {
499 return NS_ERROR_UNEXPECTED;
500 }
501
502 return NS_OK;
503 }
504
505 } // namespace widget
506 } // namespace mozilla
507

mercurial