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