/* -*- 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/. */
// Make sure the application path for this progID is this installation.
regAppName.AppendLiteral("\\shell\\open\\command");
HKEY theKey;
nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, regAppName, &theKey); if (NS_FAILED(rv)) { returnfalse;
}
wchar_t cmdFromReg[MAX_BUF] = L"";
DWORD len = sizeof(cmdFromReg);
DWORD res = ::RegQueryValueExW(theKey, nullptr, nullptr, nullptr,
(LPBYTE)cmdFromReg, &len);
::RegCloseKey(theKey); if (REG_FAILED(res)) { returnfalse;
}
NS_IMETHODIMP
nsWindowsShellService::SetDefaultBrowser(bool aForAllUsers) { // If running from within a package, don't attempt to set default with // the helper, as it will not work and will only confuse our package's // virtualized registry.
nsresult rv = NS_OK; if (!widget::WinUtils::HasPackageIdentity()) {
nsAutoString appHelperPath; if (NS_FAILED(GetHelperPath(appHelperPath))) return NS_ERROR_FAILURE;
if (NS_SUCCEEDED(rv)) {
rv = LaunchModernSettingsDialogDefaultApps(); // The above call should never really fail, but just in case // fall back to showing control panel for all defaults if (NS_FAILED(rv)) {
rv = LaunchControlPanelDefaultsSelectionUI();
}
}
nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID)); if (prefs) {
(void)prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true); // Reset the number of times the dialog should be shown // before it is silenced.
(void)prefs->SetIntPref(PREF_DEFAULTBROWSERCHECKCOUNT, 0);
}
return rv;
}
/* * Asynchronious function to Write an ico file to the disk / in a nsIFile. * Limitation: Only square images are supported as of now.
*/
NS_IMETHODIMP
nsWindowsShellService::CreateWindowsIcon(nsIFile* aIcoFile,
imgIContainer* aImage, JSContext* aCx,
dom::Promise** aPromise) {
NS_ENSURE_ARG_POINTER(aIcoFile);
NS_ENSURE_ARG_POINTER(aImage);
NS_ENSURE_ARG_POINTER(aCx);
NS_ENSURE_ARG_POINTER(aPromise);
if (!NS_IsMainThread()) { return NS_ERROR_NOT_SAME_THREAD;
}
// At time of writing only `DataSourceSurface` was guaranteed thread safe. We // need this guarantee to write the icon file off the main thread.
RefPtr<gfx::DataSourceSurface> dataSurface = surface->GetDataSurface();
NS_ENSURE_TRUE(dataSurface, NS_ERROR_FAILURE);
// For either of the following formats we want to set the biBitCount member // of the BITMAPINFOHEADER struct to 32, below. For that value the bitmap // format defines that the A8/X8 WORDs in the bitmap byte stream be ignored // for the BI_RGB value we use for the biCompression member.
MOZ_ASSERT(surface->GetFormat() == gfx::SurfaceFormat::B8G8R8A8 ||
surface->GetFormat() == gfx::SurfaceFormat::B8G8R8X8);
// get a file output stream
nsCOMPtr<nsIOutputStream> stream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(stream), aFile);
NS_ENSURE_SUCCESS(rv, rv);
gfx::DataSourceSurface::MappedSurface map; if (!dataSurface->Map(gfx::DataSourceSurface::MapType::READ, &map)) { return NS_ERROR_FAILURE;
}
// write the bitmap headers and rgb pixel data to the file
rv = NS_ERROR_FAILURE; if (stream) {
uint32_t written;
stream->Write((constchar*)&bf, sizeof(BITMAPFILEHEADER), &written); if (written == sizeof(BITMAPFILEHEADER)) {
stream->Write((constchar*)&bmi, sizeof(BITMAPINFOHEADER), &written); if (written == sizeof(BITMAPINFOHEADER)) { // write out the image data backwards because the desktop won't // show bitmaps with negative heights for top-to-bottom
uint32_t i = map.mStride * height; do {
i -= map.mStride;
stream->Write(((constchar*)map.mData) + i, bytesPerRow, &written); if (written == bytesPerRow) {
rv = NS_OK;
} else {
rv = NS_ERROR_FAILURE; break;
}
} while (i != 0);
}
}
// get the image container
nsCOMPtr<imgIRequest> request;
rv = imageContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
getter_AddRefs(request)); if (!request) return rv;
nsCOMPtr<imgIContainer> container;
rv = request->GetImage(getter_AddRefs(container)); if (!container) return NS_ERROR_FAILURE;
// get the file name from localized strings
nsCOMPtr<nsIStringBundleService> bundleService(
do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv));
NS_ENSURE_SUCCESS(rv, rv);
// write the bitmap to a file in the profile directory. // We have to write old bitmap format for Windows 7 wallpapar support.
rv = WriteBitmap(file, container);
// if the file was written successfully, set it as the system wallpaper if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
/* * Writes information about a shortcut to a shortcuts log in * %PROGRAMDATA%\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38. * (This is the same directory used for update staging.) * For more on the shortcuts log format and purpose, consult * /toolkit/mozapps/installer/windows/nsis/common.nsh. * * The shortcuts log created or appended here is named after * the currently running application and current user SID. * For example: Firefox_$SID_shortcuts.ini. * * If it does not exist, it will be created. If it exists * and a matching shortcut named already exists in the file, * a new one will not be appended. * * In an ideal world this function would not need aShortcutsLogDir * passed to it, but it is called by at least one function that runs * asynchronously, and is therefore unable to use nsDirectoryService * to look it up itself.
*/ static nsresult WriteShortcutToLog(nsIFile* aShortcutsLogDir,
KNOWNFOLDERID aFolderId, const nsAString& aShortcutName) { // the section inside the shortcuts log
nsAutoCString section; // the shortcuts log wants "Programs" shortcuts in its "STARTMENU" section if (aFolderId == FOLDERID_CommonPrograms || aFolderId == FOLDERID_Programs) {
section.Assign("STARTMENU");
} elseif (aFolderId == FOLDERID_PublicDesktop ||
aFolderId == FOLDERID_Desktop) {
section.Assign("DESKTOP");
} else { return NS_ERROR_INVALID_ARG;
}
shortcutsLog->IsFile(&fileExists); // if the shortcuts log exists, find either an existing matching // entry, or the next available shortcut index if (fileExists) {
rv = parser.Init(shortcutsLog);
NS_ENSURE_SUCCESS(rv, rv);
nsCString iniShortcut; // surely we'll never need more than 10 shortcuts in one section... for (int i = 0; i < 10; i++) {
keyName.AssignLiteral("Shortcut");
keyName.AppendInt(i);
rv = parser.GetString(section.get(), keyName.get(), iniShortcut); if (NS_FAILED(rv)) { // we found an unused index break;
} elseif (iniShortcut.Equals(shortcutName)) {
shortcutsLogEntryExists = true;
}
} // otherwise, this is safe to use
} else {
keyName.AssignLiteral("Shortcut0");
}
if (!shortcutsLogEntryExists) {
parser.SetString(section.get(), keyName.get(), shortcutName.get()); // We write this ourselves instead of using parser->WriteToFile because // the INI parser in our uninstaller needs to read this, and only supports // UTF-16LE encoding. nsINIParser does not support UTF-16.
nsAutoCString formatted;
parser.WriteToString(formatted);
FILE* writeFile;
rv = shortcutsLog->OpenANSIFileDesc("w,ccs=UTF-16LE", &writeFile);
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertUTF8toUTF16 formattedUTF16(formatted); if (fwrite(formattedUTF16.get(), sizeof(wchar_t), formattedUTF16.Length(),
writeFile) != formattedUTF16.Length()) {
fclose(writeFile); return NS_ERROR_FAILURE;
}
fclose(writeFile);
}
if (MOZ_UNLIKELY(rv.Failed())) { return rv.StealNSResult();
} // In an ideal world we'd probably send along nsIFile pointers // here, but it's easier to determine the needed shortcuts log // entry with a KNOWNFOLDERID - so we pass this along instead // and let CreateShortcutImpl take care of converting it to // an nsIFile.
KNOWNFOLDERID folderId; if (aShortcutFolder.Equals(L"Programs")) {
folderId = FOLDERID_Programs;
} elseif (aShortcutFolder.Equals(L"Desktop")) {
folderId = FOLDERID_Desktop;
} else { return NS_ERROR_INVALID_ARG;
}
// 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 (FAILED(rv)) { return NS_ERROR_FAILURE;
}
rv = binFile->GetPath(binPath); 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(startupFolderWildcard.get(), &ffd); if (fileHandle == INVALID_HANDLE_VALUE) { // This means that no files were found in the folder which // doesn't imply an error. Most of the time the user won't // have any shortcuts here. return NS_OK;
}
do { // Extract shortcut target path from every // shortcut in the startup folder.
nsString fileName(ffd.cFileName);
RefPtr<IShellLinkW> link;
RefPtr<IPersistFile> ppf;
nsString target;
target.SetLength(MAX_PATH);
CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(link));
hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(ppf)); if (NS_WARN_IF(FAILED(hr))) { continue;
}
nsString filePath = startupFolder + u"\\"_ns + fileName;
hr = ppf->Load(filePath.get(), STGM_READ); if (NS_WARN_IF(FAILED(hr))) { 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 // startup 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;
}
// Look for any installer-created shortcuts in the given location that match // the given AUMID and EXE Path. If one is found, output its path. // // NOTE: DO NOT USE if a false negative (mismatch) is unacceptable. // aExePath is compared directly to the path retrieved from the shortcut. // Due to the presence of symlinks or other filesystem issues, it's possible // for different paths to refer to the same file, which would cause the check // to fail. // This should rarely be an issue as we are most likely to be run from a path // written by the installer (shortcut, association, launch from installer), // which also wrote the shortcuts. But it is possible. // // aCSIDL the CSIDL of the directory to look for matching shortcuts in // aAUMID the AUMID to check for // aExePath the target exe path to check for, should be a long path where // possible // aShortcutSubstring a substring to limit which shortcuts in aCSIDL are // inspected for a match. Only shortcuts whose filename // contains this substring will be considered // aShortcutPath outparam, set to matching shortcut path if NS_OK is returned. // // Returns // NS_ERROR_FAILURE on errors before any shortcuts were loaded // NS_ERROR_FILE_NOT_FOUND if no shortcuts matching aShortcutSubstring exist // NS_ERROR_FILE_ALREADY_EXISTS if shortcuts were found but did not match // aAUMID or aExePath // NS_OK if a matching shortcut is found static nsresult GetMatchingShortcut(int aCSIDL, const nsAString& aAUMID, constwchar_t aExePath[MAXPATHLEN], const nsAString& aShortcutSubstring, /* out */ nsAutoString& aShortcutPath) {
nsresult result = NS_ERROR_FAILURE;
// Get list of shortcuts in aCSIDL
nsAutoString pattern(folderPath);
pattern.AppendLiteral("*.lnk");
WIN32_FIND_DATAW findData = {};
HANDLE hFindFile = FindFirstFileW(pattern.get(), &findData); if (hFindFile == INVALID_HANDLE_VALUE) {
Unused << NS_WARN_IF(GetLastError() != ERROR_FILE_NOT_FOUND); return NS_ERROR_FILE_NOT_FOUND;
} // Past this point we don't return until the end of the function, // when FindClose() is called.
// todo: improve return values here do { // Skip any that don't contain aShortcutSubstring // This is a case sensitive comparison, but that's probably fine for // the vast majority of cases -- and certainly for all the ones where // a shortcut was created by the installer. if (StrStrIW(findData.cFileName, aShortcutSubstring.Data()) == NULL) { continue;
}
// Create a shell link object for loading the shortcut
RefPtr<IShellLinkW> link;
HRESULT hr =
CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(link)); if (NS_WARN_IF(FAILED(hr))) { continue;
}
hr = persist->Load(path.get(), STGM_READ); if (FAILED(hr)) { if (NS_WARN_IF(hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))) { // empty branch, result unchanged but warning issued
} else { // If we've ever gotten past this block, result will already be // NS_ERROR_FILE_ALREADY_EXISTS, which is a more accurate error // than NS_ERROR_FILE_NOT_FOUND. if (result != NS_ERROR_FILE_ALREADY_EXISTS) {
result = NS_ERROR_FILE_NOT_FOUND;
}
} continue;
}
result = NS_ERROR_FILE_ALREADY_EXISTS;
// Check the AUMID
RefPtr<IPropertyStore> propStore;
hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore)); if (NS_WARN_IF(FAILED(hr))) { continue;
}
if (aPrivateBrowsing) { if (!PathRemoveFileSpecW(exePath)) { return NS_ERROR_FAILURE;
} if (!PathAppendW(exePath, L"private_browsing.exe")) { return NS_ERROR_FAILURE;
}
}
int shortcutCSIDLs[] = {CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS}; for (int shortcutCSIDL : shortcutCSIDLs) { // GetMatchingShortcut may fail when the exe path doesn't match, even // if it refers to the same file. This should be rare, and the worst // outcome would be failure to pin, so the risk is acceptable.
nsresult rv = GetMatchingShortcut(shortcutCSIDL, aAppUserModelId, exePath,
aShortcutSubstring, aShortcutPath); if (NS_SUCCEEDED(rv)) { return NS_OK;
}
}
return NS_ERROR_FILE_NOT_FOUND;
}
staticbool HasPinnableShortcutImpl(const nsAString& aAppUserModelId, constbool aPrivateBrowsing, const nsAutoString& aShortcutSubstring) { // unused by us, but required
nsAutoString shortcutPath;
nsresult rv = FindPinnableShortcut(aAppUserModelId, aShortcutSubstring,
aPrivateBrowsing, shortcutPath); if (SUCCEEDED(rv)) { returntrue;
}
staticbool IsCurrentAppPinnedToTaskbarSync(const nsAString& aumid) { // Use new Windows pinning APIs to determine whether or not we're pinned. // If these fail we can safely fall back to the old method for regular // installs however MSIX will always return false.
// Bug 1911343: Add a check for whether we're looking for a regular pin // or PB pin based on the AUMID value once private browser pinning // is supported on MSIX. // Right now only run this check on MSIX to avoid // false positives when only private browsing is pinned. if (widget::WinUtils::HasPackageIdentity()) { auto pinWithWin11TaskbarAPIResults =
IsCurrentAppPinnedToTaskbarWin11(false); switch (pinWithWin11TaskbarAPIResults.result) { case Win11PinToTaskBarResultStatus::NotPinned: returnfalse; break; case Win11PinToTaskBarResultStatus::AlreadyPinned: returntrue; break; default: // Fall through to the old mechanism. // The old mechanism should continue working for non-MSIX // builds. break;
}
}
// There are two shortcut targets that we created. One always matches the // binary we're running as (eg: firefox.exe). The other is the wrapper // for launching in Private Browsing mode. We need to inspect shortcuts // that point at either of these to accurately judge whether or not // the app is pinned with the given AUMID. wchar_t exePath[MAXPATHLEN] = {}; wchar_t pbExePath[MAXPATHLEN] = {};
if (NS_WARN_IF(NS_FAILED(BinaryPath::GetLong(exePath)))) { returnfalse;
}
wcscpy_s(pbExePath, MAXPATHLEN, exePath); if (!PathRemoveFileSpecW(pbExePath)) { returnfalse;
} if (!PathAppendW(pbExePath, L"private_browsing.exe")) { returnfalse;
}
WIN32_FIND_DATAW findData = {};
HANDLE hFindFile = FindFirstFileW(pattern.get(), &findData); if (hFindFile == INVALID_HANDLE_VALUE) {
Unused << NS_WARN_IF(GetLastError() != ERROR_FILE_NOT_FOUND); returnfalse;
} // Past this point we don't return until the end of the function, // when FindClose() is called.
// Check all shortcuts until a match is found bool isPinned = false; do {
nsAutoString fileName;
fileName.Assign(folder);
fileName.AppendLiteral("\\");
fileName.Append(findData.cFileName);
// Create a shell link object for loading the shortcut
RefPtr<IShellLinkW> link;
HRESULT hr =
CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLinkW, getter_AddRefs(link)); if (NS_WARN_IF(FAILED(hr))) { continue;
}
hr = persist->Load(fileName.get(), STGM_READ); if (NS_WARN_IF(FAILED(hr))) { continue;
}
// Check the exe path
static_assert(MAXPATHLEN == MAX_PATH); wchar_t storedExePath[MAX_PATH] = {}; // With no flags GetPath gets a long path
hr = link->GetPath(storedExePath, std::size(storedExePath), nullptr, 0); if (FAILED(hr) || hr == S_FALSE) { continue;
} // Case insensitive path comparison // NOTE: Because this compares the path directly, it is possible to // have a false negative mismatch. if (wcsnicmp(storedExePath, exePath, MAXPATHLEN) == 0 ||
wcsnicmp(storedExePath, pbExePath, MAXPATHLEN) == 0) {
RefPtr<IPropertyStore> propStore;
hr = link->QueryInterface(IID_IPropertyStore, getter_AddRefs(propStore)); if (NS_WARN_IF(FAILED(hr))) { continue;
}
if (aumid.Equals(storedAUMID)) {
isPinned = true; break;
}
}
} while (FindNextFileW(hFindFile, &findData));
FindClose(hFindFile);
return isPinned;
}
static nsresult ManageShortcutTaskbarPins(bool aCheckOnly, bool aPinType, const nsAString& aShortcutPath) { // This enum is likely only used for Windows telemetry, INT_MAX is chosen to // avoid confusion with existing uses. enum PINNEDLISTMODIFYCALLER { PLMC_INT_MAX = INT_MAX };
// The types below, and the idea of using IPinnedList3::Modify, // are thanks to Gee Law <https://geelaw.blog/entries/msedge-pins/> static constexpr GUID CLSID_TaskbandPin = {
0x90aa3a4e,
0x1cba,
0x4233,
{0xb8, 0xbb, 0x53, 0x57, 0x73, 0xd4, 0x84, 0x49}};
if (FAILED(hr)) { return NS_ERROR_FILE_ACCESS_DENIED;
} return NS_OK;
}
static nsresult PinShortcutToTaskbarImpl(bool aCheckOnly, const nsAString& aAppUserModelId, const nsAString& aShortcutPath) { // Verify shortcut is visible to `shell:appsfolder`. Shortcut creation - // during install or runtime - causes a race between it propagating to the // virtual `shell:appsfolder` and attempts to pin via `ITaskbarManager`, // resulting in pin failures when the latter occurs before the former. We can // skip this when we're only checking whether we're pinned. if (!aCheckOnly && !PollAppsFolderForShortcut(
aAppUserModelId, TimeDuration::FromSeconds(15))) { return NS_ERROR_FILE_NOT_FOUND;
}
auto pinWithWin11TaskbarAPIResults =
PinCurrentAppToTaskbarWin11(aCheckOnly, aAppUserModelId); switch (pinWithWin11TaskbarAPIResults.result) { case Win11PinToTaskBarResultStatus::NotSupported: // Fall through to the win 10 mechanism break;
case Win11PinToTaskBarResultStatus::Success: case Win11PinToTaskBarResultStatus::AlreadyPinned: return NS_OK;
case Win11PinToTaskBarResultStatus::NotPinned: case Win11PinToTaskBarResultStatus::NotCurrentlyAllowed: case Win11PinToTaskBarResultStatus::Failed: // return NS_ERROR_FAILURE;
// Fall through to the old mechanism for now // In future, we should be sending telemetry for when // an error occurs or for when pinning is not allowed // with the Win 11 APIs. break;
}
/* This function pins a shortcut to the taskbar based on its location. While * Windows 11 only needs the `aAppUserModelId`, `aShortcutPath` is required * for pinning in Windows 10. * @param aAppUserModelId * The same string used to create an lnk file. * @param aShortcutPaths * Path for existing shortcuts (e.g., start menu)
*/
NS_IMETHODIMP
nsWindowsShellService::PinShortcutToTaskbar(const nsAString& aAppUserModelId, const nsAString& aShortcutPath,
JSContext* aCx,
dom::Promise** aPromise) {
NS_ENSURE_ARG_POINTER(aCx);
NS_ENSURE_ARG_POINTER(aPromise);
if (!NS_IsMainThread()) { return NS_ERROR_NOT_SAME_THREAD;
}
// First available on 1809 if (!IsWin10Sep2018UpdateOrLater()) { return NS_ERROR_NOT_AVAILABLE;
}
// Ensure that the supplied name doesn't have invalid characters. staticvoid ValidateFilename(nsAString& aFilename) {
nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1"); if (NS_WARN_IF(!mimeService)) {
aFilename.Truncate(); return;
}
// 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;
}
// 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, const nsAString& aShortcutPath) { // The behavior here is identical if we're only checking or if we try to pin // but the app is already pinned so we update the variable accordingly. if (!aCheckOnly) {
aCheckOnly = IsCurrentAppPinnedToTaskbarSync(aAppUserModelId);
} constbool pinType = true; // true means pin return ManageShortcutTaskbarPins(aCheckOnly, pinType, aShortcutPath);
}
// There's a delay between shortcuts being created in locations visible to // `shell:appsfolder` and that information being propogated to // `shell:appsfolder`. APIs like `ITaskbarManager` pinning rely on said // shortcuts being visible to `shell:appsfolder`, so we have to introduce a wait // until they're visible when creating these shortcuts at runtime. staticbool PollAppsFolderForShortcut(const nsAString& aAppUserModelId, const TimeDuration aTimeout) {
MOZ_DIAGNOSTIC_ASSERT(!NS_IsMainThread(), "PollAppsFolderForShortcut blocks and should be called " "off main thread only");
// Implementation note: it was taken into consideration at the time of writing // to implement this with `SHChangeNotifyRegister` and a `HWND_MESSAGE` // window. This added significant complexity in terms of resource management // and control flow that was deemed excessive for a function that is rarely // run. Absent evidence that we're consuming excessive system resources, this // simple, poll-based approach seemed more appropriate. // // If in the future it seems appropriate to modify this to be event based,
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.20 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.