author | Dave Townsend <dtownsend@oxymoronical.com> |
Sat, 01 May 2010 11:04:44 -0700 | |
changeset 41684 | 8a7a28c65fbd68f75f7dddc50d5d96840d26d9e8 |
parent 41683 | 7bdefbba3e0f0c7a50a111ac2f47ee4616883738 |
child 41685 | 1240ddeed985c7a9fdd52b0e9def0e6e9ba1e2a4 |
push id | 13090 |
push user | dtownsend@mozilla.com |
push date | Sat, 01 May 2010 20:22:39 +0000 |
treeherder | mozilla-central@1240ddeed985 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 1.9.3a5pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
|
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -47,22 +47,40 @@ #define UNIX_BUT_NOT_MAC #endif #endif pref("general.startup.browser", true); pref("browser.chromeURL","chrome://browser/content/"); pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul"); +pref("xpinstall.dialog.confirm", "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul"); +pref("xpinstall.dialog.progress.skin", "chrome://mozapps/content/extensions/extensions.xul"); +pref("xpinstall.dialog.progress.chrome", "chrome://mozapps/content/extensions/extensions.xul"); +pref("xpinstall.dialog.progress.type.skin", "Extension:Manager"); +pref("xpinstall.dialog.progress.type.chrome", "Extension:Manager"); +// Developers can set this to |true| if they are constantly changing files in their +// extensions directory so that the extension system does not constantly think that +// their extensions are being updated and thus reregistered every time the app is +// started. +pref("extensions.ignoreMTimeChanges", false); // Enables some extra Extension System Logging (can reduce performance) pref("extensions.logging.enabled", false); +// Hides the install button in the add-ons mgr +pref("extensions.hideInstallButton", true); -// Preferences for AMO integration -pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/%APP%/discovery"); +// Preferences for the Get Add-ons pane +pref("extensions.getAddons.showPane", true); +pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/%APP%"); +pref("extensions.getAddons.maxResults", 5); +pref("extensions.getAddons.recommended.browseURL", "https://addons.mozilla.org/%LOCALE%/%APP%/recommended"); +pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/list/featured/all/10/%OS%/%VERSION%"); +pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/%APP%/search?q=%TERMS%"); +pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/%API_VERSION%/search/%TERMS%/all/10/%OS%/%VERSION%"); // Blocklist preferences pref("extensions.blocklist.enabled", true); pref("extensions.blocklist.interval", 86400); // Controls what level the blocklist switches from warning about items to forcibly // blocking them. pref("extensions.blocklist.level", 2); pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -592,34 +592,28 @@ const gXPInstallObserver = { } return null; }, observe: function (aSubject, aTopic, aData) { var brandBundle = document.getElementById("bundle_brand"); switch (aTopic) { - case "addon-install-blocked": - var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo); + case "xpinstall-install-blocked": + var installInfo = aSubject.QueryInterface(Components.interfaces.nsIXPIInstallInfo); var win = installInfo.originatingWindow; var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor) .getInterface(Components.interfaces.nsIWebNavigation) .QueryInterface(Components.interfaces.nsIDocShell); var browser = this._getBrowser(shell); if (browser) { var host = installInfo.originatingURI.host; var brandShortName = brandBundle.getString("brandShortName"); var notificationName, messageString, buttons; - var enabled = true; - try { - enabled = gPrefService.getBoolPref("xpinstall.enabled"); - } - catch (e) { - } - if (!enabled) { + if (!gPrefService.getBoolPref("xpinstall.enabled")) { notificationName = "xpinstall-disabled" if (gPrefService.prefIsLocked("xpinstall.enabled")) { messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked"); buttons = []; } else { messageString = gNavigatorBundle.getFormattedString("xpinstallDisabledMessage", [brandShortName, host]); @@ -640,17 +634,19 @@ const gXPInstallObserver = { messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning", [brandShortName, host]); buttons = [{ label: gNavigatorBundle.getString("xpinstallPromptAllowButton"), accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"), popup: null, callback: function() { - installInfo.install(); + var mgr = Components.classes["@mozilla.org/xpinstall/install-manager;1"] + .createInstance(Components.interfaces.nsIXPInstallManager); + mgr.initManagerWithInstallInfo(installInfo); return false; } }]; } var notificationBox = gBrowser.getNotificationBox(browser); if (!notificationBox.getNotificationWithValue(notificationName)) { const priority = notificationBox.PRIORITY_WARNING_MEDIUM; @@ -1182,17 +1178,17 @@ function prepareForStartup() { OfflineApps, false); // setup simple gestures support gGestureSupport.init(true); } function delayedStartup(isLoadingBlank, mustLoadSidebar) { Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false); - Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false); + Services.obs.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false); BrowserOffline.init(); OfflineApps.init(); gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true); // Ensure login manager is up and running. Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); @@ -1400,17 +1396,17 @@ function BrowserShutdown() try { FullZoom.destroy(); } catch(ex) { Components.utils.reportError(ex); } Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history"); - Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked"); + Services.obs.removeObserver(gXPInstallObserver, "xpinstall-install-blocked"); Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed"); try { gBrowser.removeProgressListener(window.XULBrowserWindow); gBrowser.removeTabsProgressListener(window.TabsProgressListener); } catch (ex) { } @@ -5718,18 +5714,31 @@ var MailIntegration = { Cc["@mozilla.org/uriloader/external-protocol-service;1"] .getService(Ci.nsIExternalProtocolService); if (extProtocolSvc) extProtocolSvc.loadUrl(aURL); } }; function BrowserOpenAddonsMgr(aPane) { - // TODO need to implement switching to the relevant view - see bug 560449 - switchToTabHavingURI("about:addons", true); + const EMTYPE = "Extension:Manager"; + var theEM = Services.wm.getMostRecentWindow(EMTYPE); + if (theEM) { + theEM.focus(); + if (aPane) + theEM.showView(aPane); + return; + } + + const EMURL = "chrome://mozapps/content/extensions/extensions.xul"; + const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable"; + if (aPane) + window.openDialog(EMURL, "", EMFEATURES, aPane); + else + window.openDialog(EMURL, "", EMFEATURES); } function AddKeywordForSearchField() { var node = document.popupNode; var charset = node.ownerDocument.characterSet; var docURI = makeURI(node.ownerDocument.URL, @@ -7502,38 +7511,44 @@ var LightWeightThemeWebInstaller = { gBrowser.tabContainer.removeEventListener("TabSelect", this, false); this._manager.resetPreview(); }, _isAllowed: function (node) { var pm = Services.perms; + var prefs = [["xpinstall.whitelist.add", pm.ALLOW_ACTION], + ["xpinstall.whitelist.add.36", pm.ALLOW_ACTION], + ["xpinstall.blacklist.add", pm.DENY_ACTION]]; + prefs.forEach(function ([pref, permission]) { + try { + var hosts = gPrefService.getCharPref(pref); + } catch (e) {} + + if (hosts) { + hosts.split(",").forEach(function (host) { + pm.add(makeURI("http://" + host), "install", permission); + }); + + gPrefService.setCharPref(pref, ""); + } + }); + var uri = node.ownerDocument.documentURIObject; return pm.testPermission(uri, "install") == pm.ALLOW_ACTION; }, _getThemeFromNode: function (node) { return this._manager.parseTheme(node.getAttribute("data-browsertheme"), node.baseURI); } } -/** - * Switch to a tab that has a given URI, and focusses its browser window. - * If a matching tab is in this window, it will be switched to. Otherwise, other - * windows will be searched. - * - * @param aURI - * URI to search for - * @param aOpenNew - * True to open a new tab and switch to it, if no existing tab is found - * @return True if a tab was switched to (or opened), false otherwise - */ -function switchToTabHavingURI(aURI, aOpenNew) { +function switchToTabHavingURI(aURI) { function switchIfURIInWindow(aWindow) { if (!("gBrowser" in aWindow)) return false; let browsers = aWindow.gBrowser.browsers; for (let i = 0; i < browsers.length; i++) { let browser = browsers[i]; if (browser.currentURI.equals(aURI)) { gURLBar.handleRevert(); @@ -7558,23 +7573,17 @@ function switchToTabHavingURI(aURI, aOpe let browserWin = winEnum.getNext(); // Skip closed (but not yet destroyed) windows, // and the current window (which was checked earlier). if (browserWin.closed || browserWin == window) continue; if (switchIfURIInWindow(browserWin)) return true; } - // No opened tab has that url. - if (aOpenNew) { - gBrowser.selectedTab = gBrowser.addTab(aURI.spec); - return true; - } - return false; } var TabContextMenu = { contextTab: null, updateContextMenu: function updateContextMenu(aPopupMenu) { this.contextTab = document.popupNode.localName == "tab" ? document.popupNode : gBrowser.selectedTab;
--- a/browser/base/content/pageinfo/pageInfo.css +++ b/browser/base/content/pageinfo/pageInfo.css @@ -1,11 +1,11 @@ #viewGroup > radio { - -moz-binding: url("chrome://browser/content/pageinfo/pageInfo.xml#viewbutton"); + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#viewbutton"); } richlistitem[feed] { -moz-binding: url("chrome://browser/content/pageinfo/feeds.xml#feed"); } richlistitem[feed]:not([selected="true"]) .feed-subscribe { display: none;
deleted file mode 100644 --- a/browser/base/content/pageinfo/pageInfo.xml +++ /dev/null @@ -1,25 +0,0 @@ -<?xml version="1.0"?> - -<bindings id="pageInfoBindings" - xmlns="http://www.mozilla.org/xbl" - xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" - xmlns:xbl="http://www.mozilla.org/xbl"> - - <!-- based on preferences.xml paneButton --> - <binding id="viewbutton" extends="chrome://global/content/bindings/radio.xml#radio"> - <content> - <xul:image class="viewButtonIcon" xbl:inherits="src"/> - <xul:label class="viewButtonLabel" xbl:inherits="value=label"/> - </content> - <implementation implements="nsIAccessibleProvider"> - <property name="accessibleType" readonly="true"> - <getter> - <![CDATA[ - return Components.interfaces.nsIAccessibleProvider.XULListitem; - ]]> - </getter> - </property> - </implementation> - </binding> - -</bindings>
--- a/browser/base/content/pageinfo/permissions.js +++ b/browser/base/content/pageinfo/permissions.js @@ -58,23 +58,19 @@ var gPermObj = { popup: function getPopupDefaultPermission() { if (gPrefs.getBoolPref("dom.disable_open_during_load")) return BLOCK; return ALLOW; }, install: function getInstallDefaultPermission() { - try { - if (!gPrefs.getBoolPref("xpinstall.whitelist.required")) - return ALLOW; - } - catch (e) { - } - return BLOCK; + if (gPrefs.getBoolPref("xpinstall.whitelist.required")) + return BLOCK; + return ALLOW; }, geo: function getGeoDefaultPermissions() { return BLOCK; } }; var permissionObserver = {
--- a/browser/base/content/test/browser_pluginnotification.js +++ b/browser/base/content/test/browser_pluginnotification.js @@ -1,67 +1,76 @@ const gTestRoot = "chrome://mochikit/content/browser/browser/base/content/test/"; var gTestBrowser = null; var gNextTest = null; function get_test_plugin() { var ph = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); var tags = ph.getPluginTags(); - + // Find the test plugin for (var i = 0; i < tags.length; i++) { if (tags[i].name == "Test Plug-in") return tags[i]; } } -// This listens for the next opened tab and checks it is of the right url. -// opencallback is called when the new tab is fully loaded -// closecallback is called when the tab is closed -function TabOpenListener(url, opencallback, closecallback) { +// This listens for the next opened window and checks it is of the right url. +// opencallback is called when the new window is fully loaded +// closecallback is called when the window is closed +function WindowOpenListener(url, opencallback, closecallback) { this.url = url; this.opencallback = opencallback; this.closecallback = closecallback; - gBrowser.tabContainer.addEventListener("TabOpen", this, false); + Services.wm.addListener(this); } -TabOpenListener.prototype = { +WindowOpenListener.prototype = { url: null, opencallback: null, closecallback: null, - tab: null, - browser: null, + window: null, + domwindow: null, handleEvent: function(event) { - if (event.type == "TabOpen") { - gBrowser.tabContainer.removeEventListener("TabOpen", this, false); - this.tab = event.originalTarget; - this.browser = this.tab.linkedBrowser; - gBrowser.addEventListener("load", this, true); - } else if (event.type == "load") { - gBrowser.removeEventListener("load", this, true); - this.tab.addEventListener("TabClose", this, false); - var url = this.browser.contentDocument.location.href; - is(url, this.url, "Should have opened the correct tab"); - // Allow any other load handlers to execute - var self = this; - executeSoon(function() { self.opencallback(self.tab, self.browser.contentWindow); } ); - } else if (event.type == "TabClose") { - if (event.originalTarget != this.tab) - return; - this.tab.removeEventListener("TabClose", this, false); - this.opencallback = null; - this.tab = null; - this.browser = null; - // Let the window close complete - executeSoon(this.closecallback); - this.closecallback = null; - } + is(this.domwindow.document.location.href, this.url, "Should have opened the correct window"); + + this.domwindow.removeEventListener("load", this, false); + // Allow any other load handlers to execute + var self = this; + executeSoon(function() { self.opencallback(self.domwindow); } ); + }, + + onWindowTitleChange: function(window, title) { + }, + + onOpenWindow: function(window) { + if (this.window) + return; + + this.window = window; + this.domwindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindowInternal); + this.domwindow.addEventListener("load", this, false); + }, + + onCloseWindow: function(window) { + if (this.window != window) + return; + + Services.wm.removeListener(this); + this.opencallback = null; + this.window = null; + this.domwindow = null; + + // Let the window close complete + executeSoon(this.closecallback); + this.closecallback = null; } }; function test() { waitForExplicitFinish(); var newTab = gBrowser.addTab(); gBrowser.selectedTab = newTab; @@ -119,25 +128,25 @@ function test2() { // Tests a page with a disabled plugin in it. function test3() { var notificationBox = gBrowser.getNotificationBox(gTestBrowser); ok(!notificationBox.getNotificationWithValue("missing-plugins"), "Test 3, Should not have displayed the missing plugin notification"); ok(!notificationBox.getNotificationWithValue("blocked-plugins"), "Test 3, Should not have displayed the blocked plugin notification"); ok(!gTestBrowser.missingPlugins, "Test 3, Should not be a missing plugin list"); - new TabOpenListener("about:addons", test4, prepareTest5); + new WindowOpenListener("chrome://mozapps/content/extensions/extensions.xul", test4, prepareTest5); EventUtils.synthesizeMouse(gTestBrowser.contentDocument.getElementById("test"), 0, 0, {}, gTestBrowser.contentWindow); } -function test4(tab, win) { - todo_is(win.gView, "plugins", "Should have displayed the plugins pane"); - gBrowser.removeTab(tab); +function test4(win) { + is(win.gView, "plugins", "Should have displayed the plugins pane"); + win.close(); } function prepareTest5() { var plugin = get_test_plugin(); plugin.disabled = false; plugin.blocklisted = true; prepareTest(test5, gTestRoot + "plugin_test.html"); }
--- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -22,17 +22,16 @@ browser.jar: * content/browser/browser.js (content/browser.js) * content/browser/browser.xul (content/browser.xul) * content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml) * content/browser/credits.xhtml (content/credits.xhtml) * content/browser/fullscreen-video.xhtml (content/fullscreen-video.xhtml) * content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul) * content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js) * content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css) -* content/browser/pageinfo/pageInfo.xml (content/pageinfo/pageInfo.xml) * content/browser/pageinfo/feeds.js (content/pageinfo/feeds.js) * content/browser/pageinfo/feeds.xml (content/pageinfo/feeds.xml) * content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js) * content/browser/pageinfo/security.js (content/pageinfo/security.js) * content/browser/openLocation.js (content/openLocation.js) * content/browser/openLocation.xul (content/openLocation.xul) * content/browser/pageReportFirstTime.xul (content/pageReportFirstTime.xul) * content/browser/safeMode.js (content/safeMode.js)
--- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -456,11 +456,23 @@ var gMainPane = { } }, /** * Displays the Add-ons Manager. */ showAddonsMgr: function () { - openUILinkIn("about:addons", "window"); + const EMTYPE = "Extension:Manager"; + var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator); + var theEM = wm.getMostRecentWindow(EMTYPE); + if (theEM) { + theEM.focus(); + theEM.showView("extensions"); + return; + } + + const EMURL = "chrome://mozapps/content/extensions/extensions.xul"; + const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable"; + window.openDialog(EMURL, "", EMFEATURES, "extensions"); } };
--- a/browser/components/preferences/permissions.js +++ b/browser/components/preferences/permissions.js @@ -203,16 +203,22 @@ var gPermissionManager = { var urlLabel = document.getElementById("urlLabel"); urlLabel.hidden = !urlFieldVisible; var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); os.addObserver(this, "perm-changed", false); + if (this._type == "install") { + var enumerator = this._pm.enumerator; + if (!enumerator.hasMoreElements()) + this._updatePermissions(); + } + this._loadPermissions(); urlField.focus(); this._ltrAtom = Components.classes["@mozilla.org/atom-service;1"] .getService(Components.interfaces.nsIAtomService) .getAtom("ltr"); }, @@ -352,16 +358,53 @@ var gPermissionManager = { (host.charAt(0) == ".") ? host.substring(1,host.length) : host, aPermission.type, capabilityString, aPermission.capability); this._permissions.push(p); } }, + _updatePermissions: function () + { + try { + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + var pbi = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch2); + var prefList = [["xpinstall.whitelist.add", nsIPermissionManager.ALLOW_ACTION], + ["xpinstall.whitelist.add.36", nsIPermissionManager.ALLOW_ACTION], + ["xpinstall.blacklist.add", nsIPermissionManager.DENY_ACTION]]; + + for (var i = 0; i < prefList.length; ++i) { + try { + // this pref is a comma-delimited list of hosts + var hosts = pbi.getCharPref(prefList[i][0]); + } catch(ex) { + continue; + } + + if (!hosts) + continue; + + hostList = hosts.split(","); + var capability = prefList[i][1]; + for (var j = 0; j < hostList.length; ++j) { + // trim leading and trailing spaces + var host = hostList[j].replace(/^\s*/,"").replace(/\s*$/,""); + try { + var uri = ioService.newURI("http://" + host, null, null); + this._pm.add(uri, this._type, capability); + } catch(ex) { } + } + pbi.setCharPref(prefList[i][0], ""); + } + } catch(ex) { } + }, + setHost: function (aHost) { document.getElementById("url").value = aHost; } }; function setHost(aHost) {
--- a/browser/fuel/src/fuelApplication.js +++ b/browser/fuel/src/fuelApplication.js @@ -652,16 +652,17 @@ var ApplicationFactory = { gSingleton = new Application(); } return gSingleton.QueryInterface(aIID); } }; + //================================================= // Application constructor function Application() { this.initToolkitHelpers(); this._bookmarks = null; } //=================================================
--- a/browser/fuel/test/Makefile.in +++ b/browser/fuel/test/Makefile.in @@ -45,15 +45,16 @@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_FILES =browser_Application.js \ browser_ApplicationPrefs.js \ browser_ApplicationStorage.js \ browser_ApplicationQuitting.js \ browser_Bookmarks.js \ browser_Browser.js \ + browser_Extensions.js \ ContentA.html \ ContentB.html \ ContentWithFrames.html \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/fuel/test/browser_Extensions.js @@ -0,0 +1,126 @@ +// The various pieces that we'll be testing +var testdata = { + dummyid: "fuel-dummy-extension@mozilla.org", + dummyname: "Dummy Extension", + inspectorid: "inspector@mozilla.org", + inspectorname: "DOM Inspector", + missing: "fuel.fuel-test-missing", + dummy: "fuel.fuel-test" +}; +var gLastEvent = ""; + +function test() { + // test to see if the extensions object is available + ok(Application.extensions, "Check for the 'Extensions' object"); + + // test to see if a non-existant extension exists + ok(!Application.extensions.has(testdata.dummyid), "Check non-existant extension for existence"); + + // BUG 420028: Must find a way to add a dummy extension for test suite + return; + + // test to see if an extension exists + ok(Application.extensions.has(testdata.inspectorid), "Check extension for existence"); + + var inspector = Application.extensions.get(testdata.inspectorid); + is(inspector.id, testdata.inspectorid, "Check 'Extension.id' for known extension"); + is(inspector.name, testdata.inspectorname, "Check 'Extension.name' for known extension"); + // The known version number changes too frequently to hardcode in + ok(inspector.version, "Check 'Extension.version' for known extension"); + ok(inspector.firstRun, "Check 'Extension.firstRun' for known extension"); + ok(inspector.enabled, "Check 'Extension.enabled' for known extension"); + + // test to see if extension find works + is(Application.extensions.all.length, 1, "Check a find for all extensions"); + // STORAGE TESTING + // Make sure the we are given the same extension (cached) so things like .storage work right + inspector.storage.set("test", "simple check"); + ok(inspector.storage.has("test"), "Checking that extension storage worked"); + + var inspector2 = Application.extensions.get(testdata.inspectorid); + is(inspector2.id, testdata.inspectorid, "Check 'Extension.id' for known extension - from cache"); + ok(inspector.storage.has("test"), "Checking that extension storage worked - from cache"); + is(inspector2.storage.get("test", "cache"), inspector.storage.get("test", "original"), "Checking that the storage of same extension is correct - from cache"); + + inspector.events.addListener("disable", onGenericEvent); + inspector.events.addListener("enable", onGenericEvent); + inspector.events.addListener("uninstall", onGenericEvent); + inspector.events.addListener("cancel", onGenericEvent); + + var extmgr = Components.classes["@mozilla.org/extensions/manager;1"] + .getService(Components.interfaces.nsIExtensionManager); + + extmgr.disableItem(testdata.inspectorid); + is(gLastEvent, "disable", "Checking that disable event is fired"); + + // enabling after a disable will only fire a 'cancel' event + // see - http://mxr.mozilla.org/seamonkey/source/toolkit/mozapps/extensions/src/nsExtensionManager.js.in#5216 + extmgr.enableItem(testdata.inspectorid); + is(gLastEvent, "cancel", "Checking that enable (cancel) event is fired"); + + extmgr.uninstallItem(testdata.inspectorid); + is(gLastEvent, "uninstall", "Checking that uninstall event is fired"); + + extmgr.cancelUninstallItem(testdata.inspectorid); + is(gLastEvent, "cancel", "Checking that cancel event is fired"); + + // PREF TESTING + // Reset the install event preference, so that we can test it again later + inspector.prefs.get("install-event-fired").reset(); + + // test the value of the preference root + is(Application.extensions.all[0].prefs.root, "extensions.inspector@mozilla.org.", "Check an extension preference root"); + + // test getting non-existing values + var itemValue = inspector.prefs.getValue(testdata.missing, "default"); + is(itemValue, "default", "Check 'Extension.prefs.getValue' for non-existing item"); + + is(inspector.prefs.get(testdata.missing), null, "Check 'Extension.prefs.get' for non-existing item"); + + // test setting and getting a value + inspector.prefs.setValue(testdata.dummy, "dummy"); + itemValue = inspector.prefs.getValue(testdata.dummy, "default"); + is(itemValue, "dummy", "Check 'Extension.prefs.getValue' for existing item"); + + // test for overwriting an existing value + inspector.prefs.setValue(testdata.dummy, "smarty"); + itemValue = inspector.prefs.getValue(testdata.dummy, "default"); + is(itemValue, "smarty", "Check 'Extension.prefs.getValue' for overwritten item"); + + // test setting and getting a value + inspector.prefs.get(testdata.dummy).value = "dummy2"; + itemValue = inspector.prefs.get(testdata.dummy).value; + is(itemValue, "dummy2", "Check 'Extension.prefs.get().value' for existing item"); + + // test resetting a pref [since there is no default value, the pref should disappear] + inspector.prefs.get(testdata.dummy).reset(); + var itemValue = inspector.prefs.getValue(testdata.dummy, "default"); + is(itemValue, "default", "Check 'Extension.prefs.getValue' for reset pref"); + + // test to see if a non-existant property exists + ok(!inspector.prefs.has(testdata.dummy), "Check non-existant property for existence"); + + waitForExplicitFinish(); + inspector.prefs.events.addListener("change", onPrefChange); + inspector.prefs.setValue("fuel.fuel-test", "change event"); +} + +function onGenericEvent(event) { + gLastEvent = event.type; +} + +function onPrefChange(evt) { + var inspector3 = Application.extensions.get(testdata.inspectorid); + + is(evt.data, testdata.dummy, "Check 'Extension.prefs.set' fired a change event"); + inspector3.prefs.events.removeListener("change", onPrefChange); + + inspector3.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChange2); + inspector3.prefs.setValue("fuel.fuel-test", "change event2"); +} + +function onPrefChange2(evt) { + is(evt.data, testdata.dummy, "Check 'Extension.prefs.set' fired a change event for a single preference"); + + finish(); +}
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -252,16 +252,17 @@ @BINPATH@/components/xpcom_base.xpt @BINPATH@/components/xpcom_system.xpt @BINPATH@/components/xpcom_components.xpt @BINPATH@/components/xpcom_ds.xpt @BINPATH@/components/xpcom_io.xpt @BINPATH@/components/xpcom_threads.xpt @BINPATH@/components/xpcom_xpti.xpt @BINPATH@/components/xpconnect.xpt +@BINPATH@/components/xpinstall.xpt @BINPATH@/components/xulapp.xpt @BINPATH@/components/xuldoc.xpt @BINPATH@/components/xultmpl.xpt @BINPATH@/components/zipwriter.xpt ; JavaScript components @BINPATH@/components/FeedProcessor.js @BINPATH@/components/FeedConverter.js @@ -289,19 +290,17 @@ @BINPATH@/components/nsFilePicker.js #endif @BINPATH@/components/nsHelperAppDlg.js @BINPATH@/components/nsDownloadManagerUI.js @BINPATH@/components/nsProxyAutoConfig.js @BINPATH@/components/NetworkGeolocationProvider.js @BINPATH@/components/GPSDGeolocationProvider.js @BINPATH@/components/nsSidebar.js -@BINPATH@/components/addonManager.js -@BINPATH@/components/amContentHandler.js -@BINPATH@/components/amWebInstallListener.js +@BINPATH@/components/nsExtensionManager.js @BINPATH@/components/nsBlocklistService.js #ifdef MOZ_UPDATER @BINPATH@/components/nsUpdateService.js @BINPATH@/components/nsUpdateServiceStub.js #endif @BINPATH@/components/nsUpdateTimerManager.js @BINPATH@/components/pluginGlue.js @BINPATH@/components/nsSessionStartup.js
--- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -74,17 +74,16 @@ components/@DLL_PREFIX@browserdirprovide components/@DLL_PREFIX@brwsrdir@DLL_SUFFIX@ components/@DLL_PREFIX@myspell@DLL_SUFFIX@ components/@DLL_PREFIX@spellchecker@DLL_SUFFIX@ components/@DLL_PREFIX@spellchk@DLL_SUFFIX@ components/xpti.dat components/xptitemp.dat components/nsBackgroundUpdateService.js components/nsCloseAllWindows.js -components/nsExtensionManager.js #ifndef XP_MACOSX components/autocomplete.xpt #endif @DLL_PREFIX@zlib@DLL_SUFFIX@ #Remove Talkback files from old location (in case user updates from 1.0.x) components/BrandRes.dll components/fullsoft.dll components/master.ini
--- a/build/automation.py.in +++ b/build/automation.py.in @@ -307,17 +307,16 @@ user_pref("browser.EULA.override", true) user_pref("javascript.options.jit.content", true); user_pref("gfx.color_management.force_srgb", true); user_pref("network.manage-offline-status", false); user_pref("test.mousescroll", true); user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs user_pref("network.http.prompt-temp-redirect", false); user_pref("media.cache_size", 100); user_pref("security.warn_viewing_mixed", false); -user_pref("extensions.enabledScopes", 3); user_pref("geo.wifi.uri", "http://%(server)s/tests/dom/tests/mochitest/geolocation/network_geolocation.sjs"); user_pref("geo.wifi.testing", true); user_pref("camino.warn_when_closing", false); // Camino-only, harmless to others // Make url-classifier updates so rare that they won't affect tests user_pref("urlclassifier.updateinterval", 172800);
--- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -121,16 +121,17 @@ MOZ_MAIL_NEWS = @MOZ_MAIL_NEWS@ MOZ_PLAINTEXT_EDITOR_ONLY = @MOZ_PLAINTEXT_EDITOR_ONLY@ BUILD_STATIC_LIBS = @BUILD_STATIC_LIBS@ MOZ_ENABLE_LIBXUL = @MOZ_ENABLE_LIBXUL@ ENABLE_TESTS = @ENABLE_TESTS@ IBMBIDI = @IBMBIDI@ MOZ_UNIVERSALCHARDET = @MOZ_UNIVERSALCHARDET@ ACCESSIBILITY = @ACCESSIBILITY@ MOZ_VIEW_SOURCE = @MOZ_VIEW_SOURCE@ +MOZ_XPINSTALL = @MOZ_XPINSTALL@ MOZ_JSLOADER = @MOZ_JSLOADER@ MOZ_USE_NATIVE_UCONV = @MOZ_USE_NATIVE_UCONV@ MOZ_BRANDING_DIRECTORY = @MOZ_BRANDING_DIRECTORY@ XPCOM_USE_LEA = @XPCOM_USE_LEA@ MOZ_ENABLE_POSTSCRIPT = @MOZ_ENABLE_POSTSCRIPT@ MOZ_INSTALLER = @MOZ_INSTALLER@ MOZ_UPDATER = @MOZ_UPDATER@ MOZ_UPDATE_PACKAGING = @MOZ_UPDATE_PACKAGING@
--- a/configure.in +++ b/configure.in @@ -4917,16 +4917,17 @@ MOZ_STORAGE=1 MOZ_SVG=1 MOZ_TIMELINE= MOZ_TOOLKIT_SEARCH=1 MOZ_UI_LOCALE=en-US MOZ_UNIVERSALCHARDET=1 MOZ_URL_CLASSIFIER= MOZ_USE_NATIVE_UCONV= MOZ_VIEW_SOURCE=1 +MOZ_XPINSTALL=1 MOZ_XSLT_STANDALONE= MOZ_XTF=1 MOZ_XUL=1 MOZ_ZIPWRITER=1 NS_PRINTING=1 NECKO_WIFI=1 NECKO_COOKIES=1 NECKO_DISK_CACHE=1 @@ -5735,16 +5736,27 @@ case "$target" in if test "$ac_cv_header_oleacc_idl" = "no"; then AC_MSG_ERROR([System header oleacc.idl is not available. See http://developer.mozilla.org/en/docs/oleacc.idl for details on fixing this problem.]) fi ;; esac fi dnl ======================================================== +dnl xpinstall support on by default +dnl ======================================================== +MOZ_ARG_DISABLE_BOOL(xpinstall, +[ --disable-xpinstall Disable xpinstall support], + MOZ_XPINSTALL=, + MOZ_XPINSTALL=1 ) +if test "$MOZ_XPINSTALL"; then + AC_DEFINE(MOZ_XPINSTALL) +fi + +dnl ======================================================== dnl xpcom js loader support on by default dnl ======================================================== if test "$MOZ_JSLOADER"; then AC_DEFINE(MOZ_JSLOADER) fi dnl ======================================================== dnl Disable printing @@ -6326,16 +6338,20 @@ if test -n "$MOZ_INSTALLER" -a "$OS_ARCH # The Windows build for NSIS requires the iconv command line utility to # convert the charset of the locale files. MOZ_PATH_PROGS(HOST_ICONV, $HOST_ICONV "iconv", "") if test -z "$HOST_ICONV"; then AC_MSG_ERROR([To build the installer iconv is required in your path. To build without the installer reconfigure using --disable-installer.]) fi fi +# Automatically disable installer if xpinstall isn't built +if test -z "$MOZ_XPINSTALL"; then + MOZ_INSTALLER= +fi AC_SUBST(MOZ_INSTALLER) AC_MSG_CHECKING([for tar archiver]) AC_CHECK_PROGS(TAR, gnutar gtar tar, "") if test -z "$TAR"; then AC_MSG_ERROR([no tar archiver found in \$PATH]) fi AC_MSG_RESULT([$TAR]) @@ -8405,16 +8421,17 @@ AC_SUBST(MOZ_XIE_LIBS) AC_SUBST(MOZ_ENABLE_POSTSCRIPT) AC_SUBST(BUILD_STATIC_LIBS) AC_SUBST(MOZ_ENABLE_LIBXUL) AC_SUBST(ENABLE_TESTS) AC_SUBST(IBMBIDI) AC_SUBST(MOZ_UNIVERSALCHARDET) AC_SUBST(ACCESSIBILITY) +AC_SUBST(MOZ_XPINSTALL) AC_SUBST(MOZ_VIEW_SOURCE) AC_SUBST(MOZ_SPELLCHECK) AC_SUBST(MOZ_USER_DIR) AC_SUBST(MOZ_CRASHREPORTER) AC_SUBST(ENABLE_STRIP) AC_SUBST(PKG_SKIP_STRIP) AC_SUBST(USE_ELF_DYNSTR_GC)
--- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp @@ -83,18 +83,16 @@ static RedirEntry kRedirMap[] = { { "licence", "chrome://global/content/license.html", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT }, { "neterror", "chrome://global/content/netError.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT | nsIAboutModule::HIDE_FROM_ABOUTABOUT }, { "memory", "chrome://global/content/aboutMemory.xhtml", nsIAboutModule::ALLOW_SCRIPT }, - { "addons", "chrome://mozapps/content/extensions/extensions.xul", - nsIAboutModule::ALLOW_SCRIPT }, { "support", "chrome://global/content/aboutSupport.xhtml", nsIAboutModule::ALLOW_SCRIPT } }; static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap); NS_IMETHODIMP nsAboutRedirector::NewChannel(nsIURI *aURI, nsIChannel **result) {
--- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -209,21 +209,16 @@ static const nsModuleComponentInfo gDocS NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", nsAboutRedirector::Create }, { "about:memory", NS_ABOUT_REDIRECTOR_MODULE_CID, NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", nsAboutRedirector::Create }, - { "about:addons", - NS_ABOUT_REDIRECTOR_MODULE_CID, - NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", - nsAboutRedirector::Create - }, { "about:support", NS_ABOUT_REDIRECTOR_MODULE_CID, NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", nsAboutRedirector::Create }, // uriloader { "Netscape URI Loader Service", NS_URI_LOADER_CID, NS_URI_LOADER_CONTRACTID, nsURILoaderConstructor, },
--- a/modules/libpref/src/Makefile.in +++ b/modules/libpref/src/Makefile.in @@ -80,17 +80,17 @@ EXTRA_DSO_LDOPTS = \ include $(topsrcdir)/config/rules.mk GARBAGE += $(addprefix $(DIST)/bin/defaults/pref/, \ mailnews.js editor.js \ aix.js beos.js unix.js winpref.js os2prefs.js openvms.js photon.js) GARBAGE += greprefs.js -GREPREF_FILES = $(topsrcdir)/netwerk/base/public/security-prefs.js $(srcdir)/init/all.js +GREPREF_FILES = $(topsrcdir)/xpinstall/public/xpinstall.js $(topsrcdir)/netwerk/base/public/security-prefs.js $(srcdir)/init/all.js # Optimizer bug with GCC 3.2.2 on OS/2 ifeq ($(OS_ARCH), OS2) nsPrefService.$(OBJ_SUFFIX): nsPrefService.cpp $(REPORT_BUILD) @$(MAKE_DEPS_AUTO_CXX) $(ELOG) $(CCC) $(OUTOPTION)$@ -c $(COMPILE_CXXFLAGS:-O2=-O1) $(_VPATH_SRCS) endif
--- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -2886,13 +2886,10 @@ pref("html5.flushtimer.initialdelay", 20 pref("html5.flushtimer.subsequentdelay", 120); // Push/Pop/Replace State prefs pref("browser.history.allowPushState", true); pref("browser.history.allowReplaceState", true); pref("browser.history.allowPopState", true); pref("browser.history.maxStateObjectSize", 655360); -// XPInstall prefs -pref("xpinstall.whitelist.required", true); - pref("network.buffer.cache.count", 24); pref("network.buffer.cache.size", 4096);
--- a/netwerk/confvars.sh +++ b/netwerk/confvars.sh @@ -34,8 +34,9 @@ # 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 ***** MOZ_APP_NAME=mozilla MOZ_EXTENSIONS_DEFAULT="" MOZ_APP_VERSION=$MOZILLA_VERSION +MOZ_XPINSTALL=
--- a/toolkit/components/exthelper/extApplication.js +++ b/toolkit/components/exthelper/extApplication.js @@ -31,17 +31,16 @@ * 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 ***** */ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/AddonManager.jsm"); //================================================= // Shutdown - used to store cleanup functions which will // be called on Application shutdown var gShutdown = []; //================================================= // Console constructor @@ -391,82 +390,89 @@ SessionStorage.prototype = { }; //================================================= // Extension constructor function Extension(aItem) { this._item = aItem; this._firstRun = false; - this._prefs = new PreferenceBranch("extensions." + this.id + "."); + this._prefs = new PreferenceBranch("extensions." + this._item.id + "."); this._storage = new SessionStorage(); this._events = new Events(); var installPref = "install-event-fired"; if (!this._prefs.has(installPref)) { this._prefs.setValue(installPref, true); this._firstRun = true; } - AddonManager.addAddonListener(this); - AddonManager.addInstallListener(this); + this._enabled = false; + const PREFIX_ITEM_URI = "urn:mozilla:item:"; + const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#"; + var rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService); + var itemResource = rdf.GetResource(PREFIX_ITEM_URI + this._item.id); + if (itemResource) { + var extmgr = Cc["@mozilla.org/extensions/manager;1"].getService(Ci.nsIExtensionManager); + var ds = extmgr.datasource; + var target = ds.GetTarget(itemResource, rdf.GetResource(PREFIX_NS_EM + "isDisabled"), true); + if (target && target instanceof Ci.nsIRDFLiteral) + this._enabled = (target.Value != "true"); + } + + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + os.addObserver(this, "em-action-requested", false); var self = this; gShutdown.push(function(){ self._shutdown(); }); } //================================================= // Extension implementation Extension.prototype = { // cleanup observer so we don't leak _shutdown: function ext_shutdown() { - AddonManager.removeAddonListener(this); - AddonManager.removeInstallListener(this); + var os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + os.removeObserver(this, "em-action-requested"); this._prefs = null; this._storage = null; this._events = null; }, - // for AddonListener - onDisabling: function(addon, needsRestart) { - if (addon.id == this.id) - this._events.dispatch("disable", this.id); - }, - - onEnabling: function(addon, needsRestart) { - if (addon.id == this.id) - this._events.dispatch("enable", this.id); - }, - - onUninstalling: function(addon, needsRestart) { - if (addon.id == this.id) - this._events.dispatch("uninstall", this.id); - }, - - onOperationCancelled: function(addon) { - if (addon.id == this.id) - this._events.dispatch("cancel", this.id); - }, - - onInstallEnded: function(install, addon) { - if (addon.id == this.id) - this._events.dispatch("upgrade", this.id); + // for nsIObserver + observe: function ext_observe(aSubject, aTopic, aData) + { + if ((aSubject instanceof Ci.nsIUpdateItem) && (aSubject.id == this._item.id)) + { + if (aData == "item-uninstalled") + this._events.dispatch("uninstall", this._item.id); + else if (aData == "item-disabled") + this._events.dispatch("disable", this._item.id); + else if (aData == "item-enabled") + this._events.dispatch("enable", this._item.id); + else if (aData == "item-cancel-action") + this._events.dispatch("cancel", this._item.id); + else if (aData == "item-upgraded") + this._events.dispatch("upgrade", this._item.id); + } }, get id() { return this._item.id; }, get name() { return this._item.name; }, get enabled() { - return this._item.isActive; + return this._enabled; }, get version() { return this._item.version; }, get firstRun() { return this._firstRun; @@ -485,54 +491,74 @@ Extension.prototype = { }, QueryInterface : XPCOMUtils.generateQI([Ci.extIExtension]) }; //================================================= // Extensions constructor -function Extensions(addons) { - this._cache = {}; +function Extensions() { + XPCOMUtils.defineLazyServiceGetter(this, "_extmgr", + "@mozilla.org/extensions/manager;1", + "nsIExtensionManager"); - addons.forEach(function(addon) { - this._cache[addon.id] = new Extension(addon); - }, this); + this._cache = {}; var self = this; gShutdown.push(function() { self._shutdown(); }); } //================================================= // Extensions implementation Extensions.prototype = { _shutdown : function exts_shutdown() { + this._extmgr = null; this._cache = null; }, + /* + * Helper method to check cache before creating a new extension + */ + _get : function exts_get(aId) { + if (this._cache.hasOwnProperty(aId)) + return this._cache[aId]; + + var newExt = new Extension(this._extmgr.getItemForID(aId)); + this._cache[aId] = newExt; + return newExt; + }, + get all() { return this.find({}); }, // XXX: Disabled until we can figure out the wrapped object issues // id: "some@id" or /id/ // name: "name" or /name/ // version: "1.0.1" // minVersion: "1.0" // maxVersion: "2.0" find : function exts_find(aOptions) { - return [e for each (e in this._cache)]; + var retVal = []; + var items = this._extmgr.getItemList(Ci.nsIUpdateItem.TYPE_EXTENSION); + + for (var i = 0; i < items.length; i++) { + retVal.push(this._get(items[i].id)); + } + + return retVal; }, has : function exts_has(aId) { - return aId in this._cache; + return this._extmgr.getItemForID(aId) != null; }, get : function exts_get(aId) { - return this.has(aId) ? this._cache[aId] : null; + return this.has(aId) ? this._get(aId) : null; }, QueryInterface : XPCOMUtils.generateQI([Ci.extIExtensions]) }; //================================================= // extApplication constructor function extApplication() { @@ -632,20 +658,20 @@ extApplication.prototype = { }, get prefs() { let prefs = new PreferenceBranch(""); this.__defineGetter__("prefs", function() prefs); return this.prefs; }, - getExtensions: function(callback) { - AddonManager.getAddonsByTypes(["extension"], function(addons) { - callback.callback(new Extensions(addons)); - }); + get extensions() { + let extensions = new Extensions(); + this.__defineGetter__("extensions", function() extensions); + return this.extensions; }, get events() { // This ensures that FUEL only registers for notifications as needed // by callers. Note that the unload (xpcom-shutdown) event is listened // for by default, as it's needed for cleanup purposes. var self = this;
--- a/toolkit/components/exthelper/extIApplication.idl +++ b/toolkit/components/exthelper/extIApplication.idl @@ -303,16 +303,17 @@ interface extIExtension : nsISupports /** * The events object for the extension. * supports: "uninstall" */ readonly attribute extIEvents events; }; + /** * Interface representing a list of all installed extensions */ [scriptable, uuid(de281930-aa5a-11db-abbd-0800200c9a66)] interface extIExtensions : nsISupports { /** * Array of extIExtension listing all extensions in the application. @@ -334,25 +335,16 @@ interface extIExtensions : nsISupports * The id of an extension * @returns An extension object or null if no extension exists * with the given id. */ extIExtension get(in AString aId); }; /** - * Interface representing a callback that receives an array of extIExtensions - */ -[scriptable, function, uuid(2571cbb5-550d-4400-8038-75df9b553f98)] -interface extIExtensionsCallback : nsISupports -{ - void callback(in nsIVariant extensions); -}; - -/** * Interface representing a simple storage system */ [scriptable, uuid(0787ac44-29b9-4889-b97f-13573aec6971)] interface extISessionStorage : nsISupports { /** * The events object for the storage * supports: "change" @@ -385,17 +377,17 @@ interface extISessionStorage : nsISuppor * @param aDefaultValue * The value to return if no item exists with the given name * @returns value of the item or the given default value if no item * exists with the given name. */ nsIVariant get(in AString aName, in nsIVariant aDefaultValue); }; -[scriptable, uuid(2be87909-0817-4292-acfa-fc39be53be3f)] +[scriptable, uuid(e53d6610-7468-11dd-ad8b-0800200c9a66)] interface extIApplication : nsISupports { /** * The id of the application. */ readonly attribute AString id; /** @@ -412,17 +404,17 @@ interface extIApplication : nsISupports * The console object for the application. */ readonly attribute extIConsole console; /** * The extensions object for the application. Contains a list * of all installed extensions. */ - void getExtensions(in extIExtensionsCallback aCallback); + readonly attribute extIExtensions extensions; /** * The preferences object for the application. Defaults to an empty * root branch. */ readonly attribute extIPreferenceBranch prefs; /**
--- a/toolkit/components/startup/public/nsIAppStartup.idl +++ b/toolkit/components/startup/public/nsIAppStartup.idl @@ -112,27 +112,17 @@ interface nsIAppStartup : nsISupports * constructed from the constants defined above. */ void quit(in PRUint32 aMode); }; [scriptable, uuid(3012668f-44b6-49b1-89fb-761a912a78c1)] interface nsIAppStartup2 : nsIAppStartup { - /** - * True if the application is in the process of shutting down. - */ readonly attribute boolean shuttingDown; - - /** - * Set to true to tell the platform that a restart is necessary to load new - * XPCOM components and interfaces. Setting this only has an effect early in - * the startup process. - */ - attribute boolean needsRestart; }; %{C++ /** * This success code may be returned by nsIAppStartup::Run to indicate that the * application should be restarted. This condition corresponds to the case in * which nsIAppStartup::Quit was called with the eRestart flag. */
--- a/toolkit/components/startup/src/nsAppStartup.cpp +++ b/toolkit/components/startup/src/nsAppStartup.cpp @@ -94,18 +94,17 @@ public: // nsAppStartup // nsAppStartup::nsAppStartup() : mConsiderQuitStopper(0), mRunning(PR_FALSE), mShuttingDown(PR_FALSE), mAttemptingQuit(PR_FALSE), - mRestart(PR_FALSE), - mNeedsRestart(PR_FALSE) + mRestart(PR_FALSE) { } nsresult nsAppStartup::Init() { nsresult rv; @@ -401,33 +400,16 @@ nsAppStartup::ExitLastWindowClosingSurvi NS_IMETHODIMP nsAppStartup::GetShuttingDown(PRBool *aResult) { *aResult = mShuttingDown; return NS_OK; } -NS_IMETHODIMP -nsAppStartup::GetNeedsRestart(PRBool *aResult) -{ - *aResult = mNeedsRestart; - return NS_OK; -} - -NS_IMETHODIMP -nsAppStartup::SetNeedsRestart(PRBool aNeedsRestart) -{ - if (mRunning) - return NS_ERROR_UNEXPECTED; - - mNeedsRestart = aNeedsRestart; - return NS_OK; -} - // // nsAppStartup->nsIWindowCreator // NS_IMETHODIMP nsAppStartup::CreateChromeWindow(nsIWebBrowserChrome *aParent, PRUint32 aChromeFlags, nsIWebBrowserChrome **_retval)
--- a/toolkit/components/startup/src/nsAppStartup.h +++ b/toolkit/components/startup/src/nsAppStartup.h @@ -80,12 +80,11 @@ private: nsCOMPtr<nsIAppShell> mAppShell; PRInt32 mConsiderQuitStopper; // if > 0, Quit(eConsiderQuit) fails PRPackedBool mRunning; // Have we started the main event loop? PRPackedBool mShuttingDown; // Quit method reentrancy check PRPackedBool mAttemptingQuit; // Quit(eAttemptQuit) still trying PRPackedBool mRestart; // Quit(eRestart) - PRPackedBool mNeedsRestart; // Used to flag that a platform restart is necessary }; #endif // nsAppStartup_h__
--- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -88,36 +88,34 @@ window.onload = function () { let supportUrl = urlFormatter.formatURLPref("app.support.baseURL"); // Update the application basics section. document.getElementById("application-box").textContent = Application.name; document.getElementById("version-box").textContent = Application.version; document.getElementById("supportLink").href = supportUrl; // Update the other sections. + populateExtensionsSection(); populatePreferencesSection(); - populateExtensionsSection(); } function populateExtensionsSection() { - Application.getExtensions(function (extensions) { - let all = extensions.all; - let trExtensions = []; - for (let i = 0; i < all.length; i++) { - let extension = all[i]; - let tr = createParentElement("tr", [ - createElement("td", extension.name), - createElement("td", extension.version), - createElement("td", extension.enabled), - createElement("td", extension.id), - ]); - trExtensions.push(tr); - } - appendChildren(document.getElementById("extensions-tbody"), trExtensions); - }); + let extensions = Application.extensions.all; + let trExtensions = []; + for (let i = 0; i < extensions.length; i++) { + let extension = extensions[i]; + let tr = createParentElement("tr", [ + createElement("td", extension.name), + createElement("td", extension.version), + createElement("td", extension.enabled), + createElement("td", extension.id), + ]); + trExtensions.push(tr); + } + appendChildren(document.getElementById("extensions-tbody"), trExtensions); } function populatePreferencesSection() { let modifiedPrefs = getModifiedPrefs(); function comparePrefs(pref1, pref2) { if (pref1.name < pref2.name) return -1;
--- a/toolkit/library/libxul-config.mk +++ b/toolkit/library/libxul-config.mk @@ -118,20 +118,22 @@ STATIC_LIBS += \ gfxutils \ $(NULL) ifdef MOZ_IPC STATIC_LIBS += chromium_s endif ifndef WINCE +ifdef MOZ_XPINSTALL STATIC_LIBS += \ mozreg_s \ $(NULL) endif +endif # component libraries COMPONENT_LIBS += \ xpconnect \ necko \ uconv \ i18n \ chardet \ @@ -143,17 +145,16 @@ COMPONENT_LIBS += \ gklayout \ docshell \ embedcomponents \ webbrwsr \ nsappshell \ txmgr \ chrome \ commandlines \ - extensions \ toolkitcomps \ pipboot \ pipnss \ appcomps \ $(NULL) ifdef BUILD_CTYPES COMPONENT_LIBS += \ @@ -191,16 +192,23 @@ endif endif ifneq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT))) COMPONENT_LIBS += \ windowsproxy \ $(NULL) endif +ifdef MOZ_XPINSTALL +DEFINES += -DMOZ_XPINSTALL +COMPONENT_LIBS += \ + xpinstall \ + $(NULL) +endif + ifdef MOZ_JSDEBUGGER DEFINES += -DMOZ_JSDEBUGGER COMPONENT_LIBS += \ jsd \ $(NULL) endif ifdef MOZ_PREF_EXTENSIONS
--- a/toolkit/library/nsStaticXULComponents.cpp +++ b/toolkit/library/nsStaticXULComponents.cpp @@ -139,16 +139,23 @@ #ifdef MOZ_PLUGINS #define PLUGINS_MODULES \ MODULE(nsPluginModule) #else #define PLUGINS_MODULES #endif +#ifdef MOZ_XPINSTALL +#define XPINSTALL_MODULES \ + MODULE(nsSoftwareUpdate) +#else +#define XPINSTALL_MODULES +#endif + #ifdef MOZ_JSDEBUGGER #define JSDEBUGGER_MODULES \ MODULE(JavaScript_Debugger) #else #define JSDEBUGGER_MODULES #endif #if defined(MOZ_FILEVIEW) && defined(MOZ_XUL) @@ -257,19 +264,19 @@ MODULE(nsChromeModule) \ MODULE(application) \ MODULE(Apprunner) \ MODULE(CommandLineModule) \ FILEVIEW_MODULE \ STORAGE_MODULE \ PLACES_MODULES \ XULENABLED_MODULES \ - MODULE(AddonsModule) \ MODULE(nsToolkitCompsModule) \ XREMOTE_MODULES \ + XPINSTALL_MODULES \ JSDEBUGGER_MODULES \ MODULE(BOOT) \ MODULE(NSS) \ SYSTEMPREF_MODULES \ SPELLCHECK_MODULE \ LAYOUT_DEBUG_MODULE \ UNIXPROXY_MODULE \ OSXPROXY_MODULE \
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.dtd @@ -1,112 +1,176 @@ -<!ENTITY addons.windowTitle "Add-ons Manager"> -<!ENTITY search.placeholder "Search all add-ons"> -<!ENTITY loading.label "Loading…"> -<!ENTITY listEmpty.installed.label "You don't have any add-ons of this type installed"> -<!ENTITY listEmpty.search.label "Could not find any matching add-ons"> -<!ENTITY listEmpty.button.label "Learn more about add-ons"> +<!ENTITY addons.title "Add-ons"> +<!-- Default window size for the addon manager in pixels --> +<!ENTITY em.width "520"> +<!ENTITY em.height "380"> -<!-- categories / views --> -<!-- LOCALIZATION NOTE: These should match the header-* entries in extensions.properties --> -<!ENTITY view.search.label "Search"> -<!ENTITY view.discover.label "Get Add-ons"> -<!ENTITY view.languages.label "Languages"> -<!ENTITY view.searchengines.label "Search Engines"> -<!ENTITY view.features.label "Extensions"> -<!ENTITY view.appearance.label "Themes"> -<!ENTITY view.plugins.label "Plugins"> +<!ENTITY cmd.info.commandKey "i"> +<!ENTITY cmd.options.commandKey ","> +<!ENTITY cmd.close.commandKey "w"> + +<!-- View labels --> +<!ENTITY search.label "Get Add-ons"> +<!ENTITY extensions.label "Extensions"> +<!ENTITY themes.label "Themes"> +<!ENTITY locales.label "Languages"> +<!ENTITY plugins.label "Plugins"> +<!ENTITY update.label "Updates"> +<!ENTITY install.label "Installation"> -<!-- addon updates --> -<!ENTITY updates.updateNow.label "Update add-ons"> -<!ENTITY updates.updating.label "Updating add-ons"> -<!ENTITY updates.installed.label "Your add-ons have been updated."> -<!ENTITY updates.downloaded.label "Your add-on updates have been downloaded."> -<!ENTITY updates.restart.label "Restart now to complete installation"> -<!ENTITY updates.noneFound.label "No updates found"> +<!-- Command Bar items --> +<!ENTITY cmd.checkUpdatesAll.label "Find Updates"> +<!ENTITY cmd.checkUpdatesAll.accesskey "F"> +<!ENTITY cmd.checkUpdatesAllAddon.tooltip "Finds Updates to your Add-ons"> +<!ENTITY cmd.checkUpdatesAllTheme.tooltip "Finds Updates to your Themes"> +<!ENTITY cmd.checkUpdatesAllPlugin.tooltip "Finds Updates to your Plugins"> +<!ENTITY cmd.installLocalFile.label "Install…"> +<!ENTITY cmd.installLocalFile.accesskey "n"> +<!ENTITY cmd.installFileAddon.tooltip "Install an Add-on"> +<!ENTITY cmd.installFileTheme.tooltip "Install a Theme"> +<!ENTITY cmd.installUpdatesAll2.label "Install Updates"> +<!ENTITY cmd.installUpdatesAll2.accesskey "I"> +<!ENTITY cmd.installUpdatesAll2.tooltip "Install the selected updates"> +<!ENTITY cmd.restartApp2.label "Restart &brandShortName;"> +<!ENTITY cmd.restartApp2.accesskey "R"> +<!ENTITY cmd.restartApp2.tooltip "Restart &brandShortName; to apply changes"> +<!ENTITY cmd.skip.label "Skip"> +<!ENTITY cmd.skip.accesskey "k"> +<!ENTITY cmd.skip.tooltip "Skip these updates"> +<!ENTITY cmd.continue.label "Continue"> +<!ENTITY cmd.continue.accesskey "C"> +<!ENTITY cmd.continue.tooltip "Continue loading &brandShortName;"> -<!-- addon actions --> -<!ENTITY cmd.showDetails.label "Show more information"> -<!ENTITY cmd.showDetails.accesskey "S"> -<!ENTITY cmd.findUpdates.label "Find updates"> -<!ENTITY cmd.findUpdates.accesskey "F"> -<!ENTITY cmd.preferences.label "Preferences"> -<!ENTITY cmd.preferences.accesskey "P"> -<!ENTITY cmd.about.label "About"> -<!ENTITY cmd.about.accesskey "A"> - -<!ENTITY cmd.enableAddon.label "Enable"> -<!ENTITY cmd.enableAddon.accesskey "E"> -<!ENTITY cmd.enableAddon.tooltip "Enable this add-on"> -<!ENTITY cmd.disableAddon.label "Disable"> -<!ENTITY cmd.disableAddon.accesskey "D"> -<!ENTITY cmd.disableAddon.tooltip "Disable this add-on"> -<!ENTITY cmd.uninstallAddon.label "Remove"> -<!ENTITY cmd.uninstallAddon.accesskey "R"> -<!ENTITY cmd.uninstallAddon.tooltip "Uninstall this add-on"> -<!ENTITY cmd.contribute.label "Contribute"> -<!ENTITY cmd.contribute.accesskey "C"> -<!ENTITY cmd.contribute.tooltip "Contribute to the development of this add-on"> - +<!ENTITY cmd.enableAll.label "Enable All"> +<!ENTITY cmd.enableAll.accesskey "a"> +<!ENTITY cmd.enableAll.tooltip "Enable all displayed Add-ons"> +<!ENTITY cmd.disableAll.label "Disable All"> +<!ENTITY cmd.disableAll.accesskey "s"> +<!ENTITY cmd.disableAll.tooltip "Disable all displayed Add-ons"> -<!-- detail view --> -<!ENTITY detail.version.label "Version"> -<!ENTITY detail.updated.label "Updated"> -<!ENTITY detail.creator.label "Developer"> -<!ENTITY detail.homepage.label "Homepage"> -<!ENTITY detail.numberOfDownloads.label "Downloads"> +<!-- Displayed in the selected Add-on's richlistitem and context menu --> +<!ENTITY cmd.useTheme.label "Use Theme"> +<!ENTITY cmd.useTheme.accesskey "T"> +<!ENTITY cmd.useTheme.tooltip "Changes &brandShortName;'s Theme"> +<!ENTITY cmd.options.label "Options"> +<!ENTITY cmd.options.accesskey "O"> +<!ENTITY cmd.options.tooltip "Set Options for the selected Extension"> +<!ENTITY cmd.optionsUnix.label "Preferences"> +<!ENTITY cmd.optionsUnix.accesskey "P"> +<!ENTITY cmd.optionsUnix.tooltip "Edit Preferences for the selected Extension"> +<!ENTITY cmd.enable.label "Enable"> +<!ENTITY cmd.enable.accesskey "E"> +<!ENTITY cmd.enable.tooltip "Enable this Add-on when &brandShortName; is restarted"> +<!ENTITY cmd.disable.label "Disable"> +<!ENTITY cmd.disable.accesskey "D"> +<!ENTITY cmd.disable.tooltip "Disable this Add-on when &brandShortName; is restarted"> +<!ENTITY cmd.uninstall.label "Uninstall"> +<!ENTITY cmd.uninstall2.accesskey "U"> +<!ENTITY cmd.uninstall2.tooltip "Uninstall this Add-on when &brandShortName; is restarted"> +<!ENTITY cmd.cancelUninstall.label "Cancel Uninstall"> +<!ENTITY cmd.cancelUninstall.accesskey "C"> +<!ENTITY cmd.cancelUninstall.tooltip "Cancel the uninstall of this Add-on"> +<!ENTITY cmd.cancelInstall.label "Cancel Install"> +<!ENTITY cmd.cancelInstall.accesskey "C"> +<!ENTITY cmd.cancelInstall.tooltip "Cancel the install of this Add-on"> +<!ENTITY cmd.cancelUpgrade.label "Cancel Upgrade"> +<!ENTITY cmd.cancelUpgrade.accesskey "C"> +<!ENTITY cmd.cancelUpgrade.tooltip "Cancel the upgrade of this Add-on"> +<!ENTITY cmd.installUpdate.label "Install Update"> +<!ENTITY cmd.installUpdate.accesskey "I"> +<!ENTITY cmd.installUpdate.tooltip "Install an update for this Add-on"> +<!ENTITY cmd.showUpdateInfo.label "Show Information"> +<!ENTITY cmd.showUpdateInfo.accesskey "S"> +<!ENTITY cmd.showUpdateInfo.tooltip "Show more information about these updates"> +<!ENTITY cmd.hideUpdateInfo.label "Hide Information"> +<!ENTITY cmd.hideUpdateInfo.accesskey "H"> +<!ENTITY cmd.hideUpdateInfo.tooltip "Hide information about these updates"> +<!ENTITY cmd.installSearchResult.label "Add to &brandShortName;…"> +<!ENTITY cmd.installSearchResult.accesskey "A"> +<!ENTITY cmd.installSearchResult.tooltip "Download and install this add-on"> +<!-- The selected add-on's cancel action button label --> +<!ENTITY cancel.label "Cancel"> +<!ENTITY cancel.accesskey "C"> +<!ENTITY cancelInstall.label "Cancel"> +<!ENTITY cancelInstall.accesskey "C"> +<!ENTITY cancelUpgrade.label "Cancel"> +<!ENTITY cancelUpgrade.accesskey "C"> -<!ENTITY detail.updateAutomatically.label "Update automatically"> -<!ENTITY detail.updateAutomatically.accesskey "U"> -<!ENTITY detail.updateAutomatically.tooltip "Update this add-on automatically whenever there is an update available"> -<!ENTITY detail.checkForUpdates.label "Check for updates"> -<!ENTITY detail.checkForUpdates.accesskey "F"> -<!ENTITY detail.checkForUpdates.tooltip "Check for updates for this add-on"> -<!ENTITY detail.showPreferences.label "Preferences"> -<!ENTITY detail.showPreferences.accesskey "P"> -<!ENTITY detail.showPreferences.tooltip "Change this add-on's preferences"> +<!-- Only displayed in the selected Add-on's context menu --> +<!ENTITY cmd.homepage.label "Visit Home Page"> +<!ENTITY cmd.homepage.accesskey "H"> +<!ENTITY cmd.about2.label "About this Add-on"> +<!ENTITY cmd.about.accesskey "A"> +<!ENTITY cmd.checkUpdate.label "Find Update"> +<!ENTITY cmd.checkUpdate.accesskey "F"> - -<!-- ratings --> -<!ENTITY rating.yourRating.label "Your rating:"> - -<!-- download/install progress --> -<!ENTITY progress.pause.tooltip "Pause"> -<!ENTITY progress.cancel.tooltip "Cancel"> - +<!ENTITY cmd.includeUpdate.label "Include Update"> +<!ENTITY cmd.includeUpdate.accesskey "n"> +<!ENTITY includeUpdate.label "Include this update"> +<!ENTITY includeUpdate.accesskey "n"> +<!ENTITY includeUpdate.tooltip "Include this Add-on when installing the updates"> -<!-- list sorting --> -<!ENTITY sort.name.label "Name"> -<!ENTITY sort.name.tooltip "Sort by name"> -<!ENTITY sort.size.label "Size"> -<!ENTITY sort.size.tooltip "Sort by size"> -<!ENTITY sort.dateUpdated.label "Last Updated"> -<!ENTITY sort.dateUpdated.tooltip "Sort by date updated"> -<!ENTITY sort.rating.label "Rating"> -<!ENTITY sort.rating.tooltip "Sort by average rating"> +<!-- Status Messsages --> +<!ENTITY insecureUpdate.label "Does not provide secure updates."> +<!ENTITY needsDependencies.label "Requires additional items."> +<!ENTITY blocklisted.label "Disabled for your protection."> +<!ENTITY softBlocklisted.label "Known to cause security or stability issues."> +<!ENTITY outdated.label "A newer, safer version is available."> +<!ENTITY toBeDisabled.label "This add-on will be disabled when &brandShortName; is restarted."> +<!ENTITY toBeEnabled.label "This add-on will be enabled when &brandShortName; is restarted."> +<!ENTITY toBeInstalled.label "This add-on will be installed when &brandShortName; is restarted."> +<!ENTITY toBeUninstalled.label "This add-on will be uninstalled when &brandShortName; is restarted."> +<!ENTITY toBeUpdated.label "This add-on will be updated when &brandShortName; is restarted."> - -<!ENTITY showMore.label "Show more"> -<!ENTITY showMore.accesskey "m"> -<!ENTITY showMore.tooltip "Show more details"> -<!ENTITY showLess.label "Show less"> -<!ENTITY showLess.accesskey "l"> -<!ENTITY showLess.tooltip "Show fewer details"> +<!ENTITY getExtensions.label "Get Extensions"> +<!ENTITY getThemes.label "Get Themes"> +<!ENTITY getPlugins.label "Get Plugins"> -<!ENTITY search.filter.label "Show:"> -<!ENTITY search.filter.installed.label "Your installed add-ons"> -<!ENTITY search.filter.available.label "Available add-ons"> +<!ENTITY searchAddons.label "Search All Add-ons"> +<!ENTITY browseAddons.label "Browse All Add-ons"> +<!ENTITY searchFailed.label "&brandShortName; couldn't retrieve add-ons"> +<!ENTITY recommendedHeader.label "Recommended"> +<!ENTITY recommendedThrobber.label "Retrieving recommended add-ons"> +<!ENTITY searchThrobber.label "Searching add-ons"> +<!ENTITY resetSearch.label "Clear Results"> +<!ENTITY noSearchResults.label "All results are already installed or incompatible."> +<!ENTITY noRecommendedResults.label "All recommendations are already installed or incompatible."> +<!ENTITY emptySearch.label "No matching add-ons"> +<!ENTITY emptySearch.button "OK"> +<!ENTITY cancelSearch.button "Cancel"> +<!ENTITY searchFailed.button "OK"> +<!ENTITY searchResultHomepage.value "Learn More"> +<!ENTITY searchBox.label "Search All Add-ons"> +<!ENTITY recommendedResults.label "See All Recommended Add-ons"> +<!ENTITY searchResultConnecting.label "Connecting…"> +<!ENTITY searchResultInstalling.label "Installing…"> +<!ENTITY searchResultFailed.label "Install Failed"> +<!ENTITY searchResultInstalled.label "Install Complete"> +<!ENTITY addonTypeExtension.label "Extension"> +<!ENTITY addonTypeTheme.label "Theme"> +<!ENTITY missingThumbnail.label "No Preview"> -<!ENTITY addon.homepage "Homepage"> -<!ENTITY addon.unknownDate "Unknown"> -<!ENTITY addon.disabled.postfix "(disabled)"> -<!ENTITY addon.undo.label "Undo?"> -<!ENTITY addon.undo.tooltip "Undo this action"> -<!ENTITY addon.undoUninstall.label "Undo?"> -<!ENTITY addon.undoUninstall.tooltip "Keep this add-on installed"> -<!ENTITY addon.restartNow.label "Restart now"> -<!ENTITY addon.restartNow.tooltip "Restart now to complete installation"> -<!ENTITY addon.install.label "Install"> -<!ENTITY addon.install.tooltip "Install this add-on"> -<!ENTITY addon.checkingForUpdates.label "Checking for updates…"> +<!ENTITY previewNoThemeSelected.label "No Theme Selected"> +<!ENTITY previewNoPreviewImage.label "This Theme does not have a Preview Image"> +<!ENTITY moreInfo.label "More Information"> +<!ENTITY infoNoAddonSelected.label "No Update Selected"> +<!ENTITY infoNoUpdateInfo.label "This update does not have any additional information"> +<!ENTITY infoUpdateInfoError.label "There was an error loading the information about this update"> -<!ENTITY addon.createdBy.label "By "> +<!ENTITY updateSuccess.label "Update completed successfully."> +<!ENTITY installSuccess.label "Install completed successfully."> +<!ENTITY installSuccessRestart.label "Restart to complete the installation."> +<!ENTITY updateSuccessRestart.label "Restart to complete the update."> +<!ENTITY installWaiting.label "Waiting…"> +<!ENTITY installIncompatibleUpdate.label "Checking compatibility…"> +<!ENTITY installFinishing.label "Installing…"> +<!ENTITY installFailure.label "Install failed."> + +<!ENTITY progressStatus.label "Checking For Updates"> + +<!ENTITY eula.title "End-User License Agreement"> +<!ENTITY eula.width "560px"> +<!ENTITY eula.height "400px"> +<!ENTITY eula.accept "Accept and Install…"> + +<!ENTITY blocklist.blocked.label "Blocked"> +<!ENTITY blocklist.checkbox.label "Disable">
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties @@ -1,53 +1,116 @@ -#LOCALIZATION NOTE (aboutWindowTitle) %S is the addon name aboutWindowTitle=About %S aboutWindowCloseButton=Close -#LOCALIZATION NOTE (aboutWindowVersionString) %S is the addon version aboutWindowVersionString=version %S -#LOCALIZATION NOTE (aboutAddon) %S is the addon name aboutAddon=About %S +updatingMsg=Looking for updates… +updateCompatibilityMsg=A compatibility update has been applied. +updateNoUpdateMsg=No updates were found. +updateErrorMessage=An error occurred while trying to find updates for %S. +updateDisabledMessage=Updates are disabled for %S. +updateReadOnlyMessage=Update not supported (install location is read only). +updateNotManagedMessage=Update not supported (install location is not managed by %S). +incompatibleUpdateMessage=%S is checking for a compatibility update to %S. +installSuccess=Install completed successfully +installWaiting=Waiting… +droppedInWarning=The following items were found in your Extensions folder. Do you want to install them? +updateNotificationTitle=Add-on updates found +updateNotificationText=%S has found an update for 1 of your add-ons +multipleUpdateNotificationText=%S has found updates for %S of your add-ons +# LOCALIZATION NOTE (lightweightThemeDescription): %S is the theme designer, either a person or organisation +lightweightThemeDescription=Created by %S. -#LOCALIZATION NOTE These should match the view.*.label entities in extensions.dtd -header-search=Search Results -header-discover=Get Add-ons -header-language=Languages -header-searchengine=Search Engines -header-extension=Extensions -header-theme=Themes -header-plugin=Plugins +uninstallButton=Uninstall +disableButton=Disable +cancelButton=Cancel +restartMessage=Restart %S to complete your changes. +restartButton=Restart %S +restartAccessKey=R +laterButton=Later +moreInfoText=More information +uninstallTitle=Uninstall %S +uninstallWarnDependMsg=%S is required by one or more add-ons. If you continue, the following items will be disabled: +uninstallQueryMessage=Do you want to uninstall %S? +cancelInstallTitle=Cancel Install of %S +cancelInstallQueryMessage=Are you sure you want to cancel the install of %S? +cancelInstallButton=Yes +cancelCancelInstallButton=No +cancelUpgradeTitle=Cancel Upgrade of %S +cancelUpgradeQueryMessage=Are you sure you want to cancel the upgrade of %S? +cancelUpgradeButton=Yes +cancelCancelUpgradeButton=No +disableTitle=Disable %S +disableWarningDependMessage=If you disable %S, the following items that require this extension will also be disabled: +disableQueryMessage=Do you want to disable %S? -#LOCALIZATION NOTE (header-goBack) %S is the name of the pane to go back to -header-goBack=Back to %S +themesTitle=Themes +extensionsTitle=Extensions -#LOCALIZATION NOTE (uninstallNotice) %S is the add-on name -uninstallNotice=%S has been removed. - -#LOCALIZATION NOTE (numReviews) #1 is the number of reviews -numReviews=#1 review;#1 reviews +type-32=Multiple Extension Package +type-16=Plugin +type-8=Language +type-4=Theme +type-2=Extension +incompatibleTitle=Incompatible %S +incompatibleMessage=%S %S could not be installed because it is not compatible with %S %S. +incompatibleThemeName=this Theme +incompatibleExtension=Disabled - not compatible with %S %S +incompatibleAddonMsg=Not compatible with %S %S +insecureUpdateMessage="%S" will not be installed because it does not provide secure updates -#LOCALIZATION NOTE (dateUpdated) %S is the date the addon was last updated -dateUpdated=Updated %S +invalidGUIDMessage="%S" could not be installed because of an error in its Install Manifest ("%S" is not a valid GUID). Please contact the author of this item about the problem. +invalidVersionMessage="%S" could not be installed because of an error in its Install Manifest ("%S" is not a valid Version String). Please contact the author of this item about the problem. +incompatiblePlatformMessage="%S" could not be installed because it is not compatible with your %S build type (%S). Please contact the author of this item about the problem. + +blocklistedInstallTitle2=This add-on is dangerous to use +blocklistedInstallMsg2=The add-on %S has a high risk of causing stability or security problems and can't be installed. +softBlockedInstallTitle=This add-on may be dangerous to use +softBlockedInstallMsg=The add-on %S may cause stability or security problems. It is highly recommended that you do not install it. +softBlockedInstallAcceptLabel=Install Anyway +softBlockedInstallAcceptKey=I -#LOCALIZATION NOTE (incompatibleWith) %1$S is brand name, %2$S is application version -incompatibleWith=Incompatible with %1$S %2$S +missingFileTitle=Missing File +missingFileMessage=%S could not load this item because the file %S was missing. + +malformedMessage=%S could not install this item because "%S" (provided by the item) is not well-formed or does not exist. Please contact the author about this problem. +malformedTitle=Malformed File -incompatibleTitle2=Incompatible add-on -#LOCALIZATION NOTE (incompatibleMessage2) %1$S is add-on name, %2$% is add-on version, %3$% is application name, %4$% is application version -incompatibleMessage2=%1$S %2$S could not be installed because it is not compatible with %3$S %4$S. +invalidFileExtTitle=Invalid File Extension +invalidFileExtMessage="%S" could not be installed because this item has an invalid file extension (%S is not a valid file extension for a %S). Please contact the author about this problem. +missingPackageFilesTitle=Missing Installation Files +missingPackageFilesMessage="%S" could not be installed because it does not contain a valid package (a %S must contain at least one extension or theme). Please contact the author about this problem. + +errorInstallTitle=Error +errorInstallMsg=%S could not install the file at \n\n%S\n\nbecause: %S -installDownloading=Downloading -installDownloaded=Downloaded -installDownloadFailed=Error downloading -installVerifying=Verifying -installInstalling=Installing -installInstallPending=Ready to install -installUpdatePending=Ready to update -installEnablePending=Restart to enable -installDisablePending=Restart to disable -installFailed=Error installing -installCancelled=Install cancelled +extensionFilter=Extensions (*.xpi) +themesFilter=Themes (*.jar) +installThemePickerTitle=Select a theme to install +installExtensionPickerTitle=Select an extension to install + +dssSwitchAfterRestart=Restart %S to use. + +finishedUpdateCheck=Finished checking for updates to %S +updateAvailableMsg=Version %S is available. -#LOCALIZATION NOTE: %1$S is the addon name, %2$S is brand name -restartToEnable=%1$% will be enabled after you restart %2$S -restartToDisable=%1$S will be disabled after you restart %2$S -restartToUninstall=%1$S will be removed after you restart %2$S -restartToUpgrade=%1$S will be updated after you restart %2$S +xpinstallDisabledMsgLocked=Software installation has been disabled by your system administrator. +xpinstallDisabledMsg=Software installation is currently disabled. Click Enable and try again. +# LOCALIZATION NOTE: Semi-colon list of plural forms. +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +newAddonsNotificationMsg2=%S new add-on has been installed.;%S new add-ons have been installed. +safeModeMsg=All add-ons have been disabled by safe mode. +disabledCompatMsg=Add-on compatibility checking is disabled. You may have incompatible add-ons. +disabledUpdateSecurityMsg=Add-on update security checking is disabled. You may be compromised by updates. +noUpdatesMsg=No updates were found. +offlineUpdateMsg2=%S is currently in offline mode and is unable to update add-ons. Click Go Online and try again. +offlineSearchMsg=%S is currently in offline mode and is unable to search for add-ons. Click Go Online and try again. +enableButtonLabel=Enable +enableButtonAccesskey=n +goOnlineButtonLabel=Go Online +goOnlineButtonAccesskey=G + +newUpdateWindowTitle=%S Add-on Updates +newUpdatesAvailableMsg=There are new updates available for your add-ons. + +searchResults=See all results (%S) + +eulaHeader=%S requires that you accept the following End User License Agreement before installation can proceed:
deleted file mode 100644 --- a/toolkit/mozapps/extensions/AddonLogging.jsm +++ /dev/null @@ -1,137 +0,0 @@ -/* -# ***** 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 the Extension Manager. -# -# 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): -# Dave Townsend <dtownsend@oxymoronical.com> -# -# 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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -const KEY_PROFILEDIR = "ProfD"; -const FILE_EXTENSIONS_LOG = "extensions.log"; - -Components.utils.import("resource://gre/modules/FileUtils.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -var EXPORTED_SYMBOLS = [ "LogManager" ]; - -var gDebugLogEnabled = false; - -function formatLogMessage(aType, aName, aStr) { - return aType.toUpperCase() + " " + aName + ": " + aStr; -} - -function AddonLogger(aName) { - this.name = aName; -} - -AddonLogger.prototype = { - name: null, - - error: function(aStr) { - let message = formatLogMessage("error", this.name, aStr); - - let consoleMessage = Cc["@mozilla.org/scripterror;1"]. - createInstance(Ci.nsIScriptError); - consoleMessage.init(message, null, null, 0, 0, Ci.nsIScriptError.errorFlag, - "component javascript"); - Services.console.logMessage(consoleMessage); - - if (gDebugLogEnabled) - dump("*** " + message + "\n"); - - try { - var tstamp = new Date(); - var logfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]); - var stream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - stream.init(logfile, 0x02 | 0x08 | 0x10, 0666, 0); // write, create, append - var writer = Cc["@mozilla.org/intl/converter-output-stream;1"]. - createInstance(Ci.nsIConverterOutputStream); - writer.init(stream, "UTF-8", 0, 0x0000); - writer.writeString(tstamp.toLocaleFormat("%Y-%m-%d %H:%M:%S ") + - message + "\n"); - writer.close(); - } - catch (e) { } - }, - - warn: function(aStr) { - let message = formatLogMessage("warn", this.name, aStr); - - let consoleMessage = Cc["@mozilla.org/scripterror;1"]. - createInstance(Ci.nsIScriptError); - consoleMessage.init(message, null, null, 0, 0, Ci.nsIScriptError.warningFlag, - "component javascript"); - Services.console.logMessage(consoleMessage); - - if (gDebugLogEnabled) - dump("*** " + message + "\n"); - }, - - log: function(aStr) { - if (gDebugLogEnabled) { - let message = formatLogMessage("log", this.name, aStr); - dump("*** " + message + "\n"); - Services.console.logStringMessage(message); - } - } -}; - -var LogManager = { - getLogger: function(aName, aTarget) { - let logger = new AddonLogger(aName); - - if (aTarget) { - ["error", "warn", "log"].forEach(function(name) { - let fname = name.toUpperCase(); - delete aTarget[fname]; - aTarget[fname] = function(aStr) { - logger[name](aStr); - }; - }); - } - - return logger; - } -}; - -try { - gDebugLogEnabled = Services.prefs.getBoolPref("extensions.logging.enabled"); -} -catch (e) { -}
deleted file mode 100644 --- a/toolkit/mozapps/extensions/AddonManager.jsm +++ /dev/null @@ -1,929 +0,0 @@ -/* -# ***** 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 the Extension Manager. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Dave Townsend <dtownsend@oxymoronical.com> -# -# 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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -const PREF_EM_UPDATE_ENABLED = "extensions.update.enabled"; - -Components.utils.import("resource://gre/modules/Services.jsm"); - -var EXPORTED_SYMBOLS = [ "AddonManager", "AddonManagerPrivate" ]; - -// A list of providers to load by default -const PROVIDERS = [ - "resource://gre/modules/XPIProvider.jsm", - "resource://gre/modules/PluginProvider.jsm", - "resource://gre/modules/LightweightThemeManager.jsm" -]; - -["LOG", "WARN", "ERROR"].forEach(function(aName) { - this.__defineGetter__(aName, function() { - Components.utils.import("resource://gre/modules/AddonLogging.jsm"); - - LogManager.getLogger("addons.manager", this); - return this[aName]; - }); -}, this); - -/** - * Calls a callback method consuming any thrown exception. Any parameters after - * the callback parameter will be passed to the callback. - * - * @param aCallback - * The callback method to call - */ -function safeCall(aCallback) { - var args = Array.slice(arguments, 1); - - try { - aCallback.apply(null, args); - } - catch (e) { - WARN("Exception calling callback: " + e); - } -} - -/** - * Calls a method on a provider if it exists and consumes any thrown exception. - * Any parameters after the dflt parameter are passed to the provider's method. - * - * @param aProvider - * The provider to call - * @param aMethod - * The method name to call - * @param aDefault - * A default return value if the provider does not implement the named - * method or throws an error. - * @return the return value from the provider or dflt if the provider does not - * implement method or throws an error - */ -function callProvider(aProvider, aMethod, aDefault) { - if (!(aMethod in aProvider)) - return aDefault; - - var args = Array.slice(arguments, 3); - - try { - return aProvider[aMethod].apply(aProvider, args); - } catch (e) { - ERROR("Exception calling provider." + aMethod + ": " + e); - return aDefault; - } -} - -/** - * A helper class to repeatedly call a listener with each object in an array - * optionally checking whether the object has a method in it. - * - * @param aObjects - * The array of objects to iterate through - * @param aMethod - * An optional method name, if not null any objects without this method - * will not be passed to the listener - * @param aListener - * A listener implementing nextObject and noMoreObjects methods. The - * former will be called with the AsyncObjectCaller as the first - * parameter and the object as the second. noMoreObjects will be passed - * just the AsyncObjectCaller - */ -function AsyncObjectCaller(aObjects, aMethod, aListener) { - this.objects = aObjects.slice(0); - this.method = aMethod; - this.listener = aListener; - - this.callNext(); -} - -AsyncObjectCaller.prototype = { - objects: null, - method: null, - listener: null, - - /** - * Passes the next object to the listener or calls noMoreObjects if there - * are none left. - */ - callNext: function AOC_callNext() { - if (this.objects.length == 0) { - this.listener.noMoreObjects(this); - return; - } - - let object = this.objects.shift(); - if (!this.method || this.method in object) - this.listener.nextObject(this, object); - else - this.callNext(); - } -}; - -/** - * This is the real manager, kept here rather than in AddonManager to keep its - * contents hidden from API users. - */ -var AddonManagerInternal = { - installListeners: null, - addonListeners: null, - providers: [], - started: false, - - /** - * Initializes the AddonManager, loading any known providers and initializing - * them. - */ - startup: function AMI_startup() { - if (this.started) - return; - - this.installListeners = []; - this.addonListeners = []; - - let appChanged = true; - - try { - appChanged = Services.appinfo.version != - Services.prefs.getCharPref("extensions.lastAppVersion"); - } - catch (e) { } - - if (appChanged) { - LOG("Application has been upgraded"); - Services.prefs.setCharPref("extensions.lastAppVersion", - Services.appinfo.version); - } - - // Ensure all default providers have had a chance to register themselves - PROVIDERS.forEach(function(url) { - try { - Components.utils.import(url, {}); - } - catch (e) { - ERROR("Exception loading provider \"" + url + "\": " + e); - } - }); - - let needsRestart = false; - this.providers.forEach(function(provider) { - callProvider(provider, "startup"); - if (callProvider(provider, "checkForChanges", false, appChanged)) - needsRestart = true; - }); - this.started = true; - - // Flag to the platform that a restart is necessary - if (needsRestart) { - let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"]. - getService(Ci.nsIAppStartup2); - appStartup.needsRestart = needsRestart; - } - }, - - /** - * Registers a new AddonProvider. - * - * @param aProvider - * The provider to register - */ - registerProvider: function AMI_registerProvider(aProvider) { - this.providers.push(aProvider); - - // If we're registering after startup call this provider's startup. - if (this.started) - callProvider(aProvider, "startup"); - }, - - /** - * Shuts down the addon manager and all registered providers, this must clean - * up everything in order for automated tests to fake restarts. - */ - shutdown: function AM_shutdown() { - this.providers.forEach(function(provider) { - callProvider(provider, "shutdown"); - }); - - this.installListeners = null; - this.addonListeners = null; - this.started = false; - }, - - /** - * Performs a background update check by starting an update for all add-ons - * that can be updated. - */ - backgroundUpdateCheck: function AMI_backgroundUpdateCheck() { - if (!Services.prefs.getBoolPref(PREF_EM_UPDATE_ENABLED)) - return; - - let scope = {}; - Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope); - scope.LightweightThemeManager.updateCurrentTheme(); - - this.getAllAddons(function getAddonsCallback(aAddons) { - aAddons.forEach(function BUC_forEachCallback(aAddon) { - // Check all add-ons for updates so that any compatibility updates will - // be applied - aAddon.findUpdates({ - onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) { - // Start installing updates when the add-on can be updated and - // background updates should be applied. - if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE && - aAddon.applyBackgroundUpdates) { - aInstall.install(); - } - } - }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE); - }); - }); - }, - - /** - * Calls all registered InstallListeners with an event. Any parameters after - * the extraListeners parameter are passed to the listener. - * - * @param aMethod - * The method on the listeners to call - * @param aExtraListeners - * An array of extra InstallListeners to also call - * @return false if any of the listeners returned false, true otherwise - */ - callInstallListeners: function AMI_callInstallListeners(aMethod, aExtraListeners) { - let result = true; - let listeners = this.installListeners; - if (aExtraListeners) - listeners = aExtraListeners.concat(listeners); - let args = Array.slice(arguments, 2); - - listeners.forEach(function(listener) { - try { - if (aMethod in listener) { - if (listener[aMethod].apply(listener, args) === false) - result = false; - } - } - catch (e) { - WARN("InstallListener threw exception when calling " + aMethod + ": " + e); - } - }); - return result; - }, - - /** - * Calls all registered AddonListeners with an event. Any parameters after - * the method parameter are passed to the listener. - * - * @param aMethod - * The method on the listeners to call - */ - callAddonListeners: function AMI_callAddonListeners(aMethod) { - var args = Array.slice(arguments, 1); - this.addonListeners.forEach(function(listener) { - try { - if (aMethod in listener) - listener[aMethod].apply(listener, args); - } - catch (e) { - WARN("AddonListener threw exception when calling " + aMethod + ": " + e); - } - }); - }, - - /** - * Notifies all providers that an add-on has been enabled when that type of - * add-on only supports a single add-on being enabled at a time. This allows - * the providers to disable theirs if necessary. - * - * @param aId - * The id of the enabled add-on - * @param aType - * The type of the enabled add-on - * @param aPendingRestart - * A boolean indicating if the change will only take place the next - * time the application is restarted - */ - notifyAddonChanged: function AMI_notifyAddonChanged(aId, aType, aPendingRestart) { - this.providers.forEach(function(provider) { - callProvider(provider, "addonChanged", null, aId, aType, aPendingRestart); - }); - }, - - /** - * Notifies all providers they need to update the appDisabled property for - * their add-ons in response to an application change such as a blocklist - * update. - */ - updateAddonAppDisabledStates: function AMI_updateAddonAppDisabledStates() { - this.providers.forEach(function(provider) { - callProvider(provider, "updateAddonAppDisabledStates"); - }); - }, - - /** - * Asynchronously gets an AddonInstall for a URL. - * - * @param aUrl - * The url the add-on is located at - * @param aCallback - * A callback to pass the AddonInstall to - * @param aMimetype - * The mimetype of the add-on - * @param aHash - * An optional hash of the add-on - * @param aName - * An optional placeholder name while the add-on is being downloaded - * @param aIconURL - * An optional placeholder icon URL while the add-on is being downloaded - * @param aVersion - * An optional placeholder version while the add-on is being downloaded - * @param aLoadGroup - * An optional nsILoadGroup to associate any network requests with - * @throws if the aUrl, aCallback or aMimetype arguments are not specified - */ - getInstallForURL: function AMI_getInstallForURL(aUrl, aCallback, aMimetype, - aHash, aName, aIconURL, - aVersion, aLoadGroup) { - if (!aUrl || !aMimetype || !aCallback) - throw new TypeError("Invalid arguments"); - - for (let i = 0; i < this.providers.length; i++) { - if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype)) { - callProvider(this.providers[i], "getInstallForURL", null, - aUrl, aHash, aName, aIconURL, aVersion, aLoadGroup, - function(aInstall) { - safeCall(aCallback, aInstall); - }); - return; - } - } - safeCall(aCallback, null); - }, - - /** - * Asynchronously gets an AddonInstall for an nsIFile. - * - * @param aFile - * the nsIFile where the add-on is located - * @param aCallback - * A callback to pass the AddonInstall to - * @param aMimetype - * An optional mimetype hint for the add-on - * @throws if the aFile or aCallback arguments are not specified - */ - getInstallForFile: function AMI_getInstallForFile(aFile, aCallback, aMimetype) { - if (!aFile || !aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - new AsyncObjectCaller(this.providers, "getInstallForFile", { - nextObject: function(aCaller, aProvider) { - callProvider(aProvider, "getInstallForFile", null, aFile, - function(aInstall) { - if (aInstall) - safeCall(aCallback, aInstall); - else - aCaller.callNext(); - }); - }, - - noMoreObjects: function(aCaller) { - safeCall(aCallback, null); - } - }); - }, - - /** - * Asynchronously gets all current AddonInstalls optionally limiting to a list - * of types. - * - * @param aTypes - * An optional array of types to retrieve. Each type is a string name - * @param aCallback - * A callback which will be passed an array of AddonInstalls - * @throws if the aCallback argument is not specified - */ - getInstallsByTypes: function AMI_getInstallsByTypes(aTypes, aCallback) { - if (!aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - let installs = []; - - new AsyncObjectCaller(this.providers, "getInstallsByTypes", { - nextObject: function(aCaller, aProvider) { - callProvider(aProvider, "getInstallsByTypes", null, aTypes, - function(aProviderInstalls) { - installs = installs.concat(aProviderInstalls); - aCaller.callNext(); - }); - }, - - noMoreObjects: function(aCaller) { - safeCall(aCallback, installs); - } - }); - }, - - /** - * Asynchronously gets all current AddonInstalls. - * - * @param aCallback - * A callback which will be passed an array of AddonInstalls - */ - getAllInstalls: function AMI_getAllInstalls(aCallback) { - this.getInstallsByTypes(null, aCallback); - }, - - /** - * Checks whether installation is enabled for a particular mimetype. - * - * @param aMimetype - * The mimetype to check - * @return true if installation is enabled for the mimetype - */ - isInstallEnabled: function AMI_isInstallEnabled(aMimetype) { - for (let i = 0; i < this.providers.length; i++) { - if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype) && - callProvider(this.providers[i], "isInstallEnabled")) - return true; - } - return false; - }, - - /** - * Checks whether a particular source is allowed to install add-ons of a - * given mimetype. - * - * @param aMimetype - * The mimetype of the add-on - * @param aURI - * The uri of the source, may be null - * @return true if the source is allowed to install this mimetype - */ - isInstallAllowed: function AMI_isInstallAllowed(aMimetype, aURI) { - for (let i = 0; i < this.providers.length; i++) { - if (callProvider(this.providers[i], "supportsMimetype", false, aMimetype) && - callProvider(this.providers[i], "isInstallAllowed", null, aURI)) - return true; - } - return false; - }, - - /** - * Starts installation of an array of AddonInstalls notifying the registered - * web install listener of blocked or started installs. - * - * @param aMimetype - * The mimetype of add-ons being installed - * @param aSource - * The nsIDOMWindowInternal that started the installs - * @param aURI - * the nsIURI that started the installs - * @param aInstalls - * The array of AddonInstalls to be installed - */ - installAddonsFromWebpage: function AMI_installAddonsFromWebpage(aMimetype, - aSource, - aURI, - aInstalls) { - if (!("@mozilla.org/addons/web-install-listener;1" in Cc)) { - WARN("No web installer available, cancelling all installs"); - aInstalls.forEach(function(aInstall) { - aInstall.cancel(); - }); - return; - } - - try { - let weblistener = Cc["@mozilla.org/addons/web-install-listener;1"]. - getService(Ci.amIWebInstallListener); - - if (!this.isInstallAllowed(aMimetype, aURI)) { - if (weblistener.onWebInstallBlocked(aSource, aURI, aInstalls, - aInstalls.length)) { - aInstalls.forEach(function(aInstall) { - aInstall.install(); - }); - } - } - else if (weblistener.onWebInstallRequested(aSource, aURI, aInstalls, - aInstalls.length)) { - aInstalls.forEach(function(aInstall) { - aInstall.install(); - }); - } - } - catch (e) { - // In the event that the weblistener throws during instatiation or when - // calling onWebInstallBlocked or onWebInstallRequested all of the - // installs should get cancelled. - WARN("Failure calling web installer: " + e); - aInstalls.forEach(function(aInstall) { - aInstall.cancel(); - }); - } - }, - - /** - * Adds a new InstallListener if the listener is not already registered. - * - * @param aListener - * The InstallListener to add - */ - addInstallListener: function AMI_addInstallListener(aListener) { - if (!this.installListeners.some(function(i) { return i == aListener; })) - this.installListeners.push(aListener); - }, - - /** - * Removes an InstallListener if the listener is registered. - * - * @param aListener - * The InstallListener to remove - */ - removeInstallListener: function AMI_removeInstallListener(aListener) { - this.installListeners = this.installListeners.filter(function(i) { - return i != aListener; - }); - }, - - /** - * Asynchronously gets an add-on with a specific ID. - * - * @param aId - * The ID of the add-on to retrieve - * @param aCallback - * The callback to pass the retrieved add-on to - * @throws if the aId or aCallback arguments are not specified - */ - getAddonByID: function AMI_getAddonByID(aId, aCallback) { - if (!aId || !aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - new AsyncObjectCaller(this.providers, "getAddonByID", { - nextObject: function(aCaller, aProvider) { - callProvider(aProvider, "getAddonByID", null, aId, function(aAddon) { - if (aAddon) - safeCall(aCallback, aAddon); - else - aCaller.callNext(); - }); - }, - - noMoreObjects: function(aCaller) { - safeCall(aCallback, null); - } - }); - }, - - /** - * Asynchronously gets an array of add-ons. - * - * @param aIds - * The array of IDs to retrieve - * @param aCallback - * The callback to pass an array of Addons to - * @throws if the aId or aCallback arguments are not specified - */ - getAddonsByIDs: function AMI_getAddonsByIDs(aIds, aCallback) { - if (!aIds || !aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - let addons = []; - - new AsyncObjectCaller(aIds, null, { - nextObject: function(aCaller, aId) { - AddonManagerInternal.getAddonByID(aId, function(aAddon) { - addons.push(aAddon); - aCaller.callNext(); - }); - }, - - noMoreObjects: function(aCaller) { - safeCall(aCallback, addons); - } - }); - }, - - /** - * Asynchronously gets add-ons of specific types. - * - * @param aTypes - * An optional array of types to retrieve. Each type is a string name - * @param aCallback - * The callback to pass an array of Addons to. - * @throws if the aCallback argument is not specified - */ - getAddonsByTypes: function AMI_getAddonsByTypes(aTypes, aCallback) { - if (!aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - let addons = []; - - new AsyncObjectCaller(this.providers, "getAddonsByTypes", { - nextObject: function(aCaller, aProvider) { - callProvider(aProvider, "getAddonsByTypes", null, aTypes, - function(aProviderAddons) { - addons = addons.concat(aProviderAddons); - aCaller.callNext(); - }); - }, - - noMoreObjects: function(aCaller) { - safeCall(aCallback, addons); - } - }); - }, - - /** - * Asynchronously gets all installed add-ons. - * - * @param aCallback - * A callback which will be passed an array of Addons - */ - getAllAddons: function AMI_getAllAddons(aCallback) { - this.getAddonsByTypes(null, aCallback); - }, - - /** - * Asynchronously gets add-ons that have operations waiting for an application - * restart to complete. - * - * @param aTypes - * An optional array of types to retrieve. Each type is a string name - * @param aCallback - * The callback to pass the array of Addons to - * @throws if the aCallback argument is not specified - */ - getAddonsWithOperationsByTypes: - function AMI_getAddonsWithOperationsByTypes(aTypes, aCallback) { - if (!aCallback) - throw Cr.NS_ERROR_INVALID_ARG; - - let addons = []; - - new AsyncObjectCaller(this.providers, "getAddonsWithOperationsByTypes", { - nextObject: function(aCaller, aProvider) { - callProvider(aProvider, "getAddonsWithOperationsByTypes", null, aTypes, - function(aProviderAddons) { - addons = addons.concat(aProviderAddons); - aCaller.callNext(); - }); - }, - - noMoreObjects: function(caller) { - safeCall(aCallback, addons); - } - }); - }, - - /** - * Adds a new AddonListener if the listener is not already registered. - * - * @param aListener - * The listener to add - */ - addAddonListener: function AMI_addAddonListener(aListener) { - if (!this.addonListeners.some(function(i) { return i == aListener; })) - this.addonListeners.push(aListener); - }, - - /** - * Removes an AddonListener if the listener is registered. - * - * @param aListener - * The listener to remove - */ - removeAddonListener: function AMI_removeAddonListener(aListener) { - this.addonListeners = this.addonListeners.filter(function(i) { - return i != aListener; - }); - } -}; - -/** - * Should not be used outside of core Mozilla code. This is a private API for - * the startup and platform integration code to use. Refer to the methods on - * AddonManagerInternal for documentation however note that these methods are - * subject to change at any time. - */ -var AddonManagerPrivate = { - startup: function AMP_startup() { - AddonManagerInternal.startup(); - }, - - registerProvider: function AMP_registerProvider(aProvider) { - AddonManagerInternal.registerProvider(aProvider); - }, - - shutdown: function AMP_shutdown() { - AddonManagerInternal.shutdown(); - }, - - backgroundUpdateCheck: function AMP_backgroundUpdateCheck() { - AddonManagerInternal.backgroundUpdateCheck(); - }, - - notifyAddonChanged: function AMP_notifyAddonChanged(aId, aType, aPendingRestart) { - AddonManagerInternal.notifyAddonChanged(aId, aType, aPendingRestart); - }, - - updateAddonAppDisabledStates: function AMP_updateAddonAppDisabledStates() { - AddonManagerInternal.updateAddonAppDisabledStates(); - }, - - callInstallListeners: function AMP_callInstallListeners(aMethod) { - return AddonManagerInternal.callInstallListeners.apply(AddonManagerInternal, - arguments); - }, - - callAddonListeners: function AMP_callAddonListeners(aMethod) { - AddonManagerInternal.callAddonListeners.apply(AddonManagerInternal, arguments); - } -}; - -/** - * This is the public API that UI and developers should be calling. All methods - * just forward to AddonManagerInternal. - */ -var AddonManager = { - // Constants for the AddonInstall.state property - // The install is available for download. - STATE_AVAILABLE: 0, - // The install is being downloaded. - STATE_DOWNLOADING: 1, - // The install is checking for compatibility information. - STATE_CHECKING: 2, - // The install is downloaded and ready to install. - STATE_DOWNLOADED: 3, - // The download failed. - STATE_DOWNLOAD_FAILED: 4, - // The add-on is being installed. - STATE_INSTALLING: 5, - // The add-on has been installed. - STATE_INSTALLED: 6, - // The install failed. - STATE_INSTALL_FAILED: 7, - // The install has been cancelled. - STATE_CANCELLED: 8, - - // Constants representing different types of errors while downloading an - // add-on. - // The download failed due to network problems. - ERROR_NETWORK_FAILURE: -1, - // The downloaded file did not match the provided hash. - ERROR_INCORRECT_HASH: -2, - // The downloaded file seems to be corrupted in some way. - ERROR_CORRUPT_FILE: -3, - - // Constants to indicate why an update check is being performed - // Update check has been requested by the user. - UPDATE_WHEN_USER_REQUESTED: 1, - // Update check is necessary to see if the Addon is compatibile with a new - // version of the application. - UPDATE_WHEN_NEW_APP_DETECTED: 2, - // Update check is necessary because a new application has been installed. - UPDATE_WHEN_NEW_APP_INSTALLED: 3, - // Update check is a regular background update check. - UPDATE_WHEN_PERIODIC_UPDATE: 16, - // Update check is needed to check an Addon that is being installed. - UPDATE_WHEN_ADDON_INSTALLED: 17, - - // Constants for operations in Addon.pendingOperations - // Indicates that the Addon has no pending operations. - PENDING_NONE: 0, - // Indicates that the Addon will be enabled after the application restarts. - PENDING_ENABLE: 1, - // Indicates that the Addon will be disabled after the application restarts. - PENDING_DISABLE: 2, - // Indicates that the Addon will be uninstalled after the application restarts. - PENDING_UNINSTALL: 4, - // Indicates that the Addon will be installed after the application restarts. - PENDING_INSTALL: 8, - PENDING_UPGRADE: 16, - - // Constants for permissions in Addon.permissions. - // Indicates that the Addon can be uninstalled. - PERM_CAN_UNINSTALL: 1, - // Indicates that the Addon can be enabled by the user. - PERM_CAN_ENABLE: 2, - // Indicates that the Addon can be disabled by the user. - PERM_CAN_DISABLE: 4, - // Indicates that the Addon can be upgraded. - PERM_CAN_UPGRADE: 8, - - // General descriptions of where items are installed. - // Installed in this profile. - SCOPE_PROFILE: 1, - // Installed for all of this user's profiles. - SCOPE_USER: 2, - // Installed and owned by the application. - SCOPE_APPLICATION: 4, - // Installed for all users of the computer. - SCOPE_SYSTEM: 8, - // The combination of all scopes. - SCOPE_ALL: 15, - - getInstallForURL: function AM_getInstallForURL(aUrl, aCallback, aMimetype, - aHash, aName, aIconURL, - aVersion, aLoadGroup) { - AddonManagerInternal.getInstallForURL(aUrl, aCallback, aMimetype, aHash, - aName, aIconURL, aVersion, aLoadGroup); - }, - - getInstallForFile: function AM_getInstallForFile(aFile, aCallback, aMimetype) { - AddonManagerInternal.getInstallForFile(aFile, aCallback, aMimetype); - }, - - getAddonByID: function AM_getAddonByID(aId, aCallback) { - AddonManagerInternal.getAddonByID(aId, aCallback); - }, - - getAddonsByIDs: function AM_getAddonsByIDs(aIds, aCallback) { - AddonManagerInternal.getAddonsByIDs(aIds, aCallback); - }, - - getAddonsWithOperationsByTypes: - function AM_getAddonsWithOperationsByTypes(aTypes, aCallback) { - AddonManagerInternal.getAddonsWithOperationsByTypes(aTypes, aCallback); - }, - - getAddonsByTypes: function AM_getAddonsByTypes(aTypes, aCallback) { - AddonManagerInternal.getAddonsByTypes(aTypes, aCallback); - }, - - getAllAddons: function AM_getAllAddons(aCallback) { - AddonManagerInternal.getAllAddons(aCallback); - }, - - getInstallsByTypes: function AM_getInstallsByTypes(aTypes, aCallback) { - AddonManagerInternal.getInstallsByTypes(aTypes, aCallback); - }, - - getAllInstalls: function AM_getAllInstalls(aCallback) { - AddonManagerInternal.getAllInstalls(aCallback); - }, - - isInstallEnabled: function AM_isInstallEnabled(aType) { - return AddonManagerInternal.isInstallEnabled(aType); - }, - - isInstallAllowed: function AM_isInstallAllowed(aType, aUri) { - return AddonManagerInternal.isInstallAllowed(aType, aUri); - }, - - installAddonsFromWebpage: function AM_installAddonsFromWebpage(aType, aSource, - aUri, aInstalls) { - AddonManagerInternal.installAddonsFromWebpage(aType, aSource, aUri, aInstalls); - }, - - addInstallListener: function AM_addInstallListener(aListener) { - AddonManagerInternal.addInstallListener(aListener); - }, - - removeInstallListener: function AM_removeInstallListener(aListener) { - AddonManagerInternal.removeInstallListener(aListener); - }, - - addAddonListener: function AM_addAddonListener(aListener) { - AddonManagerInternal.addAddonListener(aListener); - }, - - removeAddonListener: function AM_removeAddonListener(aListener) { - AddonManagerInternal.removeAddonListener(aListener); - } -};
deleted file mode 100644 --- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm +++ /dev/null @@ -1,658 +0,0 @@ -/* -# ***** 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 the Extension Manager. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Dave Townsend <dtownsend@oxymoronical.com> -# -# 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 ***** -*/ - -/** - * The AddonUpdateChecker is responsible for retrieving the update information - * from an add-on's remote update manifest. - */ - -const Cc = Components.classes; -const Ci = Components.interfaces; - -var EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ]; - -const TIMEOUT = 2 * 60 * 1000; -const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; -const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#"; -const PREFIX_ITEM = "urn:mozilla:item:"; -const PREFIX_EXTENSION = "urn:mozilla:extension:"; -const PREFIX_THEME = "urn:mozilla:theme:"; -const TOOLKIT_ID = "toolkit@mozilla.org" -const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml" - -Components.utils.import("resource://gre/modules/Services.jsm"); -// shared code for suppressing bad cert dialogs -Components.utils.import("resource://gre/modules/CertUtils.jsm"); - -var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"]. - getService(Ci.nsIRDFService); - -["LOG", "WARN", "ERROR"].forEach(function(aName) { - this.__defineGetter__(aName, function() { - Components.utils.import("resource://gre/modules/AddonLogging.jsm"); - - LogManager.getLogger("addons.updates", this); - return this[aName]; - }); -}, this); - -/** - * A serialisation method for RDF data that produces an identical string - * for matching RDF graphs. - * The serialisation is not complete, only assertions stemming from a given - * resource are included, multiple references to the same resource are not - * permitted, and the RDF prolog and epilog are not included. - * RDF Blob and Date literals are not supported. - */ -function RDFSerializer() { - this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"]. - getService(Ci.nsIRDFContainerUtils); - this.resources = []; -} - -RDFSerializer.prototype = { - INDENT: " ", // The indent used for pretty-printing - resources: null, // Array of the resources that have been found - - /** - * Escapes characters from a string that should not appear in XML. - * - * @param aString - * The string to be escaped - * @return a string with all characters invalid in XML character data - * converted to entity references. - */ - escapeEntities: function RDFS_escapeEntities(aString) { - aString = aString.replace(/&/g, "&"); - aString = aString.replace(/</g, "<"); - aString = aString.replace(/>/g, ">"); - return aString.replace(/"/g, """); - }, - - /** - * Serializes all the elements of an RDF container. - * - * @param aDs - * The RDF datasource - * @param aContainer - * The RDF container to output the child elements of - * @param aIndent - * The current level of indent for pretty-printing - * @return a string containing the serialized elements. - */ - serializeContainerItems: function RDFS_serializeContainerItems(aDs, aContainer, - aIndent) { - var result = ""; - var items = aContainer.GetElements(); - while (items.hasMoreElements()) { - var item = items.getNext().QueryInterface(Ci.nsIRDFResource); - result += aIndent + "<RDF:li>\n" - result += this.serializeResource(aDs, item, aIndent + this.INDENT); - result += aIndent + "</RDF:li>\n" - } - return result; - }, - - /** - * Serializes all em:* (see EM_NS) properties of an RDF resource except for - * the em:signature property. As this serialization is to be compared against - * the manifest signature it cannot contain the em:signature property itself. - * - * @param aDs - * The RDF datasource - * @param aResource - * The RDF resource that contains the properties to serialize - * @param aIndent - * The current level of indent for pretty-printing - * @return a string containing the serialized properties. - * @throws if the resource contains a property that cannot be serialized - */ - serializeResourceProperties: function RDFS_serializeResourceProperties(aDs, - aResource, - aIndent) { - var result = ""; - var items = []; - var arcs = aDs.ArcLabelsOut(aResource); - while (arcs.hasMoreElements()) { - var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource); - if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM) - continue; - var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length); - if (prop == "signature") - continue; - - var targets = aDs.GetTargets(aResource, arc, true); - while (targets.hasMoreElements()) { - var target = targets.getNext(); - if (target instanceof Ci.nsIRDFResource) { - var item = aIndent + "<em:" + prop + ">\n"; - item += this.serializeResource(aDs, target, aIndent + this.INDENT); - item += aIndent + "</em:" + prop + ">\n"; - items.push(item); - } - else if (target instanceof Ci.nsIRDFLiteral) { - items.push(aIndent + "<em:" + prop + ">" + - this.escapeEntities(target.Value) + "</em:" + prop + ">\n"); - } - else if (target instanceof Ci.nsIRDFInt) { - items.push(aIndent + "<em:" + prop + " NC:parseType=\"Integer\">" + - target.Value + "</em:" + prop + ">\n"); - } - else { - throw new Error("Cannot serialize unknown literal type"); - } - } - } - items.sort(); - result += items.join(""); - return result; - }, - - /** - * Recursively serializes an RDF resource and all resources it links to. - * This will only output EM_NS properties and will ignore any em:signature - * property. - * - * @param aDs - * The RDF datasource - * @param aResource - * The RDF resource to serialize - * @param aIndent - * The current level of indent for pretty-printing. If undefined no - * indent will be added - * @return a string containing the serialized resource. - * @throws if the RDF data contains multiple references to the same resource. - */ - serializeResource: function RDFS_serializeResource(aDs, aResource, aIndent) { - if (this.resources.indexOf(aResource) != -1 ) { - // We cannot output multiple references to the same resource. - throw new Error("Cannot serialize multiple references to " + aResource.Value); - } - if (aIndent === undefined) - aIndent = ""; - - this.resources.push(aResource); - var container = null; - var type = "Description"; - if (this.cUtils.IsSeq(aDs, aResource)) { - type = "Seq"; - container = this.cUtils.MakeSeq(aDs, aResource); - } - else if (this.cUtils.IsAlt(aDs, aResource)) { - type = "Alt"; - container = this.cUtils.MakeAlt(aDs, aResource); - } - else if (this.cUtils.IsBag(aDs, aResource)) { - type = "Bag"; - container = this.cUtils.MakeBag(aDs, aResource); - } - - var result = aIndent + "<RDF:" + type; - if (!gRDF.IsAnonymousResource(aResource)) - result += " about=\"" + this.escapeEntities(aResource.ValueUTF8) + "\""; - result += ">\n"; - - if (container) - result += this.serializeContainerItems(aDs, container, aIndent + this.INDENT); - - result += this.serializeResourceProperties(aDs, aResource, aIndent + this.INDENT); - - result += aIndent + "</RDF:" + type + ">\n"; - return result; - } -} - -/** - * Parses an RDF style update manifest into an array of update objects. - * - * @param aId - * The ID of the add-on being checked for updates - * @param aType - * The type of the add-on being checked for updates - * @param aUpdateKey - * An optional update key for the add-on - * @param aRequest - * The XMLHttpRequest that has retrieved the update manifest - * @return an array of update objects - * @throws if the update manifest is invalid in any way - */ -function parseRDFManifest(aId, aType, aUpdateKey, aRequest) { - function EM_R(aProp) { - return gRDF.GetResource(PREFIX_NS_EM + aProp); - } - - function getValue(aLiteral) { - if (aLiteral instanceof Ci.nsIRDFLiteral) - return aLiteral.Value; - if (aLiteral instanceof Ci.nsIRDFResource) - return aLiteral.Value; - if (aLiteral instanceof Ci.nsIRDFInt) - return aLiteral.Value; - return null; - } - - function getProperty(aDs, aSource, aProperty) { - return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true)); - } - - function getRequiredProperty(aDs, aSource, aProperty) { - let value = getProperty(aDs, aSource, aProperty); - if (!value) - throw new Error("Update manifest is missing a required " + aProperty + " property."); - return value; - } - - let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"]. - createInstance(Ci.nsIRDFXMLParser); - let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]. - createInstance(Ci.nsIRDFDataSource); - rdfParser.parseString(ds, aRequest.channel.URI, aRequest.responseText); - - switch (aType) { - case "extension": - var item = PREFIX_EXTENSION + aId; - break; - case "theme": - item = PREFIX_THEME + aId; - break; - default: - item = PREFIX_ITEM + aId; - break; - } - - let extensionRes = gRDF.GetResource(item); - - // If we have an update key then the update manifest must be signed - if (aUpdateKey) { - let signature = getProperty(ds, extensionRes, "signature"); - if (!signature) - throw new Error("Update manifest for " + aId + " does not contain a required signature"); - let serializer = new RDFSerializer(); - let updateString = null; - - try { - updateString = serializer.serializeResource(ds, extensionRes); - } - catch (e) { - throw new Error("Failed to generate signed string for " + aId + ". Serializer threw " + e); - } - - let result = false; - - try { - let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"]. - getService(Ci.nsIDataSignatureVerifier); - result = verifier.verifyData(updateString, signature, aUpdateKey); - } - catch (e) { - throw new Error("The signature or updateKey for " + aId + " is malformed"); - } - - if (!result) - throw new Error("The signature for " + aId + " was not created by the add-on's updateKey"); - } - - let updates = ds.GetTarget(extensionRes, EM_R("updates"), true); - - if (!updates || !(updates instanceof Ci.nsIRDFResource)) - throw new Error("Missing updates property for " + extensionRes.Value); - - let cu = Cc["@mozilla.org/rdf/container-utils;1"]. - getService(Ci.nsIRDFContainerUtils); - if (!cu.IsContainer(ds, updates)) - throw new Error("Updates property was not an RDF container"); - - let checkSecurity = true; - - try { - checkSecurity = Services.prefs.getBoolPref("extensions.checkUpdateSecurity"); - } - catch (e) { - } - - let results = []; - let ctr = Cc["@mozilla.org/rdf/container;1"]. - createInstance(Ci.nsIRDFContainer); - ctr.Init(ds, updates); - let items = ctr.GetElements(); - while (items.hasMoreElements()) { - let item = items.getNext().QueryInterface(Ci.nsIRDFResource); - let version = getProperty(ds, item, "version"); - if (!version) { - WARN("Update manifest is missing a required version property."); - continue; - } - - LOG("Found an update entry for " + aId + " version " + version); - - let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true); - while (targetApps.hasMoreElements()) { - let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource); - - let appEntry = {}; - try { - appEntry.id = getRequiredProperty(ds, targetApp, "id"); - appEntry.minVersion = getRequiredProperty(ds, targetApp, "minVersion"); - appEntry.maxVersion = getRequiredProperty(ds, targetApp, "maxVersion"); - } - catch (e) { - WARN(e); - continue; - } - - let result = { - version: version, - updateURL: getProperty(ds, targetApp, "updateLink"), - updateHash: getProperty(ds, targetApp, "updateHash"), - updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"), - targetApplications: [appEntry] - }; - - if (result.updateURL && checkSecurity && - result.updateURL.substring(0, 6) != "https:" && - (!result.updateHash || result.updateHash.substring(0, 3) != "sha")) { - WARN("updateLink " + result.updateURL + " is not secure and is not verified" + - " by a strong enough hash (needs to be sha1 or stronger)."); - delete result.updateURL; - delete result.updateHash; - } - results.push(result); - } - } - return results; -} - -/** - * Starts downloading an update manifest and then passes it to an appropriate - * parser to convert to an array of update objects - * - * @param aId - * The ID of the add-on being checked for updates - * @param aType - * The type of add-on being checked for updates - * @param aUpdateKey - * An optional update key for the add-on - * @param aUrl - * The URL of the update manifest - * @param aObserver - * An observer to pass results to - */ -function UpdateParser(aId, aType, aUpdateKey, aUrl, aObserver) { - this.id = aId; - this.type = aType; - this.updateKey = aUpdateKey; - this.observer = aObserver; - - this.timer = Cc["@mozilla.org/timer;1"]. - createInstance(Ci.nsITimer); - this.timer.initWithCallback(this, TIMEOUT, Ci.nsITimer.TYPE_ONE_SHOT); - - LOG("Requesting " + aUrl); - this.request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. - createInstance(Ci.nsIXMLHttpRequest); - this.request.open("GET", aUrl, true); - this.request.channel.notificationCallbacks = new BadCertHandler(); - this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; - this.request.overrideMimeType("text/xml"); - var self = this; - this.request.onload = function(event) { self.onLoad() }; - this.request.onerror = function(event) { self.onError() }; - this.request.send(null); -} - -UpdateParser.prototype = { - id: null, - type: null, - updateKey: null, - observer: null, - request: null, - timer: null, - - /** - * Called when the manifest has been successfully loaded. - */ - onLoad: function UP_onLoad() { - this.timer.cancel(); - this.timer = null; - let request = this.request; - this.request = null; - - try { - checkCert(request.channel); - } - catch (e) { - this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR); - return; - } - - if (!Components.isSuccessCode(request.status)) { - WARN("Request failed: " + request.status); - this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR); - return; - } - - let xml = request.responseXML; - if (!xml || xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR) { - WARN("Update manifest was not valid XML"); - this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR); - return; - } - - // We currently only know about RDF update manifests - if (xml.documentElement.namespaceURI == PREFIX_NS_RDF) { - let results = null; - - try { - results = parseRDFManifest(this.id, this.type, this.updateKey, request); - } - catch (e) { - WARN(e); - this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR); - return; - } - if ("onUpdateCheckComplete" in this.observer) - this.observer.onUpdateCheckComplete(results); - return; - } - - WARN("Update manifest had an unrecognised namespace: " + xml.documentElement.namespaceURI); - this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT); - }, - - /** - * Called when the manifest failed to load. - */ - onError: function UP_onError() { - this.timer.cancel(); - this.timer = null; - - WARN("Request failed: " + this.request.status); - this.request = null; - - this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR); - }, - - /** - * Helper method to notify the observer that an error occured. - */ - notifyError: function UP_notifyError(aStatus) { - if ("onUpdateCheckError" in this.observer) - this.observer.onUpdateCheckError(aStatus); - }, - - /** - * Called when the request has timed out and should be canceled. - */ - notify: function UP_notify(aTimer) { - this.timer = null; - this.request.abort(); - this.request = null; - - WARN("Request timed out"); - - this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT); - } -}; - -/** - * Tests if an update matches a version of the application or platform - * - * @param aUpdate - * The available update - * @param aAppVersion - * The application version to use - * @param aPlatformVersion - * The platform version to use - * @return true if the update is compatible with the application/platform - */ -function matchesVersions(aUpdate, aAppVersion, aPlatformVersion) { - let result = false; - for (let i = 0; i < aUpdate.targetApplications.length; i++) { - let app = aUpdate.targetApplications[i]; - if (app.id == Services.appinfo.ID) { - return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) && - (Services.vc.compare(aAppVersion, app.maxVersion) <= 0); - } - if (app.id == TOOLKIT_ID) { - result = (Services.vc.compare(aPlatformVersion, app.minVersion) >= 0) && - (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0); - } - } - return result; -} - -var AddonUpdateChecker = { - ERROR_TIMEOUT: -1, - ERROR_DOWNLOAD_ERROR: -2, - ERROR_PARSE_ERROR: -3, - ERROR_UNKNOWN_FORMAT: -4, - ERROR_SECURITY_ERROR: -5, - - /** - * Retrieves the best matching compatibility update for the application from - * a list of available update objects. - * - * @param aUpdates - * An array of update objects - * @param aVersion - * The version of the add-on to get new compatibility information for - * @param aIgnoreCompatibility - * An optional parameter to get the first compatibility update that - * is compatible with any version of the application or toolkit - * @param aAppVersion - * The version of the application or null to use the current version - * @param aPlatformVersion - * The version of the platform or null to use the current version - * @return an update object if one matches or null if not - */ - getCompatibilityUpdate: function AUC_getCompatibilityUpdate(aUpdates, aVersion, - aIgnoreCompatibility, - aAppVersion, - aPlatformVersion) { - if (!aAppVersion) - aAppVersion = Services.appinfo.version; - if (!aPlatformVersion) - aPlatformVersion = Services.appinfo.platformVersion; - - for (let i = 0; i < aUpdates.length; i++) { - if (Services.vc.compare(aUpdates[i].version, aVersion) == 0) { - if (aIgnoreCompatibility) { - for (let j = 0; j < aUpdates[i].targetApplications.length; j++) { - let id = aUpdates[i].targetApplications[j].id; - if (id == Services.appinfo.ID || id == TOOLKIT_ID) - return aUpdates[i]; - } - } - else if (matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion)) { - return aUpdates[i]; - } - } - } - return null; - }, - - /** - * Returns the newest available update from a list of update objects. - * - * @param aUpdates - * An array of update objects - * @param aAppVersion - * The version of the application or null to use the current version - * @param aPlatformVersion - * The version of the platform or null to use the current version - * @return an update object if one matches or null if not - */ - getNewestCompatibleUpdate: function AUC_getNewestCompatibleUpdate(aUpdates, - aAppVersion, - aPlatformVersion) { - if (!aAppVersion) - aAppVersion = Services.appinfo.version; - if (!aPlatformVersion) - aPlatformVersion = Services.appinfo.platformVersion; - - let newest = null; - for (let i = 0; i < aUpdates.length; i++) { - if (!aUpdates[i].updateURL) - continue; - if ((newest == null || (Services.vc.compare(newest.version, aUpdates[i].version) < 0)) && - matchesVersions(aUpdates[i], aAppVersion, aPlatformVersion)) - newest = aUpdates[i]; - } - return newest; - }, - - /** - * Starts an update check. - * - * @param aId - * The ID of the add-on being checked for updates - * @param aType - * The type of add-on being checked for updates - * @param aUpdateKey - * An optional update key for the add-on - * @param aUrl - * The URL of the add-on's update manifest - * @param aObserver - * An observer to notify of results - */ - checkForUpdates: function AUC_checkForUpdates(aId, aType, aUpdateKey, aUrl, - aObserver) { - new UpdateParser(aId, aType, aUpdateKey, aUrl, aObserver); - } -};
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm +++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm @@ -34,24 +34,16 @@ * * ***** END LICENSE BLOCK ***** */ var EXPORTED_SYMBOLS = ["LightweightThemeManager"]; const Cc = Components.classes; const Ci = Components.interfaces; -Components.utils.import("resource://gre/modules/AddonManager.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -const ID_SUFFIX = "@personas.mozilla.org"; -const PREF_LWTHEME_TO_SELECT = "extensions.lwThemeToSelect"; -const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin"; -const ADDON_TYPE = "theme"; - const MAX_USED_THEMES_COUNT = 8; const MAX_PREVIEW_SECONDS = 30; const MANDATORY = ["id", "name", "headerURL"]; const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL", "previewURL", "author", "description", "homepageURL", "updateURL", "version"]; @@ -130,31 +122,21 @@ var LightweightThemeManager = { for (let i = 0; i < usedThemes.length; i++) { if (usedThemes[i].id == aId) return usedThemes[i]; } return null; }, forgetUsedTheme: function (aId) { - let theme = this.getUsedTheme(aId); - if (!theme) - return; - - let wrapper = new AddonWrapper(theme); - AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false); - var currentTheme = this.currentTheme; - if (currentTheme && currentTheme.id == aId) { - _prefs.setBoolPref("isThemeSelected", false); - AddonManagerPrivate.notifyAddonChanged(null, ADDON_TYPE, false); - } + if (currentTheme && currentTheme.id == aId) + this.currentTheme = null; _updateUsedThemes(_usedThemesExceptId(aId)); - AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper); }, previewTheme: function (aData) { if (!aData) return; let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); cancel.data = false; @@ -221,338 +203,52 @@ var LightweightThemeManager = { return; var currentTheme = self.currentTheme; if (currentTheme && currentTheme.id == theme.id) self.currentTheme = newData; }; req.send(null); - }, - - /** - * Switches to a new lightweight theme. - * - * @param aData - * The lightweight theme to switch to - */ - themeChanged: function(aData) { - if (_previewTimer) { - _previewTimer.cancel(); - _previewTimer = null; - } - - if (aData) { - let usedThemes = _usedThemesExceptId(aData.id); - usedThemes.unshift(aData); - _updateUsedThemes(usedThemes); - if (PERSIST_ENABLED) - _persistImages(aData); - } - - _prefs.setBoolPref("isThemeSelected", aData != null); - _notifyWindows(aData); - _observerService.notifyObservers(null, "lightweight-theme-changed", null); - }, - - /** - * Starts the Addons provider and enables the new lightweight theme if - * necessary. - */ - startup: function() { - if (Services.prefs.prefHasUserValue(PREF_LWTHEME_TO_SELECT)) { - let id = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT); - if (id) - this.themeChanged(this.getUsedTheme(id)); - else - this.themeChanged(null); - Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT); - } - }, - - /** - * Called when a new add-on has been enabled when only one add-on of that type - * can be enabled. - * - * @param aId - * The ID of the newly enabled add-on - * @param aType - * The type of the newly enabled add-on - * @param aPendingRestart - * true if the newly enabled add-on will only become enabled after a - * restart - */ - addonChanged: function(aId, aType, aPendingRestart) { - if (aType != ADDON_TYPE) - return; - - let id = _getInternalID(aId); - let current = this.currentTheme; - - try { - let next = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT); - if (id == next && aPendingRestart) - return; - - Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT); - if (next) { - AddonManagerPrivate.callAddonListeners("onOperationCancelled", - new AddonWrapper(this.getUsedTheme(next))); - } - else { - if (id == current.id) { - AddonManagerPrivate.callAddonListeners("onOperationCancelled", - new AddonWrapper(current)); - return; - } - } - } - catch (e) { - } - - if (current) { - if (current.id == id) - return; - let wrapper = new AddonWrapper(current); - if (aPendingRestart) { - Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, ""); - AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, true); - } - else { - AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, false); - this.themeChanged(null); - AddonManagerPrivate.callAddonListeners("onDisabled", wrapper); - } - } - - if (id) { - let theme = this.getUsedTheme(id); - let wrapper = new AddonWrapper(theme, true); - if (aPendingRestart) { - AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, true); - Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, id); - } - else { - AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, false); - this.themeChanged(theme); - AddonManagerPrivate.callAddonListeners("onEnabled", wrapper); - } - } - }, - - /** - * Called to get an Addon with a particular ID. - * - * @param aId - * The ID of the add-on to retrieve - * @param aCallback - * A callback to pass the Addon to - */ - getAddonByID: function(aId, aCallback) { - let id = _getInternalID(aId); - if (!id) { - aCallback(null); - return; - } - - let theme = this.getUsedTheme(id); - if (!theme) { - aCallback(null); - return; - } - - aCallback(new AddonWrapper(theme)); - }, - - /** - * Called to get Addons of a particular type. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types. - * @param aCallback - * A callback to pass an array of Addons to - */ - getAddonsByTypes: function(aTypes, aCallback) { - if (aTypes && aTypes.indexOf(ADDON_TYPE) == -1) { - aCallback([]); - return; - } - - aCallback([new AddonWrapper(a) for each (a in this.usedThemes)]); - }, -}; - -/** - * The AddonWrapper wraps lightweight theme to provide the data visible to - * consumers of the AddonManager API. - */ -function AddonWrapper(aTheme, aBeingEnabled) { - this.__defineGetter__("id", function() aTheme.id + ID_SUFFIX); - this.__defineGetter__("type", function() ADDON_TYPE); - this.__defineGetter__("isActive", function() { - let current = LightweightThemeManager.currentTheme; - if (current) - return aTheme.id == current.id; - return false; - }); - - ["name", "version", "description", "homepageURL", "iconURL"].forEach(function(prop) { - this.__defineGetter__(prop, function() aTheme[prop]); - }, this); - this.__defineGetter__("creator", function() aTheme.author); - this.__defineGetter__("screenshots", function() [aTheme.previewURL]); - - this.__defineGetter__("pendingOperations", function() { - let pending = AddonManager.PENDING_NONE; - if (this.isActive == this.userDisabled) - pending |= this.isActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE; - return pending; - }); - - this.__defineGetter__("permissions", function() { - let permissions = AddonManager.PERM_CAN_UNINSTALL; - if (this.userDisabled) - permissions |= AddonManager.PERM_CAN_ENABLE; - return permissions; - }); - - this.__defineGetter__("userDisabled", function() { - if (aBeingEnabled) - return false; - - try { - let toSelect = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT); - return aTheme.id != toSelect; - } - catch (e) { - let current = LightweightThemeManager.currentTheme; - return !current || current.id != aTheme.id; - } - }); - - this.__defineSetter__("userDisabled", function(val) { - if (val == this.userDisabled) - return val; - - if (val) - throw new Error("Cannot disable the active theme"); - - LightweightThemeManager.currentTheme = aTheme; - return val; - }); - - this.uninstall = function() { - LightweightThemeManager.forgetUsedTheme(aTheme.id); - }; - - this.cancelUninstall = function() { - throw new Error("Theme is not marked to be uninstalled"); - }; - - this.findUpdates = function(listener, reason, appVersion, platformVersion) { - if ("onNoCompatibilityUpdateAvailable" in listener) - listener.onNoCompatibilityUpdateAvailable(this); - if ("onNoUpdateAvailable" in listener) - listener.onNoUpdateAvailable(this); - if ("onUpdateFinished" in listener) - listener.onUpdateFinished(this); - }; -} - -AddonWrapper.prototype = { - // Lightweight themes are never disabled by the application - get appDisabled() { - return false; - }, - - // Lightweight themes are always compatible - get isCompatible() { - return true; - }, - - get scope() { - return AddonManager.SCOPE_PROFILE; - }, - - // Lightweight themes are always compatible - isCompatibleWith: function(appVersion, platformVersion) { - return true; - }, - - // Lightweight themes are always securely updated - get providesUpdatesSecurely() { - return true; - }, - - // Lightweight themes are never blocklisted - get blocklistState() { - return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; } }; -/** - * Converts the ID used by the public AddonManager API to an lightweight theme - * ID. - * - * @param id - * The ID to be converted - * - * @return the lightweight theme ID or null if the ID was not for a lightweight - * theme. - */ -function _getInternalID(id) { - if (!id) - return null; - let len = id.length - ID_SUFFIX.length; - if (len > 0 && id.substring(len) == ID_SUFFIX) - return id.substring(0, len); - return null; -} - function _setCurrentTheme(aData, aLocal) { aData = _sanitizeTheme(aData, null, aLocal); - let needsRestart = (ADDON_TYPE == "theme") && - Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN); - let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool); cancel.data = false; _observerService.notifyObservers(cancel, "lightweight-theme-change-requested", JSON.stringify(aData)); if (aData) { - let theme = LightweightThemeManager.getUsedTheme(aData.id); - let isInstall = !theme || theme.version != aData.version; - if (isInstall) { - var oldWrapper = theme ? new AddonWrapper(theme) : null; - var wrapper = new AddonWrapper(aData); - AddonManagerPrivate.callInstallListeners("onExternalInstall", null, - wrapper, oldWrapper, false); - AddonManagerPrivate.callAddonListeners("onInstalling", wrapper, false); - } - - let current = LightweightThemeManager.currentTheme; let usedThemes = _usedThemesExceptId(aData.id); - if (current && current.id != aData.id) + if (cancel.data && _prefs.getBoolPref("isThemeSelected")) usedThemes.splice(1, 0, aData); else usedThemes.unshift(aData); _updateUsedThemes(usedThemes); - - if (isInstall) - AddonManagerPrivate.callAddonListeners("onInstalled", wrapper); } if (cancel.data) return null; - AddonManagerPrivate.notifyAddonChanged(aData ? aData.id + ID_SUFFIX : null, - ADDON_TYPE, needsRestart); + if (_previewTimer) { + _previewTimer.cancel(); + _previewTimer = null; + } - return LightweightThemeManager.currentTheme; + _prefs.setBoolPref("isThemeSelected", aData != null); + _notifyWindows(aData); + _observerService.notifyObservers(null, "lightweight-theme-changed", null); + + if (PERSIST_ENABLED && aData) + _persistImages(aData); + + return aData; } function _sanitizeTheme(aData, aBaseURI, aLocal) { if (!aData || typeof aData != "object") return null; var resourceProtocols = ["http", "https"]; if (aLocal) @@ -605,23 +301,18 @@ function _usedThemesExceptId(aId) function _version(aThemeData) aThemeData.version || ""; function _makeURI(aURL, aBaseURI) _ioService.newURI(aURL, null, aBaseURI); function _updateUsedThemes(aList) { - // Send uninstall events for all themes that need to be removed. - while (aList.length > MAX_USED_THEMES_COUNT) { - let wrapper = new AddonWrapper(aList[aList.length - 1]); - AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false); - aList.pop(); - AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper); - } + if (aList.length > MAX_USED_THEMES_COUNT) + aList.length = MAX_USED_THEMES_COUNT; var str = Cc["@mozilla.org/supports-string;1"] .createInstance(Ci.nsISupportsString); str.data = JSON.stringify(aList); _prefs.setComplexValue("usedThemes", Ci.nsISupportsString, str); _observerService.notifyObservers(null, "lightweight-theme-list-changed", null); } @@ -697,10 +388,8 @@ function _persistProgressListener(succes successCallback(); return; } } catch (e) { } // failure } }; } - -AddonManagerPrivate.registerProvider(LightweightThemeManager);
--- a/toolkit/mozapps/extensions/Makefile.in +++ b/toolkit/mozapps/extensions/Makefile.in @@ -36,58 +36,30 @@ DEPTH = ../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -MODULE = extensions -LIBRARY_NAME = extensions -IS_COMPONENT = 1 -MODULE_NAME = AddonsModule -GRE_MODULE = 1 -LIBXUL_LIBRARY = 1 -EXPORT_LIBRARY = 1 +MODULE = extensions XPIDLSRCS = \ - amIInstallTrigger.idl \ - amIWebInstallListener.idl \ - amIWebInstaller.idl \ nsIAddonRepository.idl \ - $(NULL) - -CPPSRCS = \ - amInstallTrigger.cpp \ + nsIExtensionManager.idl \ $(NULL) EXTRA_PP_COMPONENTS = \ nsAddonRepository.js \ nsBlocklistService.js \ - addonManager.js \ - amContentHandler.js \ - amWebInstallListener.js \ - $(NULL) - -EXTRA_PP_JS_MODULES = \ - AddonManager.jsm \ - XPIProvider.jsm \ - PluginProvider.jsm \ - AddonUpdateChecker.jsm \ - AddonLogging.jsm \ + nsExtensionManager.js \ $(NULL) EXTRA_JS_MODULES = \ LightweightThemeManager.jsm \ $(NULL) ifdef ENABLE_TESTS DIRS += test endif -EXTRA_DSO_LDOPTS = \ - $(MOZ_JS_LIBS) \ - $(MOZ_UNICHARUTIL_LIBS) \ - $(MOZ_COMPONENT_LIBS) \ - $(NULL) - include $(topsrcdir)/config/rules.mk
deleted file mode 100644 --- a/toolkit/mozapps/extensions/PluginProvider.jsm +++ /dev/null @@ -1,268 +0,0 @@ -/* -# ***** 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 the Extension Manager. -# -# 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): -# Dave Townsend <dtownsend@oxymoronical.com> -# -# 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 Cc = Components.classes; -const Ci = Components.interfaces; - -var EXPORTED_SYMBOLS = []; - -Components.utils.import("resource://gre/modules/AddonManager.jsm"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -["LOG", "WARN", "ERROR"].forEach(function(aName) { - this.__defineGetter__(aName, function() { - Components.utils.import("resource://gre/modules/AddonLogging.jsm"); - - LogManager.getLogger("addons.plugins", this); - return this[aName]; - }); -}, this); - -var PluginProvider = { - // A dictionary mapping IDs to names and descriptions - plugins: null, - - /** - * Called to get an Addon with a particular ID. - * - * @param aId - * The ID of the add-on to retrieve - * @param aCallback - * A callback to pass the Addon to - */ - getAddonByID: function PL_getAddon(aId, aCallback) { - if (!this.plugins) - this.buildPluginList(); - - if (aId in this.plugins) { - let name = this.plugins[aId].name; - let description = this.plugins[aId].description; - - let tags = Cc["@mozilla.org/plugin/host;1"]. - getService(Ci.nsIPluginHost). - getPluginTags({}); - let selected = []; - tags.forEach(function(aTag) { - if (aTag.name == name && aTag.description == description) - selected.push(aTag); - }, this); - - aCallback(new PluginWrapper(aId, name, description, selected)); - } - else { - aCallback(null); - } - }, - - /** - * Called to get Addons of a particular type. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types. - * @param callback - * A callback to pass an array of Addons to - */ - getAddonsByTypes: function PL_getAddonsByTypes(aTypes, aCallback) { - if (aTypes && aTypes.indexOf("plugin") < 0) { - aCallback([]); - return; - } - - if (!this.plugins) - this.buildPluginList(); - - let results = []; - - for (let id in this.plugins) { - this.getAddonByID(id, function(aAddon) { - results.push(aAddon); - }); - } - - aCallback(results); - }, - - /** - * Called to get Addons that have pending operations. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types - * @param aCallback - * A callback to pass an array of Addons to - */ - getAddonsWithOperationsByTypes: function PL_getAddonsWithOperationsByTypes(aTypes, aCallback) { - aCallback([]); - }, - - /** - * Called to get the current AddonInstalls, optionally restricting by type. - * - * @param aTypes - * An array of types or null to get all types - * @param aCallback - * A callback to pass the array of AddonInstalls to - */ - getInstallsByTypes: function PL_getInstallsByTypes(aTypes, aCallback) { - aCallback([]); - }, - - buildPluginList: function PL_buildPluginList() { - let tags = Cc["@mozilla.org/plugin/host;1"]. - getService(Ci.nsIPluginHost). - getPluginTags({}); - - this.plugins = {}; - let seen = {}; - tags.forEach(function(aTag) { - if (!(aTag.name in seen)) - seen[aTag.name] = {}; - if (!(aTag.description in seen[aTag.name])) { - let id = Cc["@mozilla.org/uuid-generator;1"]. - getService(Ci.nsIUUIDGenerator). - generateUUID(); - this.plugins[id] = { - name: aTag.name, - description: aTag.description - }; - seen[aTag.name][aTag.description] = true; - } - }, this); - } -}; - -/** - * The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to - * public callers through the API. - */ -function PluginWrapper(aId, aName, aDescription, aTags) { - let safedesc = aDescription.replace(/<\/?[a-z][^>]*>/gi, " "); - let homepageURL = null; - if (/<A\s+HREF=[^>]*>/i.test(aDescription)) - homepageURL = /<A\s+HREF=["']?([^>"'\s]*)/i.exec(aDescription)[1]; - - this.__defineGetter__("id", function() aId); - this.__defineGetter__("type", function() "plugin"); - this.__defineGetter__("name", function() aName); - this.__defineGetter__("creator", function() ""); - this.__defineGetter__("description", function() safedesc); - this.__defineGetter__("version", function() aTags[0].version); - this.__defineGetter__("homepageURL", function() homepageURL); - - this.__defineGetter__("isActive", function() !aTags[0].blocklisted && !aTags[0].disabled); - this.__defineGetter__("appDisabled", function() aTags[0].blocklisted); - this.__defineGetter__("userDisabled", function() aTags[0].disabled); - this.__defineSetter__("userDisabled", function(aVal) { - if (aTags[0].disabled == aVal) - return; - - aTags.forEach(function(aTag) { - aTag.disabled = aVal; - }); - AddonManagerPrivate.callAddonListeners(aVal ? "onDisabling" : "onEnabling", this, false); - AddonManagerPrivate.callAddonListeners(aVal ? "onDisabled" : "onEnabled", this); - return aVal; - }); - - this.__defineGetter__("blocklistState", function() { - let bs = Cc["@mozilla.org/extensions/blocklist;1"]. - getService(Ci.nsIBlocklistService); - return bs.getPluginBlocklistState(aTags[0]); - }); - - this.__defineGetter__("scope", function() { - let path = aTags[0].fullpath; - // Plugins inside the application directory are in the application scope - let dir = Services.dirsvc.get("APlugns", Ci.nsILocalFile); - if (path.substring(0, dir.path.length) == dir.path) - return AddonManager.SCOPE_APPLICATION; - - // Plugins inside the profile directory are in the profile scope - dir = Services.dirsvc.get("ProfD", Ci.nsILocalFile); - if (path.substring(0, dir.path.length) == dir.path) - return AddonManager.SCOPE_PROFILE; - - // Plugins anywhere else in the user's home are in the user scope - dir = Services.dirsvc.get("Home", Ci.nsILocalFile); - if (path.substring(0, dir.path.length) == dir.path) - return AddonManager.SCOPE_USER; - - // Any other locations are system scope - return AddonManager.SCOPE_SYSTEM; - }); - - this.__defineGetter__("pendingOperations", function() { - return 0; - }); - - this.__defineGetter__("permissions", function() { - let permissions = 0; - if (!this.appDisabled) { - if (this.userDisabled) - permissions |= AddonManager.PERM_CAN_ENABLE; - else - permissions |= AddonManager.PERM_CAN_DISABLE; - } - return permissions; - }); -} - -PluginWrapper.prototype = { - get isCompatible() { - return true; - }, - - get providesUpdatesSecurely() { - return true; - }, - - isCompatibleWith: function(aAppVerison, aPlatformVersion) { - return true; - }, - - findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) { - if ("onNoCompatibilityUpdateAvailable" in aListener) - aListener.onNoCompatibilityUpdateAvailable(this); - if ("onNoUpdateAvailable" in aListener) - aListener.onNoUpdateAvailable(this); - if ("onUpdateFinished" in aListener) - aListener.onUpdateFinished(this); - } -}; - -AddonManagerPrivate.registerProvider(PluginProvider);
deleted file mode 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ /dev/null @@ -1,4953 +0,0 @@ -/* -# ***** 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 the Extension Manager. -# -# The Initial Developer of the Original Code is -# the Mozilla Foundation. -# Portions created by the Initial Developer are Copyright (C) 2009 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Dave Townsend <dtownsend@oxymoronical.com> -# -# 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 Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -var EXPORTED_SYMBOLS = []; - -Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/AddonManager.jsm"); -Components.utils.import("resource://gre/modules/FileUtils.jsm"); -Components.utils.import("resource://gre/modules/NetUtil.jsm"); - -const PREF_DB_SCHEMA = "extensions.databaseSchema"; -const PREF_INSTALL_CACHE = "extensions.installCache"; -const PREF_BOOTSTRAP_ADDONS = "extensions.bootstrappedAddons"; -const PREF_PENDING_OPERATIONS = "extensions.pendingOperations"; -const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; -const PREF_SELECTED_LOCALE = "general.useragent.locale"; -const PREF_EM_DSS_ENABLED = "extensions.dss.enabled"; -const PREF_DSS_SWITCHPENDING = "extensions.dss.switchPending"; -const PREF_DSS_SKIN_TO_SELECT = "extensions.lastSelectedSkin"; -const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin"; -const PREF_EM_CHECK_COMPATIBILITY = "extensions.checkCompatibility"; -const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity"; -const PREF_EM_UPDATE_URL = "extensions.update.url"; -const PREF_EM_ENABLED_ADDONS = "extensions.enabledAddons"; -const PREF_EM_EXTENSION_FORMAT = "extensions."; -const PREF_EM_ENABLED_SCOPES = "extensions.enabledScopes"; -const PREF_XPI_ENABLED = "xpinstall.enabled"; -const PREF_XPI_WHITELIST_REQUIRED = "xpinstall.whitelist.required"; -const PREF_XPI_WHITELIST_PERMISSIONS = "xpinstall.whitelist.add"; -const PREF_XPI_BLACKLIST_PERMISSIONS = "xpinstall.blacklist.add"; - -const DIR_EXTENSIONS = "extensions"; -const DIR_STAGE = "staged"; - -const FILE_OLD_DATABASE = "extensions.rdf"; -const FILE_DATABASE = "extensions.sqlite"; -const FILE_INSTALL_MANIFEST = "install.rdf"; -const FILE_XPI_ADDONS_LIST = "extensions.ini"; - -const KEY_PROFILEDIR = "ProfD"; -const KEY_APPDIR = "XCurProcD"; - -const KEY_APP_PROFILE = "app-profile"; -const KEY_APP_GLOBAL = "app-global"; -const KEY_APP_SYSTEM_LOCAL = "app-system-local"; -const KEY_APP_SYSTEM_SHARE = "app-system-share"; -const KEY_APP_SYSTEM_USER = "app-system-user"; - -const CATEGORY_UPDATE_PARAMS = "extension-update-params"; - -const UNKNOWN_XPCOM_ABI = "unknownABI"; -const XPI_PERMISSION = "install"; - -const PREFIX_ITEM_URI = "urn:mozilla:item:"; -const RDFURI_ITEM_ROOT = "urn:mozilla:item:root" -const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest"; -const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#"; - -const TOOLKIT_ID = "toolkit@mozilla.org"; - -const BRANCH_REGEXP = /^([^\.]+\.[0-9]+[a-z]*).*/gi; - -const DB_SCHEMA = 1; -const REQ_VERSION = 2; - -const PROP_METADATA = ["id", "version", "type", "internalName", "updateURL", - "updateKey", "optionsURL", "aboutURL", "iconURL"] -const PROP_LOCALE_SINGLE = ["name", "description", "creator", "homepageURL"]; -const PROP_LOCALE_MULTI = ["developers", "translators", "contributors"]; -const PROP_TARGETAPP = ["id", "minVersion", "maxVersion"]; - -const BOOTSTRAP_REASONS = { - APP_STARTUP : 1, - APP_SHUTDOWN : 2, - ADDON_ENABLE : 3, - ADDON_DISABLE : 4, - ADDON_INSTALL : 5, - ADDON_UNINSTALL : 6, - ADDON_UPGRADE : 7, - ADDON_DOWNGRADE : 8 -}; - -// Map new string type identifiers to old style nsIUpdateItem types -const TYPES = { - extension: 2, - theme: 4, - locale: 8 -}; - -/** - * Valid IDs fit this pattern. - */ -var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i; - -["LOG", "WARN", "ERROR"].forEach(function(aName) { - this.__defineGetter__(aName, function() { - Components.utils.import("resource://gre/modules/AddonLogging.jsm"); - - LogManager.getLogger("addons.xpi", this); - return this[aName]; - }) -}, this); - -/** - * Gets the currently selected locale for display. - * @return the selected locale or "en-US" if none is selected - */ -function getLocale() { - if (Prefs.getBoolPref(PREF_MATCH_OS_LOCALE), false) - return Services.locale.getLocaleComponentForUserAgent(); - return Prefs.getCharPref(PREF_SELECTED_LOCALE, "en-US"); -} - -/** - * Selects the closest matching locale from a list of locales. - * - * @param aLocales - * An array of locales - * @return the best match for the currently selected locale - */ -function findClosestLocale(aLocales) { - let appLocale = getLocale(); - - // Holds the best matching localized resource - var bestmatch = null; - // The number of locale parts it matched with - var bestmatchcount = 0; - // The number of locale parts in the match - var bestpartcount = 0; - - var matchLocales = [appLocale.toLowerCase()]; - /* If the current locale is English then it will find a match if there is - a valid match for en-US so no point searching that locale too. */ - if (matchLocales[0].substring(0, 3) != "en-") - matchLocales.push("en-us"); - - for each (var locale in matchLocales) { - var lparts = locale.split("-"); - for each (var localized in aLocales) { - for each (found in localized.locales) { - found = found.toLowerCase(); - // Exact match is returned immediately - if (locale == found) - return localized; - - var fparts = found.split("-"); - /* If we have found a possible match and this one isn't any longer - then we dont need to check further. */ - if (bestmatch && fparts.length < bestmatchcount) - continue; - - // Count the number of parts that match - var maxmatchcount = Math.min(fparts.length, lparts.length); - var matchcount = 0; - while (matchcount < maxmatchcount && - fparts[matchcount] == lparts[matchcount]) - matchcount++; - - /* If we matched more than the last best match or matched the same and - this locale is less specific than the last best match. */ - if (matchcount > bestmatchcount || - (matchcount == bestmatchcount && fparts.length < bestpartcount)) { - bestmatch = localized; - bestmatchcount = matchcount; - bestpartcount = fparts.length; - } - } - } - // If we found a valid match for this locale return it - if (bestmatch) - return bestmatch; - } - return null; -} - -/** - * Calculates whether an add-on should be appDisabled or not. - * - * @param aAddon - * The add-on to check - * @return true if the add-on should not be appDisabled - */ -function isUsableAddon(aAddon) { - // Hack to ensure the default theme is always usable - if (aAddon.type == "theme" && aAddon.internalName == XPIProvider.defaultSkin) - return true; - if (XPIProvider.checkCompatibility) { - if (!aAddon.isCompatible) - return false; - } - else { - if (!aAddon.matchingTargetApplication) - return false; - } - if (XPIProvider.checkUpdateSecurity && !aAddon.providesUpdatesSecurely) - return false; - return aAddon.blocklistState != Ci.nsIBlocklistService.STATE_BLOCKED; -} - -this.__defineGetter__("gRDF", function() { - delete this.gRDF; - return this.gRDF = Cc["@mozilla.org/rdf/rdf-service;1"]. - getService(Ci.nsIRDFService); -}); - -function EM_R(aProperty) { - return gRDF.GetResource(PREFIX_NS_EM + aProperty); -} - -/** - * Converts an RDF literal, resource or integer into a string. - * - * @param aLiteral - * The RDF object to convert - * @return a string if the object could be converted or null - */ -function getRDFValue(aLiteral) { - if (aLiteral instanceof Ci.nsIRDFLiteral) - return aLiteral.Value; - if (aLiteral instanceof Ci.nsIRDFResource) - return aLiteral.Value; - if (aLiteral instanceof Ci.nsIRDFInt) - return aLiteral.Value; - return null; -} - -/** - * Gets an RDF property as a string - * - * @param aDs - * The RDF datasource to read the property from - * @param aResource - * The RDF resource to read the property from - * @param aProperty - * The property to read - * @return a string if the property existed or null - */ -function getRDFProperty(aDs, aResource, aProperty) { - return getRDFValue(aDs.GetTarget(aResource, EM_R(aProperty), true)); -} - -/** - * Reads an AddonInternal object from an RDF stream. - * - * @param aUri - * The URI that the manifest is being read from - * @param aStream - * An open stream to read the RDF from - * @return an AddonInternal object - * @throws if the install manifest in the RDF stream is corrupt or could not - * be read - */ -function loadManifestFromRDF(aUri, aStream) { - function getPropertyArray(aDs, aSource, aProperty) { - let values = []; - let targets = aDs.GetTargets(aSource, EM_R(aProperty), true); - while (targets.hasMoreElements()) - values.push(getRDFValue(targets.getNext())); - - return values; - } - - function readLocale(aDs, aSource, isDefault) { - let locale = { }; - if (!isDefault) { - locale.locales = []; - let targets = ds.GetTargets(aSource, EM_R("locale"), true); - while (targets.hasMoreElements()) - locale.locales.push(getRDFValue(targets.getNext())); - - if (locale.locales.length == 0) - throw new Error("No locales given for localized properties"); - } - - PROP_LOCALE_SINGLE.forEach(function(aProp) { - locale[aProp] = getRDFProperty(aDs, aSource, aProp); - }); - - PROP_LOCALE_MULTI.forEach(function(aProp) { - locale[aProp] = getPropertyArray(aDs, aSource, - aProp.substring(0, aProp.length - 1)); - }); - - return locale; - } - - let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"]. - createInstance(Ci.nsIRDFXMLParser) - let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]. - createInstance(Ci.nsIRDFDataSource); - let listener = rdfParser.parseAsync(ds, aUri); - let channel = Cc["@mozilla.org/network/input-stream-channel;1"]. - createInstance(Ci.nsIInputStreamChannel); - channel.setURI(aUri); - channel.contentStream = aStream; - channel.QueryInterface(Ci.nsIChannel); - channel.contentType = "text/xml"; - - listener.onStartRequest(channel, null); - - try { - let pos = 0; - let count = aStream.available(); - while (count > 0) { - listener.onDataAvailable(channel, null, aStream, pos, count); - pos += count; - count = aStream.available(); - } - listener.onStopRequest(channel, null, Components.results.NS_OK); - } - catch (e) { - listener.onStopRequest(channel, null, e.result); - throw e; - } - - let root = gRDF.GetResource(RDFURI_INSTALL_MANIFEST_ROOT); - let addon = new AddonInternal(); - PROP_METADATA.forEach(function(aProp) { - addon[aProp] = getRDFProperty(ds, root, aProp); - }); - if (!addon.id || !addon.version) - throw new Error("No ID or version in install manifest"); - - if (!addon.type) { - addon.type = addon.internalName ? "theme" : "extension"; - } - else { - for (let name in TYPES) { - if (TYPES[name] == addon.type) { - addon.type = name; - break; - } - } - } - if (!(addon.type in TYPES)) - throw new Error("Install manifest specifies unknown type: " + addon.type); - if (addon.type == "theme" && !addon.internalName) - throw new Error("Themes must include an internalName property"); - - // Only extensions are allowed to provide an optionsURL or aboutURL. For all - // other types they are silently ignored - if (addon.type != "extension") { - addon.optionsURL = null; - addon.aboutURL = null; - } - - // Only read the bootstrapped property for extensions - if (addon.type == "extension") - addon.bootstrap = getRDFProperty(ds, root, "bootstrap") == "true"; - - addon.defaultLocale = readLocale(ds, root, true); - - addon.locales = []; - let targets = ds.GetTargets(root, EM_R("localized"), true); - while (targets.hasMoreElements()) { - let target = targets.getNext().QueryInterface(Ci.nsIRDFResource); - addon.locales.push(readLocale(ds, target, false)); - } - - addon.targetApplications = []; - targets = ds.GetTargets(root, EM_R("targetApplication"), true); - while (targets.hasMoreElements()) { - let target = targets.getNext().QueryInterface(Ci.nsIRDFResource); - let targetAppInfo = {}; - PROP_TARGETAPP.forEach(function(aProp) { - targetAppInfo[aProp] = getRDFProperty(ds, target, aProp); - }); - if (!targetAppInfo.id || !targetAppInfo.minVersion || - !targetAppInfo.maxVersion) - throw new Error("Invalid targetApplication entry in install manifest"); - addon.targetApplications.push(targetAppInfo); - } - - addon.targetPlatforms = getPropertyArray(ds, root, "targetPlatform"); - - // Themes are disabled by default unless they are currently selected - if (addon.type == "theme") - addon.userDisabled = addon.internalName != XPIProvider.selectedSkin; - else - addon.userDisabled = addon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED; - addon.appDisabled = !isUsableAddon(addon); - - addon.applyBackgroundUpdates = true; - - return addon; -} - -/** - * Loads an AddonInternal object from an add-on extracted in a directory. - * - * @param aDir - * The nsIFile directory holding the add-on - * @return an AddonInternal object - * @throws if the directory does not contain a valid install manifest - */ -function loadManifestFromDir(aDir) { - let file = aDir.clone(); - file.append(FILE_INSTALL_MANIFEST); - if (!file.exists() || !file.isFile()) - throw new Error("Directory " + dir.path + " does not contain a valid " + - "install manifest"); - - let fis = Cc["@mozilla.org/network/file-input-stream;1"]. - createInstance(Ci.nsIFileInputStream); - fis.init(file, -1, -1, false); - let bis = Cc["@mozilla.org/network/buffered-input-stream;1"]. - createInstance(Ci.nsIBufferedInputStream); - bis.init(fis, 4096); - - try { - let addon = loadManifestFromRDF(Services.io.newFileURI(file), bis); - addon._sourceBundle = aDir.clone().QueryInterface(Ci.nsILocalFile); - return addon; - } - finally { - bis.close(); - fis.close(); - } -} - -/** - * Creates a jar: URI for a file inside a ZIP file. - * - * @param aJarfile - * The ZIP file as an nsIFile - * @param aPath - * The path inside the ZIP file - * @return an nsIURI for the file - */ -function buildJarURI(aJarfile, aPath) { - let uri = Services.io.newFileURI(aJarfile); - uri = "jar:" + uri.spec + "!/" + aPath; - return NetUtil.newURI(uri); -} - -/** - * Extracts files from a ZIP file into a directory. - * - * @param aZipFile - * The source ZIP file that contains the add-on. - * @param aDir - * The nsIFile to extract to. - */ -function extractFiles(aZipFile, aDir) { - function getTargetFile(aDir, entry) { - let target = aDir.clone(); - entry.split("/").forEach(function(aPart) { - target.append(aPart); - }); - return target; - } - - let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]. - createInstance(Ci.nsIZipReader); - zipReader.open(aZipFile); - - try { - // create directories first - let entries = zipReader.findEntries("*/"); - while (entries.hasMore()) { - var entryName = entries.getNext(); - let target = getTargetFile(aDir, entryName); - if (!target.exists()) { - try { - target.create(Ci.nsILocalFile.DIRECTORY_TYPE, - FileUtils.PERMS_DIRECTORY); - } - catch (e) { - ERROR("extractFiles: failed to create target directory for " + - "extraction file = " + target.path + ", exception = " + e + - "\n"); - } - } - } - - entries = zipReader.findEntries(null); - while (entries.hasMore()) { - let entryName = entries.getNext(); - let target = getTargetFile(aDir, entryName); - if (target.exists()) - continue; - - zipReader.extract(entryName, target); - target.permissions |= FileUtils.PERMS_FILE; - } - } - finally { - zipReader.close(); - } -} - -/** - * Verifies that a zip file's contents are all signed by the same principal. - * Directory entries and anything in the META-INF directory are not checked. - * - * @param aZip - * A nsIZipReader to check - * @param aPrincipal - * The nsIPrincipal to compare against - * @return true if all the contents that should be signed were signed by the - * principal - */ -function verifyZipSigning(aZip, aPrincipal) { - var count = 0; - var entries = aZip.findEntries(null); - while (entries.hasMore()) { - var entry = entries.getNext(); - // Nothing in META-INF is in the manifest. - if (entry.substr(0, 9) == "META-INF/") - continue; - // Directory entries aren't in the manifest. - if (entry.substr(-1) == "/") - continue; - count++; - var entryPrincipal = aZip.getCertificatePrincipal(entry); - if (!entryPrincipal || !aPrincipal.equals(entryPrincipal)) - return false; - } - return aZip.manifestEntriesCount == count; -} - -/** - * Replaces %...% strings in an addon url (update and updateInfo) with - * appropriate values. - * - * @param aAddon - * The AddonInternal representing the add-on - * @param aUri - * The uri to escape - * @param aUpdateType - * An optional number representing the type of update, only applicable - * when creating a url for retrieving an update manifest - * @param aAppVersion - * The optional application version to use for %APP_VERSION% - * @return the appropriately escaped uri. - */ -function escapeAddonURI(aAddon, aUri, aUpdateType, aAppVersion) -{ - var addonStatus = aAddon.userDisabled ? "userDisabled" : "userEnabled"; - - if (!aAddon.isCompatible) - addonStatus += ",incompatible"; - if (aAddon.blocklistState > 0) - addonStatus += ",blocklisted"; - - try { - var xpcomABI = Services.appinfo.XPCOMABI; - } catch (ex) { - xpcomABI = UNKNOWN_XPCOM_ABI; - } - - let uri = aUri.replace(/%ITEM_ID%/g, aAddon.id); - uri = uri.replace(/%ITEM_VERSION%/g, aAddon.version); - uri = uri.replace(/%ITEM_STATUS%/g, addonStatus); - uri = uri.replace(/%APP_ID%/g, Services.appinfo.ID); - uri = uri.replace(/%APP_VERSION%/g, aAppVersion ? aAppVersion : - Services.appinfo.version); - uri = uri.replace(/%REQ_VERSION%/g, REQ_VERSION); - uri = uri.replace(/%APP_OS%/g, Services.appinfo.OS); - uri = uri.replace(/%APP_ABI%/g, xpcomABI); - uri = uri.replace(/%APP_LOCALE%/g, getLocale()); - uri = uri.replace(/%CURRENT_APP_VERSION%/g, Services.appinfo.version); - - // If there is an updateType then replace the UPDATE_TYPE string - if (aUpdateType) - uri = uri.replace(/%UPDATE_TYPE%/g, aUpdateType); - - // If this add-on has compatibility information for either the current - // application or toolkit then replace the ITEM_MAXAPPVERSION with the - // maxVersion - let app = aAddon.matchingTargetApplication; - if (app) - var maxVersion = app.maxVersion; - else - maxVersion = ""; - uri = uri.replace(/%ITEM_MAXAPPVERSION%/g, maxVersion); - - // Replace custom parameters (names of custom parameters must have at - // least 3 characters to prevent lookups for something like %D0%C8) - var catMan = null; - uri = uri.replace(/%(\w{3,})%/g, function(aMatch, aParam) { - if (!catMan) { - catMan = Cc["@mozilla.org/categorymanager;1"]. - getService(Ci.nsICategoryManager); - } - - try { - var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, aParam); - var paramHandler = Cc[contractID].getService(Ci.nsIPropertyBag2); - return paramHandler.getPropertyAsAString(aParam); - } - catch(e) { - return aMatch; - } - }); - - // escape() does not properly encode + symbols in any embedded FVF strings. - return uri.replace(/\+/g, "%2B"); -} - -/** - * Copies properties from one object to another. If no target object is passed - * a new object will be created and returned. - * - * @param aObject - * An object to copy from - * @param aProperties - * An array of properties to be copied - * @param aTarget - * An optional target object to copy the properties to - * @return the object that the properties were copied onto - */ -function copyProperties(aObject, aProperties, aTarget) { - if (!aTarget) - aTarget = {}; - aProperties.forEach(function(aProp) { - aTarget[aProp] = aObject[aProp]; - }); - return aTarget; -} - -/** - * Copies properties from a mozIStorageRow to an object. If no target object is - * passed a new object will be created and returned. - * - * @param aRow - * A mozIStorageRow to copy from - * @param aProperties - * An array of properties to be copied - * @param aTarget - * An optional target object to copy the properties to - * @return the object that the properties were copied onto - */ -function copyRowProperties(aRow, aProperties, aTarget) { - if (!aTarget) - aTarget = {}; - aProperties.forEach(function(aProp) { - aTarget[aProp] = aRow.getResultByName(aProp); - }); - return aTarget; -} - -/** - * A generator to synchronously return result rows from an mozIStorageStatement. - * - * @param aStatement - * The statement to execute - */ -function resultRows(aStatement) { - try { - while (aStatement.executeStep()) - yield aStatement.row; - } - finally { - aStatement.reset(); - } -} - -/** - * A helpful wrapper around the prefs service that allows for default values - * when requested values aren't set. - */ -var Prefs = { - /** - * Gets a preference from the default branch ignoring user-set values. - * - * @param aName - * The name of the preference - * @param aDefaultValue - * A value to return if the preference does not exist - * @return the default value of the preference or aDefaultValue if there is - * none - */ - getDefaultCharPref: function(aName, aDefaultValue) { - try { - return Services.prefs.getDefaultBranch("").getCharPref(aName); - } - catch (e) { - } - return aDefaultValue; - }, - - /** - * Gets a string preference. - * - * @param aName - * The name of the preference - * @param aDefaultValue - * A value to return if the preference does not exist - * @return the value of the preference or aDefaultValue if there is none - */ - getCharPref: function(aName, aDefaultValue) { - try { - return Services.prefs.getCharPref(aName); - } - catch (e) { - } - return aDefaultValue; - }, - - /** - * Gets a boolean preference. - * - * @param aName - * The name of the preference - * @param aDefaultValue - * A value to return if the preference does not exist - * @return the value of the preference or aDefaultValue if there is none - */ - getBoolPref: function(aName, aDefaultValue) { - try { - return Services.prefs.getBoolPref(aName); - } - catch (e) { - } - return aDefaultValue; - }, - - /** - * Gets an integer preference. - * - * @param aName - * The name of the preference - * @param defaultValue - * A value to return if the preference does not exist - * @return the value of the preference or defaultValue if there is none - */ - getIntPref: function(aName, defaultValue) { - try { - return Services.prefs.getIntPref(aName); - } - catch (e) { - } - return defaultValue; - } -} - -var XPIProvider = { - // An array of known install locations - installLocations: null, - // A dictionary of known install locations by name - installLocationsByName: null, - // An array of currently active AddonInstalls - installs: null, - // The default skin for the application - defaultSkin: "classic/1.0", - // The currently selected skin or the skin that will be switched to after a - // restart - selectedSkin: null, - // The name of the checkCompatibility preference for the current application - // version - checkCompatibilityPref: null, - // The value of the checkCompatibility preference - checkCompatibility: true, - // The value of the checkUpdateSecurity preference - checkUpdateSecurity: true, - // A dictionary of the file descriptors for bootstrappable add-ons by ID - bootstrappedAddons: {}, - // A dictionary of JS scopes of loaded bootstrappable add-ons by ID - bootstrapScopes: {}, - // A string listing the enabled add-ons for annotating crash reports - enabledAddons: null, - - /** - * Starts the XPI provider initializes the install locations and prefs. - */ - startup: function XPI_startup() { - LOG("startup"); - this.installs = []; - this.installLocations = []; - this.installLocationsByName = {}; - - function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) { - try { - var dir = FileUtils.getDir(aKey, aPaths); - } - catch (e) { - // Some directories aren't defined on some platforms, ignore them - LOG("Skipping unavailable install location " + aName); - return; - } - - try { - var location = new DirectoryInstallLocation(aName, dir, aScope, aLocked); - } - catch (e) { - WARN("Failed to add directory install location " + aName + " " + e); - return; - } - - XPIProvider.installLocations.push(location); - XPIProvider.installLocationsByName[location.name] = location; - } - - function addRegistryInstallLocation(aName, aRootkey, aScope) { - try { - var location = new WinRegInstallLocation(aName, aRootkey, aScope); - } - catch (e) { - WARN("Failed to add registry install location " + aName + " " + e); - return; - } - - XPIProvider.installLocations.push(location); - XPIProvider.installLocationsByName[location.name] = location; - } - - let hasRegistry = ("nsIWindowsRegKey" in Ci); - - let enabledScopes = Prefs.getIntPref(PREF_EM_ENABLED_SCOPES, - AddonManager.SCOPE_ALL); - - // These must be in order of priority for processFileChanges etc. to work - if (enabledScopes & AddonManager.SCOPE_SYSTEM) { - if (hasRegistry) { - addRegistryInstallLocation("winreg-app-global", - Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE, - AddonManager.SCOPE_SYSTEM); - } - addDirectoryInstallLocation(KEY_APP_SYSTEM_LOCAL, "XRESysLExtPD", - [Services.appinfo.ID], - AddonManager.SCOPE_SYSTEM, true); - addDirectoryInstallLocation(KEY_APP_SYSTEM_SHARE, "XRESysSExtPD", - [Services.appinfo.ID], - AddonManager.SCOPE_SYSTEM, true); - } - - if (enabledScopes & AddonManager.SCOPE_APPLICATION) { - addDirectoryInstallLocation(KEY_APP_GLOBAL, KEY_APPDIR, - [DIR_EXTENSIONS], - AddonManager.SCOPE_APPLICATION, true); - } - - if (enabledScopes & AddonManager.SCOPE_USER) { - if (hasRegistry) { - addRegistryInstallLocation("winreg-app-user", - Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER, - AddonManager.SCOPE_USER); - } - addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt", - [Services.appinfo.ID], - AddonManager.SCOPE_USER, true); - } - - // The profile location is always enabled - addDirectoryInstallLocation(KEY_APP_PROFILE, KEY_PROFILEDIR, - [DIR_EXTENSIONS], - AddonManager.SCOPE_PROFILE, false); - - this.defaultSkin = Prefs.getDefaultCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, - "classic/1.0"); - this.selectedSkin = Prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, - this.defaultSkin); - - // Tell the Chrome Registry which Skin to select - if (Prefs.getBoolPref(PREF_DSS_SWITCHPENDING, false)) { - try { - this.selectedSkin = Prefs.getCharPref(PREF_DSS_SKIN_TO_SELECT); - Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, - this.selectedSkin); - Services.prefs.clearUserPref(PREF_DSS_SKIN_TO_SELECT); - LOG("Changed skin to " + this.selectedSkin); - } - catch (e) { - ERROR(e); - } - Services.prefs.clearUserPref(PREF_DSS_SWITCHPENDING); - } - - var version = Services.appinfo.version.replace(BRANCH_REGEXP, "$1"); - this.checkCompatibilityPref = PREF_EM_CHECK_COMPATIBILITY + "." + version; - this.checkCompatibility = Prefs.getBoolPref(this.checkCompatibilityPref, - true) - this.checkUpdateSecurity = Prefs.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, - true) - this.enabledAddons = Prefs.getCharPref(PREF_EM_ENABLED_ADDONS, ""); - - if ("nsICrashReporter" in Ci && - Services.appinfo instanceof Ci.nsICrashReporter) { - // Annotate the crash report with relevant add-on information. - try { - Services.appinfo.annotateCrashReport("Theme", this.selectedSkin); - } catch (e) { } - try { - Services.appinfo.annotateCrashReport("EMCheckCompatibility", - this.checkCompatibility); - } catch (e) { } - this.addAddonsToCrashReporter(); - } - - Services.prefs.addObserver(this.checkCompatibilityPref, this, false); - Services.prefs.addObserver(PREF_EM_CHECK_UPDATE_SECURITY, this, false); - }, - - /** - * Shuts down the database and releases all references. - */ - shutdown: function XPI_shutdown() { - LOG("shutdown"); - - Services.prefs.removeObserver(this.checkCompatibilityPref, this); - Services.prefs.removeObserver(PREF_EM_CHECK_UPDATE_SECURITY, this); - - if (Prefs.getBoolPref(PREF_PENDING_OPERATIONS, false)) { - XPIDatabase.updateActiveAddons(); - Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false); - } - XPIDatabase.shutdown(); - this.installs = null; - - this.installLocations = null; - this.installLocationsByName = null; - }, - - /** - * Adds a list of currently active add-ons to the next crash report. - */ - addAddonsToCrashReporter: function XPI_addAddonsToCrashReporter() { - if (!("nsICrashReporter" in Ci) || - !(Services.appinfo instanceof Ci.nsICrashReporter)) - return; - - let data = this.enabledAddons; - for (let id in this.bootstrappedAddons) - data += (data ? "," : "") + id + ":" + this.bootstrappedAddons[id].version; - - try { - Services.appinfo.annotateCrashReport("Add-ons", data); - } - catch (e) { } - }, - - /** - * Gets the add-on states for an install location. - * - * @param location - * The install location to retrieve the add-on states for - * @return a dictionary mapping add-on IDs to objects with a descriptor - * property which contains the add-ons directory descriptor and an - * mtime property which contains the add-on's last modified time as - * the number of milliseconds since the epoch. - */ - getAddonStates: function XPI_getAddonStates(aLocation) { - let addonStates = {}; - aLocation.addonLocations.forEach(function(dir) { - let id = aLocation.getIDForLocation(dir); - addonStates[id] = { - descriptor: dir.persistentDescriptor, - mtime: dir.lastModifiedTime - }; - }); - - return addonStates; - }, - - /** - * Gets an array of install location states which uniquely describes all - * installed add-ons with the add-on's InstallLocation name and last modified - * time. - * - * @return an array of add-on states for each install location. Each state - * is an object with a name property holding the location's name and - * an addons property holding the add-on states for the location - */ - getInstallLocationStates: function XPI_getInstallLocationStates() { - let states = []; - this.installLocations.forEach(function(aLocation) { - let addons = aLocation.addonLocations; - if (addons.length == 0) - return; - - let locationState = { - name: aLocation.name, - addons: this.getAddonStates(aLocation) - }; - - states.push(locationState); - }, this); - return states; - }, - - /** - * Check the staging directories of install locations for any add-ons to be - * installed or add-ons to be uninstalled. - * - * @param aManifests - * A dictionary to add detected install manifests to for the purpose - * of passing through updated compatibility information - * @return true if an add-on was installed or uninstalled - */ - processPendingFileChanges: function XPI_processPendingFileChanges(aManifests) { - // TODO maybe this should be passed off to the install locations to handle? - let changed = false; - this.installLocations.forEach(function(aLocation) { - aManifests[aLocation.name] = {}; - // We can't install or uninstall anything in locked locations - if (aLocation.locked) - return; - - let stagingDir = aLocation.getStagingDir(); - if (!stagingDir || !stagingDir.exists()) - return; - - let entries = stagingDir.directoryEntries; - while (entries.hasMoreElements()) { - let stageDirEntry = entries.getNext().QueryInterface(Ci.nsILocalFile); - - // Only directories are important. Files may be updated manifests. - if (!stageDirEntry.isDirectory()) { - WARN("Ignoring file: " + stageDirEntry.path); - continue; - } - - // Check that the directory's name is a valid ID. - let id = stageDirEntry.leafName; - if (!gIDTest.test(id)) { - WARN("Ignoring directory whose name is not a valid add-on ID: " + - stageDirEntry.path); - continue; - } - - // Check if the directory contains an install manifest. - let manifest = stageDirEntry.clone(); - manifest.append(FILE_INSTALL_MANIFEST); - - // If the install manifest doesn't exist uninstall this add-on in this - // install location. - if (!manifest.exists()) { - LOG("Processing uninstall of " + id + " in " + aLocation.name); - aLocation.uninstallAddon(id); - // The file check later will spot the removal and cleanup the database - changed = true; - continue; - } - - LOG("Processing install of " + id + " in " + aLocation.name); - try { - var addonInstallDir = aLocation.installAddon(id, stageDirEntry); - } - catch (e) { - ERROR("Failed to install staged add-on " + id + " in " + aLocation.name + - ": " + e); - continue; - } - - aManifests[aLocation.name][id] = null; - changed = true; - - // Check for a cached AddonInternal for this add-on, it may contain - // updated compatibility information - let jsonfile = stagingDir.clone(); - jsonfile.append(id + ".json"); - if (jsonfile.exists()) { - LOG("Found updated manifest for " + id + " in " + aLocation.name); - let fis = Cc["@mozilla.org/network/file-input-stream;1"]. - createInstance(Ci.nsIFileInputStream); - let json = Cc["@mozilla.org/dom/json;1"]. - createInstance(Ci.nsIJSON); - - try { - fis.init(jsonfile, -1, 0, 0); - aManifests[aLocation.name][id] = json.decodeFromStream(fis, - jsonfile.fileSize); - aManifests[aLocation.name][id]._sourceBundle = addonInstallDir; - } - catch (e) { - ERROR("Unable to read add-on manifest for " + id + " in " + - aLocation.name + ": " + e); - } - finally { - fis.close(); - } - } - } - - try { - stagingDir.remove(true); - } - catch (e) { - // Non-critical, just saves some perf on startup if we clean this up. - LOG("Error removing staging dir " + stagingDir.path + ": " + e); - } - }); - return changed; - }, - - /** - * Compares the add-ons that are currently installed to those that were - * known to be installed when the application last ran and applies any - * changes found to the database. - * - * @param aState - * The array of current install location states - * @param aManifests - * A dictionary of cached AddonInstalls for add-ons that have been - * installed - * @param aUpdateCompatibility - * true to update add-ons appDisabled property when the application - * version has changed - * @return true if a change requiring a restart was detected - */ - processFileChanges: function XPI_processFileChanges(aState, aManifests, - aUpdateCompatibility, - aMigrateData) { - let visibleAddons = {}; - - /** - * Updates an add-on's metadata and determines if a restart of the - * application is necessary. This is called when either the add-on's - * install directory path or last modified time has changed. - * - * @param aInstallLocation - * The install location containing the add-on - * @param aOldAddon - * The AddonInternal as it appeared the last time the application - * ran - * @param aAddonState - * The new state of the add-on - * @return true if restarting the application is required to complete - * changing this add-on - */ - function updateMetadata(aInstallLocation, aOldAddon, aAddonState) { - LOG("Add-on " + aOldAddon.id + " modified in " + aInstallLocation.name); - - // Check if there is an updated install manifest for this add-on - let newAddon = aManifests[aInstallLocation.name][aOldAddon.id]; - - try { - // If not load it from the directory - if (!newAddon) { - let dir = aInstallLocation.getLocationForID(aOldAddon.id); - newAddon = loadManifestFromDir(dir); - } - - // The ID in the manifest that was loaded must match the ID of the old - // add-on. - if (newAddon.id != aOldAddon.id) - throw new Error("Incorrect id in install manifest"); - } - catch (e) { - WARN("Add-on is invalid: " + e); - XPIDatabase.removeAddonMetadata(aOldAddon); - if (!aInstallLocation.locked) - aInstallLocation.uninstallAddon(aOldAddon.id); - else - WARN("Could not uninstall invalid item from locked install location"); - // If this was an active add-on then we must force a restart - if (aOldAddon.active) { - if (aOldAddon.bootstrap) - delete XPIProvider.bootstrappedAddons[aOldAddon.id]; - else - return true; - } - - return false; - } - - // Set the additional properties on the new AddonInternal - newAddon._installLocation = aInstallLocation; - newAddon.updateDate = aAddonState.mtime; - newAddon.visible = !(newAddon.id in visibleAddons); - - // Update the database - XPIDatabase.updateAddonMetadata(aOldAddon, newAddon, aAddonState.descriptor); - if (newAddon.visible) { - visibleAddons[newAddon.id] = newAddon; - // If the old version was active and wasn't bootstrapped or the new - // version will be active and isn't bootstrapped then we must force a - // restart - if ((aOldAddon.active && !aOldAddon.bootstrap) || - (newAddon.active && !newAddon.bootstrap)) { - return true; - } - } - - return false; - } - - /** - * Called when no change has been detected for an add-on's metadata. The - * add-on may have become visible due to other add-ons being removed or - * the add-on may need to be updated when the application version has - * changed. - * - * @param aInstallLocation - * The install location containing the add-on - * @param aOldAddon - * The AddonInternal as it appeared the last time the application - * ran - * @param aAddonState - * The new state of the add-on - * @return a boolean indicating if restarting the application is required - * to complete changing this add-on - */ - function updateVisibilityAndCompatibility(aInstallLocation, aOldAddon, - aAddonState) { - let changed = false; - - // This add-ons metadata has not changed but it may have become visible - if (!(aOldAddon.id in visibleAddons)) { - visibleAddons[aOldAddon.id] = aOldAddon; - - if (!aOldAddon.visible) { - XPIDatabase.makeAddonVisible(aOldAddon); - - // If the add-on is bootstrappable and it should be active then - // mark it as active and add it to the list to be activated. - if (aOldAddon.bootstrap && !aOldAddon.appDisabled && - !aOldAddon.userDisabled) { - aOldAddon.active = true; - XPIDatabase.updateAddonActive(aOldAddon); - XPIProvider.bootstrappedAddons[aOldAddon.id] = { - version: aOldAddon.version, - descriptor: aAddonState.descriptor - }; - } - else { - // Otherwise a restart is necessary - changed = true; - } - } - } - - // App version changed, we may need to update the appDisabled property. - if (aUpdateCompatibility) { - let appDisabled = !isUsableAddon(aOldAddon); - - // If the property has changed update the database. - if (appDisabled != aOldAddon.appDisabled) { - LOG("Add-on " + aOldAddon.id + " changed appDisabled state to " + - appDisabled); - XPIDatabase.setAddonProperties(aOldAddon, { - appDisabled: appDisabled - }); - - // If this is a visible add-on and it isn't userDisabled then we - // may need a restart or to update the bootstrap list. - if (aOldAddon.visible && !aOldAddon.userDisabled) { - if (aOldAddon.bootstrap) { - // When visible and not userDisabled, active is the opposite of - // appDisabled. - aOldAddon.active = !aOldAddon.appDisabled; - XPIDatabase.updateAddonActive(aOldAddon); - if (aOldAddon.active) { - XPIProvider.bootstrappedAddons[aOldAddon.id] = { - version: aOldAddon.version, - descriptor: aAddonState.descriptor - }; - } - else { - delete XPIProvider.bootstrappedAddons[aOldAddon.id]; - } - } - else { - changed = true; - } - } - } - } - - return changed; - } - - /** - * Called when an add-on has been removed. - * - * @param aInstallLocation - * The install location containing the add-on - * @param aOldAddon - * The AddonInternal as it appeared the last time the application - * ran - * @return a boolean indicating if restarting the application is required - * to complete changing this add-on - */ - function removeMetadata(aInstallLocation, aOldAddon) { - // This add-on has disappeared - LOG("Add-on " + aOldAddon.id + " removed from " + aInstallLocation.name); - XPIDatabase.removeAddonMetadata(aOldAddon); - if (aOldAddon.active) { - - // Enable the default theme if the previously active theme has been - // removed - if (aOldAddon.type == "theme") - XPIProvider.enableDefaultTheme(); - - // If this was an active add-on and bootstrapped we must remove it from - // the bootstrapped list, otherwise we need to force a restart. - if (!aOldAddon.bootstrap) - return true; - - XPIProvider.unloadBootstrapScope(aOldAddon.id); - } - - return false; - } - - /** - * Called when a new add-on has been detected. - * - * @param aInstallLocation - * The install location containing the add-on - * @param aId - * The ID of the add-on - * @param aAddonState - * The new state of the add-on - * @param aMigrateData - * If during startup the database had to be upgraded this will - * contain data that used to be held about this add-on - * @return a boolean indicating if restarting the application is required - * to complete changing this add-on - */ - function addMetadata(aInstallLocation, aId, aAddonState, aMigrateData) { - LOG("New add-on " + aId + " installed in " + aInstallLocation.name); - - // Check the updated manifests lists for a manifest for this add-on - let newAddon = aManifests[aInstallLocation.name][aId]; - - try { - // Otherwise load the manifest from the add-on's directory. - if (!newAddon) - newAddon = loadManifestFromDir(aInstallLocation.getLocationForID(aId)); - // The add-on in the manifest should match the add-on ID. - if (newAddon.id != aId) - throw new Error("Incorrect id in install manifest"); - } - catch (e) { - WARN("Add-on is invalid: " + e); - - // Remove the invalid add-on from the install location if the install - // location isn't locked, no restart will be necessary - if (!aInstallLocation.locked) - aInstallLocation.uninstallAddon(aId); - else - WARN("Could not uninstall invalid item from locked install location"); - return false; - } - - // Update the AddonInternal properties. - newAddon._installLocation = aInstallLocation; - newAddon.visible = !(newAddon.id in visibleAddons); - newAddon.installDate = aAddonState.mtime; - newAddon.updateDate = aAddonState.mtime; - - // If there is migration data then apply it. - if (aMigrateData) { - newAddon.userDisabled = aMigrateData.userDisabled; - if ("installDate" in aMigrateData) - newAddon.installDate = aMigrateData.installDate; - if ("targetApplications" in aMigrateData) - newAddon.applyCompatibilityUpdate(aMigrateData, true); - } - - // Update the database. - XPIDatabase.addAddonMetadata(newAddon, aAddonState.descriptor); - - // Visible bootstrapped add-ons need to have their install method called - if (newAddon.visible) { - visibleAddons[newAddon.id] = newAddon; - if (!newAddon.bootstrap) - return true; - - let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - dir.persistentDescriptor = aAddonState.descriptor; - XPIProvider.callBootstrapMethod(newAddon.id, newAddon.version, dir, - "install", - BOOTSTRAP_REASONS.ADDON_INSTALL); - } - - return false; - } - - let changed = false; - let knownLocations = XPIDatabase.getInstallLocations(); - - // The install locations are iterated in reverse order of priority so when - // there are multiple add-ons installed with the same ID the one that - // should be visible is the first one encountered. - aState.reverse().forEach(function(aSt) { - - // We can't include the install location directly in the state as it has - // to be cached as JSON. - let installLocation = this.installLocationsByName[aSt.name]; - let addonStates = aSt.addons; - - // Check if the database knows about any add-ons in this install location. - let pos = knownLocations.indexOf(installLocation.name); - if (pos >= 0) { - knownLocations.splice(pos, 1); - let addons = XPIDatabase.getAddonsInLocation(installLocation.name); - // Iterate through the add-ons installed the last time the application - // ran - addons.forEach(function(aOldAddon) { - // Check if the add-on is still installed - if (aOldAddon.id in addonStates) { - let addonState = addonStates[aOldAddon.id]; - delete addonStates[aOldAddon.id]; - - // The add-on has changed if the modification time has changed, or - // the directory it is installed in has changed or we have an - // updated manifest for it. - if (aOldAddon.id in aManifests[installLocation.name] || - aOldAddon.updateDate != addonState.mtime || - aOldAddon._descriptor != addonState.descriptor) { - changed = updateMetadata(installLocation, aOldAddon, addonState) || - changed; - } - else { - changed = updateVisibilityAndCompatibility(installLocation, - aOldAddon, addonState) || - changed; - } - } - else { - changed = removeMetadata(installLocation, aOldAddon) || changed; - } - }, this); - } - - // All the remaining add-ons in this install location must be new. - - // Get the migration data for this install location. - let locMigrateData = {}; - if (aMigrateData && installLocation.name in aMigrateData) - locMigrateData = aMigrateData[installLocation.name]; - for (let id in addonStates) { - changed = addMetadata(installLocation, id, addonStates[id], - locMigrateData[id]) || changed; - } - }, this); - - // The remaining locations that had add-ons installed in them no longer - // have any add-ons installed in them, or the locations no longer exist. - // The metadata for the add-ons that were in them must be removed from the - // database. - knownLocations.forEach(function(aLocation) { - let addons = XPIDatabase.getAddonsInLocation(aLocation); - addons.forEach(function(aOldAddon) { - changed = removeMetadata(aLocation, aOldAddon) || changed; - }, this); - }, this); - - // Cache the new install location states - cache = JSON.stringify(this.getInstallLocationStates()); - Services.prefs.setCharPref(PREF_INSTALL_CACHE, cache); - return changed; - }, - - /** - * Imports the xpinstall permissions from preferences into the permissions - * manager for the user to change later. - */ - importPermissions: function XPI_importPermissions() { - function importList(aPrefBranch, aAction) { - let list = Services.prefs.getChildList(aPrefBranch, {}); - list.forEach(function(aPref) { - let hosts = Prefs.getCharPref(aPref, ""); - if (!hosts) - return; - - hosts.split(",").forEach(function(aHost) { - Services.perms.add(NetUtil.newURI("http://" + aHost), XPI_PERMISSION, - aAction); - }); - }); - } - - importList(PREF_XPI_WHITELIST_PERMISSIONS, - Ci.nsIPermissionManager.ALLOW_ACTION); - importList(PREF_XPI_BLACKLIST_PERMISSIONS, - Ci.nsIPermissionManager.DENY_ACTION); - }, - - /** - * Checks for any changes that have occurred since the last time the - * application was launched. - * - * @param aAppChanged - * true if the application has changed version number since the last - * launch - * @return true if a change requiring a restart was detected - */ - checkForChanges: function XPI_checkForChanges(aAppChanged) { - LOG("checkForChanges"); - - // Import the website installation permisisons if the applicatio has changed - if (aAppChanged) - this.importPermissions(); - - // First install any new add-ons into the locations, we'll detect these when - // we read the install state - let manifests = {}; - let changed = this.processPendingFileChanges(manifests); - - // We have to hold the DB scheme in prefs so we don't need to load the - // database to see if we need to migrate data - let schema = Prefs.getIntPref(PREF_DB_SCHEMA, 0); - - let migrateData = null; - let cache = null; - if (schema != DB_SCHEMA) { - // The schema has changed so migrate data from the old schema - migrateData = XPIDatabase.migrateData(schema); - } - else { - // If the database exists then the previous file cache can be trusted - // otherwise create an empty database - let db = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); - if (db.exists()) { - cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null); - } - else { - LOG("Database is missing, recreating"); - XPIDatabase.openConnection(); - XPIDatabase.createSchema(); - } - } - - // Load the list of bootstrapped add-ons first so processFileChanges can - // modify it - this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS, - "{}")); - let state = this.getInstallLocationStates(); - if (aAppChanged || changed || cache == null || - cache != JSON.stringify(state)) { - try { - changed = this.processFileChanges(state, manifests, aAppChanged, - migrateData); - } - catch (e) { - ERROR("Error processing file changes: " + e); - } - } - - // If the application crashed before completing any pending operations then - // we should perform them now. - if (changed || Prefs.getBoolPref(PREF_PENDING_OPERATIONS)) { - LOG("Restart necessary"); - XPIDatabase.updateActiveAddons(); - Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, false); - return true; - } - - LOG("No changes found"); - - // Check that the add-ons list still exists - let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST], - true); - if (!addonsList.exists()) { - LOG("Add-ons list is missing, recreating"); - XPIDatabase.writeAddonsList(); - return true; - } - - for (let id in this.bootstrappedAddons) { - let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - dir.persistentDescriptor = this.bootstrappedAddons[id].descriptor; - this.callBootstrapMethod(id, this.bootstrappedAddons[id].version, dir, - "startup", BOOTSTRAP_REASONS.APP_STARTUP); - } - - // Let these shutdown a little earlier when they still have access to most - // of XPCOM - Services.obs.addObserver({ - observe: function(aSubject, aTopic, aData) { - Services.prefs.setCharPref(PREF_BOOTSTRAP_ADDONS, - JSON.stringify(XPIProvider.bootstrappedAddons)); - for (let id in XPIProvider.bootstrappedAddons) { - let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - dir.persistentDescriptor = XPIProvider.bootstrappedAddons[id].descriptor; - XPIProvider.callBootstrapMethod(id, XPIProvider.bootstrappedAddons[id].version, - dir, "shutdown", - BOOTSTRAP_REASONS.APP_SHUTDOWN); - } - Services.obs.removeObserver(this, "quit-application-granted"); - } - }, "quit-application-granted", false); - - return false; - }, - - /** - * Called to test whether this provider supports installing a particular - * mimetype. - * - * @param aMimetype - * The mimetype to check for - * @return true if the mimetype is application/x-xpinstall - */ - supportsMimetype: function XPI_supportsMimetype(aMimetype) { - return aMimetype == "application/x-xpinstall"; - }, - - /** - * Called to test whether installing XPI add-ons is enabled. - * - * @return true if installing is enabled - */ - isInstallEnabled: function XPI_isInstallEnabled() { - // Default to enabled if the preference does not exist - return Prefs.getBoolPref(PREF_XPI_ENABLED, true); - }, - - /** - * Called to test whether installing XPI add-ons from a URI is allowed. - * - * @param aUri - * The URI being installed from - * @return true if installing is allowed - */ - isInstallAllowed: function XPI_isInstallAllowed(aUri) { - if (!this.isInstallEnabled()) - return false; - - if (!aUri) - return true; - - // file: and chrome: don't need whitelisted hosts - if (aUri.schemeIs("chrome") || aUri.schemeIs("file")) - return true; - - - let permission = Services.perms.testPermission(aUri, XPI_PERMISSION); - if (permission == Ci.nsIPermissionManager.DENY_ACTION) - return false; - - let requireWhitelist = Prefs.getBoolPref(PREF_XPI_WHITELIST_REQUIRED, true); - if (requireWhitelist && (permission != Ci.nsIPermissionManager.ALLOW_ACTION)) - return false; - - return true; - }, - - /** - * Called to get an AddonInstall to download and install an add-on from a URL. - * - * @param aUrl - * The URL to be installed - * @param aHash - * A hash for the install - * @param aName - * A name for the install - * @param aIconURL - * An icon URL for the install - * @param aVersion - * A version for the install - * @param aLoadGroup - * An nsILoadGroup to associate requests with - * @param aCallback - * A callback to pass the AddonInstall to - */ - getInstallForURL: function XPI_getInstallForURL(aUrl, aHash, aName, aIconURL, - aVersion, aLoadGroup, aCallback) { - AddonInstall.createDownload(function(aInstall) { - aCallback(aInstall.wrapper); - }, aUrl, aHash, aName, aIconURL, aVersion, aLoadGroup); - }, - - /** - * Called to get an AddonInstall to install an add-on from a local file. - * - * @param aFile - * The file to be installed - * @param aCallback - * A callback to pass the AddonInstall to - */ - getInstallForFile: function XPI_getInstallForFile(aFile, aCallback) { - AddonInstall.createInstall(function(aInstall) { - if (aInstall) - aCallback(aInstall.wrapper); - else - aCallback(null); - }, aFile); - }, - - /** - * Removes an AddonInstall from the list of active installs. - * - * @param install - * The AddonInstall to remove - */ - removeActiveInstall: function XPI_removeActiveInstall(aInstall) { - this.installs = this.installs.filter(function(i) i != aInstall); - }, - - /** - * Called to get an Addon with a particular ID. - * - * @param aId - * The ID of the add-on to retrieve - * @param aCallback - * A callback to pass the Addon to - */ - getAddonByID: function XPI_getAddonByID(aId, aCallback) { - XPIDatabase.getVisibleAddonForID(aId, function(aAddon) { - if (aAddon) - aCallback(createWrapper(aAddon)); - else - aCallback(null); - }); - }, - - /** - * Called to get Addons of a particular type. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types. - * @param aCallback - * A callback to pass an array of Addons to - */ - getAddonsByTypes: function XPI_getAddonsByTypes(aTypes, aCallback) { - XPIDatabase.getVisibleAddons(aTypes, function(aAddons) { - aCallback([createWrapper(a) for each (a in aAddons)]); - }); - }, - - /** - * Called to get Addons that have pending operations. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types - * @param aCallback - * A