From c62687dd9e5d60478d56828d6a3c4d64a5bab577 Mon Sep 17 00:00:00 2001 From: "ben%bengoodger.com" Date: Wed, 12 May 2004 03:46:38 +0000 Subject: [PATCH] 170006 downloading stuff --- .../mozapps/extensions/content/extensions.css | 30 +- .../mozapps/extensions/content/extensions.js | 218 ++++++++++++++- .../mozapps/extensions/content/extensions.xml | 201 ++++++++++++++ .../mozapps/extensions/content/extensions.xul | 18 +- .../mozapps/extensions/locale/extensions.dtd | 3 + .../extensions/locale/extensions.properties | 6 + .../extensions/public/nsIExtensionManager.idl | 14 + .../extensions/src/nsExtensionManager.js.in | 261 ++++++++++++++++-- 8 files changed, 721 insertions(+), 30 deletions(-) diff --git a/toolkit/mozapps/extensions/content/extensions.css b/toolkit/mozapps/extensions/content/extensions.css index 7f5c6008107c..28b434dbb129 100644 --- a/toolkit/mozapps/extensions/content/extensions.css +++ b/toolkit/mozapps/extensions/content/extensions.css @@ -8,18 +8,22 @@ extension { -moz-box-orient: vertical; } -extension[install-state="pending"] { - -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-pending"); +extension[state="waiting"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-waiting"); } -extension[install-state="downloading"] { +extension[state="downloading"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-downloading"); } -extension[install-state="installing"] { +extension[state="installing"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-installing"); } +extension[state="done"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-done"); +} + extension[toBeDisabled="true"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-tobedisabled"); } @@ -36,10 +40,28 @@ extension[toBeUninstalled="true"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#extension-tobeuninstalled"); } + + extension[itemType="theme"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#theme"); } +extension[itemType="theme"][state="waiting"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#theme-waiting"); +} + +extension[itemType="theme"][state="downloading"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#theme-downloading"); +} + +extension[itemType="theme"][state="installing"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#theme-installing"); +} + +extension[itemType="theme"][state="done"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#theme-done"); +} + .themePreviewArea { width: 0px; } diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index af9b16067c11..2ba90db627cf 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -5,12 +5,14 @@ const kObserverServiceProgID = "@mozilla.org/observer-service;1"; const nsIUpdateItem = Components.interfaces.nsIUpdateItem; var gExtensionManager = null; -var gDownloadListener = null; var gExtensionssView = null; var gWindowState = ""; var gURIPrefix = ""; // extension or theme prefix var gDSRoot = ""; // extension or theme root var gGetMoreURL = ""; +var gCurrentTheme = ""; +var gDownloadManager = null; +var gObserverIndex = -1; const PREF_APP_ID = "app.id"; const PREF_EXTENSIONS_GETMORETHEMESURL = "extensions.getMoreThemesURL"; @@ -63,7 +65,8 @@ function onExtensionSelect(aEvent) // Startup, Shutdown function Startup() { - gWindowState = window.arguments[0]; + gWindowState = window.location.search.substr("?type=".length, window.location.search.length); + var isExtensions = gWindowState == "extensions"; gURIPrefix = isExtensions ? "urn:mozilla:extension:" : "urn:mozilla:theme:"; gDSRoot = isExtensions ? "urn:mozilla:extension:root" : "urn:mozilla:theme:root"; @@ -134,14 +137,225 @@ function Startup() win.setAttribute("width", isExtensions ? 400 : 500); win.setAttribute("height", isExtensions ? 300 : 380); } + + // Now look and see if we're being opened by XPInstall + var gDownloadManager = new XPInstallDownloadManager(); + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + os.addObserver(gDownloadManager, "xpinstall-download-started", false); + + gObserverIndex = gExtensionManager.addDownloadObserver(gDownloadManager); + + if ("arguments" in window) { + try { + var params = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock); + gDownloadManager.addDownloads(params); + } + catch (e) { } + } } function Shutdown() { if (gWindowState != "extensions") gExtensionsView.removeEventListener("richview-select", onThemeSelect, false); + + gExtensionManager.removeDownloadObserverAt(gObserverIndex); + + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + os.removeObserver(gObserver, "xpinstall-download-started"); } +/////////////////////////////////////////////////////////////////////////////// +// +// XPInstall +// + +function getURLSpecFromFile(aFile) +{ + var ioServ = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var fph = ioServ.getProtocolHandler("file").QueryInterface(Components.interfaces.nsIFileProtocolHandler); + return fph.getURLSpecFromFile(aFile); +} + +function XPInstallDownloadManager() +{ + var extensionsStrings = document.getElementById("extensionsStrings"); + this._statusFormatKBMB = extensionsStrings.getString("statusFormatKBMB"); + this._statusFormatKBKB = extensionsStrings.getString("statusFormatKBKB"); + this._statusFormatMBMB = extensionsStrings.getString("statusFormatMBMB"); +} + +XPInstallDownloadManager.prototype = { + _statusFormat : null, + _statusFormatKBMB : null, + _statusFormatKBKB : null, + _statusFormatMBMB : null, + + observe: function (aSubject, aTopic, aData) + { + if (aTopic == "xpinstall-download-started") { + var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray); + var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer); + paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock); + this.addDownloads(paramBlock); + } + }, + + addDownloads: function (aParams) + { + var numXPInstallItems = aParams.GetInt(1); + var isExtensions = gWindowState == "extensions"; + + var items = []; + for (var i = 0; i < numXPInstallItems;) { + var displayName = aParams.GetString(i++); + var url = aParams.GetString(i++); + var iconURL = aParams.GetString(i++); + if (!iconURL) { + iconURL = isExtensions ? "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png" : + "chrome://mozapps/skin/extensions/themeGeneric.png"; + } + + var type = isExtensions ? nsIUpdateItem.TYPE_EXTENSION : nsIUpdateItem.TYPE_THEME; + // gExtensionManager.addDownload(displayName, url, iconURL, type); + var item = Components.classes["@mozilla.org/updates/item;1"] + .createInstance(Components.interfaces.nsIUpdateItem); + item.init(url, " ", displayName, -1, url, iconURL, "", type); + items.push(item); + + // Advance the enumerator + var certName = aParams.GetString(i++); + } + + gExtensionManager.addDownloads(items, items.length); + }, + + removeDownload: function (aEvent) + { + + }, + + ///////////////////////////////////////////////////////////////////////////// + // nsIExtensionDownloadProgressListener + onStateChange: function (aURL, aState, aValue) + { + const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog; + var element = document.getElementById(aURL); + switch (aState) { + case nsIXPIProgressDialog.DOWNLOAD_START: + dump("*** download start\n"); + element.setAttribute("state", "waiting"); + element.setAttribute("progress", "0"); + break; + case nsIXPIProgressDialog.DOWNLOAD_DONE: + dump("*** download done\n"); + element.setAttribute("progress", "100"); + break; + case nsIXPIProgressDialog.INSTALL_START: + dump("*** install start\n"); + element.setAttribute("state", "installing"); + break; + case nsIXPIProgressDialog.INSTALL_DONE: + dump("*** install done\n"); + element.setAttribute("state", "done"); + var msg; + if (aValue != 0) { + var xpinstallStrings = document.getElementById("xpinstallStrings"); + try { + msg = xpinstallStrings.getString("error" + aValue); + } + catch (e) { + msg = xpinstallStrings.getFormattedString("unknown.error", [aValue]); + } + element.setAttribute("error", msg); + } + else { + // Remove the dummy, since we installed successfully + var type = gWindowState == "extensions" ? nsIUpdateItem.TYPE_EXTENSION + : nsIUpdateItem.TYPE_THEME; + gExtensionManager.removeDownload(aURL, type); + } + break; + case nsIXPIProgressDialog.DIALOG_CLOSE: + break; + } + }, + + _urls: { }, + onProgress: function (aURL, aValue, aMaxValue) + { + var element = document.getElementById(aURL); + var percent = Math.round((aValue / aMaxValue) * 100); + if (percent > 1 && !(aURL in this._urls)) { + this._urls[aURL] = true; + element.setAttribute("state", "downloading"); + } + element.setAttribute("progress", percent); + + var KBProgress = parseInt(aValue/1024 + .5); + var KBTotal = parseInt(aMaxValue/1024 + .5); + element.setAttribute("status", this._formatKBytes(KBProgress, KBTotal)); + }, + + _replaceInsert: function ( text, index, value ) + { + var result = text; + var regExp = new RegExp( "#"+index ); + result = result.replace( regExp, value ); + return result; + }, + + // aBytes aTotalKBytes returns: + // x, < 1MB y < 1MB x of y KB + // x, < 1MB y >= 1MB x KB of y MB + // x, >= 1MB y >= 1MB x of y MB + _formatKBytes: function (aKBytes, aTotalKBytes) + { + var progressHasMB = parseInt(aKBytes/1000) > 0; + var totalHasMB = parseInt(aTotalKBytes/1000) > 0; + + var format = ""; + if (!progressHasMB && !totalHasMB) { + format = this._statusFormatKBKB; + format = this._replaceInsert(format, 1, aKBytes); + format = this._replaceInsert(format, 2, aTotalKBytes); + } + else if (progressHasMB && totalHasMB) { + format = this._statusFormatMBMB; + format = this._replaceInsert(format, 1, (aKBytes / 1000).toFixed(1)); + format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1)); + } + else if (totalHasMB && !progressHasMB) { + format = this._statusFormatKBMB; + format = this._replaceInsert(format, 1, aKBytes); + format = this._replaceInsert(format, 2, (aTotalKBytes / 1000).toFixed(1)); + } + else { + // This is an undefined state! + dump("*** huh?!\n"); + } + + return format; + }, + + ///////////////////////////////////////////////////////////////////////////// + // nsISupports + QueryInterface: function (aIID) + { + if (!aIID.equals(Components.interfaces.nsIExtensionDownloadProgressListener) && + !aIID.equals(Components.interfaces.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + +/////////////////////////////////////////////////////////////////////////////// +// +// View Event Handlers +// function onViewDoubleClick() { switch (gWindowState) { diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index b6df8861698c..def533d6399b 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -189,6 +189,111 @@ + + + + + + + + + + + + + + + + &extensionItem.waiting.label; + + + + + + + "extension-" + + + + + + + + + + + + + + + + + + + + + + + + "extension-" + + + + + + + + + + + + + + + + + + + &extensionItem.installing.label; + + + + + + + "extension-" + + + + + + + + + + + + + + + + + + + + + + + + "extension-" + + + @@ -213,5 +318,101 @@ + + + + + + + + + + + + + + + &extensionItem.waiting.label; + + + + + "extension-" + + + + + + + + + + + + + + + + + + + + + + + "extension-" + + + + + + + + + + + + + + + + + + &extensionItem.installing.label; + + + + + "extension-" + + + + + + + + + + + + + + + + + + &extensionItem.done.label; + + + + + "extension-" + + + diff --git a/toolkit/mozapps/extensions/content/extensions.xul b/toolkit/mozapps/extensions/content/extensions.xul index 06cc72b06c42..1230381ecdc7 100644 --- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -64,6 +64,7 @@ + @@ -190,8 +191,22 @@ + + + + + + itemType="?itemType" downloadURL="?downloadURL" + state="?state" progress="?progress" status="?status"/> diff --git a/toolkit/mozapps/extensions/locale/extensions.dtd b/toolkit/mozapps/extensions/locale/extensions.dtd index a7e9e6bf9667..c6a02cbdad58 100644 --- a/toolkit/mozapps/extensions/locale/extensions.dtd +++ b/toolkit/mozapps/extensions/locale/extensions.dtd @@ -46,6 +46,9 @@ + + + diff --git a/toolkit/mozapps/extensions/locale/extensions.properties b/toolkit/mozapps/extensions/locale/extensions.properties index 3144f9a1cf4e..f8b5bdc33950 100644 --- a/toolkit/mozapps/extensions/locale/extensions.properties +++ b/toolkit/mozapps/extensions/locale/extensions.properties @@ -23,3 +23,9 @@ extensionsTitle=Extensions globalItemList=The following items are available to all users. \nYou can start Firefox with -lock-item "{GUID}" to prevent users from uninstalling or disabling an item. To unlock an item, start Firefox with -unlock-item "{GUID}" globalItemListExtensions=\n\nGlobally Available Extensions:\n==============================\n\n globalItemListThemes=\n\nGlobally Available Themes:\n==========================\n\n + +installSuccess=Installed Successfully + +statusFormatKBKB=#1 of #2 KB +statusFormatKBMB=#1 KB of #2 MB +statusFormatMBMB=#1 of #2 MB diff --git a/toolkit/mozapps/extensions/public/nsIExtensionManager.idl b/toolkit/mozapps/extensions/public/nsIExtensionManager.idl index df811e2c2429..488393ea4481 100644 --- a/toolkit/mozapps/extensions/public/nsIExtensionManager.idl +++ b/toolkit/mozapps/extensions/public/nsIExtensionManager.idl @@ -42,6 +42,14 @@ interface nsIFile; interface nsIRDFDataSource; interface nsIUpdateItem; +[scriptable, uuid(9048223f-ec50-49e5-9866-80ee8f26179d)] +interface nsIExtensionDownloadProgressListener : nsISupports +{ + void onStateChange(in wstring aURL, in short aState, in long aValue); + + void onProgress(in wstring aURL, in unsigned long aValue, in unsigned long aMaxValue); +}; + [scriptable, uuid(c3515b0f-99f4-453b-805e-1fdf5724d6d9)] interface nsIExtensionManager : nsISupports { @@ -53,6 +61,12 @@ interface nsIExtensionManager : nsISupports void enableExtension(in string aExtensionID); void disableExtension(in string aExtensionID); + void addDownloads([array, size_is(aItemCount)] in nsIUpdateItem aItems, + in unsigned long aItemCount); + void removeDownload(in wstring aURL, in unsigned short aType); + long addDownloadObserver(in nsIExtensionDownloadProgressListener aObserver); + void removeDownloadObserverAt(in long aIndex); + void installTheme(in nsIFile aJARFile, in unsigned long aFlags); void uninstallTheme(in string aThemeID); diff --git a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in index 7d344cac61f9..55e0e3bf7e8f 100644 --- a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in +++ b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in @@ -171,7 +171,7 @@ function getItemRoots(aItemType) function getItemType(aURI) { - var type = 0; + var type = -1; if (aURI.substr(0, PREFIX_EXTENSION.length) == PREFIX_EXTENSION) type = nsIUpdateItem.TYPE_EXTENSION; else if (aURI.substr(0, PREFIX_THEME.length) == PREFIX_THEME) @@ -850,10 +850,12 @@ nsThemeInstaller.prototype = { var elts = ctr.GetElements(); while (elts.hasMoreElements()) { var elt = elts.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - var name = chromeMetadata.GetTarget(elt, nameArc, true); - name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; - - this._writer.installSkin(name, aIsProfile); + if (getItemType(elt.Value) != -1) { + var name = chromeMetadata.GetTarget(elt, nameArc, true); + name = name.QueryInterface(Components.interfaces.nsIRDFLiteral).Value; + + this._writer.installSkin(name, aIsProfile); + } } this._writer.close(); @@ -1395,7 +1397,7 @@ nsExtensionManager.prototype = { .createInstance(Components.interfaces.nsIZipReader); zipReader.init(aXPIFile); zipReader.open(); - + var installProfile = aFlags & nsIExtensionManager.FLAG_INSTALL_PROFILE; var tempManifest = getFile(getDirKey(installProfile), [DIR_EXTENSIONS, DIR_TEMP, FILE_INSTALL_MANIFEST]); @@ -1604,6 +1606,90 @@ nsExtensionManager.prototype = { return this._ds; }, + ///////////////////////////////////////////////////////////////////////////// + // Downloads + _transactions: [], + addDownloads: function (aItems, aItemCount) + { + var txn = new nsItemDownloadTransaction(this); + for (var i = 0; i < aItemCount; ++i) { + var currItem = aItems[i]; + txn.addDownload(currItem.name, currItem.updateURL, currItem.iconURL, currItem.type); + this._transactions.push(txn); + } + + // Kick off the download process for this transaction + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); + os.notifyObservers(txn, "xpinstall-progress", "open"); + }, + + removeDownload: function (aURL, aType) + { + for (var i = 0; i < this._transactions.length; ++i) { + if (this._transactions[i].containsURL(aURL)) { + this._transactions[i].removeDownload(aURL, aType); + return; + } + } + }, + + // The nsIXPIProgressDialog implementation in the download transaction object + // forwards notifications through these methods which we then pass on to any + // front end objects implementing nsIExtensionDownloadProgressListener that + // are listening. We maintain the master state of download operations HERE, + // not in the front end, because if the user closes the extension or theme + // managers during the downloads we need to maintain state and not terminate + // the download/install process. + onStateChange: function (aTransaction, aURL, aState, aValue) + { + if (!(aURL in this._progressData)) + this._progressData[aURL] = { }; + this._progressData[aURL].state = aState; + + for (var i = 0; i < this._downloadObservers.length; ++i) + this._downloadObservers[i].onStateChange(aURL, aState, aValue); + + const nsIXPIProgressDialog = Components.interfaces.nsIXPIProgressDialog; + if (aState == nsIXPIProgressDialog.DIALOG_CLOSE) { + for (var i = 0; i < this._transactions.length; ++i) { + if (this._transactions[i] == aTransaction) { + this._transactions.splice(i, 1); + delete aTransaction; + break; + } + } + } + }, + + _progressData: { }, + onProgress: function (aURL, aValue, aMaxValue) + { + for (var i = 0; i < this._downloadObservers.length; ++i) + this._downloadObservers[i].onProgress(aURL, aValue, aMaxValue); + + if (!(aURL in this._progressData)) + this._progressData[aURL] = { }; + this._progressData[aURL].progress = Math.round((aValue / aMaxValue) * 100); + }, + + _downloadObservers: [], + addDownloadObserver: function (aXPIProgressDialog) + { + for (var i = 0; i < this._downloadObservers.length; ++i) { + if (this._downloadObservers[i] == aXPIProgressDialog) + return i; + } + this._downloadObservers.push(aXPIProgressDialog); + return this._downloadObservers.length - 1; + }, + + removeDownloadObserverAt: function (aIndex) + { + this._downloadObservers.splice(aIndex, 1); + this._ds.flushProgressInfo(this._progressData); + }, + // _ds: null, @@ -1631,6 +1717,7 @@ nsExtensionManager.prototype = { getInterfaces: function (aCount) { var interfaces = [Components.interfaces.nsIExtensionManager, + Components.interfaces.nsIXPIProgressDialog, Components.interfaces.nsIObserver]; aCount.value = interfaces.length; return interfaces; @@ -1678,6 +1765,72 @@ nsExtensionManager.prototype = { } }; +/////////////////////////////////////////////////////////////////////////////// +// +// nsItemDownloadTransaction +// +// This object implements nsIXPIProgressDialog and represents a collection of +// XPI/JAR download and install operations. There is one +// nsItemDownloadTransaction per back-end XPInstallManager object. We maintain +// a collection of separate transaction objects because it's possible to have +// multiple separate XPInstall download/install operations going on +// simultaneously, each with its own XPInstallManager instance. For instance +// you could start downloading two extensions and then download a theme. Each +// of these operations would open the appropriate FE and have to be able to +// track each operation independently. +// +function nsItemDownloadTransaction(aManager) +{ + this._manager = aManager; +} + +nsItemDownloadTransaction.prototype = { + _manager : null, + _downloads : [], + + addDownload: function (aName, aURL, aIconURL, aItemType) + { + this._downloads.push({ url: aURL, type: aItemType, waiting: true }); + this._manager._ds.addDownload(aName, aURL, aIconURL, aItemType); + }, + + removeDownload: function (aURL, aItemType) + { + this._manager._ds.removeDownload(aURL, aItemType); + }, + + containsURL: function (aURL) + { + for (var i = 0; i < this._downloads.length; ++i) { + if (this._downloads[i].url == aURL) + return true; + } + return false; + }, + + ///////////////////////////////////////////////////////////////////////////// + // nsIXPIProgressDialog + onStateChange: function (aIndex, aState, aValue) + { + this._manager.onStateChange(this, this._downloads[aIndex].url, aState, aValue); + }, + + onProgress: function (aIndex, aValue, aMaxValue) + { + this._manager.onProgress(this._downloads[aIndex].url, aValue, aMaxValue); + }, + + ///////////////////////////////////////////////////////////////////////////// + // nsISupports + QueryInterface: function (aIID) + { + if (!aIID.equals(Components.interfaces.nsIXPIProgressDialog) && + !aIID.equals(Components.interfaces.nsISupports)) + throw Components.results.NS_ERROR_NO_INTERFACE; + return this; + } +}; + /////////////////////////////////////////////////////////////////////////////// // // nsExtensionItemUpdater @@ -1948,7 +2101,7 @@ nsExtensionsDataSource.prototype = { var elements = ctr.GetElements(); while (elements.hasMoreElements()) { var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - if (!this.isCompatible(this, e)) { + if (getItemType(e.Value) != -1 && !this.isCompatible(this, e)) { var itemType = getItemType(e.Value); var id = stripPrefix(e.Value, itemType); var item = Components.classes["@mozilla.org/updates/item;1"] @@ -1986,14 +2139,16 @@ nsExtensionsDataSource.prototype = { var elements = ctr.GetElements(); while (elements.hasMoreElements()) { var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - var id = stripPrefix(e.Value, aType); - var item = Components.classes["@mozilla.org/updates/item;1"] - .createInstance(Components.interfaces.nsIUpdateItem); - item.init(id, this.getItemProperty(id, "version"), - this.getItemProperty(id, "name"), - -1, "", "", - this.getItemProperty(id, "updateURL"), aType); - items.push(item); + if (getItemType(e.Value) != -1) { + var id = stripPrefix(e.Value, aType); + var item = Components.classes["@mozilla.org/updates/item;1"] + .createInstance(Components.interfaces.nsIUpdateItem); + item.init(id, this.getItemProperty(id, "version"), + this.getItemProperty(id, "name"), + -1, "", "", + this.getItemProperty(id, "updateURL"), aType); + items.push(item); + } } } } @@ -2026,10 +2181,11 @@ nsExtensionsDataSource.prototype = { var elements = ctr.GetElements(); while (elements.hasMoreElements()) { var e = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - - var value = this.GetTarget(e, this._emR(aFlag), true); - if (!value) - items.push(stripPrefix(e.Value, getItemType(e.Value))); + if (getItemType(e.Value) != -1) { + var value = this.GetTarget(e, this._emR(aFlag), true); + if (!value) + items.push(stripPrefix(e.Value, getItemType(e.Value))); + } } } return items; @@ -2264,22 +2420,81 @@ nsExtensionsDataSource.prototype = { this.removeItemFromContainer(aThemeID, nsIUpdateItem.TYPE_THEME, isProfile); }, + // Cleans the resource of all its assertionss removeItemMetadata: function (aItemID, aItemType) { var item = this._rdf.GetResource(getItemPrefix(aItemType) + aItemID); var isProfile = this.isProfileItem(aItemID); var ds = isProfile ? this._profileExtensions : this._appExtensions; - + this._cleanResource(item, ds); + }, + + _cleanResource: function (aResource, aDS) + { // Remove outward arcs - var arcs = ds.ArcLabelsOut(item); + var arcs = aDS.ArcLabelsOut(aResource); while (arcs.hasMoreElements()) { var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource); - var value = ds.GetTarget(item, arc, true); + var value = aDS.GetTarget(aResource, arc, true); if (value) - ds.Unassert(item, arc, value); + aDS.Unassert(aResource, arc, value); } }, + addDownload: function (aName, aURL, aIconURL, aItemType) + { + var root = this._rdf.GetResource(getItemRoot(aItemType)); + + var res = this._rdf.GetResource(aURL); + this._setProperty(this._profileExtensions, res, + this._emR("name"), + this._rdf.GetLiteral(aName)) + this._setProperty(this._profileExtensions, res, + this._emR("version"), + this._rdf.GetLiteral(" ")); + this._setProperty(this._profileExtensions, res, + this._emR("iconURL"), + this._rdf.GetLiteral(aIconURL)); + this._setProperty(this._profileExtensions, res, + this._emR("downloadURL"), + this._rdf.GetLiteral(aURL)); + + var ctr = Components.classes["@mozilla.org/rdf/container;1"] + .createInstance(Components.interfaces.nsIRDFContainer); + ctr.Init(this._profileExtensions, root); + if (ctr.IndexOf(res) == -1) + ctr.InsertElementAt(res, 1, true); + + this._flush(true); + }, + + removeDownload: function (aURL, aItemType) + { + var root = this._rdf.GetResource(getItemRoot(aItemType)); + var res = this._rdf.GetResource(aURL); + var ctr = Components.classes["@mozilla.org/rdf/container;1"] + .createInstance(Components.interfaces.nsIRDFContainer); + ctr.Init(this._profileExtensions, root); + ctr.RemoveElement(res, true); + this._cleanResource(res, this._profileExtensions); + + this._flush(true); + }, + + flushProgressInfo: function (aData) + { + for (var url in aData) { + var res = this._rdf.GetResource(url); + this._setProperty(this._profileExtensions, res, + this._emR("state"), + this._rdf.GetIntLiteral(aData[url].state)); + this._setProperty(this._profileExtensions, res, + this._emR("progress"), + this._rdf.GetIntLiteral(aData[url].progress)); + } + this._flush(true); + }, + loadExtensions: function (aProfile) { var extensionsFile = getFile(getDirKey(aProfile),