1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/windows/WinTaskbar.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,507 @@ 1.4 +/* vim: se cin sw=2 ts=2 et : */ 1.5 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.6 + * 1.7 + * This Source Code Form is subject to the terms of the Mozilla Public 1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +#include "WinTaskbar.h" 1.12 +#include "TaskbarPreview.h" 1.13 +#include <nsITaskbarPreviewController.h> 1.14 + 1.15 +#include <nsError.h> 1.16 +#include <nsCOMPtr.h> 1.17 +#include <nsIWidget.h> 1.18 +#include <nsIBaseWindow.h> 1.19 +#include <nsIObserverService.h> 1.20 +#include <nsServiceManagerUtils.h> 1.21 +#include <nsAutoPtr.h> 1.22 +#include "nsIXULAppInfo.h" 1.23 +#include "nsIJumpListBuilder.h" 1.24 +#include "nsUXThemeData.h" 1.25 +#include "nsWindow.h" 1.26 +#include "WinUtils.h" 1.27 +#include "TaskbarTabPreview.h" 1.28 +#include "TaskbarWindowPreview.h" 1.29 +#include "JumpListBuilder.h" 1.30 +#include "nsWidgetsCID.h" 1.31 +#include "nsPIDOMWindow.h" 1.32 +#include "nsAppDirectoryServiceDefs.h" 1.33 +#include "mozilla/Preferences.h" 1.34 +#include "mozilla/WindowsVersion.h" 1.35 +#include <io.h> 1.36 +#include <propvarutil.h> 1.37 +#include <propkey.h> 1.38 +#include <shellapi.h> 1.39 + 1.40 +const wchar_t kShellLibraryName[] = L"shell32.dll"; 1.41 + 1.42 +static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID); 1.43 + 1.44 +namespace { 1.45 + 1.46 +HWND 1.47 +GetHWNDFromDocShell(nsIDocShell *aShell) { 1.48 + nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(reinterpret_cast<nsISupports*>(aShell))); 1.49 + 1.50 + if (!baseWindow) 1.51 + return nullptr; 1.52 + 1.53 + nsCOMPtr<nsIWidget> widget; 1.54 + baseWindow->GetMainWidget(getter_AddRefs(widget)); 1.55 + 1.56 + return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr; 1.57 +} 1.58 + 1.59 +HWND 1.60 +GetHWNDFromDOMWindow(nsIDOMWindow *dw) { 1.61 + nsCOMPtr<nsIWidget> widget; 1.62 + 1.63 + nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(dw); 1.64 + if (!window) 1.65 + return nullptr; 1.66 + 1.67 + return GetHWNDFromDocShell(window->GetDocShell()); 1.68 +} 1.69 + 1.70 +nsresult 1.71 +SetWindowAppUserModelProp(nsIDOMWindow *aParent, 1.72 + const nsString & aIdentifier) { 1.73 + NS_ENSURE_ARG_POINTER(aParent); 1.74 + 1.75 + if (aIdentifier.IsEmpty()) 1.76 + return NS_ERROR_INVALID_ARG; 1.77 + 1.78 + HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT); 1.79 + 1.80 + if (!toplevelHWND) 1.81 + return NS_ERROR_INVALID_ARG; 1.82 + 1.83 + typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr) 1.84 + (HWND hwnd, REFIID riid, void** ppv); 1.85 + SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr; 1.86 + 1.87 + HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); 1.88 + funcGetProStore = (SHGetPropertyStoreForWindowPtr) 1.89 + GetProcAddress(hDLL, "SHGetPropertyStoreForWindow"); 1.90 + 1.91 + if (!funcGetProStore) { 1.92 + FreeLibrary(hDLL); 1.93 + return NS_ERROR_NO_INTERFACE; 1.94 + } 1.95 + 1.96 + IPropertyStore* pPropStore; 1.97 + if (FAILED(funcGetProStore(toplevelHWND, 1.98 + IID_PPV_ARGS(&pPropStore)))) { 1.99 + FreeLibrary(hDLL); 1.100 + return NS_ERROR_INVALID_ARG; 1.101 + } 1.102 + 1.103 + PROPVARIANT pv; 1.104 + if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) { 1.105 + pPropStore->Release(); 1.106 + FreeLibrary(hDLL); 1.107 + return NS_ERROR_UNEXPECTED; 1.108 + } 1.109 + 1.110 + nsresult rv = NS_OK; 1.111 + if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) || 1.112 + FAILED(pPropStore->Commit())) { 1.113 + rv = NS_ERROR_FAILURE; 1.114 + } 1.115 + 1.116 + PropVariantClear(&pv); 1.117 + pPropStore->Release(); 1.118 + FreeLibrary(hDLL); 1.119 + 1.120 + return rv; 1.121 +} 1.122 + 1.123 +/////////////////////////////////////////////////////////////////////////////// 1.124 +// default nsITaskbarPreviewController 1.125 + 1.126 +class DefaultController MOZ_FINAL : public nsITaskbarPreviewController 1.127 +{ 1.128 + HWND mWnd; 1.129 +public: 1.130 + DefaultController(HWND hWnd) 1.131 + : mWnd(hWnd) 1.132 + { 1.133 + } 1.134 + 1.135 + NS_DECL_ISUPPORTS 1.136 + NS_DECL_NSITASKBARPREVIEWCONTROLLER 1.137 +}; 1.138 + 1.139 +NS_IMETHODIMP 1.140 +DefaultController::GetWidth(uint32_t *aWidth) 1.141 +{ 1.142 + RECT r; 1.143 + ::GetClientRect(mWnd, &r); 1.144 + *aWidth = r.right; 1.145 + return NS_OK; 1.146 +} 1.147 + 1.148 +NS_IMETHODIMP 1.149 +DefaultController::GetHeight(uint32_t *aHeight) 1.150 +{ 1.151 + RECT r; 1.152 + ::GetClientRect(mWnd, &r); 1.153 + *aHeight = r.bottom; 1.154 + return NS_OK; 1.155 +} 1.156 + 1.157 +NS_IMETHODIMP 1.158 +DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) { 1.159 + uint32_t width, height; 1.160 + GetWidth(&width); 1.161 + GetHeight(&height); 1.162 + if (!height) 1.163 + height = 1; 1.164 + 1.165 + *aThumbnailAspectRatio = width/float(height); 1.166 + return NS_OK; 1.167 +} 1.168 + 1.169 +NS_IMETHODIMP 1.170 +DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) { 1.171 + *rDrawFrame = true; 1.172 + return NS_OK; 1.173 +} 1.174 + 1.175 +NS_IMETHODIMP 1.176 +DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) { 1.177 + *rDrawFrame = false; 1.178 + return NS_OK; 1.179 +} 1.180 + 1.181 +NS_IMETHODIMP 1.182 +DefaultController::OnClose(void) { 1.183 + NS_NOTREACHED("OnClose should not be called for TaskbarWindowPreviews"); 1.184 + return NS_OK; 1.185 +} 1.186 + 1.187 +NS_IMETHODIMP 1.188 +DefaultController::OnActivate(bool *rAcceptActivation) { 1.189 + *rAcceptActivation = true; 1.190 + NS_NOTREACHED("OnActivate should not be called for TaskbarWindowPreviews"); 1.191 + return NS_OK; 1.192 +} 1.193 + 1.194 +NS_IMETHODIMP 1.195 +DefaultController::OnClick(nsITaskbarPreviewButton *button) { 1.196 + return NS_OK; 1.197 +} 1.198 + 1.199 +NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController) 1.200 +} 1.201 + 1.202 +namespace mozilla { 1.203 +namespace widget { 1.204 + 1.205 +/////////////////////////////////////////////////////////////////////////////// 1.206 +// nsIWinTaskbar 1.207 + 1.208 +NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar) 1.209 + 1.210 +bool 1.211 +WinTaskbar::Initialize() { 1.212 + if (mTaskbar) 1.213 + return true; 1.214 + 1.215 + ::CoInitialize(nullptr); 1.216 + HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, 1.217 + nullptr, 1.218 + CLSCTX_INPROC_SERVER, 1.219 + IID_ITaskbarList4, 1.220 + (void**)&mTaskbar); 1.221 + if (FAILED(hr)) 1.222 + return false; 1.223 + 1.224 + hr = mTaskbar->HrInit(); 1.225 + if (FAILED(hr)) { 1.226 + // This may fail with shell extensions like blackbox installed. 1.227 + NS_WARNING("Unable to initialize taskbar"); 1.228 + NS_RELEASE(mTaskbar); 1.229 + return false; 1.230 + } 1.231 + return true; 1.232 +} 1.233 + 1.234 +WinTaskbar::WinTaskbar() 1.235 + : mTaskbar(nullptr) { 1.236 +} 1.237 + 1.238 +WinTaskbar::~WinTaskbar() { 1.239 + if (mTaskbar) { // match successful Initialize() call 1.240 + NS_RELEASE(mTaskbar); 1.241 + ::CoUninitialize(); 1.242 + } 1.243 +} 1.244 + 1.245 +// static 1.246 +bool 1.247 +WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) { 1.248 + // For win8 metro builds, we can't set this. The value is static 1.249 + // for the app. 1.250 + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { 1.251 + return false; 1.252 + } 1.253 + // If marked as such in prefs, use a hash of the profile path for the id 1.254 + // instead of the install path hash setup by the installer. 1.255 + bool useProfile = 1.256 + Preferences::GetBool("taskbar.grouping.useprofile", false); 1.257 + if (useProfile) { 1.258 + nsCOMPtr<nsIFile> profileDir; 1.259 + NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, 1.260 + getter_AddRefs(profileDir)); 1.261 + bool exists = false; 1.262 + if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) { 1.263 + nsAutoCString path; 1.264 + if (NS_SUCCEEDED(profileDir->GetNativePath(path))) { 1.265 + nsAutoString id; 1.266 + id.AppendInt(HashString(path)); 1.267 + if (!id.IsEmpty()) { 1.268 + aDefaultGroupId.Assign(id); 1.269 + return true; 1.270 + } 1.271 + } 1.272 + } 1.273 + } 1.274 + 1.275 + // The default value is set by the installer and is stored in the registry 1.276 + // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason 1.277 + // hash generation operation fails, the installer will not store a value in 1.278 + // the registry or set ids on shortcuts. A lack of an id can also occur for 1.279 + // zipped builds. We skip setting the global id in this case as well. 1.280 + nsCOMPtr<nsIXULAppInfo> appInfo = 1.281 + do_GetService("@mozilla.org/xre/app-info;1"); 1.282 + if (!appInfo) 1.283 + return false; 1.284 + 1.285 + nsCString appName; 1.286 + if (NS_FAILED(appInfo->GetName(appName))) { 1.287 + // We just won't register then, let Windows handle it. 1.288 + return false; 1.289 + } 1.290 + 1.291 + nsAutoString regKey; 1.292 + regKey.AssignLiteral("Software\\Mozilla\\"); 1.293 + AppendASCIItoUTF16(appName, regKey); 1.294 + regKey.AppendLiteral("\\TaskBarIDs"); 1.295 + 1.296 + WCHAR path[MAX_PATH]; 1.297 + if (GetModuleFileNameW(nullptr, path, MAX_PATH)) { 1.298 + wchar_t* slash = wcsrchr(path, '\\'); 1.299 + if (!slash) 1.300 + return false; 1.301 + *slash = '\0'; // no trailing slash 1.302 + 1.303 + // The hash is short, but users may customize this, so use a respectable 1.304 + // string buffer. 1.305 + wchar_t buf[256]; 1.306 + if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, 1.307 + regKey.get(), 1.308 + path, 1.309 + buf, 1.310 + sizeof buf)) { 1.311 + aDefaultGroupId.Assign(buf); 1.312 + } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER, 1.313 + regKey.get(), 1.314 + path, 1.315 + buf, 1.316 + sizeof buf)) { 1.317 + aDefaultGroupId.Assign(buf); 1.318 + } 1.319 + } 1.320 + 1.321 + return !aDefaultGroupId.IsEmpty(); 1.322 + 1.323 + return true; 1.324 +} 1.325 + 1.326 +/* readonly attribute AString defaultGroupId; */ 1.327 +NS_IMETHODIMP 1.328 +WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) { 1.329 + if (!GetAppUserModelID(aDefaultGroupId)) 1.330 + return NS_ERROR_UNEXPECTED; 1.331 + 1.332 + return NS_OK; 1.333 +} 1.334 + 1.335 +// (static) Called from AppShell 1.336 +bool 1.337 +WinTaskbar::RegisterAppUserModelID() { 1.338 + if (!IsWin7OrLater()) 1.339 + return false; 1.340 + 1.341 + if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { 1.342 + return false; 1.343 + } 1.344 + 1.345 + SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr; 1.346 + bool retVal = false; 1.347 + 1.348 + nsAutoString uid; 1.349 + if (!GetAppUserModelID(uid)) 1.350 + return false; 1.351 + 1.352 + HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); 1.353 + 1.354 + funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr) 1.355 + GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID"); 1.356 + 1.357 + if (!funcAppUserModelID) { 1.358 + ::FreeLibrary(hDLL); 1.359 + return false; 1.360 + } 1.361 + 1.362 + if (SUCCEEDED(funcAppUserModelID(uid.get()))) 1.363 + retVal = true; 1.364 + 1.365 + if (hDLL) 1.366 + ::FreeLibrary(hDLL); 1.367 + 1.368 + return retVal; 1.369 +} 1.370 + 1.371 +NS_IMETHODIMP 1.372 +WinTaskbar::GetAvailable(bool *aAvailable) { 1.373 + // ITaskbarList4::HrInit() may fail with shell extensions like blackbox 1.374 + // installed. Initialize early to return available=false in those cases. 1.375 + *aAvailable = IsWin7OrLater() && Initialize(); 1.376 + 1.377 + return NS_OK; 1.378 +} 1.379 + 1.380 +NS_IMETHODIMP 1.381 +WinTaskbar::CreateTaskbarTabPreview(nsIDocShell *shell, nsITaskbarPreviewController *controller, nsITaskbarTabPreview **_retval) { 1.382 + if (!Initialize()) 1.383 + return NS_ERROR_NOT_AVAILABLE; 1.384 + 1.385 + NS_ENSURE_ARG_POINTER(shell); 1.386 + NS_ENSURE_ARG_POINTER(controller); 1.387 + 1.388 + HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT); 1.389 + 1.390 + if (!toplevelHWND) 1.391 + return NS_ERROR_INVALID_ARG; 1.392 + 1.393 + nsRefPtr<TaskbarTabPreview> preview(new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell)); 1.394 + if (!preview) 1.395 + return NS_ERROR_OUT_OF_MEMORY; 1.396 + 1.397 + preview.forget(_retval); 1.398 + 1.399 + return NS_OK; 1.400 +} 1.401 + 1.402 +NS_IMETHODIMP 1.403 +WinTaskbar::GetTaskbarWindowPreview(nsIDocShell *shell, nsITaskbarWindowPreview **_retval) { 1.404 + if (!Initialize()) 1.405 + return NS_ERROR_NOT_AVAILABLE; 1.406 + 1.407 + NS_ENSURE_ARG_POINTER(shell); 1.408 + 1.409 + HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT); 1.410 + 1.411 + if (!toplevelHWND) 1.412 + return NS_ERROR_INVALID_ARG; 1.413 + 1.414 + nsWindow *window = WinUtils::GetNSWindowPtr(toplevelHWND); 1.415 + 1.416 + if (!window) 1.417 + return NS_ERROR_FAILURE; 1.418 + 1.419 + nsCOMPtr<nsITaskbarWindowPreview> preview = window->GetTaskbarPreview(); 1.420 + if (!preview) { 1.421 + nsRefPtr<DefaultController> defaultController = new DefaultController(toplevelHWND); 1.422 + preview = new TaskbarWindowPreview(mTaskbar, defaultController, toplevelHWND, shell); 1.423 + if (!preview) 1.424 + return NS_ERROR_OUT_OF_MEMORY; 1.425 + window->SetTaskbarPreview(preview); 1.426 + } 1.427 + 1.428 + preview.forget(_retval); 1.429 + 1.430 + return NS_OK; 1.431 +} 1.432 + 1.433 +NS_IMETHODIMP 1.434 +WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) { 1.435 + nsCOMPtr<nsITaskbarWindowPreview> preview; 1.436 + nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview)); 1.437 + NS_ENSURE_SUCCESS(rv, rv); 1.438 + 1.439 + return CallQueryInterface(preview, _retval); 1.440 +} 1.441 + 1.442 +NS_IMETHODIMP 1.443 +WinTaskbar::GetOverlayIconController(nsIDocShell *shell, 1.444 + nsITaskbarOverlayIconController **_retval) { 1.445 + nsCOMPtr<nsITaskbarWindowPreview> preview; 1.446 + nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview)); 1.447 + NS_ENSURE_SUCCESS(rv, rv); 1.448 + 1.449 + return CallQueryInterface(preview, _retval); 1.450 +} 1.451 + 1.452 +/* nsIJumpListBuilder createJumpListBuilder(); */ 1.453 +NS_IMETHODIMP 1.454 +WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) { 1.455 + nsresult rv; 1.456 + 1.457 + if (JumpListBuilder::sBuildingList) 1.458 + return NS_ERROR_ALREADY_INITIALIZED; 1.459 + 1.460 + nsCOMPtr<nsIJumpListBuilder> builder = 1.461 + do_CreateInstance(kJumpListBuilderCID, &rv); 1.462 + if (NS_FAILED(rv)) 1.463 + return NS_ERROR_UNEXPECTED; 1.464 + 1.465 + NS_IF_ADDREF(*aJumpListBuilder = builder); 1.466 + 1.467 + return NS_OK; 1.468 +} 1.469 + 1.470 +/* void setGroupIdForWindow (in nsIDOMWindow aParent, in AString aIdentifier); */ 1.471 +NS_IMETHODIMP 1.472 +WinTaskbar::SetGroupIdForWindow(nsIDOMWindow *aParent, 1.473 + const nsAString & aIdentifier) { 1.474 + return SetWindowAppUserModelProp(aParent, nsString(aIdentifier)); 1.475 +} 1.476 + 1.477 +/* void prepareFullScreen(in nsIDOMWindow aWindow, in boolean aFullScreen); */ 1.478 +NS_IMETHODIMP 1.479 +WinTaskbar::PrepareFullScreen(nsIDOMWindow *aWindow, bool aFullScreen) { 1.480 + NS_ENSURE_ARG_POINTER(aWindow); 1.481 + 1.482 + HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aWindow), GA_ROOT); 1.483 + if (!toplevelHWND) 1.484 + return NS_ERROR_INVALID_ARG; 1.485 + 1.486 + return PrepareFullScreenHWND(toplevelHWND, aFullScreen); 1.487 +} 1.488 + 1.489 +/* void prepareFullScreen(in voidPtr aWindow, in boolean aFullScreen); */ 1.490 +NS_IMETHODIMP 1.491 +WinTaskbar::PrepareFullScreenHWND(void *aHWND, bool aFullScreen) { 1.492 + if (!Initialize()) 1.493 + return NS_ERROR_NOT_AVAILABLE; 1.494 + 1.495 + NS_ENSURE_ARG_POINTER(aHWND); 1.496 + 1.497 + if (!::IsWindow((HWND)aHWND)) 1.498 + return NS_ERROR_INVALID_ARG; 1.499 + 1.500 + HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen); 1.501 + if (FAILED(hr)) { 1.502 + return NS_ERROR_UNEXPECTED; 1.503 + } 1.504 + 1.505 + return NS_OK; 1.506 +} 1.507 + 1.508 +} // namespace widget 1.509 +} // namespace mozilla 1.510 +