Bug 1863636 - Part 2: Added native code changes for taskbar tabs r=win-reviewers,mhughes,gstoll

Differential Revision: https://phabricator.services.mozilla.com/D197342
This commit is contained in:
Nipun Shukla 2024-01-22 22:00:13 +00:00
parent 1b61a2bdab
commit 41645fcb5c
4 changed files with 222 additions and 10 deletions

View File

@ -189,6 +189,44 @@ interface nsIWindowsShellService : nsISupports
*/
void unpinShortcutFromTaskbar(in AString aShortcutPath);
/**
* Gets the full path of a taskbar tab shortcut.
*
* @param aShortcutName
* The name of the taskbar tab shortcut, excluding the file extension.
* @throws NS_ERROR_FAILURE
* If the user token cannot be found.
* @throws NS_ERROR_ABORT
* If converting the sid to a string fails.
*
* @return The full path of the taskbar tab shortcut
*/
AString getTaskbarTabShortcutPath(in AString aShortcutName);
/*
* Searches the %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start
* Menu\Programs folder and returns an array with the path of all
* shortcuts with a target matching the current Firefox install location
* and the -taskbar-tab argument.
*
* It is possible to return an empty array if no shortcuts are found.
*
* @return An array of paths for all taskbar tab shortcuts.
*
* @throws NS_ERROR_NOT_IMPLEMENTED
* if run on a MinGW compilation
* @throws NS_ERROR_ABORT
* if instance cannot be created.
* @throws NS_ERROR_FILE_NOT_FOUND
* if %USERPROFILE%\AppData\Roaming\ cannot be opened.
* @throws NS_ERROR_FAILURE
* if the executable file cannot be found.
* @throws NS_ERROR_FILE_UNRECOGNIZED_PATH
* if the executable file cannot be converted into a string.
*/
Array<AString> getTaskbarTabPins();
/*
* Determine where a given shortcut likely appears in the shell.
*

View File

@ -1418,14 +1418,6 @@ static nsresult ManageShortcutTaskbarPins(bool aCheckOnly, bool aPinType,
}
};
HRESULT hr = CoInitialize(nullptr);
if (FAILED(hr)) {
return NS_ERROR_FAILURE;
}
const struct ComUninitializer {
~ComUninitializer() { CoUninitialize(); }
} kCUi;
mozilla::UniquePtr<__unaligned ITEMIDLIST, ILFreeDeleter> path(
ILCreateFromPathW(nsString(aShortcutPath).get()));
if (NS_WARN_IF(!path)) {
@ -1433,8 +1425,8 @@ static nsresult ManageShortcutTaskbarPins(bool aCheckOnly, bool aPinType,
}
IPinnedList3* pinnedList = nullptr;
hr = CoCreateInstance(CLSID_TaskbandPin, NULL, CLSCTX_INPROC_SERVER,
IID_IPinnedList3, (void**)&pinnedList);
HRESULT hr = CoCreateInstance(CLSID_TaskbandPin, NULL, CLSCTX_INPROC_SERVER,
IID_IPinnedList3, (void**)&pinnedList);
if (FAILED(hr) || !pinnedList) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -1467,6 +1459,147 @@ nsWindowsShellService::UnpinShortcutFromTaskbar(
return ManageShortcutTaskbarPins(runInTestMode, pinType, aShortcutPath);
}
NS_IMETHODIMP
nsWindowsShellService::GetTaskbarTabShortcutPath(const nsAString& aShortcutName,
nsAString& aRetPath) {
// The taskbar tab shortcut will always be in
// %APPDATA%\Microsoft\Windows\Start Menu\Programs
RefPtr<IKnownFolderManager> fManager;
RefPtr<IKnownFolder> progFolder;
LPWSTR progFolderW;
nsString progFolderNS;
HRESULT hr =
CoCreateInstance(CLSID_KnownFolderManager, nullptr, CLSCTX_INPROC_SERVER,
IID_IKnownFolderManager, getter_AddRefs(fManager));
if (NS_WARN_IF(FAILED(hr))) {
return NS_ERROR_ABORT;
}
fManager->GetFolder(FOLDERID_Programs, progFolder.StartAssignment());
hr = progFolder->GetPath(0, &progFolderW);
if (FAILED(hr)) {
return NS_ERROR_FILE_NOT_FOUND;
}
progFolderNS.Assign(progFolderW);
aRetPath = progFolderNS + u"\\"_ns + aShortcutName + u".lnk"_ns;
return NS_OK;
}
NS_IMETHODIMP
nsWindowsShellService::GetTaskbarTabPins(nsTArray<nsString>& aShortcutPaths) {
#ifdef __MINGW32__
return NS_ERROR_NOT_IMPLEMENTED;
#else
aShortcutPaths.Clear();
// Get AppData\\Roaming folder using a known folder ID
RefPtr<IKnownFolderManager> fManager;
RefPtr<IKnownFolder> roamingAppData;
LPWSTR roamingAppDataW;
nsString roamingAppDataNS;
HRESULT hr =
CoCreateInstance(CLSID_KnownFolderManager, nullptr, CLSCTX_INPROC_SERVER,
IID_IKnownFolderManager, getter_AddRefs(fManager));
if (NS_WARN_IF(FAILED(hr))) {
return NS_ERROR_ABORT;
}
fManager->GetFolder(FOLDERID_RoamingAppData,
roamingAppData.StartAssignment());
hr = roamingAppData->GetPath(0, &roamingAppDataW);
if (FAILED(hr)) {
return NS_ERROR_FILE_NOT_FOUND;
}
// Append taskbar pins folder to AppData\\Roaming
roamingAppDataNS.Assign(roamingAppDataW);
CoTaskMemFree(roamingAppDataW);
nsString taskbarFolder =
roamingAppDataNS + u"\\Microsoft\\Windows\\Start Menu\\Programs"_ns;
nsString taskbarFolderWildcard = taskbarFolder + u"\\*.lnk"_ns;
// Get known path for binary file for later comparison with shortcuts.
// Returns lowercase file path which should be fine for Windows as all
// directories and files are case-insensitive by default.
RefPtr<nsIFile> binFile;
nsString binPath;
nsresult rv = XRE_GetBinaryPath(binFile.StartAssignment());
if (NS_WARN_IF(FAILED(rv))) {
return NS_ERROR_FAILURE;
}
rv = binFile->GetPath(binPath);
if (NS_WARN_IF(FAILED(rv))) {
return NS_ERROR_FILE_UNRECOGNIZED_PATH;
}
// Check for if first file exists with a shortcut extension (.lnk)
WIN32_FIND_DATAW ffd;
HANDLE fileHandle = INVALID_HANDLE_VALUE;
fileHandle = FindFirstFileW(taskbarFolderWildcard.get(), &ffd);
if (fileHandle == INVALID_HANDLE_VALUE) {
// This means that no files were found in the folder which
// doesn't imply an error.
return NS_OK;
}
do {
// Extract shortcut target path from every
// shortcut in the taskbar pins folder.
nsString fileName(ffd.cFileName);
RefPtr<IShellLinkW> link;
RefPtr<IPropertyStore> pps;
nsString target;
target.SetLength(MAX_PATH);
hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(link));
if (NS_WARN_IF(FAILED(hr))) {
continue;
}
nsString filePath = taskbarFolder + u"\\"_ns + fileName;
if (NS_WARN_IF(FAILED(hr))) {
continue;
}
// After loading shortcut, search through arguments to find if
// it is a taskbar tab shortcut.
hr = SHGetPropertyStoreFromParsingName(filePath.get(), nullptr,
GPS_READWRITE, IID_IPropertyStore,
getter_AddRefs(pps));
if (NS_WARN_IF(FAILED(hr)) || pps == nullptr) {
continue;
}
PROPVARIANT propVar;
PropVariantInit(&propVar);
auto cleanupPropVariant =
MakeScopeExit([&] { PropVariantClear(&propVar); });
// Get the PKEY_Link_Arguments property
hr = pps->GetValue(PKEY_Link_Arguments, &propVar);
if (NS_WARN_IF(FAILED(hr))) {
continue;
}
// Check if the argument matches
if (!(propVar.vt == VT_LPWSTR && propVar.pwszVal != nullptr &&
wcsstr(propVar.pwszVal, L"-taskbar-tab") != nullptr)) {
continue;
}
hr = link->GetPath(target.get(), MAX_PATH, nullptr, 0);
if (NS_WARN_IF(FAILED(hr))) {
continue;
}
// If shortcut target matches known binary file value
// then add the path to the shortcut as a valid
// shortcut. This has to be a substring search as
// the user could have added unknown command line arguments
// to the shortcut.
if (_wcsnicmp(target.get(), binPath.get(), binPath.Length()) == 0) {
aShortcutPaths.AppendElement(filePath);
}
} while (FindNextFile(fileHandle, &ffd) != 0);
FindClose(fileHandle);
return NS_OK;
#endif
}
static nsresult PinCurrentAppToTaskbarWin10(bool aCheckOnly,
const nsAString& aAppUserModelId,
nsAutoString aShortcutPath) {

View File

@ -145,6 +145,20 @@ interface nsIWinTaskbar : nsISupports
* Application window taskbar group settings
*/
/**
* Get the grouping id for a window.
*
* The runtime sets a default, global grouping id for all windows on startup.
* getGroupIdForWindow allows finding the grouping of individual windows
* on the taskbar.
*
* @throw NS_ERROR_INVALID_ARG if the window is not a valid top level window
* associated with a widget.
* @throw NS_ERROR_FAILURE if the property on the window could not be set.
* @throw NS_ERROR_UNEXPECTED for general failures.
*/
AString getGroupIdForWindow(in mozIDOMWindow aParent);
/**
* Set the grouping id for a window.
*

View File

@ -440,6 +440,33 @@ WinTaskbar::CreateJumpListBuilder(bool aPrivateBrowsing,
return NS_OK;
}
NS_IMETHODIMP
WinTaskbar::GetGroupIdForWindow(mozIDOMWindow* aParent,
nsAString& aIdentifier) {
NS_ENSURE_ARG_POINTER(aParent);
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;
PropVariantInit(&pv);
auto cleanupPropVariant = MakeScopeExit([&] { PropVariantClear(&pv); });
if (FAILED(pPropStore->GetValue(PKEY_AppUserModel_ID, &pv))) {
return NS_ERROR_FAILURE;
}
if (pv.vt != VT_LPWSTR) {
// This can happen when there is no window specific group ID set
// It's not an error case so we have to check for empty strings
// returned from the function.
return NS_OK;
}
aIdentifier.Assign(char16ptr_t(pv.pwszVal));
return NS_OK;
}
NS_IMETHODIMP
WinTaskbar::SetGroupIdForWindow(mozIDOMWindow* aParent,
const nsAString& aIdentifier) {