/* 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 "imgIContainer.idl"
#include "nsIShellService.idl"

interface nsIFile;

[scriptable, uuid(fb9b59db-5a91-4e67-92b6-35e7d6e6d3fd)]
interface nsIWindowsShellService : nsIShellService
{
  /*
   * 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
   */
  [implicit_jscontext]
  Promise 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);

  /*
   * Searches the %USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
   * folder and returns an array with the path of all shortcuts with a target matching the
   * current Firefox install location. The AUMID isn't required here as we are only looking
   * for the currently running binary, whether that's firefox.exe or the private browsing
   * proxy executable.
   *
   * It is possible to return an empty array if no shortcuts are found.
   *
   * @return An array of paths for all launch on login shortcuts.s
   *
   * @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> getLaunchOnLoginShortcuts();

  /*
   * Pins the current application to the Windows Start Menu
   *
   * The APIs used within this function are MSIX only and
   * will also not work on MINGW.
   *
   * @param aCheckOnly If only testing whether pinning is possible
   *
   * @return True if the application was successfully pinned
   *              to the Start Menu.
   *
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If used on a non-MSIX build
   * @throws NS_ERROR_NOT_IMPLEMENTED
   *         If used on a MinGW build
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         If called off main thread
   * @throws NS_ERROR_FAILURE
   *         For Windows API failures
   */
  [implicit_jscontext]
  Promise pinCurrentAppToStartMenuAsync(in boolean aCheckOnly);

  /*
   * Whether the current app is among Windows Start Menu pins
   *
   * @return True if pinned, false otherwise
   *
   * The APIs used within this function are MSIX only and
   * will also not work on MINGW.
   *
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If used on a non-MSIX build
   * @throws NS_ERROR_NOT_IMPLEMENTED
   *         If used on a MinGW build
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         If called off main thread
   * @throws NS_ERROR_FAILURE
   *         For Windows API failures
   */
  [implicit_jscontext]
  Promise isCurrentAppPinnedToStartMenuAsync();

  /*
   * Disables the startup task corresponding to the provided taskId
   * to launch upon OS login. The startup task is declared
   * within the App Manifest.
   *
   * If the task was previously disabled by the user this function
   * will not re-enable it.
   *
   * The APIs used within this function are MSIX only and
   * will also not work on MINGW.
   *
   * @param aTaskId Target taskId to enable
   *
   * @return True if the application was successfully set up to
   *         launch on OS login.
   *
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If used on a non-MSIX build
   * @throws NS_ERROR_NOT_IMPLEMENTED
   *         If used on a MinGW build
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         If called off main thread
   * @throws NS_ERROR_FAILURE
   *         For other types of failures
   */
  [implicit_jscontext]
  Promise enableLaunchOnLoginMSIXAsync(in AString aTaskId);

  /*
   * Disables the startup task corresponding to the provided taskId
   * to launch upon OS login. The startup task is declared
   * within the App Manifest.
   *
   * The APIs used within this function are MSIX only and
   * will also not work on MINGW.
   *
   * @param aTaskId Target taskId to disable
   *
   * @return True if the application was successfully disabled from
   *         launching on OS login.
   *
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If used on a non-MSIX build
   * @throws NS_ERROR_NOT_IMPLEMENTED
   *         If used on a MinGW build
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         If called off main thread
   * @throws NS_ERROR_FAILURE
   *         For other types of failures
   */
  [implicit_jscontext]
  Promise disableLaunchOnLoginMSIXAsync(in AString aTaskId);

  /*
   * Determines if the startup task corresponding to the provided taskId to
   * launch upon OS login is enabled. The startup task is declared
   * within the App Manifest. The APIs used within this function are MSIX
   * only and will also not work on MINGW.
   *
   * If the user has disabled the application from launching on login, it
   * cannot be re-enabled by the application.
   *
   * @param aTaskId Target taskId to check status of
   *
   * @return 0/1/2/3 if the application's OS launch on login is
             disabled in settings / disabled / enabled / enabled by policy
   *
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If used on a non-MSIX build
   * @throws NS_ERROR_NOT_IMPLEMENTED
   *         If used on a MinGW build
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         If called off main thread
   * @throws NS_ERROR_FAILURE
   *         For other types of failures
   */

  cenum LaunchOnLoginEnabledEnumerator : 8 {
    LAUNCH_ON_LOGIN_DISABLED_BY_SETTINGS = 0,
    LAUNCH_ON_LOGIN_DISABLED = 1,
    LAUNCH_ON_LOGIN_ENABLED = 2,
    LAUNCH_ON_LOGIN_ENABLED_BY_POLICY = 3,
  };

  [implicit_jscontext]
  Promise getLaunchOnLoginEnabledMSIXAsync(in AString aTaskId);

  /*
   * Pin the current app to the taskbar. If aPrivateBrowsing is true, the
   * Private Browsing version of the app (with a different icon and launch
   * arguments) will be pinned instead.
   *
   * This MUST only be used in response to an active request from the user.
   *
   * 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: It is possible for the shortcut match to fail even when a
   * shortcut refers to the current executable, if the paths differ due
   * to e.g. symlinks. This should be rare.
   *
   * This will definitely fail on an OS before Windows 10 build 1809
   * (October 2018 Update).
   *
   * NOTE: Can only run on the main thread, but the actual work occurs on a
   * background thread.
   *
   * @throws NS_ERROR_NOT_SAME_THREAD
   *         if called off main thread.
   * @throws NS_ERROR_NOT_AVAILABLE
   *         if OS is not at least Windows 10 build 1809, or if creating the
   *         Taskband Pin object fails
   * @throws NS_ERROR_FAILURE
   *         for unexpected errors
   *
   * @rejects NS_ERROR_FILE_NOT_FOUND
   *         if a shortcut matching this app's AUMID and exe path wasn't found
   *
   * @returns {Promise<void>} A promise that resolves to |undefined| if
   *                          successful or rejects with an nserror.
   */
  [implicit_jscontext]
  Promise pinCurrentAppToTaskbarAsync(in boolean aPrivateBrowsing);

  /*
   * Do a dry run of pinCurrentAppToTaskbar().
   *
   * NOTE: Can only be run on the main thread, but the actual work occurs on a
   * background thread.
   *
   * This does all the same checks and setup, throws the same errors, but doesn't
   * do the final step of creating the pin.
   *
   * @throws same as pinCurrentAppToTaskbarAsync()
   * @rejects same as pinCurrentAppToTaskbarAsync()
   * @returns same as pinCurrentAppToTaskbarAsync()
   */
  [implicit_jscontext]
  Promise checkPinCurrentAppToTaskbarAsync(in boolean aPrivateBrowsing);

  /*
   * Search for the current executable among taskbar pins
   *
   * NOTE: Can only be run on the main thread, but the actual work occurs on a
   * background thread.
   *
   * NOTE: It is possible for the check to fail even when a taskbar pin refers
   * to this executable, if the paths differ due to e.g. symlinks.
   * It is also possible for the check to succeed with a shortcut that doesn't
   * actually appear on the taskbar.
   * These cases should be rare.
   *
   * @return Promise that always resolves, true if pinned, false otherwise
   * @throws NS_ERROR_NOT_SAME_THREAD if not run on the main thread
   *
   */
  [implicit_jscontext]
  Promise isCurrentAppPinnedToTaskbarAsync(in AString aumid);

  /*
   * Similar to createShortcut except it removes most of the checking in that
   * function that ensures we are pinning a Firefox executable instead allowing
   * any shortcut to be pinned.
   *
   * This function should not be called unless it is certain that it's
   * necessary given how few checks there are within.
   * @param  aAppUserModelId
   *         The aumid to be contained in the .lnk file
   * @param  aShortcutPath
   *         A path to the .lnk file that should be pinned to the taskbar.
   */
  [implicit_jscontext]
  Promise pinShortcutToTaskbar(
    in AString aAppUserModelId,
    in AString aShortcutPath
  );

  /* This function can be used to convert compatible image formats into .ico
   * files to be used for .lnk files (see createShortcut).
   *
   * This function should not be called unless it is certain that it's
   * necessary given how few checks there are within.
   * @param  aFile
   *         Output file to be written
   * @param  aContainer
   *         The graphics container
   */
  [implicit_jscontext]
  Promise createWindowsIcon(
    in nsIFile aFile,
    in imgIContainer aContainer
  );

  /*
   * This function is a counterpart to pinShortcutToTaskbar and allows
   * the unpinning of any shortcut, including non-Firefox executables,
   * without the checks of createShortcut.
   *
   * This function should not be called unless it is certain that it's
   * necessary given how few checks there are within.
   * @param  aShortcutPath
   *         A path to the .lnk file that should be pinned to the taskbar.
   * @throws NS_ERROR_FAILURE
   *         If the COM service could not be initialized
   * @throws NS_ERROR_FILE_NOT_FOUND
   *         If aShortcutPath cannot be found
   * @throws NS_ERROR_NOT_AVAILABLE
   *         If the taskbar pinning service cannot be initialized
   * @throws NS_ERROR_FILE_ACCESS_DENIED
   *         If the taskbar pins cannot be modified
   */
  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.
   *
   * Returns one of:
   * - "StartMenu" or "StartMenuPrivate", Current User or All Users Start
   *    Menu, including pins
   * - "Desktop" or "DesktopPrivate", Current User or All Users Desktop
   * - "Taskbar" or "TaskbarPrivate", Taskbar Pins
   * - "" otherwise
   *
   * If a Private Browsing shortcut was used to launch, the "Private"
   * variant of one of the above entries will be returned.
   *
   * NOTE: This tries to avoid I/O, so paths are compared directly as
   * strings, which may not be accurate in all cases. It is intended
   * for noncritical telemetry use.
   */
  AString classifyShortcut(in AString aPath);

  /*
   * Determine whether a pinnable shortcut with matching AUMID is present.
   *
   * @param  aAUMID
   *         AUMID of the shortcut to find.
   * @param  aPrivateBrowsing
   *         `true` to have the private browsing suffix added to `aAUMID`.
   *
   * @returns {Promise<void>} Always resolves with boolean, true if a pinnable
   *                          shortcut exists.
   */
  [implicit_jscontext]
  Promise hasPinnableShortcut(in AString aAUMID, in boolean aPrivateBrowsing);

  /*
   * Check if setDefaultBrowserUserChoice() is expected to succeed.
   *
   * This checks the ProgIDs for this installation, and the hash of the existing
   * UserChoice association.
   *
   * @return true if the check succeeds, false otherwise.
   */
  boolean canSetDefaultBrowserUserChoice();

  /*
   * checkAllProgIDsExist() and checkBrowserUserChoiceHashes() are components
   * of canSetDefaultBrowserUserChoice(), broken out for telemetry purposes.
   *
   * @return true if the check succeeds, false otherwise.
   */
  boolean checkAllProgIDsExist();
  boolean checkBrowserUserChoiceHashes();

  /*
   * Retrieves the application-defined, explicit Application User Model ID
   * for the current process. This function is only to be used on
   * Windows for testing purposes
   *
   * @return string AUMID
   * @throws NS_ERROR_FAILURE when the AUMID cannot be fetched
   */
  AString checkCurrentProcessAUMIDForTesting();

  /*
   * Determines whether or not Firefox is the "Default Handler", i.e.,
   * is registered to handle, the given file extension (like ".pdf")
   * or protocol (like "https").
   */
  boolean isDefaultHandlerFor(in AString aFileExtensionOrProtocol);


  /*
   * Return the Windows ProgID currently registered to handle the gven
   * file extension (like ".pdf") or protocol (like "https").
   *
   * @return string ProgID, or "" when no association is registered.
   * @throws NS_ERROR_FAILURE when the file extension or protocol
   *         cannot be determined.
   */
  AString queryCurrentDefaultHandlerFor(in AString aFileExtensionOrProtocol);
};
