Follow up fix for bug 623435. (r=brendan)
[mozilla-central.git] / xulrunner / setup / nsXULAppInstall.js
blob01f966ba69c704e446c8e9876c8fce48ad073a9f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 #filter substitution
3 #if 0
4 /* ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is Mozilla XULRunner.
18  *
19  * The Initial Developer of the Original Code is
20  * Benjamin Smedberg <benjamin@smedbergs.us>
21  *
22  * Portions created by the Initial Developer are Copyright (C) 2005
23  * the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
24  *
25  * Contributor(s):
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 #endif
42 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
44 const nsIFile             = Components.interfaces.nsIFile;
45 const nsIINIParser        = Components.interfaces.nsIINIParser;
46 const nsIINIParserFactory = Components.interfaces.nsIINIParserFactory;
47 const nsILocalFile        = Components.interfaces.nsILocalFile;
48 const nsISupports         = Components.interfaces.nsISupports;
49 const nsIXULAppInstall    = Components.interfaces.nsIXULAppInstall;
50 const nsIZipReader        = Components.interfaces.nsIZipReader;
52 function getDirectoryKey(aKey) {
53   try {
54     return Components.classes["@mozilla.org/file/directory_service;1"].
55       getService(Components.interfaces.nsIProperties).
56       get(aKey, nsIFile);
57   }
58   catch (e) {
59     throw "Couln't get directory service key: " + aKey;
60   }
63 function createINIParser(aFile) {
64   return Components.manager.
65     getClassObjectByContractID("@mozilla.org/xpcom/ini-parser-factory;1",
66                                nsIINIParserFactory).
67     createINIParser(aFile);
70 function copy_recurse(aSource, aDest) {
71   var e = aSource.directoryEntries;
73   while (e.hasMoreElements()) {
74     var f = e.getNext().QueryInterface(nsIFile);
75     var leaf = f.leafName;
77     var ddest = aDest.clone();
78     ddest.append(leaf);
80     if (f.isDirectory()) {
81       copy_recurse(f, ddest);
82     }
83     else {
84       if (ddest.exists())
85         ddest.remove(false);
87       f.copyTo(aDest, leaf);
88     }
89   }
92 const PR_WRONLY = 0x02;
93 const PR_CREATE_FILE = 0x08;
94 const PR_TRUNCATE = 0x20;
96 function openFileOutputStream(aFile) {
97   var s = Components.classes["@mozilla.org/network/file-output-stream;1"].
98     createInstance(Components.interfaces.nsIFileOutputStream);
99   s.init(aFile, PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0644, 0);
100   return s;
104  * An extractor implements the following prototype:
105  * readonly attribute nsIINIPaser iniParser;
106  * void copyTo(in nsILocalFile root);
107  */
109 function directoryExtractor(aFile) {
110   this.mDirectory = aFile;
113 directoryExtractor.prototype = {
114   mINIParser : null,
116   get iniParser() {
117     if (!this.mINIParser) {
118       var iniFile = this.mDirectory.clone();
119       iniFile.append("application.ini");
121       this.mINIParser = createINIParser(iniFile);
122     }
123     return this.mINIParser;
124   },
126   copyTo : function de_copyTo(aDest) {
127     // Assume the root already exists
128     copy_recurse(this.mDirectory, aDest);
129   }
132 function zipExtractor(aFile) {
133   this.mZipReader = Components.classes["@mozilla.org/libjar/zip-reader;1"].
134     createInstance(nsIZipReader);
135   this.mZipReader.open(aFile);
136   this.mZipReader.test(null);
139 zipExtractor.prototype = {
140   mINIParser : null,
142   get iniParser() {
143     if (!this.mINIParser) {
144       // XXXbsmedberg: this is not very unique, guessing could be a problem
145       var f = getDirectoryKey("TmpD");
146       f.append("application.ini");
147       f.createUnique(nsIFile.NORMAL_FILE_TYPE, 0600);
149       try {
150         this.mZipReader.extract("application.ini", f);
151         this.mINIParser = createINIParser(f);
152       }
153       catch (e) {
154         try {
155           f.remove();
156         }
157         catch (ee) { }
159         throw e;
160       }
161       try {
162         f.remove();
163       }
164       catch (e) { }
165     }
166     return this.mINIParser;
167   },
169   copyTo : function ze_CopyTo(aDest) {
170     var entries = this.mZipReader.findEntries(null);
171     while (entries.hasMore()) {
172       var entryName = entries.getNext();
174       this._installZipEntry(this.mZipReader, entryName, aDest);
175     }
176   },
178   _installZipEntry : function ze_installZipEntry(aZipReader, aZipEntry,
179                                                  aDestination) {
180     var file = aDestination.clone();
182     var dirs = aZipEntry.split(/\//);
183     var isDirectory = /\/$/.test(aZipEntry);
185     var end = dirs.length;
186     if (!isDirectory)
187       --end;
189     for (var i = 0; i < end; ++i) {
190       file.append(dirs[i]);
191       if (!file.exists()) {
192         file.create(nsIFile.DIRECTORY_TYPE, 0755);
193       }
194     }
196     if (!isDirectory) {
197       file.append(dirs[end]);
198       aZipReader.extract(aZipEntry, file);
199     }
200   }
203 function createExtractor(aFile) {
204   if (aFile.isDirectory())
205     return new directoryExtractor(aFile);
207   return new zipExtractor(aFile);
210 function AppInstall() {
213 AppInstall.prototype = {
214   classID: Components.ID("{00790a19-27e2-4d9a-bef0-244080feabfd}"),
216   /* nsISupports */
217   QueryInterface : XPCOMUtils.generateQI([nsIXULAppInstall]),
219   /* nsIXULAppInstall */
220   installApplication : function ai_IA(aAppFile, aDirectory, aLeafName) {
221     var extractor = createExtractor(aAppFile);
222     var iniParser = extractor.iniParser;
224     var appName = iniParser.getString("App", "Name");
226     // vendor is optional
227     var vendor;
228     try {
229       vendor = iniParser.getString("App", "Vendor");
230     }
231     catch (e) { }
233     if (aDirectory == null) {
234 #ifdef XP_WIN
235       aDirectory = getDirectoryKey("ProgF");
236       if (vendor)
237         aDirectory.append(vendor);
238 #else
239 #ifdef XP_MACOSX
240       aDirectory = getDirectoryKey("LocApp");
241       if (vendor)
242         aDirectory.append(vendor);
243 #else
244       aDirectory = Components.classes["@mozilla.org/file/local;1"].
245         createInstance(nsILocalFile);
246       aDirectory.initWithPath("/usr/local/lib");
247       if (vendor)
248         aDirectory.append(vendor.toLowerCase());
249 #endif
250 #endif
251     }
252     else {
253       aDirectory = aDirectory.clone();
254     }
256     if (!aDirectory.exists()) {
257       aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
258     }
260     if (aLeafName == "") {
261 #ifdef XP_MACOSX
262       aLeafName = appName + ".app";
263 #else
264 #ifdef XP_WIN
265       aLeafName = appName;
266 #else
267       aLeafName = appName.toLowerCase();
268 #endif
269 #endif
270     }
272     aDirectory.append(aLeafName);
273     if (!aDirectory.exists()) {
274       aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
275     }
277 #ifdef XP_MACOSX
278     aDirectory.append("Contents");
279     if (!aDirectory.exists()) {
280       aDirectory.create(nsIFile.DIRECTORY_TYPE, 0755);
281     }
283     var version = iniParser.getString("App", "Version");
284     var buildID = iniParser.getString("App", "BuildID");
286     var infoString = "";
287     if (vendor) {
288       infoString = vendor + " ";
289     }
290     infoString += appName + " " + version;
292     var plistFile = aDirectory.clone();
293     plistFile.append("Info.plist");
294     var ostream = openFileOutputStream(plistFile);
296     var contents =
297     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
298     "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" +
299     "<plist version=\"1.0\">\n" +
300     "<dict>\n" +
301     "<key>CFBundleInfoDictionaryVersion</key>\n" +
302     "<string>6.0</string>\n" +
303     "<key>CFBundlePackageType</key>\n" +
304     "<string>APPL</string>\n" +
305     "<key>CFBundleExecutable</key>\n" +
306     "<string>xulrunner</string>\n" +
307     "<key>NSAppleScriptEnabled</key>\n" +
308     "<true/>\n" +
309     "<key>CFBundleGetInfoString</key>\n" +
310     "<string>" + infoString + "</string>\n" +
311     "<key>CFBundleName</key>\n" +
312     "<string>" + appName + "</string>\n" +
313     "<key>CFBundleShortVersionString</key>\n" +
314     "<string>" + version + "</string>\n" +
315     "<key>CFBundleVersion</key>\n" +
316     "<string>" + version + "." + buildID + "</string>\n" +
317     "</dict>\n" +
318     "</plist>";
320     // "<key>CFBundleIdentifier</key>\n" +
321     // "<string>org.%s.%s</string>\n" +
322     // "<key>CFBundleSignature</key>\n" +
323     // "<string>MOZB</string>\n" +
324     // "<key>CFBundleIconFile</key>\n" +
325     // "<string>document.icns</string>\n" +
327     ostream.write(contents, contents.length);
328     ostream.close();
330     var contentsDir = aDirectory.clone();
331     contentsDir.append("MacOS");
333     var xulrunnerBinary = getDirectoryKey("GreD");
334     xulrunnerBinary.append("xulrunner");
336     xulrunnerBinary.copyTo(contentsDir, "xulrunner");
338     aDirectory.append("Resources");
339     extractor.copyTo(aDirectory);
340 #else
341     extractor.copyTo(aDirectory);
343     var xulrunnerBinary = getDirectoryKey("GreD");
344     xulrunnerBinary.append("xulrunner-stub@BIN_SUFFIX@");
346     xulrunnerBinary.copyTo(aDirectory, appName.toLowerCase() + "@BIN_SUFFIX@");
347 #endif
348   }
351 const NSGetFactory = XPCOMUtils.generateNSGetFactory([AppInstall]);