Bug 404024: Add AMO integration pane. r=robstrong

This commit is contained in:
dtownsend%oxymoronical.com 2008-01-28 17:11:52 +00:00
Родитель 913327a909
Коммит f23afb923b
27 изменённых файлов: 2128 добавлений и 178 удалений

Просмотреть файл

@ -68,6 +68,15 @@ 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);

Просмотреть файл

@ -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));
}

Просмотреть файл

@ -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>

Просмотреть файл

@ -10,6 +10,46 @@ 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");
}
@ -99,6 +139,10 @@ 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;
@ -148,3 +192,36 @@ 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;
}

Просмотреть файл

@ -67,6 +67,13 @@ 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";
@ -79,6 +86,9 @@ 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";
@ -108,6 +118,14 @@ function setElementDisabledByID(aID, aDoDisable) {
}
}
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");
@ -158,23 +176,17 @@ function showMessage(aIconURL, aMessage, aButtonLabel, aButtonAccesskey,
// 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();
},
@ -184,7 +196,7 @@ var AddonsViewBuilder = {
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");
@ -202,16 +214,16 @@ var AddonsViewBuilder = {
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;
},
@ -256,31 +268,32 @@ function showView(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;
@ -293,9 +306,35 @@ function showView(aView) {
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;
@ -315,24 +354,24 @@ function showView(aView) {
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;
@ -343,40 +382,40 @@ function showView(aView) {
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"]
@ -409,8 +448,9 @@ function showView(aView) {
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];
@ -524,6 +564,268 @@ function setRestartMessage(aItem)
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"]
@ -536,23 +838,15 @@ 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) {
@ -592,40 +886,40 @@ function rebuildPluginsDS()
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);
}
}
@ -639,12 +933,10 @@ function togglePluginDisabled(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();
}
@ -681,16 +973,27 @@ function Startup()
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");
@ -744,7 +1047,6 @@ function Startup()
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) {
@ -768,6 +1070,7 @@ function Startup()
}
}
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
@ -784,6 +1087,10 @@ function Startup()
function Shutdown()
{
if (gAddonRepository && gAddonRepository.isSearching)
gAddonRepository.cancelSearch();
gRDF = null;
gPref = null;
gExtensionsView.removeEventListener("select", onAddonSelect, false);
gExtensionsView.database.RemoveDataSource(gExtensionManager.datasource);
@ -830,7 +1137,6 @@ XPInstallDownloadManager.prototype = {
{
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);
@ -845,6 +1151,7 @@ XPInstallDownloadManager.prototype = {
{
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++);
@ -865,11 +1172,22 @@ XPInstallDownloadManager.prototype = {
// 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)
@ -1169,11 +1487,7 @@ function onAddonSelect(aEvent)
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;
@ -1552,6 +1866,11 @@ const gAddonsMsgObserver = {
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;
@ -1861,6 +2180,38 @@ function confirmOperation(aName, aTitle, aQueryMsg, aAcceptBtn, aCancelBtn,
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)
{
@ -1881,6 +2232,8 @@ var gExtensionsViewController = {
return false;
}
switch (aCommand) {
case "cmd_installSearchResult":
return true;
case "cmd_useTheme":
return selectedItem.type == nsIUpdateItem.TYPE_THEME &&
!selectedItem.isDisabled &&
@ -1969,6 +2322,41 @@ var gExtensionsViewController = {
},
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);
@ -2035,11 +2423,7 @@ var gExtensionsViewController = {
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);
},

Просмотреть файл

@ -385,6 +385,250 @@
</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>
@ -599,6 +843,104 @@
</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>

Просмотреть файл

@ -104,6 +104,7 @@
<command id="cmd_enable"/>
<command id="cmd_disable"/>
<command id="cmd_useTheme"/>
<command id="cmd_installSearchResult"/>
</commandset>
<commandset id="globalCommands">
@ -114,6 +115,7 @@
<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">
@ -156,6 +158,7 @@
<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"/>
@ -174,46 +177,57 @@
</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">

Просмотреть файл

@ -11,4 +11,5 @@ toolkit.jar:
* 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)

Просмотреть файл

@ -44,7 +44,11 @@ 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

Просмотреть файл

@ -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();
};

Просмотреть файл

@ -45,7 +45,11 @@ 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

Просмотреть файл

@ -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]);
}

Просмотреть файл

@ -6215,13 +6215,11 @@ RDFItemUpdater.prototype = {
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); };

Просмотреть файл

@ -264,6 +264,131 @@ 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;
}
@ -339,6 +464,13 @@ 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;

Просмотреть файл

@ -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;
}

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/extensionIcons.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -220,6 +220,136 @@ 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;
}

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/ratings.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 568 B

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/searchIcons.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Просмотреть файл

@ -13,10 +13,14 @@ classic.jar:
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)

Просмотреть файл

@ -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;
}

Двоичные данные
toolkit/themes/winstripe/mozapps/extensions/extensionIcons.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.3 KiB

Просмотреть файл

@ -263,6 +263,138 @@ 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;
}
@ -345,6 +477,13 @@ 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;

Двоичные данные
toolkit/themes/winstripe/mozapps/extensions/ratings.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 568 B

Двоичные данные
toolkit/themes/winstripe/mozapps/extensions/searchIcons.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.5 KiB

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 23 KiB

После

Ширина:  |  Высота:  |  Размер: 24 KiB

Просмотреть файл

@ -15,6 +15,10 @@ classic.jar:
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)