Bug 1750991: Automatically create shortcut if it's not already present when pinning to the Windows Taskbar. r=mhowell
There's some general improvements to CreateShortcut here: writing them out to a log, setting the working directory, allowing the icon to be customized. To support the shortcuts log, I had to restrict how shortcut locations are specified to be a KNOWNFOLDERID -- I couldn't think of any other way to make sure we accurately record them. Notably, this code has been unused since the Site Specific Browsing work was backed out - so there's no existing callers to worry about breaking. This patch also enhances the pin to taskbar code to automatically create a shortcut if none exist (this is how we'll want things to work for the Private Browsing shortcuts, and it seemed like a generally useful thing to do). Differential Revision: https://phabricator.services.mozilla.com/D138197
This commit is contained in:
@@ -55,6 +55,7 @@ elif CONFIG["OS_ARCH"] == "WINNT":
|
||||
]
|
||||
LOCAL_INCLUDES += [
|
||||
"../../../other-licenses/nsis/Contrib/CityHash/cityhash",
|
||||
"/toolkit/xre",
|
||||
]
|
||||
OS_LIBS += [
|
||||
"bcrypt",
|
||||
|
||||
@@ -9,9 +9,47 @@ interface nsIFile;
|
||||
[scriptable, uuid(fb9b59db-5a91-4e67-92b6-35e7d6e6d3fd)]
|
||||
interface nsIWindowsShellService : nsISupports
|
||||
{
|
||||
void createShortcut(in nsIFile aBinary, in Array<AString> aArguments,
|
||||
in AString aDescription, in nsIFile aIconFile, in AString aAppUserModelId,
|
||||
in nsIFile aTarget);
|
||||
/*
|
||||
* Creates a new shortcut (.lnk) file. This shortcut will be recorded in
|
||||
* a new shortcuts log file located in %PROGRAMDATA%\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38
|
||||
* that is named after the currently running application and current user, eg:
|
||||
* Firefox_user123_shortcuts.ini.
|
||||
*
|
||||
* For reasons that we haven't been able to pin down, these shortcuts get created with
|
||||
* extra metadata on them (KnownFolderDataBlock, SpecialFolderDataBlock) that cause
|
||||
* the Windows ShellLink classes to improperly read their target path with certain
|
||||
* parameters. This causes any 32-bit programs that read the links (such as our
|
||||
* installer and uninstaller) to think that 64-bit installs are located in the 32-bit
|
||||
* Program Files directory.
|
||||
* See https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/6f2e7920-50a9-459d-bfdd-316e459e87c0/ishelllink-getpath-returns-wrong-folder-for-64-bit-application-when-called-from-32-bit-application
|
||||
* for some additional discussion of this.
|
||||
*
|
||||
* @param aBinary Target file of the shortcut.
|
||||
* @param aArguments Arguments to set for the shortcut. May be empty.
|
||||
* @param aDescription The description of the shortcut. The string used here
|
||||
* shows up as the hover text of the shortcut in Explorer and on the
|
||||
* Taskbar (if the shortcut is pinned there).
|
||||
* @param aIconFile The file containing the desired icon for the shortcut. This
|
||||
* can be the same file as aBinary.
|
||||
* @param aIconIndex The index of the in aIconFile. Note that this is 0 based index
|
||||
* that IShellLinkW requires, _not_ a Resource ID that is sometimes used
|
||||
* for icons.
|
||||
* @param aAppUserModelId The App User Model ID to set for the shortcut. This will
|
||||
* affect which icon on the Taskbar the application groups with when first
|
||||
* launched.
|
||||
* @param aShortcutFolder The special Windows folder to create the shortcut in. One of:
|
||||
* CommonStartMenu, StartMenu, PublicDesktop, Desktop, or QuickLaunch.
|
||||
* @param aShortcutName The filename of the shortcut within aShortcutFolder.
|
||||
* @return The full native path to the created shortcut.
|
||||
*
|
||||
* @throws NS_ERROR_INVALID_ARG if an invalid shortcut folder is passed
|
||||
* @throws NS_ERROR_FILE_NOT_FOUND if the shortcut file or shortcuts log cannot be
|
||||
* created or accessed
|
||||
* @throws NS_ERROR_FAILURE for other types of failures
|
||||
*/
|
||||
AString createShortcut(in nsIFile aBinary, in Array<AString> aArguments,
|
||||
in AString aDescription, in nsIFile aIconFile, in unsigned short aIconIndex,
|
||||
in AString aAppUserModelId, in AString aShortcutFolder, in AString aShortcutName);
|
||||
|
||||
/*
|
||||
* Pin the current app to the taskbar. If aPrivateBrowsing is true, the
|
||||
@@ -20,10 +58,10 @@ interface nsIWindowsShellService : nsISupports
|
||||
*
|
||||
* This MUST only be used in response to an active request from the user.
|
||||
*
|
||||
* Uses an existing shortcut on the Desktop or Start Menu, which would have
|
||||
* been created by the installer (for All Users or Current User), in order
|
||||
* to ensure that the pin is associated with this executable and AUMID for
|
||||
* proper launching and grouping.
|
||||
* If it exists, uses an existing shortcut on the Desktop or Start Menu,
|
||||
* which would have been created by the installer (for All Users or
|
||||
* Current User). If none can be found, one will be created with the correct
|
||||
* AUMID for proper launching and grouping.
|
||||
*
|
||||
* NOTE: This method probably shouldn't be used on the main thread, it
|
||||
* performs blocking disk I/O.
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "nsLocalFile.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
#include "nsINIParser.h"
|
||||
#include "nsNativeAppSupportWin.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
@@ -730,15 +731,16 @@ static nsresult WriteShortcutToLog(KNOWNFOLDERID aFolderId,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
|
||||
const nsTArray<nsString>& aArguments,
|
||||
const nsAString& aDescription,
|
||||
nsIFile* aIconFile,
|
||||
const nsAString& aAppUserModelId,
|
||||
nsIFile* aTarget) {
|
||||
static nsresult CreateShortcutImpl(
|
||||
nsIFile* aBinary, const nsTArray<nsString>& aArguments,
|
||||
const nsAString& aDescription, nsIFile* aIconFile, uint16_t aIconIndex,
|
||||
const nsAString& aAppUserModelId, KNOWNFOLDERID aShortcutFolder,
|
||||
const nsAString& aShortcutName, nsAString& aShortcutPath) {
|
||||
NS_ENSURE_ARG(aBinary);
|
||||
NS_ENSURE_ARG(aTarget);
|
||||
NS_ENSURE_ARG(aIconFile);
|
||||
|
||||
nsresult rv = WriteShortcutToLog(aShortcutFolder, aShortcutName);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
|
||||
@@ -748,6 +750,11 @@ nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
|
||||
nsString path(aBinary->NativePath());
|
||||
link->SetPath(path.get());
|
||||
|
||||
wchar_t workingDir[MAX_PATH + 1];
|
||||
wcscpy_s(workingDir, MAX_PATH + 1, aBinary->NativePath().get());
|
||||
PathRemoveFileSpecW(workingDir);
|
||||
link->SetWorkingDirectory(workingDir);
|
||||
|
||||
if (!aDescription.IsEmpty()) {
|
||||
link->SetDescription(PromiseFlatString(aDescription).get());
|
||||
}
|
||||
@@ -762,7 +769,7 @@ nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
|
||||
|
||||
if (aIconFile) {
|
||||
nsString icon(aIconFile->NativePath());
|
||||
link->SetIconLocation(icon.get(), 0);
|
||||
link->SetIconLocation(icon.get(), aIconIndex);
|
||||
}
|
||||
|
||||
if (!aAppUserModelId.IsEmpty()) {
|
||||
@@ -788,13 +795,59 @@ nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
|
||||
hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
|
||||
NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
|
||||
|
||||
nsString target(aTarget->NativePath());
|
||||
nsCOMPtr<nsIProperties> directoryService =
|
||||
do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> shortcutFile;
|
||||
if (aShortcutFolder == FOLDERID_StartMenu) {
|
||||
rv = directoryService->Get(NS_WIN_PROGRAMS_DIR, NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(shortcutFile));
|
||||
} else if (aShortcutFolder == FOLDERID_Desktop) {
|
||||
rv = directoryService->Get(NS_OS_DESKTOP_DIR, NS_GET_IID(nsIFile),
|
||||
getter_AddRefs(shortcutFile));
|
||||
} else {
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
shortcutFile->Append(aShortcutName);
|
||||
|
||||
nsString target(shortcutFile->NativePath());
|
||||
hr = persist->Save(target.get(), TRUE);
|
||||
NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
|
||||
|
||||
aShortcutPath.Assign(target);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::CreateShortcut(
|
||||
nsIFile* aBinary, const nsTArray<nsString>& aArguments,
|
||||
const nsAString& aDescription, nsIFile* aIconFile, uint16_t aIconIndex,
|
||||
const nsAString& aAppUserModelId, const nsAString& aShortcutFolder,
|
||||
const nsAString& aShortcutName, nsAString& aShortcutPath) {
|
||||
// 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"StartMenu")) {
|
||||
folderId = FOLDERID_StartMenu;
|
||||
} else if (aShortcutFolder.Equals(L"Desktop")) {
|
||||
folderId = FOLDERID_Desktop;
|
||||
} else {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return CreateShortcutImpl(aBinary, aArguments, aDescription, aIconFile,
|
||||
aIconIndex, aAppUserModelId, folderId,
|
||||
aShortcutName, aShortcutPath);
|
||||
}
|
||||
|
||||
// Constructs a path to an installer-created shortcut, under a directory
|
||||
// specified by a CSIDL.
|
||||
static nsresult GetShortcutPath(int aCSIDL, bool aPrivateBrowsing,
|
||||
@@ -824,7 +877,7 @@ static nsresult GetShortcutPath(int aCSIDL, bool aPrivateBrowsing,
|
||||
//
|
||||
// Private shortcuts are not created by the installer (they're created
|
||||
// upon user request, ultimately by CreateShortcutImpl, and recorded in
|
||||
// a separate shortcuts log. As with non-private shortcutsthey have a known
|
||||
// a separate shortcuts log. As with non-private shortcuts they have a known
|
||||
// name - so there's no need to look through logs to find them.
|
||||
if (aPrivateBrowsing) {
|
||||
// This is explicitly not localized until we finalize the English string.
|
||||
@@ -1033,7 +1086,59 @@ static nsresult PinCurrentAppToTaskbarImpl(bool aCheckOnly,
|
||||
}
|
||||
}
|
||||
if (shortcutPath.IsEmpty()) {
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
if (aCheckOnly) {
|
||||
// The other checks we do further down can only be done if
|
||||
// a shortcut already exists. We don't want to do that in
|
||||
// check only mode, so we just have to assume it will work...
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString desc;
|
||||
nsTArray<nsString> arguments;
|
||||
|
||||
if (aPrivateBrowsing) {
|
||||
nsAutoString arg;
|
||||
// Localization disabled until we finalize the English string.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1758961 tracks following
|
||||
// up on this before it becomes user visible.
|
||||
/*nsAutoCString pbStr;
|
||||
IgnoredErrorResult rv;
|
||||
nsTArray<nsCString> resIds{
|
||||
"branding/brand.ftl"_ns,
|
||||
"browser/browser.ftl"_ns,
|
||||
};
|
||||
RefPtr<Localization> l10n = Localization::Create(resIds, true);
|
||||
l10n->FormatValueSync("private-browsing-shortcut-text"_ns, {}, pbStr, rv);
|
||||
desc.Assign(NS_ConvertUTF8toUTF16(pbStr));*/
|
||||
|
||||
desc.AssignLiteral(MOZ_APP_DISPLAYNAME " Private Browsing");
|
||||
arg.AssignLiteral("-private-window");
|
||||
arguments.AppendElement(arg);
|
||||
} else {
|
||||
desc.AssignLiteral(MOZ_APP_DISPLAYNAME);
|
||||
}
|
||||
|
||||
nsAutoString linkName(desc);
|
||||
linkName.AppendLiteral(".lnk");
|
||||
|
||||
nsAutoString exeStr;
|
||||
nsCOMPtr<nsIFile> exeFile;
|
||||
exeStr.Assign(exePath);
|
||||
nsresult rv = NS_NewLocalFile(exeStr, true, getter_AddRefs(exeFile));
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint16_t iconIndex = aPrivateBrowsing ? IDI_PBMODE : IDI_APPICON;
|
||||
// Icon indexes are defined as Resource IDs, but CreateShortcutImpl
|
||||
// needs an index.
|
||||
iconIndex--;
|
||||
|
||||
rv = CreateShortcutImpl(exeFile, arguments, desc, exeFile, iconIndex, aumid,
|
||||
FOLDERID_StartMenu, linkName, shortcutPath);
|
||||
if (!NS_SUCCEEDED(rv)) {
|
||||
return NS_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
mozilla::UniquePtr<__unaligned ITEMIDLIST, ILFreeDeleter> path(
|
||||
|
||||
Reference in New Issue
Block a user