michael@0: /* vim: se cin sw=2 ts=2 et : */ michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "WinTaskbar.h" michael@0: #include "TaskbarPreview.h" michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "nsIXULAppInfo.h" michael@0: #include "nsIJumpListBuilder.h" michael@0: #include "nsUXThemeData.h" michael@0: #include "nsWindow.h" michael@0: #include "WinUtils.h" michael@0: #include "TaskbarTabPreview.h" michael@0: #include "TaskbarWindowPreview.h" michael@0: #include "JumpListBuilder.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsAppDirectoryServiceDefs.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: const wchar_t kShellLibraryName[] = L"shell32.dll"; michael@0: michael@0: static NS_DEFINE_CID(kJumpListBuilderCID, NS_WIN_JUMPLISTBUILDER_CID); michael@0: michael@0: namespace { michael@0: michael@0: HWND michael@0: GetHWNDFromDocShell(nsIDocShell *aShell) { michael@0: nsCOMPtr baseWindow(do_QueryInterface(reinterpret_cast(aShell))); michael@0: michael@0: if (!baseWindow) michael@0: return nullptr; michael@0: michael@0: nsCOMPtr widget; michael@0: baseWindow->GetMainWidget(getter_AddRefs(widget)); michael@0: michael@0: return widget ? (HWND)widget->GetNativeData(NS_NATIVE_WINDOW) : nullptr; michael@0: } michael@0: michael@0: HWND michael@0: GetHWNDFromDOMWindow(nsIDOMWindow *dw) { michael@0: nsCOMPtr widget; michael@0: michael@0: nsCOMPtr window = do_QueryInterface(dw); michael@0: if (!window) michael@0: return nullptr; michael@0: michael@0: return GetHWNDFromDocShell(window->GetDocShell()); michael@0: } michael@0: michael@0: nsresult michael@0: SetWindowAppUserModelProp(nsIDOMWindow *aParent, michael@0: const nsString & aIdentifier) { michael@0: NS_ENSURE_ARG_POINTER(aParent); michael@0: michael@0: if (aIdentifier.IsEmpty()) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aParent), GA_ROOT); michael@0: michael@0: if (!toplevelHWND) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: typedef HRESULT (WINAPI * SHGetPropertyStoreForWindowPtr) michael@0: (HWND hwnd, REFIID riid, void** ppv); michael@0: SHGetPropertyStoreForWindowPtr funcGetProStore = nullptr; michael@0: michael@0: HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); michael@0: funcGetProStore = (SHGetPropertyStoreForWindowPtr) michael@0: GetProcAddress(hDLL, "SHGetPropertyStoreForWindow"); michael@0: michael@0: if (!funcGetProStore) { michael@0: FreeLibrary(hDLL); michael@0: return NS_ERROR_NO_INTERFACE; michael@0: } michael@0: michael@0: IPropertyStore* pPropStore; michael@0: if (FAILED(funcGetProStore(toplevelHWND, michael@0: IID_PPV_ARGS(&pPropStore)))) { michael@0: FreeLibrary(hDLL); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: PROPVARIANT pv; michael@0: if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) { michael@0: pPropStore->Release(); michael@0: FreeLibrary(hDLL); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (FAILED(pPropStore->SetValue(PKEY_AppUserModel_ID, pv)) || michael@0: FAILED(pPropStore->Commit())) { michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: PropVariantClear(&pv); michael@0: pPropStore->Release(); michael@0: FreeLibrary(hDLL); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: // default nsITaskbarPreviewController michael@0: michael@0: class DefaultController MOZ_FINAL : public nsITaskbarPreviewController michael@0: { michael@0: HWND mWnd; michael@0: public: michael@0: DefaultController(HWND hWnd) michael@0: : mWnd(hWnd) michael@0: { michael@0: } michael@0: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSITASKBARPREVIEWCONTROLLER michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::GetWidth(uint32_t *aWidth) michael@0: { michael@0: RECT r; michael@0: ::GetClientRect(mWnd, &r); michael@0: *aWidth = r.right; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::GetHeight(uint32_t *aHeight) michael@0: { michael@0: RECT r; michael@0: ::GetClientRect(mWnd, &r); michael@0: *aHeight = r.bottom; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::GetThumbnailAspectRatio(float *aThumbnailAspectRatio) { michael@0: uint32_t width, height; michael@0: GetWidth(&width); michael@0: GetHeight(&height); michael@0: if (!height) michael@0: height = 1; michael@0: michael@0: *aThumbnailAspectRatio = width/float(height); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::DrawPreview(nsISupports *ctx, bool *rDrawFrame) { michael@0: *rDrawFrame = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::DrawThumbnail(nsISupports *ctx, uint32_t width, uint32_t height, bool *rDrawFrame) { michael@0: *rDrawFrame = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::OnClose(void) { michael@0: NS_NOTREACHED("OnClose should not be called for TaskbarWindowPreviews"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::OnActivate(bool *rAcceptActivation) { michael@0: *rAcceptActivation = true; michael@0: NS_NOTREACHED("OnActivate should not be called for TaskbarWindowPreviews"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DefaultController::OnClick(nsITaskbarPreviewButton *button) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(DefaultController, nsITaskbarPreviewController) michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: /////////////////////////////////////////////////////////////////////////////// michael@0: // nsIWinTaskbar michael@0: michael@0: NS_IMPL_ISUPPORTS(WinTaskbar, nsIWinTaskbar) michael@0: michael@0: bool michael@0: WinTaskbar::Initialize() { michael@0: if (mTaskbar) michael@0: return true; michael@0: michael@0: ::CoInitialize(nullptr); michael@0: HRESULT hr = ::CoCreateInstance(CLSID_TaskbarList, michael@0: nullptr, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_ITaskbarList4, michael@0: (void**)&mTaskbar); michael@0: if (FAILED(hr)) michael@0: return false; michael@0: michael@0: hr = mTaskbar->HrInit(); michael@0: if (FAILED(hr)) { michael@0: // This may fail with shell extensions like blackbox installed. michael@0: NS_WARNING("Unable to initialize taskbar"); michael@0: NS_RELEASE(mTaskbar); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: WinTaskbar::WinTaskbar() michael@0: : mTaskbar(nullptr) { michael@0: } michael@0: michael@0: WinTaskbar::~WinTaskbar() { michael@0: if (mTaskbar) { // match successful Initialize() call michael@0: NS_RELEASE(mTaskbar); michael@0: ::CoUninitialize(); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: WinTaskbar::GetAppUserModelID(nsAString & aDefaultGroupId) { michael@0: // For win8 metro builds, we can't set this. The value is static michael@0: // for the app. michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: return false; michael@0: } michael@0: // If marked as such in prefs, use a hash of the profile path for the id michael@0: // instead of the install path hash setup by the installer. michael@0: bool useProfile = michael@0: Preferences::GetBool("taskbar.grouping.useprofile", false); michael@0: if (useProfile) { michael@0: nsCOMPtr profileDir; michael@0: NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, michael@0: getter_AddRefs(profileDir)); michael@0: bool exists = false; michael@0: if (profileDir && NS_SUCCEEDED(profileDir->Exists(&exists)) && exists) { michael@0: nsAutoCString path; michael@0: if (NS_SUCCEEDED(profileDir->GetNativePath(path))) { michael@0: nsAutoString id; michael@0: id.AppendInt(HashString(path)); michael@0: if (!id.IsEmpty()) { michael@0: aDefaultGroupId.Assign(id); michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // The default value is set by the installer and is stored in the registry michael@0: // under (HKLM||HKCU)/Software/Mozilla/Firefox/TaskBarIDs. If for any reason michael@0: // hash generation operation fails, the installer will not store a value in michael@0: // the registry or set ids on shortcuts. A lack of an id can also occur for michael@0: // zipped builds. We skip setting the global id in this case as well. michael@0: nsCOMPtr appInfo = michael@0: do_GetService("@mozilla.org/xre/app-info;1"); michael@0: if (!appInfo) michael@0: return false; michael@0: michael@0: nsCString appName; michael@0: if (NS_FAILED(appInfo->GetName(appName))) { michael@0: // We just won't register then, let Windows handle it. michael@0: return false; michael@0: } michael@0: michael@0: nsAutoString regKey; michael@0: regKey.AssignLiteral("Software\\Mozilla\\"); michael@0: AppendASCIItoUTF16(appName, regKey); michael@0: regKey.AppendLiteral("\\TaskBarIDs"); michael@0: michael@0: WCHAR path[MAX_PATH]; michael@0: if (GetModuleFileNameW(nullptr, path, MAX_PATH)) { michael@0: wchar_t* slash = wcsrchr(path, '\\'); michael@0: if (!slash) michael@0: return false; michael@0: *slash = '\0'; // no trailing slash michael@0: michael@0: // The hash is short, but users may customize this, so use a respectable michael@0: // string buffer. michael@0: wchar_t buf[256]; michael@0: if (WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, michael@0: regKey.get(), michael@0: path, michael@0: buf, michael@0: sizeof buf)) { michael@0: aDefaultGroupId.Assign(buf); michael@0: } else if (WinUtils::GetRegistryKey(HKEY_CURRENT_USER, michael@0: regKey.get(), michael@0: path, michael@0: buf, michael@0: sizeof buf)) { michael@0: aDefaultGroupId.Assign(buf); michael@0: } michael@0: } michael@0: michael@0: return !aDefaultGroupId.IsEmpty(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* readonly attribute AString defaultGroupId; */ michael@0: NS_IMETHODIMP michael@0: WinTaskbar::GetDefaultGroupId(nsAString & aDefaultGroupId) { michael@0: if (!GetAppUserModelID(aDefaultGroupId)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // (static) Called from AppShell michael@0: bool michael@0: WinTaskbar::RegisterAppUserModelID() { michael@0: if (!IsWin7OrLater()) michael@0: return false; michael@0: michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: return false; michael@0: } michael@0: michael@0: SetCurrentProcessExplicitAppUserModelIDPtr funcAppUserModelID = nullptr; michael@0: bool retVal = false; michael@0: michael@0: nsAutoString uid; michael@0: if (!GetAppUserModelID(uid)) michael@0: return false; michael@0: michael@0: HMODULE hDLL = ::LoadLibraryW(kShellLibraryName); michael@0: michael@0: funcAppUserModelID = (SetCurrentProcessExplicitAppUserModelIDPtr) michael@0: GetProcAddress(hDLL, "SetCurrentProcessExplicitAppUserModelID"); michael@0: michael@0: if (!funcAppUserModelID) { michael@0: ::FreeLibrary(hDLL); michael@0: return false; michael@0: } michael@0: michael@0: if (SUCCEEDED(funcAppUserModelID(uid.get()))) michael@0: retVal = true; michael@0: michael@0: if (hDLL) michael@0: ::FreeLibrary(hDLL); michael@0: michael@0: return retVal; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WinTaskbar::GetAvailable(bool *aAvailable) { michael@0: // ITaskbarList4::HrInit() may fail with shell extensions like blackbox michael@0: // installed. Initialize early to return available=false in those cases. michael@0: *aAvailable = IsWin7OrLater() && Initialize(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WinTaskbar::CreateTaskbarTabPreview(nsIDocShell *shell, nsITaskbarPreviewController *controller, nsITaskbarTabPreview **_retval) { michael@0: if (!Initialize()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(shell); michael@0: NS_ENSURE_ARG_POINTER(controller); michael@0: michael@0: HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT); michael@0: michael@0: if (!toplevelHWND) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsRefPtr preview(new TaskbarTabPreview(mTaskbar, controller, toplevelHWND, shell)); michael@0: if (!preview) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: preview.forget(_retval); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WinTaskbar::GetTaskbarWindowPreview(nsIDocShell *shell, nsITaskbarWindowPreview **_retval) { michael@0: if (!Initialize()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(shell); michael@0: michael@0: HWND toplevelHWND = ::GetAncestor(GetHWNDFromDocShell(shell), GA_ROOT); michael@0: michael@0: if (!toplevelHWND) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsWindow *window = WinUtils::GetNSWindowPtr(toplevelHWND); michael@0: michael@0: if (!window) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr preview = window->GetTaskbarPreview(); michael@0: if (!preview) { michael@0: nsRefPtr defaultController = new DefaultController(toplevelHWND); michael@0: preview = new TaskbarWindowPreview(mTaskbar, defaultController, toplevelHWND, shell); michael@0: if (!preview) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: window->SetTaskbarPreview(preview); michael@0: } michael@0: michael@0: preview.forget(_retval); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WinTaskbar::GetTaskbarProgress(nsIDocShell *shell, nsITaskbarProgress **_retval) { michael@0: nsCOMPtr preview; michael@0: nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return CallQueryInterface(preview, _retval); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: WinTaskbar::GetOverlayIconController(nsIDocShell *shell, michael@0: nsITaskbarOverlayIconController **_retval) { michael@0: nsCOMPtr preview; michael@0: nsresult rv = GetTaskbarWindowPreview(shell, getter_AddRefs(preview)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return CallQueryInterface(preview, _retval); michael@0: } michael@0: michael@0: /* nsIJumpListBuilder createJumpListBuilder(); */ michael@0: NS_IMETHODIMP michael@0: WinTaskbar::CreateJumpListBuilder(nsIJumpListBuilder * *aJumpListBuilder) { michael@0: nsresult rv; michael@0: michael@0: if (JumpListBuilder::sBuildingList) michael@0: return NS_ERROR_ALREADY_INITIALIZED; michael@0: michael@0: nsCOMPtr builder = michael@0: do_CreateInstance(kJumpListBuilderCID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: NS_IF_ADDREF(*aJumpListBuilder = builder); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* void setGroupIdForWindow (in nsIDOMWindow aParent, in AString aIdentifier); */ michael@0: NS_IMETHODIMP michael@0: WinTaskbar::SetGroupIdForWindow(nsIDOMWindow *aParent, michael@0: const nsAString & aIdentifier) { michael@0: return SetWindowAppUserModelProp(aParent, nsString(aIdentifier)); michael@0: } michael@0: michael@0: /* void prepareFullScreen(in nsIDOMWindow aWindow, in boolean aFullScreen); */ michael@0: NS_IMETHODIMP michael@0: WinTaskbar::PrepareFullScreen(nsIDOMWindow *aWindow, bool aFullScreen) { michael@0: NS_ENSURE_ARG_POINTER(aWindow); michael@0: michael@0: HWND toplevelHWND = ::GetAncestor(GetHWNDFromDOMWindow(aWindow), GA_ROOT); michael@0: if (!toplevelHWND) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: return PrepareFullScreenHWND(toplevelHWND, aFullScreen); michael@0: } michael@0: michael@0: /* void prepareFullScreen(in voidPtr aWindow, in boolean aFullScreen); */ michael@0: NS_IMETHODIMP michael@0: WinTaskbar::PrepareFullScreenHWND(void *aHWND, bool aFullScreen) { michael@0: if (!Initialize()) michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: michael@0: NS_ENSURE_ARG_POINTER(aHWND); michael@0: michael@0: if (!::IsWindow((HWND)aHWND)) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: HRESULT hr = mTaskbar->MarkFullscreenWindow((HWND)aHWND, aFullScreen); michael@0: if (FAILED(hr)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla michael@0: