mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Bug 1602191: Create a shortcut on the windows desktop to launch an installed ssb. r=mhowell,Gijs
Adds an XPCOM API for creating a windows shortcut and uses it to create one when installing a SSB. Differential Revision: https://phabricator.services.mozilla.com/D56292 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
82b556f808
commit
541fc82db0
@ -35,6 +35,9 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gtk':
|
||||
'nsGNOMEShellService.cpp',
|
||||
]
|
||||
elif CONFIG['OS_ARCH'] == 'WINNT':
|
||||
XPIDL_SOURCES += [
|
||||
'nsIWindowsShellService.idl',
|
||||
]
|
||||
SOURCES += [
|
||||
'nsWindowsShellService.cpp',
|
||||
]
|
||||
|
13
browser/components/shell/nsIWindowsShellService.idl
Normal file
13
browser/components/shell/nsIWindowsShellService.idl
Normal file
@ -0,0 +1,13 @@
|
||||
/* 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 "nsISupports.idl"
|
||||
|
||||
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 aTarget);
|
||||
};
|
@ -57,11 +57,29 @@
|
||||
|
||||
#define APP_REG_NAME_BASE L"Firefox-"
|
||||
|
||||
#ifdef DEBUG
|
||||
# define NS_ENSURE_HRESULT(hres, ret) \
|
||||
do { \
|
||||
HRESULT result = hres; \
|
||||
if (MOZ_UNLIKELY(FAILED(result))) { \
|
||||
mozilla::SmprintfPointer msg = mozilla::Smprintf( \
|
||||
"NS_ENSURE_HRESULT(%s, %s) failed with " \
|
||||
"result 0x%" PRIX32, \
|
||||
#hres, #ret, static_cast<uint32_t>(result)); \
|
||||
NS_WARNING(msg.get()); \
|
||||
return ret; \
|
||||
} \
|
||||
} while (false)
|
||||
#else
|
||||
# define NS_ENSURE_HRESULT(hres, ret) \
|
||||
if (MOZ_UNLIKELY(FAILED(hres))) return ret
|
||||
#endif
|
||||
|
||||
using mozilla::IsWin8OrLater;
|
||||
using namespace mozilla;
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIToolkitShellService,
|
||||
nsIShellService)
|
||||
nsIShellService, nsIWindowsShellService)
|
||||
|
||||
static nsresult OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName,
|
||||
HKEY* aKey) {
|
||||
@ -679,6 +697,45 @@ nsWindowsShellService::SetDesktopBackgroundColor(uint32_t aColor) {
|
||||
return regKey->Close();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindowsShellService::CreateShortcut(nsIFile* aBinary,
|
||||
const nsTArray<nsString>& aArguments,
|
||||
const nsAString& aDescription,
|
||||
nsIFile* aTarget) {
|
||||
NS_ENSURE_ARG(aBinary);
|
||||
NS_ENSURE_ARG(aTarget);
|
||||
|
||||
RefPtr<IShellLinkW> link;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IShellLinkW, getter_AddRefs(link));
|
||||
NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
|
||||
|
||||
nsString path(aBinary->NativePath());
|
||||
link->SetPath(path.get());
|
||||
|
||||
if (!aDescription.IsEmpty()) {
|
||||
link->SetDescription(PromiseFlatString(aDescription).get());
|
||||
}
|
||||
|
||||
// TODO: Properly escape quotes in the string, see bug 1604287.
|
||||
nsString arguments;
|
||||
for (auto& arg : aArguments) {
|
||||
arguments.AppendPrintf("\"%S\" ", arg.get());
|
||||
}
|
||||
|
||||
link->SetArguments(arguments.get());
|
||||
|
||||
RefPtr<IPersistFile> persist;
|
||||
hr = link->QueryInterface(IID_IPersistFile, getter_AddRefs(persist));
|
||||
NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
|
||||
|
||||
nsString target(aTarget->NativePath());
|
||||
hr = persist->Save(target.get(), TRUE);
|
||||
NS_ENSURE_HRESULT(hr, NS_ERROR_FAILURE);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsWindowsShellService::nsWindowsShellService() {}
|
||||
|
||||
nsWindowsShellService::~nsWindowsShellService() {}
|
||||
|
@ -10,12 +10,14 @@
|
||||
#include "nsString.h"
|
||||
#include "nsToolkitShellService.h"
|
||||
#include "nsIShellService.h"
|
||||
#include "nsIWindowsShellService.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <ole2.h>
|
||||
|
||||
class nsWindowsShellService : public nsIShellService,
|
||||
public nsToolkitShellService {
|
||||
public nsToolkitShellService,
|
||||
public nsIWindowsShellService {
|
||||
virtual ~nsWindowsShellService();
|
||||
|
||||
public:
|
||||
@ -23,6 +25,7 @@ class nsWindowsShellService : public nsIShellService,
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISHELLSERVICE
|
||||
NS_DECL_NSIWINDOWSSHELLSERVICE
|
||||
|
||||
protected:
|
||||
nsresult LaunchControlPanelDefaultsSelectionUI();
|
||||
|
@ -39,8 +39,17 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
KeyValueService: "resource://gre/modules/kvstore.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
ImageTools: "resource:///modules/ssb/ImageTools.jsm",
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
});
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
ChromeUtils.defineModuleGetter(
|
||||
this,
|
||||
"WindowsSupport",
|
||||
"resource:///modules/ssb/WindowsSupport.jsm"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A schema version for the SSB data stored in the kvstore.
|
||||
*
|
||||
@ -418,9 +427,13 @@ class SiteSpecificBrowser extends SiteSpecificBrowserBase {
|
||||
);
|
||||
}
|
||||
|
||||
return SiteSpecificBrowser.createFromManifest(
|
||||
await buildManifestForBrowser(browser)
|
||||
);
|
||||
let manifest = await buildManifestForBrowser(browser);
|
||||
let ssb = SiteSpecificBrowser.createFromManifest(manifest);
|
||||
|
||||
if (!manifest.name) {
|
||||
ssb.name = browser.contentTitle;
|
||||
}
|
||||
return ssb;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -479,6 +492,10 @@ class SiteSpecificBrowser extends SiteSpecificBrowserBase {
|
||||
|
||||
this._config.persisted = true;
|
||||
await this._maybeSave();
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
await WindowsSupport.install(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -490,6 +507,10 @@ class SiteSpecificBrowser extends SiteSpecificBrowserBase {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
await WindowsSupport.uninstall(this);
|
||||
}
|
||||
|
||||
this._config.persisted = false;
|
||||
let kvstore = await SiteSpecificBrowserService.getKVStore();
|
||||
await kvstore.delete(storeKey(this.id));
|
||||
@ -502,6 +523,23 @@ class SiteSpecificBrowser extends SiteSpecificBrowserBase {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get name() {
|
||||
if (this._config.name) {
|
||||
return this._config.name;
|
||||
}
|
||||
|
||||
if (this._manifest.name) {
|
||||
return this._manifest.name;
|
||||
}
|
||||
|
||||
return this.startURI.host;
|
||||
}
|
||||
|
||||
set name(val) {
|
||||
this._config.name = val;
|
||||
this._maybeSave();
|
||||
}
|
||||
|
||||
/**
|
||||
* The default URI to load.
|
||||
*/
|
||||
@ -658,6 +696,19 @@ const SiteSpecificBrowserService = {
|
||||
return this.kvstore;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if OS integration is enabled. This will affect whether installs and
|
||||
* uninstalls have effects on the OS itself amongst other things. Generally
|
||||
* only disabled for testing.
|
||||
*/
|
||||
get useOSIntegration() {
|
||||
if (Services.appinfo.OS != "WINNT") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Services.prefs.getBoolPref("browser.ssb.osintegration", true);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a promise that resolves to an array of all of the installed SSBs.
|
||||
*/
|
||||
|
65
browser/components/ssb/WindowsSupport.jsm
Normal file
65
browser/components/ssb/WindowsSupport.jsm
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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/. */
|
||||
|
||||
var EXPORTED_SYMBOLS = ["WindowsSupport"];
|
||||
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
const { SiteSpecificBrowserService } = ChromeUtils.import(
|
||||
"resource:///modules/SiteSpecificBrowserService.jsm"
|
||||
);
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
const shellService = Cc["@mozilla.org/browser/shell-service;1"].getService(
|
||||
Ci.nsIWindowsShellService
|
||||
);
|
||||
|
||||
const File = Components.Constructor(
|
||||
"@mozilla.org/file/local;1",
|
||||
Ci.nsIFile,
|
||||
"initWithPath"
|
||||
);
|
||||
|
||||
const WindowsSupport = {
|
||||
/**
|
||||
* Installs an SSB by creating a shortcut to launch it on the user's desktop.
|
||||
*
|
||||
* @param {SiteSpecificBrowser} ssb the SSB to install.
|
||||
*/
|
||||
async install(ssb) {
|
||||
if (!SiteSpecificBrowserService.useOSIntegration) {
|
||||
return;
|
||||
}
|
||||
|
||||
let desktop = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
let link = OS.Path.join(desktop.path, `${ssb.name}.lnk`);
|
||||
|
||||
shellService.createShortcut(
|
||||
Services.dirsvc.get("XREExeF", Ci.nsIFile),
|
||||
["-profile", OS.Constants.Path.profileDir, "-start-ssb", ssb.id],
|
||||
ssb.name,
|
||||
new File(link)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstalls an SSB by deleting its shortcut from the user's desktop.
|
||||
*
|
||||
* @param {SiteSpecificBrowser} ssb the SSB to uninstall.
|
||||
*/
|
||||
async uninstall(ssb) {
|
||||
if (!SiteSpecificBrowserService.useOSIntegration) {
|
||||
return;
|
||||
}
|
||||
|
||||
let desktop = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
let link = OS.Path.join(desktop.path, `${ssb.name}.lnk`);
|
||||
|
||||
try {
|
||||
await OS.File.remove(link);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
};
|
@ -24,3 +24,8 @@ FINAL_TARGET_FILES.actors += [
|
||||
'SiteSpecificBrowserChild.jsm',
|
||||
'SiteSpecificBrowserParent.jsm',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
EXTRA_JS_MODULES.ssb += [
|
||||
'WindowsSupport.jsm',
|
||||
]
|
||||
|
@ -5,6 +5,7 @@ support-files =
|
||||
empty_page.html
|
||||
prefs =
|
||||
browser.ssb.enabled=true
|
||||
browser.ssb.osintegration=false
|
||||
|
||||
[browser_ssb_direct.js]
|
||||
[browser_ssb_lasttab.js]
|
||||
|
@ -13,6 +13,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ManifestProcessor: "resource://gre/modules/ManifestProcessor.jsm",
|
||||
KeyValueService: "resource://gre/modules/kvstore.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
});
|
||||
|
||||
const SSB_STORE_PREFIX = "ssb:";
|
||||
@ -25,6 +26,7 @@ let gSSBData = gProfD.clone();
|
||||
gSSBData.append("ssb");
|
||||
|
||||
Services.prefs.setBoolPref("browser.ssb.enabled", true);
|
||||
Services.prefs.setBoolPref("browser.ssb.osintegration", false);
|
||||
|
||||
async function getKVStore() {
|
||||
await OS.File.makeDir(gSSBData.path);
|
||||
|
@ -3,6 +3,8 @@
|
||||
|
||||
// Tests that installing adds it to the store.
|
||||
add_task(async () => {
|
||||
Services.prefs.setBoolPref("browser.ssb.osintegration", true);
|
||||
|
||||
let ssb = SiteSpecificBrowser.createFromURI(uri("https://www.mozilla.org/"));
|
||||
await ssb.install();
|
||||
|
||||
@ -17,4 +19,15 @@ add_task(async () => {
|
||||
let data = JSON.parse(await kvstore.get(`ssb:${ssb.id}`));
|
||||
Assert.ok("manifest" in data);
|
||||
Assert.ok("config" in data);
|
||||
|
||||
if (AppConstants.platform == "win") {
|
||||
// Check that the shortcut is made and destroyed.
|
||||
let link = Services.dirsvc.get("Desk", Ci.nsIFile);
|
||||
link.append("www.mozilla.org.lnk");
|
||||
|
||||
Assert.ok(link.isFile());
|
||||
|
||||
await ssb.uninstall();
|
||||
Assert.ok(!link.exists());
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user