|
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 |