mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 01:05:45 +00:00
243d8bb24d
Once we start supporting different model IDs for different windows of the same application we need to make sure that the normal windows have a model ID and the jump lists and recent documents are tagged with the same ID. We do this by always having an app default model ID, when no window specific model ID is set the app default is used. At some point we may want to extend the jump list stuff to support using a different model ID but that isn't needed right now. Differential Revision: https://phabricator.services.mozilla.com/D57152 --HG-- extra : moz-landing-system : lando
423 lines
12 KiB
C++
423 lines
12 KiB
C++
/* 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 <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 "nsAppRunner.h"
|
|
#include "nsXREDirProvider.h"
|
|
#include <io.h>
|
|
#include <propvarutil.h>
|
|
#include <propkey.h>
|
|
#include <shellapi.h>
|
|
|
|
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;
|
|
|
|
RefPtr<IPropertyStore> pPropStore;
|
|
if (FAILED(SHGetPropertyStoreForWindow(toplevelHWND, IID_IPropertyStore,
|
|
getter_AddRefs(pPropStore)))) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
PROPVARIANT pv;
|
|
if (FAILED(InitPropVariantFromString(aIdentifier.get(), &pv))) {
|
|
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);
|
|
|
|
return rv;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// default nsITaskbarPreviewController
|
|
|
|
class DefaultController final : public nsITaskbarPreviewController {
|
|
~DefaultController() {}
|
|
HWND mWnd;
|
|
|
|
public:
|
|
explicit 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;
|
|
}
|
|
|
|
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) {
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"OnClose should not be called for "
|
|
"TaskbarWindowPreviews");
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
DefaultController::OnActivate(bool* rAcceptActivation) {
|
|
*rAcceptActivation = true;
|
|
MOZ_ASSERT_UNREACHABLE(
|
|
"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
|
|
|
|
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 an ID has already been set then use that.
|
|
PWSTR id;
|
|
if (SUCCEEDED(GetCurrentProcessExplicitAppUserModelID(&id))) {
|
|
aDefaultGroupId.Assign(id);
|
|
CoTaskMemFree(id);
|
|
}
|
|
|
|
// 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->GetPersistentDescriptor(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.
|
|
nsCOMPtr<nsIXULAppInfo> appInfo =
|
|
do_GetService("@mozilla.org/xre/app-info;1");
|
|
nsCString appName;
|
|
if (appInfo && NS_SUCCEEDED(appInfo->GetName(appName))) {
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we haven't found an ID yet then use the install hash. In xpcshell tests
|
|
// the directory provider may not have been initialized so bypass in this
|
|
// case.
|
|
if (aDefaultGroupId.IsEmpty() && gDirServiceProvider) {
|
|
gDirServiceProvider->GetInstallHash(aDefaultGroupId);
|
|
}
|
|
|
|
return !aDefaultGroupId.IsEmpty();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WinTaskbar::GetDefaultGroupId(nsAString& aDefaultGroupId) {
|
|
if (!GetAppUserModelID(aDefaultGroupId)) return NS_ERROR_UNEXPECTED;
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
// (static) Called from AppShell
|
|
bool WinTaskbar::RegisterAppUserModelID() {
|
|
nsAutoString uid;
|
|
if (!GetAppUserModelID(uid)) return false;
|
|
|
|
return SUCCEEDED(SetCurrentProcessExplicitAppUserModelID(uid.get()));
|
|
}
|
|
|
|
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 = 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
|