author | dtownsend@oxymoronical.com |
Mon, 28 Jan 2008 09:11:48 -0800 | |
changeset 10827 | 033a35620a04b28e0df5c8f21a325269bcc60795 |
parent 10826 | f8ec442314c816971efd7c386d76b1f2d2c3bcbf |
child 10828 | 85d832500dcae5899bd757fd438ac19a9e2377cc |
push id | unknown |
push user | unknown |
push date | unknown |
reviewers | robstrong |
bugs | 404024 |
milestone | 1.9b3pre |
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -63,16 +63,25 @@ pref("xpinstall.dialog.progress.type.chr // 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 the Get Add-ons pane +pref("extensions.getAddons.showPane", true); +pref("extensions.getAddons.browseAddons", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%"); +pref("extensions.getAddons.maxResults", 5); +pref("extensions.getAddons.recommended.browseURL", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%/recommended"); +pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/list/featured/all/10"); +pref("extensions.getAddons.search.browseURL", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/%APP%/search?q=%TERMS%"); +pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/%APP%/api/search/%TERMS%"); + // Blocklist preferences pref("extensions.blocklist.enabled", true); pref("extensions.blocklist.interval", 86400); pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/1/%APP_ID%/%APP_VERSION%/"); pref("extensions.blocklist.detailsURL", "http://%LOCALE%.www.mozilla.com/%LOCALE%/blocklist/"); // Dictionary download preference pref("browser.dictionaries.download.url", "https://%LOCALE%.add-ons.mozilla.com/%LOCALE%/firefox/%VERSION%/dictionaries/");
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/content/eula.js @@ -0,0 +1,43 @@ +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# ***** 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 mozilla.org +# Portions created by the Initial Developer are Copyright (C) 2008 +# 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 ***** + +function Startup() { + var bundle = document.getElementById("extensionsStrings"); + var label = document.createTextNode(bundle.getFormattedString("eulaHeader", [window.arguments[0].name])); + document.getElementById("heading").appendChild(label); + document.getElementById("eula").appendChild(document.createTextNode(window.arguments[0].text)); +}
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/content/eula.xul @@ -0,0 +1,68 @@ +<?xml version="1.0"?> + +# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +# ***** 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 mozilla.org +# Portions created by the Initial Developer are Copyright (C) 2008 +# 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 ***** + +<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> +<?xml-stylesheet href="chrome://mozapps/skin/extensions/eula.css" type="text/css"?> + +<!DOCTYPE window [ +<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> +%brandDTD; +<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd"> +%extensionsDTD; +]> + +<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + title="&eula.title;" width="&eula.width;" height="&eula.height;" + buttons="accept,cancel" buttonlabelaccept="&eula.accept;" + ondialogaccept="window.arguments[0].accepted = true" + onload="Startup();"> + + <script type="application/javascript" src="chrome://mozapps/content/extensions/eula.js"/> + + <stringbundleset id="extensionsSet"> + <stringbundle id="extensionsStrings" src="chrome://mozapps/locale/extensions/extensions.properties"/> + </stringbundleset> + + <label id="heading"/> + + <vbox id="scrollbox" flex="1"> + <description id="eula"/> + </vbox> + +</dialog>
--- a/toolkit/mozapps/extensions/content/extensions.css +++ b/toolkit/mozapps/extensions/content/extensions.css @@ -5,16 +5,56 @@ richlistbox#extensionsView { richlistitem { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon"); } richlistitem[selected="true"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-selected"); } +richlistitem[typeName="searchResult"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#searchresult"); +} + +richlistitem[typeName="searchResult"][selected="true"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#searchresult-selected"); +} + +vbox[typeName="status"][type="search-failure"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-search-failure"); +} + +vbox[typeName="status"][type="recommended-failure"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-recommended-failure"); +} + +vbox[typeName="status"][type="header-recommended"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-header-recommended"); +} + +vbox[typeName="status"][type="footer-recommended"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-recommended"); +} + +vbox[typeName="status"][type="footer-search"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-search"); +} + +vbox[typeName="status"][type="footer-search"][count="0"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-footer-search-empty"); +} + +vbox[typeName="status"][type="retrieve-search"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-retrieve-search"); +} + +vbox[typeName="status"][type="retrieve-recommended"] { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#status-retrieve-recommended"); +} + richlistitem[typeName="update"] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#update-found"); } richlistitem[updateStatus] { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#update-checking"); } @@ -94,16 +134,20 @@ richlistitem[opType="needs-enable"] hbox -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-needs-enable"); } richlistitem[opType="needs-disable"] hbox.addon-optype, richlistitem[opType="needs-disable"] hbox.addon-description { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-needs-disable"); } +#searchbox { + -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#search-textbox"); +} + #viewGroup radio { -moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#viewbutton"); -moz-box-orient: vertical; -moz-box-align: center; -moz-appearance: none; } /* Hide / Display buttons depending on the state of the add-on */ @@ -143,8 +187,41 @@ richlistitem[type="4"] .disableShow, richlistitem[type="4"] .disableHide { display: none; } richlistitem:not([plugin]) .pluginIcon , richlistitem[plugin] .addonIcon:not(.pluginIcon) { display: none; } + +richlistitem[action] .addonInstallButton { + display: none; +} + +.searchResultInstalling, .searchResultFailed, .searchResultInstalled { + display: none; +} + +richlistitem[action="installing"] .searchResultInstalling { + display: -moz-box; +} + +richlistitem[action="failed"] .searchResultFailed, +richlistitem[action="failed"] .addonInstallButton { + display: -moz-box; +} + +richlistitem[action="installed"] .searchResultInstalled { + display: -moz-box; +} + +richlistitem .addonType { + display: none; +} + +richlistitem[addonType="2"] .addonTypeExtension { + display: -moz-box; +} + +richlistitem[addonType="4"] .addonTypeTheme { + display: -moz-box; +}
--- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -62,28 +62,38 @@ var gCheckUpdateSecurity = true; var gUpdatesOnly = false; var gAppID = ""; var gPref = null; var gPriorityCount = 0; var gInstallCount = 0; var gPendingActions = false; var gPlugins = null; var gPluginsDS = null; +var gSearchDS = null; +var gAddonRepository = null; +var gShowGetAddonsPane = false; +var gRetrievedResults = false; +var gRecommendedAddons = null; +var gRDF = null; +var gPendingInstalls = []; const PREF_EM_CHECK_COMPATIBILITY = "extensions.checkCompatibility"; const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity"; const PREF_EXTENSIONS_GETMORETHEMESURL = "extensions.getMoreThemesURL"; const PREF_EXTENSIONS_GETMOREEXTENSIONSURL = "extensions.getMoreExtensionsURL"; const PREF_EXTENSIONS_GETMOREPLUGINSURL = "extensions.getMorePluginsURL"; const PREF_EXTENSIONS_DSS_ENABLED = "extensions.dss.enabled"; const PREF_EXTENSIONS_DSS_SWITCHPENDING = "extensions.dss.switchPending"; const PREF_EXTENSIONS_HIDE_INSTALL_BTN = "extensions.hideInstallButton"; const PREF_EM_LAST_SELECTED_SKIN = "extensions.lastSelectedSkin"; const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin"; const PREF_UPDATE_NOTIFYUSER = "extensions.update.notifyUser"; +const PREF_GETADDONS_SHOWPANE = "extensions.getAddons.showPane"; +const PREF_GETADDONS_REPOSITORY = "extensions.getAddons.repository"; +const PREF_GETADDONS_MAXRESULTS = "extensions.getAddons.maxResults"; const URI_GENERIC_ICON_XPINSTALL = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png"; const URI_GENERIC_ICON_THEME = "chrome://mozapps/skin/extensions/themeGeneric.png"; const RDFURI_ITEM_ROOT = "urn:mozilla:item:root"; const PREFIX_ITEM_URI = "urn:mozilla:item:"; const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#"; const kXULNSURI = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; @@ -103,16 +113,24 @@ function setElementDisabledByID(aID, aDo if (element) { if (aDoDisable) element.setAttribute("disabled", "true"); else element.removeAttribute("disabled"); } } +function isSafeURI(aURI) { + try { + var uri = makeURI(aURI); + var scheme = uri.scheme; + } catch (ex) {} + return (uri && (scheme == "http" || scheme == "https" || scheme == "ftp")); +} + function getBrandShortName() { var brandStrings = document.getElementById("brandStrings"); return brandStrings.getString("brandShortName"); } function getExtensionString(key, strings) { if (strings) return gExtensionStrings.getFormattedString(key, strings); @@ -153,43 +171,37 @@ function showMessage(aIconURL, aMessage, gPriorityCount = 0; addonsMsg.appendNotification(aMessage, aMessage, aIconURL, addonsMsg.PRIORITY_WARNING_LOW + gPriorityCount, buttons).hideclose = !aShowCloseButton; } // dynamically creates a template var AddonsViewBuilder = { - _bindingList: null, - _actionList: null, - // if aActionList is null aBindingList will be used to generate actions - updateView: function(aRulesList, aURI, aBindingList, aActionList) + updateView: function(aRulesList, aURIList, aBindingList, aActionList) { - this._bindingList = aBindingList; - this._actionList = aActionList ? aActionList : aBindingList; + var actionList = aActionList ? aActionList : aBindingList; this.clearChildren(gExtensionsView); var template = document.createElementNS(kXULNSURI, "template"); gExtensionsView.appendChild(template); for (var i = 0; i < aRulesList.length; ++i) - template.appendChild(this.createRule(aRulesList[i], aURI)); + template.appendChild(this.createRule(aRulesList[i], aURIList[i], aBindingList[i], actionList[i])); - this._bindingList = null; - this._actionList = null; gExtensionsView.builder.rebuild(); }, clearChildren: function(aEl) { while (aEl.hasChildNodes()) aEl.removeChild(aEl.lastChild); }, - createRule: function(aTriplesList, aURI) + createRule: function(aTriplesList, aURI, aBindingList, aActionList) { var rule = document.createElementNS(kXULNSURI, "rule"); var conditions = document.createElementNS(kXULNSURI, "conditions"); rule.appendChild(conditions); var content = document.createElementNS(kXULNSURI, "content"); content.setAttribute("uri", "?uri"); conditions.appendChild(content); @@ -197,26 +209,26 @@ var AddonsViewBuilder = { var member = this.createMember("?uri", "?" + aURI); conditions.appendChild(member); for (var i = 0; i < aTriplesList.length; ++i) conditions.appendChild(this.createTriple("?" + aURI, PREFIX_NS_EM + aTriplesList[i][0], aTriplesList[i][1], aTriplesList[i][2])); var bindings = document.createElementNS(kXULNSURI, "bindings"); rule.appendChild(bindings); - for (i = 0; i < this._bindingList.length; ++i) - bindings.appendChild(this.createBinding(this._bindingList[i], aURI)); + for (i = 0; i < aBindingList.length; ++i) + bindings.appendChild(this.createBinding(aBindingList[i], aURI)); var action = document.createElementNS(kXULNSURI, "action"); rule.appendChild(action); var extension = document.createElementNS(kXULNSURI, aURI); action.appendChild(extension); extension.setAttribute("uri", "?" + aURI); - for (i = 0; i < this._actionList.length; ++i) - extension.setAttribute(this._actionList[i][0], this._actionList[i][1]); + for (i = 0; i < aActionList.length; ++i) + extension.setAttribute(aActionList[i][0], aActionList[i][1]); return rule; }, createTriple: function(aSubject, aPredicate, aObject, aParseType) { var triple = document.createElementNS(kXULNSURI, "triple"); triple.setAttribute("subject", aSubject); @@ -251,56 +263,83 @@ function showView(aView) { return; updateLastSelected(aView); gView = aView; // Using disabled to represent add-on state in regards to the EM causes evil // focus behavior when used as an element attribute when the element isn't // really disabled. - var bindingList = [ ["aboutURL", "?aboutURL"], - ["addonID", "?addonID"], - ["availableUpdateURL", "?availableUpdateURL"], - ["availableUpdateVersion", "?availableUpdateVersion"], - ["blocklisted", "?blocklisted"], - ["compatible", "?compatible"], - ["description", "?description"], - ["downloadURL", "?downloadURL"], - ["isDisabled", "?isDisabled"], - ["hidden", "?hidden"], - ["homepageURL", "?homepageURL"], - ["iconURL", "?iconURL"], - ["internalName", "?internalName"], - ["locked", "?locked"], - ["name", "?name"], - ["optionsURL", "?optionsURL"], - ["opType", "?opType"], - ["plugin", "?plugin"], - ["previewImage", "?previewImage"], - ["satisfiesDependencies", "?satisfiesDependencies"], - ["providesUpdatesSecurely", "?providesUpdatesSecurely"], - ["type", "?type"], - ["updateable", "?updateable"], - ["updateURL", "?updateURL"], - ["version", "?version"] ]; + var bindingList = [ [ ["aboutURL", "?aboutURL"], + ["addonID", "?addonID"], + ["availableUpdateURL", "?availableUpdateURL"], + ["availableUpdateVersion", "?availableUpdateVersion"], + ["blocklisted", "?blocklisted"], + ["compatible", "?compatible"], + ["description", "?description"], + ["downloadURL", "?downloadURL"], + ["isDisabled", "?isDisabled"], + ["hidden", "?hidden"], + ["homepageURL", "?homepageURL"], + ["iconURL", "?iconURL"], + ["internalName", "?internalName"], + ["locked", "?locked"], + ["name", "?name"], + ["optionsURL", "?optionsURL"], + ["opType", "?opType"], + ["plugin", "?plugin"], + ["previewImage", "?previewImage"], + ["satisfiesDependencies", "?satisfiesDependencies"], + ["providesUpdatesSecurely", "?providesUpdatesSecurely"], + ["type", "?type"], + ["updateable", "?updateable"], + ["updateURL", "?updateURL"], + ["version", "?version"] ] ]; + var displays = [ "richlistitem" ]; var prefURL; var showInstallFile = true; try { showInstallFile = !gPref.getBoolPref(PREF_EXTENSIONS_HIDE_INSTALL_BTN); } catch (e) { } var showCheckUpdatesAll = true; var showInstallUpdatesAll = false; var showSkip = false; var showContinue = false; switch (aView) { + case "search": + var bindingList = [ [ ["action", "?action"], + ["addonID", "?addonID"], + ["description", "?description"], + ["eula", "?eula"], + ["homepageURL", "?homepageURL"], + ["iconURL", "?iconURL"], + ["name", "?name"], + ["previewImage", "?previewImage"], + ["rating", "?rating"], + ["addonType", "?addonType"], + ["thumbnailURL", "?thumbnailURL"], + ["version", "?version"], + ["xpiHash", "?xpiHash"], + ["xpiURL", "?xpiURL"], + ["typeName", "searchResult"] ], + [ ["type", "?type"], + ["typeName", "status"], + ["count", "?count"], + ["link", "?link" ] ] ]; + var types = [ [ ["searchResult", "true", null] ], + [ ["statusMessage", "true", null] ] ]; + var displays = [ "richlistitem", "vbox" ]; + showCheckUpdatesAll = false; + document.getElementById("searchbox").disabled = isOffline("offlineSearchMsg"); + break; case "extensions": prefURL = PREF_EXTENSIONS_GETMOREEXTENSIONSURL; - var types = [ [ ["type", nsIUpdateItem.TYPE_EXTENSION, "Integer"] ] ]; + types = [ [ ["type", nsIUpdateItem.TYPE_EXTENSION, "Integer"] ] ]; break; case "themes": prefURL = PREF_EXTENSIONS_GETMORETHEMESURL; types = [ [ ["type", nsIUpdateItem.TYPE_THEME, "Integer"] ] ]; break; case "locales": types = [ [ ["type", nsIUpdateItem.TYPE_LOCALE, "Integer"] ] ]; break; @@ -310,78 +349,78 @@ function showView(aView) { break; case "updates": document.getElementById("updates-view").hidden = false; showInstallFile = false; showCheckUpdatesAll = false; showInstallUpdatesAll = true; if (gUpdatesOnly) showSkip = true; - bindingList = [ ["aboutURL", "?aboutURL"], - ["availableUpdateURL", "?availableUpdateURL"], - ["availableUpdateVersion", "?availableUpdateVersion"], - ["availableUpdateInfo", "?availableUpdateInfo"], - ["blocklisted", "?blocklisted"], - ["homepageURL", "?homepageURL"], - ["iconURL", "?iconURL"], - ["internalName", "?internalName"], - ["locked", "?locked"], - ["name", "?name"], - ["opType", "?opType"], - ["previewImage", "?previewImage"], - ["satisfiesDependencies", "?satisfiesDependencies"], - ["providesUpdatesSecurely", "?providesUpdatesSecurely"], - ["type", "?type"], - ["updateURL", "?updateURL"], - ["version", "?version"], - ["typeName", "update"] ]; + bindingList = [ [ ["aboutURL", "?aboutURL"], + ["availableUpdateURL", "?availableUpdateURL"], + ["availableUpdateVersion", "?availableUpdateVersion"], + ["availableUpdateInfo", "?availableUpdateInfo"], + ["blocklisted", "?blocklisted"], + ["homepageURL", "?homepageURL"], + ["iconURL", "?iconURL"], + ["internalName", "?internalName"], + ["locked", "?locked"], + ["name", "?name"], + ["opType", "?opType"], + ["previewImage", "?previewImage"], + ["satisfiesDependencies", "?satisfiesDependencies"], + ["providesUpdatesSecurely", "?providesUpdatesSecurely"], + ["type", "?type"], + ["updateURL", "?updateURL"], + ["version", "?version"], + ["typeName", "update"] ] ]; types = [ [ ["availableUpdateVersion", "?availableUpdateVersion", null], ["updateable", "true", null] ] ]; break; case "installs": document.getElementById("installs-view").hidden = false; showInstallFile = false; showCheckUpdatesAll = false; showInstallUpdatesAll = false; if (gUpdatesOnly) showContinue = true; - bindingList = [ ["aboutURL", "?aboutURL"], - ["addonID", "?addonID"], - ["availableUpdateURL", "?availableUpdateURL"], - ["availableUpdateVersion", "?availableUpdateVersion"], - ["blocklisted", "?blocklisted"], - ["compatible", "?compatible"], - ["description", "?description"], - ["downloadURL", "?downloadURL"], - ["incompatibleUpdate", "?incompatibleUpdate"], - ["isDisabled", "?isDisabled"], - ["hidden", "?hidden"], - ["homepageURL", "?homepageURL"], - ["iconURL", "?iconURL"], - ["internalName", "?internalName"], - ["locked", "?locked"], - ["name", "?name"], - ["optionsURL", "?optionsURL"], - ["opType", "?opType"], - ["previewImage", "?previewImage"], - ["progress", "?progress"], - ["state", "?state"], - ["type", "?type"], - ["updateable", "?updateable"], - ["updateURL", "?updateURL"], - ["version", "?version"], - ["newVersion", "?newVersion"], - ["typeName", "install"] ]; + bindingList = [ [ ["aboutURL", "?aboutURL"], + ["addonID", "?addonID"], + ["availableUpdateURL", "?availableUpdateURL"], + ["availableUpdateVersion", "?availableUpdateVersion"], + ["blocklisted", "?blocklisted"], + ["compatible", "?compatible"], + ["description", "?description"], + ["downloadURL", "?downloadURL"], + ["incompatibleUpdate", "?incompatibleUpdate"], + ["isDisabled", "?isDisabled"], + ["hidden", "?hidden"], + ["homepageURL", "?homepageURL"], + ["iconURL", "?iconURL"], + ["internalName", "?internalName"], + ["locked", "?locked"], + ["name", "?name"], + ["optionsURL", "?optionsURL"], + ["opType", "?opType"], + ["previewImage", "?previewImage"], + ["progress", "?progress"], + ["state", "?state"], + ["type", "?type"], + ["updateable", "?updateable"], + ["updateURL", "?updateURL"], + ["version", "?version"], + ["newVersion", "?newVersion"], + ["typeName", "install"] ] ]; types = [ [ ["state", "?state", null] ] ]; break; } var showGetMore = false; var getMore = document.getElementById("getMore"); - if (prefURL) { + if (prefURL && !gShowGetAddonsPane) { try { getMore.setAttribute("value", getMore.getAttribute("value" + aView)); var getMoreURL = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] .getService(Components.interfaces.nsIURLFormatter) .formatURLPref(prefURL); getMore.setAttribute("getMoreURL", getMoreURL); showGetMore = getMoreURL == "about:blank" ? false : true; } @@ -404,18 +443,19 @@ function showView(aView) { document.getElementById("checkUpdatesAllButton").hidden = !showCheckUpdatesAll; document.getElementById("installUpdatesAllButton").hidden = !showInstallUpdatesAll; document.getElementById("skipDialogButton").hidden = !showSkip; document.getElementById("continueDialogButton").hidden = !showContinue; document.getElementById("themePreviewArea").hidden = !isThemes; document.getElementById("themeSplitter").hidden = !isThemes; document.getElementById("showUpdateInfoButton").hidden = aView != "updates"; document.getElementById("hideUpdateInfoButton").hidden = true; + document.getElementById("searchPanel").hidden = aView != "search"; - AddonsViewBuilder.updateView(types, "richlistitem", bindingList, null); + AddonsViewBuilder.updateView(types, displays, bindingList, null); if (aView == "updates" || aView == "installs") gExtensionsView.selectedItem = gExtensionsView.children[0]; if (showSkip) { var button = document.getElementById("installUpdatesAllButton"); button.setAttribute("default", "true"); window.setTimeout(function () { button.focus(); }, 0); @@ -519,45 +559,299 @@ function setRestartMessage(aItem) item.setAttribute("description", item.getAttribute("oldDescription")); item.removeAttribute("oldDescription"); } } aItem.setAttribute("oldDescription", aItem.getAttribute("description")); aItem.setAttribute("description", restartMessage); } +// Removes any assertions in the datasource about a given resource +function cleanResource(ds, resource) { + // Remove outward arcs + var arcs = ds.ArcLabelsOut(resource); + while (arcs.hasMoreElements()) { + var arc = arcs.getNext().QueryInterface(Components.interfaces.nsIRDFResource); + var targets = ds.GetTargets(resource, arc, true); + while (targets.hasMoreElements()) { + var value = targets.getNext().QueryInterface(Components.interfaces.nsIRDFNode); + if (value) + ds.Unassert(resource, arc, value); + } + } +} + +// Wipes the datasource clean of assertions +function cleanDataSource(ds, rootctr) { + // Remove old entries from the list + var nodes = rootctr.GetElements(); + while (nodes.hasMoreElements()) { + var node = nodes.getNext() + .QueryInterface(Components.interfaces.nsIRDFResource); + rootctr.RemoveElement(node, false); + cleanResource(ds, node); + } +} + +// Displays the search status message +function displaySearchThrobber(aKey) { + var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"] + .getService(Components.interfaces.nsIRDFContainerUtils); + var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT)); + + cleanDataSource(gSearchDS, rootctr); + + var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header"); + rootctr.AppendElement(labelNode); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "statusMessage"), + gRDF.GetLiteral("true"), + true); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "type"), + gRDF.GetLiteral(aKey), + true); +} + +// Clears the search box and updates the result list +function resetSearch() { + var searchbox = document.getElementById("searchbox"); + searchbox.value = ""; + retrieveRepositoryAddons(""); +} + +// Searches for results +function retrieveRepositoryAddons(aTerms) { + if (gAddonRepository.isSearching) + gAddonRepository.cancelSearch(); + if (aTerms) { + displaySearchThrobber("retrieve-search"); + gAddonRepository.searchAddons(aTerms, + gPref.getIntPref(PREF_GETADDONS_MAXRESULTS), + AddonSearchResults); + } + else { + if (gRecommendedAddons) { + displaySearchResults(gRecommendedAddons, -1, true); + } + else { + displaySearchThrobber("retrieve-recommended"); + gAddonRepository.retrieveRecommendedAddons(gPref.getIntPref(PREF_GETADDONS_MAXRESULTS), + RecommendedSearchResults); + } + } + gRetrievedResults = true; +} + +// Puts search results into the search datasource +function displaySearchResults(addons, count, isRecommended) { + var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"] + .getService(Components.interfaces.nsIRDFContainerUtils); + var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT)); + + gSearchDS.beginUpdateBatch(); + + cleanDataSource(gSearchDS, rootctr); + + if (isRecommended) { + var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header"); + rootctr.AppendElement(labelNode); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "statusMessage"), + gRDF.GetLiteral("true"), + true); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "type"), + gRDF.GetLiteral("header-recommended"), + true); + + // Case insensitive sort + function compare(a, b) { + if (a.name.toLowerCase() < b.name.toLowerCase()) + return -1; + if (a.name.toLowerCase() > b.name.toLowerCase()) + return 1; + return 0; + } + addons.sort(compare); + } + + var urlproperties = [ "iconURL", "homepageURL", "thumbnailURL", "xpiURL" ]; + var properties = [ "name", "eula", "iconURL", "homepageURL", "thumbnailURL", "xpiURL", "xpiHash" ]; + for (var i = 0; i < addons.length; i++) { + var addon = addons[i]; + // Strip out any items with potentially unsafe urls + var unsafe = false; + for (var j = 0; j < urlproperties.length; j++) { + if (!isSafeURI(addon[urlproperties[j]])) { + unsafe = true; + break; + } + } + if (unsafe) + continue; + + var resultNode = gRDF.GetResource("urn:mozilla:addons:search:" + addon.xpiURL); + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "addonID"), + gRDF.GetLiteral(addon.id), + true); + // Use the short summary for our "description" + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "description"), + gRDF.GetLiteral(addon.summary), + true); + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "addonType"), + gRDF.GetIntLiteral(addon.type), + true); + if (addon.rating >= 0) { + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "rating"), + gRDF.GetIntLiteral(addon.rating), + gRDF.GetIntLiteral(3), + true); + } + + for (var j = 0; j < properties.length; j++) { + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + properties[j]), + gRDF.GetLiteral(addon[properties[j]]), + true); + } + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "searchResult"), + gRDF.GetLiteral("true"), + true); + rootctr.AppendElement(resultNode); + } + + labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:footer"); + rootctr.AppendElement(labelNode); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "statusMessage"), + gRDF.GetLiteral("true"), + true); + if (isRecommended) { + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "type"), + gRDF.GetLiteral("footer-recommended"), + true); + var url = gAddonRepository.getRecommendedURL(); + } + else { + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "type"), + gRDF.GetLiteral("footer-search"), + true); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "count"), + gRDF.GetIntLiteral(count), + true); + var searchbox = document.getElementById("searchbox"); + // The value attribute will be the persisted value of the last search run + url = gAddonRepository.getSearchURL(searchbox.getAttribute("value")); + } + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "link"), + gRDF.GetLiteral(url), + true); + + gSearchDS.endUpdateBatch(); +} + +// Displays the search failure status message +function displaySearchFailure(isRecommended) { + var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"] + .getService(Components.interfaces.nsIRDFContainerUtils); + var rootctr = rdfCU.MakeSeq(gSearchDS, gRDF.GetResource(RDFURI_ITEM_ROOT)); + + cleanDataSource(gSearchDS, rootctr); + + var labelNode = gRDF.GetResource("urn:mozilla:addons:search:status:header"); + rootctr.AppendElement(labelNode); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "statusMessage"), + gRDF.GetLiteral("true"), + true); + gSearchDS.Assert(labelNode, + gRDF.GetResource(PREFIX_NS_EM + "type"), + gRDF.GetLiteral(isRecommended ? "recommended-failure" : "search-failure"), + true); +} + +// nsIAddonSearchResultsCallback for the recommended search +var RecommendedSearchResults = { + searchSucceeded: function(aAddons, aAddonCount, aTotalResults) { + gRecommendedAddons = aAddons; + displaySearchResults(aAddons, aTotalResults, true); + }, + + searchFailed: function() { + displaySearchFailure(true); + } +} + +// nsIAddonSearchResultsCallback for a standard search +var AddonSearchResults = { + searchSucceeded: function(aAddons, aAddonCount, aTotalResults) { + displaySearchResults(aAddons, aTotalResults, false); + }, + + searchFailed: function() { + displaySearchFailure(false); + } +} + +// Initialises the search repository and the results datasource +function initSearchDS() { + var repository = "@mozilla.org/extensions/addon-repository;1"; + try { + var repo = gPref.getCharPref(PREF_GETADDONS_REPOSITORY); + if (repo in Components.classes) + repository = repo; + } catch (e) { } + gAddonRepository = Components.classes[repository] + .createInstance(Components.interfaces.nsIAddonRepository); + var browseAddons = document.getElementById("browseAddons"); + var homepage = gAddonRepository.homepageURL; + if (homepage) + browseAddons.setAttribute("homepageURL", homepage); + else + browseAddons.hidden = true; + gSearchDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] + .createInstance(Components.interfaces.nsIRDFDataSource); + gExtensionsView.database.AddDataSource(gSearchDS); + var ioService = Components.classes["@mozilla.org/network/io-service;1"] + .getService(nsIIOService); + if (!ioService.offline) + retrieveRepositoryAddons(document.getElementById("searchbox").value); +} + function initPluginsDS() { gPluginsDS = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"] .createInstance(Components.interfaces.nsIRDFDataSource); rebuildPluginsDS(); } function rebuildPluginsDS() { var phs = Components.classes["@mozilla.org/plugin/host;1"] .getService(Components.interfaces.nsIPluginHost); var plugins = phs.getPluginTags({ }); - var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"] - .getService(Components.interfaces.nsIRDFService); var rdfCU = Components.classes["@mozilla.org/rdf/container-utils;1"] .getService(Components.interfaces.nsIRDFContainerUtils); - var rootctr = rdfCU.MakeSeq(gPluginsDS, rdf.GetResource(RDFURI_ITEM_ROOT)); + var rootctr = rdfCU.MakeSeq(gPluginsDS, gRDF.GetResource(RDFURI_ITEM_ROOT)); gPlugins = { }; // Running in a batch stops the template builder from running gPluginsDS.beginUpdateBatch(); - // Remove old entries from the list - var nodes = rootctr.GetElements(); - while (nodes.hasMoreElements()) { - var node = nodes.getNext() - .QueryInterface(Components.interfaces.nsIRDFResource); - rootctr.RemoveElement(node, false); - } + cleanDataSource(gPluginsDS, rootctr); // Case insensitive sort function compare(a, b) { if (a.name.toLowerCase() < b.name.toLowerCase()) return -1; if (a.name.toLowerCase() > b.name.toLowerCase()) return 1; return 0; @@ -587,69 +881,67 @@ function rebuildPluginsDS() plugins : [] }; } gPlugins[name][desc].plugins.push(plugin); } for (var pluginName in gPlugins) { for (var pluginDesc in gPlugins[pluginName]) { plugin = gPlugins[pluginName][pluginDesc]; - var pluginNode = rdf.GetResource(PREFIX_ITEM_URI + plugin.filename); + var pluginNode = gRDF.GetResource(PREFIX_ITEM_URI + plugin.filename); rootctr.AppendElement(pluginNode); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "name"), - rdf.GetLiteral(pluginName), + gRDF.GetResource(PREFIX_NS_EM + "name"), + gRDF.GetLiteral(pluginName), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "addonID"), - rdf.GetLiteral(plugin.filename), + gRDF.GetResource(PREFIX_NS_EM + "addonID"), + gRDF.GetLiteral(plugin.filename), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "description"), - rdf.GetLiteral(pluginDesc), + gRDF.GetResource(PREFIX_NS_EM + "description"), + gRDF.GetLiteral(pluginDesc), true); if (plugin.homepageURL) gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "homepageURL"), - rdf.GetLiteral(plugin.homepageURL), + gRDF.GetResource(PREFIX_NS_EM + "homepageURL"), + gRDF.GetLiteral(plugin.homepageURL), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "isDisabled"), - rdf.GetLiteral(plugin.disabled ? "true" : "false"), + gRDF.GetResource(PREFIX_NS_EM + "isDisabled"), + gRDF.GetLiteral(plugin.disabled ? "true" : "false"), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "blocklisted"), - rdf.GetLiteral(plugin.blocklisted ? "true" : "false"), + gRDF.GetResource(PREFIX_NS_EM + "blocklisted"), + gRDF.GetLiteral(plugin.blocklisted ? "true" : "false"), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "compatible"), - rdf.GetLiteral("true"), + gRDF.GetResource(PREFIX_NS_EM + "compatible"), + gRDF.GetLiteral("true"), true); gPluginsDS.Assert(pluginNode, - rdf.GetResource(PREFIX_NS_EM + "plugin"), - rdf.GetLiteral("true"), + gRDF.GetResource(PREFIX_NS_EM + "plugin"), + gRDF.GetLiteral("true"), true); } } gPluginsDS.endUpdateBatch(); } function togglePluginDisabled(aName, aDesc) { var plugin = gPlugins[aName][aDesc]; plugin.disabled = !plugin.disabled; for (var i = 0; i < plugin.plugins.length; ++i) plugin.plugins[i].disabled = plugin.disabled; - var rdf = Components.classes["@mozilla.org/rdf/rdf-service;1"] - .getService(Components.interfaces.nsIRDFService); - gPluginsDS.Change(rdf.GetResource(PREFIX_ITEM_URI + plugin.filename), - rdf.GetResource(PREFIX_NS_EM + "isDisabled"), - rdf.GetLiteral(plugin.disabled ? "false" : "true"), - rdf.GetLiteral(plugin.disabled ? "true" : "false")); + gPluginsDS.Change(gRDF.GetResource(PREFIX_ITEM_URI + plugin.filename), + gRDF.GetResource(PREFIX_NS_EM + "isDisabled"), + gRDF.GetLiteral(plugin.disabled ? "false" : "true"), + gRDF.GetLiteral(plugin.disabled ? "true" : "false")); gExtensionsViewController.onCommandUpdate(); gExtensionsView.selectedItem.focus(); } /////////////////////////////////////////////////////////////////////////////// // Startup, Shutdown function Startup() { @@ -676,26 +968,37 @@ function Startup() try { gCheckCompat = gPref.getBoolPref(PREF_EM_CHECK_COMPATIBILITY); } catch(e) { } try { gCheckUpdateSecurity = gPref.getBoolPref(PREF_EM_CHECK_UPDATE_SECURITY); } catch(e) { } + try { + gShowGetAddonsPane = gPref.getBoolPref(PREF_GETADDONS_SHOWPANE); + } catch(e) { } + // Sort on startup and anytime an add-on is installed or upgraded. gExtensionManager.sortTypeByProperty(nsIUpdateItem.TYPE_ADDON, "name", true); // Extension Command Updating is handled by a command controller. gExtensionsView.controllers.appendController(gExtensionsViewController); gExtensionsView.addEventListener("select", onAddonSelect, false); + gRDF = Components.classes["@mozilla.org/rdf/rdf-service;1"] + .getService(Components.interfaces.nsIRDFService); + initPluginsDS(); gExtensionsView.database.AddDataSource(gPluginsDS); + if (gShowGetAddonsPane) + initSearchDS(); gExtensionsView.database.AddDataSource(gExtensionManager.datasource); gExtensionsView.setAttribute("ref", RDFURI_ITEM_ROOT); + + document.getElementById("search-view").hidden = !gShowGetAddonsPane; updateOptionalViews(); var viewGroup = document.getElementById("viewGroup"); gExtensionsView.focus(); gExtensionsViewController.onCommandUpdate(); // Now look and see if we're being opened by XPInstall @@ -739,17 +1042,16 @@ function Startup() getExtensionString("safeModeMsg"), null, null, true, null); } if ("arguments" in window) { try { var params = window.arguments[0].QueryInterface(Components.interfaces.nsIDialogParamBlock); var manager = window.arguments[1].QueryInterface(Components.interfaces.nsIObserver); - showView("installs"); gDownloadManager.addDownloads(params, manager); } catch (e) { if (window.arguments[0] == "updates-only") { gUpdatesOnly = true; #ifdef MOZ_PHOENIX // If we are Firefox when updating on startup don't display context // menuitems that can open a browser window. @@ -763,32 +1065,37 @@ function Startup() null, null, true, null); document.title = getExtensionString("newUpdateWindowTitle", [getBrandShortName()]); } else showView(window.arguments[0]); } } else if (viewGroup.hasAttribute("last-selected") && + document.getElementById(viewGroup.getAttribute("last-selected") + "-view") && !document.getElementById(viewGroup.getAttribute("last-selected") + "-view").hidden) showView(viewGroup.getAttribute("last-selected")); else showView("extensions"); if (gExtensionsView.selectedItem) gExtensionsView.scrollBoxObject.scrollToElement(gExtensionsView.selectedItem); gPref.setBoolPref(PREF_UPDATE_NOTIFYUSER, false); if (gUpdatesOnly && gExtensionsView.children.length == 0) window.close(); } function Shutdown() { + if (gAddonRepository && gAddonRepository.isSearching) + gAddonRepository.cancelSearch(); + + gRDF = null; gPref = null; gExtensionsView.removeEventListener("select", onAddonSelect, false); gExtensionsView.database.RemoveDataSource(gExtensionManager.datasource); gExtensionManager.removeUpdateListenerAt(gObserverIndex); var os = Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService); @@ -825,31 +1132,31 @@ XPInstallDownloadManager.prototype = { _statusFormatKBMB : null, _statusFormatKBKB : null, _statusFormatMBMB : null, observe: function (aSubject, aTopic, aData) { switch (aTopic) { case "xpinstall-download-started": - showView("installs"); var params = aSubject.QueryInterface(Components.interfaces.nsISupportsArray); var paramBlock = params.GetElementAt(0).QueryInterface(Components.interfaces.nsISupportsInterfacePointer); paramBlock = paramBlock.data.QueryInterface(Components.interfaces.nsIDialogParamBlock); var manager = params.GetElementAt(1).QueryInterface(Components.interfaces.nsISupportsInterfacePointer); manager = manager.data.QueryInterface(Components.interfaces.nsIObserver); this.addDownloads(paramBlock, manager); break; } }, addDownloads: function (aParams, aManager) { var numXPInstallItems = aParams.GetInt(1); var items = []; + var switchPane = false; for (var i = 0; i < numXPInstallItems;) { var displayName = aParams.GetString(i++); var url = aParams.GetString(i++); var iconURL = aParams.GetString(i++); var uri = Components.classes["@mozilla.org/network/io-service;1"] .getService(nsIIOService).newURI(url, null, null); var isTheme = uri.QueryInterface(nsIURL).fileExtension.toLowerCase() == "jar"; if (!iconURL) { @@ -860,21 +1167,32 @@ XPInstallDownloadManager.prototype = { var type = isTheme ? nsIUpdateItem.TYPE_THEME : nsIUpdateItem.TYPE_EXTENSION; var item = Components.classes["@mozilla.org/updates/item;1"] .createInstance(Components.interfaces.nsIUpdateItem); item.init(url, " ", "app-profile", "", "", displayName, url, "", iconURL, "", "", type, ""); items.push(item); // Advance the enumerator var certName = aParams.GetString(i++); + + // Check whether the install was triggered from the Get Add-ons pane. + var pos = gPendingInstalls.indexOf(url); + if (pos >= 0) + gPendingInstalls.splice(pos, 1); + else + switchPane = true; } gExtensionManager.addDownloads(items, items.length, aManager); updateOptionalViews(); updateGlobalCommands(); + // Only switch to the installs pane if there was an not started by the + // Get Add-ons pane + if (switchPane) + showView("installs"); }, getElementForAddon: function(aAddon) { var element = document.getElementById(PREFIX_ITEM_URI + aAddon.id); if (aAddon.id == aAddon.xpiURL) element = document.getElementById(aAddon.xpiURL); return element; @@ -1164,21 +1482,17 @@ function onAddonSelect(aEvent) } } else if (gView == "updates") { UpdateInfoLoader.cancelLoad(); if (!gExtensionsView.selectedItem) { previewImageDeck.selectedIndex = 3; } else { - try { - var uri = makeURI(gExtensionsView.selectedItem.getAttribute("availableUpdateInfo")); - var scheme = uri.scheme; - } catch (ex) {} - if (uri && (scheme == "http" || scheme == "https")) + if (isSafeURI(gExtensionsView.selectedItem.getAttribute("availableUpdateInfo"))) UpdateInfoLoader.loadInfo(uri.spec); else previewImageDeck.selectedIndex = 4; } } } } @@ -1547,16 +1861,21 @@ const gAddonsMsgObserver = { if (child.hasAttribute("updateStatus")) child.removeAttribute("updateStatus"); } break; case "addons-go-online": var ioService = Components.classes["@mozilla.org/network/io-service;1"] .getService(nsIIOService); ioService.offline = false; + // If no results have been retrieved start pulling some + if (!gRetrievedResults) + retrieveRepositoryAddons(document.getElementById("searchbox").value); + if (gView == "search") + document.getElementById("searchbox").disabled = false; break; case "addons-message-dismiss": break; case "addons-restart-app": restartApp(); break; } if (gExtensionsView.selectedItem) @@ -1856,16 +2175,48 @@ function confirmOperation(aName, aTitle, for (var i = 0; i < aDependantItems.length; ++i) names.push(aDependantItems[i].name + " " + aDependantItems[i].version); window.openDialog("chrome://mozapps/content/extensions/list.xul", "", "titlebar,modal,centerscreen", names, params); return params.result == "accept"; } +function installCallback(item, status) { + var resultNode = gRDF.GetResource(item.id); + + // Strip out old status + gSearchDS.Unassert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "action"), + gRDF.GetLiteral("installing"), + true); + + if (status == -210) { + // User cancelled + var pos = gPendingInstalls.indexOf(item.getAttribute("xpiURL")); + if (pos >= 0) + gPendingInstalls.splice(pos, 1); + return; + } + if (status < 0) { + // Some other failure + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "action"), + gRDF.GetLiteral("failed"), + true); + } + else { + // Success + gSearchDS.Assert(resultNode, + gRDF.GetResource(PREFIX_NS_EM + "action"), + gRDF.GetLiteral("installed"), + true); + } +} + var gExtensionsViewController = { supportsCommand: function (aCommand) { var commandNode = document.getElementById(aCommand); return commandNode && (commandNode.parentNode == document.getElementById("extensionsCommands")); }, isCommandEnabled: function (aCommand) @@ -1876,16 +2227,18 @@ var gExtensionsViewController = { if (selectedItem.hasAttribute("downloadURL") && selectedItem.getAttribute("downloadURL") != "") { if (aCommand == "cmd_uninstall") return true; return false; } switch (aCommand) { + case "cmd_installSearchResult": + return true; case "cmd_useTheme": return selectedItem.type == nsIUpdateItem.TYPE_THEME && !selectedItem.isDisabled && selectedItem.opType != OP_NEEDS_UNINSTALL && gCurrentTheme != selectedItem.getAttribute("internalName"); case "cmd_options": return selectedItem.type == nsIUpdateItem.TYPE_EXTENSION && !selectedItem.isDisabled && @@ -1964,16 +2317,51 @@ var gExtensionsViewController = { { if (this.isCommandEnabled(command.id)) command.removeAttribute("disabled"); else command.setAttribute("disabled", "true"); }, commands: { + cmd_installSearchResult: function (aSelectedItem) + { + if (!isXPInstallEnabled()) + return; + + if (aSelectedItem.hasAttribute("eula")) { + var eula = { + name: aSelectedItem.getAttribute("name"), + text: aSelectedItem.getAttribute("eula"), + accepted: false + }; + window.openDialog("chrome://mozapps/content/extensions/eula.xul", "_blank", + "chrome,dialog,modal,centerscreen,resizable=no", eula); + if (!eula.accepted) + return; + } + + var details = { + URL: aSelectedItem.getAttribute("xpiURL"), + Hash: aSelectedItem.getAttribute("xpiHash"), + IconURL: aSelectedItem.getAttribute("iconURL"), + toString: function () { return this.URL; } + }; + var params = []; + params[aSelectedItem.getAttribute("name")] = details; + + gSearchDS.Assert(gRDF.GetResource(aSelectedItem.id), + gRDF.GetResource(PREFIX_NS_EM + "action"), + gRDF.GetLiteral("installing"), + true); + // Remember that we don't want to change panes for this install + gPendingInstalls.push(details.URL); + InstallTrigger.install(params, function(url, status) { installCallback(aSelectedItem, status); }); + }, + cmd_close: function (aSelectedItem) { closeWindow(true); }, cmd_useTheme: function (aSelectedItem) { gCurrentTheme = aSelectedItem.getAttribute("internalName"); @@ -2030,21 +2418,17 @@ var gExtensionsViewController = { openDialog(optionsURL, "", features); }, cmd_homepage: function (aSelectedItem) { if (!aSelectedItem) return; var homepageURL = aSelectedItem.getAttribute("homepageURL"); // only allow http(s) homepages - try { - var uri = makeURI(homepageURL); - var scheme = uri.scheme; - } catch (ex) {} - if (uri && (scheme == "http" || scheme == "https")) + if (isSafeURI(homepageURL)) openURL(uri.spec); }, cmd_about: function (aSelectedItem) { if (!aSelectedItem) return; var aboutURL = aSelectedItem.getAttribute("aboutURL"); if (aboutURL != "")
--- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -380,16 +380,260 @@ if (opType == "needs-uninstall" && cmd == "cmd_uninstall" || opType == "needs-disable" && cmd == "cmd_disable" && !this.isDisabled || opType == "needs-enable" && cmd == "cmd_disable" && this.isDisabled) this.parentNode.ensureElementIsVisible(this); ]]> </handler> </handlers> </binding> + + <binding id="searchresult" extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> + <resources> + <stylesheet src="chrome://mozapps/skin/extensions/extensions.css"/> + </resources> + + <content> + <xul:hbox flex="1"> + <xul:vbox class="addon-icon" xbl:inherits="iconURL"/> + <xul:vbox flex="1" class="addonTextBox"> + <xul:hbox align="center"> + <xul:hbox anonid="addonNameVersion" class="addon-name-version" xbl:inherits="name, version"/> + <xul:spacer flex="1"/> + <xul:image class="addonRating" xbl:inherits="rating"/> + </xul:hbox> + <xul:hbox anonid="addonDescription" class="addon-description" xbl:inherits="description, opType"/> + </xul:vbox> + </xul:hbox> + </content> + + <implementation extends="nsIAccessibleProvider"> + <constructor> + if (!this.hasAttribute("thumbnailURL")) + return this._hideImage(); + + if (this.hasAttribute("thumbwidth")) + return this._displayImage(); + + var image = new Image(); + var id = this.id; + image.onload = function() { + document.getElementById(id)._imageLoaded(image); + }; + image.onerror = function() { + document.getElementById(id)._hideImage(); + }; + image.src = this.getAttribute("thumbnailURL"); + </constructor> + + <field name="_maxSize">125</field> + + <method name="_displayImage"> + <body> + </body> + </method> + + <method name="_hideImage"> + <body> + </body> + </method> + + <method name="_imageLoaded"> + <parameter name="image"/> + <body> + <![CDATA[ + if ((this._maxSize >= image.width) && + (this._maxSize >= image.height)) { + var width = image.width; + var height = image.height; + } + else if (image.width > image.height) { + width = this._maxSize; + height = (this._maxSize / image.width) * image.height; + } + else { + height = this._maxSize; + width = (this._maxSize / image.height) * image.width; + } + + this.setAttribute("thumbwidth", width); + this.setAttribute("thumbheight", height); + this._displayImage(); + ]]> + </body> + </method> + </implementation> + </binding> + + <binding id="searchresult-selected" extends="chrome://mozapps/content/extensions/extensions.xml#searchresult"> + <content> + <xul:hbox flex="1"> + <xul:vbox class="addon-icon" xbl:inherits="iconURL"/> + <xul:vbox flex="1" class="addonTextBox"> + <xul:hbox align="center"> + <xul:hbox anonid="addonNameVersion" class="addon-name-version" xbl:inherits="name, version"/> + <xul:spacer flex="1"/> + <xul:image class="addonRating" xbl:inherits="rating"/> + </xul:hbox> + <xul:hbox flex="1" align="stretch" class="addon-search-details"> + <xul:vbox pack="start"> + <xul:deck class="addonThumbnailContainer" selectedIndex="0"> + <xul:vbox flex="1" align="center" pack="center"> + <xul:image class="addonThrobber"/> + </xul:vbox> + <xul:vbox flex="1" align="center" pack="center"> + <xul:image class="addonThumbnail"/> + </xul:vbox> + <xul:vbox flex="1" align="center" pack="center"> + <xul:label class="addonMissingThumbnail" value="&missingThumbnail.label;"/> + </xul:vbox> + </xul:deck> + </xul:vbox> + <xul:vbox flex="1"> + <xul:label anonid="addonDescriptionWrap" class="descriptionWrap" + xbl:inherits="xbl:text=description"/> + <xul:label class="addonLearnMore text-link" xbl:inherits="homepageURL" + value="&searchResultHomepage.value;" + onclick="openURL(this.getAttribute('homepageURL'));"/> + <xul:spacer flex="1"/> + <xul:hbox anonid="selectedButtons" class="selectedButtons"> + <xul:hbox align="center" class="addonType addonTypeExtension"> + <xul:image/> + <xul:label value="&addonTypeExtension.label;"/> + </xul:hbox> + <xul:hbox align="center" class="addonType addonTypeTheme"> + <xul:image/> + <xul:label value="&addonTypeTheme.label;"/> + </xul:hbox> + <xul:spacer flex="1"/> + <xul:hbox align="center" class="searchResultInstalling"> + <xul:image class="addonThrobber"/> + <xul:label value="&searchResultInstalling.label;"/> + </xul:hbox> + <xul:hbox align="center" class="searchResultFailed"> + <xul:label value="&searchResultFailed.label;"/> + </xul:hbox> + <xul:hbox align="center" class="searchResultInstalled"> + <xul:label value="&searchResultInstalled.label;"/> + </xul:hbox> + <xul:button class="addonInstallButton" + label="&cmd.installSearchResult.label;" + accesskey="&cmd.installSearchResult.accesskey;" + tooltiptext="&cmd.installSearchResult.tooltip;" + command="cmd_installSearchResult"/> + </xul:hbox> + </xul:vbox> + </xul:hbox> + </xul:vbox> + </xul:hbox> + </content> + + <implementation implements="nsIAccessibleProvider"> + <field name="_thumbnailContainer"> + document.getAnonymousElementByAttribute(this, "class", "addonThumbnailContainer"); + </field> + <field name="_thumbnail"> + document.getAnonymousElementByAttribute(this, "class", "addonThumbnail"); + </field> + + <method name="_displayImage"> + <body> + this._thumbnail.style.width = this.getAttribute("thumbwidth") + "px"; + this._thumbnail.style.height = this.getAttribute("thumbheight") + "px"; + this._thumbnail.src = this.getAttribute("thumbnailURL"); + this._thumbnailContainer.selectedIndex = 1; + </body> + </method> + + <method name="_hideImage"> + <body> + this._thumbnailContainer.selectedIndex = 2; + </body> + </method> + </implementation> + </binding> + + <binding id="status-search-failure"> + <content align="center"> + <xul:hbox align="center" pack="center"> + <xul:image class="addonFailure"/> + <xul:label value="&searchFailed.label;"/> + </xul:hbox> + <xul:button command="cmd_resetSearch" label="&cancelSearch.button;"/> + </content> + </binding> + + <binding id="status-recommended-failure"> + <content> + <xul:hbox align="center" pack="center"> + <xul:image class="addonFailure"/> + <xul:label value="&searchFailed.label;"/> + </xul:hbox> + </content> + </binding> + + <binding id="status-header-recommended"> + <content> + <xul:label value="&recommendedHeader.label;"/> + </content> + </binding> + + <binding id="status-footer-recommended"> + <content align="end"> + <xul:label class="text-link" xbl:inherits="searchURL=link" value="&recommendedResults.label;" + onclick="openURL(this.getAttribute('homepageURL'));"/> + </content> + </binding> + + <binding id="status-footer-search"> + <content align="stretch"> + <xul:hbox align="center"> + <xul:label class="text-link" xbl:inherits="searchURL=link" anonid="searchLink" + onclick="openURL(this.getAttribute('searchURL'));"/> + <xul:spacer flex="1"/> + <xul:button command="cmd_resetSearch" label="&resetSearch.label;"/> + </xul:hbox> + </content> + + <implementation> + <constructor> + var strings = [this.getAttribute("count")]; + var text = document.getElementById("extensionsStrings") + .getFormattedString("searchResults", strings); + var label = document.getAnonymousElementByAttribute(this, "anonid", "searchLink"); + label.value = text; + </constructor> + </implementation> + </binding> + + <binding id="status-footer-search-empty"> + <content align="center"> + <xul:label value="&searchEmpty.label;"/> + <xul:button command="cmd_resetSearch" label="&searchEmpty.button;"/> + </content> + </binding> + + <binding id="status-retrieve-search"> + <content align="center"> + <xul:hbox align="center" pack="center"> + <xul:image class="addonThrobber"/> + <xul:label value="&searchThrobber.label;"/> + </xul:hbox> + <xul:button command="cmd_resetSearch" label="&cancelSearch.button;"/> + </content> + </binding> + + <binding id="status-retrieve-recommended"> + <content> + <xul:hbox align="center" pack="center"> + <xul:image class="addonThrobber"/> + <xul:label value="&recommendedThrobber.label;"/> + </xul:hbox> + </content> + </binding> <binding id="addon-icon"> <content> <xul:stack class="addonIconStack"> <xul:vbox pack="start" align="start"> <xul:image class="addonIcon" xbl:inherits="src=iconURL"/> </xul:vbox> <xul:vbox pack="start" align="start"> @@ -594,16 +838,114 @@ <xul:hbox class="addon-name-version" xbl:inherits="name, version"/> <xul:progressmeter class="extension-item-progress" xbl:inherits="value=progress"/> <xul:label class="extension-item-status" xbl:inherits="value=status" value="&installWaiting.label;"/> </xul:vbox> </xul:hbox> </content> </binding> + <binding id="search-textbox" extends="xul:box"> + <resources> + <stylesheet src="chrome://global/skin/textbox.css"/> + </resources> + + <content align="center"> + <xul:image class="searchbox-image" xbl:inherits="src=image"/> + <xul:textbox class="plain" + xbl:inherits="emptyText,onfocus,onblur,spellcheck,value,maxlength,disabled,size,readonly,tabindex,accesskey"/> + <xul:button class="searchbox-search" xbl:inherits="disabled" oncommand="this.parentNode.startSearch()"/> + <xul:button class="searchbox-cancel" xbl:inherits="disabled" hidden="true" oncommand="this.parentNode.clearSearch()"/> + </content> + + <implementation> + <field name="textbox"> + document.getAnonymousNodes(this)[1]; + </field> + + <field name="_searchButton"> + document.getAnonymousElementByAttribute(this, "class", "searchbox-search"); + </field> + + <field name="_cancelButton"> + document.getAnonymousElementByAttribute(this, "class", "searchbox-cancel"); + </field> + + <property name="value" onget="return this.textbox.value" + onset="this.textbox.value = val"/> + + <property name="disabled" onget="return this.textbox.disabled"> + <setter> + if (val) + this.setAttribute("disabled", "true"); + else + this.removeAttribute("disabled"); + </setter> + </property> + + <constructor> + if (this.value) { + this._cancelButton.hidden = false; + this._searchButton.hidden = true; + } + </constructor> + + <method name="startSearch"> + <body> + if (this.value) { + this._cancelButton.hidden = false; + this._searchButton.hidden = true; + } + this.setAttribute("value", this.value); + </body> + </method> + + <method name="clearSearch"> + <body> + this.value = ""; + this.setAttribute("value", ""); + this._cancelButton.hidden = true; + this._searchButton.hidden = false; + </body> + </method> + + <method name="_dispatchCommandEvent"> + <parameter name="aEvent"/> + <body> + var event = document.createEvent("commandevent"); + event.initCommandEvent("command", true, true, window, null, + false, false, false, false, aEvent); + this.dispatchEvent(event); + </body> + </method> + </implementation> + + <handlers> + <handler event="keypress" keycode="VK_ENTER"> + if (event.originalTarget == this.textbox.inputField) { + this.startSearch(); + this._dispatchCommandEvent(event); + } + </handler> + + <handler event="keypress" keycode="VK_RETURN"> + if (event.originalTarget == this.textbox.inputField) { + this.startSearch(); + this._dispatchCommandEvent(event); + } + </handler> + + <handler event="input"> + this._cancelButton.hidden = true; + this._searchButton.hidden = false; + </handler> + + </handlers> + </binding> + <!-- based on preferences.xml paneButton --> <binding id="viewbutton" extends="chrome://global/content/bindings/radio.xml#radio"> <resources> <stylesheet src="chrome://mozapps/skin/extensions/extensions.css"/> </resources> <content> <xul:image class="viewButtonIcon" xbl:inherits="src"/> <xul:label class="viewButtonLabel" xbl:inherits="value=label"/>
--- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -99,26 +99,28 @@ <command id="cmd_cancelInstall"/> <command id="cmd_cancelUpgrade"/> <command id="cmd_checkUpdate"/> <command id="cmd_includeUpdate"/> <command id="cmd_installUpdate"/> <command id="cmd_enable"/> <command id="cmd_disable"/> <command id="cmd_useTheme"/> + <command id="cmd_installSearchResult"/> </commandset> <commandset id="globalCommands"> <command id="cmd_installFile" oncommand="installWithFilePicker();"/> <command id="cmd_checkUpdatesAll" oncommand="checkUpdatesAll();"/> <command id="cmd_installUpdatesAll" oncommand="installUpdatesAll();"/> <command id="cmd_continue" oncommand="closeEM();" disabled="true"/> <command id="cmd_close" oncommand="closeEM();"/> <command id="cmd_showUpdateInfo" oncommand="showUpdateInfo();"/> <command id="cmd_hideUpdateInfo" oncommand="hideUpdateInfo();"/> + <command id="cmd_resetSearch" oncommand="resetSearch();"/> </commandset> <vbox id="addonContextMenuPalette" hidden="true"> <menuitem id="menuitem_useTheme" default="true" command="cmd_useTheme" label="&cmd.useTheme.label;" accesskey="&cmd.useTheme.accesskey;"/> <menuitem id="menuitem_options" default="true" command="cmd_options" #ifdef XP_WIN label="&cmd.options.label;" accesskey="&cmd.options.accesskey;"/> @@ -151,16 +153,17 @@ type="checkbox"/> </vbox> <popup id="addonContextMenu" onpopupshowing="return buildContextMenu(event);"/> <stack id="topStackBar"> <radiogroup id="viewGroup" xhtml:role="listbox" persist="last-selected" class="viewSelector chromeclass-toolbar" orient="horizontal"> + <radio id="search-view" label="&search.label;" oncommand="showView('search');" persist="last-selected"/> <radio id="extensions-view" label="&extensions.label;" oncommand="showView('extensions');" persist="last-selected"/> <radio id="themes-view" label="&themes.label;" oncommand="showView('themes');" persist="last-selected"/> <radio id="locales-view" label="&locales.label;" oncommand="showView('locales');" persist="last-selected"/> <radio id="plugins-view" label="&plugins.label;" oncommand="showView('plugins');" persist="last-selected"/> <radio id="updates-view" label="&update.label;" oncommand="showView('updates');"/> <radio id="installs-view" label="&install.label;" oncommand="showView('installs');" hidden="true"/> </radiogroup> <vbox id="progressBox" hidden="true" class="viewSelector" flex="1"> @@ -169,56 +172,67 @@ <image class="addonThrobber"/> <label id="progressStatus" value="&progressStatus.label;"/> </hbox> <progressmeter id="addonsProgress" class="extension-item-progress" flex="1"/> <spacer flex="1"/> </vbox> </stack> <notificationbox id="addonsMsg" flex="1"> - <hbox id="extensionsBox" flex="1"> - <richlistbox id="extensionsView" flex="1" - datasources="rdf:null" context="addonContextMenu" - ondragenter="nsDragAndDrop.dragEnter(event, gExtensionsDNDObserver);" - ondragover="nsDragAndDrop.dragOver(event, gExtensionsDNDObserver);" - ondragdrop="nsDragAndDrop.drop(event, gExtensionsDNDObserver);" - ondblclick="onViewDoubleClick(event);"/> - - <splitter id="themeSplitter" hidden="true" collapse="after" persist="state"/> - - <vbox id="themePreviewArea" hidden="true" width="220" persist="width"> - <deck id="previewImageDeck" flex="1"> - <vbox id="noThemeSelected" pack="center" align="center"> - <label class="previewText">&previewNoThemeSelected.label;</label> - </vbox> - <vbox id="noPreviewImage" pack="center" align="center"> - <label class="previewText">&previewNoPreviewImage.label;</label> - </vbox> - <vbox id="previewImageContainer" align="center" pack="center"> - <description> - <image id="previewImage"/> - </description> - </vbox> - <vbox id="infoNoAddonSelected" align="center" pack="center"> - <label class="previewText">&infoNoAddonSelected.label;</label> - </vbox> - <vbox id="infoNoUpdateInfo" align="center" pack="center"> - <label class="previewText">&infoNoUpdateInfo.label;</label> - </vbox> - <vbox id="infoLoadingInfo" align="center" pack="center"> - <image class="addonThrobber"/> - </vbox> - <vbox id="infoUpdateInfoError" align="center" pack="center"> - <label class="previewText">&infoUpdateInfoError.label;</label> - </vbox> - <vbox id="infoDisplay"> - </vbox> - </deck> - </vbox> - </hbox> + <vbox id="extensionsBox" flex="1"> + <hbox id="searchPanel" align="center"> + <textbox id="searchbox" emptyText="&searchAddons.label;" + oncommand="retrieveRepositoryAddons(this.value);" + persist="value"/> + <spacer flex="1"/> + <label id="browseAddons" class="text-link" value="&browseAddons.label;" + onclick="openURL(this.getAttribute('homepageURL'));"/> + </hbox> + + <hbox flex="1"> + <richlistbox id="extensionsView" flex="1" + datasources="rdf:null" context="addonContextMenu" + ondragenter="nsDragAndDrop.dragEnter(event, gExtensionsDNDObserver);" + ondragover="nsDragAndDrop.dragOver(event, gExtensionsDNDObserver);" + ondragdrop="nsDragAndDrop.drop(event, gExtensionsDNDObserver);" + ondblclick="onViewDoubleClick(event);"/> + + <splitter id="themeSplitter" hidden="true" collapse="after" persist="state"/> + + <vbox id="themePreviewArea" hidden="true" width="220" persist="width"> + <deck id="previewImageDeck" flex="1"> + <vbox id="noThemeSelected" pack="center" align="center"> + <label class="previewText">&previewNoThemeSelected.label;</label> + </vbox> + <vbox id="noPreviewImage" pack="center" align="center"> + <label class="previewText">&previewNoPreviewImage.label;</label> + </vbox> + <vbox id="previewImageContainer" align="center" pack="center"> + <description> + <image id="previewImage"/> + </description> + </vbox> + <vbox id="infoNoAddonSelected" align="center" pack="center"> + <label class="previewText">&infoNoAddonSelected.label;</label> + </vbox> + <vbox id="infoNoUpdateInfo" align="center" pack="center"> + <label class="previewText">&infoNoUpdateInfo.label;</label> + </vbox> + <vbox id="infoLoadingInfo" align="center" pack="center"> + <image class="addonThrobber"/> + </vbox> + <vbox id="infoUpdateInfoError" align="center" pack="center"> + <label class="previewText">&infoUpdateInfoError.label;</label> + </vbox> + <vbox id="infoDisplay"> + </vbox> + </deck> + </vbox> + </hbox> + </vbox> </notificationbox> <vbox> <hbox id="commandBarBottom" align="center"> <button id="installFileButton" label="&cmd.installLocalFile.label;" accesskey="&cmd.installLocalFile.accesskey;" tooltiptextaddons="&cmd.installFileAddon.tooltip;" tooltiptextthemes="&cmd.installFileTheme.tooltip;" command="cmd_installFile"/>
--- a/toolkit/mozapps/extensions/jar.mn +++ b/toolkit/mozapps/extensions/jar.mn @@ -6,9 +6,10 @@ toolkit.jar: content/mozapps/extensions/updateinfo.xsl (content/updateinfo.xsl) content/mozapps/extensions/extensions.css (content/extensions.css) * content/mozapps/extensions/about.xul (content/about.xul) * content/mozapps/extensions/about.js (content/about.js) * content/mozapps/extensions/list.xul (content/list.xul) * content/mozapps/extensions/list.js (content/list.js) * content/mozapps/extensions/update.xul (content/update.xul) * content/mozapps/extensions/update.js (content/update.js) - +* content/mozapps/extensions/eula.xul (content/eula.xul) +* content/mozapps/extensions/eula.js (content/eula.js)
--- a/toolkit/mozapps/extensions/public/Makefile.in +++ b/toolkit/mozapps/extensions/public/Makefile.in @@ -39,12 +39,16 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = extensions XPIDL_MODULE = extensions -XPIDLSRCS = nsIExtensionManager.idl nsIBlocklistService.idl +XPIDLSRCS = \ + nsIExtensionManager.idl \ + nsIBlocklistService.idl \ + nsIAddonRepository.idl \ + $(NULL) include $(topsrcdir)/config/rules.mk
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/public/nsIAddonRepository.idl @@ -0,0 +1,198 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Extension Manager. + * + * The Initial Developer of the Original Code is mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2008 + * 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 ***** */ + + +#include "nsISupports.idl" + +[scriptable, uuid(a549a714-2ada-4bb9-8a47-be26e73d49a5)] +interface nsIAddonSearchResult : nsISupports +{ + /** + * The ID of the add-on + */ + readonly attribute AString id; + + /** + * The name of the add-on + */ + readonly attribute AString name; + + /** + * The version of the add-on + */ + readonly attribute AString version; + + /** + * A short summary of the add-on + */ + readonly attribute AString summary; + + /** + * The full description of the add-on + */ + readonly attribute AString description; + + /** + * The rating of the add-on, 0-10 or -1 if unrated. + */ + readonly attribute long rating; + + /** + * The url of the add-ons icon or empty if there is no icon. + */ + readonly attribute AString iconURL; + + /** + * The url of a thumbnail for the add-on + */ + readonly attribute AString thumbnailURL; + + /** + * The homepage for the add-on + */ + readonly attribute AString homepageURL; + + /** + * A EULA that must be accepted before install. + */ + readonly attribute AString eula; + + /** + * The add-on type (see nsIUpdateItem). + */ + readonly attribute unsigned long type; + + /** + * The url of the xpi for this add-on + */ + readonly attribute AString xpiURL; + + /** + * The hash for the xpi. + */ + readonly attribute AString xpiHash; +}; + +[scriptable, uuid(a6f70917-dd30-4eb6-8b3d-453204f96f33)] +interface nsIAddonSearchResultsCallback : nsISupports +{ + /** + * Called when a search has suceeded. + * + * @param aAddons an array of the add-on results. In the case of + * searching for specific terms the ordering of results + * may be determined by the search provider. + * @param aAddonCount The length of aAddons + * @param aTotalResults The total results actually available in the + * repository + */ + void searchSucceeded([array, size_is(aAddonCount)] in nsIAddonSearchResult aAddons, + in unsigned long aAddonCount, + in unsigned long aTotalResults); + + /** + * Called when an error occured when performing a search. + */ + void searchFailed(); +}; + +/** + * The add-on repository is a source of add-ons that can be installed. It can + * be searched in two ways. One returns a list of add-ons that come highly + * recommended, this list should change frequently. The other way is to + * search for specific search terms entered by the user. Searches are + * asynchronous and results should be passed to the provided callback object + * when complete. The results passed to the callback should only include add-ons + * that are compatible with the current application and are not already + * installed. Searches are always asynchronous and should be passed to the + * callback object provided. + */ +[scriptable, uuid(c4d2ac29-6edc-43cd-8dc8-e4cf213aa1be)] +interface nsIAddonRepository : nsISupports +{ + /** + * The homepage for visiting this repository. This may be null or an empty + * string. + */ + readonly attribute AString homepageURL; + + /** + * Returns whether this instance is currently performing a search. New + * searches will not be performed while this is the case. + */ + readonly attribute boolean isSearching; + + /** + * The url that can be visited to see recommended add-ons in this repository. + */ + AString getRecommendedURL(); + + /** + * Retrieves the url that can be visited to see search results for the given + * terms. + * + * @param aSearchTerms search terms used to search the repository + */ + AString getSearchURL(in AString aSearchTerms); + + /** + * Begins a search for recommended add-ons in this repository. Results will + * be passed to the given callback. + * + * @param aMaxResults the maximum number of results to return + * @param aCallback the callback to pass results to + */ + void retrieveRecommendedAddons(in unsigned long aMaxResults, + in nsIAddonSearchResultsCallback aCallback); + + /** + * Begins a search for add-ons in this repository. Results will be passed to + * the given callback. + * + * @param aSearchTerms the terms to search for + * @param aMaxResults the maximum number of results to return + * @param aCallback the callback to pass results to + */ + void searchAddons(in AString aSearchTerms, in unsigned long aMaxResults, + in nsIAddonSearchResultsCallback aCallback); + + /** + * Cancels the search in progress. If there is no search in progress this + * does nothing. + */ + void cancelSearch(); +};
--- a/toolkit/mozapps/extensions/src/Makefile.in +++ b/toolkit/mozapps/extensions/src/Makefile.in @@ -40,16 +40,20 @@ topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = extensions EXTRA_COMPONENTS = nsExtensionManager.js -EXTRA_PP_COMPONENTS = nsBlocklistService.js +EXTRA_PP_COMPONENTS = \ + nsBlocklistService.js \ + nsAddonRepository.js \ + $(NULL) + GARBAGE += nsExtensionManager.js include $(topsrcdir)/config/rules.mk nsExtensionManager.js: nsExtensionManager.js.in $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/src/nsAddonRepository.js @@ -0,0 +1,367 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Extension Manager. +# +# The Initial Developer of the Original Code is mozilla.org +# Portions created by the Initial Developer are Copyright (C) 2008 +# 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; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons"; +const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL"; +const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url"; +const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL"; +const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url"; + +const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml" + +function AddonSearchResult() { +} + +AddonSearchResult.prototype = { + id: null, + name: null, + version: null, + summary: null, + description: null, + rating: null, + iconURL: null, + thumbnailURL: null, + homepageURL: null, + eula: null, + type: null, + xpiURL: null, + xpiHash: null, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonSearchResult]) +} + +function AddonRepository() { +} + +AddonRepository.prototype = { + // The current set of results + _addons: [], + + // Whether we are currently searching or not + _searching: false, + + // Is this a search for recommended add-ons + _recommended: false, + + // XHR associated with the current request + _request: null, + + // How many times in succession has a search yielded no new results. Stops + // us trying to find more results indefinately + _emptyCount: null, + + // Callback object to notify on completion + _callback: null, + + // The uri to pull results from + _retrieveURI: null, + + // Maximum number of results to return + _maxResults: null, + + get homepageURL() { + return Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter) + .formatURLPref(PREF_GETADDONS_BROWSEADDONS); + }, + + get isSearching() { + return this._searching; + }, + + getRecommendedURL: function() { + var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter); + + return urlf.formatURLPref(PREF_GETADDONS_BROWSERECOMMENDED); + }, + + getSearchURL: function(aSearchTerms) { + var prefs = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter); + + var url = prefs.getCharPref(PREF_GETADDONS_BROWSESEARCHRESULTS); + url = url.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms)); + return urlf.formatURL(url); + }, + + cancelSearch: function() { + this._searching = false; + if (this._request) { + this._request.abort(); + this._request = null; + } + }, + + retrieveRecommendedAddons: function(aMaxResults, aCallback) { + if (this._searching) + return; + + this._searching = true; + this._addons = []; + this._callback = aCallback; + this._recommended = true; + this._emptyCount = 0; + this._maxResults = aMaxResults; + + var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter); + + this._retrieveURI = urlf.formatURLPref(PREF_GETADDONS_GETRECOMMENDED); + this._loadList(); + }, + + searchAddons: function(aSearchTerms, aMaxResults, aCallback) { + if (this._searching) + return; + + this._searching = true; + this._addons = []; + this._callback = aCallback; + this._recommended = false; + this._emptyCount = 0; + this._maxResults = aMaxResults; + + var prefs = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + var urlf = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"] + .getService(Components.interfaces.nsIURLFormatter); + + this._retrieveURI = prefs.getCharPref(PREF_GETADDONS_GETSEARCHRESULTS); + this._retrieveURI = this._retrieveURI.replace(/%TERMS%/g, encodeURIComponent(aSearchTerms)); + this._retrieveURI = urlf.formatURL(this._retrieveURI); + this._loadList(); + }, + + // Posts results to the callback + _reportSuccess: function(aCount) { + this._searching = false; + this._request = null; + this._callback.searchSucceeded(this._addons, this._addons.length, + this._recommended ? -1 : aCount); + }, + + // Notifies the callback of a failure + _reportFailure: function(aEvent) { + this._searching = false; + this._request = null; + this._callback.searchFailed(); + }, + + // Parses an add-on entry from an <addon> element + _parseAddon: function(element) { + var em = Cc["@mozilla.org/extensions/manager;1"]. + getService(Ci.nsIExtensionManager); + var app = Cc["@mozilla.org/xre/app-info;1"]. + getService(Ci.nsIXULAppInfo). + QueryInterface(Ci.nsIXULRuntime); + + var guid = element.getElementsByTagName("guid"); + if (guid.length != 1) + return; + + // Ignore add-ons already seen in the results + for (var i = 0; i < this._addons.length; i++) + if (this._addons[i].id == guid[0].textContent) + return; + + // Ignore installed add-ons + if (em.getItemForID(guid[0].textContent) != null) + return; + + // Ignore sandboxed add-ons + var status = element.getElementsByTagName("status"); + // The status element has a unique id for each status type. 4 is Public. + if (status.length != 1 || status[0].getAttribute("id") != 4) + return; + + // Ignore add-ons not compatible with this OS + var compatible = false; + var os = element.getElementsByTagName("compatible_os"); + var i = 0; + while (i < os.length && !compatible) { + if (os[i].textContent == "ALL" || os[i].textContent == app.OS) { + compatible = true; + break; + } + i++; + } + if (!compatible) + return; + + // Ignore add-ons not compatible with this Application + compatible = false; + var tags = element.getElementsByTagName("compatible_applications"); + if (tags.length != 1) + return; + var vc = Cc["@mozilla.org/xpcom/version-comparator;1"]. + getService(Ci.nsIVersionComparator); + var apps = tags[0].getElementsByTagName("name"); + var i = 0; + while (i < apps.length) { + if (apps[i].textContent.toLowerCase() == app.name.toLowerCase()) { + var minversion = apps[i].parentNode.getElementsByTagName("min_version")[0].textContent; + var maxversion = apps[i].parentNode.getElementsByTagName("max_version")[0].textContent; + if ((vc.compare(minversion, app.version) > 0) || + (vc.compare(app.version, maxversion) > 0)) + return; + compatible = true; + break; + } + i++; + } + if (!compatible) + return; + + var addon = new AddonSearchResult(); + addon.id = guid[0].textContent; + addon.rating = -1; + var node = element.firstChild; + while (node) { + if (node instanceof Ci.nsIDOMElement) { + switch (node.localName) { + case "name": + case "version": + case "summary": + case "description": + case "eula": + addon[node.localName] = node.textContent; + break; + case "rating": + if (node.textContent.length > 0) + addon.rating = parseInt(node.textContent); + break; + case "thumbnail": + addon.thumbnailURL = node.textContent; + break; + case "icon": + addon.iconURL = node.textContent; + break; + case "learnmore": + addon.homepageURL = node.textContent; + break; + case "type": + // The type element has an id attribute that is the id from AMO's + // database. This doesn't match our type values to perform a mapping + if (node.getAttribute("id") == 2) + addon.type = Ci.nsIUpdateItem.TYPE_THEME; + else + addon.type = Ci.nsIUpdateItem.TYPE_EXTENSION; + break; + case "install": + addon.xpiURL = node.textContent; + if (node.hasAttribute("hash")) + addon.xpiHash = node.getAttribute("hash"); + break; + } + } + node = node.nextSibling; + } + + this._addons.push(addon); + }, + + // Called when a single request has completed, parses out any add-ons and + // either notifies the callback or does a new request for more results + _listLoaded: function(aEvent) { + var request = aEvent.target; + var responseXML = request.responseXML; + + if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR || + (request.status != 200 && request.status != 0)) { + this._reportFailure(); + return; + } + var elements = responseXML.documentElement.getElementsByTagName("addon"); + var oldcount = this._addons.length; + for (var i = 0; i < elements.length; i++) { + this._parseAddon(elements[i]); + + var prefs = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + if (this._addons.length == this._maxResults) { + this._reportSuccess(elements.length); + return; + } + } + + // We didn't find any new add-ons this pass + if (oldcount == this._addons.length) + this._emptyCount++; + else + this._emptyCount = 0; + + /* We should keep trying to find new recommended add-ons, but bail if we + don't get a new result for a few passes. */ + if (this._recommended && this._emptyCount < 5) + this._loadList(); + else + this._reportSuccess(elements.length); + }, + + // Performs a new request for results + _loadList: function() { + this._request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + this._request.open("GET", this._retrieveURI, true); + this._request.overrideMimeType("text/xml"); + + var self = this; + this._request.onerror = function(event) { self._reportFailure(event); }; + this._request.onload = function(event) { self._listLoaded(event); }; + this._request.send(null); + }, + + classDescription: "Addon Repository", + contractID: "@mozilla.org/extensions/addon-repository;1", + classID: Components.ID("{8eaaf524-7d6d-4f7d-ae8b-9277b324008d}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonRepository]) +} + +function NSGetModule(aCompMgr, aFileSpec) { + return XPCOMUtils.generateModule([AddonRepository]); +}
--- a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in +++ b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in @@ -6210,23 +6210,21 @@ RDFItemUpdater.prototype = { Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE); return; } LOG("RDFItemUpdater:checkForUpdates sending a request to server for: " + uri.spec + ", item = " + aItem.objectSource); var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. - createInstance(Ci.nsIXMLHttpRequest). - QueryInterface(Ci.nsIJSXMLHttpRequest); + createInstance(Ci.nsIXMLHttpRequest); request.open("GET", uri.spec, true); request.channel.notificationCallbacks = new BadCertHandler(); request.overrideMimeType("text/xml"); request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE; - request.QueryInterface(Ci.nsIJSXMLHttpRequest); var self = this; request.onerror = function(event) { self.onXMLError(event, aItem); }; request.onload = function(event) { self.onXMLLoad(event, aItem); }; request.send(null); }, onXMLLoad: function(aEvent, aItem) {
--- a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css @@ -259,16 +259,141 @@ richlistitem[loading="true"] .updateBadg font-weight: normal; border: none; } richlistitem[opType="needs-uninstall"] .notifyBadge { display: none; } +.addon-search-details { + margin-top: 5px; + margin-bottom: 5px; + -moz-margin-start: 6px; + -moz-margin-end: 0; +} + +.addonThumbnailContainer { + background: window; + padding: 5px; + border: 2px solid ActiveBorder; + width: 135px; + min-height: 104px; + -moz-margin-end: 5px; +} + +.addonMissingThumbnail { + color: GrayText; +} + +.addonFailure { + width: 16px; + height: 16px; + list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png"); + -moz-image-region: rect(0px 48px 16px 32px); +} + +.addonRating { + display: none; +} + +.addonLearnMore { + margin-top: 4px; + margin-bottom: 4px; +} + +.addonRating[rating] { + display: -moz-box; + width: 68px; + height: 12px; + list-style-image: url("chrome://mozapps/skin/extensions/ratings.png"); +} + +.addonRating[rating="0"] { + -moz-image-region: rect(0px 68px 12px 0px); +} + +.addonRating[rating="1"], .addonRating[rating="2"] { + -moz-image-region: rect(12px 68px 24px 0px); +} + +.addonRating[rating="3"], .addonRating[rating="4"] { + -moz-image-region: rect(24px 68px 36px 0px); +} + +.addonRating[rating="5"], .addonRating[rating="6"] { + -moz-image-region: rect(36px 68px 48px 0px); +} + +.addonRating[rating="7"], .addonRating[rating="8"] { + -moz-image-region: rect(48px 68px 60px 0px); +} + +.addonRating[rating="9"], .addonRating[rating="10"] { + -moz-image-region: rect(60px 68px 72px 0px); +} + +.addonType image { + -moz-margin-start: 6px; + list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png"); + width: 16px; + height: 16px; +} + +.addonTypeExtension image { + -moz-image-region: rect(0px 16px 16px 0px); +} + +.addonTypeTheme image { + -moz-image-region: rect(0px 32px 16px 16px); +} + +vbox[typeName="status"][type="search-failure"], +vbox[typeName="status"][type="recommended-failure"], +vbox[typeName="status"][type="retrieve-search"], +vbox[typeName="status"][type="retrieve-recommended"] { + margin-top: 2em; +} + +vbox[typeName="status"][type="footer-recommended"], +vbox[typeName="status"][type="footer-search"] { + margin-top: 1em; +} + +vbox[typeName="status"][type="header-recommended"] { + font-size: 150%; + background: -moz-dialog; +} + +#searchbox { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 19px 19px 0px); +} + +.searchbox-search, .searchbox-cancel { + -moz-appearance: none; + cursor: pointer; + margin: 0; + border: 0; + padding: 0; + width: 19px; + height: 19px; + min-width: 19px; +} + +.searchbox-search { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 38px 19px 19px); +} + +.searchbox-cancel { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 57px 19px 38px); +} + #progressBox { padding: 5px 5px 5px 5px; } #progressBox > hbox { -moz-box-align: center; } @@ -334,16 +459,23 @@ radio#updates-view:hover, radio#updates- radio#installs-view { -moz-image-region: rect(0px, 192px, 32px, 160px) } radio#installs-view:hover, radio#installs-view[selected="true"] { -moz-image-region: rect(32px, 192px, 64px, 160px) } +radio#search-view { + -moz-image-region: rect(0px, 224px, 32px, 192px) +} +radio#search-view:hover, radio#search-view[selected="true"] { + -moz-image-region: rect(32px, 224px, 64px, 192px) +} + /* Update view checkbox */ .includeUpdate { -moz-user-focus: none; } richlistitem[selected="true"] .includeUpdate { -moz-user-focus: normal; }
new file mode 100644 --- /dev/null +++ b/toolkit/themes/pinstripe/mozapps/extensions/eula.css @@ -0,0 +1,16 @@ +#heading { + font-size: 120%; +} + +#scrollbox { + overflow-y: auto; + background-color: window; + border: 1px solid ActiveBorder; + margin: 1em 0 1em 0; + padding: 5px; +} + +#eula { + font-family: monospace; + white-space: -moz-pre-wrap; +}
new file mode 100644 index 0000000000000000000000000000000000000000..ca7f72ed45241fff11c0a8d47f69104738cb840b GIT binary patch literal 1375 zc$@)W1)%zgP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3=|9$0g2bOSpWb8@kvBMR7l6g zm3wHGRUF4Z=Xsv@Hs7hc^*Y_0wJcFbnxZnJ?4m_w*aaCyW>%I-p(wS)O3V}^D-F>K zu`IigTA7(v=wj2_nx&b}VY=z2TQ2AJZhJ4!b1wa{`DXqR6hQ|LoWt)Nz8}87-|rmw zpNF_`vtrM*;sN1;-9bC#cPtyrvXEM0w4tW;!1blqepL2vM2>BJ>F&=LKh-#=n#zk~ z)?aBvUb?T3FF!nxfxQQjpI$&-xPa_n1WP!@|3-Yv(z~YAm+#z=%o{!RlaD{J|JDJ3 zm3tn3dPru`vdmD1Fo0Ap)y-93Jvsi(oBxUM#<$;iz)1JOk->wbzgf3kj9s#ku2`Jk zTiY7i+M}}`ojG%N3Sj;x_e}8F_5>jWAhM;Alcx;5VW8BCs)qfhEgo9|bj3Wk?3Ft= z{cRdHu3vL+MrQVs!YhVGbW;_lHax>?M@o2n*?fw;UkK&qQr!~OH8qDO&z=2PnFG+$ z6`OEXzuwPZo_!@?<Jb;oyIK$khF)@+&_>U<Fzh(;6F}MY4Yw{3!rHt3{%t9k?OQiR zVjUgphmRb6-RUMt=@YNBs-%G*YaODITk$j`7R@H^4>Dp)rFE?C<g36)2jJ|vP7>Jx zcGOg>Vf_Z!+3CH(blal=Z8VmEwyv&TC2!uaGc(Pd)E-kQ2e)tC6wylUzUj6p1&-sR zuKo~@OcEU4_5~wmt!3&h#Uv!eOIMSW;KiU%M&H4yB22o{-B;Io=y-Gc@K1j_*wLP7 zBO&7?wTr8gc*-T3jMCl}pVvRjnk2QPGvTFd?~Jyu9Y0}W!NU*DV*0d)7!YozC{)8S zHHyNCCEel@zP}8n_MJ^-M+HJy0>A<Idfwipz*2zX<zr-X^bD@2v27cH!IctEORnti z7YR?2RFb%qDS+&p$c;XqAB;gNg=*YK`IZVsy;y-~9O4oZ5|Rp(0ZTwM<|b1B7Y?H# z=EhMPP+&}V3Z&Kup-Cu(HU`X}4Wx}R0PEga$ElNbbbPr6-{hqP`xVle@bK{ASLKX! z3@dZ?lPltEZ`%BR3gGI+qZj37IHmP1YEDMj@^vO$EDJ0I8jYtw2+iKw#L1!g{`_!I z2rwyt*0U`ut1Bz!Ml!=z;^<3M9<s@rbY0z!kKcMPJu@;fXYjajMd8QD3HczjkMGLA zZA$N(voHnVO1L!Q1VRC^CM#?cmy+6M6*WRAZBY4r$ja_*iEzOF{MXa6VNk?g<|$G| zF!jC}&u#l`^Qyz;A0A0}cZEE48;z$=H>8C#u8GIp!3ib!EwGw&JHQX+I~$XpzP<F( zlD5C3KxxS6WnqM&_LOYsI1?XsV8zcd!02UT-bxEtGl1W#7FI16ua2)X(0F0$I;!6- z3}FQ;c3E=JEroXB@Uho=T9B1Dn1IjXMD1_7?)cHomJ^)w>=V=4&fiM`_#J_u+t{K4 zU;aT@2h6<s#W8mjMw|!xqzU|C#{zm)&fT-);@Vlg;nVzJ*jl7~xn;Rdcu2hM%xWhP zbWR?q@aj(--`0BW+^V%Nzp$@I;=EKhK*-1UyMB(pB7`t>M@(ACst5c&i>AYk@@1ur z74VrVpcfG6(T$$xuQ<Uspo4HKuzUZ4C{PTXfBO~S0eL_YNB}Xw760L{Dtg`(yJuP# ho$2W-RL|M~<`2~8HIHbijvxR4002ovPDHLkV1fnqrO^NY
--- a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css @@ -215,16 +215,146 @@ richlistitem[loading="true"] .updateBadg .attention { -moz-box-pack: end; } richlistitem[opType="needs-uninstall"] .notifyBadge { display: none; } +.addon-search-details { + margin-top: 5px; + margin-bottom: 5px; + -moz-margin-start: 6px; + -moz-margin-end: 0; +} + +.addonThumbnailContainer { + background: window; + padding: 5px; + border: 2px solid ActiveBorder; + width: 135px; + min-height: 104px; + -moz-margin-end: 5px; +} + +.addonMissingThumbnail { + color: GrayText; +} + +.addonFailure { + width: 16px; + height: 16px; + list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png"); + -moz-image-region: rect(0px 48px 16px 32px); +} + +.addonRating { + display: none; +} + +.addonLearnMore { + margin-top: 4px; + margin-bottom: 4px; +} + +.addonRating[rating] { + display: -moz-box; + width: 68px; + height: 12px; + list-style-image: url("chrome://mozapps/skin/extensions/ratings.png"); +} + +.addonRating[rating="0"] { + -moz-image-region: rect(0px 68px 12px 0px); +} + +.addonRating[rating="1"], .addonRating[rating="2"] { + -moz-image-region: rect(12px 68px 24px 0px); +} + +.addonRating[rating="3"], .addonRating[rating="4"] { + -moz-image-region: rect(24px 68px 36px 0px); +} + +.addonRating[rating="5"], .addonRating[rating="6"] { + -moz-image-region: rect(36px 68px 48px 0px); +} + +.addonRating[rating="7"], .addonRating[rating="8"] { + -moz-image-region: rect(48px 68px 60px 0px); +} + +.addonRating[rating="9"], .addonRating[rating="10"] { + -moz-image-region: rect(60px 68px 72px 0px); +} + +.addonType image { + -moz-margin-start: 6px; + list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png"); + width: 16px; + height: 16px; +} + +.addonTypeExtension image { + -moz-image-region: rect(0px 16px 16px 0px); +} + +.addonTypeTheme image { + -moz-image-region: rect(0px 32px 16px 16px); +} + +vbox[typeName="status"][type="search-failure"], +vbox[typeName="status"][type="recommended-failure"], +vbox[typeName="status"][type="retrieve-search"], +vbox[typeName="status"][type="retrieve-recommended"] { + margin-top: 2em; +} + +vbox[typeName="status"][type="footer-recommended"], +vbox[typeName="status"][type="footer-search"] { + margin-top: 1em; +} + +vbox[typeName="status"][type="header-recommended"] { + font-size: 150%; + background: -moz-dialog; +} + +#searchPanel { + border-bottom: 1px solid #878787; +} + +#searchbox { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 19px 19px 0px); +} + +.searchbox-search, .searchbox-cancel { + -moz-appearance: none; + cursor: pointer; + margin: 0; + border: 0; + padding: 0; + width: 19px; + height: 19px; + min-width: 19px; + min-height: 19px; +} + +.searchbox-search { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 38px 19px 19px); +} + +.searchbox-cancel { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 57px 19px 38px); +} + #progressBox { padding: 5px 5px 5px 5px; } #progressBox > hbox { -moz-box-align: center; }
new file mode 100644 index 0000000000000000000000000000000000000000..986f999c78f12f8e71ec20755252d8df1f6d447f GIT binary patch literal 568 zc%17D@N?(olHy`uVBq!ia0vp^E<o(T!3-oT_!s{MQjEnx?oJHr&dIz4a$*B~LR|m< z|9|GpnNOcSeV)kh_3PI=ckcZ8^XJ;NYnLuv`uX$cn>TOn-@pI#>C-=t_y2u$9tgf~ z3HbNpJrF#4^yuZwmtU6|{QY?U*R}aSb|w5gQU7(m!j~BWzwfO7b-w?{!GgoWVLl*R zOM?7@862M7NCR>>3p^r=85p>QL70(Y)*K0-AbW|YuPgg?9uaO%{q;(R_<?#tJY5_^ zA`YLud^+!tfk10wj5-S!ujlQ8*H8b-Pwq*acroM54<D8}tyASS<6LUGkJd!z&2g;0 zZ`mBy?jOIWmwTbyc`oi-<vZ3!CQl4o8<amSW69c;+3ri;``E9MDg1lx_9{+Mxh-w; zw#Qd2`G4<8OVY8g$0gs$)TM2>d^CRk-2Ottvd1?cmHm5^d-z`Ba@*zBeS1%zoA>U; zoHD+WlQ%P$cP}sNSbP4Q!~6OFYRi6|{25dx;BknL$@7Mzf}u5sN+Nrs%3FmAOJrFl zZD@87LP**k=@SgP2f>r_j&o?{ALn@4C#$@~x~ylyCp0XV@7^Ag0Kq9+7CUQg0OE~M uya5$gZITsR0Y)8QaswExfTOvYF$_<(*-vRdB6S%U<P4s!elF{r5}E)fj|-gu
new file mode 100644 index 0000000000000000000000000000000000000000..68863e2bed5b3c00d8ddb44907a7c32d5244cade GIT binary patch literal 1540 zc$@(Q2K)JmP)<h;3K|Lk000e1NJLTq0021v000vR0ssI2rA;z|00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3?B>$8Y|zIr~m*3mPtfGR7l6g zmQ83>R}{z3{d(ghF=Na)$#llpq|nqXMp2@mSb~D?x{zSojTEJnmLlD%?M7^=1wl)t z3yEYQG+i|iA#Q34L6I`H2}L`t&4&{;nbf2+m1dmx?m4H68)J;mEF!dL_1?WR@BZ$& z|MR~`Ff+VBFf#)K0}%lLF)f}IAQl3|{OgL>(k}u^OpekKm*>j&?%%J9M&t3g5JE^H zg<Sjq5`baFxFM|H=e{k@JwU|9+LvE%ePfx7I#6P&JgW5d^<BDj$(>R%nLK##U~_Y` zqm)W&rG*d@BuEgjLRPut-M5cQb2G`B4awT3slW2eoj$MwMT<Q>J$-$BQp&crwuXj= z$;rvnr%&hedB5TZL4X#6AYf)9f)`NS(!6?l6%!)|kAg?BXdIS@Wk!n=6BAdiT!}`b zCr+G5r_)4q@#4k){{G(H-b05DX|0)A8!fERMiY^gav4QEZ}tgm@6gx8L`X=4h|Hj+ znV22x{^1L2Eh4_R?~|pUCUU7A85se9j*gCWIt>6)%FfQt{{H^4v9WTw?0KFmyZJiP z;R48Jv)8U&d+^{vG#YJcYTCble>55mXC<bF>Ly}xG9+dONY<?<CT9Be&M%=0phd^w z(4UMWGBYuk%Vjq%m2|;jA~^fy+1Z)dAP4~9;lqcyT<+AVQ^$@S^E@v^uZZNZX8xJX z&-|XB$xY1sHdz{<EZzYSXl>|`F)$*gQYmI07#J8G9cAVq2zq*Y03e-8RVo$V_kG_- zi*5r<OIfd8z3Lfn_wL;_H8pNz)22=B?d@mIoUu=@s~0knpZOzdYSvV*E6x4Ij7Ui2 z6C(lDR@a^U=0j_(MLhY=`6r4cX68g9v1`|^!NI}f$B#ERH_y(_7K=rtRQtB}APAIF zN-1lt)*2C|dP<Jg+JS+Axw*N9hK3_Yj&xn<>gec*)yBHJyOmP6Zr#dcGD}(C+Wrv$ zTp0MQHo68WAQwyoR47g)6RC#yOAPem(ACAYHGdZ{@7=psDK$7a=u#`C+_rsNbyc<R z`&w%TKtyN5OC(S(mtFF**{l%agZDp}o}NB`{=Dz|LWn}4@LV52w8Vr+#7IoUM1-gi ziJa`L#ZaS_QtsTjGn2^_3Wa<=fBpLP@v-rZuWfWYA)J@>vv*`<RRj?MKnT(DN=va= zjKyMgb#;Y8K}uOwRTaY8HSj4~td7(Y5t1b!a?DKitCQn*N2mXs3apRx6zjtM>FP#n zy>{)|s;a6RH*QQ#O_fTeL?YqJP6#od*QY~CwR-jHQmJ&{%>%L8*tv7(GMUWb!-u=N zx=N)|JRT3ztHlk8R|vT9_ucjNuM)U2phEEuGfz$5`~2PRrTG*90;H5mDItZB(imf{ zW#+cFw&CI7TrQVPCWVyRnE95#{cAC+Qk|WhmoH!L{jS&deP+&Pv$t>GZftBUl}fFx ztsy=Tphcju>6`U6jd!Q>!@2Cww|=~HZ!BJuLPQ(p15!vSq+>POL_E(JV>~YsiMWCo z86Ay8BAzkwapI+v&stVXOUvfXn=6&dLZ9IKez{yedh}?xzuF*RqV@4sB>Z!>@bTMU z0l=}YLoX##7A;Z;>*Av~#vmdh3L&g9#+ZHk_60%U=(W~bYxn!mUSP+L9j&dcLqkL3 z<Kyvl@pLM^Wy_XlTbu>YvNuPDk<eNTEFN#bUjJ!ONmCt(g|dPGr4%!3tv%0kMW&R} zT5sLDRS2P!(#Ck6=bUT4H4KaT`uaV4_AK8Ih!yv*#KgqTjQqg2fenJdK8}UYdY)&D zahq`ojzl8=b^j_rfCR}B06gQlPQXb5ADfhvQV8Kh;%0^XKYDE;1TaV?-NAa`p+q4B qGrL-JRyBY6QfRHElq+st0pMSe+h-J1q4hNY0000<MNUMnLSTXjZ|)QT
--- a/toolkit/themes/pinstripe/mozapps/jar.mn +++ b/toolkit/themes/pinstripe/mozapps/jar.mn @@ -8,20 +8,24 @@ classic.jar: skin/classic/mozapps/downloads/unknownContentType.css (downloads/unknownContentType.css) skin/classic/mozapps/extensions/extensionItem.png (extensions/extensionItem.png) skin/classic/mozapps/extensions/itemDisabledFader.png (extensions/itemDisabledFader.png) skin/classic/mozapps/extensions/itemEnabledFader.png (extensions/itemEnabledFader.png) skin/classic/mozapps/extensions/notifyBadges.png (extensions/notifyBadges.png) skin/classic/mozapps/extensions/question.png (extensions/question.png) skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png) skin/classic/mozapps/extensions/viewButtons.png (extensions/viewButtons.png) + skin/classic/mozapps/extensions/ratings.png (extensions/ratings.png) + skin/classic/mozapps/extensions/extensionIcons.png (extensions/extensionIcons.png) + skin/classic/mozapps/extensions/searchIcons.png (extensions/searchIcons.png) skin/classic/mozapps/extensions/about.css (extensions/about.css) skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css) skin/classic/mozapps/extensions/extensions.xml (extensions/extensions.xml) skin/classic/mozapps/extensions/update.css (extensions/update.css) + skin/classic/mozapps/extensions/eula.css (extensions/eula.css) skin/classic/mozapps/plugins/missingPlugin.css (plugins/missingPlugin.css) skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png) skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png) skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css) skin/classic/mozapps/shared/itemFader.png (shared/itemFader.png) skin/classic/mozapps/shared/itemSelected.png (shared/itemSelected.png) skin/classic/mozapps/shared/viewFader.png (shared/viewFader.png) skin/classic/mozapps/shared/richview.css (shared/richview.css)
new file mode 100644 --- /dev/null +++ b/toolkit/themes/winstripe/mozapps/extensions/eula.css @@ -0,0 +1,16 @@ +#heading { + font-size: 120%; +} + +#scrollbox { + overflow-y: auto; + background-color: window; + border: 1px solid ActiveBorder; + margin: 1em 0 1em 0; + padding: 5px; +} + +#eula { + font-family: monospace; + white-space: -moz-pre-wrap; +}
new file mode 100644 index 0000000000000000000000000000000000000000..ca7f72ed45241fff11c0a8d47f69104738cb840b GIT binary patch literal 1375 zc$@)W1)%zgP)<h;3K|Lk000e1NJLTq001BW000mO1^@s6cL04^00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3=|9$0g2bOSpWb8@kvBMR7l6g zm3wHGRUF4Z=Xsv@Hs7hc^*Y_0wJcFbnxZnJ?4m_w*aaCyW>%I-p(wS)O3V}^D-F>K zu`IigTA7(v=wj2_nx&b}VY=z2TQ2AJZhJ4!b1wa{`DXqR6hQ|LoWt)Nz8}87-|rmw zpNF_`vtrM*;sN1;-9bC#cPtyrvXEM0w4tW;!1blqepL2vM2>BJ>F&=LKh-#=n#zk~ z)?aBvUb?T3FF!nxfxQQjpI$&-xPa_n1WP!@|3-Yv(z~YAm+#z=%o{!RlaD{J|JDJ3 zm3tn3dPru`vdmD1Fo0Ap)y-93Jvsi(oBxUM#<$;iz)1JOk->wbzgf3kj9s#ku2`Jk zTiY7i+M}}`ojG%N3Sj;x_e}8F_5>jWAhM;Alcx;5VW8BCs)qfhEgo9|bj3Wk?3Ft= z{cRdHu3vL+MrQVs!YhVGbW;_lHax>?M@o2n*?fw;UkK&qQr!~OH8qDO&z=2PnFG+$ z6`OEXzuwPZo_!@?<Jb;oyIK$khF)@+&_>U<Fzh(;6F}MY4Yw{3!rHt3{%t9k?OQiR zVjUgphmRb6-RUMt=@YNBs-%G*YaODITk$j`7R@H^4>Dp)rFE?C<g36)2jJ|vP7>Jx zcGOg>Vf_Z!+3CH(blal=Z8VmEwyv&TC2!uaGc(Pd)E-kQ2e)tC6wylUzUj6p1&-sR zuKo~@OcEU4_5~wmt!3&h#Uv!eOIMSW;KiU%M&H4yB22o{-B;Io=y-Gc@K1j_*wLP7 zBO&7?wTr8gc*-T3jMCl}pVvRjnk2QPGvTFd?~Jyu9Y0}W!NU*DV*0d)7!YozC{)8S zHHyNCCEel@zP}8n_MJ^-M+HJy0>A<Idfwipz*2zX<zr-X^bD@2v27cH!IctEORnti z7YR?2RFb%qDS+&p$c;XqAB;gNg=*YK`IZVsy;y-~9O4oZ5|Rp(0ZTwM<|b1B7Y?H# z=EhMPP+&}V3Z&Kup-Cu(HU`X}4Wx}R0PEga$ElNbbbPr6-{hqP`xVle@bK{ASLKX! z3@dZ?lPltEZ`%BR3gGI+qZj37IHmP1YEDMj@^vO$EDJ0I8jYtw2+iKw#L1!g{`_!I z2rwyt*0U`ut1Bz!Ml!=z;^<3M9<s@rbY0z!kKcMPJu@;fXYjajMd8QD3HczjkMGLA zZA$N(voHnVO1L!Q1VRC^CM#?cmy+6M6*WRAZBY4r$ja_*iEzOF{MXa6VNk?g<|$G| zF!jC}&u#l`^Qyz;A0A0}cZEE48;z$=H>8C#u8GIp!3ib!EwGw&JHQX+I~$XpzP<F( zlD5C3KxxS6WnqM&_LOYsI1?XsV8zcd!02UT-bxEtGl1W#7FI16ua2)X(0F0$I;!6- z3}FQ;c3E=JEroXB@Uho=T9B1Dn1IjXMD1_7?)cHomJ^)w>=V=4&fiM`_#J_u+t{K4 zU;aT@2h6<s#W8mjMw|!xqzU|C#{zm)&fT-);@Vlg;nVzJ*jl7~xn;Rdcu2hM%xWhP zbWR?q@aj(--`0BW+^V%Nzp$@I;=EKhK*-1UyMB(pB7`t>M@(ACst5c&i>AYk@@1ur z74VrVpcfG6(T$$xuQ<Uspo4HKuzUZ4C{PTXfBO~S0eL_YNB}Xw760L{Dtg`(yJuP# ho$2W-RL|M~<`2~8HIHbijvxR4002ovPDHLkV1fnqrO^NY
--- a/toolkit/themes/winstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/winstripe/mozapps/extensions/extensions.css @@ -258,16 +258,148 @@ richlistitem[loading="true"] .updateBadg font-weight: normal; border: none; } richlistitem[opType="needs-uninstall"] .notifyBadge { display: none; } +.addon-search-details { + margin-top: 5px; + margin-bottom: 5px; + -moz-margin-start: 6px; + -moz-margin-end: 0; +} + +.addonThumbnailContainer { + background: window; + padding: 5px; + border: 2px solid ActiveBorder; + width: 135px; + min-height: 104px; + -moz-margin-end: 5px; +} + +.addonMissingThumbnail { + color: GrayText; +} + +.addonFailure { + width: 16px; + height: 16px; + list-style-image: url("chrome://mozapps/skin/extensions/notifyBadges.png"); + -moz-image-region: rect(0px 48px 16px 32px); +} + +.addonRating { + display: none; +} + +.addonLearnMore { + margin-top: 4px; + margin-bottom: 4px; +} + +.addonRating[rating] { + display: -moz-box; + width: 68px; + height: 12px; + list-style-image: url("chrome://mozapps/skin/extensions/ratings.png"); +} + +.addonRating[rating="0"] { + -moz-image-region: rect(0px 68px 12px 0px); +} + +.addonRating[rating="1"], .addonRating[rating="2"] { + -moz-image-region: rect(12px 68px 24px 0px); +} + +.addonRating[rating="3"], .addonRating[rating="4"] { + -moz-image-region: rect(24px 68px 36px 0px); +} + +.addonRating[rating="5"], .addonRating[rating="6"] { + -moz-image-region: rect(36px 68px 48px 0px); +} + +.addonRating[rating="7"], .addonRating[rating="8"] { + -moz-image-region: rect(48px 68px 60px 0px); +} + +.addonRating[rating="9"], .addonRating[rating="10"] { + -moz-image-region: rect(60px 68px 72px 0px); +} + +.addonType image { + -moz-margin-start: 6px; + list-style-image: url("chrome://mozapps/skin/extensions/extensionIcons.png"); + width: 16px; + height: 16px; +} + +.addonTypeExtension image { + -moz-image-region: rect(0px 16px 16px 0px); +} + +.addonTypeTheme image { + -moz-image-region: rect(0px 32px 16px 16px); +} + +vbox[typeName="status"][type="search-failure"], +vbox[typeName="status"][type="recommended-failure"], +vbox[typeName="status"][type="retrieve-search"], +vbox[typeName="status"][type="retrieve-recommended"] { + margin-top: 2em; +} + +vbox[typeName="status"][type="footer-recommended"], +vbox[typeName="status"][type="footer-search"] { + margin-top: 1em; +} + +vbox[typeName="status"][type="header-recommended"] { + font-size: 150%; + background: -moz-dialog; +} + +#searchbox { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 19px 19px 0px); + padding: 0; +} + +.searchbox-search, .searchbox-cancel { + -moz-appearance: none; + cursor: pointer; + margin: 0; + border: 0; + padding: 0; + width: 19px; + height: 19px; + min-width: 19px; +} + +.searchbox-search .button-box, +.searchbox-cancel .button-box { + border: 0px; + padding: 0px; +} + +.searchbox-search { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 38px 19px 19px); +} + +.searchbox-cancel { + list-style-image: url(chrome://mozapps/skin/extensions/searchIcons.png); + -moz-image-region: rect(0px 57px 19px 38px); +} + #progressBox { padding: 5px 5px 5px 5px; } #progressBox > hbox { -moz-box-align: center; } @@ -340,16 +472,23 @@ radio#updates-view:hover, radio#updates- radio#installs-view { -moz-image-region: rect(0px, 192px, 32px, 160px) } radio#installs-view:hover, radio#installs-view[selected="true"] { -moz-image-region: rect(32px, 192px, 64px, 160px) } +radio#search-view { + -moz-image-region: rect(0px, 224px, 32px, 192px) +} +radio#search-view:hover, radio#search-view[selected="true"] { + -moz-image-region: rect(32px, 224px, 64px, 192px) +} + /* Update view checkbox */ .includeUpdate { -moz-user-focus: none; } richlistitem[selected="true"] .includeUpdate { -moz-user-focus: normal; }
new file mode 100644 index 0000000000000000000000000000000000000000..986f999c78f12f8e71ec20755252d8df1f6d447f GIT binary patch literal 568 zc%17D@N?(olHy`uVBq!ia0vp^E<o(T!3-oT_!s{MQjEnx?oJHr&dIz4a$*B~LR|m< z|9|GpnNOcSeV)kh_3PI=ckcZ8^XJ;NYnLuv`uX$cn>TOn-@pI#>C-=t_y2u$9tgf~ z3HbNpJrF#4^yuZwmtU6|{QY?U*R}aSb|w5gQU7(m!j~BWzwfO7b-w?{!GgoWVLl*R zOM?7@862M7NCR>>3p^r=85p>QL70(Y)*K0-AbW|YuPgg?9uaO%{q;(R_<?#tJY5_^ zA`YLud^+!tfk10wj5-S!ujlQ8*H8b-Pwq*acroM54<D8}tyASS<6LUGkJd!z&2g;0 zZ`mBy?jOIWmwTbyc`oi-<vZ3!CQl4o8<amSW69c;+3ri;``E9MDg1lx_9{+Mxh-w; zw#Qd2`G4<8OVY8g$0gs$)TM2>d^CRk-2Ottvd1?cmHm5^d-z`Ba@*zBeS1%zoA>U; zoHD+WlQ%P$cP}sNSbP4Q!~6OFYRi6|{25dx;BknL$@7Mzf}u5sN+Nrs%3FmAOJrFl zZD@87LP**k=@SgP2f>r_j&o?{ALn@4C#$@~x~ylyCp0XV@7^Ag0Kq9+7CUQg0OE~M uya5$gZITsR0Y)8QaswExfTOvYF$_<(*-vRdB6S%U<P4s!elF{r5}E)fj|-gu
new file mode 100644 index 0000000000000000000000000000000000000000..68863e2bed5b3c00d8ddb44907a7c32d5244cade GIT binary patch literal 1540 zc$@(Q2K)JmP)<h;3K|Lk000e1NJLTq0021v000vR0ssI2rA;z|00001b5ch_0Itp) z=>Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RS3?B>$8Y|zIr~m*3mPtfGR7l6g zmQ83>R}{z3{d(ghF=Na)$#llpq|nqXMp2@mSb~D?x{zSojTEJnmLlD%?M7^=1wl)t z3yEYQG+i|iA#Q34L6I`H2}L`t&4&{;nbf2+m1dmx?m4H68)J;mEF!dL_1?WR@BZ$& z|MR~`Ff+VBFf#)K0}%lLF)f}IAQl3|{OgL>(k}u^OpekKm*>j&?%%J9M&t3g5JE^H zg<Sjq5`baFxFM|H=e{k@JwU|9+LvE%ePfx7I#6P&JgW5d^<BDj$(>R%nLK##U~_Y` zqm)W&rG*d@BuEgjLRPut-M5cQb2G`B4awT3slW2eoj$MwMT<Q>J$-$BQp&crwuXj= z$;rvnr%&hedB5TZL4X#6AYf)9f)`NS(!6?l6%!)|kAg?BXdIS@Wk!n=6BAdiT!}`b zCr+G5r_)4q@#4k){{G(H-b05DX|0)A8!fERMiY^gav4QEZ}tgm@6gx8L`X=4h|Hj+ znV22x{^1L2Eh4_R?~|pUCUU7A85se9j*gCWIt>6)%FfQt{{H^4v9WTw?0KFmyZJiP z;R48Jv)8U&d+^{vG#YJcYTCble>55mXC<bF>Ly}xG9+dONY<?<CT9Be&M%=0phd^w z(4UMWGBYuk%Vjq%m2|;jA~^fy+1Z)dAP4~9;lqcyT<+AVQ^$@S^E@v^uZZNZX8xJX z&-|XB$xY1sHdz{<EZzYSXl>|`F)$*gQYmI07#J8G9cAVq2zq*Y03e-8RVo$V_kG_- zi*5r<OIfd8z3Lfn_wL;_H8pNz)22=B?d@mIoUu=@s~0knpZOzdYSvV*E6x4Ij7Ui2 z6C(lDR@a^U=0j_(MLhY=`6r4cX68g9v1`|^!NI}f$B#ERH_y(_7K=rtRQtB}APAIF zN-1lt)*2C|dP<Jg+JS+Axw*N9hK3_Yj&xn<>gec*)yBHJyOmP6Zr#dcGD}(C+Wrv$ zTp0MQHo68WAQwyoR47g)6RC#yOAPem(ACAYHGdZ{@7=psDK$7a=u#`C+_rsNbyc<R z`&w%TKtyN5OC(S(mtFF**{l%agZDp}o}NB`{=Dz|LWn}4@LV52w8Vr+#7IoUM1-gi ziJa`L#ZaS_QtsTjGn2^_3Wa<=fBpLP@v-rZuWfWYA)J@>vv*`<RRj?MKnT(DN=va= zjKyMgb#;Y8K}uOwRTaY8HSj4~td7(Y5t1b!a?DKitCQn*N2mXs3apRx6zjtM>FP#n zy>{)|s;a6RH*QQ#O_fTeL?YqJP6#od*QY~CwR-jHQmJ&{%>%L8*tv7(GMUWb!-u=N zx=N)|JRT3ztHlk8R|vT9_ucjNuM)U2phEEuGfz$5`~2PRrTG*90;H5mDItZB(imf{ zW#+cFw&CI7TrQVPCWVyRnE95#{cAC+Qk|WhmoH!L{jS&deP+&Pv$t>GZftBUl}fFx ztsy=Tphcju>6`U6jd!Q>!@2Cww|=~HZ!BJuLPQ(p15!vSq+>POL_E(JV>~YsiMWCo z86Ay8BAzkwapI+v&stVXOUvfXn=6&dLZ9IKez{yedh}?xzuF*RqV@4sB>Z!>@bTMU z0l=}YLoX##7A;Z;>*Av~#vmdh3L&g9#+ZHk_60%U=(W~bYxn!mUSP+L9j&dcLqkL3 z<Kyvl@pLM^Wy_XlTbu>YvNuPDk<eNTEFN#bUjJ!ONmCt(g|dPGr4%!3tv%0kMW&R} zT5sLDRS2P!(#Ck6=bUT4H4KaT`uaV4_AK8Ih!yv*#KgqTjQqg2fenJdK8}UYdY)&D zahq`ojzl8=b^j_rfCR}B06gQlPQXb5ADfhvQV8Kh;%0^XKYDE;1TaV?-NAa`p+q4B qGrL-JRyBY6QfRHElq+st0pMSe+h-J1q4hNY0000<MNUMnLSTXjZ|)QT
index 8bed36fc4608e2d72a230611dda759b5c7d4f26e..9ee4d9a68f093ee08a3d7c614a4144683e78a703 GIT binary patch literal 24552 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 XLlv?%rcXIw00000NkvXXu0mjf)1!5a
--- a/toolkit/themes/winstripe/mozapps/jar.mn +++ b/toolkit/themes/winstripe/mozapps/jar.mn @@ -10,16 +10,20 @@ classic.jar: skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css) skin/classic/mozapps/extensions/itemDisabledFader.png (extensions/itemDisabledFader.png) skin/classic/mozapps/extensions/itemEnabledFader.png (extensions/itemEnabledFader.png) skin/classic/mozapps/extensions/notifyBadges.png (extensions/notifyBadges.png) skin/classic/mozapps/extensions/question.png (extensions/question.png) skin/classic/mozapps/extensions/update.css (extensions/update.css) skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png) skin/classic/mozapps/extensions/viewButtons.png (extensions/viewButtons.png) + skin/classic/mozapps/extensions/ratings.png (extensions/ratings.png) + skin/classic/mozapps/extensions/extensionIcons.png (extensions/extensionIcons.png) + skin/classic/mozapps/extensions/searchIcons.png (extensions/searchIcons.png) + skin/classic/mozapps/extensions/eula.css (extensions/eula.css) skin/classic/mozapps/plugins/missingPlugin.css (plugins/missingPlugin.css) skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png) skin/classic/mozapps/plugins/pluginInstallerWizard.css (plugins/pluginInstallerWizard.css) skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png) skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css) skin/classic/mozapps/shared/itemFader.png (shared/itemFader.png) skin/classic/mozapps/shared/itemSelected.png (shared/itemSelected.png) skin/classic/mozapps/shared/richview.css (shared/richview.css)