/* ***** 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.org code. * * The Initial Developer of the Original Code is * the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Strong (Original Author) * * 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 INSTALL_LOCALE = "@AB_CD@"; const APP_BIN_NAME = "@MOZ_APP_NAME@"; const BIN_SUFFIX = "@BIN_SUFFIX@"; #ifdef XP_UNIX const APP_BIN_SUFFIX = "-bin"; #else const APP_BIN_SUFFIX = "@BIN_SUFFIX@"; #endif #ifdef XP_WIN const IS_WIN = true; #else const IS_WIN = false; #endif #ifdef WINCE const IS_WINCE = true; #else const IS_WINCE = false; #endif #ifdef XP_OS2 const IS_OS2 = true; #else const IS_OS2 = false; #endif #ifdef XP_MACOSX const IS_MACOSX = true; #else const IS_MACOSX = false; #endif #ifdef XP_UNIX const IS_UNIX = true; #else const IS_UNIX = false; #endif #ifdef ANDROID const IS_ANDROID = true; #else const IS_ANDROID = false; #endif const URL_HOST = "http://localhost:4444/"; const URL_PATH = "data"; const AFTER_APPLY_DIR = "afterApplyDir"; const APPLY_TO_DIR_SUFFIX = "_applyToDir"; const HELPER_BIN_FILE = "TestAUSHelper" + BIN_SUFFIX; const MAR_COMPLETE_FILE = "data/complete.mar"; const MAR_PARTIAL_FILE = "data/partial.mar"; const UPDATER_BIN_FILE = "updater" + BIN_SUFFIX; const UPDATES_DIR_SUFFIX = "_mar"; const CALLBACK_BIN_FILE = "callback_app" + BIN_SUFFIX; const CALLBACK_ARGS = [AFTER_APPLY_DIR + "/output.log", "Test Arg 2", "Test Arg 3"]; var gTestserver; var gXHR; var gXHRCallback; var gCheckFunc; var gResponseBody; var gResponseStatusCode = 200; var gRequestURL; var gUpdateCount; var gUpdates; var gStatusCode; var gStatusText; // Set to true to log additional information for debugging. To log additional // information for an individual test set DEBUG_AUS_TEST to true in the test's // run_test function. var DEBUG_AUS_TEST = true; #include ../shared.js /** * Nulls out the most commonly used global vars used by tests as appropriate. */ function cleanUp() { removeUpdateDirsAndFiles(); // Force the update manager to reload the update data to prevent it from // writing the old data to the files that have just been removed. reloadUpdateManagerData(); // Call app update's observe method passing xpcom-shutdown to test that the // shutdown of app update runs without throwing or leaking. The observer // method is used directly instead of calling notifyObservers so components // outside of the scope of this test don't assert and thereby cause app update // tests to fail. gAUS.observe(null, "xpcom-shutdown", ""); Services.dirsvc.unregisterProvider(gDirProvider); if (gXHR) { gXHRCallback = null; gXHR.responseXML = null; // null out the event handlers to prevent a mFreeCount leak of 1 gXHR.onerror = null; gXHR.onload = null; gXHR.onprogress = null; gXHR = null; } gTestserver = null; } /** * Sets the most commonly used preferences used by tests */ function setDefaultPrefs() { Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true); // Don't display UI for a successful installation. Some apps may not set this // pref to false like Firefox does. Services.prefs.setBoolPref(PREF_APP_UPDATE_SHOW_INSTALLED_UI, false); // Enable Update logging Services.prefs.setBoolPref(PREF_APP_UPDATE_LOG, true); } /** * Initializes the most commonly used settings and creates an instance of the * update service stub. */ function standardInit() { createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1.0", "2.0"); setDefaultPrefs(); // Initialize the update service stub component initUpdateServiceStub(); } /* Custom path handler for the http server */ function pathHandler(metadata, response) { response.setHeader("Content-Type", "text/xml", false); response.setStatusLine(metadata.httpVersion, gResponseStatusCode, "OK"); response.bodyOutputStream.write(gResponseBody, gResponseBody.length); } /** * Helper function for updater tests for launching the updater binary to apply * a mar file. * * @param aTestID * A string used to identify the name of directories for a test. * @return The exit value returned from the updater binary. */ function runUpdate(aTestID) { // Copy the updater binary to the updates directory. let binDir = getGREDir(); let updater = binDir.clone(); updater.append("updater.app"); if (!updater.exists()) { updater = binDir.clone(); updater.append(UPDATER_BIN_FILE); if (!updater.exists()) { do_throw("Unable to find updater binary!"); } } let updatesDir = do_get_file(aTestID + UPDATES_DIR_SUFFIX, true); updater.copyTo(updatesDir, updater.leafName); let updateBin = updatesDir.clone(); updateBin.append(updater.leafName); if (updateBin.leafName == "updater.app") { updateBin.append("Contents"); updateBin.append("MacOS"); updateBin.append("updater"); if (!updateBin.exists()) do_throw("Unable to find the updater executable!"); } let updatesDirPath = updatesDir.path; if (/ /.test(updatesDirPath)) updatesDirPath = '"' + updatesDirPath + '"'; let applyToDir = do_get_file(aTestID + APPLY_TO_DIR_SUFFIX, true); let applyToDirPath = applyToDir.path; if (/ /.test(applyToDirPath)) applyToDirPath = '"' + applyToDirPath + '"'; let callbackApp = applyToDir.clone(); callbackApp.append(AFTER_APPLY_DIR); callbackApp.append(CALLBACK_BIN_FILE); callbackApp.permissions = PERMS_DIRECTORY; let cwdPath = callbackApp.parent.parent.path; if (/ /.test(cwdPath)) cwdPath = '"' + cwdPath + '"'; let callbackAppPath = callbackApp.path; if (/ /.test(callbackAppPath)) callbackAppPath = '"' + callbackAppPath + '"'; let args = [updatesDirPath, applyToDirPath, 0, cwdPath, callbackAppPath]. concat(CALLBACK_ARGS); let process = AUS_Cc["@mozilla.org/process/util;1"]. createInstance(AUS_Ci.nsIProcess); process.init(updateBin); process.run(true, args, args.length); return process.exitValue; } /** * Gets the platform specific shell binary that is launched using nsIProcess and * in turn launches the updater. * * @return nsIFile for the shell binary to launch using nsIProcess. * @throws if the shell binary doesn't exist. */ function getLaunchBin() { let launchBin; if (IS_WIN) { launchBin = Services.dirsvc.get("WinD", AUS_Ci.nsIFile); launchBin.append("System32"); launchBin.append("cmd.exe"); } else { launchBin = AUS_Cc["@mozilla.org/file/local;1"]. createInstance(AUS_Ci.nsILocalFile); launchBin.initWithPath("/bin/sh"); } if (!launchBin.exists()) do_throw(launchBin.path + " must exist to run this test!"); return launchBin; } /** * Helper function for updater tests for setting up the files and directories * used by the updater tests. * * @param aTestID * A string used to identify the name of directories for a test. * @param aMarFile * The mar file to copy the update mar to. * @param aTestFiles * An array of JavaScript objects representing the test files to create * for the test. */ function setupUpdaterTest(aTestID, aMarFile, aTestFiles) { // Remove the directory where the updater, mar file, etc. will be copied to let updatesDir = do_get_file(aTestID + UPDATES_DIR_SUFFIX, true); try { removeDirRecursive(updatesDir); } catch (e) { dump("Unable to remove directory\n" + "path: " + updatesDir.path + "\n" + "Exception: " + e + "\n"); } if (!updatesDir.exists()) { updatesDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); } // Remove the directory where the update will be applied if it exists. let applyToDir = do_get_file(aTestID + APPLY_TO_DIR_SUFFIX, true); try { removeDirRecursive(applyToDir); } catch (e) { dump("Unable to remove directory\n" + "path: " + applyToDir.path + "\n" + "Exception: " + e + "\n"); } logTestInfo("testing successful removal of the directory used to apply the " + "mar file"); do_check_false(applyToDir.exists()); // Add the test files that will be updated for a successful update or left in // the initial state for a failed update. for (let i = 0; i < aTestFiles.length; i++) { let f = aTestFiles[i]; if (f.originalFile || f.originalContents) { let testDir = do_get_file(f.destinationDir, true); if (!testDir.exists()) testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); let testFile; if (f.originalFile) { testFile = do_get_file(f.originalFile); testFile.copyTo(testDir, f.fileName); testFile = do_get_file(f.destinationDir + f.fileName); } else { testFile = do_get_file(f.destinationDir + f.fileName, true); writeFile(testFile, f.originalContents); } // Skip these tests on Windows (includes WinCE) and OS/2 since their // implementaions of chmod doesn't really set permissions. if (!IS_WIN && !IS_OS2 && f.originalPerms) { testFile.permissions = f.originalPerms; // Store the actual permissions on the file for reference later after // setting the permissions. if (!f.comparePerms) f.comparePerms = testFile.permissions; } } } let afterApplyBinDir = applyToDir.clone(); afterApplyBinDir.append(AFTER_APPLY_DIR); let helperBin = do_get_file(HELPER_BIN_FILE); helperBin.copyTo(afterApplyBinDir, CALLBACK_BIN_FILE); let updaterIniContents = "[Strings]\n" + "Title=Update XPCShell Test\n" + "Info=Application Update Test - " + aTestID + "\n"; let updaterIni = updatesDir.clone(); updaterIni.append(FILE_UPDATER_INI); writeFile(updaterIni, updaterIniContents); updaterIni.copyTo(afterApplyBinDir, FILE_UPDATER_INI); // Copy the mar that will be applied let mar = do_get_file(aMarFile); mar.copyTo(updatesDir, FILE_UPDATE_ARCHIVE); } /** * Helper function for updater tests for cleaning up the state after an updater * test has finished. * * @param aTestID * A string used to identify the name of directories for a test. */ function cleanupUpdaterTest(aTestID) { let updatesDir = do_get_file(aTestID + UPDATES_DIR_SUFFIX, true); try { removeDirRecursive(updatesDir); } catch (e) { dump("Unable to remove directory\n" + "path: " + updatesDir.path + "\n" + "Exception: " + e + "\n"); } // Try to remove the updates and the apply to directories. let applyToDir = do_get_file(aTestID + APPLY_TO_DIR_SUFFIX, true); try { removeDirRecursive(applyToDir); } catch (e) { dump("Unable to remove directory\n" + "path: " + applyToDir.path + "\n" + "Exception: " + e + "\n"); } cleanUp(); } /** * Helper function for updater tests for verifying the state of files and * directories after a successful update. * * @param aTestID * A string used to identify the name of directories for a test. * @param aTestFiles * An array of JavaScript objects representing the test files to create * for the test. */ function checkFilesAfterUpdateSuccess(aTestID, aTestFiles) { logTestInfo("testing contents of files after a successful update"); for (let i = 0; i < aTestFiles.length; i++) { let f = aTestFiles[i]; let testFile = do_get_file(f.destinationDir + f.fileName, true); logTestInfo("testing file: " + testFile.path); if (f.compareFile || f.compareContents) { do_check_true(testFile.exists()); // Skip these tests on Windows (includes WinCE) and OS/2 since their // implementaions of chmod doesn't really set permissions. if (!IS_WIN && !IS_OS2 && f.comparePerms) { // Check if the permssions as set in the complete mar file are correct. let logPerms = "testing file permissions - "; if (f.originalPerms) { logPerms += "original permissions: " + f.originalPerms.toString(8) + ", "; } logPerms += "compare permissions : " + f.comparePerms.toString(8) + ", "; logPerms += "updated permissions : " + testFile.permissions.toString(8); logTestInfo(logPerms); do_check_eq(testFile.permissions & 0xfff, f.comparePerms & 0xfff); } if (f.compareFile) { do_check_eq(readFileBytes(testFile), readFileBytes(do_get_file(f.compareFile))); if (f.originalFile) { // Verify that readFileBytes returned the entire contents by checking // the contents against the original file. do_check_neq(readFileBytes(testFile), readFileBytes(do_get_file(f.originalFile))); } } else { do_check_eq(readFileBytes(testFile), f.compareContents); } } else { do_check_false(testFile.exists()); } } checkFilesAfterUpdateCommon(aTestID); } /** * Helper function for updater tests for verifying the state of files and * directories after a failed update. * * @param aTestID * A string used to identify the name of directories for a test. * @param aTestFiles * An array of JavaScript objects representing the test files to create * for the test. */ function checkFilesAfterUpdateFailure(aTestID, aTestFiles) { logTestInfo("testing contents of files after a failed update"); for (let i = 0; i < aTestFiles.length; i++) { let f = aTestFiles[i]; let testFile = do_get_file(f.destinationDir + f.fileName, true); logTestInfo("testing file: " + testFile.path); if (f.compareFile || f.compareContents) { do_check_true(testFile.exists()); // Skip these tests on Windows (includes WinCE) and OS/2 since their // implementaions of chmod doesn't really set permissions. if (!IS_WIN && !IS_OS2 && f.comparePerms) { // Check the original permssions are retained on the file. let logPerms = "testing file permissions - "; if (f.originalPerms) { logPerms += "original permissions: " + f.originalPerms.toString(8) + ", "; } logPerms += "compare permissions : " + f.comparePerms.toString(8) + ", "; logPerms += "updated permissions : " + testFile.permissions.toString(8); logTestInfo(logPerms); do_check_eq(testFile.permissions & 0xfff, f.comparePerms & 0xfff); } if (f.compareFile) { do_check_eq(readFileBytes(testFile), readFileBytes(do_get_file(f.compareFile))); } else { do_check_eq(readFileBytes(testFile), f.compareContents); } } else { do_check_false(testFile.exists()); } } checkFilesAfterUpdateCommon(aTestID); } /** * Helper function for updater tests for verifying patch files and moz-backup * files aren't left behind after a successful or failed update. * * @param aTestID * A string used to identify the name of directories for a test. */ function checkFilesAfterUpdateCommon(aTestID) { logTestInfo("testing patch files should not be left behind"); let updatesDir = do_get_file(aTestID + UPDATES_DIR_SUFFIX, true); let entries = updatesDir.QueryInterface(AUS_Ci.nsIFile).directoryEntries; while (entries.hasMoreElements()) { let entry = entries.getNext().QueryInterface(AUS_Ci.nsIFile); do_check_neq(getFileExtension(entry), "patch"); } logTestInfo("testing backup files should not be left behind"); let applyToDir = do_get_file(aTestID + APPLY_TO_DIR_SUFFIX, true); checkFilesInDirRecursive(applyToDir, checkForBackupFiles); } /** * Helper function for updater tests for verifying the contents of the updater * callback application log which should contain the string executed and the * arguments passed to the callback application. */ function checkCallbackAppLog(aTestID) { let appLaunchLog = do_get_file(aTestID + APPLY_TO_DIR_SUFFIX + "/" + CALLBACK_ARGS[0], true); if (!appLaunchLog.exists()) { do_execute_soon(function() { checkCallbackAppLog(aTestID); }); return; } let expectedLogContents = "executed\n" + CALLBACK_ARGS.join("\n") + "\n"; let logContents = readFile(appLaunchLog).replace(/\r\n/g, "\n"); // It is possible for the log file contents check to occur before the log file // contents are completely written so wait until the contents are the expected // value. If the contents are never the expected value then the test will // fail by timing out. if (logContents != expectedLogContents) { do_execute_soon(function() { checkCallbackAppLog(aTestID); }); return; } logTestInfo("testing that the callback application successfully launched " + "and the expected command line arguments passed to it"); do_check_eq(logContents, expectedLogContents); do_test_finished(); } /** * Helper function for updater tests for verifying there are no update backup * files left behind after an update. * * @param aFile * An nsIFile to check if it has moz-backup for its extension. */ function checkForBackupFiles(aFile) { do_check_neq(getFileExtension(aFile), "moz-backup"); } /** * Helper function for updater tests for recursively enumerating a directory and * calls a callback function with the file as a parameter for each file found. * * @param aDir * A nsIFile for the directory to be deleted * @param aCallback * A callback function that will be called with the file as a * parameter for each file found. */ function checkFilesInDirRecursive(aDir, aCallback) { if (!aDir.exists()) do_throw("Directory must exist!"); let dirEntries = aDir.directoryEntries; while (dirEntries.hasMoreElements()) { let entry = dirEntries.getNext().QueryInterface(AUS_Ci.nsIFile); if (entry.isDirectory()) { checkFilesInDirRecursive(entry, aCallback); } else { aCallback(entry); } } } /** * Sets up the bare bones XMLHttpRequest implementation below. * * @param callback * The callback function that will call the nsIDomEventListener's * handleEvent method. * * Example of the callback function * * function callHandleEvent() { * gXHR.status = gExpectedStatus; * var e = { target: gXHR }; * gXHR.onload.handleEvent(e); * } */ function overrideXHR(callback) { gXHRCallback = callback; gXHR = new xhr(); var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); registrar.registerFactory(gXHR.classID, gXHR.classDescription, gXHR.contractID, gXHR); } /** * Bare bones XMLHttpRequest implementation for testing onprogress, onerror, * and onload nsIDomEventListener handleEvent. */ function xhr() { } xhr.prototype = { overrideMimeType: function(mimetype) { }, setRequestHeader: function(header, value) { }, status: null, channel: { set notificationCallbacks(val) { } }, _url: null, _method: null, open: function (method, url) { gXHR.channel.originalURI = Services.io.newURI(url, null, null); gXHR._method = method; gXHR._url = url; }, responseXML: null, responseText: null, send: function(body) { do_execute_soon(gXHRCallback); // Use a timeout so the XHR completes }, _onprogress: null, set onprogress(val) { gXHR._onprogress = val; }, get onprogress() { return gXHR._onprogress; }, _onerror: null, set onerror(val) { gXHR._onerror = val; }, get onerror() { return gXHR._onerror; }, _onload: null, set onload(val) { gXHR._onload = val; }, get onload() { return gXHR._onload; }, flags: AUS_Ci.nsIClassInfo.SINGLETON, implementationLanguage: AUS_Ci.nsIProgrammingLanguage.JAVASCRIPT, getHelperForLanguage: function(language) null, getInterfaces: function(count) { var interfaces = [AUS_Ci.nsIXMLHttpRequest, AUS_Ci.nsIJSXMLHttpRequest, AUS_Ci.nsIXMLHttpRequestEventTarget]; count.value = interfaces.length; return interfaces; }, classDescription: "XMLHttpRequest", contractID: "@mozilla.org/xmlextras/xmlhttprequest;1", classID: Components.ID("{c9b37f43-4278-4304-a5e0-600991ab08cb}"), createInstance: function (outer, aIID) { if (outer == null) return gXHR.QueryInterface(aIID); throw AUS_Cr.NS_ERROR_NO_AGGREGATION; }, QueryInterface: function(aIID) { if (aIID.equals(AUS_Ci.nsIXMLHttpRequest) || aIID.equals(AUS_Ci.nsIJSXMLHttpRequest) || aIID.equals(AUS_Ci.nsIXMLHttpRequestEventTarget) || aIID.equals(AUS_Ci.nsIClassInfo) || aIID.equals(AUS_Ci.nsISupports)) return gXHR; throw AUS_Cr.NS_ERROR_NO_INTERFACE; } }; /* Update check listener */ const updateCheckListener = { onProgress: function UCL_onProgress(request, position, totalSize) { }, onCheckComplete: function UCL_onCheckComplete(request, updates, updateCount) { gRequestURL = request.channel.originalURI.spec; gUpdateCount = updateCount; gUpdates = updates; logTestInfo("url = " + gRequestURL + ", " + "request.status = " + request.status + ", " + "update.statusText = " + request.statusText + ", " + "updateCount = " + updateCount); // Use a timeout to allow the XHR to complete do_execute_soon(gCheckFunc); }, onError: function UCL_onError(request, update) { gRequestURL = request.channel.originalURI.spec; gStatusCode = request.status; gStatusText = update.statusText; logTestInfo("url = " + gRequestURL + ", " + "request.status = " + gStatusCode + ", " + "update.statusText = " + gStatusText); // Use a timeout to allow the XHR to complete do_execute_soon(gCheckFunc); }, QueryInterface: function(aIID) { if (!aIID.equals(AUS_Ci.nsIUpdateCheckListener) && !aIID.equals(AUS_Ci.nsISupports)) throw AUS_Cr.NS_ERROR_NO_INTERFACE; return this; } }; /** * Helper for starting the http server used by the tests * * @param aRelativeDirName * The directory name to register relative to * toolkit/mozapps/update/test/unit/ */ function start_httpserver(aRelativeDirName) { var dir = do_get_file(aRelativeDirName); if (!dir.exists()) do_throw("The directory used by nsHttpServer does not exist! path: " + dir.path + "\n"); if (!dir.isDirectory()) do_throw("A file instead of a directory was specified for nsHttpServer " + "registerDirectory! path: " + dir.path + "\n"); do_load_httpd_js(); gTestserver = new nsHttpServer(); gTestserver.registerDirectory("/data/", dir); gTestserver.start(4444); } /* Helper for stopping the http server used by the tests */ function stop_httpserver(callback) { do_check_true(!!callback); gTestserver.stop(callback); } /** * Creates an nsIXULAppInfo * * @param id * The ID of the test application * @param name * A name for the test application * @param version * The version of the application * @param platformVersion * The gecko version of the application */ function createAppInfo(id, name, version, platformVersion) { const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1"; const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}"); var XULAppInfo = { vendor: "Mozilla", name: name, ID: id, version: version, appBuildID: "2007010101", platformVersion: platformVersion, platformBuildID: "2007010101", inSafeMode: false, logConsoleErrors: true, OS: "XPCShell", XPCOMABI: "noarch-spidermonkey", QueryInterface: function QueryInterface(iid) { if (iid.equals(AUS_Ci.nsIXULAppInfo) || iid.equals(AUS_Ci.nsIXULRuntime) || iid.equals(AUS_Ci.nsISupports)) return this; throw AUS_Cr.NS_ERROR_NO_INTERFACE; } }; var XULAppInfoFactory = { createInstance: function (outer, iid) { if (outer == null) return XULAppInfo.QueryInterface(iid); throw AUS_Cr.NS_ERROR_NO_AGGREGATION; } }; var registrar = Components.manager.QueryInterface(AUS_Ci.nsIComponentRegistrar); registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", XULAPPINFO_CONTRACTID, XULAppInfoFactory); } // On Vista XRE_UPDATE_ROOT_DIR can be a directory other than the one in the // application directory. This will reroute it back to the one in the // application directory. var gDirProvider = { getFile: function DP_getFile(prop, persistent) { persistent.value = true; if (prop == XRE_UPDATE_ROOT_DIR) return getCurrentProcessDir(); return null; }, QueryInterface: function(iid) { if (iid.equals(AUS_Ci.nsIDirectoryServiceProvider) || iid.equals(AUS_Ci.nsISupports)) return this; throw AUS_Cr.NS_ERROR_NO_INTERFACE; } }; Services.dirsvc.QueryInterface(AUS_Ci.nsIDirectoryService).registerProvider(gDirProvider);