Bug 300139 - API and command-line flag to install a XUL application r=robstrong (xulrunner-only)

git-svn-id: svn://10.0.0.236/branches/MOZILLA_1_8_BRANCH@181775 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bsmedberg%covad.net
2005-10-07 15:41:05 +00:00
parent c783c7beac
commit 1953135dda
8 changed files with 624 additions and 2 deletions

View File

@@ -44,6 +44,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
DIRS = \
setup \
stub \
util \
app \

View File

@@ -68,6 +68,7 @@ REQUIRES = \
string \
appshell \
xulapp \
xulrunner \
$(NULL)
CPPSRCS = nsXULRunnerApp.cpp

View File

@@ -47,8 +47,10 @@
#include "nsAppRunner.h"
#include "nsINIParser.h"
#include "nsILocalFile.h"
#include "nsIXULAppInstall.h"
#include "nsCOMPtr.h"
#include "nsMemory.h"
#include "nsNativeCharsetUtils.h"
#include "nsBuildID.h"
#include "plstr.h"
#include "prprf.h"
@@ -270,6 +272,8 @@ static void Usage()
" --register-user\n"
" --find-gre <version> Find a GRE with version <version> and print\n"
" the path on stdout\n"
" --install-app <application> [<destination> [<directoryname>]]\n"
" Install a XUL application.\n"
"\n"
"APP-FILE\n"
" Application initialization file.\n"
@@ -298,6 +302,53 @@ GetXULRunnerDir(const char *argv0, nsIFile* *aResult)
return rv;
}
static int
InstallXULApp(nsIFile* aXULRunnerDir,
const char *aAppLocation,
const char *aInstallTo,
const char *aLeafName)
{
nsCOMPtr<nsILocalFile> appLocation;
nsCOMPtr<nsILocalFile> installTo;
nsAutoString leafName;
nsresult rv = XRE_GetFileFromPath(aAppLocation, getter_AddRefs(appLocation));
if (NS_FAILED(rv))
return 2;
if (aInstallTo) {
rv = XRE_GetFileFromPath(aInstallTo, getter_AddRefs(installTo));
if (NS_FAILED(rv))
return 2;
}
if (aLeafName)
NS_CopyNativeToUnicode(nsDependentCString(aLeafName), leafName);
rv = NS_InitXPCOM2(nsnull, aXULRunnerDir, nsnull);
if (NS_FAILED(rv))
return 3;
{
// Scope our COMPtr to avoid holding XPCOM refs beyond xpcom shutdown
nsCOMPtr<nsIXULAppInstall> install
(do_GetService("@mozilla.org/xulrunner/app-install-service;1"));
if (!install) {
rv = NS_ERROR_FAILURE;
}
else {
rv = install->InstallApplication(appLocation, installTo, leafName);
}
}
NS_ShutdownXPCOM(nsnull);
if (NS_FAILED(rv))
return 3;
return 0;
}
int main(int argc, char* argv[])
{
if (argc > 1 && (IsArg(argv[1], "h") ||
@@ -376,6 +427,36 @@ int main(int argc, char* argv[])
printf("%s\n", GRE_BUILD_ID);
return 0;
}
if (IsArg(argv[1], "install-app")) {
if (argc < 3 || argc > 5) {
Usage();
return 1;
}
char *appLocation = argv[2];
char *installTo = nsnull;
if (argc > 3) {
installTo = argv[3];
if (!*installTo) // left blank?
installTo = nsnull;
}
char *leafName = nsnull;
if (argc > 4) {
leafName = argv[4];
if (!*leafName)
leafName = nsnull;
}
nsCOMPtr<nsIFile> regDir;
nsresult rv = GetXULRunnerDir(argv[0], getter_AddRefs(regDir));
if (NS_FAILED(rv))
return 2;
return InstallXULApp(regDir, appLocation, installTo, leafName);
}
}
geckoVersion = ParseVersion(GRE_BUILD_ID);

View File

@@ -8,7 +8,7 @@ Vendor=MozillaTest
; This field specifies your application's name. This field is required.
Name=Simple
;
; This field specifies your application's version. This field is optional.
; This field specifies your application's version. This field is required.
Version=0.1
;
; This field specifies your application's build ID (timestamp). This field is

View File

@@ -0,0 +1,54 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla XULRunner.
#
# The Initial Developer of the Original Code is
# Benjamin Smedberg <benjamin@smedbergs.us>.
#
# Portions created by the Initial Developer are Copyright (C) 2005
# the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
#
# Contributor(s):
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****
DEPTH = ../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = xulrunner
XPIDL_MODULE = xulapp_setup
XPIDLSRCS = \
nsIXULAppInstall.idl \
$(NULL)
EXTRA_PP_COMPONENTS = nsXULAppInstall.js
include $(topsrcdir)/config/rules.mk

View File

@@ -0,0 +1,68 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XULRunner.
*
* The Initial Developer of the Original Code is
* Benjamin Smedberg <benjamin@smedbergs.us>
*
* Portions created by the Initial Developer are Copyright (C) 2005
* the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.idl"
interface nsIFile;
/**
* Installation and management of XUL applications.
*
* @status IN_FLUX This interface is not stable and will change in the
* future.
*/
[scriptable, uuid(800ace15-6b38-48c4-b057-7928378f6cd2)]
interface nsIXULAppInstall : nsISupports
{
/**
* Install a XUL application into a form that can be run by the native
* operating system.
*
* @param aAppFile Directory or a zip file containing a
* XULRunner package (with the required application.ini
* file in the root).
* @param aDirectory Path specifying the location to install the
* application. If null, an appropriate default install
* location will be used. e.g. "C:\Program Files\<Vendor>"
* on Windows.
* @param aLeafName The leaf name of the application directory. If empty
* an appropriate default will be chosen. e.g. "Simple.app"
* on Mac.
*/
void installApplication(in nsIFile aAppFile, in nsIFile aDirectory,
in AString aLeafName);
};

View File

@@ -0,0 +1,412 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla XULRunner.
*
* The Initial Developer of the Original Code is
* Benjamin Smedberg <benjamin@smedbergs.us>
*
* Portions created by the Initial Developer are Copyright (C) 2005
* the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const nsIFile = Components.interfaces.nsIFile;
const nsIINIParser = Components.interfaces.nsIINIParser;
const nsIINIParserFactory = Components.interfaces.nsIINIParserFactory;
const nsILocalFile = Components.interfaces.nsILocalFile;
const nsISupports = Components.interfaces.nsISupports;
const nsIXULAppInstall = Components.interfaces.nsIXULAppInstall;
const nsIZipEntry = Components.interfaces.nsIZipEntry;
const nsIZipReader = Components.interfaces.nsIZipReader;
function getDirectoryKey(aKey) {
try {
return Components.classes["@mozilla.org/file/directory_service;1"].
getService(Components.interfaces.nsIProperties).
get(aKey, nsIFile);
}
catch (e) {
throw "Couln't get directory service key: " + aKey;
}
}
function createINIParser(aFile) {
return Components.manager.
getClassObjectByContractID("@mozilla.org/xpcom/ini-parser-factory;1",
nsIINIParserFactory).
createINIParser(aFile);
}
function copy_recurse(aSource, aDest) {
var e = aSource.directoryEntries;
while (e.hasMoreElements()) {
var f = e.getNext().QueryInterface(nsIFile);
var leaf = f.leafName;
var ddest = aDest.clone();
ddest.append(leaf);
if (f.isDirectory()) {
copy_recurse(f, ddest);
}
else {
if (ddest.exists())
ddest.remove(false);
f.copyTo(aDest, leaf);
}
}
}
const PR_WRONLY = 0x02;
const PR_CREATE_FILE = 0x08;
const PR_TRUNCATE = 0x20;
function openFileOutputStream(aFile) {
var s = Components.classes["@mozilla.org/network/file-output-stream;1"].
createInstance(Components.interfaces.nsIFileOutputStream);
s.init(aFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, 0);
return s;
}
/**
* An extractor implements the following prototype:
* readonly attribute nsIINIPaser iniParser;
* void copyTo(in nsILocalFile root);
*/
function directoryExtractor(aFile) {
this.mDirectory = aFile;
}
directoryExtractor.prototype = {
mINIParser : null,
get iniParser() {
if (!this.mINIParser) {
var iniFile = this.mDirectory.clone();
iniFile.append("application.ini");
this.mINIParser = createINIParser(iniFile);
}
return this.mINIParser;
},
copyTo : function de_copyTo(aDest) {
// Assume the root already exists
copy_recurse(this.mDirectory, aDest);
}
};
function zipExtractor(aFile) {
this.mZipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"].
createInstance(nsIZipReader);
this.mZipReader.init(aFile);
this.mZipReader.open();
this.mZipReader.test(null);
}
zipExtractor.prototype = {
mINIParser : null,
get iniParser() {
if (!this.mINIParser) {
// XXXbsmedberg: this is not very unique, guessing could be a problem
var f = getDirectoryKey("TmpD");
f.append("application.ini");
f.createUnique(nsIFile.NORMAL_FILE_TYPE, 0600);
try {
this.mZipReader.extract("application.ini", f);
this.mINIParser = createINIParser(f);
}
catch (e) {
try {
f.remove();
}
catch (ee) { }
throw e;
}
try {
f.remove();
}
catch (e) { }
}
return this.mINIParser;
},
copyTo : function ze_CopyTo(aDest) {
var entries = this.mZipReader.findEntries("*");
while (entries.hasMoreElements()) {
var entry = entries.getNext().QueryInterface(nsIZipEntry);
this._installZipEntry(this.mZipReader, entry, aDest);
}
},
_installZipEntry : function ze_installZipEntry(aZipReader, aZipEntry,
aDestination) {
var file = aDestination.clone();
var path = aZipEntry.name;
var dirs = path.split(/\//);
var isDirectory = path.match(/\/$/) != null;
var end = dirs.length;
if (!isDirectory)
--end;
for (var i = 0; i < end; ++i) {
file.append(dirs[i]);
if (!file.exists()) {
file.create(nsIFile.DIRECTORY_TYPE, 0755);
}
}
if (!isDirectory) {
file.append(dirs[end]);
aZipReader.extract(path, file);
}
}
};
function createExtractor(aFile) {
if (aFile.isDirectory())
return new directoryExtractor(aFile);
return new zipExtractor(aFile);
}
const AppInstall = {
/* nsISupports */
QueryInterface : function ai_QI(iid) {
if (iid.equals(nsIXULAppInstall) ||
iid.equals(nsISupports))
return this;
throw Components.result.NS_ERROR_NO_INTERFACE;
},
/* nsIXULAppInstall */
installApplication : function ai_IA(aAppFile, aDirectory, aLeafName) {
var extractor = createExtractor(aAppFile);
var iniParser = extractor.iniParser;
var appName = iniParser.getString("App", "Name");
// vendor is optional
var vendor;
try {
vendor = iniParser.getString("App", "Vendor");
}
catch (e) { }
if (aDirectory == null) {
#ifdef XP_WIN
aDirectory = getDirectoryKey("ProgF");
if (vendor)
aDirectory.append(vendor);
#else
#ifdef XP_MACOSX
aDirectory = getDirectoryKey("LocApp");
if (vendor)
aDirectory.append(vendor);
#else
aDirectory = Components.classes["@mozilla.org/file/local;1"].
createInstance(nsILocalFile);
aDirectory.initWithPath("/usr/lib");
aDirectory.append(vendor.toLowerCase());
#endif
#endif
}
else {
aDirectory = aDirectory.clone();
}
if (!aDirectory.exists()) {
aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
}
if (aLeafName == "") {
#ifdef XP_MACOSX
aLeafName = appName + ".app";
#else
#ifdef XP_WIN
aLeafName = appName;
#else
aLeafName = appName.toLowerCase();
#endif
#endif
}
aDirectory.append(aLeafName);
if (!aDirectory.exists()) {
aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
}
#ifdef XP_MACOSX
aDirectory.append("Contents");
if (!aDirectory.exists()) {
aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
}
var version = iniParser.getString("App", "Version");
var buildID = iniParser.getString("App", "BuildID");
var infoString = "";
if (vendor) {
infoString = vendor + " ";
}
infoString += appName + " " + version;
var plistFile = aDirectory.clone();
plistFile.append("Info.plist");
var ostream = openFileOutputStream(plistFile);
var contents =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
"<plist version=\"1.0\">\n" +
"<dict>\n" +
"<key>CFBundleInfoDictionaryVersion</key>\n" +
"<string>6.0</string>\n" +
"<key>CFBundlePackageType</key>\n" +
"<string>APPL</string>\n" +
"<key>CFBundleExecutable</key>\n" +
"<string>xulrunner</string>\n" +
"<key>NSAppleScriptEnabled</key>\n" +
"<true/>\n" +
"<key>CFBundleGetInfoString</key>\n" +
"<string>" + infoString + "</string>\n" +
"<key>CFBundleName</key>\n" +
"<string>" + appName + "</string>\n" +
"<key>CFBundleShortVersionString</key>\n" +
"<string>" + version + "</string>\n" +
"<key>CFBundleVersion</key>\n" +
"<string>" + version + "." + buildID + "</string>\n" +
"</dict>\n" +
"</plist>";
// "<key>CFBundleIdentifier</key>\n" +
// "<string>org.%s.%s</string>\n" +
// "<key>CFBundleSignature</key>\n" +
// "<string>MOZB</string>\n" +
// "<key>CFBundleIconFile</key>\n" +
// "<string>document.icns</string>\n" +
ostream.write(contents, contents.length);
ostream.close();
var contentsDir = aDirectory.clone();
contentsDir.append("MacOS");
var xulrunnerBinary = getDirectoryKey("XCurProcD");
xulrunnerBinary.append("xulrunner");
xulrunnerBinary.copyTo(contentsDir, "xulrunner");
aDirectory.append("Resources");
extractor.copyTo(aDirectory);
#else
extractor.copyTo(aDirectory);
// Need to copy the XULRunner stub (which doesn't exist yet)
#endif
}
};
const AppInstallFactory = {
/* nsISupports */
QueryInterface : function aif_QI(iid) {
if (iid.equals(Components.interfaces.nsIFactory) ||
iid.equals(nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/* nsIFactory */
createInstance : function aif_CI(aOuter, aIID) {
if (aOuter)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return AppInstall.QueryInterface(aIID);
},
lockFactory : function aif_lock(aLock) { }
};
const AppInstallContractID = "@mozilla.org/xulrunner/app-install-service;1";
const AppInstallCID = Components.ID("{00790a19-27e2-4d9a-bef0-244080feabfd}");
const AppInstallModule = {
/* nsISupports */
QueryInterface : function mod_QI(iid) {
if (iid.equals(Components.interfaces.nsIModule) ||
iid.equals(nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/* nsIModule */
getClassObject : function mod_gco(aCompMgr, aClass, aIID) {
if (aClass.equals(AppInstallCID))
return AppInstallFactory.QueryInterface(aIID);
return Components.results.NS_ERROR_FACTORY_NOT_REGISTERED;
},
registerSelf : function mod_regself(aCompMgr, aLocation,
aLoaderStr, aType) {
var reg = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
reg.registerFactoryLocation(AppInstallCID,
"nsXULAppInstall",
AppInstallContractID,
aLocation,
aLoaderStr,
aType);
},
unregisterSelf : function mod_unreg(aCompMgr, aLocation, aLoaderStr) {
var reg = aCompMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
reg.unregisterFactoryLocation(AppInstallCID,
aLocation);
},
canUnload : function mod_unload(aCompMgr) {
return true;
}
};
function NSGetModule(compMgr, fileSpec) {
return AppInstallModule;
}

View File

@@ -70,7 +70,7 @@ main(int argc, char **argv)
absResourcesURL,
CFSTR("application.ini"),
false);
CFRelease(resourcesURL);
CFRelease(absResourcesURL);
if (!iniFileURL)
return 1;
@@ -131,6 +131,11 @@ main(int argc, char **argv)
return 1;
}
char *lastSlash = strrchr(greDir, '/');
if (lastSlash) {
*lastSlash = '\0';
}
char **argv2 = (char**) alloca(sizeof(char*) * (argc + 2));
char xulBin[PATH_MAX];