Merge mozilla-beta to b2g34. a=merge
[gecko.git] / widget / windows / WinTaskbar.cpp
blobb5c08e011c3d6080605733bb85461dd82b3fc6d1
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 "WinTaskbar.h"
9 #include "TaskbarPreview.h"
10 #include <nsITaskbarPreviewController.h>
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>
37 const wchar_t kShellLibraryName[] = L"shell32.dll";
39 static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID);
41 namespace {
43 HWND
44 GetHWNDFromDocShell(nsIDocShell *aShell) {
45 nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(reinterpret_cast<nsISupports*>(aShell)));
47 if (!baseWindow)
48 return nullptr;
50 nsCOMPtr<nsIWidget> widget;
51 baseWindow->GetMainWidget(getter_AddRefs(widget));
53 return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr;
56 HWND
57 GetHWNDFromDOMWindow(nsIDOMWindow *dw) {
58 nsCOMPtr<nsIWidget> widget;
60 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(dw);
61 if (!window)
62 return nullptr;
64 return GetHWNDFromDocShell(window->GetDocShell());
67 nsresult
68 SetWindowAppUserModelProp(nsIDOMWindow *aParent,
69 const nsString & aIdentifier) {
70 NS_ENSURE_ARG_POINTER(aParent);
72 if (aIdentifier.IsEmpty())
73 return NS_ERROR_INVALID_ARG;
75 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT);
77 if (!toplevelHWND)
78 return NS_ERROR_INVALID_ARG;
80 typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr)
81 (HWND hwnd, REFIID riid, void** ppv);
82 SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr;
84 HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
85 funcGetProStore = (SHGetPropertyStoreForWindowPtr)
86 GetProcAddress(hDLL, "SHGetPropertyStoreForWindow");
88 if (!funcGetProStore) {
89 FreeLibrary(hDLL);
90 return NS_ERROR_NO_INTERFACE;
93 IPropertyStore* pPropStore;
94 if (FAILED(funcGetProStore(toplevelHWND,
95 IID_PPV_ARGS(&pPropStore)))) {
96 FreeLibrary(hDLL);
97 return NS_ERROR_INVALID_ARG;
100 PROPVARIANT pv;
101 if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
102 pPropStore->Release();
103 FreeLibrary(hDLL);
104 return NS_ERROR_UNEXPECTED;
107 nsresult rv = NS_OK;
108 if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) ||
109 FAILED(pPropStore->Commit())) {
110 rv = NS_ERROR_FAILURE;
113 PropVariantClear(&pv);
114 pPropStore->Release();
115 FreeLibrary(hDLL);
117 return rv;
120 ///////////////////////////////////////////////////////////////////////////////
121 // default nsITaskbarPreviewController
123 class DefaultController MOZ_FINAL : public nsITaskbarPreviewController
125 ~DefaultController() {}
126 HWND mWnd;
127 public:
128 DefaultController(HWND hWnd)
129 : mWnd(hWnd)
133 NS_DECL_ISUPPORTS
134 NS_DECL_NSITASKBARPREVIEWCONTROLLER
137 NS_IMETHODIMP
138 DefaultController::GetWidth(uint32_t *aWidth)
140 RECT r;
141 ::GetClientRect(mWnd, &r);
142 *aWidth = r.right;
143 return NS_OK;
146 NS_IMETHODIMP
147 DefaultController::GetHeight(uint32_t *aHeight)
149 RECT r;
150 ::GetClientRect(mWnd, &r);
151 *aHeight = r.bottom;
152 return NS_OK;
155 NS_IMETHODIMP
156 DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) {
157 uint32_t width, height;
158 GetWidth(&width);
159 GetHeight(&height);
160 if (!height)
161 height = 1;
163 *aThumbnailAspectRatio = width/float(height);
164 return NS_OK;
167 NS_IMETHODIMP
168 DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) {
169 *rDrawFrame = true;
170 return NS_OK;
173 NS_IMETHODIMP
174 DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) {
175 *rDrawFrame = false;
176 return NS_OK;
179 NS_IMETHODIMP
180 DefaultController::OnClose(void) {
181 NS_NOTREACHED("OnClose should not be called for TaskbarWindowPreviews");
182 return NS_OK;
185 NS_IMETHODIMP
186 DefaultController::OnActivate(bool *rAcceptActivation) {
187 *rAcceptActivation = true;
188 NS_NOTREACHED("OnActivate should not be called for TaskbarWindowPreviews");
189 return NS_OK;
192 NS_IMETHODIMP
193 DefaultController::OnClick(nsITaskbarPreviewButton *button) {
194 return NS_OK;
197 NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController)
200 namespace mozilla {
201 namespace widget {
203 ///////////////////////////////////////////////////////////////////////////////
204 // nsIWinTaskbar
206 NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar)
208 bool
209 WinTaskbar::Initialize() {
210 if (mTaskbar)
211 return true;
213 ::CoInitialize(nullptr);
214 HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList,
215 nullptr,
216 CLSCTX_INPROC_SERVER,
217 IID_ITaskbarList4,
218 (void**)&mTaskbar);
219 if (FAILED(hr))
220 return false;
222 hr = mTaskbar->HrInit();
223 if (FAILED(hr)) {
224 // This may fail with shell extensions like blackbox installed.
225 NS_WARNING("Unable to initialize taskbar");
226 NS_RELEASE(mTaskbar);
227 return false;
229 return true;
232 WinTaskbar::WinTaskbar()
233 : mTaskbar(nullptr) {
236 WinTaskbar::~WinTaskbar() {
237 if (mTaskbar) { // match successful Initialize() call
238 NS_RELEASE(mTaskbar);
239 ::CoUninitialize();
243 // static
244 bool
245 WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) {
246 // For win8 metro builds, we can't set this. The value is static
247 // for the app.
248 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
249 return false;
251 // If marked as such in prefs, use a hash of the profile path for the id
252 // instead of the install path hash setup by the installer.
253 bool useProfile =
254 Preferences::GetBool("taskbar.grouping.useprofile", false);
255 if (useProfile) {
256 nsCOMPtr<nsIFile> profileDir;
257 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
258 getter_AddRefs(profileDir));
259 bool exists = false;
260 if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) {
261 nsAutoCString path;
262 if (NS_SUCCEEDED(profileDir->GetNativePath(path))) {
263 nsAutoString id;
264 id.AppendInt(HashString(path));
265 if (!id.IsEmpty()) {
266 aDefaultGroupId.Assign(id);
267 return true;
273 // The default value is set by the installer and is stored in the registry
274 // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason
275 // hash generation operation fails, the installer will not store a value in
276 // the registry or set ids on shortcuts. A lack of an id can also occur for
277 // zipped builds. We skip setting the global id in this case as well.
278 nsCOMPtr<nsIXULAppInfo> appInfo =
279 do_GetService("@mozilla.org/xre/app-info;1");
280 if (!appInfo)
281 return false;
283 nsCString appName;
284 if (NS_FAILED(appInfo->GetName(appName))) {
285 // We just won't register then, let Windows handle it.
286 return false;
289 nsAutoString regKey;
290 regKey.AssignLiteral("Software\\Mozilla\\");
291 AppendASCIItoUTF16(appName, regKey);
292 regKey.AppendLiteral("\\TaskBarIDs");
294 WCHAR path[MAX_PATH];
295 if (GetModuleFileNameW(nullptr, path, MAX_PATH)) {
296 wchar_t* slash = wcsrchr(path, '\\');
297 if (!slash)
298 return false;
299 *slash = '\0'; // no trailing slash
301 // The hash is short, but users may customize this, so use a respectable
302 // string buffer.
303 wchar_t buf[256];
304 if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE,
305 regKey.get(),
306 path,
307 buf,
308 sizeof buf)) {
309 aDefaultGroupId.Assign(buf);
310 } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER,
311 regKey.get(),
312 path,
313 buf,
314 sizeof buf)) {
315 aDefaultGroupId.Assign(buf);
319 return !aDefaultGroupId.IsEmpty();
321 return true;
324 /* readonly attribute AString defaultGroupId; */
325 NS_IMETHODIMP
326 WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) {
327 if (!GetAppUserModelID(aDefaultGroupId))
328 return NS_ERROR_UNEXPECTED;
330 return NS_OK;
333 // (static) Called from AppShell
334 bool
335 WinTaskbar::RegisterAppUserModelID() {
336 if (!IsWin7OrLater())
337 return false;
339 if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) {
340 return false;
343 SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr;
344 bool retVal = false;
346 nsAutoString uid;
347 if (!GetAppUserModelID(uid))
348 return false;
350 HMODULE hDLL = ::LoadLibraryW(kShellLibraryName);
352 funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr)
353 GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID");
355 if (!funcAppUserModelID) {
356 ::FreeLibrary(hDLL);
357 return false;
360 if (SUCCEEDED(funcAppUserModelID(uid.get())))
361 retVal = true;
363 if (hDLL)
364 ::FreeLibrary(hDLL);
366 return retVal;
369 NS_IMETHODIMP
370 WinTaskbar::GetAvailable(bool *aAvailable) {
371 // ITaskbarList4::HrInit() may fail with shell extensions like blackbox
372 // installed. Initialize early to return available=false in those cases.
373 *aAvailable = IsWin7OrLater() && Initialize();
375 return NS_OK;
378 NS_IMETHODIMP
379 WinTaskbar::CreateTaskbarTabPreview(nsIDocShell *shell, nsITaskbarPreviewController *controller, nsITaskbarTabPreview **_retval) {
380 if (!Initialize())
381 return NS_ERROR_NOT_AVAILABLE;
383 NS_ENSURE_ARG_POINTER(shell);
384 NS_ENSURE_ARG_POINTER(controller);
386 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
388 if (!toplevelHWND)
389 return NS_ERROR_INVALID_ARG;
391 nsRefPtr<TaskbarTabPreview> preview(new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell));
392 if (!preview)
393 return NS_ERROR_OUT_OF_MEMORY;
395 preview.forget(_retval);
397 return NS_OK;
400 NS_IMETHODIMP
401 WinTaskbar::GetTaskbarWindowPreview(nsIDocShell *shell, nsITaskbarWindowPreview **_retval) {
402 if (!Initialize())
403 return NS_ERROR_NOT_AVAILABLE;
405 NS_ENSURE_ARG_POINTER(shell);
407 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT);
409 if (!toplevelHWND)
410 return NS_ERROR_INVALID_ARG;
412 nsWindow *window = WinUtils::GetNSWindowPtr(toplevelHWND);
414 if (!window)
415 return NS_ERROR_FAILURE;
417 nsCOMPtr<nsITaskbarWindowPreview> preview = window->GetTaskbarPreview();
418 if (!preview) {
419 nsRefPtr<DefaultController> defaultController = new DefaultController(toplevelHWND);
420 preview = new TaskbarWindowPreview(mTaskbar, defaultController, toplevelHWND, shell);
421 if (!preview)
422 return NS_ERROR_OUT_OF_MEMORY;
423 window->SetTaskbarPreview(preview);
426 preview.forget(_retval);
428 return NS_OK;
431 NS_IMETHODIMP
432 WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) {
433 nsCOMPtr<nsITaskbarWindowPreview> preview;
434 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
435 NS_ENSURE_SUCCESS(rv, rv);
437 return CallQueryInterface(preview, _retval);
440 NS_IMETHODIMP
441 WinTaskbar::GetOverlayIconController(nsIDocShell *shell,
442 nsITaskbarOverlayIconController **_retval) {
443 nsCOMPtr<nsITaskbarWindowPreview> preview;
444 nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview));
445 NS_ENSURE_SUCCESS(rv, rv);
447 return CallQueryInterface(preview, _retval);
450 /* nsIJumpListBuilder createJumpListBuilder(); */
451 NS_IMETHODIMP
452 WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) {
453 nsresult rv;
455 if (JumpListBuilder::sBuildingList)
456 return NS_ERROR_ALREADY_INITIALIZED;
458 nsCOMPtr<nsIJumpListBuilder> builder =
459 do_CreateInstance(kJumpListBuilderCID, &rv);
460 if (NS_FAILED(rv))
461 return NS_ERROR_UNEXPECTED;
463 NS_IF_ADDREF(*aJumpListBuilder = builder);
465 return NS_OK;
468 /* void setGroupIdForWindow (in nsIDOMWindow aParent, in AString aIdentifier); */
469 NS_IMETHODIMP
470 WinTaskbar::SetGroupIdForWindow(nsIDOMWindow *aParent,
471 const nsAString & aIdentifier) {
472 return SetWindowAppUserModelProp(aParent, nsString(aIdentifier));
475 /* void prepareFullScreen(in nsIDOMWindow aWindow, in boolean aFullScreen); */
476 NS_IMETHODIMP
477 WinTaskbar::PrepareFullScreen(nsIDOMWindow *aWindow, bool aFullScreen) {
478 NS_ENSURE_ARG_POINTER(aWindow);
480 HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aWindow), GA_ROOT);
481 if (!toplevelHWND)
482 return NS_ERROR_INVALID_ARG;
484 return PrepareFullScreenHWND(toplevelHWND, aFullScreen);
487 /* void prepareFullScreen(in voidPtr aWindow, in boolean aFullScreen); */
488 NS_IMETHODIMP
489 WinTaskbar::PrepareFullScreenHWND(void *aHWND, bool aFullScreen) {
490 if (!Initialize())
491 return NS_ERROR_NOT_AVAILABLE;
493 NS_ENSURE_ARG_POINTER(aHWND);
495 if (!::IsWindow((HWND)aHWND))
496 return NS_ERROR_INVALID_ARG;
498 HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen);
499 if (FAILED(hr)) {
500 return NS_ERROR_UNEXPECTED;
503 return NS_OK;
506 } // namespace widget
507 } // namespace mozilla