mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-06 07:17:30 +09:00
Move WebExtensions enabled Add-ons Manager
This commit is contained in:
parent
d7d16e7fbb
commit
358cd0404a
1100 changed files with 133 additions and 106 deletions
|
|
@ -1,8 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = { // eslint-disable-line no-undef
|
||||
"rules": {
|
||||
// No using undeclared variables
|
||||
"no-undef": "error",
|
||||
}
|
||||
};
|
||||
|
|
@ -1,478 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AddonContentPolicy.h"
|
||||
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsIContentSecurityPolicy.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIStringBundle.h"
|
||||
#include "nsIUUIDGenerator.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
/* Enforces content policies for WebExtension scopes. Currently:
|
||||
*
|
||||
* - Prevents loading scripts with a non-default JavaScript version.
|
||||
* - Checks custom content security policies for sufficiently stringent
|
||||
* script-src and object-src directives.
|
||||
*/
|
||||
|
||||
#define VERSIONED_JS_BLOCKED_MESSAGE \
|
||||
u"Versioned JavaScript is a non-standard, deprecated extension, and is " \
|
||||
u"not supported in WebExtension code. For alternatives, please see: " \
|
||||
u"https://developer.mozilla.org/Add-ons/WebExtensions/Tips"
|
||||
|
||||
AddonContentPolicy::AddonContentPolicy()
|
||||
{
|
||||
}
|
||||
|
||||
AddonContentPolicy::~AddonContentPolicy()
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(AddonContentPolicy, nsIContentPolicy, nsIAddonContentPolicy)
|
||||
|
||||
static nsresult
|
||||
GetWindowIDFromContext(nsISupports* aContext, uint64_t *aResult)
|
||||
{
|
||||
NS_ENSURE_TRUE(aContext, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aContext);
|
||||
NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDocument> document = content->OwnerDoc();
|
||||
NS_ENSURE_TRUE(document, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = document->GetInnerWindow();
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
|
||||
*aResult = window->WindowID();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
LogMessage(const nsAString &aMessage, nsIURI* aSourceURI, const nsAString &aSourceSample,
|
||||
nsISupports* aContext)
|
||||
{
|
||||
nsCOMPtr<nsIScriptError> error = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
|
||||
NS_ENSURE_TRUE(error, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
nsCString sourceName = aSourceURI->GetSpecOrDefault();
|
||||
|
||||
uint64_t windowID = 0;
|
||||
GetWindowIDFromContext(aContext, &windowID);
|
||||
|
||||
nsresult rv =
|
||||
error->InitWithWindowID(aMessage, NS_ConvertUTF8toUTF16(sourceName),
|
||||
aSourceSample, 0, 0, nsIScriptError::errorFlag,
|
||||
"JavaScript", windowID);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIConsoleService> console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
NS_ENSURE_TRUE(console, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
console->LogMessage(error);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// Content policy enforcement:
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonContentPolicy::ShouldLoad(uint32_t aContentType,
|
||||
nsIURI* aContentLocation,
|
||||
nsIURI* aRequestOrigin,
|
||||
nsISupports* aContext,
|
||||
const nsACString& aMimeTypeGuess,
|
||||
nsISupports* aExtra,
|
||||
nsIPrincipal* aRequestPrincipal,
|
||||
int16_t* aShouldLoad)
|
||||
{
|
||||
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
|
||||
"We should only see external content policy types here.");
|
||||
|
||||
*aShouldLoad = nsIContentPolicy::ACCEPT;
|
||||
|
||||
if (!aRequestOrigin) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Only apply this policy to requests from documents loaded from
|
||||
// moz-extension URLs, or to resources being loaded from moz-extension URLs.
|
||||
bool equals;
|
||||
if (!((NS_SUCCEEDED(aContentLocation->SchemeIs("moz-extension", &equals)) && equals) ||
|
||||
(NS_SUCCEEDED(aRequestOrigin->SchemeIs("moz-extension", &equals)) && equals))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aContentType == nsIContentPolicy::TYPE_SCRIPT) {
|
||||
NS_ConvertUTF8toUTF16 typeString(aMimeTypeGuess);
|
||||
nsContentTypeParser mimeParser(typeString);
|
||||
|
||||
// Reject attempts to load JavaScript scripts with a non-default version.
|
||||
nsAutoString mimeType, version;
|
||||
if (NS_SUCCEEDED(mimeParser.GetType(mimeType)) &&
|
||||
nsContentUtils::IsJavascriptMIMEType(mimeType) &&
|
||||
NS_SUCCEEDED(mimeParser.GetParameter("version", version))) {
|
||||
*aShouldLoad = nsIContentPolicy::REJECT_REQUEST;
|
||||
|
||||
LogMessage(NS_MULTILINE_LITERAL_STRING(VERSIONED_JS_BLOCKED_MESSAGE),
|
||||
aRequestOrigin, typeString, aContext);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonContentPolicy::ShouldProcess(uint32_t aContentType,
|
||||
nsIURI* aContentLocation,
|
||||
nsIURI* aRequestOrigin,
|
||||
nsISupports* aRequestingContext,
|
||||
const nsACString& aMimeTypeGuess,
|
||||
nsISupports* aExtra,
|
||||
nsIPrincipal* aRequestPrincipal,
|
||||
int16_t* aShouldProcess)
|
||||
{
|
||||
MOZ_ASSERT(aContentType == nsContentUtils::InternalContentPolicyTypeToExternal(aContentType),
|
||||
"We should only see external content policy types here.");
|
||||
|
||||
*aShouldProcess = nsIContentPolicy::ACCEPT;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// CSP Validation:
|
||||
|
||||
static const char* allowedSchemes[] = {
|
||||
"blob",
|
||||
"filesystem",
|
||||
nullptr
|
||||
};
|
||||
|
||||
static const char* allowedHostSchemes[] = {
|
||||
"https",
|
||||
"moz-extension",
|
||||
nullptr
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a CSP directive to ensure that it is sufficiently stringent.
|
||||
* In particular, ensures that:
|
||||
*
|
||||
* - No remote sources are allowed other than from https: schemes
|
||||
*
|
||||
* - No remote sources specify host wildcards for generic domains
|
||||
* (*.blogspot.com, *.com, *)
|
||||
*
|
||||
* - All remote sources and local extension sources specify a host
|
||||
*
|
||||
* - No scheme sources are allowed other than blob:, filesystem:,
|
||||
* moz-extension:, and https:
|
||||
*
|
||||
* - No keyword sources are allowed other than 'none', 'self', 'unsafe-eval',
|
||||
* and hash sources.
|
||||
*/
|
||||
class CSPValidator final : public nsCSPSrcVisitor {
|
||||
public:
|
||||
CSPValidator(nsAString& aURL, CSPDirective aDirective, bool aDirectiveRequired = true) :
|
||||
mURL(aURL),
|
||||
mDirective(CSP_CSPDirectiveToString(aDirective)),
|
||||
mFoundSelf(false)
|
||||
{
|
||||
// Start with the default error message for a missing directive, since no
|
||||
// visitors will be called if the directive isn't present.
|
||||
if (aDirectiveRequired) {
|
||||
FormatError("csp.error.missing-directive");
|
||||
}
|
||||
}
|
||||
|
||||
// Visitors
|
||||
|
||||
bool visitSchemeSrc(const nsCSPSchemeSrc& src) override
|
||||
{
|
||||
nsAutoString scheme;
|
||||
src.getScheme(scheme);
|
||||
|
||||
if (SchemeInList(scheme, allowedHostSchemes)) {
|
||||
FormatError("csp.error.missing-host", scheme);
|
||||
return false;
|
||||
}
|
||||
if (!SchemeInList(scheme, allowedSchemes)) {
|
||||
FormatError("csp.error.illegal-protocol", scheme);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
bool visitHostSrc(const nsCSPHostSrc& src) override
|
||||
{
|
||||
nsAutoString scheme, host;
|
||||
|
||||
src.getScheme(scheme);
|
||||
src.getHost(host);
|
||||
|
||||
if (scheme.LowerCaseEqualsLiteral("https")) {
|
||||
if (!HostIsAllowed(host)) {
|
||||
FormatError("csp.error.illegal-host-wildcard", scheme);
|
||||
return false;
|
||||
}
|
||||
} else if (scheme.LowerCaseEqualsLiteral("moz-extension")) {
|
||||
// The CSP parser silently converts 'self' keywords to the origin
|
||||
// URL, so we need to reconstruct the URL to see if it was present.
|
||||
if (!mFoundSelf) {
|
||||
nsAutoString url(u"moz-extension://");
|
||||
url.Append(host);
|
||||
|
||||
mFoundSelf = url.Equals(mURL);
|
||||
}
|
||||
|
||||
if (host.IsEmpty() || host.EqualsLiteral("*")) {
|
||||
FormatError("csp.error.missing-host", scheme);
|
||||
return false;
|
||||
}
|
||||
} else if (!SchemeInList(scheme, allowedSchemes)) {
|
||||
FormatError("csp.error.illegal-protocol", scheme);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool visitKeywordSrc(const nsCSPKeywordSrc& src) override
|
||||
{
|
||||
switch (src.getKeyword()) {
|
||||
case CSP_NONE:
|
||||
case CSP_SELF:
|
||||
case CSP_UNSAFE_EVAL:
|
||||
return true;
|
||||
|
||||
default:
|
||||
NS_ConvertASCIItoUTF16 keyword(CSP_EnumToKeyword(src.getKeyword()));
|
||||
|
||||
FormatError("csp.error.illegal-keyword", keyword);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
bool visitNonceSrc(const nsCSPNonceSrc& src) override
|
||||
{
|
||||
FormatError("csp.error.illegal-keyword", NS_LITERAL_STRING("'nonce-*'"));
|
||||
return false;
|
||||
};
|
||||
|
||||
bool visitHashSrc(const nsCSPHashSrc& src) override
|
||||
{
|
||||
return true;
|
||||
};
|
||||
|
||||
// Accessors
|
||||
|
||||
inline nsAString& GetError()
|
||||
{
|
||||
return mError;
|
||||
};
|
||||
|
||||
inline bool FoundSelf()
|
||||
{
|
||||
return mFoundSelf;
|
||||
};
|
||||
|
||||
|
||||
// Formatters
|
||||
|
||||
template <typename... T>
|
||||
inline void FormatError(const char* aName, const T ...aParams)
|
||||
{
|
||||
const char16_t* params[] = { mDirective.get(), aParams.get()... };
|
||||
FormatErrorParams(aName, params, MOZ_ARRAY_LENGTH(params));
|
||||
};
|
||||
|
||||
private:
|
||||
// Validators
|
||||
|
||||
bool HostIsAllowed(nsAString& host)
|
||||
{
|
||||
if (host.First() == '*') {
|
||||
if (host.EqualsLiteral("*") || host[1] != '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
host.Cut(0, 2);
|
||||
|
||||
nsCOMPtr<nsIEffectiveTLDService> tldService =
|
||||
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
|
||||
|
||||
if (!tldService) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 cHost(host);
|
||||
nsAutoCString publicSuffix;
|
||||
|
||||
nsresult rv = tldService->GetPublicSuffixFromHost(cHost, publicSuffix);
|
||||
|
||||
return NS_SUCCEEDED(rv) && !cHost.Equals(publicSuffix);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
bool SchemeInList(nsAString& scheme, const char** schemes)
|
||||
{
|
||||
for (; *schemes; schemes++) {
|
||||
if (scheme.LowerCaseEqualsASCII(*schemes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
// Formatters
|
||||
|
||||
already_AddRefed<nsIStringBundle>
|
||||
GetStringBundle()
|
||||
{
|
||||
nsCOMPtr<nsIStringBundleService> sbs =
|
||||
mozilla::services::GetStringBundleService();
|
||||
NS_ENSURE_TRUE(sbs, nullptr);
|
||||
|
||||
nsCOMPtr<nsIStringBundle> stringBundle;
|
||||
sbs->CreateBundle("chrome://global/locale/extensions.properties",
|
||||
getter_AddRefs(stringBundle));
|
||||
|
||||
return stringBundle.forget();
|
||||
};
|
||||
|
||||
void FormatErrorParams(const char* aName, const char16_t** aParams, int32_t aLength)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIStringBundle> stringBundle = GetStringBundle();
|
||||
|
||||
if (stringBundle) {
|
||||
NS_ConvertASCIItoUTF16 name(aName);
|
||||
|
||||
rv = stringBundle->FormatStringFromName(name.get(), aParams, aLength,
|
||||
getter_Copies(mError));
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
mError.AssignLiteral("An unexpected error occurred");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Data members
|
||||
|
||||
nsAutoString mURL;
|
||||
NS_ConvertASCIItoUTF16 mDirective;
|
||||
nsXPIDLString mError;
|
||||
|
||||
bool mFoundSelf;
|
||||
};
|
||||
|
||||
/**
|
||||
* Validates a custom content security policy string for use by an add-on.
|
||||
* In particular, ensures that:
|
||||
*
|
||||
* - Both object-src and script-src directives are present, and meet
|
||||
* the policies required by the CSPValidator class
|
||||
*
|
||||
* - The script-src directive includes the source 'self'
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
AddonContentPolicy::ValidateAddonCSP(const nsAString& aPolicyString,
|
||||
nsAString& aResult)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
// Validate against a randomly-generated extension origin.
|
||||
// There is no add-on-specific behavior in the CSP code, beyond the ability
|
||||
// for add-ons to specify a custom policy, but the parser requires a valid
|
||||
// origin in order to operate correctly.
|
||||
nsAutoString url(u"moz-extension://");
|
||||
{
|
||||
nsCOMPtr<nsIUUIDGenerator> uuidgen = services::GetUUIDGenerator();
|
||||
NS_ENSURE_TRUE(uuidgen, NS_ERROR_FAILURE);
|
||||
|
||||
nsID id;
|
||||
rv = uuidgen->GenerateUUIDInPlace(&id);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
char idString[NSID_LENGTH];
|
||||
id.ToProvidedString(idString);
|
||||
|
||||
MOZ_RELEASE_ASSERT(idString[0] == '{' && idString[NSID_LENGTH - 2] == '}',
|
||||
"UUID generator did not return a valid UUID");
|
||||
|
||||
url.AppendASCII(idString + 1, NSID_LENGTH - 3);
|
||||
}
|
||||
|
||||
|
||||
RefPtr<BasePrincipal> principal =
|
||||
BasePrincipal::CreateCodebasePrincipal(NS_ConvertUTF16toUTF8(url));
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = principal->EnsureCSP(nullptr, getter_AddRefs(csp));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
csp->AppendPolicy(aPolicyString, false, false);
|
||||
|
||||
const nsCSPPolicy* policy = csp->GetPolicy(0);
|
||||
if (!policy) {
|
||||
CSPValidator validator(url, nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE);
|
||||
aResult.Assign(validator.GetError());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool haveValidDefaultSrc = false;
|
||||
{
|
||||
CSPDirective directive = nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive);
|
||||
|
||||
haveValidDefaultSrc = policy->visitDirectiveSrcs(directive, &validator);
|
||||
}
|
||||
|
||||
aResult.SetIsVoid(true);
|
||||
{
|
||||
CSPDirective directive = nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive, !haveValidDefaultSrc);
|
||||
|
||||
if (!policy->visitDirectiveSrcs(directive, &validator)) {
|
||||
aResult.Assign(validator.GetError());
|
||||
} else if (!validator.FoundSelf()) {
|
||||
validator.FormatError("csp.error.missing-source", NS_LITERAL_STRING("'self'"));
|
||||
aResult.Assign(validator.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
if (aResult.IsVoid()) {
|
||||
CSPDirective directive = nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
|
||||
CSPValidator validator(url, directive, !haveValidDefaultSrc);
|
||||
|
||||
if (!policy->visitDirectiveSrcs(directive, &validator)) {
|
||||
aResult.Assign(validator.GetError());
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsIContentPolicy.h"
|
||||
#include "nsIAddonPolicyService.h"
|
||||
|
||||
class AddonContentPolicy : public nsIContentPolicy,
|
||||
public nsIAddonContentPolicy
|
||||
{
|
||||
protected:
|
||||
virtual ~AddonContentPolicy();
|
||||
|
||||
public:
|
||||
AddonContentPolicy();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSICONTENTPOLICY
|
||||
NS_DECL_NSIADDONCONTENTPOLICY
|
||||
};
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,171 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AddonManagerWebAPI.h"
|
||||
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/NavigatorBinding.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
|
||||
namespace mozilla {
|
||||
using namespace mozilla::dom;
|
||||
|
||||
static bool
|
||||
IsValidHost(const nsACString& host) {
|
||||
// This is ugly, but Preferences.h doesn't have support
|
||||
// for default prefs or locked prefs
|
||||
nsCOMPtr<nsIPrefService> prefService (do_GetService(NS_PREFSERVICE_CONTRACTID));
|
||||
nsCOMPtr<nsIPrefBranch> prefs;
|
||||
if (prefService) {
|
||||
prefService->GetDefaultBranch(nullptr, getter_AddRefs(prefs));
|
||||
bool isEnabled;
|
||||
if (NS_SUCCEEDED(prefs->GetBoolPref("xpinstall.enabled", &isEnabled)) && !isEnabled) {
|
||||
bool isLocked;
|
||||
prefs->PrefIsLocked("xpinstall.enabled", &isLocked);
|
||||
if (isLocked) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (host.Equals("addons.mozilla.org") ||
|
||||
host.Equals("discovery.addons.mozilla.org") ||
|
||||
host.Equals("testpilot.firefox.com")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When testing allow access to the developer sites.
|
||||
if (Preferences::GetBool("extensions.webapi.testing", false)) {
|
||||
if (host.LowerCaseEqualsLiteral("addons.allizom.org") ||
|
||||
host.LowerCaseEqualsLiteral("discovery.addons.allizom.org") ||
|
||||
host.LowerCaseEqualsLiteral("addons-dev.allizom.org") ||
|
||||
host.LowerCaseEqualsLiteral("discovery.addons-dev.allizom.org") ||
|
||||
host.LowerCaseEqualsLiteral("testpilot.stage.mozaws.net") ||
|
||||
host.LowerCaseEqualsLiteral("testpilot.dev.mozaws.net") ||
|
||||
host.LowerCaseEqualsLiteral("example.com")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks if the given uri is secure and matches one of the hosts allowed to
|
||||
// access the API.
|
||||
bool
|
||||
AddonManagerWebAPI::IsValidSite(nsIURI* uri)
|
||||
{
|
||||
if (!uri) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isSecure;
|
||||
nsresult rv = uri->SchemeIs("https", &isSecure);
|
||||
if (NS_FAILED(rv) || !isSecure) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoCString host;
|
||||
rv = uri->GetHost(host);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsValidHost(host);
|
||||
}
|
||||
|
||||
bool
|
||||
AddonManagerWebAPI::IsAPIEnabled(JSContext* cx, JSObject* obj)
|
||||
{
|
||||
nsGlobalWindow* global = xpc::WindowGlobalOrNull(obj);
|
||||
if (!global) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> win = global->AsInner();
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the current window and all parent frames are allowed access to
|
||||
// the API.
|
||||
while (win) {
|
||||
nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(win);
|
||||
if (!sop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = sop->GetPrincipal();
|
||||
if (!principal) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reaching a window with a system principal means we have reached
|
||||
// privileged UI of some kind so stop at this point and allow access.
|
||||
if (principal->GetIsSystemPrincipal()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
|
||||
if (!docShell) {
|
||||
// This window has been torn down so don't allow access to the API.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsValidSite(win->GetDocumentURI())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks whether there is a parent frame of the same type. This won't cross
|
||||
// mozbrowser or chrome boundaries.
|
||||
nsCOMPtr<nsIDocShellTreeItem> parent;
|
||||
nsresult rv = docShell->GetSameTypeParent(getter_AddRefs(parent));
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!parent) {
|
||||
// No parent means we've hit a mozbrowser or chrome boundary so allow
|
||||
// access to the API.
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIDocument* doc = win->GetDoc();
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
doc = doc->GetParentDocument();
|
||||
if (!doc) {
|
||||
// Getting here means something has been torn down so fail safe.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
win = doc->GetInnerWindow();
|
||||
}
|
||||
|
||||
// Found a document with no inner window, don't grant access to the API.
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
bool
|
||||
AddonManagerPermissions::IsHostPermitted(const GlobalObject& /*unused*/, const nsAString& host)
|
||||
{
|
||||
return IsValidHost(NS_ConvertUTF16toUTF8(host));
|
||||
}
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef addonmanagerwebapi_h_
|
||||
#define addonmanagerwebapi_h_
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AddonManagerWebAPI {
|
||||
public:
|
||||
static bool IsAPIEnabled(JSContext* cx, JSObject* obj);
|
||||
|
||||
private:
|
||||
static bool IsValidSite(nsIURI* uri);
|
||||
};
|
||||
|
||||
namespace dom {
|
||||
|
||||
class AddonManagerPermissions {
|
||||
public:
|
||||
static bool IsHostPermitted(const GlobalObject&, const nsAString& host);
|
||||
};
|
||||
|
||||
} // namespace mozilla::dom
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // addonmanagerwebapi_h_
|
||||
|
|
@ -1,258 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "AddonPathService.h"
|
||||
|
||||
#include "amIAddonManager.h"
|
||||
#include "nsIURI.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "jsapi.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsLiteralString.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsIAddonPolicyService.h"
|
||||
#include "nsIFileURL.h"
|
||||
#include "nsIResProtocolHandler.h"
|
||||
#include "nsIChromeRegistry.h"
|
||||
#include "nsIJARURI.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/Omnijar.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
struct PathEntryComparator
|
||||
{
|
||||
typedef AddonPathService::PathEntry PathEntry;
|
||||
|
||||
bool Equals(const PathEntry& entry1, const PathEntry& entry2) const
|
||||
{
|
||||
return entry1.mPath == entry2.mPath;
|
||||
}
|
||||
|
||||
bool LessThan(const PathEntry& entry1, const PathEntry& entry2) const
|
||||
{
|
||||
return entry1.mPath < entry2.mPath;
|
||||
}
|
||||
};
|
||||
|
||||
AddonPathService::AddonPathService()
|
||||
{
|
||||
}
|
||||
|
||||
AddonPathService::~AddonPathService()
|
||||
{
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(AddonPathService, amIAddonPathService)
|
||||
|
||||
AddonPathService *AddonPathService::sInstance;
|
||||
|
||||
/* static */ AddonPathService*
|
||||
AddonPathService::GetInstance()
|
||||
{
|
||||
if (!sInstance) {
|
||||
sInstance = new AddonPathService();
|
||||
}
|
||||
NS_ADDREF(sInstance);
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
static JSAddonId*
|
||||
ConvertAddonId(const nsAString& addonIdString)
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
JS::RootedValue strv(cx);
|
||||
if (!mozilla::dom::ToJSValue(cx, addonIdString, &strv)) {
|
||||
return nullptr;
|
||||
}
|
||||
JS::RootedString str(cx, strv.toString());
|
||||
return JS::NewAddonId(cx, str);
|
||||
}
|
||||
|
||||
JSAddonId*
|
||||
AddonPathService::Find(const nsAString& path)
|
||||
{
|
||||
// Use binary search to find the nearest entry that is <= |path|.
|
||||
PathEntryComparator comparator;
|
||||
unsigned index = mPaths.IndexOfFirstElementGt(PathEntry(path, nullptr), comparator);
|
||||
if (index == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
const PathEntry& entry = mPaths[index - 1];
|
||||
|
||||
// Return the entry's addon if its path is a prefix of |path|.
|
||||
if (StringBeginsWith(path, entry.mPath)) {
|
||||
return entry.mAddonId;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonPathService::FindAddonId(const nsAString& path, nsAString& addonIdString)
|
||||
{
|
||||
if (JSAddonId* id = Find(path)) {
|
||||
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
|
||||
AssignJSFlatString(addonIdString, flat);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */ JSAddonId*
|
||||
AddonPathService::FindAddonId(const nsAString& path)
|
||||
{
|
||||
// If no service has been created, then we're not going to find anything.
|
||||
if (!sInstance) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sInstance->Find(path);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonPathService::InsertPath(const nsAString& path, const nsAString& addonIdString)
|
||||
{
|
||||
JSAddonId* addonId = ConvertAddonId(addonIdString);
|
||||
|
||||
// Add the new path in sorted order.
|
||||
PathEntryComparator comparator;
|
||||
mPaths.InsertElementSorted(PathEntry(path, addonId), comparator);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
AddonPathService::MapURIToAddonId(nsIURI* aURI, nsAString& addonIdString)
|
||||
{
|
||||
if (JSAddonId* id = MapURIToAddonID(aURI)) {
|
||||
JSFlatString* flat = JS_ASSERT_STRING_IS_FLAT(JS::StringOfAddonId(id));
|
||||
AssignJSFlatString(addonIdString, flat);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
ResolveURI(nsIURI* aURI, nsAString& out)
|
||||
{
|
||||
bool equals;
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsAutoCString spec;
|
||||
|
||||
// Resolve resource:// URIs. At the end of this if/else block, we
|
||||
// have both spec and uri variables identifying the same URI.
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("resource", &equals)) && equals) {
|
||||
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIProtocolHandler> ph;
|
||||
rv = ioService->GetProtocolHandler("resource", getter_AddRefs(ph));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIResProtocolHandler> irph(do_QueryInterface(ph, &rv));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
rv = irph->ResolveURI(aURI, spec);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
rv = ioService->NewURI(spec, nullptr, nullptr, getter_AddRefs(uri));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
} else if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &equals)) && equals) {
|
||||
nsCOMPtr<nsIChromeRegistry> chromeReg =
|
||||
mozilla::services::GetChromeRegistryService();
|
||||
if (NS_WARN_IF(!chromeReg))
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
|
||||
rv = chromeReg->ConvertChromeURL(aURI, getter_AddRefs(uri));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
} else {
|
||||
uri = aURI;
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(uri->SchemeIs("jar", &equals)) && equals) {
|
||||
nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(uri, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIURI> jarFileURI;
|
||||
rv = jarURI->GetJARFile(getter_AddRefs(jarFileURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
return ResolveURI(jarFileURI, out);
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(uri->SchemeIs("file", &equals)) && equals) {
|
||||
nsCOMPtr<nsIFileURL> baseFileURL = do_QueryInterface(uri, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = baseFileURL->GetFile(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv)))
|
||||
return rv;
|
||||
|
||||
return file->GetPath(out);
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSAddonId*
|
||||
MapURIToAddonID(nsIURI* aURI)
|
||||
{
|
||||
if (!NS_IsMainThread() || !XRE_IsParentProcess()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool equals;
|
||||
nsresult rv;
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("moz-extension", &equals)) && equals) {
|
||||
nsCOMPtr<nsIAddonPolicyService> service = do_GetService("@mozilla.org/addons/policy-service;1");
|
||||
if (service) {
|
||||
nsString addonId;
|
||||
rv = service->ExtensionURIToAddonId(aURI, addonId);
|
||||
if (NS_FAILED(rv))
|
||||
return nullptr;
|
||||
|
||||
return ConvertAddonId(addonId);
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString filePath;
|
||||
rv = ResolveURI(aURI, filePath);
|
||||
if (NS_FAILED(rv))
|
||||
return nullptr;
|
||||
|
||||
nsCOMPtr<nsIFile> greJar = Omnijar::GetPath(Omnijar::GRE);
|
||||
nsCOMPtr<nsIFile> appJar = Omnijar::GetPath(Omnijar::APP);
|
||||
if (greJar && appJar) {
|
||||
nsAutoString greJarString, appJarString;
|
||||
if (NS_FAILED(greJar->GetPath(greJarString)) || NS_FAILED(appJar->GetPath(appJarString)))
|
||||
return nullptr;
|
||||
|
||||
// If |aURI| is part of either Omnijar, then it can't be part of an
|
||||
// add-on. This catches pretty much all URLs for Firefox content.
|
||||
if (filePath.Equals(greJarString) || filePath.Equals(appJarString))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If it's not part of Firefox, we resort to binary searching through the
|
||||
// add-on paths.
|
||||
return AddonPathService::FindAddonId(filePath);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef AddonPathService_h
|
||||
#define AddonPathService_h
|
||||
|
||||
#include "amIAddonPathService.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIURI;
|
||||
class JSAddonId;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
JSAddonId*
|
||||
MapURIToAddonID(nsIURI* aURI);
|
||||
|
||||
class AddonPathService final : public amIAddonPathService
|
||||
{
|
||||
public:
|
||||
AddonPathService();
|
||||
|
||||
static AddonPathService* GetInstance();
|
||||
|
||||
JSAddonId* Find(const nsAString& path);
|
||||
static JSAddonId* FindAddonId(const nsAString& path);
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_AMIADDONPATHSERVICE
|
||||
|
||||
struct PathEntry
|
||||
{
|
||||
nsString mPath;
|
||||
JSAddonId* mAddonId;
|
||||
|
||||
PathEntry(const nsAString& aPath, JSAddonId* aAddonId)
|
||||
: mPath(aPath), mAddonId(aAddonId)
|
||||
{}
|
||||
};
|
||||
|
||||
private:
|
||||
virtual ~AddonPathService();
|
||||
|
||||
// Paths are stored sorted in order of their mPath.
|
||||
nsTArray<PathEntry> mPaths;
|
||||
|
||||
static AddonPathService* sInstance;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["ChromeManifestParser"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
const MSG_JAR_FLUSH = "AddonJarFlush";
|
||||
|
||||
|
||||
/**
|
||||
* Sends local and remote notifications to flush a JAR file cache entry
|
||||
*
|
||||
* @param aJarFile
|
||||
* The ZIP/XPI/JAR file as a nsIFile
|
||||
*/
|
||||
function flushJarCache(aJarFile) {
|
||||
Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
|
||||
Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIMessageBroadcaster)
|
||||
.broadcastAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses chrome manifest files.
|
||||
*/
|
||||
this.ChromeManifestParser = {
|
||||
|
||||
/**
|
||||
* Reads and parses a chrome manifest file located at a specified URI, and all
|
||||
* secondary manifests it references.
|
||||
*
|
||||
* @param aURI
|
||||
* A nsIURI pointing to a chrome manifest.
|
||||
* Typically a file: or jar: URI.
|
||||
* @return Array of objects describing each manifest instruction, in the form:
|
||||
* { type: instruction-type, baseURI: string-uri, args: [arguments] }
|
||||
**/
|
||||
parseSync: function(aURI) {
|
||||
function parseLine(aLine) {
|
||||
let line = aLine.trim();
|
||||
if (line.length == 0 || line.charAt(0) == '#')
|
||||
return;
|
||||
let tokens = line.split(/\s+/);
|
||||
let type = tokens.shift();
|
||||
if (type == "manifest") {
|
||||
let uri = NetUtil.newURI(tokens.shift(), null, aURI);
|
||||
data = data.concat(this.parseSync(uri));
|
||||
} else {
|
||||
data.push({type: type, baseURI: baseURI, args: tokens});
|
||||
}
|
||||
}
|
||||
|
||||
let contents = "";
|
||||
try {
|
||||
if (aURI.scheme == "jar")
|
||||
contents = this._readFromJar(aURI);
|
||||
else
|
||||
contents = this._readFromFile(aURI);
|
||||
} catch (e) {
|
||||
// Silently fail.
|
||||
}
|
||||
|
||||
if (!contents)
|
||||
return [];
|
||||
|
||||
let baseURI = NetUtil.newURI(".", null, aURI).spec;
|
||||
|
||||
let data = [];
|
||||
let lines = contents.split("\n");
|
||||
lines.forEach(parseLine.bind(this));
|
||||
return data;
|
||||
},
|
||||
|
||||
_readFromJar: function(aURI) {
|
||||
let data = "";
|
||||
let entries = [];
|
||||
let readers = [];
|
||||
|
||||
try {
|
||||
// Deconstrict URI, which can be nested jar: URIs.
|
||||
let uri = aURI.clone();
|
||||
while (uri instanceof Ci.nsIJARURI) {
|
||||
entries.push(uri.JAREntry);
|
||||
uri = uri.JARFile;
|
||||
}
|
||||
|
||||
// Open the base jar.
|
||||
let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
|
||||
createInstance(Ci.nsIZipReader);
|
||||
reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
|
||||
readers.push(reader);
|
||||
|
||||
// Open the nested jars.
|
||||
for (let i = entries.length - 1; i > 0; i--) {
|
||||
let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
|
||||
createInstance(Ci.nsIZipReader);
|
||||
innerReader.openInner(reader, entries[i]);
|
||||
readers.push(innerReader);
|
||||
reader = innerReader;
|
||||
}
|
||||
|
||||
// First entry is the actual file we want to read.
|
||||
let zis = reader.getInputStream(entries[0]);
|
||||
data = NetUtil.readInputStreamToString(zis, zis.available());
|
||||
}
|
||||
finally {
|
||||
// Close readers in reverse order.
|
||||
for (let i = readers.length - 1; i >= 0; i--) {
|
||||
readers[i].close();
|
||||
flushJarCache(readers[i].file);
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
_readFromFile: function(aURI) {
|
||||
let file = aURI.QueryInterface(Ci.nsIFileURL).file;
|
||||
if (!file.exists() || !file.isFile())
|
||||
return "";
|
||||
|
||||
let data = "";
|
||||
let fis = Cc["@mozilla.org/network/file-input-stream;1"].
|
||||
createInstance(Ci.nsIFileInputStream);
|
||||
try {
|
||||
fis.init(file, -1, -1, false);
|
||||
data = NetUtil.readInputStreamToString(fis, fis.available());
|
||||
} finally {
|
||||
fis.close();
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Detects if there were any instructions of a specified type in a given
|
||||
* chrome manifest.
|
||||
*
|
||||
* @param aManifest
|
||||
* Manifest data, as returned by ChromeManifestParser.parseSync().
|
||||
* @param aType
|
||||
* Instruction type to filter by.
|
||||
* @return True if any matching instructions were found in the manifest.
|
||||
*/
|
||||
hasType: function(aManifest, aType) {
|
||||
return aManifest.some(entry => entry.type == aType);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,275 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
/* globals OS*/
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
|
||||
// Make it possible to mock out timers for testing
|
||||
var MakeTimer = () => Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DeferredSave"];
|
||||
|
||||
// If delay parameter is not provided, default is 50 milliseconds.
|
||||
const DEFAULT_SAVE_DELAY_MS = 50;
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
// Configure a logger at the parent 'DeferredSave' level to format
|
||||
// messages for all the modules under DeferredSave.*
|
||||
const DEFERREDSAVE_PARENT_LOGGER_ID = "DeferredSave";
|
||||
var parentLogger = Log.repository.getLogger(DEFERREDSAVE_PARENT_LOGGER_ID);
|
||||
parentLogger.level = Log.Level.Warn;
|
||||
var formatter = new Log.BasicFormatter();
|
||||
// Set parent logger (and its children) to append to
|
||||
// the Javascript section of the Browser Console
|
||||
parentLogger.addAppender(new Log.ConsoleAppender(formatter));
|
||||
// Set parent logger (and its children) to
|
||||
// also append to standard out
|
||||
parentLogger.addAppender(new Log.DumpAppender(formatter));
|
||||
|
||||
// Provide the ability to enable/disable logging
|
||||
// messages at runtime.
|
||||
// If the "extensions.logging.enabled" preference is
|
||||
// missing or 'false', messages at the WARNING and higher
|
||||
// severity should be logged to the JS console and standard error.
|
||||
// If "extensions.logging.enabled" is set to 'true', messages
|
||||
// at DEBUG and higher should go to JS console and standard error.
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
|
||||
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
||||
|
||||
/**
|
||||
* Preference listener which listens for a change in the
|
||||
* "extensions.logging.enabled" preference and changes the logging level of the
|
||||
* parent 'addons' level logger accordingly.
|
||||
*/
|
||||
var PrefObserver = {
|
||||
init: function() {
|
||||
Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
}
|
||||
else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
|
||||
let debugLogEnabled = false;
|
||||
try {
|
||||
debugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
if (debugLogEnabled) {
|
||||
parentLogger.level = Log.Level.Debug;
|
||||
}
|
||||
else {
|
||||
parentLogger.level = Log.Level.Warn;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PrefObserver.init();
|
||||
|
||||
/**
|
||||
* A module to manage deferred, asynchronous writing of data files
|
||||
* to disk. Writing is deferred by waiting for a specified delay after
|
||||
* a request to save the data, before beginning to write. If more than
|
||||
* one save request is received during the delay, all requests are
|
||||
* fulfilled by a single write.
|
||||
*
|
||||
* @constructor
|
||||
* @param aPath
|
||||
* String representing the full path of the file where the data
|
||||
* is to be written.
|
||||
* @param aDataProvider
|
||||
* Callback function that takes no argument and returns the data to
|
||||
* be written. If aDataProvider returns an ArrayBufferView, the
|
||||
* bytes it contains are written to the file as is.
|
||||
* If aDataProvider returns a String the data are UTF-8 encoded
|
||||
* and then written to the file.
|
||||
* @param [optional] aDelay
|
||||
* The delay in milliseconds between the first saveChanges() call
|
||||
* that marks the data as needing to be saved, and when the DeferredSave
|
||||
* begins writing the data to disk. Default 50 milliseconds.
|
||||
*/
|
||||
this.DeferredSave = function(aPath, aDataProvider, aDelay) {
|
||||
// Create a new logger (child of 'DeferredSave' logger)
|
||||
// for use by this particular instance of DeferredSave object
|
||||
let leafName = OS.Path.basename(aPath);
|
||||
let logger_id = DEFERREDSAVE_PARENT_LOGGER_ID + "." + leafName;
|
||||
this.logger = Log.repository.getLogger(logger_id);
|
||||
|
||||
// @type {Deferred|null}, null when no data needs to be written
|
||||
// @resolves with the result of OS.File.writeAtomic when all writes complete
|
||||
// @rejects with the error from OS.File.writeAtomic if the write fails,
|
||||
// or with the error from aDataProvider() if that throws.
|
||||
this._pending = null;
|
||||
|
||||
// @type {Promise}, completes when the in-progress write (if any) completes,
|
||||
// kept as a resolved promise at other times to simplify logic.
|
||||
// Because _deferredSave() always uses _writing.then() to execute
|
||||
// its next action, we don't need a special case for whether a write
|
||||
// is in progress - if the previous write is complete (and the _writing
|
||||
// promise is already resolved/rejected), _writing.then() starts
|
||||
// the next action immediately.
|
||||
//
|
||||
// @resolves with the result of OS.File.writeAtomic
|
||||
// @rejects with the error from OS.File.writeAtomic
|
||||
this._writing = Promise.resolve(0);
|
||||
|
||||
// Are we currently waiting for a write to complete
|
||||
this.writeInProgress = false;
|
||||
|
||||
this._path = aPath;
|
||||
this._dataProvider = aDataProvider;
|
||||
|
||||
this._timer = null;
|
||||
|
||||
// Some counters for telemetry
|
||||
// The total number of times the file was written
|
||||
this.totalSaves = 0;
|
||||
|
||||
// The number of times the data became dirty while
|
||||
// another save was in progress
|
||||
this.overlappedSaves = 0;
|
||||
|
||||
// Error returned by the most recent write (if any)
|
||||
this._lastError = null;
|
||||
|
||||
if (aDelay && (aDelay > 0))
|
||||
this._delay = aDelay;
|
||||
else
|
||||
this._delay = DEFAULT_SAVE_DELAY_MS;
|
||||
}
|
||||
|
||||
this.DeferredSave.prototype = {
|
||||
get dirty() {
|
||||
return this._pending || this.writeInProgress;
|
||||
},
|
||||
|
||||
get lastError() {
|
||||
return this._lastError;
|
||||
},
|
||||
|
||||
// Start the pending timer if data is dirty
|
||||
_startTimer: function() {
|
||||
if (!this._pending) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.debug("Starting timer");
|
||||
if (!this._timer)
|
||||
this._timer = MakeTimer();
|
||||
this._timer.initWithCallback(() => this._deferredSave(),
|
||||
this._delay, Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark the current stored data dirty, and schedule a flush to disk
|
||||
* @return A Promise<integer> that will be resolved after the data is written to disk;
|
||||
* the promise is resolved with the number of bytes written.
|
||||
*/
|
||||
saveChanges: function() {
|
||||
this.logger.debug("Save changes");
|
||||
if (!this._pending) {
|
||||
if (this.writeInProgress) {
|
||||
this.logger.debug("Data changed while write in progress");
|
||||
this.overlappedSaves++;
|
||||
}
|
||||
this._pending = Promise.defer();
|
||||
// Wait until the most recent write completes or fails (if it hasn't already)
|
||||
// and then restart our timer
|
||||
this._writing.then(count => this._startTimer(), error => this._startTimer());
|
||||
}
|
||||
return this._pending.promise;
|
||||
},
|
||||
|
||||
_deferredSave: function() {
|
||||
let pending = this._pending;
|
||||
this._pending = null;
|
||||
let writing = this._writing;
|
||||
this._writing = pending.promise;
|
||||
|
||||
// In either the success or the exception handling case, we don't need to handle
|
||||
// the error from _writing here; it's already being handled in another then()
|
||||
let toSave = null;
|
||||
try {
|
||||
toSave = this._dataProvider();
|
||||
}
|
||||
catch (e) {
|
||||
this.logger.error("Deferred save dataProvider failed", e);
|
||||
writing.then(null, error => {})
|
||||
.then(count => {
|
||||
pending.reject(e);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
writing.then(null, error => { return 0; })
|
||||
.then(count => {
|
||||
this.logger.debug("Starting write");
|
||||
this.totalSaves++;
|
||||
this.writeInProgress = true;
|
||||
|
||||
OS.File.writeAtomic(this._path, toSave, {tmpPath: this._path + ".tmp"})
|
||||
.then(
|
||||
result => {
|
||||
this._lastError = null;
|
||||
this.writeInProgress = false;
|
||||
this.logger.debug("Write succeeded");
|
||||
pending.resolve(result);
|
||||
},
|
||||
error => {
|
||||
this._lastError = error;
|
||||
this.writeInProgress = false;
|
||||
this.logger.warn("Write failed", error);
|
||||
pending.reject(error);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Immediately save the dirty data to disk, skipping
|
||||
* the delay of normal operation. Note that the write
|
||||
* still happens asynchronously in the worker
|
||||
* thread from OS.File.
|
||||
*
|
||||
* There are four possible situations:
|
||||
* 1) Nothing to flush
|
||||
* 2) Data is not currently being written, in-memory copy is dirty
|
||||
* 3) Data is currently being written, in-memory copy is clean
|
||||
* 4) Data is being written and in-memory copy is dirty
|
||||
*
|
||||
* @return Promise<integer> that will resolve when all in-memory data
|
||||
* has finished being flushed, returning the number of bytes
|
||||
* written. If all in-memory data is clean, completes with the
|
||||
* result of the most recent write.
|
||||
*/
|
||||
flush: function() {
|
||||
// If we have pending changes, cancel our timer and set up the write
|
||||
// immediately (_deferredSave queues the write for after the most
|
||||
// recent write completes, if it hasn't already)
|
||||
if (this._pending) {
|
||||
this.logger.debug("Flush called while data is dirty");
|
||||
if (this._timer) {
|
||||
this._timer.cancel();
|
||||
this._timer = null;
|
||||
}
|
||||
this._deferredSave();
|
||||
}
|
||||
|
||||
return this._writing;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,909 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LightweightThemeManager"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
||||
/* globals AddonManagerPrivate*/
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const ID_SUFFIX = "@personas.mozilla.org";
|
||||
const PREF_LWTHEME_TO_SELECT = "extensions.lwThemeToSelect";
|
||||
const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
|
||||
const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
|
||||
const ADDON_TYPE = "theme";
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
|
||||
const STRING_TYPE_NAME = "type.%ID%.name";
|
||||
|
||||
const DEFAULT_MAX_USED_THEMES_COUNT = 30;
|
||||
|
||||
const MAX_PREVIEW_SECONDS = 30;
|
||||
|
||||
const MANDATORY = ["id", "name", "headerURL"];
|
||||
const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
|
||||
"previewURL", "author", "description", "homepageURL",
|
||||
"updateURL", "version"];
|
||||
|
||||
const PERSIST_ENABLED = true;
|
||||
const PERSIST_BYPASS_CACHE = false;
|
||||
const PERSIST_FILES = {
|
||||
headerURL: "lightweighttheme-header",
|
||||
footerURL: "lightweighttheme-footer"
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeImageOptimizer",
|
||||
"resource://gre/modules/addons/LightweightThemeImageOptimizer.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest",
|
||||
"resource://gre/modules/ServiceRequest.jsm");
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_prefs", () => {
|
||||
return Services.prefs.getBranch("lightweightThemes.");
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "_maxUsedThemes", {
|
||||
get: function() {
|
||||
delete this._maxUsedThemes;
|
||||
try {
|
||||
this._maxUsedThemes = _prefs.getIntPref("maxUsedThemes");
|
||||
}
|
||||
catch (e) {
|
||||
this._maxUsedThemes = DEFAULT_MAX_USED_THEMES_COUNT;
|
||||
}
|
||||
return this._maxUsedThemes;
|
||||
},
|
||||
|
||||
set: function(val) {
|
||||
delete this._maxUsedThemes;
|
||||
return this._maxUsedThemes = val;
|
||||
},
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
// Holds the ID of the theme being enabled or disabled while sending out the
|
||||
// events so cached AddonWrapper instances can return correct values for
|
||||
// permissions and pendingOperations
|
||||
var _themeIDBeingEnabled = null;
|
||||
var _themeIDBeingDisabled = null;
|
||||
|
||||
// Convert from the old storage format (in which the order of usedThemes
|
||||
// was combined with isThemeSelected to determine which theme was selected)
|
||||
// to the new one (where a selectedThemeID determines which theme is selected).
|
||||
(function() {
|
||||
let wasThemeSelected = false;
|
||||
try {
|
||||
wasThemeSelected = _prefs.getBoolPref("isThemeSelected");
|
||||
} catch (e) { }
|
||||
|
||||
if (wasThemeSelected) {
|
||||
_prefs.clearUserPref("isThemeSelected");
|
||||
let themes = [];
|
||||
try {
|
||||
themes = JSON.parse(_prefs.getComplexValue("usedThemes",
|
||||
Ci.nsISupportsString).data);
|
||||
} catch (e) { }
|
||||
|
||||
if (Array.isArray(themes) && themes[0]) {
|
||||
_prefs.setCharPref("selectedThemeID", themes[0].id);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
this.LightweightThemeManager = {
|
||||
get name() {
|
||||
return "LightweightThemeManager";
|
||||
},
|
||||
|
||||
// Themes that can be added for an application. They can't be removed, and
|
||||
// will always show up at the top of the list.
|
||||
_builtInThemes: new Map(),
|
||||
|
||||
get usedThemes () {
|
||||
let themes = [];
|
||||
try {
|
||||
themes = JSON.parse(_prefs.getComplexValue("usedThemes",
|
||||
Ci.nsISupportsString).data);
|
||||
} catch (e) { }
|
||||
|
||||
themes.push(...this._builtInThemes.values());
|
||||
return themes;
|
||||
},
|
||||
|
||||
get currentTheme () {
|
||||
let selectedThemeID = null;
|
||||
try {
|
||||
selectedThemeID = _prefs.getCharPref("selectedThemeID");
|
||||
} catch (e) {}
|
||||
|
||||
let data = null;
|
||||
if (selectedThemeID) {
|
||||
data = this.getUsedTheme(selectedThemeID);
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
get currentThemeForDisplay () {
|
||||
var data = this.currentTheme;
|
||||
|
||||
if (data && PERSIST_ENABLED) {
|
||||
for (let key in PERSIST_FILES) {
|
||||
try {
|
||||
if (data[key] && _prefs.getBoolPref("persisted." + key))
|
||||
data[key] = _getLocalImageURI(PERSIST_FILES[key]).spec
|
||||
+ "?" + data.id + ";" + _version(data);
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
set currentTheme (aData) {
|
||||
return _setCurrentTheme(aData, false);
|
||||
},
|
||||
|
||||
setLocalTheme: function(aData) {
|
||||
_setCurrentTheme(aData, true);
|
||||
},
|
||||
|
||||
getUsedTheme: function(aId) {
|
||||
var usedThemes = this.usedThemes;
|
||||
for (let usedTheme of usedThemes) {
|
||||
if (usedTheme.id == aId)
|
||||
return usedTheme;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
forgetUsedTheme: function(aId) {
|
||||
let theme = this.getUsedTheme(aId);
|
||||
if (!theme || LightweightThemeManager._builtInThemes.has(theme.id))
|
||||
return;
|
||||
|
||||
let wrapper = new AddonWrapper(theme);
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
|
||||
|
||||
var currentTheme = this.currentTheme;
|
||||
if (currentTheme && currentTheme.id == aId) {
|
||||
this.themeChanged(null);
|
||||
AddonManagerPrivate.notifyAddonChanged(null, ADDON_TYPE, false);
|
||||
}
|
||||
|
||||
_updateUsedThemes(_usedThemesExceptId(aId));
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
|
||||
},
|
||||
|
||||
addBuiltInTheme: function(theme) {
|
||||
if (!theme || !theme.id || this.usedThemes.some(t => t.id == theme.id)) {
|
||||
throw new Error("Trying to add invalid builtIn theme");
|
||||
}
|
||||
|
||||
this._builtInThemes.set(theme.id, theme);
|
||||
|
||||
if (_prefs.getCharPref("selectedThemeID") == theme.id) {
|
||||
this.currentTheme = theme;
|
||||
}
|
||||
},
|
||||
|
||||
forgetBuiltInTheme: function(id) {
|
||||
if (!this._builtInThemes.has(id)) {
|
||||
let currentTheme = this.currentTheme;
|
||||
if (currentTheme && currentTheme.id == id) {
|
||||
this.currentTheme = null;
|
||||
}
|
||||
}
|
||||
return this._builtInThemes.delete(id);
|
||||
},
|
||||
|
||||
clearBuiltInThemes: function() {
|
||||
for (let id of this._builtInThemes.keys()) {
|
||||
this.forgetBuiltInTheme(id);
|
||||
}
|
||||
},
|
||||
|
||||
previewTheme: function(aData) {
|
||||
let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
|
||||
cancel.data = false;
|
||||
Services.obs.notifyObservers(cancel, "lightweight-theme-preview-requested",
|
||||
JSON.stringify(aData));
|
||||
if (cancel.data)
|
||||
return;
|
||||
|
||||
if (_previewTimer)
|
||||
_previewTimer.cancel();
|
||||
else
|
||||
_previewTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
_previewTimer.initWithCallback(_previewTimerCallback,
|
||||
MAX_PREVIEW_SECONDS * 1000,
|
||||
_previewTimer.TYPE_ONE_SHOT);
|
||||
|
||||
_notifyWindows(aData);
|
||||
},
|
||||
|
||||
resetPreview: function() {
|
||||
if (_previewTimer) {
|
||||
_previewTimer.cancel();
|
||||
_previewTimer = null;
|
||||
_notifyWindows(this.currentThemeForDisplay);
|
||||
}
|
||||
},
|
||||
|
||||
parseTheme: function(aString, aBaseURI) {
|
||||
try {
|
||||
return _sanitizeTheme(JSON.parse(aString), aBaseURI, false);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
updateCurrentTheme: function() {
|
||||
try {
|
||||
if (!_prefs.getBoolPref("update.enabled"))
|
||||
return;
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
var theme = this.currentTheme;
|
||||
if (!theme || !theme.updateURL)
|
||||
return;
|
||||
|
||||
var req = new ServiceRequest();
|
||||
|
||||
req.mozBackgroundRequest = true;
|
||||
req.overrideMimeType("text/plain");
|
||||
req.open("GET", theme.updateURL, true);
|
||||
// Prevent the request from reading from the cache.
|
||||
req.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
req.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
|
||||
req.addEventListener("load", () => {
|
||||
if (req.status != 200)
|
||||
return;
|
||||
|
||||
let newData = this.parseTheme(req.responseText, theme.updateURL);
|
||||
if (!newData ||
|
||||
newData.id != theme.id ||
|
||||
_version(newData) == _version(theme))
|
||||
return;
|
||||
|
||||
var currentTheme = this.currentTheme;
|
||||
if (currentTheme && currentTheme.id == theme.id)
|
||||
this.currentTheme = newData;
|
||||
}, false);
|
||||
|
||||
req.send(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Switches to a new lightweight theme.
|
||||
*
|
||||
* @param aData
|
||||
* The lightweight theme to switch to
|
||||
*/
|
||||
themeChanged: function(aData) {
|
||||
if (_previewTimer) {
|
||||
_previewTimer.cancel();
|
||||
_previewTimer = null;
|
||||
}
|
||||
|
||||
if (aData) {
|
||||
let usedThemes = _usedThemesExceptId(aData.id);
|
||||
usedThemes.unshift(aData);
|
||||
_updateUsedThemes(usedThemes);
|
||||
if (PERSIST_ENABLED) {
|
||||
LightweightThemeImageOptimizer.purge();
|
||||
_persistImages(aData, function() {
|
||||
_notifyWindows(this.currentThemeForDisplay);
|
||||
}.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
if (aData)
|
||||
_prefs.setCharPref("selectedThemeID", aData.id);
|
||||
else
|
||||
_prefs.setCharPref("selectedThemeID", "");
|
||||
|
||||
_notifyWindows(aData);
|
||||
Services.obs.notifyObservers(null, "lightweight-theme-changed", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts the Addons provider and enables the new lightweight theme if
|
||||
* necessary.
|
||||
*/
|
||||
startup: function() {
|
||||
if (Services.prefs.prefHasUserValue(PREF_LWTHEME_TO_SELECT)) {
|
||||
let id = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
|
||||
if (id)
|
||||
this.themeChanged(this.getUsedTheme(id));
|
||||
else
|
||||
this.themeChanged(null);
|
||||
Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT);
|
||||
}
|
||||
|
||||
_prefs.addObserver("", _prefObserver, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Shuts down the provider.
|
||||
*/
|
||||
shutdown: function() {
|
||||
_prefs.removeObserver("", _prefObserver);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a new add-on has been enabled when only one add-on of that type
|
||||
* can be enabled.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the newly enabled add-on
|
||||
* @param aType
|
||||
* The type of the newly enabled add-on
|
||||
* @param aPendingRestart
|
||||
* true if the newly enabled add-on will only become enabled after a
|
||||
* restart
|
||||
*/
|
||||
addonChanged: function(aId, aType, aPendingRestart) {
|
||||
if (aType != ADDON_TYPE)
|
||||
return;
|
||||
|
||||
let id = _getInternalID(aId);
|
||||
let current = this.currentTheme;
|
||||
|
||||
try {
|
||||
let next = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
|
||||
if (id == next && aPendingRestart)
|
||||
return;
|
||||
|
||||
Services.prefs.clearUserPref(PREF_LWTHEME_TO_SELECT);
|
||||
if (next) {
|
||||
AddonManagerPrivate.callAddonListeners("onOperationCancelled",
|
||||
new AddonWrapper(this.getUsedTheme(next)));
|
||||
}
|
||||
else if (id == current.id) {
|
||||
AddonManagerPrivate.callAddonListeners("onOperationCancelled",
|
||||
new AddonWrapper(current));
|
||||
return;
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (current) {
|
||||
if (current.id == id)
|
||||
return;
|
||||
_themeIDBeingDisabled = current.id;
|
||||
let wrapper = new AddonWrapper(current);
|
||||
if (aPendingRestart) {
|
||||
Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, "");
|
||||
AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, true);
|
||||
}
|
||||
else {
|
||||
AddonManagerPrivate.callAddonListeners("onDisabling", wrapper, false);
|
||||
this.themeChanged(null);
|
||||
AddonManagerPrivate.callAddonListeners("onDisabled", wrapper);
|
||||
}
|
||||
_themeIDBeingDisabled = null;
|
||||
}
|
||||
|
||||
if (id) {
|
||||
let theme = this.getUsedTheme(id);
|
||||
_themeIDBeingEnabled = id;
|
||||
let wrapper = new AddonWrapper(theme);
|
||||
if (aPendingRestart) {
|
||||
AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, true);
|
||||
Services.prefs.setCharPref(PREF_LWTHEME_TO_SELECT, id);
|
||||
|
||||
// Flush the preferences to disk so they survive any crash
|
||||
Services.prefs.savePrefFile(null);
|
||||
}
|
||||
else {
|
||||
AddonManagerPrivate.callAddonListeners("onEnabling", wrapper, false);
|
||||
this.themeChanged(theme);
|
||||
AddonManagerPrivate.callAddonListeners("onEnabled", wrapper);
|
||||
}
|
||||
_themeIDBeingEnabled = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get an Addon with a particular ID.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on to retrieve
|
||||
* @param aCallback
|
||||
* A callback to pass the Addon to
|
||||
*/
|
||||
getAddonByID: function(aId, aCallback) {
|
||||
let id = _getInternalID(aId);
|
||||
if (!id) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let theme = this.getUsedTheme(id);
|
||||
if (!theme) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
aCallback(new AddonWrapper(theme));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get Addons of a particular type.
|
||||
*
|
||||
* @param aTypes
|
||||
* An array of types to fetch. Can be null to get all types.
|
||||
* @param aCallback
|
||||
* A callback to pass an array of Addons to
|
||||
*/
|
||||
getAddonsByTypes: function(aTypes, aCallback) {
|
||||
if (aTypes && aTypes.indexOf(ADDON_TYPE) == -1) {
|
||||
aCallback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
aCallback(this.usedThemes.map(a => new AddonWrapper(a)));
|
||||
},
|
||||
};
|
||||
|
||||
const wrapperMap = new WeakMap();
|
||||
let themeFor = wrapper => wrapperMap.get(wrapper);
|
||||
|
||||
/**
|
||||
* The AddonWrapper wraps lightweight theme to provide the data visible to
|
||||
* consumers of the AddonManager API.
|
||||
*/
|
||||
function AddonWrapper(aTheme) {
|
||||
wrapperMap.set(this, aTheme);
|
||||
}
|
||||
|
||||
AddonWrapper.prototype = {
|
||||
get id() {
|
||||
return themeFor(this).id + ID_SUFFIX;
|
||||
},
|
||||
|
||||
get type() {
|
||||
return ADDON_TYPE;
|
||||
},
|
||||
|
||||
get isActive() {
|
||||
let current = LightweightThemeManager.currentTheme;
|
||||
if (current)
|
||||
return themeFor(this).id == current.id;
|
||||
return false;
|
||||
},
|
||||
|
||||
get name() {
|
||||
return themeFor(this).name;
|
||||
},
|
||||
|
||||
get version() {
|
||||
let theme = themeFor(this);
|
||||
return "version" in theme ? theme.version : "";
|
||||
},
|
||||
|
||||
get creator() {
|
||||
let theme = themeFor(this);
|
||||
return "author" in theme ? new AddonManagerPrivate.AddonAuthor(theme.author) : null;
|
||||
},
|
||||
|
||||
get screenshots() {
|
||||
let url = themeFor(this).previewURL;
|
||||
return [new AddonManagerPrivate.AddonScreenshot(url)];
|
||||
},
|
||||
|
||||
get pendingOperations() {
|
||||
let pending = AddonManager.PENDING_NONE;
|
||||
if (this.isActive == this.userDisabled)
|
||||
pending |= this.isActive ? AddonManager.PENDING_DISABLE : AddonManager.PENDING_ENABLE;
|
||||
return pending;
|
||||
},
|
||||
|
||||
get operationsRequiringRestart() {
|
||||
// If a non-default theme is in use then a restart will be required to
|
||||
// enable lightweight themes unless dynamic theme switching is enabled
|
||||
if (Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN)) {
|
||||
try {
|
||||
if (Services.prefs.getBoolPref(PREF_EM_DSS_ENABLED))
|
||||
return AddonManager.OP_NEEDS_RESTART_NONE;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
return AddonManager.OP_NEEDS_RESTART_ENABLE;
|
||||
}
|
||||
|
||||
return AddonManager.OP_NEEDS_RESTART_NONE;
|
||||
},
|
||||
|
||||
get size() {
|
||||
// The size changes depending on whether the theme is in use or not, this is
|
||||
// probably not worth exposing.
|
||||
return null;
|
||||
},
|
||||
|
||||
get permissions() {
|
||||
let permissions = 0;
|
||||
|
||||
// Do not allow uninstall of builtIn themes.
|
||||
if (!LightweightThemeManager._builtInThemes.has(themeFor(this).id))
|
||||
permissions = AddonManager.PERM_CAN_UNINSTALL;
|
||||
if (this.userDisabled)
|
||||
permissions |= AddonManager.PERM_CAN_ENABLE;
|
||||
else
|
||||
permissions |= AddonManager.PERM_CAN_DISABLE;
|
||||
return permissions;
|
||||
},
|
||||
|
||||
get userDisabled() {
|
||||
let id = themeFor(this).id;
|
||||
if (_themeIDBeingEnabled == id)
|
||||
return false;
|
||||
if (_themeIDBeingDisabled == id)
|
||||
return true;
|
||||
|
||||
try {
|
||||
let toSelect = Services.prefs.getCharPref(PREF_LWTHEME_TO_SELECT);
|
||||
return id != toSelect;
|
||||
}
|
||||
catch (e) {
|
||||
let current = LightweightThemeManager.currentTheme;
|
||||
return !current || current.id != id;
|
||||
}
|
||||
},
|
||||
|
||||
set userDisabled(val) {
|
||||
if (val == this.userDisabled)
|
||||
return val;
|
||||
|
||||
if (val)
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
else
|
||||
LightweightThemeManager.currentTheme = themeFor(this);
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
// Lightweight themes are never disabled by the application
|
||||
get appDisabled() {
|
||||
return false;
|
||||
},
|
||||
|
||||
// Lightweight themes are always compatible
|
||||
get isCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get isPlatformCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get scope() {
|
||||
return AddonManager.SCOPE_PROFILE;
|
||||
},
|
||||
|
||||
get foreignInstall() {
|
||||
return false;
|
||||
},
|
||||
|
||||
uninstall: function() {
|
||||
LightweightThemeManager.forgetUsedTheme(themeFor(this).id);
|
||||
},
|
||||
|
||||
cancelUninstall: function() {
|
||||
throw new Error("Theme is not marked to be uninstalled");
|
||||
},
|
||||
|
||||
findUpdates: function(listener, reason, appVersion, platformVersion) {
|
||||
AddonManagerPrivate.callNoUpdateListeners(this, listener, reason, appVersion, platformVersion);
|
||||
},
|
||||
|
||||
// Lightweight themes are always compatible
|
||||
isCompatibleWith: function(appVersion, platformVersion) {
|
||||
return true;
|
||||
},
|
||||
|
||||
// Lightweight themes are always securely updated
|
||||
get providesUpdatesSecurely() {
|
||||
return true;
|
||||
},
|
||||
|
||||
// Lightweight themes are never blocklisted
|
||||
get blocklistState() {
|
||||
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
|
||||
}
|
||||
};
|
||||
|
||||
["description", "homepageURL", "iconURL"].forEach(function(prop) {
|
||||
Object.defineProperty(AddonWrapper.prototype, prop, {
|
||||
get: function() {
|
||||
let theme = themeFor(this);
|
||||
return prop in theme ? theme[prop] : null;
|
||||
},
|
||||
enumarable: true,
|
||||
});
|
||||
});
|
||||
|
||||
["installDate", "updateDate"].forEach(function(prop) {
|
||||
Object.defineProperty(AddonWrapper.prototype, prop, {
|
||||
get: function() {
|
||||
let theme = themeFor(this);
|
||||
return prop in theme ? new Date(theme[prop]) : null;
|
||||
},
|
||||
enumarable: true,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Converts the ID used by the public AddonManager API to an lightweight theme
|
||||
* ID.
|
||||
*
|
||||
* @param id
|
||||
* The ID to be converted
|
||||
*
|
||||
* @return the lightweight theme ID or null if the ID was not for a lightweight
|
||||
* theme.
|
||||
*/
|
||||
function _getInternalID(id) {
|
||||
if (!id)
|
||||
return null;
|
||||
let len = id.length - ID_SUFFIX.length;
|
||||
if (len > 0 && id.substring(len) == ID_SUFFIX)
|
||||
return id.substring(0, len);
|
||||
return null;
|
||||
}
|
||||
|
||||
function _setCurrentTheme(aData, aLocal) {
|
||||
aData = _sanitizeTheme(aData, null, aLocal);
|
||||
|
||||
let needsRestart = (ADDON_TYPE == "theme") &&
|
||||
Services.prefs.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN);
|
||||
|
||||
let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
|
||||
cancel.data = false;
|
||||
Services.obs.notifyObservers(cancel, "lightweight-theme-change-requested",
|
||||
JSON.stringify(aData));
|
||||
|
||||
if (aData) {
|
||||
let theme = LightweightThemeManager.getUsedTheme(aData.id);
|
||||
let isInstall = !theme || theme.version != aData.version;
|
||||
if (isInstall) {
|
||||
aData.updateDate = Date.now();
|
||||
if (theme && "installDate" in theme)
|
||||
aData.installDate = theme.installDate;
|
||||
else
|
||||
aData.installDate = aData.updateDate;
|
||||
|
||||
var oldWrapper = theme ? new AddonWrapper(theme) : null;
|
||||
var wrapper = new AddonWrapper(aData);
|
||||
AddonManagerPrivate.callInstallListeners("onExternalInstall", null,
|
||||
wrapper, oldWrapper, false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalling", wrapper, false);
|
||||
}
|
||||
|
||||
let current = LightweightThemeManager.currentTheme;
|
||||
let usedThemes = _usedThemesExceptId(aData.id);
|
||||
if (current && current.id != aData.id)
|
||||
usedThemes.splice(1, 0, aData);
|
||||
else
|
||||
usedThemes.unshift(aData);
|
||||
_updateUsedThemes(usedThemes);
|
||||
|
||||
if (isInstall)
|
||||
AddonManagerPrivate.callAddonListeners("onInstalled", wrapper);
|
||||
}
|
||||
|
||||
if (cancel.data)
|
||||
return null;
|
||||
|
||||
AddonManagerPrivate.notifyAddonChanged(aData ? aData.id + ID_SUFFIX : null,
|
||||
ADDON_TYPE, needsRestart);
|
||||
|
||||
return LightweightThemeManager.currentTheme;
|
||||
}
|
||||
|
||||
function _sanitizeTheme(aData, aBaseURI, aLocal) {
|
||||
if (!aData || typeof aData != "object")
|
||||
return null;
|
||||
|
||||
var resourceProtocols = ["http", "https", "resource"];
|
||||
if (aLocal)
|
||||
resourceProtocols.push("file");
|
||||
var resourceProtocolExp = new RegExp("^(" + resourceProtocols.join("|") + "):");
|
||||
|
||||
function sanitizeProperty(prop) {
|
||||
if (!(prop in aData))
|
||||
return null;
|
||||
if (typeof aData[prop] != "string")
|
||||
return null;
|
||||
let val = aData[prop].trim();
|
||||
if (!val)
|
||||
return null;
|
||||
|
||||
if (!/URL$/.test(prop))
|
||||
return val;
|
||||
|
||||
try {
|
||||
val = _makeURI(val, aBaseURI ? _makeURI(aBaseURI) : null).spec;
|
||||
if ((prop == "updateURL" ? /^https:/ : resourceProtocolExp).test(val))
|
||||
return val;
|
||||
return null;
|
||||
}
|
||||
catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
let result = {};
|
||||
for (let mandatoryProperty of MANDATORY) {
|
||||
let val = sanitizeProperty(mandatoryProperty);
|
||||
if (!val)
|
||||
throw Components.results.NS_ERROR_INVALID_ARG;
|
||||
result[mandatoryProperty] = val;
|
||||
}
|
||||
|
||||
for (let optionalProperty of OPTIONAL) {
|
||||
let val = sanitizeProperty(optionalProperty);
|
||||
if (!val)
|
||||
continue;
|
||||
result[optionalProperty] = val;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function _usedThemesExceptId(aId) {
|
||||
return LightweightThemeManager.usedThemes.filter(function(t) {
|
||||
return "id" in t && t.id != aId;
|
||||
});
|
||||
}
|
||||
|
||||
function _version(aThemeData) {
|
||||
return aThemeData.version || "";
|
||||
}
|
||||
|
||||
function _makeURI(aURL, aBaseURI) {
|
||||
return Services.io.newURI(aURL, null, aBaseURI);
|
||||
}
|
||||
|
||||
function _updateUsedThemes(aList) {
|
||||
// Remove app-specific themes before saving them to the usedThemes pref.
|
||||
aList = aList.filter(theme => !LightweightThemeManager._builtInThemes.has(theme.id));
|
||||
|
||||
// Send uninstall events for all themes that need to be removed.
|
||||
while (aList.length > _maxUsedThemes) {
|
||||
let wrapper = new AddonWrapper(aList[aList.length - 1]);
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", wrapper, false);
|
||||
aList.pop();
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
|
||||
}
|
||||
|
||||
var str = Cc["@mozilla.org/supports-string;1"]
|
||||
.createInstance(Ci.nsISupportsString);
|
||||
str.data = JSON.stringify(aList);
|
||||
_prefs.setComplexValue("usedThemes", Ci.nsISupportsString, str);
|
||||
|
||||
Services.obs.notifyObservers(null, "lightweight-theme-list-changed", null);
|
||||
}
|
||||
|
||||
function _notifyWindows(aThemeData) {
|
||||
Services.obs.notifyObservers(null, "lightweight-theme-styling-update",
|
||||
JSON.stringify(aThemeData));
|
||||
}
|
||||
|
||||
var _previewTimer;
|
||||
var _previewTimerCallback = {
|
||||
notify: function() {
|
||||
LightweightThemeManager.resetPreview();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when any of the lightweightThemes preferences are changed.
|
||||
*/
|
||||
function _prefObserver(aSubject, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "maxUsedThemes":
|
||||
try {
|
||||
_maxUsedThemes = _prefs.getIntPref(aData);
|
||||
}
|
||||
catch (e) {
|
||||
_maxUsedThemes = DEFAULT_MAX_USED_THEMES_COUNT;
|
||||
}
|
||||
// Update the theme list to remove any themes over the number we keep
|
||||
_updateUsedThemes(LightweightThemeManager.usedThemes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function _persistImages(aData, aCallback) {
|
||||
function onSuccess(key) {
|
||||
return function () {
|
||||
let current = LightweightThemeManager.currentTheme;
|
||||
if (current && current.id == aData.id) {
|
||||
_prefs.setBoolPref("persisted." + key, true);
|
||||
}
|
||||
if (--numFilesToPersist == 0 && aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let numFilesToPersist = 0;
|
||||
for (let key in PERSIST_FILES) {
|
||||
_prefs.setBoolPref("persisted." + key, false);
|
||||
if (aData[key]) {
|
||||
numFilesToPersist++;
|
||||
_persistImage(aData[key], PERSIST_FILES[key], onSuccess(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _getLocalImageURI(localFileName) {
|
||||
var localFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
localFile.append(localFileName);
|
||||
return Services.io.newFileURI(localFile);
|
||||
}
|
||||
|
||||
function _persistImage(sourceURL, localFileName, successCallback) {
|
||||
if (/^(file|resource):/.test(sourceURL))
|
||||
return;
|
||||
|
||||
var targetURI = _getLocalImageURI(localFileName);
|
||||
var sourceURI = _makeURI(sourceURL);
|
||||
|
||||
var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
|
||||
.createInstance(Ci.nsIWebBrowserPersist);
|
||||
|
||||
persist.persistFlags =
|
||||
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
|
||||
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
|
||||
(PERSIST_BYPASS_CACHE ?
|
||||
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE :
|
||||
Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE);
|
||||
|
||||
persist.progressListener = new _persistProgressListener(successCallback);
|
||||
|
||||
persist.saveURI(sourceURI, null,
|
||||
null, Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE,
|
||||
null, null, targetURI, null);
|
||||
}
|
||||
|
||||
function _persistProgressListener(successCallback) {
|
||||
this.onLocationChange = function() {};
|
||||
this.onProgressChange = function() {};
|
||||
this.onStatusChange = function() {};
|
||||
this.onSecurityChange = function() {};
|
||||
this.onStateChange = function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
if (aRequest &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
|
||||
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
||||
try {
|
||||
if (aRequest.QueryInterface(Ci.nsIHttpChannel).requestSucceeded) {
|
||||
// success
|
||||
successCallback();
|
||||
return;
|
||||
}
|
||||
} catch (e) { }
|
||||
// failure
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AddonManagerPrivate.registerProvider(LightweightThemeManager, [
|
||||
new AddonManagerPrivate.AddonType("theme", URI_EXTENSION_STRINGS,
|
||||
STRING_TYPE_NAME,
|
||||
AddonManager.VIEW_TYPE_LIST, 5000)
|
||||
]);
|
||||
|
|
@ -1,296 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This component serves as integration between the platform and AddonManager.
|
||||
* It is responsible for initializing and shutting down the AddonManager as well
|
||||
* as passing new installs from webpages to the AddonManager.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
||||
|
||||
// The old XPInstall error codes
|
||||
const EXECUTION_ERROR = -203;
|
||||
const CANT_READ_ARCHIVE = -207;
|
||||
const USER_CANCELLED = -210;
|
||||
const DOWNLOAD_ERROR = -228;
|
||||
const UNSUPPORTED_TYPE = -244;
|
||||
const SUCCESS = 0;
|
||||
|
||||
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
|
||||
const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
|
||||
const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
|
||||
|
||||
const MSG_PROMISE_REQUEST = "WebAPIPromiseRequest";
|
||||
const MSG_PROMISE_RESULT = "WebAPIPromiseResult";
|
||||
const MSG_INSTALL_EVENT = "WebAPIInstallEvent";
|
||||
const MSG_INSTALL_CLEANUP = "WebAPICleanup";
|
||||
const MSG_ADDON_EVENT_REQ = "WebAPIAddonEventRequest";
|
||||
const MSG_ADDON_EVENT = "WebAPIAddonEvent";
|
||||
|
||||
const CHILD_SCRIPT = "resource://gre/modules/addons/Content.js";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gSingleton = null;
|
||||
|
||||
function amManager() {
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
/* globals AddonManagerPrivate*/
|
||||
|
||||
Services.mm.loadFrameScript(CHILD_SCRIPT, true);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_ADDONS, this);
|
||||
Services.mm.addMessageListener(MSG_PROMISE_REQUEST, this);
|
||||
Services.mm.addMessageListener(MSG_INSTALL_CLEANUP, this);
|
||||
Services.mm.addMessageListener(MSG_ADDON_EVENT_REQ, this);
|
||||
|
||||
Services.obs.addObserver(this, "message-manager-close", false);
|
||||
Services.obs.addObserver(this, "message-manager-disconnect", false);
|
||||
|
||||
AddonManager.webAPI.setEventHandler(this.sendEvent);
|
||||
|
||||
// Needed so receiveMessage can be called directly by JS callers
|
||||
this.wrappedJSObject = this;
|
||||
}
|
||||
|
||||
amManager.prototype = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "addons-startup":
|
||||
AddonManagerPrivate.startup();
|
||||
break;
|
||||
|
||||
case "message-manager-close":
|
||||
case "message-manager-disconnect":
|
||||
this.childClosed(aSubject);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIAddonManager.idl
|
||||
*/
|
||||
mapURIToAddonID: function(uri, id) {
|
||||
id.value = AddonManager.mapURIToAddonID(uri);
|
||||
return !!id.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstaller.idl
|
||||
*/
|
||||
isInstallEnabled: function(aMimetype, aReferer) {
|
||||
return AddonManager.isInstallEnabled(aMimetype);
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstaller.idl
|
||||
*/
|
||||
installAddonsFromWebpage: function(aMimetype, aBrowser, aInstallingPrincipal,
|
||||
aUris, aHashes, aNames, aIcons, aCallback) {
|
||||
if (aUris.length == 0)
|
||||
return false;
|
||||
|
||||
let retval = true;
|
||||
if (!AddonManager.isInstallAllowed(aMimetype, aInstallingPrincipal)) {
|
||||
aCallback = null;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
let installs = [];
|
||||
function buildNextInstall() {
|
||||
if (aUris.length == 0) {
|
||||
AddonManager.installAddonsFromWebpage(aMimetype, aBrowser, aInstallingPrincipal, installs);
|
||||
return;
|
||||
}
|
||||
let uri = aUris.shift();
|
||||
AddonManager.getInstallForURL(uri, function(aInstall) {
|
||||
function callCallback(aUri, aStatus) {
|
||||
try {
|
||||
aCallback.onInstallEnded(aUri, aStatus);
|
||||
}
|
||||
catch (e) {
|
||||
Components.utils.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (aInstall) {
|
||||
installs.push(aInstall);
|
||||
if (aCallback) {
|
||||
aInstall.addListener({
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
callCallback(uri, USER_CANCELLED);
|
||||
},
|
||||
|
||||
onDownloadFailed: function(aInstall) {
|
||||
if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
|
||||
callCallback(uri, CANT_READ_ARCHIVE);
|
||||
else
|
||||
callCallback(uri, DOWNLOAD_ERROR);
|
||||
},
|
||||
|
||||
onInstallFailed: function(aInstall) {
|
||||
callCallback(uri, EXECUTION_ERROR);
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall, aStatus) {
|
||||
callCallback(uri, SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (aCallback) {
|
||||
aCallback.onInstallEnded(uri, UNSUPPORTED_TYPE);
|
||||
}
|
||||
buildNextInstall();
|
||||
}, aMimetype, aHashes.shift(), aNames.shift(), aIcons.shift(), null, aBrowser);
|
||||
}
|
||||
buildNextInstall();
|
||||
|
||||
return retval;
|
||||
},
|
||||
|
||||
notify: function(aTimer) {
|
||||
AddonManagerPrivate.backgroundUpdateTimerHandler();
|
||||
},
|
||||
|
||||
// Maps message manager instances for content processes to the associated
|
||||
// AddonListener instances.
|
||||
addonListeners: new Map(),
|
||||
|
||||
_addAddonListener(target) {
|
||||
if (!this.addonListeners.has(target)) {
|
||||
let handler = (event, id, needsRestart) => {
|
||||
target.sendAsyncMessage(MSG_ADDON_EVENT, {event, id, needsRestart});
|
||||
};
|
||||
let listener = {
|
||||
onEnabling: (addon, needsRestart) => handler("onEnabling", addon.id, needsRestart),
|
||||
onEnabled: (addon) => handler("onEnabled", addon.id, false),
|
||||
onDisabling: (addon, needsRestart) => handler("onDisabling", addon.id, needsRestart),
|
||||
onDisabled: (addon) => handler("onDisabled", addon.id, false),
|
||||
onInstalling: (addon, needsRestart) => handler("onInstalling", addon.id, needsRestart),
|
||||
onInstalled: (addon) => handler("onInstalled", addon.id, false),
|
||||
onUninstalling: (addon, needsRestart) => handler("onUninstalling", addon.id, needsRestart),
|
||||
onUninstalled: (addon) => handler("onUninstalled", addon.id, false),
|
||||
onOperationCancelled: (addon) => handler("onOperationCancelled", addon.id, false),
|
||||
};
|
||||
this.addonListeners.set(target, listener);
|
||||
AddonManager.addAddonListener(listener);
|
||||
}
|
||||
},
|
||||
|
||||
_removeAddonListener(target) {
|
||||
if (this.addonListeners.has(target)) {
|
||||
AddonManager.removeAddonListener(this.addonListeners.get(target));
|
||||
this.addonListeners.delete(target);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* messageManager callback function.
|
||||
*
|
||||
* Listens to requests from child processes for InstallTrigger
|
||||
* activity, and sends back callbacks.
|
||||
*/
|
||||
receiveMessage: function(aMessage) {
|
||||
let payload = aMessage.data;
|
||||
|
||||
switch (aMessage.name) {
|
||||
case MSG_INSTALL_ENABLED:
|
||||
return AddonManager.isInstallEnabled(payload.mimetype);
|
||||
|
||||
case MSG_INSTALL_ADDONS: {
|
||||
let callback = null;
|
||||
if (payload.callbackID != -1) {
|
||||
let mm = aMessage.target.messageManager;
|
||||
callback = {
|
||||
onInstallEnded: function(url, status) {
|
||||
mm.sendAsyncMessage(MSG_INSTALL_CALLBACK, {
|
||||
callbackID: payload.callbackID,
|
||||
url: url,
|
||||
status: status
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return this.installAddonsFromWebpage(payload.mimetype,
|
||||
aMessage.target, payload.triggeringPrincipal, payload.uris,
|
||||
payload.hashes, payload.names, payload.icons, callback);
|
||||
}
|
||||
|
||||
case MSG_PROMISE_REQUEST: {
|
||||
let mm = aMessage.target.messageManager;
|
||||
let resolve = (value) => {
|
||||
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
callbackID: payload.callbackID,
|
||||
resolve: value
|
||||
});
|
||||
}
|
||||
let reject = (value) => {
|
||||
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
|
||||
callbackID: payload.callbackID,
|
||||
reject: value
|
||||
});
|
||||
}
|
||||
|
||||
let API = AddonManager.webAPI;
|
||||
if (payload.type in API) {
|
||||
API[payload.type](aMessage.target, ...payload.args).then(resolve, reject);
|
||||
}
|
||||
else {
|
||||
reject("Unknown Add-on API request.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_INSTALL_CLEANUP: {
|
||||
AddonManager.webAPI.clearInstalls(payload.ids);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_ADDON_EVENT_REQ: {
|
||||
let target = aMessage.target.messageManager;
|
||||
if (payload.enabled) {
|
||||
this._addAddonListener(target);
|
||||
} else {
|
||||
this._removeAddonListener(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
childClosed(target) {
|
||||
AddonManager.webAPI.clearInstallsFrom(target);
|
||||
this._removeAddonListener(target);
|
||||
},
|
||||
|
||||
sendEvent(mm, data) {
|
||||
mm.sendAsyncMessage(MSG_INSTALL_EVENT, data);
|
||||
},
|
||||
|
||||
classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
|
||||
_xpcom_factory: {
|
||||
createInstance: function(aOuter, aIid) {
|
||||
if (aOuter != null)
|
||||
throw Components.Exception("Component does not support aggregation",
|
||||
Cr.NS_ERROR_NO_AGGREGATION);
|
||||
|
||||
if (!gSingleton)
|
||||
gSingleton = new amManager();
|
||||
return gSingleton.QueryInterface(aIid);
|
||||
}
|
||||
},
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIAddonManager,
|
||||
Ci.amIWebInstaller,
|
||||
Ci.nsITimerCallback,
|
||||
Ci.nsIObserver,
|
||||
Ci.nsIMessageListener])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amManager]);
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
const XPI_CONTENT_TYPE = "application/x-xpinstall";
|
||||
const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function amContentHandler() {
|
||||
}
|
||||
|
||||
amContentHandler.prototype = {
|
||||
/**
|
||||
* Handles a new request for an application/x-xpinstall file.
|
||||
*
|
||||
* @param aMimetype
|
||||
* The mimetype of the file
|
||||
* @param aContext
|
||||
* The context passed to nsIChannel.asyncOpen
|
||||
* @param aRequest
|
||||
* The nsIRequest dealing with the content
|
||||
*/
|
||||
handleContent: function(aMimetype, aContext, aRequest) {
|
||||
if (aMimetype != XPI_CONTENT_TYPE)
|
||||
throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
|
||||
|
||||
if (!(aRequest instanceof Ci.nsIChannel))
|
||||
throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
|
||||
|
||||
let uri = aRequest.URI;
|
||||
|
||||
let window = null;
|
||||
let callbacks = aRequest.notificationCallbacks ?
|
||||
aRequest.notificationCallbacks :
|
||||
aRequest.loadGroup.notificationCallbacks;
|
||||
if (callbacks)
|
||||
window = callbacks.getInterface(Ci.nsIDOMWindow);
|
||||
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
let installs = {
|
||||
uris: [uri.spec],
|
||||
hashes: [null],
|
||||
names: [null],
|
||||
icons: [null],
|
||||
mimetype: XPI_CONTENT_TYPE,
|
||||
triggeringPrincipal: aRequest.loadInfo.triggeringPrincipal,
|
||||
callbackID: -1
|
||||
};
|
||||
|
||||
if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||
// When running in the main process this might be a frame inside an
|
||||
// in-content UI page, walk up to find the first frame element in a chrome
|
||||
// privileged document
|
||||
let element = window.frameElement;
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
|
||||
element = element.ownerDocument.defaultView.frameElement;
|
||||
|
||||
if (element) {
|
||||
let listener = Cc["@mozilla.org/addons/integration;1"].
|
||||
getService(Ci.nsIMessageListener);
|
||||
listener.wrappedJSObject.receiveMessage({
|
||||
name: MSG_INSTALL_ADDONS,
|
||||
target: element,
|
||||
data: installs,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to sending through the message manager
|
||||
let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
|
||||
messageManager.sendAsyncMessage(MSG_INSTALL_ADDONS, installs);
|
||||
},
|
||||
|
||||
classID: Components.ID("{7beb3ba8-6ec3-41b4-b67c-da89b8518922}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentHandler]),
|
||||
|
||||
log : function(aMsg) {
|
||||
let msg = "amContentHandler.js: " + (aMsg.join ? aMsg.join("") : aMsg);
|
||||
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService).
|
||||
logStringMessage(msg);
|
||||
dump(msg + "\n");
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([amContentHandler]);
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* A service to make some AddonManager functionality available to C++ callers.
|
||||
* Javascript callers should still use AddonManager.jsm directly.
|
||||
*/
|
||||
[scriptable, function, uuid(7b45d82d-7ad5-48d7-9b05-f32eb9818cd4)]
|
||||
interface amIAddonManager : nsISupports
|
||||
{
|
||||
/**
|
||||
* Synchronously map a URI to the corresponding Addon ID.
|
||||
*
|
||||
* Mappable URIs are limited to in-application resources belonging to the
|
||||
* add-on, such as Javascript compartments, XUL windows, XBL bindings, etc.
|
||||
* but do not include URIs from meta data, such as the add-on homepage.
|
||||
*
|
||||
* @param aURI
|
||||
* The nsIURI to map
|
||||
* @return
|
||||
* true if the URI has been mapped successfully to an Addon ID
|
||||
*/
|
||||
boolean mapURIToAddonID(in nsIURI aURI, out AUTF8String aID);
|
||||
};
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* This service maps file system paths where add-ons reside to the ID
|
||||
* of the add-on. Paths are added by the add-on manager. They can
|
||||
* looked up by anyone.
|
||||
*/
|
||||
[scriptable, uuid(fcd9e270-dfb1-11e3-8b68-0800200c9a66)]
|
||||
interface amIAddonPathService : nsISupports
|
||||
{
|
||||
/**
|
||||
* Given a path to a file, return the ID of the add-on that the file belongs
|
||||
* to. Returns an empty string if there is no add-on there. Note that if an
|
||||
* add-on is located at /a/b/c, then looking up the path /a/b/c/d will return
|
||||
* that add-on.
|
||||
*/
|
||||
AString findAddonId(in AString path);
|
||||
|
||||
/**
|
||||
* Call this function to inform the service that the given file system path is
|
||||
* associated with the given add-on ID.
|
||||
*/
|
||||
void insertPath(in AString path, in AString addonId);
|
||||
|
||||
/**
|
||||
* Given a URI to a file, return the ID of the add-on that the file belongs
|
||||
* to. Returns an empty string if there is no add-on there.
|
||||
*/
|
||||
AString mapURIToAddonId(in nsIURI aURI);
|
||||
};
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMElement;
|
||||
interface nsIURI;
|
||||
interface nsIVariant;
|
||||
|
||||
/**
|
||||
* amIWebInstallInfo is used by the default implementation of
|
||||
* amIWebInstallListener to communicate with the running application and allow
|
||||
* it to warn the user about blocked installs and start the installs running.
|
||||
*/
|
||||
[scriptable, uuid(fa0b47a3-f819-47ac-bc66-4bd1d7f67b1d)]
|
||||
interface amIWebInstallInfo : nsISupports
|
||||
{
|
||||
readonly attribute nsIDOMElement browser;
|
||||
readonly attribute nsIURI originatingURI;
|
||||
readonly attribute nsIVariant installs;
|
||||
|
||||
/**
|
||||
* Starts all installs.
|
||||
*/
|
||||
void install();
|
||||
};
|
||||
|
||||
/**
|
||||
* The registered amIWebInstallListener is used to notify about new installs
|
||||
* triggered by websites. The default implementation displays a confirmation
|
||||
* dialog when add-ons are ready to install and uses the observer service to
|
||||
* notify when installations are blocked.
|
||||
*/
|
||||
[scriptable, uuid(d9240d4b-6b3a-4cad-b402-de6c93337e0c)]
|
||||
interface amIWebInstallListener : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when installation by websites is currently disabled.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
void onWebInstallDisabled(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
|
||||
/**
|
||||
* Called when the website is not allowed to directly prompt the user to
|
||||
* install add-ons.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
* @return true if the caller should start the installs
|
||||
*/
|
||||
boolean onWebInstallBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
|
||||
/**
|
||||
* Called when a website wants to ask the user to install add-ons.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were requested
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
* @return true if the caller should start the installs
|
||||
*/
|
||||
boolean onWebInstallRequested(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
||||
|
||||
[scriptable, uuid(a80b89ad-bb1a-4c43-9cb7-3ae656556f78)]
|
||||
interface amIWebInstallListener2 : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when a non-same-origin resource attempted to initiate an install.
|
||||
* Installs will have already been cancelled and cannot be restarted.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were blocked
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
boolean onWebInstallOriginBlocked(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
||||
|
||||
/**
|
||||
* amIWebInstallPrompt is used, if available, by the default implementation of
|
||||
* amIWebInstallInfo to display a confirmation UI to the user before running
|
||||
* installs.
|
||||
*/
|
||||
[scriptable, uuid(386906f1-4d18-45bf-bc81-5dcd68e42c3b)]
|
||||
interface amIWebInstallPrompt : nsISupports
|
||||
{
|
||||
/**
|
||||
* Get a confirmation that the user wants to start the installs.
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that triggered the installs
|
||||
* @param aUri
|
||||
* The URI of the site that triggered the installs
|
||||
* @param aInstalls
|
||||
* The AddonInstalls that were requested
|
||||
* @param aCount
|
||||
* The number of AddonInstalls
|
||||
*/
|
||||
void confirm(in nsIDOMElement aBrowser, in nsIURI aUri,
|
||||
[array, size_is(aCount)] in nsIVariant aInstalls,
|
||||
[optional] in uint32_t aCount);
|
||||
};
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIDOMElement;
|
||||
interface nsIVariant;
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* A callback function used to notify webpages when a requested install has
|
||||
* ended.
|
||||
*
|
||||
* NOTE: This is *not* the same as InstallListener.
|
||||
*/
|
||||
[scriptable, function, uuid(bb22f5c0-3ca1-48f6-873c-54e87987700f)]
|
||||
interface amIInstallCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Called when an install completes or fails.
|
||||
*
|
||||
* @param aUrl
|
||||
* The url of the add-on being installed
|
||||
* @param aStatus
|
||||
* 0 if the install was successful or negative if not
|
||||
*/
|
||||
void onInstallEnded(in AString aUrl, in int32_t aStatus);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This interface is used to allow webpages to start installing add-ons.
|
||||
*/
|
||||
[scriptable, uuid(658d6c09-15e0-4688-bee8-8551030472a9)]
|
||||
interface amIWebInstaller : nsISupports
|
||||
{
|
||||
/**
|
||||
* Checks if installation is enabled for a webpage.
|
||||
*
|
||||
* @param aMimetype
|
||||
* The mimetype for the add-on to be installed
|
||||
* @param referer
|
||||
* The URL of the webpage trying to install an add-on
|
||||
* @return true if installation is enabled
|
||||
*/
|
||||
boolean isInstallEnabled(in AString aMimetype, in nsIURI aReferer);
|
||||
|
||||
/**
|
||||
* Installs an array of add-ons at the request of a webpage
|
||||
*
|
||||
* @param aMimetype
|
||||
* The mimetype for the add-ons
|
||||
* @param aBrowser
|
||||
* The browser installing the add-ons.
|
||||
* @param aReferer
|
||||
* The URI for the webpage installing the add-ons
|
||||
* @param aUris
|
||||
* The URIs of add-ons to be installed
|
||||
* @param aHashes
|
||||
* The hashes for the add-ons to be installed
|
||||
* @param aNames
|
||||
* The names for the add-ons to be installed
|
||||
* @param aIcons
|
||||
* The icons for the add-ons to be installed
|
||||
* @param aCallback
|
||||
* An optional callback to notify about installation success and
|
||||
* failure
|
||||
* @param aInstallCount
|
||||
* An optional argument including the number of add-ons to install
|
||||
* @return true if the installation was successfully started
|
||||
*/
|
||||
boolean installAddonsFromWebpage(in AString aMimetype,
|
||||
in nsIDOMElement aBrowser,
|
||||
in nsIURI aReferer,
|
||||
[array, size_is(aInstallCount)] in wstring aUris,
|
||||
[array, size_is(aInstallCount)] in wstring aHashes,
|
||||
[array, size_is(aInstallCount)] in wstring aNames,
|
||||
[array, size_is(aInstallCount)] in wstring aIcons,
|
||||
[optional] in amIInstallCallback aCallback,
|
||||
[optional] in uint32_t aInstallCount);
|
||||
};
|
||||
|
|
@ -1,240 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
|
||||
const XPINSTALL_MIMETYPE = "application/x-xpinstall";
|
||||
|
||||
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
|
||||
const MSG_INSTALL_ADDONS = "WebInstallerInstallAddonsFromWebpage";
|
||||
const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
|
||||
|
||||
|
||||
var log = Log.repository.getLogger("AddonManager.InstallTrigger");
|
||||
log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"];
|
||||
|
||||
function CallbackObject(id, callback, urls, mediator) {
|
||||
this.id = id;
|
||||
this.callback = callback;
|
||||
this.urls = new Set(urls);
|
||||
this.callCallback = function(url, status) {
|
||||
try {
|
||||
this.callback(url, status);
|
||||
}
|
||||
catch (e) {
|
||||
log.warn("InstallTrigger callback threw an exception: " + e);
|
||||
}
|
||||
|
||||
this.urls.delete(url);
|
||||
if (this.urls.size == 0)
|
||||
mediator._callbacks.delete(id);
|
||||
};
|
||||
}
|
||||
|
||||
function RemoteMediator(window) {
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
let utils = window.getInterface(Ci.nsIDOMWindowUtils);
|
||||
this._windowID = utils.currentInnerWindowID;
|
||||
|
||||
this.mm = window
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
this.mm.addWeakMessageListener(MSG_INSTALL_CALLBACK, this);
|
||||
|
||||
this._lastCallbackID = 0;
|
||||
this._callbacks = new Map();
|
||||
}
|
||||
|
||||
RemoteMediator.prototype = {
|
||||
receiveMessage: function(message) {
|
||||
if (message.name == MSG_INSTALL_CALLBACK) {
|
||||
let payload = message.data;
|
||||
let callbackHandler = this._callbacks.get(payload.callbackID);
|
||||
if (callbackHandler) {
|
||||
callbackHandler.callCallback(payload.url, payload.status);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
enabled: function(url) {
|
||||
let params = {
|
||||
mimetype: XPINSTALL_MIMETYPE
|
||||
};
|
||||
return this.mm.sendSyncMessage(MSG_INSTALL_ENABLED, params)[0];
|
||||
},
|
||||
|
||||
install: function(installs, principal, callback, window) {
|
||||
let callbackID = this._addCallback(callback, installs.uris);
|
||||
|
||||
installs.mimetype = XPINSTALL_MIMETYPE;
|
||||
installs.triggeringPrincipal = principal;
|
||||
installs.callbackID = callbackID;
|
||||
|
||||
if (Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
|
||||
// When running in the main process this might be a frame inside an
|
||||
// in-content UI page, walk up to find the first frame element in a chrome
|
||||
// privileged document
|
||||
let element = window.frameElement;
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
while (element && !ssm.isSystemPrincipal(element.ownerDocument.nodePrincipal))
|
||||
element = element.ownerDocument.defaultView.frameElement;
|
||||
|
||||
if (element) {
|
||||
let listener = Cc["@mozilla.org/addons/integration;1"].
|
||||
getService(Ci.nsIMessageListener);
|
||||
return listener.wrappedJSObject.receiveMessage({
|
||||
name: MSG_INSTALL_ADDONS,
|
||||
target: element,
|
||||
data: installs,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to sending through the message manager
|
||||
let messageManager = window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
|
||||
return messageManager.sendSyncMessage(MSG_INSTALL_ADDONS, installs)[0];
|
||||
},
|
||||
|
||||
_addCallback: function(callback, urls) {
|
||||
if (!callback || typeof callback != "function")
|
||||
return -1;
|
||||
|
||||
let callbackID = this._windowID + "-" + ++this._lastCallbackID;
|
||||
let callbackObject = new CallbackObject(callbackID, callback, urls, this);
|
||||
this._callbacks.set(callbackID, callbackObject);
|
||||
return callbackID;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference])
|
||||
};
|
||||
|
||||
|
||||
function InstallTrigger() {
|
||||
}
|
||||
|
||||
InstallTrigger.prototype = {
|
||||
// Here be magic. We've declared ourselves as providing the
|
||||
// nsIDOMGlobalPropertyInitializer interface, and are registered in the
|
||||
// "JavaScript-global-property" category in the XPCOM category manager. This
|
||||
// means that for newly created windows, XPCOM will createinstance this
|
||||
// object, and then call init, passing in the window for which we need to
|
||||
// provide an instance. We then initialize ourselves and return the webidl
|
||||
// version of this object using the webidl-provided _create method, which
|
||||
// XPCOM will then duly expose as a property value on the window. All this
|
||||
// indirection is necessary because webidl does not (yet) support statics
|
||||
// (bug 863952). See bug 926712 for more details about this implementation.
|
||||
init: function(window) {
|
||||
this._window = window;
|
||||
this._principal = window.document.nodePrincipal;
|
||||
this._url = window.document.documentURIObject;
|
||||
|
||||
try {
|
||||
this._mediator = new RemoteMediator(window);
|
||||
} catch (ex) {
|
||||
// If we can't set up IPC (e.g., because this is a top-level window
|
||||
// or something), then don't expose InstallTrigger.
|
||||
return null;
|
||||
}
|
||||
|
||||
return window.InstallTriggerImpl._create(window, this);
|
||||
},
|
||||
|
||||
enabled: function() {
|
||||
return this._mediator.enabled(this._url.spec);
|
||||
},
|
||||
|
||||
updateEnabled: function() {
|
||||
return this.enabled();
|
||||
},
|
||||
|
||||
install: function(installs, callback) {
|
||||
let installData = {
|
||||
uris: [],
|
||||
hashes: [],
|
||||
names: [],
|
||||
icons: [],
|
||||
};
|
||||
|
||||
for (let name of Object.keys(installs)) {
|
||||
let item = installs[name];
|
||||
if (typeof item === "string") {
|
||||
item = { URL: item };
|
||||
}
|
||||
if (!item.URL) {
|
||||
throw new this._window.Error("Missing URL property for '" + name + "'");
|
||||
}
|
||||
|
||||
let url = this._resolveURL(item.URL);
|
||||
if (!this._checkLoadURIFromScript(url)) {
|
||||
throw new this._window.Error("Insufficient permissions to install: " + url.spec);
|
||||
}
|
||||
|
||||
let iconUrl = null;
|
||||
if (item.IconURL) {
|
||||
iconUrl = this._resolveURL(item.IconURL);
|
||||
if (!this._checkLoadURIFromScript(iconUrl)) {
|
||||
iconUrl = null; // If page can't load the icon, just ignore it
|
||||
}
|
||||
}
|
||||
|
||||
installData.uris.push(url.spec);
|
||||
installData.hashes.push(item.Hash || null);
|
||||
installData.names.push(name);
|
||||
installData.icons.push(iconUrl ? iconUrl.spec : null);
|
||||
}
|
||||
|
||||
return this._mediator.install(installData, this._principal, callback, this._window);
|
||||
},
|
||||
|
||||
startSoftwareUpdate: function(url, flags) {
|
||||
let filename = Services.io.newURI(url, null, null)
|
||||
.QueryInterface(Ci.nsIURL)
|
||||
.filename;
|
||||
let args = {};
|
||||
args[filename] = { "URL": url };
|
||||
return this.install(args);
|
||||
},
|
||||
|
||||
installChrome: function(type, url, skin) {
|
||||
return this.startSoftwareUpdate(url);
|
||||
},
|
||||
|
||||
_resolveURL: function(url) {
|
||||
return Services.io.newURI(url, null, this._url);
|
||||
},
|
||||
|
||||
_checkLoadURIFromScript: function(uri) {
|
||||
let secman = Services.scriptSecurityManager;
|
||||
try {
|
||||
secman.checkLoadURIWithPrincipal(this._principal,
|
||||
uri,
|
||||
secman.DISALLOW_INHERIT_PRINCIPAL);
|
||||
return true;
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
classID: Components.ID("{9df8ef2b-94da-45c9-ab9f-132eb55fddf1}"),
|
||||
contractID: "@mozilla.org/addons/installtrigger;1",
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIDOMGlobalPropertyInitializer])
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([InstallTrigger]);
|
||||
|
|
@ -1,269 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
const MSG_PROMISE_REQUEST = "WebAPIPromiseRequest";
|
||||
const MSG_PROMISE_RESULT = "WebAPIPromiseResult";
|
||||
const MSG_INSTALL_EVENT = "WebAPIInstallEvent";
|
||||
const MSG_INSTALL_CLEANUP = "WebAPICleanup";
|
||||
const MSG_ADDON_EVENT_REQ = "WebAPIAddonEventRequest";
|
||||
const MSG_ADDON_EVENT = "WebAPIAddonEvent";
|
||||
|
||||
class APIBroker {
|
||||
constructor(mm) {
|
||||
this.mm = mm;
|
||||
|
||||
this._promises = new Map();
|
||||
|
||||
// _installMap maps integer ids to DOM AddonInstall instances
|
||||
this._installMap = new Map();
|
||||
|
||||
this.mm.addMessageListener(MSG_PROMISE_RESULT, this);
|
||||
this.mm.addMessageListener(MSG_INSTALL_EVENT, this);
|
||||
|
||||
this._eventListener = null;
|
||||
}
|
||||
|
||||
receiveMessage(message) {
|
||||
let payload = message.data;
|
||||
|
||||
switch (message.name) {
|
||||
case MSG_PROMISE_RESULT: {
|
||||
if (!this._promises.has(payload.callbackID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let resolve = this._promises.get(payload.callbackID);
|
||||
this._promises.delete(payload.callbackID);
|
||||
resolve(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_INSTALL_EVENT: {
|
||||
let install = this._installMap.get(payload.id);
|
||||
if (!install) {
|
||||
let err = new Error(`Got install event for unknown install ${payload.id}`);
|
||||
Cu.reportError(err);
|
||||
return;
|
||||
}
|
||||
install._dispatch(payload);
|
||||
break;
|
||||
}
|
||||
|
||||
case MSG_ADDON_EVENT: {
|
||||
if (this._eventListener) {
|
||||
this._eventListener(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sendRequest(type, ...args) {
|
||||
return new Promise(resolve => {
|
||||
let callbackID = APIBroker._nextID++;
|
||||
|
||||
this._promises.set(callbackID, resolve);
|
||||
this.mm.sendAsyncMessage(MSG_PROMISE_REQUEST, { type, callbackID, args });
|
||||
});
|
||||
}
|
||||
|
||||
setAddonListener(callback) {
|
||||
this._eventListener = callback;
|
||||
if (callback) {
|
||||
this.mm.addMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: true});
|
||||
} else {
|
||||
this.mm.removeMessageListener(MSG_ADDON_EVENT, this);
|
||||
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: false});
|
||||
}
|
||||
}
|
||||
|
||||
sendCleanup(ids) {
|
||||
this.setAddonListener(null);
|
||||
this.mm.sendAsyncMessage(MSG_INSTALL_CLEANUP, { ids });
|
||||
}
|
||||
}
|
||||
|
||||
APIBroker._nextID = 0;
|
||||
|
||||
// Base class for building classes to back content-exposed interfaces.
|
||||
class APIObject {
|
||||
init(window, broker, properties) {
|
||||
this.window = window;
|
||||
this.broker = broker;
|
||||
|
||||
// Copy any provided properties onto this object, webidl bindings
|
||||
// will only expose to content what should be exposed.
|
||||
for (let key of Object.keys(properties)) {
|
||||
this[key] = properties[key];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to implement an asychronous method visible to content, where
|
||||
* the method is implemented by sending a message to the parent process
|
||||
* and then wrapping the returned object or error in an appropriate object.
|
||||
* This helper method ensures that:
|
||||
* - Returned Promise objects are from the content window
|
||||
* - Rejected Promises have Error objects from the content window
|
||||
* - Only non-internal errors are exposed to the caller
|
||||
*
|
||||
* @param {string} apiRequest The command to invoke in the parent process.
|
||||
* @param {array<cloneable>} apiArgs The arguments to include with the
|
||||
* request to the parent process.
|
||||
* @param {function} resultConvert If provided, a function called with the
|
||||
* result from the parent process as an
|
||||
* argument. Used to convert the result
|
||||
* into something appropriate for content.
|
||||
* @returns {Promise<any>} A Promise suitable for passing directly to content.
|
||||
*/
|
||||
_apiTask(apiRequest, apiArgs, resultConverter) {
|
||||
let win = this.window;
|
||||
let broker = this.broker;
|
||||
return new win.Promise((resolve, reject) => {
|
||||
Task.spawn(function*() {
|
||||
let result = yield broker.sendRequest(apiRequest, ...apiArgs);
|
||||
if ("reject" in result) {
|
||||
let err = new win.Error(result.reject.message);
|
||||
// We don't currently put any other properties onto Errors
|
||||
// generated by mozAddonManager. If/when we do, they will
|
||||
// need to get copied here.
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = result.resolve;
|
||||
if (resultConverter) {
|
||||
obj = resultConverter(obj);
|
||||
}
|
||||
resolve(obj);
|
||||
}).catch(err => {
|
||||
Cu.reportError(err);
|
||||
reject(new win.Error("Unexpected internal error"));
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class Addon extends APIObject {
|
||||
constructor(...args) {
|
||||
super();
|
||||
this.init(...args);
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
return this._apiTask("addonUninstall", [this.id]);
|
||||
}
|
||||
|
||||
setEnabled(value) {
|
||||
return this._apiTask("addonSetEnabled", [this.id, value]);
|
||||
}
|
||||
}
|
||||
|
||||
class AddonInstall extends APIObject {
|
||||
constructor(window, broker, properties) {
|
||||
super();
|
||||
this.init(window, broker, properties);
|
||||
|
||||
broker._installMap.set(properties.id, this);
|
||||
}
|
||||
|
||||
_dispatch(data) {
|
||||
// The message for the event includes updated copies of all install
|
||||
// properties. Use the usual "let webidl filter visible properties" trick.
|
||||
for (let key of Object.keys(data)) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
|
||||
let event = new this.window.Event(data.event);
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
}
|
||||
|
||||
install() {
|
||||
return this._apiTask("addonInstallDoInstall", [this.id]);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
return this._apiTask("addonInstallCancel", [this.id]);
|
||||
}
|
||||
}
|
||||
|
||||
class WebAPI extends APIObject {
|
||||
constructor() {
|
||||
super();
|
||||
this.allInstalls = [];
|
||||
this.listenerCount = 0;
|
||||
}
|
||||
|
||||
init(window) {
|
||||
let mm = window
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
let broker = new APIBroker(mm);
|
||||
|
||||
super.init(window, broker, {});
|
||||
|
||||
window.addEventListener("unload", event => {
|
||||
this.broker.sendCleanup(this.allInstalls);
|
||||
});
|
||||
}
|
||||
|
||||
getAddonByID(id) {
|
||||
return this._apiTask("getAddonByID", [id], addonInfo => {
|
||||
if (!addonInfo) {
|
||||
return null;
|
||||
}
|
||||
let addon = new Addon(this.window, this.broker, addonInfo);
|
||||
return this.window.Addon._create(this.window, addon);
|
||||
});
|
||||
}
|
||||
|
||||
createInstall(options) {
|
||||
return this._apiTask("createInstall", [options], installInfo => {
|
||||
if (!installInfo) {
|
||||
return null;
|
||||
}
|
||||
let install = new AddonInstall(this.window, this.broker, installInfo);
|
||||
this.allInstalls.push(installInfo.id);
|
||||
return this.window.AddonInstall._create(this.window, install);
|
||||
});
|
||||
}
|
||||
|
||||
eventListenerWasAdded(type) {
|
||||
if (this.listenerCount == 0) {
|
||||
this.broker.setAddonListener(data => {
|
||||
let event = new this.window.AddonEvent(data.event, data);
|
||||
this.__DOM_IMPL__.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
this.listenerCount++;
|
||||
}
|
||||
|
||||
eventListenerWasRemoved(type) {
|
||||
this.listenerCount--;
|
||||
if (this.listenerCount == 0) {
|
||||
this.broker.setAddonListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
QueryInterface(iid) {
|
||||
if (iid.equals(WebAPI.classID) || iid.equals(Ci.nsISupports)
|
||||
|| iid.equals(Ci.nsIDOMGlobalPropertyInitializer)) {
|
||||
return this;
|
||||
}
|
||||
return Cr.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
}
|
||||
|
||||
WebAPI.prototype.classID = Components.ID("{8866d8e3-4ea5-48b7-a891-13ba0ac15235}");
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([WebAPI]);
|
||||
|
|
@ -1,348 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* This is a default implementation of amIWebInstallListener that should work
|
||||
* for most applications but can be overriden. It notifies the observer service
|
||||
* about blocked installs. For normal installs it pops up an install
|
||||
* confirmation when all the add-ons have been downloaded.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PromptUtils", "resource://gre/modules/SharedPromptUtils.jsm");
|
||||
|
||||
const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
|
||||
|
||||
// Installation can begin from any of these states
|
||||
const READY_STATES = [
|
||||
AddonManager.STATE_AVAILABLE,
|
||||
AddonManager.STATE_DOWNLOAD_FAILED,
|
||||
AddonManager.STATE_INSTALL_FAILED,
|
||||
AddonManager.STATE_CANCELLED
|
||||
];
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
const LOGGER_ID = "addons.weblistener";
|
||||
|
||||
// Create a new logger for use by the Addons Web Listener
|
||||
// (Requires AddonManager.jsm)
|
||||
var logger = Log.repository.getLogger(LOGGER_ID);
|
||||
|
||||
function notifyObservers(aTopic, aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, aTopic, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new installer to monitor downloads and prompt to install when
|
||||
* ready
|
||||
*
|
||||
* @param aBrowser
|
||||
* The browser that started the installations
|
||||
* @param aUrl
|
||||
* The URL that started the installations
|
||||
* @param aInstalls
|
||||
* An array of AddonInstalls
|
||||
*/
|
||||
function Installer(aBrowser, aUrl, aInstalls) {
|
||||
this.browser = aBrowser;
|
||||
this.url = aUrl;
|
||||
this.downloads = aInstalls;
|
||||
this.installed = [];
|
||||
|
||||
notifyObservers("addon-install-started", aBrowser, aUrl, aInstalls);
|
||||
|
||||
for (let install of aInstalls) {
|
||||
install.addListener(this);
|
||||
|
||||
// Start downloading if it hasn't already begun
|
||||
if (READY_STATES.indexOf(install.state) != -1)
|
||||
install.install();
|
||||
}
|
||||
|
||||
this.checkAllDownloaded();
|
||||
}
|
||||
|
||||
Installer.prototype = {
|
||||
browser: null,
|
||||
downloads: null,
|
||||
installed: null,
|
||||
isDownloading: true,
|
||||
|
||||
/**
|
||||
* Checks if all downloads are now complete and if so prompts to install.
|
||||
*/
|
||||
checkAllDownloaded: function() {
|
||||
// Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
|
||||
// installs.
|
||||
if (!this.isDownloading)
|
||||
return;
|
||||
|
||||
var failed = [];
|
||||
var installs = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_AVAILABLE:
|
||||
case AddonManager.STATE_DOWNLOADING:
|
||||
// Exit early if any add-ons haven't started downloading yet or are
|
||||
// still downloading
|
||||
return;
|
||||
case AddonManager.STATE_DOWNLOAD_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
// App disabled items are not compatible and so fail to install
|
||||
if (install.addon.appDisabled)
|
||||
failed.push(install);
|
||||
else
|
||||
installs.push(install);
|
||||
|
||||
if (install.linkedInstalls) {
|
||||
for (let linkedInstall of install.linkedInstalls) {
|
||||
linkedInstall.addListener(this);
|
||||
// Corrupt or incompatible items fail to install
|
||||
if (linkedInstall.state == AddonManager.STATE_DOWNLOAD_FAILED || linkedInstall.addon.appDisabled)
|
||||
failed.push(linkedInstall);
|
||||
else
|
||||
installs.push(linkedInstall);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AddonManager.STATE_CANCELLED:
|
||||
// Just ignore cancelled downloads
|
||||
break;
|
||||
default:
|
||||
logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
|
||||
install.state);
|
||||
}
|
||||
}
|
||||
|
||||
this.isDownloading = false;
|
||||
this.downloads = installs;
|
||||
|
||||
if (failed.length > 0) {
|
||||
// Stop listening and cancel any installs that are failed because of
|
||||
// compatibility reasons.
|
||||
for (let install of failed) {
|
||||
if (install.state == AddonManager.STATE_DOWNLOADED) {
|
||||
install.removeListener(this);
|
||||
install.cancel();
|
||||
}
|
||||
}
|
||||
notifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
}
|
||||
|
||||
// If none of the downloads were successful then exit early
|
||||
if (this.downloads.length == 0)
|
||||
return;
|
||||
|
||||
// Check for a custom installation prompt that may be provided by the
|
||||
// applicaton
|
||||
if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
|
||||
try {
|
||||
let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
|
||||
getService(Ci.amIWebInstallPrompt);
|
||||
prompt.confirm(this.browser, this.url, this.downloads, this.downloads.length);
|
||||
return;
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
if (Preferences.get("xpinstall.customConfirmationUI", false)) {
|
||||
notifyObservers("addon-install-confirmation", this.browser, this.url, this.downloads);
|
||||
return;
|
||||
}
|
||||
|
||||
let args = {};
|
||||
args.url = this.url;
|
||||
args.installs = this.downloads;
|
||||
args.wrappedJSObject = args;
|
||||
|
||||
try {
|
||||
Cc["@mozilla.org/base/telemetry;1"].
|
||||
getService(Ci.nsITelemetry).
|
||||
getHistogramById("SECURITY_UI").
|
||||
add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
|
||||
let parentWindow = null;
|
||||
if (this.browser) {
|
||||
parentWindow = this.browser.ownerDocument.defaultView;
|
||||
PromptUtils.fireDialogEvent(parentWindow, "DOMWillOpenModalDialog", this.browser);
|
||||
}
|
||||
Services.ww.openWindow(parentWindow, URI_XPINSTALL_DIALOG,
|
||||
null, "chrome,modal,centerscreen", args);
|
||||
} catch (e) {
|
||||
logger.warn("Exception showing install confirmation dialog", e);
|
||||
for (let install of this.downloads) {
|
||||
install.removeListener(this);
|
||||
// Cancel the installs, as currently there is no way to make them fail
|
||||
// from here.
|
||||
install.cancel();
|
||||
}
|
||||
notifyObservers("addon-install-cancelled", this.browser, this.url,
|
||||
this.downloads);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if all installs are now complete and if so notifies observers.
|
||||
*/
|
||||
checkAllInstalled: function() {
|
||||
var failed = [];
|
||||
|
||||
for (let install of this.downloads) {
|
||||
switch (install.state) {
|
||||
case AddonManager.STATE_DOWNLOADED:
|
||||
case AddonManager.STATE_INSTALLING:
|
||||
// Exit early if any add-ons haven't started installing yet or are
|
||||
// still installing
|
||||
return;
|
||||
case AddonManager.STATE_INSTALL_FAILED:
|
||||
failed.push(install);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.downloads = null;
|
||||
|
||||
if (failed.length > 0)
|
||||
notifyObservers("addon-install-failed", this.browser, this.url, failed);
|
||||
|
||||
if (this.installed.length > 0)
|
||||
notifyObservers("addon-install-complete", this.browser, this.url, this.installed);
|
||||
this.installed = null;
|
||||
},
|
||||
|
||||
onDownloadCancelled: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadFailed: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllDownloaded();
|
||||
},
|
||||
|
||||
onDownloadEnded: function(aInstall) {
|
||||
this.checkAllDownloaded();
|
||||
return false;
|
||||
},
|
||||
|
||||
onInstallCancelled: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallFailed: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.checkAllInstalled();
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall) {
|
||||
aInstall.removeListener(this);
|
||||
this.installed.push(aInstall);
|
||||
|
||||
// If installing a theme that is disabled and can be enabled then enable it
|
||||
if (aInstall.addon.type == "theme" &&
|
||||
aInstall.addon.userDisabled == true &&
|
||||
aInstall.addon.appDisabled == false) {
|
||||
aInstall.addon.userDisabled = false;
|
||||
}
|
||||
|
||||
this.checkAllInstalled();
|
||||
}
|
||||
};
|
||||
|
||||
function extWebInstallListener() {
|
||||
}
|
||||
|
||||
extWebInstallListener.prototype = {
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallDisabled: function(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-disabled", null);
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallOriginBlocked: function(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
install: function() {
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-origin-blocked", null);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallBlocked: function(aBrowser, aUri, aInstalls) {
|
||||
let info = {
|
||||
browser: aBrowser,
|
||||
originatingURI: aUri,
|
||||
installs: aInstalls,
|
||||
|
||||
install: function() {
|
||||
new Installer(this.browser, this.originatingURI, this.installs);
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
|
||||
};
|
||||
Services.obs.notifyObservers(info, "addon-install-blocked", null);
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* @see amIWebInstallListener.idl
|
||||
*/
|
||||
onWebInstallRequested: function(aBrowser, aUri, aInstalls) {
|
||||
new Installer(aBrowser, aUri, aInstalls);
|
||||
|
||||
// We start the installs ourself
|
||||
return false;
|
||||
},
|
||||
|
||||
classDescription: "XPI Install Handler",
|
||||
contractID: "@mozilla.org/addons/web-install-listener;1",
|
||||
classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener,
|
||||
Ci.amIWebInstallListener2])
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([extWebInstallListener]);
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
-------------------------------------------------------
|
||||
About The Cisco-Provided Binary of OpenH264 Video Codec
|
||||
-------------------------------------------------------
|
||||
|
||||
Cisco provides this program under the terms of the BSD license.
|
||||
|
||||
Additionally, this binary is licensed under Cisco’s AVC/H.264 Patent Portfolio License from MPEG LA, at no cost to you, provided that the requirements and conditions shown below in the AVC/H.264 Patent Portfolio sections are met.
|
||||
|
||||
As with all AVC/H.264 codecs, you may also obtain your own patent license from MPEG LA or from the individual patent owners, or proceed at your own risk. Your rights from Cisco under the BSD license are not affected by this choice.
|
||||
|
||||
For more information on the OpenH264 binary licensing, please see the OpenH264 FAQ found at http://www.openh264.org/faq.html#binary
|
||||
|
||||
A corresponding source code to this binary program is available under the same BSD terms, which can be found at http://www.openh264.org
|
||||
|
||||
-----------
|
||||
BSD License
|
||||
-----------
|
||||
|
||||
Copyright © 2014 Cisco Systems, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-----------------------------------------
|
||||
AVC/H.264 Patent Portfolio License Notice
|
||||
-----------------------------------------
|
||||
|
||||
The binary form of this Software is distributed by Cisco under the AVC/H.264 Patent Portfolio License from MPEG LA, and is subject to the following requirements, which may or may not be applicable to your use of this software:
|
||||
|
||||
THIS PRODUCT IS LICENSED UNDER THE AVC PATENT PORTFOLIO LICENSE FOR THE PERSONAL USE OF A CONSUMER OR OTHER USES IN WHICH IT DOES NOT RECEIVE REMUNERATION TO (i) ENCODE VIDEO IN COMPLIANCE WITH THE AVC STANDARD (“AVC VIDEO”) AND/OR (ii) DECODE AVC VIDEO THAT WAS ENCODED BY A CONSUMER ENGAGED IN A PERSONAL ACTIVITY AND/OR WAS OBTAINED FROM A VIDEO PROVIDER LICENSED TO PROVIDE AVC VIDEO. NO LICENSE IS GRANTED OR SHALL BE IMPLIED FOR ANY OTHER USE. ADDITIONAL INFORMATION MAY BE OBTAINED FROM MPEG LA, L.L.C. SEE HTTP://WWW.MPEGLA.COM
|
||||
|
||||
Accordingly, please be advised that content providers and broadcasters using AVC/H.264 in their service may be required to obtain a separate use license from MPEG LA, referred to as "(b) sublicenses" in the SUMMARY OF AVC/H.264 LICENSE TERMS from MPEG LA found at http://www.openh264.org/mpegla
|
||||
|
||||
---------------------------------------------
|
||||
AVC/H.264 Patent Portfolio License Conditions
|
||||
---------------------------------------------
|
||||
|
||||
In addition, the Cisco-provided binary of this Software is licensed under Cisco's license from MPEG LA only if the following conditions are met:
|
||||
|
||||
1. The Cisco-provided binary is separately downloaded to an end user’s device, and not integrated into or combined with third party software prior to being downloaded to the end user’s device;
|
||||
|
||||
2. The end user must have the ability to control (e.g., to enable, disable, or re-enable) the use of the Cisco-provided binary;
|
||||
|
||||
3. Third party software, in the location where end users can control the use of the Cisco-provided binary, must display the following text:
|
||||
|
||||
"OpenH264 Video Codec provided by Cisco Systems, Inc."
|
||||
|
||||
4. Any third-party software that makes use of the Cisco-provided binary must reproduce all of the above text, as well as this last condition, in the EULA and/or in another location where licensing information is to be presented to the end user.
|
||||
|
||||
|
||||
|
||||
v1.0
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
/* import-globals-from ../../../content/contentAreaUtils.js */
|
||||
|
||||
var Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
function init() {
|
||||
var addon = window.arguments[0];
|
||||
var extensionsStrings = document.getElementById("extensionsStrings");
|
||||
|
||||
document.documentElement.setAttribute("addontype", addon.type);
|
||||
|
||||
var iconURL = AddonManager.getPreferredIconURL(addon, 48, window);
|
||||
if (iconURL) {
|
||||
var extensionIcon = document.getElementById("extensionIcon");
|
||||
extensionIcon.src = iconURL;
|
||||
}
|
||||
|
||||
document.title = extensionsStrings.getFormattedString("aboutWindowTitle", [addon.name]);
|
||||
var extensionName = document.getElementById("extensionName");
|
||||
extensionName.textContent = addon.name;
|
||||
|
||||
var extensionVersion = document.getElementById("extensionVersion");
|
||||
if (addon.version)
|
||||
extensionVersion.setAttribute("value", extensionsStrings.getFormattedString("aboutWindowVersionString", [addon.version]));
|
||||
else
|
||||
extensionVersion.hidden = true;
|
||||
|
||||
var extensionDescription = document.getElementById("extensionDescription");
|
||||
if (addon.description)
|
||||
extensionDescription.textContent = addon.description;
|
||||
else
|
||||
extensionDescription.hidden = true;
|
||||
|
||||
var numDetails = 0;
|
||||
|
||||
var extensionCreator = document.getElementById("extensionCreator");
|
||||
if (addon.creator) {
|
||||
extensionCreator.setAttribute("value", addon.creator);
|
||||
numDetails++;
|
||||
} else {
|
||||
extensionCreator.hidden = true;
|
||||
var extensionCreatorLabel = document.getElementById("extensionCreatorLabel");
|
||||
extensionCreatorLabel.hidden = true;
|
||||
}
|
||||
|
||||
var extensionHomepage = document.getElementById("extensionHomepage");
|
||||
var homepageURL = addon.homepageURL;
|
||||
if (homepageURL) {
|
||||
extensionHomepage.setAttribute("homepageURL", homepageURL);
|
||||
extensionHomepage.setAttribute("tooltiptext", homepageURL);
|
||||
numDetails++;
|
||||
} else {
|
||||
extensionHomepage.hidden = true;
|
||||
}
|
||||
|
||||
numDetails += appendToList("extensionDevelopers", "developersBox", addon.developers);
|
||||
numDetails += appendToList("extensionTranslators", "translatorsBox", addon.translators);
|
||||
numDetails += appendToList("extensionContributors", "contributorsBox", addon.contributors);
|
||||
|
||||
if (numDetails == 0) {
|
||||
var groove = document.getElementById("groove");
|
||||
groove.hidden = true;
|
||||
var extensionDetailsBox = document.getElementById("extensionDetailsBox");
|
||||
extensionDetailsBox.hidden = true;
|
||||
}
|
||||
|
||||
var acceptButton = document.documentElement.getButton("accept");
|
||||
acceptButton.label = extensionsStrings.getString("aboutWindowCloseButton");
|
||||
|
||||
setTimeout(sizeToContent, 0);
|
||||
}
|
||||
|
||||
function appendToList(aHeaderId, aNodeId, aItems) {
|
||||
var header = document.getElementById(aHeaderId);
|
||||
var node = document.getElementById(aNodeId);
|
||||
|
||||
if (!aItems || aItems.length == 0) {
|
||||
header.hidden = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (let currentItem of aItems) {
|
||||
var label = document.createElement("label");
|
||||
label.textContent = currentItem;
|
||||
label.setAttribute("class", "contributor");
|
||||
node.appendChild(label);
|
||||
}
|
||||
|
||||
return aItems.length;
|
||||
}
|
||||
|
||||
function loadHomepage(aEvent) {
|
||||
window.close();
|
||||
openURL(aEvent.target.getAttribute("homepageURL"));
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/extensions/about.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://mozapps/locale/extensions/about.dtd">
|
||||
|
||||
<dialog id="genericAbout"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="init();"
|
||||
buttons="accept"
|
||||
buttoniconaccept="close"
|
||||
onaccept="close();">
|
||||
|
||||
<script type="application/javascript" src="chrome://mozapps/content/extensions/about.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
|
||||
|
||||
<stringbundleset id="aboutSet">
|
||||
<stringbundle id="extensionsStrings" src="chrome://mozapps/locale/extensions/extensions.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<vbox id="clientBox" flex="1">
|
||||
<hbox class="basic-info">
|
||||
<vbox pack="center">
|
||||
<image id="extensionIcon"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<label id="extensionName"/>
|
||||
<label id="extensionVersion" crop="end"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<description id="extensionDescription" class="boxIndent"/>
|
||||
|
||||
<separator id="groove" class="groove"/>
|
||||
|
||||
<vbox id="extensionDetailsBox" flex="1">
|
||||
<label id="extensionCreatorLabel" class="sectionTitle">&creator.label;</label>
|
||||
<hbox id="creatorBox" class="boxIndent">
|
||||
<label id="extensionCreator" flex="1" crop="end"/>
|
||||
<label id="extensionHomepage" onclick="if (event.button == 0) { loadHomepage(event); }"
|
||||
class="text-link" value="&homepage.label;"/>
|
||||
</hbox>
|
||||
|
||||
<label id="extensionDevelopers" class="sectionTitle">&developers.label;</label>
|
||||
<vbox flex="1" id="developersBox" class="boxIndent"/>
|
||||
<label id="extensionTranslators" class="sectionTitle">&translators.label;</label>
|
||||
<vbox flex="1" id="translatorsBox" class="boxIndent"/>
|
||||
<label id="extensionContributors" class="sectionTitle">&contributors.label;</label>
|
||||
<vbox flex="1" id="contributorsBox" class="boxIndent"/>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.hardBlockedAddon {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/blocklist.xml#hardblockedaddon");
|
||||
}
|
||||
|
||||
.softBlockedAddon {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/blocklist.xml#softblockedaddon");
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gArgs;
|
||||
|
||||
function init() {
|
||||
var hasHardBlocks = false;
|
||||
var hasSoftBlocks = false;
|
||||
gArgs = window.arguments[0].wrappedJSObject;
|
||||
|
||||
// NOTE: We use strings from the "updates.properties" bundleset to change the
|
||||
// text on the "Cancel" button to "Restart Later". (bug 523784)
|
||||
let bundle = Services.strings.
|
||||
createBundle("chrome://mozapps/locale/update/updates.properties");
|
||||
let cancelButton = document.documentElement.getButton("cancel");
|
||||
cancelButton.setAttribute("label", bundle.GetStringFromName("restartLaterButton"));
|
||||
cancelButton.setAttribute("accesskey",
|
||||
bundle.GetStringFromName("restartLaterButton.accesskey"));
|
||||
|
||||
var richlist = document.getElementById("addonList");
|
||||
var list = gArgs.list;
|
||||
list.sort(function(a, b) { return String.localeCompare(a.name, b.name); });
|
||||
for (let listItem of list) {
|
||||
let item = document.createElement("richlistitem");
|
||||
item.setAttribute("name", listItem.name);
|
||||
item.setAttribute("version", listItem.version);
|
||||
item.setAttribute("icon", listItem.icon);
|
||||
if (listItem.blocked) {
|
||||
item.setAttribute("class", "hardBlockedAddon");
|
||||
hasHardBlocks = true;
|
||||
}
|
||||
else {
|
||||
item.setAttribute("class", "softBlockedAddon");
|
||||
hasSoftBlocks = true;
|
||||
}
|
||||
richlist.appendChild(item);
|
||||
}
|
||||
|
||||
if (hasHardBlocks && hasSoftBlocks)
|
||||
document.getElementById("bothMessage").hidden = false;
|
||||
else if (hasHardBlocks)
|
||||
document.getElementById("hardBlockMessage").hidden = false;
|
||||
else
|
||||
document.getElementById("softBlockMessage").hidden = false;
|
||||
|
||||
var link = document.getElementById("moreInfo");
|
||||
if (list.length == 1 && list[0].url) {
|
||||
link.setAttribute("href", list[0].url);
|
||||
}
|
||||
else {
|
||||
var url = Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL");
|
||||
link.setAttribute("href", url);
|
||||
}
|
||||
}
|
||||
|
||||
function finish(shouldRestartNow) {
|
||||
gArgs.restart = shouldRestartNow;
|
||||
var list = gArgs.list;
|
||||
var items = document.getElementById("addonList").childNodes;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (!list[i].blocked)
|
||||
list[i].disable = items[i].checked;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE bindings [
|
||||
<!ENTITY % blocklistDTD SYSTEM "chrome://mozapps/locale/extensions/blocklist.dtd" >
|
||||
%blocklistDTD;
|
||||
]>
|
||||
|
||||
<bindings id="blocklistBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="hardblockedaddon">
|
||||
<content align="start">
|
||||
<xul:image xbl:inherits="src=icon"/>
|
||||
<xul:vbox flex="1">
|
||||
<xul:hbox class="addon-name-version">
|
||||
<xul:label class="addonName" crop="end" xbl:inherits="value=name"/>
|
||||
<xul:label class="addonVersion" xbl:inherits="value=version"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:label class="blockedLabel" value="&blocklist.blocked.label;"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="softblockedaddon">
|
||||
<content align="start">
|
||||
<xul:image xbl:inherits="src=icon"/>
|
||||
<xul:vbox flex="1">
|
||||
<xul:hbox class="addon-name-version">
|
||||
<xul:label class="addonName" crop="end" xbl:inherits="value=name"/>
|
||||
<xul:label class="addonVersion" xbl:inherits="value=version"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox>
|
||||
<xul:spacer flex="1"/>
|
||||
<xul:checkbox class="disableCheckbox" checked="true" label="&blocklist.checkbox.label;"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</content>
|
||||
<implementation>
|
||||
<field name="_checkbox">
|
||||
document.getAnonymousElementByAttribute(this, "class", "disableCheckbox")
|
||||
</field>
|
||||
<property name="checked" readonly="true">
|
||||
<getter>
|
||||
return this._checkbox.checked;
|
||||
</getter>
|
||||
</property>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/extensions/blocklist.css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/content/extensions/blocklist.css"?>
|
||||
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/blocklist.dtd">
|
||||
%extensionsDTD;
|
||||
]>
|
||||
|
||||
<dialog windowtype="Addons:Blocklist" title="&blocklist.title;" align="stretch"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="init();" ondialogaccept="return finish(true)"
|
||||
ondialogcancel="return finish(false)"
|
||||
buttons="accept,cancel" style="&blocklist.style;"
|
||||
buttonlabelaccept="&blocklist.accept.label;"
|
||||
buttonaccesskeyaccept="&blocklist.accept.accesskey;">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<script type="application/javascript" src="chrome://mozapps/content/extensions/blocklist.js"/>
|
||||
|
||||
<hbox align="stretch" flex="1">
|
||||
<vbox pack="start">
|
||||
<image class="error-icon"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<label>&blocklist.summary;</label>
|
||||
<separator class="thin"/>
|
||||
<richlistbox id="addonList" flex="1"/>
|
||||
<separator class="thin"/>
|
||||
<description id="bothMessage" hidden="true" class="bold">&blocklist.softandhard;</description>
|
||||
<description id="hardBlockMessage" hidden="true" class="bold">&blocklist.hardblocked;</description>
|
||||
<description id="softBlockMessage" hidden="true" class="bold">&blocklist.softblocked;</description>
|
||||
<hbox pack="start">
|
||||
<label id="moreInfo" class="text-link" value="&blocklist.moreinfo;"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</dialog>
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
function Startup() {
|
||||
var bundle = document.getElementById("extensionsStrings");
|
||||
var addon = window.arguments[0].addon;
|
||||
|
||||
document.documentElement.setAttribute("addontype", addon.type);
|
||||
|
||||
var iconURL = AddonManager.getPreferredIconURL(addon, 48, window);
|
||||
if (iconURL)
|
||||
document.getElementById("icon").src = iconURL;
|
||||
|
||||
var label = document.createTextNode(bundle.getFormattedString("eulaHeader", [addon.name]));
|
||||
document.getElementById("heading").appendChild(label);
|
||||
document.getElementById("eula").value = addon.eula;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?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>
|
||||
|
||||
<hbox id="heading-container">
|
||||
<image id="icon"/>
|
||||
<label id="heading" flex="1"/>
|
||||
</hbox>
|
||||
|
||||
<textbox id="eula" multiline="true" readonly="true" flex="1"/>
|
||||
</dialog>
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@namespace xhtml "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/* HTML link elements do weird things to the layout if they are not hidden */
|
||||
xhtml|link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#categories {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#categories-list");
|
||||
}
|
||||
|
||||
.category {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#category");
|
||||
}
|
||||
|
||||
.sort-controls {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#sorters");
|
||||
}
|
||||
|
||||
.addon[status="installed"] {
|
||||
-moz-box-orient: vertical;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-generic");
|
||||
}
|
||||
|
||||
.addon[status="installing"] {
|
||||
-moz-box-orient: vertical;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-installing");
|
||||
}
|
||||
|
||||
.addon[pending="uninstall"] {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#addon-uninstalled");
|
||||
}
|
||||
|
||||
.creator {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#creator-link");
|
||||
}
|
||||
|
||||
.meta-rating {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#rating");
|
||||
}
|
||||
|
||||
.download-progress, .download-progress[mode="undetermined"] {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#download-progress");
|
||||
}
|
||||
|
||||
.install-status {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#install-status");
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/extensions.xml#detail-row");
|
||||
}
|
||||
|
||||
.text-list {
|
||||
white-space: pre-line;
|
||||
-moz-user-select: element;
|
||||
}
|
||||
|
||||
setting, row[unsupported="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
setting[type="bool"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-bool");
|
||||
}
|
||||
|
||||
setting[type="bool"][localized="true"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-localized-bool");
|
||||
}
|
||||
|
||||
setting[type="bool"]:not([learnmore]) .preferences-learnmore,
|
||||
setting[type="boolint"]:not([learnmore]) .preferences-learnmore {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
setting[type="boolint"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-boolint");
|
||||
}
|
||||
|
||||
setting[type="integer"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer");
|
||||
}
|
||||
|
||||
setting[type="integer"]:not([size]) textbox {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
||||
setting[type="control"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control");
|
||||
}
|
||||
|
||||
setting[type="string"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string");
|
||||
}
|
||||
|
||||
setting[type="color"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-color");
|
||||
}
|
||||
|
||||
setting[type="file"],
|
||||
setting[type="directory"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-path");
|
||||
}
|
||||
|
||||
setting[type="radio"],
|
||||
setting[type="menulist"] {
|
||||
display: -moz-grid-line;
|
||||
-moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi");
|
||||
}
|
||||
|
||||
#addonitem-popup > menuitem[disabled="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addonitem-popup[addontype="theme"] > #menuitem_enableItem,
|
||||
#addonitem-popup[addontype="theme"] > #menuitem_disableItem,
|
||||
#addonitem-popup:not([addontype="theme"]) > #menuitem_enableTheme,
|
||||
#addonitem-popup:not([addontype="theme"]) > #menuitem_disableTheme {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#show-disabled-unsigned-extensions .button-text {
|
||||
margin-inline-start: 3px !important;
|
||||
margin-inline-end: 2px !important;
|
||||
}
|
||||
|
||||
#header-searching:not([active]) {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#search-list[local="false"] > .addon[remote="false"],
|
||||
#search-list[remote="false"] > .addon[remote="true"] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
#detail-view {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.addon:not([notification="warning"]) .warning,
|
||||
.addon:not([notification="error"]) .error,
|
||||
.addon:not([notification="info"]) .info,
|
||||
.addon:not([pending]) .pending,
|
||||
.addon:not([upgrade="true"]) .update-postfix,
|
||||
.addon[active="true"] .disabled-postfix,
|
||||
.addon[pending="install"] .update-postfix,
|
||||
.addon[pending="install"] .disabled-postfix,
|
||||
#detail-view:not([notification="warning"]) .warning,
|
||||
#detail-view:not([notification="error"]) .error,
|
||||
#detail-view:not([notification="info"]) .info,
|
||||
#detail-view:not([pending]) .pending,
|
||||
#detail-view:not([upgrade="true"]) .update-postfix,
|
||||
#detail-view[active="true"] .disabled-postfix,
|
||||
#detail-view[loading] .detail-view-container,
|
||||
#detail-view:not([loading]) .alert-container,
|
||||
.detail-row:not([value]),
|
||||
#search-list[remote="false"] #search-allresults-link {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addons-page:not([warning]) #list-view > .global-warning-container {
|
||||
display: none;
|
||||
}
|
||||
#addon-list .date-updated {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-pane:not(#updates-view) .addon .relnotes-toggle,
|
||||
.view-pane:not(#updates-view) .addon .include-update,
|
||||
#updates-view:not([updatetype="available"]) .addon .include-update,
|
||||
#updates-view[updatetype="available"] .addon .update-available-notice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addons-page:not([warning]) .global-warning,
|
||||
#addons-page:not([warning="safemode"]) .global-warning-safemode,
|
||||
#addons-page:not([warning="checkcompatibility"]) .global-warning-checkcompatibility,
|
||||
#addons-page:not([warning="updatesecurity"]) .global-warning-updatesecurity {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Plugins aren't yet disabled by safemode (bug 342333),
|
||||
so don't show that warning when viewing plugins. */
|
||||
#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
|
||||
#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addons-page .view-pane:not([type="plugin"]) #plugindeprecation-notice {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#addons-page .view-pane:not([type="experiment"]) .experiment-info-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.addon .relnotes {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
#detail-name, #detail-desc, #detail-fulldesc {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
/* Make sure we're not animating hidden images. See bug 623739. */
|
||||
#view-port:not([selectedIndex="0"]) #discover-view .loading,
|
||||
#discover-view:not([selectedIndex="0"]) .loading {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Elements in unselected richlistitems cannot be focused */
|
||||
richlistitem:not([selected]) * {
|
||||
-moz-user-focus: ignore;
|
||||
}
|
||||
|
||||
#header-search {
|
||||
width: 22em;
|
||||
}
|
||||
|
||||
#header-utils-btn {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
.discover-button[disabled="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#experiments-learn-more[disabled="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#experiments-change-telemetry[disabled="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-pane[type="experiment"] .error,
|
||||
.view-pane[type="experiment"] .warning,
|
||||
.view-pane[type="experiment"] .addon:not([pending="uninstall"]) .pending,
|
||||
.view-pane[type="experiment"] .disabled-postfix,
|
||||
.view-pane[type="experiment"] .update-postfix,
|
||||
.view-pane[type="experiment"] .addon-control.enable,
|
||||
.view-pane[type="experiment"] .addon-control.disable,
|
||||
#detail-view[type="experiment"] .alert-container,
|
||||
#detail-view[type="experiment"] #detail-version,
|
||||
#detail-view[type="experiment"] #detail-creator,
|
||||
#detail-view[type="experiment"] #detail-enable-btn,
|
||||
#detail-view[type="experiment"] #detail-disable-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.view-pane:not([type="experiment"]) .experiment-container,
|
||||
.view-pane:not([type="experiment"]) #detail-experiment-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.addon[type="experiment"][status="installing"] .experiment-time,
|
||||
.addon[type="experiment"][status="installing"] .experiment-state {
|
||||
display: none;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,715 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/content/extensions/extensions.css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/extensions/extensions.css"?>
|
||||
|
||||
<!DOCTYPE page [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
|
||||
%extensionsDTD;
|
||||
]>
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
||||
id="addons-page" title="&addons.windowTitle;"
|
||||
role="application" windowtype="Addons:Manager"
|
||||
disablefastfind="true">
|
||||
|
||||
<xhtml:link rel="shortcut icon"
|
||||
href="chrome://mozapps/skin/extensions/extensionGeneric-16.png"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mozapps/content/extensions/extensions.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/contentAreaUtils.js"/>
|
||||
|
||||
<popupset>
|
||||
<!-- menu for an addon item -->
|
||||
<menupopup id="addonitem-popup">
|
||||
<menuitem id="menuitem_showDetails" command="cmd_showItemDetails"
|
||||
default="true" label="&cmd.showDetails.label;"
|
||||
accesskey="&cmd.showDetails.accesskey;"/>
|
||||
<menuitem id="menuitem_enableItem" command="cmd_enableItem"
|
||||
label="&cmd.enableAddon.label;"
|
||||
accesskey="&cmd.enableAddon.accesskey;"/>
|
||||
<menuitem id="menuitem_disableItem" command="cmd_disableItem"
|
||||
label="&cmd.disableAddon.label;"
|
||||
accesskey="&cmd.disableAddon.accesskey;"/>
|
||||
<menuitem id="menuitem_enableTheme" command="cmd_enableItem"
|
||||
label="&cmd.enableTheme.label;"
|
||||
accesskey="&cmd.enableTheme.accesskey;"/>
|
||||
<menuitem id="menuitem_disableTheme" command="cmd_disableItem"
|
||||
label="&cmd.disableTheme.label;"
|
||||
accesskey="&cmd.disableTheme.accesskey;"/>
|
||||
<menuitem id="menuitem_installItem" command="cmd_installItem"
|
||||
label="&cmd.installAddon.label;"
|
||||
accesskey="&cmd.installAddon.accesskey;"/>
|
||||
<menuitem id="menuitem_uninstallItem" command="cmd_uninstallItem"
|
||||
label="&cmd.uninstallAddon.label;"
|
||||
accesskey="&cmd.uninstallAddon.accesskey;"/>
|
||||
<menuseparator id="addonitem-menuseparator" />
|
||||
<menuitem id="menuitem_preferences" command="cmd_showItemPreferences"
|
||||
#ifdef XP_WIN
|
||||
label="&cmd.preferencesWin.label;"
|
||||
accesskey="&cmd.preferencesWin.accesskey;"/>
|
||||
#else
|
||||
label="&cmd.preferencesUnix.label;"
|
||||
accesskey="&cmd.preferencesUnix.accesskey;"/>
|
||||
#endif
|
||||
<menuitem id="menuitem_findUpdates" command="cmd_findItemUpdates"
|
||||
label="&cmd.findUpdates.label;"
|
||||
accesskey="&cmd.findUpdates.accesskey;"/>
|
||||
<menuitem id="menuitem_about" command="cmd_showItemAbout"
|
||||
label="&cmd.about.label;"
|
||||
accesskey="&cmd.about.accesskey;"/>
|
||||
</menupopup>
|
||||
|
||||
<tooltip id="addonitem-tooltip"/>
|
||||
</popupset>
|
||||
|
||||
<!-- global commands - these act on all addons, or affect the addons manager
|
||||
in some other way -->
|
||||
<commandset id="globalCommandSet">
|
||||
<!-- XXXsw remove useless oncommand attribute once bug 371900 is fixed -->
|
||||
<command id="cmd_focusSearch" oncommand=";"/>
|
||||
<command id="cmd_findAllUpdates"/>
|
||||
<command id="cmd_restartApp"/>
|
||||
<command id="cmd_goToDiscoverPane"/>
|
||||
<command id="cmd_goToRecentUpdates"/>
|
||||
<command id="cmd_goToAvailableUpdates"/>
|
||||
<command id="cmd_installFromFile"/>
|
||||
<command id="cmd_debugAddons"/>
|
||||
<command id="cmd_back"/>
|
||||
<command id="cmd_forward"/>
|
||||
<command id="cmd_enableCheckCompatibility"/>
|
||||
<command id="cmd_enableUpdateSecurity"/>
|
||||
<command id="cmd_toggleAutoUpdateDefault"/>
|
||||
<command id="cmd_resetAddonAutoUpdate"/>
|
||||
<command id="cmd_experimentsLearnMore"/>
|
||||
<command id="cmd_experimentsOpenTelemetryPreferences"/>
|
||||
<command id="cmd_showUnsignedExtensions"/>
|
||||
<command id="cmd_showAllExtensions"/>
|
||||
</commandset>
|
||||
|
||||
<!-- view commands - these act on the selected addon -->
|
||||
<commandset id="viewCommandSet"
|
||||
events="richlistbox-select" commandupdater="true">
|
||||
<command id="cmd_showItemDetails"/>
|
||||
<command id="cmd_findItemUpdates"/>
|
||||
<command id="cmd_showItemPreferences"/>
|
||||
<command id="cmd_showItemAbout"/>
|
||||
<command id="cmd_enableItem"/>
|
||||
<command id="cmd_disableItem"/>
|
||||
<command id="cmd_installItem"/>
|
||||
<command id="cmd_purchaseItem"/>
|
||||
<command id="cmd_uninstallItem"/>
|
||||
<command id="cmd_cancelUninstallItem"/>
|
||||
<command id="cmd_cancelOperation"/>
|
||||
<command id="cmd_contribute"/>
|
||||
<command id="cmd_askToActivateItem"/>
|
||||
<command id="cmd_alwaysActivateItem"/>
|
||||
<command id="cmd_neverActivateItem"/>
|
||||
</commandset>
|
||||
|
||||
<keyset>
|
||||
<key id="focusSearch" key="&search.commandkey;" modifiers="accel"
|
||||
command="cmd_focusSearch"/>
|
||||
</keyset>
|
||||
<hbox flex="1">
|
||||
<vbox>
|
||||
<hbox id="nav-header"
|
||||
align="center"
|
||||
pack="center">
|
||||
<toolbarbutton id="back-btn"
|
||||
class="nav-button header-button"
|
||||
command="cmd_back"
|
||||
tooltiptext="&cmd.back.tooltip;"
|
||||
hidden="true"
|
||||
disabled="true"/>
|
||||
<toolbarbutton id="forward-btn"
|
||||
class="nav-button header-button"
|
||||
command="cmd_forward"
|
||||
tooltiptext="&cmd.forward.tooltip;"
|
||||
hidden="true"
|
||||
disabled="true"/>
|
||||
</hbox>
|
||||
<!-- category list -->
|
||||
<richlistbox id="categories" flex="1">
|
||||
<richlistitem id="category-search" value="addons://search/"
|
||||
class="category"
|
||||
name="&view.search.label;" priority="0"
|
||||
tooltiptext="&view.search.label;" disabled="true"/>
|
||||
<richlistitem id="category-discover" value="addons://discover/"
|
||||
class="category"
|
||||
name="&view.discover.label;" priority="1000"
|
||||
tooltiptext="&view.discover.label;"/>
|
||||
<richlistitem id="category-availableUpdates" value="addons://updates/available"
|
||||
class="category"
|
||||
name="&view.availableUpdates.label;" priority="100000"
|
||||
tooltiptext="&view.availableUpdates.label;"
|
||||
disabled="true"/>
|
||||
<richlistitem id="category-recentUpdates" value="addons://updates/recent"
|
||||
class="category"
|
||||
name="&view.recentUpdates.label;" priority="101000"
|
||||
tooltiptext="&view.recentUpdates.label;" disabled="true"/>
|
||||
</richlistbox>
|
||||
</vbox>
|
||||
<vbox class="main-content" flex="1">
|
||||
<!-- view port -->
|
||||
<deck id="view-port" flex="1" selectedIndex="0">
|
||||
<!-- discover view -->
|
||||
<deck id="discover-view" flex="1" class="view-pane" selectedIndex="0" tabindex="0">
|
||||
<vbox id="discover-loading" align="center" pack="stretch" flex="1" class="alert-container">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<hbox class="alert loading" align="center">
|
||||
<image/>
|
||||
<label value="&loading.label;"/>
|
||||
</hbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<vbox id="discover-error" align="center" pack="stretch" flex="1" class="alert-container">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<hbox>
|
||||
<spacer class="discover-spacer-before"/>
|
||||
<hbox class="alert" align="center">
|
||||
<image class="discover-logo"/>
|
||||
<vbox flex="1" align="stretch">
|
||||
<label class="discover-title">&discover.title;</label>
|
||||
<description class="discover-description">&discover.description2;</description>
|
||||
<description class="discover-footer">&discover.footer;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<spacer class="discover-spacer-after"/>
|
||||
</hbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<browser id="discover-browser" type="content" flex="1"
|
||||
disablehistory="true" homepage="about:blank"/>
|
||||
</deck>
|
||||
|
||||
<!-- container for views with the search/tools header -->
|
||||
<vbox id="headered-views" flex="1">
|
||||
<!-- main header -->
|
||||
<hbox id="header" align="center">
|
||||
<button id="show-all-extensions" hidden="true"
|
||||
label="&showAllExtensions.button.label;"
|
||||
command="cmd_showAllExtensions"/>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="updates-container" align="center">
|
||||
<image class="spinner"/>
|
||||
<label id="updates-noneFound" hidden="true"
|
||||
value="&updates.noneFound.label;"/>
|
||||
<button id="updates-manualUpdatesFound-btn" class="button-link"
|
||||
hidden="true" label="&updates.manualUpdatesFound.label;"
|
||||
command="cmd_goToAvailableUpdates"/>
|
||||
<label id="updates-progress" hidden="true"
|
||||
value="&updates.updating.label;"/>
|
||||
<label id="updates-installed" hidden="true"
|
||||
value="&updates.installed.label;"/>
|
||||
<label id="updates-downloaded" hidden="true"
|
||||
value="&updates.downloaded.label;"/>
|
||||
<button id="updates-restart-btn" class="button-link" hidden="true"
|
||||
label="&updates.restart.label;"
|
||||
command="cmd_restartApp"/>
|
||||
</hbox>
|
||||
<button id="show-disabled-unsigned-extensions" hidden="true"
|
||||
class="warning"
|
||||
label="&showUnsignedExtensions.button.label;"
|
||||
command="cmd_showUnsignedExtensions"/>
|
||||
<toolbarbutton id="header-utils-btn" class="header-button" type="menu"
|
||||
tooltiptext="&toolsMenu.tooltip;">
|
||||
<menupopup id="utils-menu">
|
||||
<menuitem id="utils-updateNow"
|
||||
label="&updates.checkForUpdates.label;"
|
||||
accesskey="&updates.checkForUpdates.accesskey;"
|
||||
command="cmd_findAllUpdates"/>
|
||||
<menuitem id="utils-viewUpdates"
|
||||
label="&updates.viewUpdates.label;"
|
||||
accesskey="&updates.viewUpdates.accesskey;"
|
||||
command="cmd_goToRecentUpdates"/>
|
||||
<menuseparator id="utils-installFromFile-separator"/>
|
||||
<menuitem id="utils-installFromFile"
|
||||
label="&installAddonFromFile.label;"
|
||||
accesskey="&installAddonFromFile.accesskey;"
|
||||
command="cmd_installFromFile"/>
|
||||
<menuitem id="utils-debugAddons"
|
||||
label="&debugAddons.label;"
|
||||
accesskey="&debugAddons.accesskey;"
|
||||
command="cmd_debugAddons"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="utils-autoUpdateDefault"
|
||||
label="&updates.updateAddonsAutomatically.label;"
|
||||
accesskey="&updates.updateAddonsAutomatically.accesskey;"
|
||||
type="checkbox" autocheck="false"
|
||||
command="cmd_toggleAutoUpdateDefault"/>
|
||||
<menuitem id="utils-resetAddonUpdatesToAutomatic"
|
||||
label="&updates.resetUpdatesToAutomatic.label;"
|
||||
accesskey="&updates.resetUpdatesToAutomatic.accesskey;"
|
||||
command="cmd_resetAddonAutoUpdate"/>
|
||||
<menuitem id="utils-resetAddonUpdatesToManual"
|
||||
label="&updates.resetUpdatesToManual.label;"
|
||||
accesskey="&updates.resetUpdatesToManual.accesskey;"
|
||||
command="cmd_resetAddonAutoUpdate"/>
|
||||
</menupopup>
|
||||
</toolbarbutton>
|
||||
<textbox id="header-search" type="search" searchbutton="true"
|
||||
searchbuttonlabel="&search.buttonlabel;"
|
||||
placeholder="&search.placeholder;"/>
|
||||
</hbox>
|
||||
|
||||
<deck id="headered-views-content" flex="1" selectedIndex="0">
|
||||
<!-- search view -->
|
||||
<vbox id="search-view" flex="1" class="view-pane" tabindex="0">
|
||||
<hbox class="view-header global-warning-container" align="center">
|
||||
<!-- global warnings -->
|
||||
<hbox class="global-warning" flex="1">
|
||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||
tooltiptext="&warning.safemode.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
</hbox>
|
||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||
tooltiptext="&warning.checkcompatibility.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||
tooltiptext="&warning.updatesecurity.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="search-sorters" class="sort-controls"
|
||||
showrelevance="true" sortby="relevancescore" ascending="false"/>
|
||||
</hbox>
|
||||
<hbox id="search-filter" align="center">
|
||||
<label id="search-filter-label" value="&search.filter2.label;"/>
|
||||
<radiogroup id="search-filter-radiogroup" orient="horizontal"
|
||||
align="center" persist="value" value="remote">
|
||||
<radio id="search-filter-local" class="search-filter-radio"
|
||||
label="&search.filter2.installed.label;" value="local"
|
||||
tooltiptext="&search.filter2.installed.tooltip;"/>
|
||||
<radio id="search-filter-remote" class="search-filter-radio"
|
||||
label="&search.filter2.available.label;" value="remote"
|
||||
tooltiptext="&search.filter2.available.tooltip;"/>
|
||||
</radiogroup>
|
||||
</hbox>
|
||||
<vbox id="search-loading" class="alert-container"
|
||||
flex="1" hidden="true">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<hbox class="alert loading" align="center">
|
||||
<image/>
|
||||
<label value="&loading.label;"/>
|
||||
</hbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<vbox id="search-list-empty" class="alert-container"
|
||||
flex="1" hidden="true">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<vbox class="alert">
|
||||
<label value="&listEmpty.search.label;"/>
|
||||
<button class="discover-button"
|
||||
id="discover-button-search"
|
||||
label="&listEmpty.button.label;"
|
||||
command="cmd_goToDiscoverPane"/>
|
||||
</vbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<richlistbox id="search-list" class="list" flex="1">
|
||||
<hbox pack="center">
|
||||
<label id="search-allresults-link" class="text-link"/>
|
||||
</hbox>
|
||||
</richlistbox>
|
||||
</vbox>
|
||||
|
||||
<!-- list view -->
|
||||
<vbox id="list-view" flex="1" class="view-pane" align="stretch" tabindex="0">
|
||||
<!-- info UI for add-ons that have been disabled for being unsigned -->
|
||||
<vbox id="disabled-unsigned-addons-info" hidden="true">
|
||||
<label id="disabled-unsigned-addons-heading" value="&disabledUnsigned.heading;"/>
|
||||
<description>
|
||||
&disabledUnsigned.description.start;<label class="text-link plain" id="find-alternative-addons">&disabledUnsigned.description.findAddonsLink;</label>&disabledUnsigned.description.end;
|
||||
</description>
|
||||
<hbox pack="start"><label class="text-link" id="signing-learn-more">&disabledUnsigned.learnMore;</label></hbox>
|
||||
<description id="signing-dev-info">
|
||||
&disabledUnsigned.devInfo.start;<label class="text-link plain" id="signing-dev-manual-link">&disabledUnsigned.devInfo.linkToManual;</label>&disabledUnsigned.devInfo.end;
|
||||
</description>
|
||||
</vbox>
|
||||
<vbox id="plugindeprecation-notice" class="alert-container">
|
||||
<hbox class="alert">
|
||||
<description>&pluginDeprecation.description;  
|
||||
<label class="text-link plain" id="plugindeprecation-learnmore-link">&pluginDeprecation.learnMore;</label>
|
||||
</description>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<hbox class="view-header global-warning-container">
|
||||
<!-- global warnings -->
|
||||
<hbox class="global-warning" flex="1">
|
||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||
tooltiptext="&warning.safemode.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
</hbox>
|
||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||
tooltiptext="&warning.checkcompatibility.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||
tooltiptext="&warning.updatesecurity.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
</hbox>
|
||||
<hbox class="view-header global-info-container experiment-info-container">
|
||||
<hbox class="global-info" flex="1" align="center">
|
||||
<label value="&experiment.info.label;"/>
|
||||
<button id="experiments-learn-more"
|
||||
label="&experiment.info.learnmore;"
|
||||
tooltiptext="&experiment.info.learnmore;"
|
||||
accesskey="&experiment.info.learnmore.accesskey;"
|
||||
command="cmd_experimentsLearnMore"/>
|
||||
<button id="experiments-change-telemetry"
|
||||
label="&experiment.info.changetelemetry;"
|
||||
tooltiptext="&experiment.info.changetelemetry;"
|
||||
accesskey="&experiment.info.changetelemetry.accesskey;"
|
||||
command="cmd_experimentsOpenTelemetryPreferences"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap. -->
|
||||
</hbox>
|
||||
</hbox>
|
||||
<vbox id="addon-list-empty" class="alert-container"
|
||||
flex="1" hidden="true">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<vbox class="alert">
|
||||
<label value="&listEmpty.installed.label;"/>
|
||||
<button class="discover-button"
|
||||
id="discover-button-install"
|
||||
label="&listEmpty.button.label;"
|
||||
command="cmd_goToDiscoverPane"/>
|
||||
</vbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<richlistbox id="addon-list" class="list" flex="1"/>
|
||||
</vbox>
|
||||
<!-- updates view -->
|
||||
<vbox id="updates-view" flex="1" class="view-pane" tabindex="0">
|
||||
<hbox class="view-header global-warning-container" align="center">
|
||||
<!-- global warnings -->
|
||||
<hbox class="global-warning" flex="1">
|
||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||
tooltiptext="&warning.safemode.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
</hbox>
|
||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||
tooltiptext="&warning.checkcompatibility.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||
tooltiptext="&warning.updatesecurity.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
<spacer flex="1"/>
|
||||
<hbox id="updates-sorters" class="sort-controls" sortby="updateDate"
|
||||
ascending="false"/>
|
||||
</hbox>
|
||||
<vbox id="updates-list-empty" class="alert-container"
|
||||
flex="1" hidden="true">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<vbox class="alert">
|
||||
<label id="empty-availableUpdates-msg" value="&listEmpty.availableUpdates.label;"/>
|
||||
<label id="empty-recentUpdates-msg" value="&listEmpty.recentUpdates.label;"/>
|
||||
<button label="&listEmpty.findUpdates.label;"
|
||||
command="cmd_findAllUpdates"/>
|
||||
</vbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<hbox id="update-actions" pack="center">
|
||||
<button id="update-selected-btn" hidden="true"
|
||||
label="&updates.updateSelected.label;"
|
||||
tooltiptext="&updates.updateSelected.tooltip;"/>
|
||||
</hbox>
|
||||
<richlistbox id="updates-list" class="list" flex="1"/>
|
||||
</vbox>
|
||||
|
||||
<!-- detail view -->
|
||||
<scrollbox id="detail-view" flex="1" class="view-pane addon-view" orient="vertical" tabindex="0"
|
||||
role="document">
|
||||
<!-- global warnings -->
|
||||
<hbox class="global-warning-container global-warning">
|
||||
<hbox class="global-warning-safemode" flex="1" align="center"
|
||||
tooltiptext="&warning.safemode.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.safemode.label;"/>
|
||||
</hbox>
|
||||
<hbox class="global-warning-checkcompatibility" flex="1" align="center"
|
||||
tooltiptext="&warning.checkcompatibility.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.checkcompatibility.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-checkcompatibility"
|
||||
label="&warning.checkcompatibility.enable.label;"
|
||||
tooltiptext="&warning.checkcompatibility.enable.tooltip;"
|
||||
command="cmd_enableCheckCompatibility"/>
|
||||
<hbox class="global-warning-updatesecurity" flex="1" align="center"
|
||||
tooltiptext="&warning.updatesecurity.label;">
|
||||
<image class="warning-icon"/>
|
||||
<label class="global-warning-text" flex="1" crop="end"
|
||||
value="&warning.updatesecurity.label;"/>
|
||||
</hbox>
|
||||
<button class="button-link global-warning-updatesecurity"
|
||||
label="&warning.updatesecurity.enable.label;"
|
||||
tooltiptext="&warning.updatesecurity.enable.tooltip;"
|
||||
command="cmd_enableUpdateSecurity"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
<hbox flex="1">
|
||||
<spacer flex="1"/>
|
||||
<!-- "loading" splash screen -->
|
||||
<vbox class="alert-container">
|
||||
<spacer class="alert-spacer-before"/>
|
||||
<hbox class="alert loading">
|
||||
<image/>
|
||||
<label value="&loading.label;"/>
|
||||
</hbox>
|
||||
<spacer class="alert-spacer-after"/>
|
||||
</vbox>
|
||||
<!-- actual detail view -->
|
||||
<vbox class="detail-view-container" flex="3" contextmenu="addonitem-popup">
|
||||
<vbox id="detail-notifications">
|
||||
<hbox id="warning-container" align="center" class="warning">
|
||||
<image class="warning-icon"/>
|
||||
<label id="detail-warning" flex="1"/>
|
||||
<label id="detail-warning-link" class="text-link"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
<hbox id="error-container" align="center" class="error">
|
||||
<image class="error-icon"/>
|
||||
<label id="detail-error" flex="1"/>
|
||||
<label id="detail-error-link" class="text-link"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
<hbox id="pending-container" align="center" class="pending">
|
||||
<image class="pending-icon"/>
|
||||
<label id="detail-pending" flex="1"/>
|
||||
<button id="detail-restart-btn" class="button-link"
|
||||
label="&addon.restartNow.label;"
|
||||
command="cmd_restartApp"/>
|
||||
<button id="detail-undo-btn" class="button-link"
|
||||
label="&addon.undoAction.label;"
|
||||
tooltipText="&addon.undoAction.tooltip;"
|
||||
command="cmd_cancelOperation"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the message to wrap -->
|
||||
</hbox>
|
||||
</vbox>
|
||||
<hbox align="start">
|
||||
<vbox id="detail-icon-container" align="end">
|
||||
<image id="detail-icon" class="icon"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<vbox id="detail-summary">
|
||||
<hbox id="detail-name-container" class="name-container"
|
||||
align="start">
|
||||
<label id="detail-name" flex="1"/>
|
||||
<label id="detail-version"/>
|
||||
<label class="disabled-postfix" value="&addon.disabled.postfix;"/>
|
||||
<label class="update-postfix" value="&addon.update.postfix;"/>
|
||||
<spacer flex="5000"/> <!-- Necessary to allow the name to wrap -->
|
||||
</hbox>
|
||||
<label id="detail-creator" class="creator"/>
|
||||
</vbox>
|
||||
<hbox id="detail-experiment-container">
|
||||
<svg width="8" height="8" viewBox="0 0 8 8" version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="detail-experiment-bullet-container">
|
||||
<circle cx="4" cy="4" r="4" id="detail-experiment-bullet"/>
|
||||
</svg>
|
||||
<label id="detail-experiment-state"/>
|
||||
<label id="detail-experiment-time"/>
|
||||
</hbox>
|
||||
<hbox id="detail-desc-container" align="start">
|
||||
<vbox id="detail-screenshot-box" pack="center" hidden="true"> <!-- Necessary to work around bug 394738 -->
|
||||
<image id="detail-screenshot"/>
|
||||
</vbox>
|
||||
<vbox flex="1">
|
||||
<description id="detail-desc"/>
|
||||
<description id="detail-fulldesc"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<vbox id="detail-contributions">
|
||||
<description id="detail-contrib-description">
|
||||
&detail.contributions.description;
|
||||
</description>
|
||||
<hbox align="center">
|
||||
<label id="detail-contrib-suggested"/>
|
||||
<spacer flex="1"/>
|
||||
<button id="detail-contrib-btn"
|
||||
label="&cmd.contribute.label;"
|
||||
accesskey="&cmd.contribute.accesskey;"
|
||||
tooltiptext="&cmd.contribute.tooltip;"
|
||||
command="cmd_contribute"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<grid id="detail-grid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column flex="2"/>
|
||||
</columns>
|
||||
<rows id="detail-rows">
|
||||
<row class="detail-row-complex" id="detail-updates-row">
|
||||
<label class="detail-row-label" value="&detail.updateType;"/>
|
||||
<hbox align="center">
|
||||
<radiogroup id="detail-autoUpdate" orient="horizontal">
|
||||
<!-- The values here need to match the values of
|
||||
AddonManager.AUTOUPDATE_* -->
|
||||
<radio label="&detail.updateDefault.label;"
|
||||
tooltiptext="&detail.updateDefault.tooltip;"
|
||||
value="1"/>
|
||||
<radio label="&detail.updateAutomatic.label;"
|
||||
tooltiptext="&detail.updateAutomatic.tooltip;"
|
||||
value="2"/>
|
||||
<radio label="&detail.updateManual.label;"
|
||||
tooltiptext="&detail.updateManual.tooltip;"
|
||||
value="0"/>
|
||||
</radiogroup>
|
||||
<button id="detail-findUpdates-btn" class="button-link"
|
||||
label="&detail.checkForUpdates.label;"
|
||||
accesskey="&detail.checkForUpdates.accesskey;"
|
||||
tooltiptext="&detail.checkForUpdates.tooltip;"
|
||||
command="cmd_findItemUpdates"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row class="detail-row" id="detail-dateUpdated" label="&detail.lastupdated.label;"/>
|
||||
<row class="detail-row-complex" id="detail-homepage-row" label="&detail.home;">
|
||||
<label class="detail-row-label" value="&detail.home;"/>
|
||||
<label id="detail-homepage" class="detail-row-value text-link" crop="end"/>
|
||||
</row>
|
||||
<row class="detail-row-complex" id="detail-repository-row" label="&detail.repository;">
|
||||
<label class="detail-row-label" value="&detail.repository;"/>
|
||||
<label id="detail-repository" class="detail-row-value text-link"/>
|
||||
</row>
|
||||
<row class="detail-row" id="detail-size" label="&detail.size;"/>
|
||||
<row class="detail-row-complex" id="detail-rating-row">
|
||||
<label class="detail-row-label" value="&rating2.label;"/>
|
||||
<hbox>
|
||||
<label id="detail-rating" class="meta-value meta-rating"
|
||||
showrating="average"/>
|
||||
<label id="detail-reviews" class="text-link"/>
|
||||
</hbox>
|
||||
</row>
|
||||
<row class="detail-row" id="detail-downloads" label="&detail.numberOfDownloads.label;"/>
|
||||
</rows>
|
||||
</grid>
|
||||
<hbox id="detail-controls">
|
||||
<button id="detail-prefs-btn" class="addon-control preferences"
|
||||
#ifdef XP_WIN
|
||||
label="&detail.showPreferencesWin.label;"
|
||||
accesskey="&detail.showPreferencesWin.accesskey;"
|
||||
tooltiptext="&detail.showPreferencesWin.tooltip;"
|
||||
#else
|
||||
label="&detail.showPreferencesUnix.label;"
|
||||
accesskey="&detail.showPreferencesUnix.accesskey;"
|
||||
tooltiptext="&detail.showPreferencesUnix.tooltip;"
|
||||
#endif
|
||||
command="cmd_showItemPreferences"/>
|
||||
<spacer flex="1"/>
|
||||
<button id="detail-enable-btn" class="addon-control enable"
|
||||
label="&cmd.enableAddon.label;"
|
||||
accesskey="&cmd.enableAddon.accesskey;"
|
||||
command="cmd_enableItem"/>
|
||||
<button id="detail-disable-btn" class="addon-control disable"
|
||||
label="&cmd.disableAddon.label;"
|
||||
accesskey="&cmd.disableAddon.accesskey;"
|
||||
command="cmd_disableItem"/>
|
||||
<button id="detail-uninstall-btn" class="addon-control remove"
|
||||
label="&cmd.uninstallAddon.label;"
|
||||
accesskey="&cmd.uninstallAddon.accesskey;"
|
||||
command="cmd_uninstallItem"/>
|
||||
<button id="detail-purchase-btn" class="addon-control purchase"
|
||||
command="cmd_purchaseItem"/>
|
||||
<button id="detail-install-btn" class="addon-control install"
|
||||
label="&cmd.installAddon.label;"
|
||||
accesskey="&cmd.installAddon.accesskey;"
|
||||
command="cmd_installItem"/>
|
||||
<menulist id="detail-state-menulist"
|
||||
crop="none" sizetopopup="always"
|
||||
tooltiptext="&cmd.stateMenu.tooltip;">
|
||||
<menupopup>
|
||||
<menuitem id="detail-ask-to-activate-menuitem"
|
||||
class="addon-control"
|
||||
label="&cmd.askToActivate.label;"
|
||||
tooltiptext="&cmd.askToActivate.tooltip;"
|
||||
command="cmd_askToActivateItem"/>
|
||||
<menuitem id="detail-always-activate-menuitem"
|
||||
class="addon-control"
|
||||
label="&cmd.alwaysActivate.label;"
|
||||
tooltiptext="&cmd.alwaysActivate.tooltip;"
|
||||
command="cmd_alwaysActivateItem"/>
|
||||
<menuitem id="detail-never-activate-menuitem"
|
||||
class="addon-control"
|
||||
label="&cmd.neverActivate.label;"
|
||||
tooltiptext="&cmd.neverActivate.tooltip;"
|
||||
command="cmd_neverActivateItem"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
</scrollbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</page>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!-- This is intentionally empty and a dummy to let the GMPProvider
|
||||
have a preferences button in the list view. -->
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
const kDialog = "dialog";
|
||||
|
||||
/**
|
||||
* This dialog can be initialized from parameters supplied via window.arguments
|
||||
* or it can be used to display blocklist notification and blocklist blocked
|
||||
* installs via nsIDialogParamBlock as is done by nsIExtensionManager.
|
||||
*
|
||||
* When using this dialog with window.arguments it must be opened modally, the
|
||||
* caller can inspect the user action after the dialog closes by inspecting the
|
||||
* value of the |result| parameter on this object which is set to the dlgtype
|
||||
* of the button used to close the dialog.
|
||||
*
|
||||
* window.arguments[0] is an array of strings to display in the tree. If the
|
||||
* array is empty the tree will not be displayed.
|
||||
* window.arguments[1] a JS Object with the following properties:
|
||||
*
|
||||
* title: A title string, to be displayed in the title bar of the dialog.
|
||||
* message1: A message string, displayed above the addon list
|
||||
* message2: A message string, displayed below the addon list
|
||||
* message3: A bolded message string, displayed below the addon list
|
||||
* moreInfoURL: An url for displaying more information
|
||||
* iconClass : Can be one of the following values (default is alert-icon)
|
||||
* alert-icon, error-icon, or question-icon
|
||||
*
|
||||
* If no value is supplied for message1, message2, message3, or moreInfoURL,
|
||||
* the element is not displayed.
|
||||
*
|
||||
* buttons: {
|
||||
* accept: { label: "A Label for the Accept button",
|
||||
* focused: true },
|
||||
* cancel: { label: "A Label for the Cancel button" },
|
||||
* ...
|
||||
* },
|
||||
*
|
||||
* result: The dlgtype of button that was used to dismiss the dialog.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var gButtons = { };
|
||||
|
||||
function init() {
|
||||
var de = document.documentElement;
|
||||
var items = [];
|
||||
if (window.arguments[0] instanceof Components.interfaces.nsIDialogParamBlock) {
|
||||
// This is a warning about a blocklisted item the user is trying to install
|
||||
var args = window.arguments[0];
|
||||
var softblocked = args.GetInt(0) == 1 ? true : false;
|
||||
|
||||
var extensionsBundle = document.getElementById("extensionsBundle");
|
||||
try {
|
||||
var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
||||
.getService(Components.interfaces.nsIURLFormatter);
|
||||
var url = formatter.formatURLPref("extensions.blocklist.detailsURL");
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
var params = {
|
||||
moreInfoURL: url,
|
||||
};
|
||||
|
||||
if (softblocked) {
|
||||
params.title = extensionsBundle.getString("softBlockedInstallTitle");
|
||||
params.message1 = extensionsBundle.getFormattedString("softBlockedInstallMsg",
|
||||
[args.GetString(0)]);
|
||||
var accept = de.getButton("accept");
|
||||
accept.label = extensionsBundle.getString("softBlockedInstallAcceptLabel");
|
||||
accept.accessKey = extensionsBundle.getString("softBlockedInstallAcceptKey");
|
||||
de.getButton("cancel").focus();
|
||||
document.addEventListener("dialogaccept", allowInstall, false);
|
||||
}
|
||||
else {
|
||||
params.title = extensionsBundle.getString("blocklistedInstallTitle2");
|
||||
params.message1 = extensionsBundle.getFormattedString("blocklistedInstallMsg2",
|
||||
[args.GetString(0)]);
|
||||
de.buttons = "accept";
|
||||
de.getButton("accept").focus();
|
||||
}
|
||||
}
|
||||
else {
|
||||
items = window.arguments[0];
|
||||
params = window.arguments[1];
|
||||
}
|
||||
|
||||
var addons = document.getElementById("addonsChildren");
|
||||
if (items.length > 0)
|
||||
document.getElementById("addonsTree").hidden = false;
|
||||
|
||||
// Fill the addons list
|
||||
for (var item of items) {
|
||||
var treeitem = document.createElementNS(kXULNS, "treeitem");
|
||||
var treerow = document.createElementNS(kXULNS, "treerow");
|
||||
var treecell = document.createElementNS(kXULNS, "treecell");
|
||||
treecell.setAttribute("label", item);
|
||||
treerow.appendChild(treecell);
|
||||
treeitem.appendChild(treerow);
|
||||
addons.appendChild(treeitem);
|
||||
}
|
||||
|
||||
// Set the messages
|
||||
var messages = ["message1", "message2", "message3"];
|
||||
for (let messageEntry of messages) {
|
||||
if (messageEntry in params) {
|
||||
var message = document.getElementById(messageEntry);
|
||||
message.hidden = false;
|
||||
message.appendChild(document.createTextNode(params[messageEntry]));
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("infoIcon").className =
|
||||
params["iconClass"] ? "spaced " + params["iconClass"] : "spaced alert-icon";
|
||||
|
||||
if ("moreInfoURL" in params && params["moreInfoURL"]) {
|
||||
message = document.getElementById("moreInfo");
|
||||
message.value = extensionsBundle.getString("moreInfoText");
|
||||
message.setAttribute("href", params["moreInfoURL"]);
|
||||
document.getElementById("moreInfoBox").hidden = false;
|
||||
}
|
||||
|
||||
// Set the window title
|
||||
if ("title" in params)
|
||||
document.title = params.title;
|
||||
|
||||
// Set up the buttons
|
||||
if ("buttons" in params) {
|
||||
gButtons = params.buttons;
|
||||
var buttonString = "";
|
||||
for (var buttonType in gButtons)
|
||||
buttonString += "," + buttonType;
|
||||
de.buttons = buttonString.substr(1);
|
||||
for (buttonType in gButtons) {
|
||||
var button = de.getButton(buttonType);
|
||||
button.label = gButtons[buttonType].label;
|
||||
if (gButtons[buttonType].focused)
|
||||
button.focus();
|
||||
document.addEventListener(kDialog + buttonType, handleButtonCommand, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
for (var buttonType in gButtons)
|
||||
document.removeEventListener(kDialog + buttonType, handleButtonCommand, true);
|
||||
}
|
||||
|
||||
function allowInstall() {
|
||||
var args = window.arguments[0];
|
||||
args.SetInt(1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch for the user hitting one of the buttons to dismiss the dialog
|
||||
* and report the result back to the caller through the |result| property on
|
||||
* the arguments object.
|
||||
*/
|
||||
function handleButtonCommand(event) {
|
||||
window.arguments[1].result = event.type.substr(kDialog.length);
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
||||
|
||||
<dialog id="addonList" windowtype="Addons:List"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onunload="shutdown();"
|
||||
buttons="accept,cancel" onload="init();">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mozapps/content/extensions/list.js"/>
|
||||
|
||||
<stringbundle id="extensionsBundle"
|
||||
src="chrome://mozapps/locale/extensions/extensions.properties"/>
|
||||
<stringbundle id="brandBundle"
|
||||
src="chrome://branding/locale/brand.properties"/>
|
||||
|
||||
<hbox align="start">
|
||||
<vbox>
|
||||
<image id="infoIcon"/>
|
||||
</vbox>
|
||||
<vbox class="spaced" style="min-width: 20em; max-width: 40em">
|
||||
<label id="message1" class="spaced" hidden="true"/>
|
||||
<separator class="thin"/>
|
||||
<tree id="addonsTree" rows="6" hidecolumnpicker="true" hidden="true" class="spaced">
|
||||
<treecols style="max-width: 25em;">
|
||||
<treecol flex="1" id="nameColumn" hideheader="true"/>
|
||||
</treecols>
|
||||
<treechildren id="addonsChildren"/>
|
||||
</tree>
|
||||
<label id="message2" class="spaced" hidden="true"/>
|
||||
<label class="bold spaced" id="message3" hidden="true"/>
|
||||
<hbox id="moreInfoBox" hidden="true">
|
||||
<label id="moreInfo" class="text-link spaced"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</dialog>
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
var gAddon = null;
|
||||
|
||||
// If the user enables the add-on through some other UI close this window
|
||||
var EnableListener = {
|
||||
onEnabling: function(aAddon) {
|
||||
if (aAddon.id == gAddon.id)
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
AddonManager.addAddonListener(EnableListener);
|
||||
|
||||
function initialize() {
|
||||
// About URIs don't implement nsIURL so we have to find the query string
|
||||
// manually
|
||||
let spec = document.location.href;
|
||||
let pos = spec.indexOf("?");
|
||||
let query = "";
|
||||
if (pos >= 0)
|
||||
query = spec.substring(pos + 1);
|
||||
|
||||
// Just assume the query is "id=<id>"
|
||||
let id = query.substring(3);
|
||||
if (!id) {
|
||||
window.location = "about:blank";
|
||||
return;
|
||||
}
|
||||
|
||||
let bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/newaddon.properties");
|
||||
|
||||
AddonManager.getAddonByID(id, function(aAddon) {
|
||||
// If the add-on doesn't exist or it is already enabled or it has already
|
||||
// been seen or it cannot be enabled then this UI is useless, just close it.
|
||||
// This shouldn't normally happen unless session restore restores the tab.
|
||||
if (!aAddon || !aAddon.userDisabled || aAddon.seen ||
|
||||
!(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) {
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
gAddon = aAddon;
|
||||
|
||||
document.getElementById("addon-info").setAttribute("type", aAddon.type);
|
||||
|
||||
let icon = document.getElementById("icon");
|
||||
if (aAddon.icon64URL)
|
||||
icon.src = aAddon.icon64URL;
|
||||
else if (aAddon.iconURL)
|
||||
icon.src = aAddon.iconURL;
|
||||
|
||||
let name = bundle.formatStringFromName("name", [aAddon.name, aAddon.version],
|
||||
2);
|
||||
document.getElementById("name").value = name;
|
||||
|
||||
if (aAddon.creator) {
|
||||
let creator = bundle.formatStringFromName("author", [aAddon.creator], 1);
|
||||
document.getElementById("author").value = creator;
|
||||
} else {
|
||||
document.getElementById("author").hidden = true;
|
||||
}
|
||||
|
||||
let uri = "getResourceURI" in aAddon ? aAddon.getResourceURI() : null;
|
||||
let locationLabel = document.getElementById("location");
|
||||
if (uri instanceof Ci.nsIFileURL) {
|
||||
let location = bundle.formatStringFromName("location", [uri.file.path], 1);
|
||||
locationLabel.value = location;
|
||||
locationLabel.setAttribute("tooltiptext", location);
|
||||
} else {
|
||||
document.getElementById("location").hidden = true;
|
||||
}
|
||||
|
||||
// Only mark the add-on as seen if the page actually gets focus
|
||||
if (document.hasFocus()) {
|
||||
aAddon.markAsSeen();
|
||||
}
|
||||
else {
|
||||
document.addEventListener("focus", () => aAddon.markAsSeen(), false);
|
||||
}
|
||||
|
||||
var event = document.createEvent("Events");
|
||||
event.initEvent("AddonDisplayed", true, true);
|
||||
document.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
||||
function unload() {
|
||||
AddonManager.removeAddonListener(EnableListener);
|
||||
}
|
||||
|
||||
function continueClicked() {
|
||||
AddonManager.removeAddonListener(EnableListener);
|
||||
|
||||
if (document.getElementById("allow").checked) {
|
||||
gAddon.userDisabled = false;
|
||||
|
||||
if (gAddon.pendingOperations & AddonManager.PENDING_ENABLE) {
|
||||
document.getElementById("allow").disabled = true;
|
||||
document.getElementById("buttonDeck").selectedPanel = document.getElementById("restartPanel");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
window.close();
|
||||
}
|
||||
|
||||
function restartClicked() {
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
|
||||
createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested",
|
||||
"restart");
|
||||
if (cancelQuit.data)
|
||||
return; // somebody canceled our quit request
|
||||
|
||||
window.close();
|
||||
|
||||
let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
|
||||
getService(Components.interfaces.nsIAppStartup);
|
||||
appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
|
||||
}
|
||||
|
||||
function cancelClicked() {
|
||||
gAddon.userDisabled = true;
|
||||
AddonManager.addAddonListener(EnableListener);
|
||||
|
||||
document.getElementById("allow").disabled = false;
|
||||
document.getElementById("buttonDeck").selectedPanel = document.getElementById("continuePanel");
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/extensions/newaddon.css"?>
|
||||
|
||||
<!DOCTYPE page [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % newaddonDTD SYSTEM "chrome://mozapps/locale/extensions/newaddon.dtd">
|
||||
%newaddonDTD;
|
||||
]>
|
||||
|
||||
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xhtml="http://www.w3.org/1999/xhtml" title="&title;"
|
||||
disablefastfind="true" id="addon-page" onload="initialize()"
|
||||
onunload="unload()" role="application" align="stretch" pack="stretch">
|
||||
|
||||
<xhtml:link rel="shortcut icon" style="display: none"
|
||||
href="chrome://mozapps/skin/extensions/extensionGeneric-16.png"/>
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mozapps/content/extensions/newaddon.js"/>
|
||||
|
||||
<scrollbox id="addon-scrollbox" align="center">
|
||||
<spacer id="spacer-start"/>
|
||||
|
||||
<vbox id="addon-container" class="main-content">
|
||||
<description>&intro;</description>
|
||||
|
||||
<hbox id="addon-info">
|
||||
<image id="icon"/>
|
||||
<vbox flex="1">
|
||||
<label id="name"/>
|
||||
<label id="author"/>
|
||||
<label id="location" crop="end"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
||||
<hbox id="warning">
|
||||
<image id="warning-icon"/>
|
||||
<description flex="1">&warning;</description>
|
||||
</hbox>
|
||||
|
||||
<checkbox id="allow" label="&allow;"/>
|
||||
<description id="later">&later;</description>
|
||||
|
||||
<deck id="buttonDeck">
|
||||
<hbox id="continuePanel">
|
||||
<button id="continue-button" label="&continue;"
|
||||
oncommand="continueClicked()"/>
|
||||
</hbox>
|
||||
<vbox id="restartPanel">
|
||||
<description id="restartMessage">&restartMessage;</description>
|
||||
<hbox id="restartPanelButtons">
|
||||
<button id="restart-button" label="&restartButton;" oncommand="restartClicked()"/>
|
||||
<button id="cancel-button" label="&cancelButton;" oncommand="cancelClicked()"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</deck>
|
||||
</vbox>
|
||||
|
||||
<spacer id="spacer-end"/>
|
||||
</scrollbox>
|
||||
</page>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
- You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window SYSTEM "chrome://pluginproblem/locale/pluginproblem.dtd">
|
||||
|
||||
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<setting type="control" title="&plugin.file;">
|
||||
<label class="text-list" id="pluginLibraries"/>
|
||||
</setting>
|
||||
<setting type="control" title="&plugin.mimeTypes;">
|
||||
<label class="text-list" id="pluginMimeTypes"/>
|
||||
</setting>
|
||||
<setting type="bool" pref="dom.ipc.plugins.flash.disable-protected-mode"
|
||||
inverted="true" title="&plugin.flashProtectedMode.label;"
|
||||
id="pluginEnableProtectedMode"
|
||||
learnmore="https://support.mozilla.org/kb/flash-protected-mode-settings" />
|
||||
</vbox>
|
||||
|
|
@ -1,486 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE page [
|
||||
<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd">
|
||||
%extensionsDTD;
|
||||
]>
|
||||
|
||||
<!-- import-globals-from extensions.js -->
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="setting-base">
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
this.preferenceChanged();
|
||||
|
||||
this.addEventListener("keypress", function(event) {
|
||||
event.stopPropagation();
|
||||
}, false);
|
||||
|
||||
if (this.usePref)
|
||||
Services.prefs.addObserver(this.pref, this._observer, true);
|
||||
]]></constructor>
|
||||
|
||||
<field name="_observer"><![CDATA[({
|
||||
_self: this,
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
const Ci = Components.interfaces;
|
||||
if (aIID.equals(Ci.nsIObserver) ||
|
||||
aIID.equals(Ci.nsISupportsWeakReference) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Components.Exception("No interface", Components.results.NS_ERROR_NO_INTERFACE);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aPrefName) {
|
||||
if (aTopic != "nsPref:changed")
|
||||
return;
|
||||
|
||||
if (this._self.pref == aPrefName)
|
||||
this._self.preferenceChanged();
|
||||
}
|
||||
})]]>
|
||||
</field>
|
||||
|
||||
<method name="fireEvent">
|
||||
<parameter name="eventName"/>
|
||||
<parameter name="funcStr"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let body = funcStr || this.getAttribute(eventName);
|
||||
if (!body)
|
||||
return;
|
||||
|
||||
try {
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent(eventName, true, true);
|
||||
let f = new Function("event", body);
|
||||
f.call(this, event);
|
||||
}
|
||||
catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Should be code to set the from the preference input.value
|
||||
throw Components.Exception("No valueFromPreference implementation",
|
||||
Components.results.NS_ERROR_NOT_IMPLEMENTED);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Should be code to set the input.value from the preference
|
||||
throw Components.Exception("No valueToPreference implementation",
|
||||
Components.results.NS_ERROR_NOT_IMPLEMENTED);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="inputChanged">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.usePref && !this._updatingInput) {
|
||||
this.valueToPreference();
|
||||
this.fireEvent("oninputchanged");
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="preferenceChanged">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.usePref) {
|
||||
this._updatingInput = true;
|
||||
try {
|
||||
this.valueFromPreference();
|
||||
this.fireEvent("onpreferencechanged");
|
||||
} catch (e) {}
|
||||
this._updatingInput = false;
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<property name="usePref" readonly="true" onget="return this.hasAttribute('pref');"/>
|
||||
<property name="pref" readonly="true" onget="return this.getAttribute('pref');"/>
|
||||
<property name="type" readonly="true" onget="return this.getAttribute('type');"/>
|
||||
<property name="value" onget="return this.input.value;" onset="return this.input.value = val;"/>
|
||||
|
||||
<field name="_updatingInput">false</field>
|
||||
<field name="input">document.getAnonymousElementByAttribute(this, "anonid", "input");</field>
|
||||
<field name="settings">
|
||||
this.parentNode.localName == "settings" ? this.parentNode : null;
|
||||
</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
<xul:label class="preferences-learnmore text-link"
|
||||
onclick="document.getBindingParent(this).openLearnMore()">&setting.learnmore;</xul:label>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:checkbox anonid="input" xbl:inherits="disabled,onlabel,offlabel,label=checkboxlabel" oncommand="inputChanged();"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = Services.prefs.getBoolPref(this.pref);
|
||||
this.value = this.inverted ? !val : val;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = this.value;
|
||||
Services.prefs.setBoolPref(this.pref, this.inverted ? !val : val);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<property name="value" onget="return this.input.checked;" onset="return this.input.setChecked(val);"/>
|
||||
<property name="inverted" readonly="true" onget="return this.getAttribute('inverted');"/>
|
||||
|
||||
<method name="openLearnMore">
|
||||
<body>
|
||||
<![CDATA[
|
||||
window.open(this.getAttribute("learnmore"), "_blank");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-boolint" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = Services.prefs.getIntPref(this.pref);
|
||||
this.value = (val == this.getAttribute("on"));
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Services.prefs.setIntPref(this.pref, this.getAttribute(this.value ? "on" : "off"));
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-localized-bool" extends="chrome://mozapps/content/extensions/setting.xml#setting-bool">
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = Services.prefs.getComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString).data;
|
||||
if (this.inverted) val = !val;
|
||||
this.value = (val == "true");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = this.value;
|
||||
if (this.inverted) val = !val;
|
||||
let pref = Components.classes["@mozilla.org/pref-localizedstring;1"].createInstance(Components.interfaces.nsIPrefLocalizedString);
|
||||
pref.data = this.inverted ? (!val).toString() : val.toString();
|
||||
Services.prefs.setComplexValue(this.pref, Components.interfaces.nsIPrefLocalizedString, pref);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-integer" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:textbox type="number" anonid="input" oninput="inputChanged();" onchange="inputChanged();"
|
||||
xbl:inherits="disabled,emptytext,min,max,increment,hidespinbuttons,wraparound,size"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = Services.prefs.getIntPref(this.pref);
|
||||
this.value = val;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Services.prefs.setIntPref(this.pref, this.value);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-control" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<children/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-string" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:textbox anonid="input" flex="1" oninput="inputChanged();"
|
||||
xbl:inherits="disabled,emptytext,type=inputtype,min,max,increment,hidespinbuttons,decimalplaces,wraparound"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.value = Preferences.get(this.pref, "");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Preferences.set(this.pref, this.value);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-color" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:colorpicker type="button" anonid="input" xbl:inherits="disabled" onchange="document.getBindingParent(this).inputChanged();"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// We must wait for the colorpicker's binding to be applied before setting the value
|
||||
if (!this.input.color)
|
||||
this.input.initialize();
|
||||
this.value = Services.prefs.getCharPref(this.pref);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Services.prefs.setCharPref(this.pref, this.value);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<property name="value" onget="return this.input.color;" onset="return this.input.color = val;"/>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-path" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:button type="button" anonid="button" label="&settings.path.button.label;" xbl:inherits="disabled" oncommand="showPicker();"/>
|
||||
<xul:label anonid="input" flex="1" crop="center" xbl:inherits="disabled"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<method name="showPicker">
|
||||
<body>
|
||||
<![CDATA[
|
||||
var filePicker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||
filePicker.init(window, this.getAttribute("title"),
|
||||
this.type == "file" ? Ci.nsIFilePicker.modeOpen : Ci.nsIFilePicker.modeGetFolder);
|
||||
if (this.value) {
|
||||
try {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file.initWithPath(this.value);
|
||||
filePicker.displayDirectory = this.type == "file" ? file.parent : file;
|
||||
if (this.type == "file") {
|
||||
filePicker.defaultString = file.leafName;
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (filePicker.show() != Ci.nsIFilePicker.returnCancel) {
|
||||
this.value = filePicker.file.path;
|
||||
this.inputChanged();
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this.value = Preferences.get(this.pref, "");
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
Preferences.set(this.pref, this.value);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<field name="_value"></field>
|
||||
|
||||
<property name="value">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
return this._value;
|
||||
]]>
|
||||
</getter>
|
||||
<setter>
|
||||
<![CDATA[
|
||||
this._value = val;
|
||||
let label = "";
|
||||
if (val) {
|
||||
try {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file.initWithPath(val);
|
||||
label = this.hasAttribute("fullpath") ? file.path : file.leafName;
|
||||
} catch (e) {}
|
||||
}
|
||||
this.input.tooltipText = val;
|
||||
return this.input.value = label;
|
||||
]]>
|
||||
</setter>
|
||||
</property>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<binding id="setting-multi" extends="chrome://mozapps/content/extensions/setting.xml#setting-base">
|
||||
<content>
|
||||
<xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<xul:label class="preferences-title" flex="1" xbl:inherits="xbl:text=title"/>
|
||||
</xul:hbox>
|
||||
<xul:description class="preferences-description" flex="1" xbl:inherits="xbl:text=desc"/>
|
||||
</xul:vbox>
|
||||
<xul:hbox class="preferences-alignment">
|
||||
<children includes="radiogroup|menulist"/>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this.control.addEventListener("command", this.inputChanged.bind(this), false);
|
||||
]]>
|
||||
</constructor>
|
||||
|
||||
<method name="valueFromPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
let val = Preferences.get(this.pref, "").toString();
|
||||
|
||||
if ("itemCount" in this.control) {
|
||||
for (let i = 0; i < this.control.itemCount; i++) {
|
||||
if (this.control.getItemAtIndex(i).value == val) {
|
||||
this.control.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.control.setAttribute("value", val);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="valueToPreference">
|
||||
<body>
|
||||
<![CDATA[
|
||||
// We might not have a pref already set, so we guess the type from the value attribute
|
||||
let val = this.control.selectedItem.value;
|
||||
if (val == "true" || val == "false") {
|
||||
val = val == "true";
|
||||
} else if (/^-?\d+$/.test(val)) {
|
||||
val = parseInt(val, 10);
|
||||
}
|
||||
Preferences.set(this.pref, val);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<field name="control">this.getElementsByTagName(this.getAttribute("type") == "radio" ? "radiogroup" : "menulist")[0];</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
|
@ -1,663 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This UI is only opened from the Extension Manager when the app is upgraded.
|
||||
|
||||
"use strict";
|
||||
|
||||
const PREF_UPDATE_EXTENSIONS_ENABLED = "extensions.update.enabled";
|
||||
const PREF_XPINSTALL_ENABLED = "xpinstall.enabled";
|
||||
|
||||
// timeout (in milliseconds) to wait for response to the metadata ping
|
||||
const METADATA_TIMEOUT = 30000;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate", "resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository", "resource://gre/modules/addons/AddonRepository.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
|
||||
var logger = null;
|
||||
|
||||
var gUpdateWizard = {
|
||||
// When synchronizing app compatibility info this contains all installed
|
||||
// add-ons. When checking for compatible versions this contains only
|
||||
// incompatible add-ons.
|
||||
addons: [],
|
||||
// Contains a Set of IDs for add-on that were disabled by the application update.
|
||||
affectedAddonIDs: null,
|
||||
// The add-ons that we found updates available for
|
||||
addonsToUpdate: [],
|
||||
shouldSuggestAutoChecking: false,
|
||||
shouldAutoCheck: false,
|
||||
xpinstallEnabled: true,
|
||||
xpinstallLocked: false,
|
||||
// cached AddonInstall entries for add-ons we might want to update,
|
||||
// keyed by add-on ID
|
||||
addonInstalls: new Map(),
|
||||
shuttingDown: false,
|
||||
// Count the add-ons disabled by this update, enabled/disabled by
|
||||
// metadata checks, and upgraded.
|
||||
disabled: 0,
|
||||
metadataEnabled: 0,
|
||||
metadataDisabled: 0,
|
||||
upgraded: 0,
|
||||
upgradeFailed: 0,
|
||||
upgradeDeclined: 0,
|
||||
|
||||
init: function()
|
||||
{
|
||||
logger = Log.repository.getLogger("addons.update-dialog");
|
||||
// XXX could we pass the addons themselves rather than the IDs?
|
||||
this.affectedAddonIDs = new Set(window.arguments[0]);
|
||||
|
||||
try {
|
||||
this.shouldSuggestAutoChecking =
|
||||
!Services.prefs.getBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
this.xpinstallEnabled = Services.prefs.getBoolPref(PREF_XPINSTALL_ENABLED);
|
||||
this.xpinstallLocked = Services.prefs.prefIsLocked(PREF_XPINSTALL_ENABLED);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (Services.io.offline)
|
||||
document.documentElement.currentPage = document.getElementById("offline");
|
||||
else
|
||||
document.documentElement.currentPage = document.getElementById("versioninfo");
|
||||
},
|
||||
|
||||
onWizardFinish: function gUpdateWizard_onWizardFinish ()
|
||||
{
|
||||
if (this.shouldSuggestAutoChecking)
|
||||
Services.prefs.setBoolPref(PREF_UPDATE_EXTENSIONS_ENABLED, this.shouldAutoCheck);
|
||||
},
|
||||
|
||||
_setUpButton: function(aButtonID, aButtonKey, aDisabled)
|
||||
{
|
||||
var strings = document.getElementById("updateStrings");
|
||||
var button = document.documentElement.getButton(aButtonID);
|
||||
if (aButtonKey) {
|
||||
button.label = strings.getString(aButtonKey);
|
||||
try {
|
||||
button.setAttribute("accesskey", strings.getString(aButtonKey + "Accesskey"));
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
}
|
||||
button.disabled = aDisabled;
|
||||
},
|
||||
|
||||
setButtonLabels: function(aBackButton, aBackButtonIsDisabled,
|
||||
aNextButton, aNextButtonIsDisabled,
|
||||
aCancelButton, aCancelButtonIsDisabled)
|
||||
{
|
||||
this._setUpButton("back", aBackButton, aBackButtonIsDisabled);
|
||||
this._setUpButton("next", aNextButton, aNextButtonIsDisabled);
|
||||
this._setUpButton("cancel", aCancelButton, aCancelButtonIsDisabled);
|
||||
},
|
||||
|
||||
// Update Errors
|
||||
errorItems: [],
|
||||
|
||||
checkForErrors: function(aElementIDToShow)
|
||||
{
|
||||
if (this.errorItems.length > 0)
|
||||
document.getElementById(aElementIDToShow).hidden = false;
|
||||
},
|
||||
|
||||
onWizardClose: function(aEvent)
|
||||
{
|
||||
return this.onWizardCancel();
|
||||
},
|
||||
|
||||
onWizardCancel: function()
|
||||
{
|
||||
gUpdateWizard.shuttingDown = true;
|
||||
// Allow add-ons to continue downloading and installing
|
||||
// in the background, though some may require a later restart
|
||||
// Pages that are waiting for user input go into the background
|
||||
// on cancel
|
||||
if (gMismatchPage.waiting) {
|
||||
logger.info("Dialog closed in mismatch page");
|
||||
if (gUpdateWizard.addonInstalls.size > 0) {
|
||||
gInstallingPage.startInstalls(
|
||||
Array.from(gUpdateWizard.addonInstalls.values()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pages that do asynchronous things will just keep running and check
|
||||
// gUpdateWizard.shuttingDown to trigger background behaviour
|
||||
if (!gInstallingPage.installing) {
|
||||
logger.info("Dialog closed while waiting for updated compatibility information");
|
||||
}
|
||||
else {
|
||||
logger.info("Dialog closed while downloading and installing updates");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
var gOfflinePage = {
|
||||
onPageAdvanced: function()
|
||||
{
|
||||
Services.io.offline = false;
|
||||
return true;
|
||||
},
|
||||
|
||||
toggleOffline: function()
|
||||
{
|
||||
var nextbtn = document.documentElement.getButton("next");
|
||||
nextbtn.disabled = !nextbtn.disabled;
|
||||
}
|
||||
}
|
||||
|
||||
// Addon listener to count addons enabled/disabled by metadata checks
|
||||
var listener = {
|
||||
onDisabled: function(aAddon) {
|
||||
gUpdateWizard.affectedAddonIDs.add(aAddon.id);
|
||||
gUpdateWizard.metadataDisabled++;
|
||||
},
|
||||
onEnabled: function(aAddon) {
|
||||
gUpdateWizard.affectedAddonIDs.delete(aAddon.id);
|
||||
gUpdateWizard.metadataEnabled++;
|
||||
}
|
||||
};
|
||||
|
||||
var gVersionInfoPage = {
|
||||
_completeCount: 0,
|
||||
_totalCount: 0,
|
||||
_versionInfoDone: false,
|
||||
onPageShow: Task.async(function*() {
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"nextButtonText", true,
|
||||
"cancelButtonText", false);
|
||||
|
||||
gUpdateWizard.disabled = gUpdateWizard.affectedAddonIDs.size;
|
||||
|
||||
// Ensure compatibility overrides are up to date before checking for
|
||||
// individual addon updates.
|
||||
AddonManager.addAddonListener(listener);
|
||||
if (AddonRepository.isMetadataStale()) {
|
||||
// Do the metadata ping, listening for any newly enabled/disabled add-ons.
|
||||
yield AddonRepository.repopulateCache(METADATA_TIMEOUT);
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
logger.debug("repopulateCache completed after dialog closed");
|
||||
}
|
||||
}
|
||||
// Fetch the add-ons that are still affected by this update,
|
||||
// excluding the hotfix add-on.
|
||||
let idlist = Array.from(gUpdateWizard.affectedAddonIDs).filter(
|
||||
a => a.id != AddonManager.hotfixID);
|
||||
if (idlist.length < 1) {
|
||||
gVersionInfoPage.onAllUpdatesFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug("Fetching affected addons " + idlist.toSource());
|
||||
let fetchedAddons = yield new Promise((resolve, reject) =>
|
||||
AddonManager.getAddonsByIDs(idlist, resolve));
|
||||
// We shouldn't get nulls here, but let's be paranoid...
|
||||
gUpdateWizard.addons = fetchedAddons.filter(a => a);
|
||||
if (gUpdateWizard.addons.length < 1) {
|
||||
gVersionInfoPage.onAllUpdatesFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
gVersionInfoPage._totalCount = gUpdateWizard.addons.length;
|
||||
|
||||
for (let addon of gUpdateWizard.addons) {
|
||||
logger.debug("VersionInfo Finding updates for ${id}", addon);
|
||||
addon.findUpdates(gVersionInfoPage, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
|
||||
}
|
||||
}),
|
||||
|
||||
onAllUpdatesFinished: function() {
|
||||
AddonManager.removeAddonListener(listener);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_disabled",
|
||||
gUpdateWizard.disabled);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_enabled",
|
||||
gUpdateWizard.metadataEnabled);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_metadata_disabled",
|
||||
gUpdateWizard.metadataDisabled);
|
||||
// Record 0 for these here in case we exit early; values will be replaced
|
||||
// later if we actually upgrade any.
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded", 0);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed", 0);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined", 0);
|
||||
// Filter out any add-ons that are now enabled.
|
||||
let addonList = gUpdateWizard.addons.map(a => a.id + ":" + a.appDisabled);
|
||||
logger.debug("VersionInfo updates finished: found " + addonList.toSource());
|
||||
let filteredAddons = [];
|
||||
for (let a of gUpdateWizard.addons) {
|
||||
if (a.appDisabled) {
|
||||
logger.debug("Continuing with add-on " + a.id);
|
||||
filteredAddons.push(a);
|
||||
}
|
||||
else if (gUpdateWizard.addonInstalls.has(a.id)) {
|
||||
gUpdateWizard.addonInstalls.get(a.id).cancel();
|
||||
gUpdateWizard.addonInstalls.delete(a.id);
|
||||
}
|
||||
}
|
||||
gUpdateWizard.addons = filteredAddons;
|
||||
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
// jump directly to updating auto-update add-ons in the background
|
||||
if (gUpdateWizard.addonInstalls.size > 0) {
|
||||
let installs = Array.from(gUpdateWizard.addonInstalls.values());
|
||||
gInstallingPage.startInstalls(installs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (filteredAddons.length > 0) {
|
||||
if (!gUpdateWizard.xpinstallEnabled && gUpdateWizard.xpinstallLocked) {
|
||||
document.documentElement.currentPage = document.getElementById("adminDisabled");
|
||||
return;
|
||||
}
|
||||
document.documentElement.currentPage = document.getElementById("mismatch");
|
||||
}
|
||||
else {
|
||||
logger.info("VersionInfo: No updates require further action");
|
||||
// VersionInfo compatibility updates resolved all compatibility problems,
|
||||
// close this window and continue starting the application...
|
||||
// XXX Bug 314754 - We need to use setTimeout to close the window due to
|
||||
// the EM using xmlHttpRequest when checking for updates.
|
||||
setTimeout(close, 0);
|
||||
}
|
||||
},
|
||||
|
||||
// UpdateListener
|
||||
onUpdateFinished: function(aAddon, status) {
|
||||
++this._completeCount;
|
||||
|
||||
if (status != AddonManager.UPDATE_STATUS_NO_ERROR) {
|
||||
logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
|
||||
" failed for " + aAddon.id + ": " + status);
|
||||
gUpdateWizard.errorItems.push(aAddon);
|
||||
}
|
||||
else {
|
||||
logger.debug("VersionInfo update " + this._completeCount + " of " + this._totalCount +
|
||||
" finished for " + aAddon.id);
|
||||
}
|
||||
|
||||
// If we're not in the background, just make a list of add-ons that have
|
||||
// updates available
|
||||
if (!gUpdateWizard.shuttingDown) {
|
||||
// If we're still in the update check window and the add-on is now active
|
||||
// then it won't have been disabled by startup
|
||||
if (aAddon.active) {
|
||||
AddonManagerPrivate.removeStartupChange(AddonManager.STARTUP_CHANGE_DISABLED, aAddon.id);
|
||||
gUpdateWizard.metadataEnabled++;
|
||||
}
|
||||
|
||||
// Update the status text and progress bar
|
||||
var updateStrings = document.getElementById("updateStrings");
|
||||
var statusElt = document.getElementById("versioninfo.status");
|
||||
var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
|
||||
statusElt.setAttribute("value", statusString);
|
||||
|
||||
// Update the status text and progress bar
|
||||
var progress = document.getElementById("versioninfo.progress");
|
||||
progress.mode = "normal";
|
||||
progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
|
||||
}
|
||||
|
||||
if (this._completeCount == this._totalCount)
|
||||
this.onAllUpdatesFinished();
|
||||
},
|
||||
|
||||
onUpdateAvailable: function(aAddon, aInstall) {
|
||||
logger.debug("VersionInfo got an install for " + aAddon.id + ": " + aAddon.version);
|
||||
gUpdateWizard.addonInstalls.set(aAddon.id, aInstall);
|
||||
},
|
||||
};
|
||||
|
||||
var gMismatchPage = {
|
||||
waiting: false,
|
||||
|
||||
onPageShow: function()
|
||||
{
|
||||
gMismatchPage.waiting = true;
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"mismatchCheckNow", false,
|
||||
"mismatchDontCheck", false);
|
||||
document.documentElement.getButton("next").focus();
|
||||
|
||||
var incompatible = document.getElementById("mismatch.incompatible");
|
||||
for (let addon of gUpdateWizard.addons) {
|
||||
var listitem = document.createElement("listitem");
|
||||
listitem.setAttribute("label", addon.name + " " + addon.version);
|
||||
incompatible.appendChild(listitem);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var gUpdatePage = {
|
||||
_totalCount: 0,
|
||||
_completeCount: 0,
|
||||
onPageShow: function()
|
||||
{
|
||||
gMismatchPage.waiting = false;
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"nextButtonText", true,
|
||||
"cancelButtonText", false);
|
||||
document.documentElement.getButton("next").focus();
|
||||
|
||||
gUpdateWizard.errorItems = [];
|
||||
|
||||
this._totalCount = gUpdateWizard.addons.length;
|
||||
for (let addon of gUpdateWizard.addons) {
|
||||
logger.debug("UpdatePage requesting update for " + addon.id);
|
||||
// Redundant call to find updates again here when we already got them
|
||||
// in the VersionInfo page: https://bugzilla.mozilla.org/show_bug.cgi?id=960597
|
||||
addon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
|
||||
}
|
||||
},
|
||||
|
||||
onAllUpdatesFinished: function() {
|
||||
if (gUpdateWizard.shuttingDown)
|
||||
return;
|
||||
|
||||
var nextPage = document.getElementById("noupdates");
|
||||
if (gUpdateWizard.addonsToUpdate.length > 0)
|
||||
nextPage = document.getElementById("found");
|
||||
document.documentElement.currentPage = nextPage;
|
||||
},
|
||||
|
||||
// UpdateListener
|
||||
onUpdateAvailable: function(aAddon, aInstall) {
|
||||
logger.debug("UpdatePage got an update for " + aAddon.id + ": " + aAddon.version);
|
||||
gUpdateWizard.addonsToUpdate.push(aInstall);
|
||||
},
|
||||
|
||||
onUpdateFinished: function(aAddon, status) {
|
||||
if (status != AddonManager.UPDATE_STATUS_NO_ERROR)
|
||||
gUpdateWizard.errorItems.push(aAddon);
|
||||
|
||||
++this._completeCount;
|
||||
|
||||
if (!gUpdateWizard.shuttingDown) {
|
||||
// Update the status text and progress bar
|
||||
var updateStrings = document.getElementById("updateStrings");
|
||||
var statusElt = document.getElementById("checking.status");
|
||||
var statusString = updateStrings.getFormattedString("statusPrefix", [aAddon.name]);
|
||||
statusElt.setAttribute("value", statusString);
|
||||
|
||||
var progress = document.getElementById("checking.progress");
|
||||
progress.value = Math.ceil((this._completeCount / this._totalCount) * 100);
|
||||
}
|
||||
|
||||
if (this._completeCount == this._totalCount)
|
||||
this.onAllUpdatesFinished()
|
||||
},
|
||||
};
|
||||
|
||||
var gFoundPage = {
|
||||
onPageShow: function()
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"installButtonText", false,
|
||||
null, false);
|
||||
|
||||
var foundUpdates = document.getElementById("found.updates");
|
||||
var itemCount = gUpdateWizard.addonsToUpdate.length;
|
||||
for (let install of gUpdateWizard.addonsToUpdate) {
|
||||
let listItem = foundUpdates.appendItem(install.name + " " + install.version);
|
||||
listItem.setAttribute("type", "checkbox");
|
||||
listItem.setAttribute("checked", "true");
|
||||
listItem.install = install;
|
||||
}
|
||||
|
||||
if (!gUpdateWizard.xpinstallEnabled) {
|
||||
document.getElementById("xpinstallDisabledAlert").hidden = false;
|
||||
document.getElementById("enableXPInstall").focus();
|
||||
document.documentElement.getButton("next").disabled = true;
|
||||
}
|
||||
else {
|
||||
document.documentElement.getButton("next").focus();
|
||||
document.documentElement.getButton("next").disabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
toggleXPInstallEnable: function(aEvent)
|
||||
{
|
||||
var enabled = aEvent.target.checked;
|
||||
gUpdateWizard.xpinstallEnabled = enabled;
|
||||
var pref = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
pref.setBoolPref(PREF_XPINSTALL_ENABLED, enabled);
|
||||
this.updateNextButton();
|
||||
},
|
||||
|
||||
updateNextButton: function()
|
||||
{
|
||||
if (!gUpdateWizard.xpinstallEnabled) {
|
||||
document.documentElement.getButton("next").disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var oneChecked = false;
|
||||
var foundUpdates = document.getElementById("found.updates");
|
||||
var updates = foundUpdates.getElementsByTagName("listitem");
|
||||
for (let update of updates) {
|
||||
if (!update.checked)
|
||||
continue;
|
||||
oneChecked = true;
|
||||
break;
|
||||
}
|
||||
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"installButtonText", true,
|
||||
null, false);
|
||||
document.getElementById("found").setAttribute("next", "installing");
|
||||
document.documentElement.getButton("next").disabled = !oneChecked;
|
||||
}
|
||||
};
|
||||
|
||||
var gInstallingPage = {
|
||||
_installs : [],
|
||||
_errors : [],
|
||||
_strings : null,
|
||||
_currentInstall : -1,
|
||||
_installing : false,
|
||||
|
||||
// Initialize fields we need for installing and tracking progress,
|
||||
// and start iterating through the installations
|
||||
startInstalls: function(aInstallList) {
|
||||
if (!gUpdateWizard.xpinstallEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
let installs = Array.from(aInstallList).map(a => a.existingAddon.id);
|
||||
logger.debug("Start installs for " + installs.toSource());
|
||||
this._errors = [];
|
||||
this._installs = aInstallList;
|
||||
this._installing = true;
|
||||
this.startNextInstall();
|
||||
},
|
||||
|
||||
onPageShow: function()
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true,
|
||||
"nextButtonText", true,
|
||||
null, true);
|
||||
|
||||
var foundUpdates = document.getElementById("found.updates");
|
||||
var updates = foundUpdates.getElementsByTagName("listitem");
|
||||
let toInstall = [];
|
||||
for (let update of updates) {
|
||||
if (!update.checked) {
|
||||
logger.info("User chose to cancel update of " + update.label);
|
||||
gUpdateWizard.upgradeDeclined++;
|
||||
update.install.cancel();
|
||||
continue;
|
||||
}
|
||||
toInstall.push(update.install);
|
||||
}
|
||||
this._strings = document.getElementById("updateStrings");
|
||||
|
||||
this.startInstalls(toInstall);
|
||||
},
|
||||
|
||||
startNextInstall: function() {
|
||||
if (this._currentInstall >= 0) {
|
||||
this._installs[this._currentInstall].removeListener(this);
|
||||
}
|
||||
|
||||
this._currentInstall++;
|
||||
|
||||
if (this._installs.length == this._currentInstall) {
|
||||
Services.obs.notifyObservers(null, "TEST:all-updates-done", null);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgraded",
|
||||
gUpdateWizard.upgraded);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeFailed",
|
||||
gUpdateWizard.upgradeFailed);
|
||||
AddonManagerPrivate.recordSimpleMeasure("appUpdate_upgradeDeclined",
|
||||
gUpdateWizard.upgradeDeclined);
|
||||
this._installing = false;
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
var nextPage = this._errors.length > 0 ? "installerrors" : "finished";
|
||||
document.getElementById("installing").setAttribute("next", nextPage);
|
||||
document.documentElement.advance();
|
||||
return;
|
||||
}
|
||||
|
||||
let install = this._installs[this._currentInstall];
|
||||
|
||||
if (gUpdateWizard.shuttingDown && !AddonManager.shouldAutoUpdate(install.existingAddon)) {
|
||||
logger.debug("Don't update " + install.existingAddon.id + " in background");
|
||||
gUpdateWizard.upgradeDeclined++;
|
||||
install.cancel();
|
||||
this.startNextInstall();
|
||||
return;
|
||||
}
|
||||
install.addListener(this);
|
||||
install.install();
|
||||
},
|
||||
|
||||
// InstallListener
|
||||
onDownloadStarted: function(aInstall) {
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
var strings = document.getElementById("updateStrings");
|
||||
var label = strings.getFormattedString("downloadingPrefix", [aInstall.name]);
|
||||
var actionItem = document.getElementById("actionItem");
|
||||
actionItem.value = label;
|
||||
},
|
||||
|
||||
onDownloadProgress: function(aInstall) {
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
var downloadProgress = document.getElementById("downloadProgress");
|
||||
downloadProgress.value = Math.ceil(100 * aInstall.progress / aInstall.maxProgress);
|
||||
},
|
||||
|
||||
onDownloadEnded: function(aInstall) {
|
||||
},
|
||||
|
||||
onDownloadFailed: function(aInstall) {
|
||||
this._errors.push(aInstall);
|
||||
|
||||
gUpdateWizard.upgradeFailed++;
|
||||
this.startNextInstall();
|
||||
},
|
||||
|
||||
onInstallStarted: function(aInstall) {
|
||||
if (gUpdateWizard.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
var strings = document.getElementById("updateStrings");
|
||||
var label = strings.getFormattedString("installingPrefix", [aInstall.name]);
|
||||
var actionItem = document.getElementById("actionItem");
|
||||
actionItem.value = label;
|
||||
},
|
||||
|
||||
onInstallEnded: function(aInstall, aAddon) {
|
||||
if (!gUpdateWizard.shuttingDown) {
|
||||
// Remember that this add-on was updated during startup
|
||||
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_CHANGED,
|
||||
aAddon.id);
|
||||
}
|
||||
|
||||
gUpdateWizard.upgraded++;
|
||||
this.startNextInstall();
|
||||
},
|
||||
|
||||
onInstallFailed: function(aInstall) {
|
||||
this._errors.push(aInstall);
|
||||
|
||||
gUpdateWizard.upgradeFailed++;
|
||||
this.startNextInstall();
|
||||
}
|
||||
};
|
||||
|
||||
var gInstallErrorsPage = {
|
||||
onPageShow: function()
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
|
||||
document.documentElement.getButton("finish").focus();
|
||||
},
|
||||
};
|
||||
|
||||
// Displayed when there are incompatible add-ons and the xpinstall.enabled
|
||||
// pref is false and locked.
|
||||
var gAdminDisabledPage = {
|
||||
onPageShow: function()
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true, null, true,
|
||||
"cancelButtonText", true);
|
||||
document.documentElement.getButton("finish").focus();
|
||||
}
|
||||
};
|
||||
|
||||
// Displayed when selected add-on updates have been installed without error.
|
||||
// There can still be add-ons that are not compatible and don't have an update.
|
||||
var gFinishedPage = {
|
||||
onPageShow: function()
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
|
||||
document.documentElement.getButton("finish").focus();
|
||||
|
||||
if (gUpdateWizard.shouldSuggestAutoChecking) {
|
||||
document.getElementById("finishedCheckDisabled").hidden = false;
|
||||
gUpdateWizard.shouldAutoCheck = true;
|
||||
}
|
||||
else
|
||||
document.getElementById("finishedCheckEnabled").hidden = false;
|
||||
|
||||
document.documentElement.getButton("finish").focus();
|
||||
}
|
||||
};
|
||||
|
||||
// Displayed when there are incompatible add-ons and there are no available
|
||||
// updates.
|
||||
var gNoUpdatesPage = {
|
||||
onPageShow: function(aEvent)
|
||||
{
|
||||
gUpdateWizard.setButtonLabels(null, true, null, true, null, true);
|
||||
if (gUpdateWizard.shouldSuggestAutoChecking) {
|
||||
document.getElementById("noupdatesCheckDisabled").hidden = false;
|
||||
gUpdateWizard.shouldAutoCheck = true;
|
||||
}
|
||||
else
|
||||
document.getElementById("noupdatesCheckEnabled").hidden = false;
|
||||
|
||||
gUpdateWizard.checkForErrors("updateCheckErrorNotFound");
|
||||
document.documentElement.getButton("finish").focus();
|
||||
}
|
||||
};
|
||||
|
|
@ -1,194 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/extensions/update.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE wizard [
|
||||
<!ENTITY % updateDTD SYSTEM "chrome://mozapps/locale/extensions/update.dtd">
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%updateDTD;
|
||||
%brandDTD;
|
||||
]>
|
||||
|
||||
<wizard id="updateWizard"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&updateWizard.title;"
|
||||
windowtype="Addons:Compatibility"
|
||||
branded="true"
|
||||
onload="gUpdateWizard.init();"
|
||||
onwizardfinish="gUpdateWizard.onWizardFinish();"
|
||||
onwizardcancel="return gUpdateWizard.onWizardCancel();"
|
||||
onclose="return gUpdateWizard.onWizardClose(event);"
|
||||
buttons="accept,cancel">
|
||||
|
||||
<script type="application/javascript" src="chrome://mozapps/content/extensions/update.js"/>
|
||||
|
||||
<stringbundleset id="updateSet">
|
||||
<stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
|
||||
<stringbundle id="updateStrings" src="chrome://mozapps/locale/extensions/update.properties"/>
|
||||
</stringbundleset>
|
||||
|
||||
<wizardpage id="dummy" pageid="dummy"/>
|
||||
|
||||
<wizardpage id="offline" pageid="offline" next="versioninfo"
|
||||
label="&offline.title;"
|
||||
onpageadvanced="return gOfflinePage.onPageAdvanced();">
|
||||
<description>&offline.description;</description>
|
||||
<checkbox id="toggleOffline"
|
||||
checked="true"
|
||||
label="&offline.toggleOffline.label;"
|
||||
accesskey="&offline.toggleOffline.accesskey;"
|
||||
oncommand="gOfflinePage.toggleOffline();"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="versioninfo" pageid="versioninfo" next="mismatch"
|
||||
label="&versioninfo.wizard.title;"
|
||||
onpageshow="gVersionInfoPage.onPageShow();">
|
||||
<label>&versioninfo.top.label;</label>
|
||||
<separator class="thin"/>
|
||||
<progressmeter id="versioninfo.progress" mode="undetermined"/>
|
||||
<hbox align="center">
|
||||
<image id="versioninfo.throbber" class="throbber"/>
|
||||
<label flex="1" id="versioninfo.status" crop="right">&versioninfo.waiting;</label>
|
||||
</hbox>
|
||||
<separator/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="mismatch" pageid="mismatch" next="checking"
|
||||
label="&mismatch.win.title;"
|
||||
onpageshow="gMismatchPage.onPageShow();">
|
||||
<label>&mismatch.top.label;</label>
|
||||
<separator class="thin"/>
|
||||
<listbox id="mismatch.incompatible" flex="1"/>
|
||||
<separator class="thin"/>
|
||||
<label>&mismatch.bottom.label;</label>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="checking" pageid="checking" next="noupdates"
|
||||
label="&checking.wizard.title;"
|
||||
onpageshow="gUpdatePage.onPageShow();">
|
||||
<label>&checking.top.label;</label>
|
||||
<separator class="thin"/>
|
||||
<progressmeter id="checking.progress"/>
|
||||
<hbox align="center">
|
||||
<image id="checking.throbber" class="throbber"/>
|
||||
<label id="checking.status" flex="1" crop="right">&checking.status;</label>
|
||||
</hbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="noupdates" pageid="noupdates"
|
||||
label="&noupdates.wizard.title;"
|
||||
onpageshow="gNoUpdatesPage.onPageShow();">
|
||||
<description>&noupdates.intro.desc;</description>
|
||||
<separator class="thin"/>
|
||||
<hbox id="updateCheckErrorNotFound" class="alertBox" hidden="true" align="top">
|
||||
<description flex="1">&noupdates.error.desc;</description>
|
||||
</hbox>
|
||||
<separator class="thin"/>
|
||||
<description id="noupdatesCheckEnabled" hidden="true">
|
||||
&noupdates.checkEnabled.desc;
|
||||
</description>
|
||||
<vbox id="noupdatesCheckDisabled" hidden="true">
|
||||
<description>&finished.checkDisabled.desc;</description>
|
||||
<checkbox label="&enableChecking.label;" checked="true"
|
||||
oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
|
||||
</vbox>
|
||||
<separator flex="1"/>
|
||||
#ifndef XP_MACOSX
|
||||
<label>&clickFinish.label;</label>
|
||||
#else
|
||||
<label>&clickFinish.labelMac;</label>
|
||||
#endif
|
||||
<separator class="thin"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="found" pageid="found" next="installing"
|
||||
label="&found.wizard.title;"
|
||||
onpageshow="gFoundPage.onPageShow();">
|
||||
<label>&found.top.label;</label>
|
||||
<separator class="thin"/>
|
||||
<listbox id="found.updates" flex="1" seltype="multiple"
|
||||
onclick="gFoundPage.updateNextButton();"/>
|
||||
<separator class="thin"/>
|
||||
<vbox align="left" id="xpinstallDisabledAlert" hidden="true">
|
||||
<description>&found.disabledXPinstall.label;</description>
|
||||
<checkbox label="&found.enableXPInstall.label;"
|
||||
id="enableXPInstall"
|
||||
accesskey="&found.enableXPInstall.accesskey;"
|
||||
oncommand="gFoundPage.toggleXPInstallEnable(event);"/>
|
||||
</vbox>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="installing" pageid="installing" next="finished"
|
||||
label="&installing.wizard.title;"
|
||||
onpageshow="gInstallingPage.onPageShow();">
|
||||
<label>&installing.top.label;</label>
|
||||
<progressmeter id="downloadProgress"/>
|
||||
<hbox align="center">
|
||||
<image id="installing.throbber" class="throbber"/>
|
||||
<label id="actionItem" flex="1" crop="right"/>
|
||||
</hbox>
|
||||
<separator/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="installerrors" pageid="installerrors"
|
||||
label="&installerrors.wizard.title;"
|
||||
onpageshow="gInstallErrorsPage.onPageShow();">
|
||||
<hbox align="top" class="alertBox">
|
||||
<description flex="1">&installerrors.intro.label;</description>
|
||||
</hbox>
|
||||
<separator flex="1"/>
|
||||
#ifndef XP_MACOSX
|
||||
<label>&clickFinish.label;</label>
|
||||
#else
|
||||
<label>&clickFinish.labelMac;</label>
|
||||
#endif
|
||||
<separator class="thin"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="adminDisabled" pageid="adminDisabled"
|
||||
label="&adminDisabled.wizard.title;"
|
||||
onpageshow="gAdminDisabledPage.onPageShow();">
|
||||
<separator/>
|
||||
<hbox class="alertBox" align="top">
|
||||
<description flex="1">&adminDisabled.warning.label;</description>
|
||||
</hbox>
|
||||
<separator flex="1"/>
|
||||
#ifndef XP_MACOSX
|
||||
<label>&clickFinish.label;</label>
|
||||
#else
|
||||
<label>&clickFinish.labelMac;</label>
|
||||
#endif
|
||||
<separator class="thin"/>
|
||||
</wizardpage>
|
||||
|
||||
<wizardpage id="finished" pageid="finished"
|
||||
label="&finished.wizard.title;"
|
||||
onpageshow="gFinishedPage.onPageShow();">
|
||||
|
||||
<label>&finished.top.label;</label>
|
||||
<separator/>
|
||||
<description id="finishedCheckEnabled" hidden="true">
|
||||
&finished.checkEnabled.desc;
|
||||
</description>
|
||||
<vbox id="finishedCheckDisabled" hidden="true">
|
||||
<description>&finished.checkDisabled.desc;</description>
|
||||
<checkbox label="&enableChecking.label;" checked="true"
|
||||
oncommand="gUpdateWizard.shouldAutoCheck = this.checked;"/>
|
||||
</vbox>
|
||||
<separator flex="1"/>
|
||||
#ifndef XP_MACOSX
|
||||
<label>&clickFinish.label;</label>
|
||||
#else
|
||||
<label>&clickFinish.labelMac;</label>
|
||||
#endif
|
||||
<separator class="thin"/>
|
||||
</wizardpage>
|
||||
|
||||
</wizard>
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<xsl:stylesheet version="1.0" xmlns:xhtml="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<!-- Any elements not otherwise specified will be stripped but the contents
|
||||
will be displayed. All attributes are stripped from copied elements. -->
|
||||
|
||||
<!-- Block these elements and their contents -->
|
||||
<xsl:template match="xhtml:head|xhtml:script|xhtml:style">
|
||||
</xsl:template>
|
||||
|
||||
<!-- Allowable styling elements -->
|
||||
<xsl:template match="xhtml:b|xhtml:i|xhtml:em|xhtml:strong|xhtml:u|xhtml:q|xhtml:sub|xhtml:sup|xhtml:code">
|
||||
<xsl:copy><xsl:apply-templates/></xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Allowable block formatting elements -->
|
||||
<xsl:template match="xhtml:h1|xhtml:h2|xhtml:h3|xhtml:p|xhtml:div|xhtml:blockquote|xhtml:pre">
|
||||
<xsl:copy><xsl:apply-templates/></xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- Allowable list formatting elements -->
|
||||
<xsl:template match="xhtml:ul|xhtml:ol|xhtml:li|xhtml:dl|xhtml:dt|xhtml:dd">
|
||||
<xsl:copy><xsl:apply-templates/></xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- These elements are copied and their contents dropped -->
|
||||
<xsl:template match="xhtml:br|xhtml:hr">
|
||||
<xsl:copy/>
|
||||
</xsl:template>
|
||||
|
||||
<!-- The root document -->
|
||||
<xsl:template match="/">
|
||||
<xhtml:body><xsl:apply-templates/></xhtml:body>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
installitem {
|
||||
-moz-binding: url("chrome://mozapps/content/xpinstall/xpinstallItem.xml#installitem");
|
||||
display: -moz-box;
|
||||
}
|
||||
|
|
@ -1,196 +0,0 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var XPInstallConfirm = {};
|
||||
|
||||
XPInstallConfirm.init = function()
|
||||
{
|
||||
Components.utils.import("resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
var _installCountdown;
|
||||
var _installCountdownInterval;
|
||||
var _focused;
|
||||
var _timeout;
|
||||
|
||||
// Default to cancelling the install when the window unloads
|
||||
XPInstallConfirm._installOK = false;
|
||||
|
||||
var bundle = document.getElementById("xpinstallConfirmStrings");
|
||||
|
||||
let args = window.arguments[0].wrappedJSObject;
|
||||
|
||||
// If all installs have already been cancelled in some way then just close
|
||||
// the window
|
||||
if (args.installs.every(i => i.state != AddonManager.STATE_DOWNLOADED)) {
|
||||
window.close();
|
||||
return;
|
||||
}
|
||||
|
||||
var _installCountdownLength = 5;
|
||||
try {
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
var delay_in_milliseconds = prefs.getIntPref("security.dialog_enable_delay");
|
||||
_installCountdownLength = Math.round(delay_in_milliseconds / 500);
|
||||
} catch (ex) { }
|
||||
|
||||
var itemList = document.getElementById("itemList");
|
||||
|
||||
let installMap = new WeakMap();
|
||||
let installListener = {
|
||||
onDownloadCancelled: function(install) {
|
||||
itemList.removeChild(installMap.get(install));
|
||||
if (--numItemsToInstall == 0)
|
||||
window.close();
|
||||
}
|
||||
};
|
||||
|
||||
var numItemsToInstall = args.installs.length;
|
||||
for (let install of args.installs) {
|
||||
var installItem = document.createElement("installitem");
|
||||
itemList.appendChild(installItem);
|
||||
|
||||
installItem.name = install.addon.name;
|
||||
installItem.url = install.sourceURI.spec;
|
||||
var icon = install.iconURL;
|
||||
if (icon)
|
||||
installItem.icon = icon;
|
||||
var type = install.type;
|
||||
if (type)
|
||||
installItem.type = type;
|
||||
if (install.certName) {
|
||||
installItem.cert = bundle.getFormattedString("signed", [install.certName]);
|
||||
}
|
||||
else {
|
||||
installItem.cert = bundle.getString("unverified");
|
||||
}
|
||||
installItem.signed = install.certName ? "true" : "false";
|
||||
|
||||
installMap.set(install, installItem);
|
||||
install.addListener(installListener);
|
||||
}
|
||||
|
||||
var introString = bundle.getString("itemWarnIntroSingle");
|
||||
if (numItemsToInstall > 4)
|
||||
introString = bundle.getFormattedString("itemWarnIntroMultiple", [numItemsToInstall]);
|
||||
var textNode = document.createTextNode(introString);
|
||||
var introNode = document.getElementById("itemWarningIntro");
|
||||
while (introNode.hasChildNodes())
|
||||
introNode.removeChild(introNode.firstChild);
|
||||
introNode.appendChild(textNode);
|
||||
|
||||
var okButton = document.documentElement.getButton("accept");
|
||||
okButton.focus();
|
||||
|
||||
function okButtonCountdown() {
|
||||
_installCountdown -= 1;
|
||||
|
||||
if (_installCountdown < 1) {
|
||||
okButton.label = bundle.getString("installButtonLabel");
|
||||
okButton.disabled = false;
|
||||
clearInterval(_installCountdownInterval);
|
||||
}
|
||||
else
|
||||
okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
|
||||
}
|
||||
|
||||
function myfocus() {
|
||||
// Clear the timeout if it exists so only the last one will be used.
|
||||
if (_timeout)
|
||||
clearTimeout(_timeout);
|
||||
|
||||
// Use setTimeout since the last focus or blur event to fire is the one we
|
||||
// want
|
||||
_timeout = setTimeout(setWidgetsAfterFocus, 0);
|
||||
}
|
||||
|
||||
function setWidgetsAfterFocus() {
|
||||
if (_focused)
|
||||
return;
|
||||
|
||||
_installCountdown = _installCountdownLength;
|
||||
_installCountdownInterval = setInterval(okButtonCountdown, 500);
|
||||
okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
|
||||
_focused = true;
|
||||
}
|
||||
|
||||
function myblur() {
|
||||
// Clear the timeout if it exists so only the last one will be used.
|
||||
if (_timeout)
|
||||
clearTimeout(_timeout);
|
||||
|
||||
// Use setTimeout since the last focus or blur event to fire is the one we
|
||||
// want
|
||||
_timeout = setTimeout(setWidgetsAfterBlur, 0);
|
||||
}
|
||||
|
||||
function setWidgetsAfterBlur() {
|
||||
if (!_focused)
|
||||
return;
|
||||
|
||||
// Set _installCountdown to the inital value set in setWidgetsAfterFocus
|
||||
// plus 1 so when the window is focused there is immediate ui feedback.
|
||||
_installCountdown = _installCountdownLength + 1;
|
||||
okButton.label = bundle.getFormattedString("installButtonDisabledLabel", [_installCountdown]);
|
||||
okButton.disabled = true;
|
||||
clearInterval(_installCountdownInterval);
|
||||
_focused = false;
|
||||
}
|
||||
|
||||
function myUnload() {
|
||||
if (_installCountdownLength > 0) {
|
||||
document.removeEventListener("focus", myfocus, true);
|
||||
document.removeEventListener("blur", myblur, true);
|
||||
}
|
||||
window.removeEventListener("unload", myUnload, false);
|
||||
|
||||
for (let install of args.installs)
|
||||
install.removeListener(installListener);
|
||||
|
||||
// Now perform the desired action - either install the
|
||||
// addons or cancel the installations
|
||||
if (XPInstallConfirm._installOK) {
|
||||
for (let install of args.installs)
|
||||
install.install();
|
||||
}
|
||||
else {
|
||||
for (let install of args.installs) {
|
||||
if (install.state != AddonManager.STATE_CANCELLED)
|
||||
install.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("unload", myUnload, false);
|
||||
|
||||
if (_installCountdownLength > 0) {
|
||||
document.addEventListener("focus", myfocus, true);
|
||||
document.addEventListener("blur", myblur, true);
|
||||
|
||||
okButton.disabled = true;
|
||||
setWidgetsAfterFocus();
|
||||
}
|
||||
else
|
||||
okButton.label = bundle.getString("installButtonLabel");
|
||||
}
|
||||
|
||||
XPInstallConfirm.onOK = function()
|
||||
{
|
||||
Components.classes["@mozilla.org/base/telemetry;1"].
|
||||
getService(Components.interfaces.nsITelemetry).
|
||||
getHistogramById("SECURITY_UI").
|
||||
add(Components.interfaces.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL_CLICK_THROUGH);
|
||||
// Perform the install or cancel after the window has unloaded
|
||||
XPInstallConfirm._installOK = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
XPInstallConfirm.onCancel = function()
|
||||
{
|
||||
// Perform the install or cancel after the window has unloaded
|
||||
XPInstallConfirm._installOK = false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://mozapps/content/xpinstall/xpinstallConfirm.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mozapps/skin/xpinstall/xpinstallConfirm.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE dialog SYSTEM "chrome://mozapps/locale/xpinstall/xpinstallConfirm.dtd">
|
||||
|
||||
<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
id="xpinstallConfirm" title="&dialog.title;" style="&dialog.style;"
|
||||
windowtype="Addons:Install"
|
||||
onload="XPInstallConfirm.init()"
|
||||
ondialogaccept="return XPInstallConfirm.onOK();"
|
||||
ondialogcancel="return XPInstallConfirm.onCancel();">
|
||||
|
||||
<script src="chrome://mozapps/content/xpinstall/xpinstallConfirm.js" type="application/javascript"/>
|
||||
|
||||
<stringbundle id="xpinstallConfirmStrings"
|
||||
src="chrome://mozapps/locale/xpinstall/xpinstallConfirm.properties"/>
|
||||
|
||||
<vbox flex="1" id="dialogContentBox">
|
||||
<hbox id="xpinstallheader" align="start">
|
||||
<image class="alert-icon"/>
|
||||
<vbox flex="1">
|
||||
<description class="warning">&warningPrimary.label;</description>
|
||||
<description>&warningSecondary.label;</description>
|
||||
</vbox>
|
||||
</hbox>
|
||||
<label id="itemWarningIntro"/>
|
||||
<vbox id="itemList" class="listbox" flex="1" style="overflow: auto;"/>
|
||||
</vbox>
|
||||
|
||||
</dialog>
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<!DOCTYPE bindings SYSTEM "chrome://mozapps/locale/xpinstall/xpinstallConfirm.dtd">
|
||||
|
||||
<bindings id="xpinstallItemBindings"
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl">
|
||||
|
||||
<binding id="installitem">
|
||||
<resources>
|
||||
<stylesheet src="chrome://mozapps/skin/xpinstall/xpinstallConfirm.css"/>
|
||||
</resources>
|
||||
<content>
|
||||
<xul:hbox flex="1">
|
||||
<xul:vbox align="center" pack="center" class="xpinstallIconContainer">
|
||||
<xul:image class="xpinstallItemIcon" xbl:inherits="src=icon"/>
|
||||
</xul:vbox>
|
||||
<xul:vbox flex="1" pack="center">
|
||||
<xul:hbox class="xpinstallItemNameRow" align="center">
|
||||
<xul:label class="xpinstallItemName" xbl:inherits="value=name" crop="right"/>
|
||||
<xul:label class="xpinstallItemSigned" xbl:inherits="value=cert,signed"/>
|
||||
</xul:hbox>
|
||||
<xul:hbox class="xpinstallItemDetailsRow" align="center">
|
||||
<xul:textbox class="xpinstallItemURL" xbl:inherits="value=url" flex="1" readonly="true" crop="right"/>
|
||||
</xul:hbox>
|
||||
</xul:vbox>
|
||||
</xul:hbox>
|
||||
</content>
|
||||
<implementation>
|
||||
<property name="name" onset="this.setAttribute('name', val); return val;"
|
||||
onget="return this.getAttribute('name');"/>
|
||||
<property name="cert" onset="this.setAttribute('cert', val); return val;"
|
||||
onget="return this.getAttribute('cert');"/>
|
||||
<property name="signed" onset="this.setAttribute('signed', val); return val;"
|
||||
onget="return this.getAttribute('signed');"/>
|
||||
<property name="url" onset="this.setAttribute('url', val); return val;"
|
||||
onget="return this.getAttribute('url');"/>
|
||||
<property name="icon" onset="this.setAttribute('icon', val); return val;"
|
||||
onget="return this.getAttribute('icon');"/>
|
||||
<property name="type" onset="this.setAttribute('type', val); return val;"
|
||||
onget="return this.getAttribute('type');"/>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
||||
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
Firefox System Add-on Update Protocol
|
||||
=====================================
|
||||
This document describes the protocol that Firefox uses when retrieving updates
|
||||
for System Add-ons from the automatic update service (AUS, currently `Balrog`_),
|
||||
and the expected behavior of Firefox based on the updater service's response.
|
||||
|
||||
.. _Balrog: https://wiki.mozilla.org/Balrog
|
||||
|
||||
System Add-ons
|
||||
--------------
|
||||
System add-ons:
|
||||
|
||||
* Are add-ons that ship with Firefox, are hidden from the UI, and cannot be
|
||||
disabled
|
||||
* Can be updated by Firefox depending on the AUS response to Firefox's update
|
||||
request
|
||||
* Are stored in two locations:
|
||||
|
||||
* The **default** set ships with Firefox and is stored in the application
|
||||
directory.
|
||||
* The **update** set is stored in the user’s profile directory. If an add-on
|
||||
is both in the update and default set, the update version gets precedence.
|
||||
|
||||
Update Request
|
||||
--------------
|
||||
To determine what updates to install, Firefox makes an HTTP **GET** request to
|
||||
AUS once a day via a URL of the form::
|
||||
|
||||
https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml
|
||||
|
||||
The path segments surrounded by ``%`` symbols are variable fields that Firefox
|
||||
fills in with information about itself and the environment it's running in:
|
||||
|
||||
``VERSION``
|
||||
Firefox version number
|
||||
``BUILD_ID``
|
||||
Build ID
|
||||
``BUILD_TARGET``
|
||||
Build target
|
||||
``LOCALE``
|
||||
Build locale
|
||||
``CHANNEL``
|
||||
Update channel
|
||||
``OS_VERSION``
|
||||
OS Version
|
||||
``DISTRIBUTION``
|
||||
Firefox Distribution
|
||||
``DISTRIBUTION_VERSION``
|
||||
Firefox Distribution version
|
||||
|
||||
Update Response
|
||||
---------------
|
||||
AUS should respond with an XML document that looks something like this:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="flyweb@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/flyweb/flyweb@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
<addon id="pocket@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/pocket/pocket@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
</addons>
|
||||
</updates>
|
||||
|
||||
* The root element is ``<updates>``, used for all updater responses.
|
||||
* The only child of ``<updates>`` is ``<addons>``, which represents a list of
|
||||
system add-ons to update.
|
||||
* Within ``<addons>`` are several ``<addon>`` tags, each one corresponding to a
|
||||
system add-on to update.
|
||||
|
||||
``<addon>`` tags **must** have the following attributes:
|
||||
|
||||
``id``
|
||||
The extension ID
|
||||
``URL``
|
||||
URL to a signed XPI of the specified add-on version to download
|
||||
``hashFunction``
|
||||
Identifier of the hash function used to generate the hashValue attribute.
|
||||
``hashValue``
|
||||
Hash of the XPI file linked from the URL attribute, calculated using the function specified in the hashValue attribute.
|
||||
``size``
|
||||
Size (in bytes) of the XPI file linked from the URL attribute.
|
||||
``version``
|
||||
Version number of the add-on
|
||||
|
||||
Update Behavior
|
||||
---------------
|
||||
After receiving the update response, Firefox modifies the **update** add-ons
|
||||
according to the following algorithm:
|
||||
|
||||
1. If the ``<addons>`` tag is empty (``<addons></addons>``) in the response,
|
||||
**remove all system add-on updates**.
|
||||
2. If no add-ons were specified in the response (i.e. the ``<addons>`` tag
|
||||
is not present), do nothing and finish.
|
||||
3. If the **update** add-on set is equal to the set of add-ons specified in the
|
||||
update response, do nothing and finish.
|
||||
4. If the set of **default** add-ons is equal to the set of add-ons specified in
|
||||
the update response, remove all the **update** add-ons and finish.
|
||||
5. Download each add-on specified in the update response and store them in the
|
||||
"downloaded add-on set". A failed download **must** abort the entire system
|
||||
add-on update.
|
||||
6. Validate the downloaded add-ons. The following **must** be true for all
|
||||
downloaded add-ons, or the update process is aborted:
|
||||
|
||||
a. The ID and version of the downloaded add-on must match the specified ID or
|
||||
version in the update response.
|
||||
b. The hash provided in the update response must match the downloaded add-on
|
||||
file.
|
||||
c. The downloaded add-on file size must match the size given in the update
|
||||
response.
|
||||
d. The add-on must be compatible with Firefox (i.e. it must not be for a
|
||||
different application, such as Thunderbird).
|
||||
e. The add-on must be packed (i.e. be an XPI file).
|
||||
f. The add-on must be restartless.
|
||||
g. The add-on must be signed by the system add-on root certificate.
|
||||
|
||||
6. Once all downloaded add-ons are validated, install them into the profile
|
||||
directory as part of the **update** set.
|
||||
|
||||
Notes on the update process:
|
||||
|
||||
* Add-ons are considered "equal" if they have the same ID and version number.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The follow section describes common situations that we have or expect to run
|
||||
into and how the protocol described above handles them.
|
||||
|
||||
For simplicity, unless otherwise specified, all examples assume that there are
|
||||
two system add-ons in existence: **FlyWeb** and **Pocket**.
|
||||
|
||||
Basic
|
||||
~~~~~
|
||||
A user has Firefox 45, which shipped with FlyWeb 1.0 and Pocket 1.0. We want to
|
||||
update users to FlyWeb 2.0. AUS sends out the following update response:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="flyweb@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/flyweb/flyweb@mozilla.org-2.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="2.0"/>
|
||||
<addon id="pocket@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/pocket/pocket@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
</addons>
|
||||
</updates>
|
||||
|
||||
Firefox will download FlyWeb 2.0 and Pocket 1.0 and store them in the profile directory.
|
||||
|
||||
Missing Add-on
|
||||
~~~~~~~~~~~~~~
|
||||
A user has Firefox 45, which shipped with FlyWeb 1.0 and Pocket 1.0. We want to
|
||||
update users to FlyWeb 2.0, but accidentally forget to specify Pocket in the
|
||||
update response. AUS sends out the following:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="flyweb@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/flyweb/flyweb@mozilla.org-2.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="2.0"/>
|
||||
</addons>
|
||||
</updates>
|
||||
|
||||
Firefox will download FlyWeb 2.0 and store it in the profile directory. Pocket
|
||||
1.0 from the **default** location will be used.
|
||||
|
||||
Remove all system add-on updates
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
A response from AUS with an empty add-on set will *remove all system add-on
|
||||
updates*:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates>
|
||||
<addons></addons>
|
||||
</updates>
|
||||
|
||||
Rollout
|
||||
~~~~~~~
|
||||
A user has Firefox 45, which shipped with FlyWeb 1.0 and Pocket 1.0. We want to
|
||||
rollout FlyWeb 2.0 at a 10% sample rate. 10% of the time, AUS sends out:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="flyweb@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/flyweb/flyweb@mozilla.org-2.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="2.0"/>
|
||||
<addon id="pocket@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/pocket/pocket@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
</addons>
|
||||
</updates>
|
||||
|
||||
With this response, Firefox will download Pocket 1.0 and FlyWeb 2.0 and install
|
||||
them into the profile directory.
|
||||
|
||||
The other 90% of the time, AUS sends out an empty response:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates></updates>
|
||||
|
||||
With the empty response, Firefox will not make any changes. This means users who
|
||||
haven’t seen the 10% update response will stay on FlyWeb 1.0, and users who have
|
||||
seen it will stay on FlyWeb 2.0.
|
||||
|
||||
Once we’re happy with the rollout and want to switch to 100%, AUS will send the
|
||||
10% update response to 100% of users, upgrading everyone to FlyWeb 2.0.
|
||||
|
||||
Rollback
|
||||
~~~~~~~~
|
||||
This example continues from the “Rollout” example. If, during the 10% rollout,
|
||||
we find a major issue with FlyWeb 2.0, we want to roll all users back to FlyWeb 1.0.
|
||||
AUS sends out the following:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<updates>
|
||||
<addons>
|
||||
<addon id="flyweb@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/flyweb/flyweb@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
<addon id="pocket@mozilla.org" URL="https://ftp.mozilla.org/pub/system-addons/pocket/pocket@mozilla.org-1.0.xpi" hashFunction="sha512" hashValue="abcdef123" size="1234" version="1.0"/>
|
||||
</addons>
|
||||
</updates>
|
||||
|
||||
For users who have updated, Firefox will download FlyWeb 1.0 and Pocket 1.0 and
|
||||
install them into the profile directory. For users that haven’t yet updated,
|
||||
Firefox will see that the **default** add-on set matches the set in the update
|
||||
ping and clear the **update** add-on set.
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
==============
|
||||
Add-on Manager
|
||||
==============
|
||||
|
||||
This is the nascent documentation of the Add-on Manager code.
|
||||
|
||||
The public Add-on Manager interfaces are documented on MDN:
|
||||
|
||||
https://developer.mozilla.org/en-US/Add-ons/Add-on_Manager
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
SystemAddons
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
component {66354bc9-7ed1-4692-ae1d-8da97d6b205e} nsBlocklistService.js process=main
|
||||
contract @mozilla.org/extensions/blocklist;1 {66354bc9-7ed1-4692-ae1d-8da97d6b205e} process=main
|
||||
category profile-after-change nsBlocklistService @mozilla.org/extensions/blocklist;1 process=main
|
||||
component {e0a106ed-6ad4-47a4-b6af-2f1c8aa4712d} nsBlocklistServiceContent.js process=content
|
||||
contract @mozilla.org/extensions/blocklist;1 {e0a106ed-6ad4-47a4-b6af-2f1c8aa4712d} process=content
|
||||
|
||||
category update-timer nsBlocklistService @mozilla.org/extensions/blocklist;1,getService,blocklist-background-update-timer,extensions.blocklist.interval,86400
|
||||
#ifndef MOZ_WIDGET_GONK
|
||||
component {4399533d-08d1-458c-a87a-235f74451cfa} addonManager.js
|
||||
contract @mozilla.org/addons/integration;1 {4399533d-08d1-458c-a87a-235f74451cfa}
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
category update-timer addonManager @mozilla.org/addons/integration;1,getService,addon-background-update-timer,extensions.update.interval,86400
|
||||
#endif
|
||||
component {7beb3ba8-6ec3-41b4-b67c-da89b8518922} amContentHandler.js
|
||||
contract @mozilla.org/uriloader/content-handler;1?type=application/x-xpinstall {7beb3ba8-6ec3-41b4-b67c-da89b8518922}
|
||||
component {0f38e086-89a3-40a5-8ffc-9b694de1d04a} amWebInstallListener.js
|
||||
contract @mozilla.org/addons/web-install-listener;1 {0f38e086-89a3-40a5-8ffc-9b694de1d04a}
|
||||
component {9df8ef2b-94da-45c9-ab9f-132eb55fddf1} amInstallTrigger.js
|
||||
contract @mozilla.org/addons/installtrigger;1 {9df8ef2b-94da-45c9-ab9f-132eb55fddf1}
|
||||
category JavaScript-global-property InstallTrigger @mozilla.org/addons/installtrigger;1
|
||||
#ifndef MOZ_WIDGET_ANDROID
|
||||
category addon-provider-module PluginProvider resource://gre/modules/addons/PluginProvider.jsm
|
||||
#endif
|
||||
category addon-provider-module GMPProvider resource://gre/modules/addons/GMPProvider.jsm
|
||||
#endif
|
||||
component {8866d8e3-4ea5-48b7-a891-13ba0ac15235} amWebAPI.js
|
||||
contract @mozilla.org/addon-web-api/manager;1 {8866d8e3-4ea5-48b7-a891-13ba0ac15235}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/ExtensionManagement.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var namespace;
|
||||
var resource;
|
||||
var resProto;
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
namespace = data.id.replace(/@.*/, "");
|
||||
resource = `extension-${namespace}-api`;
|
||||
|
||||
resProto = Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Components.interfaces.nsIResProtocolHandler);
|
||||
|
||||
resProto.setSubstitution(resource, data.resourceURI);
|
||||
|
||||
ExtensionManagement.registerAPI(
|
||||
namespace,
|
||||
`resource://${resource}/schema.json`,
|
||||
`resource://${resource}/api.js`);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
resProto.setSubstitution(resource, null);
|
||||
|
||||
ExtensionManagement.unregisterAPI(namespace);
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "ADDON_SIGNING", "REQUIRE_SIGNING" ];
|
||||
|
||||
// Make these non-changable properties so they can't be manipulated from other
|
||||
// code in the app.
|
||||
Object.defineProperty(this, "ADDON_SIGNING", {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
#ifdef MOZ_ADDON_SIGNING
|
||||
value: true,
|
||||
#else
|
||||
value: false,
|
||||
#endif
|
||||
});
|
||||
|
||||
Object.defineProperty(this, "REQUIRE_SIGNING", {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
#ifdef MOZ_REQUIRE_SIGNING
|
||||
value: true,
|
||||
#else
|
||||
value: false,
|
||||
#endif
|
||||
});
|
||||
|
|
@ -1,192 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
const KEY_PROFILEDIR = "ProfD";
|
||||
const FILE_EXTENSIONS_LOG = "extensions.log";
|
||||
const PREF_LOGGING_ENABLED = "extensions.logging.enabled";
|
||||
|
||||
const LOGGER_FILE_PERM = parseInt("666", 8);
|
||||
|
||||
const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
|
||||
|
||||
Components.utils.import("resource://gre/modules/FileUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "LogManager" ];
|
||||
|
||||
var gDebugLogEnabled = false;
|
||||
|
||||
function formatLogMessage(aType, aName, aStr, aException) {
|
||||
let message = aType.toUpperCase() + " " + aName + ": " + aStr;
|
||||
if (aException) {
|
||||
if (typeof aException == "number")
|
||||
return message + ": " + Components.Exception("", aException).name;
|
||||
|
||||
message = message + ": " + aException;
|
||||
// instanceOf doesn't work here, let's duck type
|
||||
if (aException.fileName)
|
||||
message = message + " (" + aException.fileName + ":" + aException.lineNumber + ")";
|
||||
|
||||
if (aException.message == "too much recursion")
|
||||
dump(message + "\n" + aException.stack + "\n");
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
function getStackDetails(aException) {
|
||||
// Defensively wrap all this to ensure that failing to get the message source
|
||||
// doesn't stop the message from being logged
|
||||
try {
|
||||
if (aException) {
|
||||
if (aException instanceof Ci.nsIException) {
|
||||
return {
|
||||
sourceName: aException.filename,
|
||||
lineNumber: aException.lineNumber
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof aException == "object") {
|
||||
return {
|
||||
sourceName: aException.fileName,
|
||||
lineNumber: aException.lineNumber
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let stackFrame = Components.stack.caller.caller.caller;
|
||||
return {
|
||||
sourceName: stackFrame.filename,
|
||||
lineNumber: stackFrame.lineNumber
|
||||
};
|
||||
}
|
||||
catch (e) {
|
||||
return {
|
||||
sourceName: null,
|
||||
lineNumber: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function AddonLogger(aName) {
|
||||
this.name = aName;
|
||||
}
|
||||
|
||||
AddonLogger.prototype = {
|
||||
name: null,
|
||||
|
||||
error: function(aStr, aException) {
|
||||
let message = formatLogMessage("error", this.name, aStr, aException);
|
||||
|
||||
let stack = getStackDetails(aException);
|
||||
|
||||
let consoleMessage = Cc["@mozilla.org/scripterror;1"].
|
||||
createInstance(Ci.nsIScriptError);
|
||||
consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
|
||||
Ci.nsIScriptError.errorFlag, "component javascript");
|
||||
Services.console.logMessage(consoleMessage);
|
||||
|
||||
// Always dump errors, in case the Console Service isn't listening yet
|
||||
dump("*** " + message + "\n");
|
||||
|
||||
function formatTimestamp(date) {
|
||||
// Format timestamp as: "%Y-%m-%d %H:%M:%S"
|
||||
let year = String(date.getFullYear());
|
||||
let month = String(date.getMonth() + 1).padStart(2, "0");
|
||||
let day = String(date.getDate()).padStart(2, "0");
|
||||
let hours = String(date.getHours()).padStart(2, "0");
|
||||
let minutes = String(date.getMinutes()).padStart(2, "0");
|
||||
let seconds = String(date.getSeconds()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
try {
|
||||
var tstamp = new Date();
|
||||
var logfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]);
|
||||
var stream = Cc["@mozilla.org/network/file-output-stream;1"].
|
||||
createInstance(Ci.nsIFileOutputStream);
|
||||
stream.init(logfile, 0x02 | 0x08 | 0x10, LOGGER_FILE_PERM, 0); // write, create, append
|
||||
var writer = Cc["@mozilla.org/intl/converter-output-stream;1"].
|
||||
createInstance(Ci.nsIConverterOutputStream);
|
||||
writer.init(stream, "UTF-8", 0, 0x0000);
|
||||
writer.writeString(formatTimestamp(tstamp) + " " +
|
||||
message + " at " + stack.sourceName + ":" +
|
||||
stack.lineNumber + "\n");
|
||||
writer.close();
|
||||
}
|
||||
catch (e) { }
|
||||
},
|
||||
|
||||
warn: function(aStr, aException) {
|
||||
let message = formatLogMessage("warn", this.name, aStr, aException);
|
||||
|
||||
let stack = getStackDetails(aException);
|
||||
|
||||
let consoleMessage = Cc["@mozilla.org/scripterror;1"].
|
||||
createInstance(Ci.nsIScriptError);
|
||||
consoleMessage.init(message, stack.sourceName, null, stack.lineNumber, 0,
|
||||
Ci.nsIScriptError.warningFlag, "component javascript");
|
||||
Services.console.logMessage(consoleMessage);
|
||||
|
||||
if (gDebugLogEnabled)
|
||||
dump("*** " + message + "\n");
|
||||
},
|
||||
|
||||
log: function(aStr, aException) {
|
||||
if (gDebugLogEnabled) {
|
||||
let message = formatLogMessage("log", this.name, aStr, aException);
|
||||
dump("*** " + message + "\n");
|
||||
Services.console.logStringMessage(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.LogManager = {
|
||||
getLogger: function(aName, aTarget) {
|
||||
let logger = new AddonLogger(aName);
|
||||
|
||||
if (aTarget) {
|
||||
["error", "warn", "log"].forEach(function(name) {
|
||||
let fname = name.toUpperCase();
|
||||
delete aTarget[fname];
|
||||
aTarget[fname] = function(aStr, aException) {
|
||||
logger[name](aStr, aException);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return logger;
|
||||
}
|
||||
};
|
||||
|
||||
var PrefObserver = {
|
||||
init: function() {
|
||||
Services.prefs.addObserver(PREF_LOGGING_ENABLED, this, false);
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
this.observe(null, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, PREF_LOGGING_ENABLED);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "xpcom-shutdown") {
|
||||
Services.prefs.removeObserver(PREF_LOGGING_ENABLED, this);
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||
}
|
||||
else if (aTopic == NS_PREFBRANCH_PREFCHANGE_TOPIC_ID) {
|
||||
try {
|
||||
gDebugLogEnabled = Services.prefs.getBoolPref(PREF_LOGGING_ENABLED);
|
||||
}
|
||||
catch (e) {
|
||||
gDebugLogEnabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PrefObserver.init();
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,522 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
/* globals AddonManagerPrivate*/
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
const KEY_PROFILEDIR = "ProfD";
|
||||
const FILE_DATABASE = "addons.sqlite";
|
||||
const LAST_DB_SCHEMA = 4;
|
||||
|
||||
// Add-on properties present in the columns of the database
|
||||
const PROP_SINGLE = ["id", "type", "name", "version", "creator", "description",
|
||||
"fullDescription", "developerComments", "eula",
|
||||
"homepageURL", "supportURL", "contributionURL",
|
||||
"contributionAmount", "averageRating", "reviewCount",
|
||||
"reviewURL", "totalDownloads", "weeklyDownloads",
|
||||
"dailyUsers", "sourceURI", "repositoryStatus", "size",
|
||||
"updateDate"];
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
const LOGGER_ID = "addons.repository.sqlmigrator";
|
||||
|
||||
// Create a new logger for use by the Addons Repository SQL Migrator
|
||||
// (Requires AddonManager.jsm)
|
||||
var logger = Log.repository.getLogger(LOGGER_ID);
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["AddonRepository_SQLiteMigrator"];
|
||||
|
||||
|
||||
this.AddonRepository_SQLiteMigrator = {
|
||||
|
||||
/**
|
||||
* Migrates data from a previous SQLite version of the
|
||||
* database to the JSON version.
|
||||
*
|
||||
* @param structFunctions an object that contains functions
|
||||
* to create the various objects used
|
||||
* in the new JSON format
|
||||
* @param aCallback A callback to be called when migration
|
||||
* finishes, with the results in an array
|
||||
* @returns bool True if a migration will happen (DB was
|
||||
* found and succesfully opened)
|
||||
*/
|
||||
migrate: function(aCallback) {
|
||||
if (!this._openConnection()) {
|
||||
this._closeConnection();
|
||||
aCallback([]);
|
||||
return false;
|
||||
}
|
||||
|
||||
logger.debug("Importing addon repository from previous " + FILE_DATABASE + " storage.");
|
||||
|
||||
this._retrieveStoredData((results) => {
|
||||
this._closeConnection();
|
||||
let resultArray = Object.keys(results).map(k => results[k]);
|
||||
logger.debug(resultArray.length + " addons imported.")
|
||||
aCallback(resultArray);
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously opens a new connection to the database file.
|
||||
*
|
||||
* @return bool Whether the DB was opened successfully.
|
||||
*/
|
||||
_openConnection: function() {
|
||||
delete this.connection;
|
||||
|
||||
let dbfile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true);
|
||||
if (!dbfile.exists())
|
||||
return false;
|
||||
|
||||
try {
|
||||
this.connection = Services.storage.openUnsharedDatabase(dbfile);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE");
|
||||
|
||||
// Any errors in here should rollback
|
||||
try {
|
||||
this.connection.beginTransaction();
|
||||
|
||||
switch (this.connection.schemaVersion) {
|
||||
case 0:
|
||||
return false;
|
||||
|
||||
case 1:
|
||||
logger.debug("Upgrading database schema to version 2");
|
||||
this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN width INTEGER");
|
||||
this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN height INTEGER");
|
||||
this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailWidth INTEGER");
|
||||
this.connection.executeSimpleSQL("ALTER TABLE screenshot ADD COLUMN thumbnailHeight INTEGER");
|
||||
case 2:
|
||||
logger.debug("Upgrading database schema to version 3");
|
||||
this.connection.createTable("compatibility_override",
|
||||
"addon_internal_id INTEGER, " +
|
||||
"num INTEGER, " +
|
||||
"type TEXT, " +
|
||||
"minVersion TEXT, " +
|
||||
"maxVersion TEXT, " +
|
||||
"appID TEXT, " +
|
||||
"appMinVersion TEXT, " +
|
||||
"appMaxVersion TEXT, " +
|
||||
"PRIMARY KEY (addon_internal_id, num)");
|
||||
case 3:
|
||||
logger.debug("Upgrading database schema to version 4");
|
||||
this.connection.createTable("icon",
|
||||
"addon_internal_id INTEGER, " +
|
||||
"size INTEGER, " +
|
||||
"url TEXT, " +
|
||||
"PRIMARY KEY (addon_internal_id, size)");
|
||||
this._createIndices();
|
||||
this._createTriggers();
|
||||
this.connection.schemaVersion = LAST_DB_SCHEMA;
|
||||
case LAST_DB_SCHEMA:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
this.connection.commitTransaction();
|
||||
} catch (e) {
|
||||
logger.error("Failed to open " + FILE_DATABASE + ". Data import will not happen.", e);
|
||||
this.logSQLError(this.connection.lastError, this.connection.lastErrorString);
|
||||
this.connection.rollbackTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_closeConnection: function() {
|
||||
for (let key in this.asyncStatementsCache) {
|
||||
let stmt = this.asyncStatementsCache[key];
|
||||
stmt.finalize();
|
||||
}
|
||||
this.asyncStatementsCache = {};
|
||||
|
||||
if (this.connection)
|
||||
this.connection.asyncClose();
|
||||
|
||||
delete this.connection;
|
||||
},
|
||||
|
||||
/**
|
||||
* Asynchronously retrieve all add-ons from the database, and pass it
|
||||
* to the specified callback
|
||||
*
|
||||
* @param aCallback
|
||||
* The callback to pass the add-ons back to
|
||||
*/
|
||||
_retrieveStoredData: function(aCallback) {
|
||||
let addons = {};
|
||||
|
||||
// Retrieve all data from the addon table
|
||||
let getAllAddons = () => {
|
||||
this.getAsyncStatement("getAllAddons").executeAsync({
|
||||
handleResult: aResults => {
|
||||
let row = null;
|
||||
while ((row = aResults.getNextRow())) {
|
||||
let internal_id = row.getResultByName("internal_id");
|
||||
addons[internal_id] = this._makeAddonFromAsyncRow(row);
|
||||
}
|
||||
},
|
||||
|
||||
handleError: this.asyncErrorLogger,
|
||||
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
logger.error("Error retrieving add-ons from database. Returning empty results");
|
||||
aCallback({});
|
||||
return;
|
||||
}
|
||||
|
||||
getAllDevelopers();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all data from the developer table
|
||||
let getAllDevelopers = () => {
|
||||
this.getAsyncStatement("getAllDevelopers").executeAsync({
|
||||
handleResult: aResults => {
|
||||
let row = null;
|
||||
while ((row = aResults.getNextRow())) {
|
||||
let addon_internal_id = row.getResultByName("addon_internal_id");
|
||||
if (!(addon_internal_id in addons)) {
|
||||
logger.warn("Found a developer not linked to an add-on in database");
|
||||
continue;
|
||||
}
|
||||
|
||||
let addon = addons[addon_internal_id];
|
||||
if (!addon.developers)
|
||||
addon.developers = [];
|
||||
|
||||
addon.developers.push(this._makeDeveloperFromAsyncRow(row));
|
||||
}
|
||||
},
|
||||
|
||||
handleError: this.asyncErrorLogger,
|
||||
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
logger.error("Error retrieving developers from database. Returning empty results");
|
||||
aCallback({});
|
||||
return;
|
||||
}
|
||||
|
||||
getAllScreenshots();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Retrieve all data from the screenshot table
|
||||
let getAllScreenshots = () => {
|
||||
this.getAsyncStatement("getAllScreenshots").executeAsync({
|
||||
handleResult: aResults => {
|
||||
let row = null;
|
||||
while ((row = aResults.getNextRow())) {
|
||||
let addon_internal_id = row.getResultByName("addon_internal_id");
|
||||
if (!(addon_internal_id in addons)) {
|
||||
logger.warn("Found a screenshot not linked to an add-on in database");
|
||||
continue;
|
||||
}
|
||||
|
||||
let addon = addons[addon_internal_id];
|
||||
if (!addon.screenshots)
|
||||
addon.screenshots = [];
|
||||
addon.screenshots.push(this._makeScreenshotFromAsyncRow(row));
|
||||
}
|
||||
},
|
||||
|
||||
handleError: this.asyncErrorLogger,
|
||||
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
logger.error("Error retrieving screenshots from database. Returning empty results");
|
||||
aCallback({});
|
||||
return;
|
||||
}
|
||||
|
||||
getAllCompatOverrides();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let getAllCompatOverrides = () => {
|
||||
this.getAsyncStatement("getAllCompatOverrides").executeAsync({
|
||||
handleResult: aResults => {
|
||||
let row = null;
|
||||
while ((row = aResults.getNextRow())) {
|
||||
let addon_internal_id = row.getResultByName("addon_internal_id");
|
||||
if (!(addon_internal_id in addons)) {
|
||||
logger.warn("Found a compatibility override not linked to an add-on in database");
|
||||
continue;
|
||||
}
|
||||
|
||||
let addon = addons[addon_internal_id];
|
||||
if (!addon.compatibilityOverrides)
|
||||
addon.compatibilityOverrides = [];
|
||||
addon.compatibilityOverrides.push(this._makeCompatOverrideFromAsyncRow(row));
|
||||
}
|
||||
},
|
||||
|
||||
handleError: this.asyncErrorLogger,
|
||||
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
logger.error("Error retrieving compatibility overrides from database. Returning empty results");
|
||||
aCallback({});
|
||||
return;
|
||||
}
|
||||
|
||||
getAllIcons();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let getAllIcons = () => {
|
||||
this.getAsyncStatement("getAllIcons").executeAsync({
|
||||
handleResult: aResults => {
|
||||
let row = null;
|
||||
while ((row = aResults.getNextRow())) {
|
||||
let addon_internal_id = row.getResultByName("addon_internal_id");
|
||||
if (!(addon_internal_id in addons)) {
|
||||
logger.warn("Found an icon not linked to an add-on in database");
|
||||
continue;
|
||||
}
|
||||
|
||||
let addon = addons[addon_internal_id];
|
||||
let { size, url } = this._makeIconFromAsyncRow(row);
|
||||
addon.icons[size] = url;
|
||||
if (size == 32)
|
||||
addon.iconURL = url;
|
||||
}
|
||||
},
|
||||
|
||||
handleError: this.asyncErrorLogger,
|
||||
|
||||
handleCompletion: function(aReason) {
|
||||
if (aReason != Ci.mozIStorageStatementCallback.REASON_FINISHED) {
|
||||
logger.error("Error retrieving icons from database. Returning empty results");
|
||||
aCallback({});
|
||||
return;
|
||||
}
|
||||
|
||||
let returnedAddons = {};
|
||||
for (let id in addons) {
|
||||
let addon = addons[id];
|
||||
returnedAddons[addon.id] = addon;
|
||||
}
|
||||
aCallback(returnedAddons);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Begin asynchronous process
|
||||
getAllAddons();
|
||||
},
|
||||
|
||||
// A cache of statements that are used and need to be finalized on shutdown
|
||||
asyncStatementsCache: {},
|
||||
|
||||
/**
|
||||
* Gets a cached async statement or creates a new statement if it doesn't
|
||||
* already exist.
|
||||
*
|
||||
* @param aKey
|
||||
* A unique key to reference the statement
|
||||
* @return a mozIStorageAsyncStatement for the SQL corresponding to the
|
||||
* unique key
|
||||
*/
|
||||
getAsyncStatement: function(aKey) {
|
||||
if (aKey in this.asyncStatementsCache)
|
||||
return this.asyncStatementsCache[aKey];
|
||||
|
||||
let sql = this.queries[aKey];
|
||||
try {
|
||||
return this.asyncStatementsCache[aKey] = this.connection.createAsyncStatement(sql);
|
||||
} catch (e) {
|
||||
logger.error("Error creating statement " + aKey + " (" + sql + ")");
|
||||
throw Components.Exception("Error creating statement " + aKey + " (" + sql + "): " + e,
|
||||
e.result);
|
||||
}
|
||||
},
|
||||
|
||||
// The queries used by the database
|
||||
queries: {
|
||||
getAllAddons: "SELECT internal_id, id, type, name, version, " +
|
||||
"creator, creatorURL, description, fullDescription, " +
|
||||
"developerComments, eula, homepageURL, supportURL, " +
|
||||
"contributionURL, contributionAmount, averageRating, " +
|
||||
"reviewCount, reviewURL, totalDownloads, weeklyDownloads, " +
|
||||
"dailyUsers, sourceURI, repositoryStatus, size, updateDate " +
|
||||
"FROM addon",
|
||||
|
||||
getAllDevelopers: "SELECT addon_internal_id, name, url FROM developer " +
|
||||
"ORDER BY addon_internal_id, num",
|
||||
|
||||
getAllScreenshots: "SELECT addon_internal_id, url, width, height, " +
|
||||
"thumbnailURL, thumbnailWidth, thumbnailHeight, caption " +
|
||||
"FROM screenshot ORDER BY addon_internal_id, num",
|
||||
|
||||
getAllCompatOverrides: "SELECT addon_internal_id, type, minVersion, " +
|
||||
"maxVersion, appID, appMinVersion, appMaxVersion " +
|
||||
"FROM compatibility_override " +
|
||||
"ORDER BY addon_internal_id, num",
|
||||
|
||||
getAllIcons: "SELECT addon_internal_id, size, url FROM icon " +
|
||||
"ORDER BY addon_internal_id, size",
|
||||
},
|
||||
|
||||
/**
|
||||
* Make add-on structure from an asynchronous row.
|
||||
*
|
||||
* @param aRow
|
||||
* The asynchronous row to use
|
||||
* @return The created add-on
|
||||
*/
|
||||
_makeAddonFromAsyncRow: function(aRow) {
|
||||
// This is intentionally not an AddonSearchResult object in order
|
||||
// to allow AddonDatabase._parseAddon to parse it, same as if it
|
||||
// was read from the JSON database.
|
||||
|
||||
let addon = { icons: {} };
|
||||
|
||||
for (let prop of PROP_SINGLE) {
|
||||
addon[prop] = aRow.getResultByName(prop)
|
||||
}
|
||||
|
||||
return addon;
|
||||
},
|
||||
|
||||
/**
|
||||
* Make a developer from an asynchronous row
|
||||
*
|
||||
* @param aRow
|
||||
* The asynchronous row to use
|
||||
* @return The created developer
|
||||
*/
|
||||
_makeDeveloperFromAsyncRow: function(aRow) {
|
||||
let name = aRow.getResultByName("name");
|
||||
let url = aRow.getResultByName("url")
|
||||
return new AddonManagerPrivate.AddonAuthor(name, url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Make a screenshot from an asynchronous row
|
||||
*
|
||||
* @param aRow
|
||||
* The asynchronous row to use
|
||||
* @return The created screenshot
|
||||
*/
|
||||
_makeScreenshotFromAsyncRow: function(aRow) {
|
||||
let url = aRow.getResultByName("url");
|
||||
let width = aRow.getResultByName("width");
|
||||
let height = aRow.getResultByName("height");
|
||||
let thumbnailURL = aRow.getResultByName("thumbnailURL");
|
||||
let thumbnailWidth = aRow.getResultByName("thumbnailWidth");
|
||||
let thumbnailHeight = aRow.getResultByName("thumbnailHeight");
|
||||
let caption = aRow.getResultByName("caption");
|
||||
return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL,
|
||||
thumbnailWidth, thumbnailHeight, caption);
|
||||
},
|
||||
|
||||
/**
|
||||
* Make a CompatibilityOverride from an asynchronous row
|
||||
*
|
||||
* @param aRow
|
||||
* The asynchronous row to use
|
||||
* @return The created CompatibilityOverride
|
||||
*/
|
||||
_makeCompatOverrideFromAsyncRow: function(aRow) {
|
||||
let type = aRow.getResultByName("type");
|
||||
let minVersion = aRow.getResultByName("minVersion");
|
||||
let maxVersion = aRow.getResultByName("maxVersion");
|
||||
let appID = aRow.getResultByName("appID");
|
||||
let appMinVersion = aRow.getResultByName("appMinVersion");
|
||||
let appMaxVersion = aRow.getResultByName("appMaxVersion");
|
||||
return new AddonManagerPrivate.AddonCompatibilityOverride(type,
|
||||
minVersion,
|
||||
maxVersion,
|
||||
appID,
|
||||
appMinVersion,
|
||||
appMaxVersion);
|
||||
},
|
||||
|
||||
/**
|
||||
* Make an icon from an asynchronous row
|
||||
*
|
||||
* @param aRow
|
||||
* The asynchronous row to use
|
||||
* @return An object containing the size and URL of the icon
|
||||
*/
|
||||
_makeIconFromAsyncRow: function(aRow) {
|
||||
let size = aRow.getResultByName("size");
|
||||
let url = aRow.getResultByName("url");
|
||||
return { size: size, url: url };
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper function to log an SQL error.
|
||||
*
|
||||
* @param aError
|
||||
* The storage error code associated with the error
|
||||
* @param aErrorString
|
||||
* An error message
|
||||
*/
|
||||
logSQLError: function(aError, aErrorString) {
|
||||
logger.error("SQL error " + aError + ": " + aErrorString);
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper function to log any errors that occur during async statements.
|
||||
*
|
||||
* @param aError
|
||||
* A mozIStorageError to log
|
||||
*/
|
||||
asyncErrorLogger: function(aError) {
|
||||
logger.error("Async SQL error " + aError.result + ": " + aError.message);
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously creates the triggers in the database.
|
||||
*/
|
||||
_createTriggers: function() {
|
||||
this.connection.executeSimpleSQL("DROP TRIGGER IF EXISTS delete_addon");
|
||||
this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
|
||||
"ON addon BEGIN " +
|
||||
"DELETE FROM developer WHERE addon_internal_id=old.internal_id; " +
|
||||
"DELETE FROM screenshot WHERE addon_internal_id=old.internal_id; " +
|
||||
"DELETE FROM compatibility_override WHERE addon_internal_id=old.internal_id; " +
|
||||
"DELETE FROM icon WHERE addon_internal_id=old.internal_id; " +
|
||||
"END");
|
||||
},
|
||||
|
||||
/**
|
||||
* Synchronously creates the indices in the database.
|
||||
*/
|
||||
_createIndices: function() {
|
||||
this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS developer_idx " +
|
||||
"ON developer (addon_internal_id)");
|
||||
this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS screenshot_idx " +
|
||||
"ON screenshot (addon_internal_id)");
|
||||
this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS compatibility_override_idx " +
|
||||
"ON compatibility_override (addon_internal_id)");
|
||||
this.connection.executeSimpleSQL("CREATE INDEX IF NOT EXISTS icon_idx " +
|
||||
"ON icon (addon_internal_id)");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,934 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* The AddonUpdateChecker is responsible for retrieving the update information
|
||||
* from an add-on's remote update manifest.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ];
|
||||
|
||||
const TIMEOUT = 60 * 1000;
|
||||
const PREFIX_NS_RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
|
||||
const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
|
||||
const PREFIX_ITEM = "urn:mozilla:item:";
|
||||
const PREFIX_EXTENSION = "urn:mozilla:extension:";
|
||||
const PREFIX_THEME = "urn:mozilla:theme:";
|
||||
const TOOLKIT_ID = "toolkit@mozilla.org"
|
||||
const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
|
||||
|
||||
const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
|
||||
"resource://gre/modules/addons/AddonRepository.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest",
|
||||
"resource://gre/modules/ServiceRequest.jsm");
|
||||
|
||||
|
||||
// Shared code for suppressing bad cert dialogs.
|
||||
XPCOMUtils.defineLazyGetter(this, "CertUtils", function() {
|
||||
let certUtils = {};
|
||||
Components.utils.import("resource://gre/modules/CertUtils.jsm", certUtils);
|
||||
return certUtils;
|
||||
});
|
||||
|
||||
var gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
|
||||
getService(Ci.nsIRDFService);
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
const LOGGER_ID = "addons.update-checker";
|
||||
|
||||
// Create a new logger for use by the Addons Update Checker
|
||||
// (Requires AddonManager.jsm)
|
||||
var logger = Log.repository.getLogger(LOGGER_ID);
|
||||
|
||||
/**
|
||||
* A serialisation method for RDF data that produces an identical string
|
||||
* for matching RDF graphs.
|
||||
* The serialisation is not complete, only assertions stemming from a given
|
||||
* resource are included, multiple references to the same resource are not
|
||||
* permitted, and the RDF prolog and epilog are not included.
|
||||
* RDF Blob and Date literals are not supported.
|
||||
*/
|
||||
function RDFSerializer() {
|
||||
this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
|
||||
getService(Ci.nsIRDFContainerUtils);
|
||||
this.resources = [];
|
||||
}
|
||||
|
||||
RDFSerializer.prototype = {
|
||||
INDENT: " ", // The indent used for pretty-printing
|
||||
resources: null, // Array of the resources that have been found
|
||||
|
||||
/**
|
||||
* Escapes characters from a string that should not appear in XML.
|
||||
*
|
||||
* @param aString
|
||||
* The string to be escaped
|
||||
* @return a string with all characters invalid in XML character data
|
||||
* converted to entity references.
|
||||
*/
|
||||
escapeEntities: function(aString) {
|
||||
aString = aString.replace(/&/g, "&");
|
||||
aString = aString.replace(/</g, "<");
|
||||
aString = aString.replace(/>/g, ">");
|
||||
return aString.replace(/"/g, """);
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes all the elements of an RDF container.
|
||||
*
|
||||
* @param aDs
|
||||
* The RDF datasource
|
||||
* @param aContainer
|
||||
* The RDF container to output the child elements of
|
||||
* @param aIndent
|
||||
* The current level of indent for pretty-printing
|
||||
* @return a string containing the serialized elements.
|
||||
*/
|
||||
serializeContainerItems: function(aDs, aContainer, aIndent) {
|
||||
var result = "";
|
||||
var items = aContainer.GetElements();
|
||||
while (items.hasMoreElements()) {
|
||||
var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
|
||||
result += aIndent + "<RDF:li>\n"
|
||||
result += this.serializeResource(aDs, item, aIndent + this.INDENT);
|
||||
result += aIndent + "</RDF:li>\n"
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Serializes all em:* (see EM_NS) properties of an RDF resource except for
|
||||
* the em:signature property. As this serialization is to be compared against
|
||||
* the manifest signature it cannot contain the em:signature property itself.
|
||||
*
|
||||
* @param aDs
|
||||
* The RDF datasource
|
||||
* @param aResource
|
||||
* The RDF resource that contains the properties to serialize
|
||||
* @param aIndent
|
||||
* The current level of indent for pretty-printing
|
||||
* @return a string containing the serialized properties.
|
||||
* @throws if the resource contains a property that cannot be serialized
|
||||
*/
|
||||
serializeResourceProperties: function(aDs, aResource, aIndent) {
|
||||
var result = "";
|
||||
var items = [];
|
||||
var arcs = aDs.ArcLabelsOut(aResource);
|
||||
while (arcs.hasMoreElements()) {
|
||||
var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
|
||||
if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM)
|
||||
continue;
|
||||
var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length);
|
||||
if (prop == "signature")
|
||||
continue;
|
||||
|
||||
var targets = aDs.GetTargets(aResource, arc, true);
|
||||
while (targets.hasMoreElements()) {
|
||||
var target = targets.getNext();
|
||||
if (target instanceof Ci.nsIRDFResource) {
|
||||
var item = aIndent + "<em:" + prop + ">\n";
|
||||
item += this.serializeResource(aDs, target, aIndent + this.INDENT);
|
||||
item += aIndent + "</em:" + prop + ">\n";
|
||||
items.push(item);
|
||||
}
|
||||
else if (target instanceof Ci.nsIRDFLiteral) {
|
||||
items.push(aIndent + "<em:" + prop + ">" +
|
||||
this.escapeEntities(target.Value) + "</em:" + prop + ">\n");
|
||||
}
|
||||
else if (target instanceof Ci.nsIRDFInt) {
|
||||
items.push(aIndent + "<em:" + prop + " NC:parseType=\"Integer\">" +
|
||||
target.Value + "</em:" + prop + ">\n");
|
||||
}
|
||||
else {
|
||||
throw Components.Exception("Cannot serialize unknown literal type");
|
||||
}
|
||||
}
|
||||
}
|
||||
items.sort();
|
||||
result += items.join("");
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively serializes an RDF resource and all resources it links to.
|
||||
* This will only output EM_NS properties and will ignore any em:signature
|
||||
* property.
|
||||
*
|
||||
* @param aDs
|
||||
* The RDF datasource
|
||||
* @param aResource
|
||||
* The RDF resource to serialize
|
||||
* @param aIndent
|
||||
* The current level of indent for pretty-printing. If undefined no
|
||||
* indent will be added
|
||||
* @return a string containing the serialized resource.
|
||||
* @throws if the RDF data contains multiple references to the same resource.
|
||||
*/
|
||||
serializeResource: function(aDs, aResource, aIndent) {
|
||||
if (this.resources.indexOf(aResource) != -1 ) {
|
||||
// We cannot output multiple references to the same resource.
|
||||
throw Components.Exception("Cannot serialize multiple references to " + aResource.Value);
|
||||
}
|
||||
if (aIndent === undefined)
|
||||
aIndent = "";
|
||||
|
||||
this.resources.push(aResource);
|
||||
var container = null;
|
||||
var type = "Description";
|
||||
if (this.cUtils.IsSeq(aDs, aResource)) {
|
||||
type = "Seq";
|
||||
container = this.cUtils.MakeSeq(aDs, aResource);
|
||||
}
|
||||
else if (this.cUtils.IsAlt(aDs, aResource)) {
|
||||
type = "Alt";
|
||||
container = this.cUtils.MakeAlt(aDs, aResource);
|
||||
}
|
||||
else if (this.cUtils.IsBag(aDs, aResource)) {
|
||||
type = "Bag";
|
||||
container = this.cUtils.MakeBag(aDs, aResource);
|
||||
}
|
||||
|
||||
var result = aIndent + "<RDF:" + type;
|
||||
if (!gRDF.IsAnonymousResource(aResource))
|
||||
result += " about=\"" + this.escapeEntities(aResource.ValueUTF8) + "\"";
|
||||
result += ">\n";
|
||||
|
||||
if (container)
|
||||
result += this.serializeContainerItems(aDs, container, aIndent + this.INDENT);
|
||||
|
||||
result += this.serializeResourceProperties(aDs, aResource, aIndent + this.INDENT);
|
||||
|
||||
result += aIndent + "</RDF:" + type + ">\n";
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the update URL in an update item, as returned by
|
||||
* parseRDFManifest and parseJSONManifest. Ensures that:
|
||||
*
|
||||
* - The URL is secure, or secured by a strong enough hash.
|
||||
* - The security principal of the update manifest has permission to
|
||||
* load the URL.
|
||||
*
|
||||
* @param aUpdate
|
||||
* The update item to sanitize.
|
||||
* @param aRequest
|
||||
* The XMLHttpRequest used to load the manifest.
|
||||
* @param aHashPattern
|
||||
* The regular expression used to validate the update hash.
|
||||
* @param aHashString
|
||||
* The human-readable string specifying which hash functions
|
||||
* are accepted.
|
||||
*/
|
||||
function sanitizeUpdateURL(aUpdate, aRequest, aHashPattern, aHashString) {
|
||||
if (aUpdate.updateURL) {
|
||||
let scriptSecurity = Services.scriptSecurityManager;
|
||||
let principal = scriptSecurity.getChannelURIPrincipal(aRequest.channel);
|
||||
try {
|
||||
// This logs an error on failure, so no need to log it a second time
|
||||
scriptSecurity.checkLoadURIStrWithPrincipal(principal, aUpdate.updateURL,
|
||||
scriptSecurity.DISALLOW_SCRIPT);
|
||||
} catch (e) {
|
||||
delete aUpdate.updateURL;
|
||||
return;
|
||||
}
|
||||
|
||||
if (AddonManager.checkUpdateSecurity &&
|
||||
!aUpdate.updateURL.startsWith("https:") &&
|
||||
!aHashPattern.test(aUpdate.updateHash)) {
|
||||
logger.warn(`Update link ${aUpdate.updateURL} is not secure and is not verified ` +
|
||||
`by a strong enough hash (needs to be ${aHashString}).`);
|
||||
delete aUpdate.updateURL;
|
||||
delete aUpdate.updateHash;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an RDF style update manifest into an array of update objects.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on being checked for updates
|
||||
* @param aUpdateKey
|
||||
* An optional update key for the add-on
|
||||
* @param aRequest
|
||||
* The XMLHttpRequest that has retrieved the update manifest
|
||||
* @param aManifestData
|
||||
* The pre-parsed manifest, as a bare XML DOM document
|
||||
* @return an array of update objects
|
||||
* @throws if the update manifest is invalid in any way
|
||||
*/
|
||||
function parseRDFManifest(aId, aUpdateKey, aRequest, aManifestData) {
|
||||
if (aManifestData.documentElement.namespaceURI != PREFIX_NS_RDF) {
|
||||
throw Components.Exception("Update manifest had an unrecognised namespace: " +
|
||||
aManifestData.documentElement.namespaceURI);
|
||||
}
|
||||
|
||||
function EM_R(aProp) {
|
||||
return gRDF.GetResource(PREFIX_NS_EM + aProp);
|
||||
}
|
||||
|
||||
function getValue(aLiteral) {
|
||||
if (aLiteral instanceof Ci.nsIRDFLiteral)
|
||||
return aLiteral.Value;
|
||||
if (aLiteral instanceof Ci.nsIRDFResource)
|
||||
return aLiteral.Value;
|
||||
if (aLiteral instanceof Ci.nsIRDFInt)
|
||||
return aLiteral.Value;
|
||||
return null;
|
||||
}
|
||||
|
||||
function getProperty(aDs, aSource, aProperty) {
|
||||
return getValue(aDs.GetTarget(aSource, EM_R(aProperty), true));
|
||||
}
|
||||
|
||||
function getBooleanProperty(aDs, aSource, aProperty) {
|
||||
let propValue = aDs.GetTarget(aSource, EM_R(aProperty), true);
|
||||
if (!propValue)
|
||||
return undefined;
|
||||
return getValue(propValue) == "true";
|
||||
}
|
||||
|
||||
function getRequiredProperty(aDs, aSource, aProperty) {
|
||||
let value = getProperty(aDs, aSource, aProperty);
|
||||
if (!value)
|
||||
throw Components.Exception("Update manifest is missing a required " + aProperty + " property.");
|
||||
return value;
|
||||
}
|
||||
|
||||
let rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
|
||||
createInstance(Ci.nsIRDFXMLParser);
|
||||
let ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
|
||||
createInstance(Ci.nsIRDFDataSource);
|
||||
rdfParser.parseString(ds, aRequest.channel.URI, aRequest.responseText);
|
||||
|
||||
// Differentiating between add-on types is deprecated
|
||||
let extensionRes = gRDF.GetResource(PREFIX_EXTENSION + aId);
|
||||
let themeRes = gRDF.GetResource(PREFIX_THEME + aId);
|
||||
let itemRes = gRDF.GetResource(PREFIX_ITEM + aId);
|
||||
let addonRes;
|
||||
if (ds.ArcLabelsOut(extensionRes).hasMoreElements())
|
||||
addonRes = extensionRes;
|
||||
else if (ds.ArcLabelsOut(themeRes).hasMoreElements())
|
||||
addonRes = themeRes;
|
||||
else
|
||||
addonRes = itemRes;
|
||||
|
||||
// If we have an update key then the update manifest must be signed
|
||||
if (aUpdateKey) {
|
||||
let signature = getProperty(ds, addonRes, "signature");
|
||||
if (!signature)
|
||||
throw Components.Exception("Update manifest for " + aId + " does not contain a required signature");
|
||||
let serializer = new RDFSerializer();
|
||||
let updateString = null;
|
||||
|
||||
try {
|
||||
updateString = serializer.serializeResource(ds, addonRes);
|
||||
}
|
||||
catch (e) {
|
||||
throw Components.Exception("Failed to generate signed string for " + aId + ". Serializer threw " + e,
|
||||
e.result);
|
||||
}
|
||||
|
||||
let result = false;
|
||||
|
||||
try {
|
||||
let verifier = Cc["@mozilla.org/security/datasignatureverifier;1"].
|
||||
getService(Ci.nsIDataSignatureVerifier);
|
||||
result = verifier.verifyData(updateString, signature, aUpdateKey);
|
||||
}
|
||||
catch (e) {
|
||||
throw Components.Exception("The signature or updateKey for " + aId + " is malformed." +
|
||||
"Verifier threw " + e, e.result);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
throw Components.Exception("The signature for " + aId + " was not created by the add-on's updateKey");
|
||||
}
|
||||
|
||||
let updates = ds.GetTarget(addonRes, EM_R("updates"), true);
|
||||
|
||||
// A missing updates property doesn't count as a failure, just as no avialable
|
||||
// update information
|
||||
if (!updates) {
|
||||
logger.warn("Update manifest for " + aId + " did not contain an updates property");
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!(updates instanceof Ci.nsIRDFResource))
|
||||
throw Components.Exception("Missing updates property for " + addonRes.Value);
|
||||
|
||||
let cu = Cc["@mozilla.org/rdf/container-utils;1"].
|
||||
getService(Ci.nsIRDFContainerUtils);
|
||||
if (!cu.IsContainer(ds, updates))
|
||||
throw Components.Exception("Updates property was not an RDF container");
|
||||
|
||||
let results = [];
|
||||
let ctr = Cc["@mozilla.org/rdf/container;1"].
|
||||
createInstance(Ci.nsIRDFContainer);
|
||||
ctr.Init(ds, updates);
|
||||
let items = ctr.GetElements();
|
||||
while (items.hasMoreElements()) {
|
||||
let item = items.getNext().QueryInterface(Ci.nsIRDFResource);
|
||||
let version = getProperty(ds, item, "version");
|
||||
if (!version) {
|
||||
logger.warn("Update manifest is missing a required version property.");
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug("Found an update entry for " + aId + " version " + version);
|
||||
|
||||
let targetApps = ds.GetTargets(item, EM_R("targetApplication"), true);
|
||||
while (targetApps.hasMoreElements()) {
|
||||
let targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
|
||||
|
||||
let appEntry = {};
|
||||
try {
|
||||
appEntry.id = getRequiredProperty(ds, targetApp, "id");
|
||||
appEntry.minVersion = getRequiredProperty(ds, targetApp, "minVersion");
|
||||
appEntry.maxVersion = getRequiredProperty(ds, targetApp, "maxVersion");
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn(e);
|
||||
continue;
|
||||
}
|
||||
|
||||
let result = {
|
||||
id: aId,
|
||||
version: version,
|
||||
multiprocessCompatible: getBooleanProperty(ds, item, "multiprocessCompatible"),
|
||||
updateURL: getProperty(ds, targetApp, "updateLink"),
|
||||
updateHash: getProperty(ds, targetApp, "updateHash"),
|
||||
updateInfoURL: getProperty(ds, targetApp, "updateInfoURL"),
|
||||
strictCompatibility: !!getBooleanProperty(ds, targetApp, "strictCompatibility"),
|
||||
targetApplications: [appEntry]
|
||||
};
|
||||
|
||||
// The JSON update protocol requires an SHA-2 hash. RDF still
|
||||
// supports SHA-1, for compatibility reasons.
|
||||
sanitizeUpdateURL(result, aRequest, /^sha/, "sha1 or stronger");
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an JSON update manifest into an array of update objects.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on being checked for updates
|
||||
* @param aUpdateKey
|
||||
* An optional update key for the add-on
|
||||
* @param aRequest
|
||||
* The XMLHttpRequest that has retrieved the update manifest
|
||||
* @param aManifestData
|
||||
* The pre-parsed manifest, as a JSON object tree
|
||||
* @return an array of update objects
|
||||
* @throws if the update manifest is invalid in any way
|
||||
*/
|
||||
function parseJSONManifest(aId, aUpdateKey, aRequest, aManifestData) {
|
||||
if (aUpdateKey)
|
||||
throw Components.Exception("Update keys are not supported for JSON update manifests");
|
||||
|
||||
let TYPE_CHECK = {
|
||||
"array": val => Array.isArray(val),
|
||||
"object": val => val && typeof val == "object" && !Array.isArray(val),
|
||||
};
|
||||
|
||||
function getProperty(aObj, aProperty, aType, aDefault = undefined) {
|
||||
if (!(aProperty in aObj))
|
||||
return aDefault;
|
||||
|
||||
let value = aObj[aProperty];
|
||||
|
||||
let matchesType = aType in TYPE_CHECK ? TYPE_CHECK[aType](value) : typeof value == aType;
|
||||
if (!matchesType)
|
||||
throw Components.Exception(`Update manifest property '${aProperty}' has incorrect type (expected ${aType})`);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
function getRequiredProperty(aObj, aProperty, aType) {
|
||||
let value = getProperty(aObj, aProperty, aType);
|
||||
if (value === undefined)
|
||||
throw Components.Exception(`Update manifest is missing a required ${aProperty} property.`);
|
||||
return value;
|
||||
}
|
||||
|
||||
let manifest = aManifestData;
|
||||
|
||||
if (!TYPE_CHECK["object"](manifest))
|
||||
throw Components.Exception("Root element of update manifest must be a JSON object literal");
|
||||
|
||||
// The set of add-ons this manifest has updates for
|
||||
let addons = getRequiredProperty(manifest, "addons", "object");
|
||||
|
||||
// The entry for this particular add-on
|
||||
let addon = getProperty(addons, aId, "object");
|
||||
|
||||
// A missing entry doesn't count as a failure, just as no avialable update
|
||||
// information
|
||||
if (!addon) {
|
||||
logger.warn("Update manifest did not contain an entry for " + aId);
|
||||
return [];
|
||||
}
|
||||
|
||||
// The list of available updates
|
||||
let updates = getProperty(addon, "updates", "array", []);
|
||||
|
||||
let results = [];
|
||||
|
||||
for (let update of updates) {
|
||||
let version = getRequiredProperty(update, "version", "string");
|
||||
|
||||
logger.debug(`Found an update entry for ${aId} version ${version}`);
|
||||
|
||||
let applications = getProperty(update, "applications", "object",
|
||||
{ gecko: {} });
|
||||
|
||||
// "gecko" is currently the only supported application entry. If
|
||||
// it's missing, skip this update.
|
||||
if (!("gecko" in applications)) {
|
||||
logger.debug("gecko not in application entry, skipping update of ${addon}")
|
||||
continue;
|
||||
}
|
||||
|
||||
let app = getProperty(applications, "gecko", "object");
|
||||
|
||||
let appEntry = {
|
||||
id: TOOLKIT_ID,
|
||||
minVersion: getProperty(app, "strict_min_version", "string",
|
||||
AddonManagerPrivate.webExtensionsMinPlatformVersion),
|
||||
maxVersion: "*",
|
||||
};
|
||||
|
||||
let result = {
|
||||
id: aId,
|
||||
version: version,
|
||||
multiprocessCompatible: getProperty(update, "multiprocess_compatible", "boolean", true),
|
||||
updateURL: getProperty(update, "update_link", "string"),
|
||||
updateHash: getProperty(update, "update_hash", "string"),
|
||||
updateInfoURL: getProperty(update, "update_info_url", "string"),
|
||||
strictCompatibility: false,
|
||||
targetApplications: [appEntry],
|
||||
};
|
||||
|
||||
if ("strict_max_version" in app) {
|
||||
if ("advisory_max_version" in app) {
|
||||
logger.warn("Ignoring 'advisory_max_version' update manifest property for " +
|
||||
aId + " property since 'strict_max_version' also present");
|
||||
}
|
||||
|
||||
appEntry.maxVersion = getProperty(app, "strict_max_version", "string");
|
||||
result.strictCompatibility = appEntry.maxVersion != "*";
|
||||
} else if ("advisory_max_version" in app) {
|
||||
appEntry.maxVersion = getProperty(app, "advisory_max_version", "string");
|
||||
}
|
||||
|
||||
// The JSON update protocol requires an SHA-2 hash. RDF still
|
||||
// supports SHA-1, for compatibility reasons.
|
||||
sanitizeUpdateURL(result, aRequest, /^sha(256|512):/, "sha256 or sha512");
|
||||
|
||||
results.push(result);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts downloading an update manifest and then passes it to an appropriate
|
||||
* parser to convert to an array of update objects
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on being checked for updates
|
||||
* @param aUpdateKey
|
||||
* An optional update key for the add-on
|
||||
* @param aUrl
|
||||
* The URL of the update manifest
|
||||
* @param aObserver
|
||||
* An observer to pass results to
|
||||
*/
|
||||
function UpdateParser(aId, aUpdateKey, aUrl, aObserver) {
|
||||
this.id = aId;
|
||||
this.updateKey = aUpdateKey;
|
||||
this.observer = aObserver;
|
||||
this.url = aUrl;
|
||||
|
||||
let requireBuiltIn = true;
|
||||
try {
|
||||
requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
logger.debug("Requesting " + aUrl);
|
||||
try {
|
||||
this.request = new ServiceRequest();
|
||||
this.request.open("GET", this.url, true);
|
||||
this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
|
||||
this.request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to cache.
|
||||
this.request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
this.request.overrideMimeType("text/plain");
|
||||
this.request.setRequestHeader("Moz-XPI-Update", "1", true);
|
||||
this.request.timeout = TIMEOUT;
|
||||
this.request.addEventListener("load", () => this.onLoad(), false);
|
||||
this.request.addEventListener("error", () => this.onError(), false);
|
||||
this.request.addEventListener("timeout", () => this.onTimeout(), false);
|
||||
this.request.send(null);
|
||||
}
|
||||
catch (e) {
|
||||
logger.error("Failed to request update manifest", e);
|
||||
}
|
||||
}
|
||||
|
||||
UpdateParser.prototype = {
|
||||
id: null,
|
||||
updateKey: null,
|
||||
observer: null,
|
||||
request: null,
|
||||
url: null,
|
||||
|
||||
/**
|
||||
* Called when the manifest has been successfully loaded.
|
||||
*/
|
||||
onLoad: function() {
|
||||
let request = this.request;
|
||||
this.request = null;
|
||||
this._doneAt = new Error("place holder");
|
||||
|
||||
let requireBuiltIn = true;
|
||||
try {
|
||||
requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS);
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
try {
|
||||
CertUtils.checkCert(request.channel, !requireBuiltIn);
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("Request failed: " + this.url + " - " + e);
|
||||
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Components.isSuccessCode(request.status)) {
|
||||
logger.warn("Request failed: " + this.url + " - " + request.status);
|
||||
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
let channel = request.channel;
|
||||
if (channel instanceof Ci.nsIHttpChannel && !channel.requestSucceeded) {
|
||||
logger.warn("Request failed: " + this.url + " - " + channel.responseStatus +
|
||||
": " + channel.responseStatusText);
|
||||
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Detect the manifest type by first attempting to parse it as
|
||||
// JSON, and falling back to parsing it as XML if that fails.
|
||||
let parser;
|
||||
try {
|
||||
try {
|
||||
let json = JSON.parse(request.responseText);
|
||||
|
||||
parser = () => parseJSONManifest(this.id, this.updateKey, request, json);
|
||||
} catch (e) {
|
||||
if (!(e instanceof SyntaxError))
|
||||
throw e;
|
||||
let domParser = Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
let xml = domParser.parseFromString(request.responseText, "text/xml");
|
||||
|
||||
if (xml.documentElement.namespaceURI == XMLURI_PARSE_ERROR)
|
||||
throw new Error("Update manifest was not valid XML or JSON");
|
||||
|
||||
parser = () => parseRDFManifest(this.id, this.updateKey, request, xml);
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn("onUpdateCheckComplete failed to determine manifest type");
|
||||
this.notifyError(AddonUpdateChecker.ERROR_UNKNOWN_FORMAT);
|
||||
return;
|
||||
}
|
||||
|
||||
let results;
|
||||
try {
|
||||
results = parser();
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("onUpdateCheckComplete failed to parse update manifest", e);
|
||||
this.notifyError(AddonUpdateChecker.ERROR_PARSE_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("onUpdateCheckComplete" in this.observer) {
|
||||
try {
|
||||
this.observer.onUpdateCheckComplete(results);
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("onUpdateCheckComplete notification failed", e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the request times out
|
||||
*/
|
||||
onTimeout: function() {
|
||||
this.request = null;
|
||||
this._doneAt = new Error("Timed out");
|
||||
logger.warn("Request for " + this.url + " timed out");
|
||||
this.notifyError(AddonUpdateChecker.ERROR_TIMEOUT);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the manifest failed to load.
|
||||
*/
|
||||
onError: function() {
|
||||
if (!Components.isSuccessCode(this.request.status)) {
|
||||
logger.warn("Request failed: " + this.url + " - " + this.request.status);
|
||||
}
|
||||
else if (this.request.channel instanceof Ci.nsIHttpChannel) {
|
||||
try {
|
||||
if (this.request.channel.requestSucceeded) {
|
||||
logger.warn("Request failed: " + this.url + " - " +
|
||||
this.request.channel.responseStatus + ": " +
|
||||
this.request.channel.responseStatusText);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("HTTP Request failed for an unknown reason");
|
||||
}
|
||||
}
|
||||
else {
|
||||
logger.warn("Request failed for an unknown reason");
|
||||
}
|
||||
|
||||
this.request = null;
|
||||
this._doneAt = new Error("UP_onError");
|
||||
|
||||
this.notifyError(AddonUpdateChecker.ERROR_DOWNLOAD_ERROR);
|
||||
},
|
||||
|
||||
/**
|
||||
* Helper method to notify the observer that an error occured.
|
||||
*/
|
||||
notifyError: function(aStatus) {
|
||||
if ("onUpdateCheckError" in this.observer) {
|
||||
try {
|
||||
this.observer.onUpdateCheckError(aStatus);
|
||||
}
|
||||
catch (e) {
|
||||
logger.warn("onUpdateCheckError notification failed", e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to cancel an in-progress update check.
|
||||
*/
|
||||
cancel: function() {
|
||||
if (!this.request) {
|
||||
logger.error("Trying to cancel already-complete request", this._doneAt);
|
||||
return;
|
||||
}
|
||||
this.request.abort();
|
||||
this.request = null;
|
||||
this._doneAt = new Error("UP_cancel");
|
||||
this.notifyError(AddonUpdateChecker.ERROR_CANCELLED);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests if an update matches a version of the application or platform
|
||||
*
|
||||
* @param aUpdate
|
||||
* The available update
|
||||
* @param aAppVersion
|
||||
* The application version to use
|
||||
* @param aPlatformVersion
|
||||
* The platform version to use
|
||||
* @param aIgnoreMaxVersion
|
||||
* Ignore maxVersion when testing if an update matches. Optional.
|
||||
* @param aIgnoreStrictCompat
|
||||
* Ignore strictCompatibility when testing if an update matches. Optional.
|
||||
* @param aCompatOverrides
|
||||
* AddonCompatibilityOverride objects to match against. Optional.
|
||||
* @return true if the update is compatible with the application/platform
|
||||
*/
|
||||
function matchesVersions(aUpdate, aAppVersion, aPlatformVersion,
|
||||
aIgnoreMaxVersion, aIgnoreStrictCompat,
|
||||
aCompatOverrides) {
|
||||
if (aCompatOverrides) {
|
||||
let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
|
||||
aCompatOverrides,
|
||||
aAppVersion,
|
||||
aPlatformVersion);
|
||||
if (override && override.type == "incompatible")
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
|
||||
aIgnoreMaxVersion = false;
|
||||
|
||||
let result = false;
|
||||
for (let app of aUpdate.targetApplications) {
|
||||
if (app.id == Services.appinfo.ID) {
|
||||
return (Services.vc.compare(aAppVersion, app.minVersion) >= 0) &&
|
||||
(aIgnoreMaxVersion || (Services.vc.compare(aAppVersion, app.maxVersion) <= 0));
|
||||
}
|
||||
if (app.id == TOOLKIT_ID) {
|
||||
result = (Services.vc.compare(aPlatformVersion, app.minVersion) >= 0) &&
|
||||
(aIgnoreMaxVersion || (Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
this.AddonUpdateChecker = {
|
||||
// These must be kept in sync with AddonManager
|
||||
// The update check timed out
|
||||
ERROR_TIMEOUT: -1,
|
||||
// There was an error while downloading the update information.
|
||||
ERROR_DOWNLOAD_ERROR: -2,
|
||||
// The update information was malformed in some way.
|
||||
ERROR_PARSE_ERROR: -3,
|
||||
// The update information was not in any known format.
|
||||
ERROR_UNKNOWN_FORMAT: -4,
|
||||
// The update information was not correctly signed or there was an SSL error.
|
||||
ERROR_SECURITY_ERROR: -5,
|
||||
// The update was cancelled
|
||||
ERROR_CANCELLED: -6,
|
||||
|
||||
/**
|
||||
* Retrieves the best matching compatibility update for the application from
|
||||
* a list of available update objects.
|
||||
*
|
||||
* @param aUpdates
|
||||
* An array of update objects
|
||||
* @param aVersion
|
||||
* The version of the add-on to get new compatibility information for
|
||||
* @param aIgnoreCompatibility
|
||||
* An optional parameter to get the first compatibility update that
|
||||
* is compatible with any version of the application or toolkit
|
||||
* @param aAppVersion
|
||||
* The version of the application or null to use the current version
|
||||
* @param aPlatformVersion
|
||||
* The version of the platform or null to use the current version
|
||||
* @param aIgnoreMaxVersion
|
||||
* Ignore maxVersion when testing if an update matches. Optional.
|
||||
* @param aIgnoreStrictCompat
|
||||
* Ignore strictCompatibility when testing if an update matches. Optional.
|
||||
* @return an update object if one matches or null if not
|
||||
*/
|
||||
getCompatibilityUpdate: function(aUpdates, aVersion, aIgnoreCompatibility,
|
||||
aAppVersion, aPlatformVersion,
|
||||
aIgnoreMaxVersion, aIgnoreStrictCompat) {
|
||||
if (!aAppVersion)
|
||||
aAppVersion = Services.appinfo.version;
|
||||
if (!aPlatformVersion)
|
||||
aPlatformVersion = Services.appinfo.platformVersion;
|
||||
|
||||
for (let update of aUpdates) {
|
||||
if (Services.vc.compare(update.version, aVersion) == 0) {
|
||||
if (aIgnoreCompatibility) {
|
||||
for (let targetApp of update.targetApplications) {
|
||||
let id = targetApp.id;
|
||||
if (id == Services.appinfo.ID || id == TOOLKIT_ID)
|
||||
return update;
|
||||
}
|
||||
}
|
||||
else if (matchesVersions(update, aAppVersion, aPlatformVersion,
|
||||
aIgnoreMaxVersion, aIgnoreStrictCompat)) {
|
||||
return update;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the newest available update from a list of update objects.
|
||||
*
|
||||
* @param aUpdates
|
||||
* An array of update objects
|
||||
* @param aAppVersion
|
||||
* The version of the application or null to use the current version
|
||||
* @param aPlatformVersion
|
||||
* The version of the platform or null to use the current version
|
||||
* @param aIgnoreMaxVersion
|
||||
* When determining compatible updates, ignore maxVersion. Optional.
|
||||
* @param aIgnoreStrictCompat
|
||||
* When determining compatible updates, ignore strictCompatibility. Optional.
|
||||
* @param aCompatOverrides
|
||||
* Array of AddonCompatibilityOverride to take into account. Optional.
|
||||
* @return an update object if one matches or null if not
|
||||
*/
|
||||
getNewestCompatibleUpdate: function(aUpdates, aAppVersion, aPlatformVersion,
|
||||
aIgnoreMaxVersion, aIgnoreStrictCompat,
|
||||
aCompatOverrides) {
|
||||
if (!aAppVersion)
|
||||
aAppVersion = Services.appinfo.version;
|
||||
if (!aPlatformVersion)
|
||||
aPlatformVersion = Services.appinfo.platformVersion;
|
||||
|
||||
let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
|
||||
let newest = null;
|
||||
for (let update of aUpdates) {
|
||||
if (!update.updateURL)
|
||||
continue;
|
||||
let state = blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
|
||||
if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
|
||||
continue;
|
||||
if ((newest == null || (Services.vc.compare(newest.version, update.version) < 0)) &&
|
||||
matchesVersions(update, aAppVersion, aPlatformVersion,
|
||||
aIgnoreMaxVersion, aIgnoreStrictCompat,
|
||||
aCompatOverrides)) {
|
||||
newest = update;
|
||||
}
|
||||
}
|
||||
return newest;
|
||||
},
|
||||
|
||||
/**
|
||||
* Starts an update check.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on being checked for updates
|
||||
* @param aUpdateKey
|
||||
* An optional update key for the add-on
|
||||
* @param aUrl
|
||||
* The URL of the add-on's update manifest
|
||||
* @param aObserver
|
||||
* An observer to notify of results
|
||||
* @return UpdateParser so that the caller can use UpdateParser.cancel() to shut
|
||||
* down in-progress update requests
|
||||
*/
|
||||
checkForUpdates: function(aId, aUpdateKey, aUrl, aObserver) {
|
||||
return new UpdateParser(aId, aUpdateKey, aUrl, aObserver);
|
||||
}
|
||||
};
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* globals addMessageListener*/
|
||||
|
||||
"use strict";
|
||||
|
||||
(function() {
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
var {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
var nsIFile = Components.Constructor("@mozilla.org/file/local;1", "nsIFile",
|
||||
"initWithPath");
|
||||
|
||||
const MSG_JAR_FLUSH = "AddonJarFlush";
|
||||
const MSG_MESSAGE_MANAGER_CACHES_FLUSH = "AddonMessageManagerCachesFlush";
|
||||
|
||||
|
||||
try {
|
||||
if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) {
|
||||
// Propagate JAR cache flush notifications across process boundaries.
|
||||
addMessageListener(MSG_JAR_FLUSH, function(message) {
|
||||
let file = new nsIFile(message.data);
|
||||
Services.obs.notifyObservers(file, "flush-cache-entry", null);
|
||||
});
|
||||
// Propagate message manager caches flush notifications across processes.
|
||||
addMessageListener(MSG_MESSAGE_MANAGER_CACHES_FLUSH, function() {
|
||||
Services.obs.notifyObservers(null, "message-manager-flush-caches", null);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
|
||||
})();
|
||||
|
|
@ -1,982 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "isAddonPartOfE10SRollout" ];
|
||||
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const PREF_E10S_ADDON_BLOCKLIST = "extensions.e10s.rollout.blocklist";
|
||||
const PREF_E10S_ADDON_POLICY = "extensions.e10s.rollout.policy";
|
||||
|
||||
const ADDONS = {
|
||||
"Greasemonkey": { // Greasemonkey
|
||||
id: "{e4a8a97b-f2ed-450b-b12d-ee082ba24781}", minVersion: "3.8",
|
||||
},
|
||||
|
||||
"DYTV": { // Download YouTube Videos as MP4
|
||||
id: "{b9bfaf1c-a63f-47cd-8b9a-29526ced9060}", minVersion: "1.8.7",
|
||||
},
|
||||
|
||||
"VDH": { // Video Download Helper
|
||||
id: "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}", minVersion: "5.6.1",
|
||||
},
|
||||
|
||||
"Lightbeam": { // Lightbeam
|
||||
id: "jid1-F9UJ2thwoAm5gQ@jetpack", minVersion: "1.3.0.1",
|
||||
},
|
||||
|
||||
"ABP": { // Adblock Plus
|
||||
id: "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}", minVersion: "2.7.3",
|
||||
},
|
||||
|
||||
"uBlockOrigin": { // uBlock Origin
|
||||
id: "uBlock0@raymondhill.net", minVersion: "1.7.6",
|
||||
},
|
||||
|
||||
"Emoji": { // Emoji Cheatsheet
|
||||
id: "jid1-Xo5SuA6qc1DFpw@jetpack", minVersion: "1.1.1",
|
||||
},
|
||||
|
||||
"ASP": { // Awesome Screenshot Plus
|
||||
id: "jid0-GXjLLfbCoAx0LcltEdFrEkQdQPI@jetpack", minVersion: "3.0.10",
|
||||
},
|
||||
|
||||
"PersonasPlus": { // PersonasPlus
|
||||
id: "personas@christopher.beard", minVersion: "1.8.0",
|
||||
},
|
||||
|
||||
"ACR": { // Add-on Compatibility Reporter
|
||||
id: "compatibility@addons.mozilla.org", minVersion: "2.2.0",
|
||||
},
|
||||
|
||||
// Add-ons used for testing
|
||||
"test1": {
|
||||
id: "bootstrap1@tests.mozilla.org", minVersion: "1.0",
|
||||
},
|
||||
|
||||
"test2": {
|
||||
id: "bootstrap2@tests.mozilla.org", minVersion: "1.0",
|
||||
},
|
||||
};
|
||||
|
||||
// NOTE: Do not modify sets or policies after they have already been
|
||||
// published to users. They must remain unchanged to provide valid data.
|
||||
|
||||
// Set 2 used during 48 Beta cycle. Kept here for historical reasons.
|
||||
const set2 = [ADDONS.Greasemonkey,
|
||||
ADDONS.DYTV,
|
||||
ADDONS.VDH,
|
||||
ADDONS.Lightbeam,
|
||||
ADDONS.ABP,
|
||||
ADDONS.uBlockOrigin,
|
||||
ADDONS.Emoji,
|
||||
ADDONS.ASP,
|
||||
ADDONS.PersonasPlus];
|
||||
|
||||
const set49Release = [
|
||||
ADDONS.Greasemonkey,
|
||||
ADDONS.DYTV,
|
||||
ADDONS.VDH,
|
||||
ADDONS.Lightbeam,
|
||||
ADDONS.ABP,
|
||||
ADDONS.uBlockOrigin,
|
||||
ADDONS.Emoji,
|
||||
ADDONS.ASP,
|
||||
ADDONS.PersonasPlus,
|
||||
ADDONS.ACR
|
||||
];
|
||||
|
||||
// These are only the add-ons in the Add-Ons Manager Discovery
|
||||
// pane. This set is here in case we need to reduce add-ons
|
||||
// exposure live on Release.
|
||||
const set49PaneOnly = [
|
||||
ADDONS.ABP,
|
||||
ADDONS.VDH,
|
||||
ADDONS.Emoji,
|
||||
ADDONS.ASP,
|
||||
ADDONS.ACR
|
||||
]
|
||||
|
||||
// ================== ADDONS FOR 51 RELEASE ==================
|
||||
//
|
||||
// During the 51 beta cycle, we tested e10s with all addons
|
||||
// except those explicitly marked as being incompatible.
|
||||
// For release, instead of opening this up, we assembled
|
||||
// the lists below with all addons that were seen on beta
|
||||
// and had over 50 installs.
|
||||
//
|
||||
// This list is in a new format to allow fast access and also
|
||||
// to allow controlling by the number of addons installed.
|
||||
|
||||
const set51Release = {
|
||||
"_65Members_@download.fromdoctopdf.com": {minVersion: "7.102.10.4221", installs: 32092},
|
||||
"light_plugin_ACF0E80077C511E59DED005056C00008@kaspersky.com": {minVersion: "4.6.3-15", installs: 27758},
|
||||
"_ceMembers_@free.easypdfcombine.com": {minVersion: "7.102.10.4117", installs: 17797},
|
||||
"caa1-aDOiCAxFFMOVIX@jetpack": {minVersion: "0.1.7", installs: 13150},
|
||||
"{4ED1F68A-5463-4931-9384-8FFF5ED91D92}": {minVersion: "5.0.248.0", installs: 12774},
|
||||
"_dbMembers_@free.getformsonline.com": {minVersion: "7.102.10.4251", installs: 11909},
|
||||
"_4zMembers_@www.videodownloadconverter.com": {minVersion: "7.102.10.5033", installs: 11612},
|
||||
"light_plugin_F6F079488B53499DB99380A7E11A93F6@kaspersky.com": {minVersion: "5.0.141-4-20161031140250", installs: 10944},
|
||||
"YoutubeDownloader@PeterOlayev.com": {minVersion: "2.4.1", installs: 10722},
|
||||
"{82AF8DCA-6DE9-405D-BD5E-43525BDAD38A}": {minVersion: "8.0.0.9103", installs: 8856},
|
||||
"client@anonymox.net": {minVersion: "2.5.2", installs: 8225},
|
||||
"_8hMembers_@download.allin1convert.com": {minVersion: "7.102.10.3584", installs: 7681},
|
||||
"light_plugin_D772DC8D6FAF43A29B25C4EBAA5AD1DE@kaspersky.com": {minVersion: "4.6.2-42-20160922074409", installs: 7177},
|
||||
"_dzMembers_@www.pconverter.com": {minVersion: "7.102.10.4851", installs: 7115},
|
||||
"fxdevtools-adapters@mozilla.org": {minVersion: "0.3.5", installs: 6926},
|
||||
"_9pMembers_@free.onlinemapfinder.com": {minVersion: "7.102.10.4836", installs: 6583},
|
||||
"@DownloadManager": {minVersion: "0.2.1", installs: 6412},
|
||||
"ar1er-ewrgfdgomusix@jetpack": {minVersion: "1.0.6", installs: 5975},
|
||||
"_agMembers_@free.premierdownloadmanager.com": {minVersion: "7.102.10.4846", installs: 5605},
|
||||
"_paMembers_@www.filmfanatic.com": {minVersion: "7.102.10.4163", installs: 5448},
|
||||
"_gtMembers_@free.gamingwonderland.com": {minVersion: "7.102.10.4263", installs: 5241},
|
||||
"LVD-SAE@iacsearchandmedia.com": {minVersion: "8.5", installs: 4694},
|
||||
"_fsMembers_@free.pdfconverterhq.com": {minVersion: "7.102.10.4849", installs: 4526},
|
||||
"_6xMembers_@www.readingfanatic.com": {minVersion: "7.102.10.4914", installs: 4417},
|
||||
"@mysmartprice-ff": {minVersion: "0.0.6", installs: 4381},
|
||||
"jid1-YcMV6ngYmQRA2w@jetpack": {minVersion: "1.37.9", installs: 3899},
|
||||
"{58d735b4-9d6c-4e37-b146-7b9f7e79e318}": {minVersion: "1.6", installs: 3733},
|
||||
"anttoolbar@ant.com": {minVersion: "2.4.7.47", installs: 3720},
|
||||
"adblockpopups@jessehakanen.net": {minVersion: "0.9.2.1-signed.1-signed", installs: 3602},
|
||||
"ERAIL.IN.FFPLUGIN@jetpack": {minVersion: "6.0.rev142", installs: 3545},
|
||||
"WebProtection@360safe.com": {minVersion: "5.0.0.1005", installs: 3475},
|
||||
"yasearch@yandex.ru": {minVersion: "8.20.4", installs: 3299},
|
||||
"{19503e42-ca3c-4c27-b1e2-9cdb2170ee34}": {minVersion: "1.5.6.14", installs: 3106},
|
||||
"{C1A2A613-35F1-4FCF-B27F-2840527B6556}": {minVersion: "2016.8.1.9", installs: 3083},
|
||||
"_b7Members_@free.mytransitguide.com": {minVersion: "7.102.10.4812", installs: 3011},
|
||||
"_9tMembers_@free.internetspeedtracker.com": {minVersion: "7.102.10.4339", installs: 2828},
|
||||
"_64Members_@www.televisionfanatic.com": {minVersion: "7.102.10.4968", installs: 2821},
|
||||
"info@youtube-mp3.org": {minVersion: "1.0.9.1-signed.1-signed", installs: 2717},
|
||||
"ffext_basicvideoext@startpage24": {minVersion: "1.97.37.1-signed.1-signed", installs: 2663},
|
||||
"MUB-SAE@iacsearchandmedia.com": {minVersion: "8.7", installs: 2650},
|
||||
"_4jMembers_@www.radiorage.com": {minVersion: "7.102.10.4916", installs: 2631},
|
||||
"@Email": {minVersion: "4.0.12", installs: 2583},
|
||||
"_gcMembers_@www.weatherblink.com": {minVersion: "7.38.8.56523", installs: 2519},
|
||||
"_dqMembers_@www.downspeedtest.com": {minVersion: "7.102.10.3827", installs: 2445},
|
||||
"translator@zoli.bod": {minVersion: "2.1.0.5.1.1-signed", installs: 2310},
|
||||
"{a38384b3-2d1d-4f36-bc22-0f7ae402bcd7}": {minVersion: "1.0.0.51", installs: 2190},
|
||||
"_1eMembers_@www.videoscavenger.com": {minVersion: "7.38.8.45273", installs: 2185},
|
||||
"tvplusnewtab-the-extension1@mozilla.com": {minVersion: "0.1.5", installs: 2155},
|
||||
"homepage@mail.ru": {minVersion: "1.0.2", installs: 2124},
|
||||
"search@mail.ru": {minVersion: "1.0.7", installs: 2038},
|
||||
"_69Members_@www.packagetracer.com": {minVersion: "7.102.10.4831", installs: 2036},
|
||||
"{7b8a500a-a464-4624-bd4f-73eaafe0f766}": {minVersion: "3", installs: 2027},
|
||||
"paulsaintuzb@gmail.com": {minVersion: "8.2.1", installs: 2005},
|
||||
"k7srff_enUS@k7computing.com": {minVersion: "2.4", installs: 1929},
|
||||
"_e5Members_@www.productivityboss.com": {minVersion: "7.38.8.46590", installs: 1892},
|
||||
"vdpure@link64": {minVersion: "1.97.43", installs: 1860},
|
||||
"_9tMembers_@download.internetspeedtracker.com": {minVersion: "7.38.8.56171", installs: 1824},
|
||||
"_g3Members_@free.easyphotoedit.com": {minVersion: "7.102.10.4108", installs: 1822},
|
||||
"_64Members_@download.televisionfanatic.com": {minVersion: "7.38.9.3004", installs: 1730},
|
||||
"_8iMembers_@download.audiotoaudio.com": {minVersion: "7.102.10.3585", installs: 1704},
|
||||
"adblockultimate@adblockultimate.net": {minVersion: "2.25", installs: 1648},
|
||||
"eagleget_ffext@eagleget.com": {minVersion: "3.8", installs: 1640},
|
||||
"_9eMembers_@free.findmefreebies.com": {minVersion: "7.102.10.4193", installs: 1638},
|
||||
"content_blocker_663BE8@kaspersky.com": {minVersion: "4.5.4.19.1", installs: 1625},
|
||||
"virtual_keyboard_074028@kaspersky.com": {minVersion: "4.5.4.19.1", installs: 1624},
|
||||
"browsec@browsec.com": {minVersion: "2.0.3", installs: 1610},
|
||||
"@Maps": {minVersion: "4.0.0", installs: 1587},
|
||||
"_exMembers_@free.easydocmerge.com": {minVersion: "7.102.10.4137", installs: 1493},
|
||||
"{635abd67-4fe9-1b23-4f01-e679fa7484c1}": {minVersion: "5.0.2", installs: 1490},
|
||||
"abb@amazon.com": {minVersion: "10.1612.1.304", installs: 1463},
|
||||
"{1BC9BA34-1EED-42ca-A505-6D2F1A935BBB}": {minVersion: "6.2.18.1", installs: 1436},
|
||||
"mp4downloader@jeff.net": {minVersion: "1.3.3.1-signed.1-signed", installs: 1410},
|
||||
"jid1-16aeif9OQIRKxA@jetpack": {minVersion: "1.1.4", installs: 1399},
|
||||
"{c45c406e-ab73-11d8-be73-000a95be3b12}": {minVersion: "1.2.11", installs: 1367},
|
||||
"online_banking_08806E@kaspersky.com": {minVersion: "4.5.4.19.1", installs: 1356},
|
||||
"_ewMembers_@free.mergedocsonline.com": {minVersion: "7.102.10.4710", installs: 1337},
|
||||
"@DiscreteSearch": {minVersion: "0.2.1", installs: 1306},
|
||||
"{6AC85730-7D0F-4de0-B3FA-21142DD85326}": {minVersion: "2.8.2", installs: 1286},
|
||||
"{063DA41A-2561-401B-91FA-AC75E460F4EB}": {minVersion: "1.0.7.1", installs: 1280},
|
||||
"netvideohunter@netvideohunter.com": {minVersion: "1.2", installs: 1260},
|
||||
"_8eMembers_@download.howtosimplified.com": {minVersion: "7.102.10.4285", installs: 1230},
|
||||
"FGZ-SAE@iacsearchandmedia.com": {minVersion: "8.5", installs: 1220},
|
||||
"adguardadblocker@adguard.com": {minVersion: "2.4.14", installs: 1172},
|
||||
"_39Members_@www.mapsgalaxy.com": {minVersion: "7.102.10.4730", installs: 1171},
|
||||
"_euMembers_@free.filesendsuite.com": {minVersion: "7.102.10.4154", installs: 1166},
|
||||
"_brMembers_@free.yourtemplatefinder.com": {minVersion: "7.102.10.5047", installs: 1159},
|
||||
"_8jMembers_@download.myimageconverter.com": {minVersion: "7.102.10.4778", installs: 1150},
|
||||
"_12Members_@free.myscrapnook.com": {minVersion: "7.102.10.4739", installs: 1113},
|
||||
"_7eMembers_@www.homeworksimplified.com": {minVersion: "7.102.10.4290", installs: 1109},
|
||||
"{fe272bd1-5f76-4ea4-8501-a05d35d823fc}": {minVersion: "2.1.9.1-signed.1-let-fixed.1-signed", installs: 1108},
|
||||
"_frMembers_@free.testforspeed.com": {minVersion: "7.102.10.4993", installs: 1107},
|
||||
"{068e178c-61a9-4a63-b74f-87404a6f5ea1}": {minVersion: "2", installs: 1104},
|
||||
"@Package": {minVersion: "0.2.0", installs: 1092},
|
||||
"6asa42dfa4784fsf368g@youtubeconverter.me": {minVersion: "0.1", installs: 1071},
|
||||
"_diMembers_@www.free.easymaillogin.com": {minVersion: "7.102.10.4112", installs: 1043},
|
||||
"_v4Members_@www.dictionaryboss.com": {minVersion: "7.102.10.3797", installs: 1035},
|
||||
"colorPicker@colorPicker": {minVersion: "3.0.1-signed.1-signed", installs: 1023},
|
||||
"hotspot-shield@anchorfree.com": {minVersion: "1.2.87", installs: 1000},
|
||||
"manishjain9@hotmail.com_easiestyoutube": {minVersion: "7.2.1-signed.1-let-fixed.1-signed", installs: 993},
|
||||
"{cd617375-6743-4ee8-bac4-fbf10f35729e}": {minVersion: "2.9.6", installs: 987},
|
||||
"@Converter": {minVersion: "4.1.0", installs: 986},
|
||||
"{dd3d7613-0246-469d-bc65-2a3cc1668adc}": {minVersion: "1.1.8.1-signed.1-signed", installs: 983},
|
||||
"ubufox@ubuntu.com": {minVersion: "3.2", installs: 950},
|
||||
"jid1-lpoiffmusixlib@jetpack": {minVersion: "0.1.9", installs: 945},
|
||||
"_5aMembers_@download.mywebface.com": {minVersion: "7.102.10.4837", installs: 930},
|
||||
"leethax@leethax.net": {minVersion: "2016.12.02", installs: 930},
|
||||
"{1A2D0EC4-75F5-4c91-89C4-3656F6E44B68}": {minVersion: "0.6.3.1-signed.1-signed", installs: 885},
|
||||
"{64161300-e22b-11db-8314-0800200c9a66}": {minVersion: "0.9.6.18", installs: 875},
|
||||
"_bfMembers_@free.snapmyscreen.com": {minVersion: "7.102.10.4951", installs: 827},
|
||||
"uriloader@pdf.js": {minVersion: "1.0.277.1-signed.1-signed", installs: 815},
|
||||
"{e968fc70-8f95-4ab9-9e79-304de2a71ee1}": {minVersion: "0.7.3.1-signed.1-signed", installs: 805},
|
||||
"save-as-pdf-ff@pdfcrowd.com": {minVersion: "1.5.1-signed.1-signed", installs: 804},
|
||||
"{75CEEE46-9B64-46f8-94BF-54012DE155F0}": {minVersion: "0.4.15", installs: 794},
|
||||
"safesearchplus2@avira.com": {minVersion: "1.4.1.371", installs: 786},
|
||||
"easyscreenshot@mozillaonline.com": {minVersion: "1.2.8", installs: 785},
|
||||
"_eeMembers_@download.freeradiocast.com": {minVersion: "7.38.8.46366", installs: 783},
|
||||
"_89Members_@download.safepcrepair.com": {minVersion: "7.39.8.51080", installs: 777},
|
||||
"{a3a5c777-f583-4fef-9380-ab4add1bc2a5}": {minVersion: "2.4.2.1-signed", installs: 771},
|
||||
"content_blocker@kaspersky.com": {minVersion: "4.0.10.15", installs: 770},
|
||||
"safesearch@avira.com": {minVersion: "1.4.1.371", installs: 767},
|
||||
"youtube2mp3@mondayx.de": {minVersion: "1.2.3.1-signed.1-signed", installs: 748},
|
||||
"2020Player_IKEA@2020Technologies.com": {minVersion: "5.0.94.1", installs: 736},
|
||||
"_edMembers_@free.myradioaccess.com": {minVersion: "7.102.10.4797", installs: 734},
|
||||
"_dmMembers_@free.gounzip.com": {minVersion: "7.102.10.4277", installs: 733},
|
||||
"Media-Newtab-the-extension1@mozilla.com": {minVersion: "0.1.6", installs: 732},
|
||||
"foxmarks@kei.com": {minVersion: "4.3.19", installs: 728},
|
||||
"{e8deb9e5-5688-4655-838a-b7a121a9f16e}": {minVersion: "48.4", installs: 726},
|
||||
"{195A3098-0BD5-4e90-AE22-BA1C540AFD1E}": {minVersion: "4.1.0.1-signed.1-signed", installs: 722},
|
||||
"jid1-4P0kohSJxU1qGg@jetpack": {minVersion: "1.22.550", installs: 719},
|
||||
"DailymotionVideoDownloader@PeterOlayev.com": {minVersion: "1.0.6.1-signed.1-signed", installs: 717},
|
||||
"jid1-P34HaABBBpOerQ@jetpack": {minVersion: "0.2.1-signed.1-signed", installs: 715},
|
||||
"SQLiteManager@mrinalkant.blogspot.com": {minVersion: "0.8.3.1-signed.1-signed", installs: 700},
|
||||
"2.0@disconnect.me": {minVersion: "3.15.3.1-signed.1-signed", installs: 693},
|
||||
"multifox@hultmann": {minVersion: "3.2.3", installs: 690},
|
||||
"_5mMembers_@download.myfuncards.com": {minVersion: "7.102.10.4783", installs: 679},
|
||||
"_btMembers_@free.catsandcatapults.com": {minVersion: "7.102.10.3677", installs: 673},
|
||||
"pavel.sherbakov@gmail.com": {minVersion: "19.1.1", installs: 666},
|
||||
"_fbMembers_@free.smarterpassword.com": {minVersion: "7.102.10.4936", installs: 644},
|
||||
"jid2-l8SPBzHJWBIiHQ@jetpack": {minVersion: "3.1", installs: 639},
|
||||
"{B17C1C5A-04B1-11DB-9804-B622A1EF5492}": {minVersion: "1.3.2", installs: 633},
|
||||
"myplaycitycom@gametab": {minVersion: "1.6", installs: 616},
|
||||
"{ad0d925d-88f8-47f1-85ea-8463569e756e}": {minVersion: "2.0.5", installs: 604},
|
||||
"{37964A3C-4EE8-47b1-8321-34DE2C39BA4D}": {minVersion: "2.5.4.174", installs: 603},
|
||||
"youtubemp3podcaster@jeremy.d.gregorio.com": {minVersion: "3.9.0", installs: 601},
|
||||
"caa1-aDOiCAxFFPRIVATE@jetpack": {minVersion: "0.2.0", installs: 598},
|
||||
"_f5Members_@free.typingfanatic.com": {minVersion: "7.102.10.5014", installs: 595},
|
||||
"_94Members_@www.motitags.com": {minVersion: "7.102.10.4744", installs: 594},
|
||||
"{888d99e7-e8b5-46a3-851e-1ec45da1e644}": {minVersion: "45.0.0", installs: 581},
|
||||
"_1cMembers_@www.bringmesports.com": {minVersion: "7.102.10.3646", installs: 580},
|
||||
"{a6fd85ed-e919-4a43-a5af-8da18bda539f}": {minVersion: "2.9.1.1-signed", installs: 572},
|
||||
"{0fc22c4c-93ed-48ea-ad12-dc8039cf3795}": {minVersion: "1.3", installs: 568},
|
||||
"homeutil@yandex.ru": {minVersion: "1.0.13", installs: 565},
|
||||
"_doMembers_@free.convertanyfile.com": {minVersion: "7.38.8.45860", installs: 563},
|
||||
"SocialNewPages-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 561},
|
||||
"wappalyzer@crunchlabz.com": {minVersion: "3.2.7", installs: 557},
|
||||
"_5qMembers_@www.zwinky.com": {minVersion: "7.38.8.45270", installs: 551},
|
||||
"{0545b830-f0aa-4d7e-8820-50a4629a56fe}": {minVersion: "31.0.9", installs: 531},
|
||||
"vk@sergeykolosov.mp": {minVersion: "0.3.9.5", installs: 522},
|
||||
"{77b819fa-95ad-4f2c-ac7c-486b356188a9}": {minVersion: "4.0.20130422.1-signed.1-signed", installs: 505},
|
||||
"@true-key": {minVersion: "1.23.0.2433", installs: 501},
|
||||
"_1pMembers_@www.referenceboss.com": {minVersion: "7.102.10.4932", installs: 499},
|
||||
"{C7AE725D-FA5C-4027-BB4C-787EF9F8248A}": {minVersion: "1.0.0.4", installs: 494},
|
||||
"alx-ffdeveloper@amazon.com": {minVersion: "3.0.2", installs: 493},
|
||||
"{3d7eb24f-2740-49df-8937-200b1cc08f8a}": {minVersion: "1.5.20", installs: 491},
|
||||
"_1gMembers_@www.inboxace.com": {minVersion: "7.38.8.56535", installs: 488},
|
||||
"{7DD78D43-0962-4d9b-BC76-ABF13B3B2ED1}": {minVersion: "3.5.0.1428", installs: 484},
|
||||
"imageblock@hemantvats.com": {minVersion: "3.1", installs: 472},
|
||||
"online_banking@kaspersky.com": {minVersion: "4.0.10.15", installs: 463},
|
||||
"virtual_keyboard@kaspersky.com": {minVersion: "4.0.10.15", installs: 463},
|
||||
"button@scholar.google.com": {minVersion: "1.1.1-signed.1-signed", installs: 463},
|
||||
"anti_banner@kaspersky.com": {minVersion: "4.0.10.15", installs: 462},
|
||||
"url_advisor@kaspersky.com": {minVersion: "4.0.10.15", installs: 461},
|
||||
"{6d96bb5e-1175-4ebf-8ab5-5f56f1c79f65}": {minVersion: "0.9.8", installs: 457},
|
||||
"_14Members_@download.totalrecipesearch.com": {minVersion: "7.102.10.4983", installs: 456},
|
||||
"{394DCBA4-1F92-4f8e-8EC9-8D2CB90CB69B}": {minVersion: "5.1.1", installs: 447},
|
||||
"_57Members_@free.marineaquariumfree.com": {minVersion: "7.102.10.4716", installs: 446},
|
||||
"e67f8350-7edf-11e3-baa7-0800200c9a66@fri-gate.org": {minVersion: "2.2.1.1-signed", installs: 446},
|
||||
"FireXPath@pierre.tholence.com": {minVersion: "0.9.7.1.1-signed.1-signed", installs: 442},
|
||||
"@youtube_downloader": {minVersion: "0.0.9", installs: 435},
|
||||
"ff_hpset@jetpack": {minVersion: "1.0.8", installs: 428},
|
||||
"{d0bfdcce-52c7-4b32-bb45-948f62db8d3f}": {minVersion: "49.1", installs: 406},
|
||||
"_j2Members_@www.soccerinferno.com": {minVersion: "7.102.10.4948", installs: 405},
|
||||
"autoform@olifozzy": {minVersion: "1.2.4.1-signed.1-signed", installs: 405},
|
||||
"FunSafeTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 405},
|
||||
"testpilot@labs.mozilla.com": {minVersion: "1.2.3.1-signed", installs: 405},
|
||||
"vwof@drev.com": {minVersion: "3.1.2", installs: 401},
|
||||
"_ftMembers_@free.mytelevisionhq.com": {minVersion: "7.102.10.4817", installs: 397},
|
||||
"{e001c731-5e37-4538-a5cb-8168736a2360}": {minVersion: "0.9.9.152", installs: 396},
|
||||
"{95E84BD3-3604-4AAC-B2CA-D9AC3E55B64B}": {minVersion: "2.0.0.78", installs: 393},
|
||||
"_8lMembers_@free.filesharefanatic.com": {minVersion: "7.102.10.4171", installs: 389},
|
||||
"clipconverter@clipconverter.cc": {minVersion: "1.5.2", installs: 387},
|
||||
"_7jMembers_@download.gardeningenthusiast.com": {minVersion: "7.102.10.4260", installs: 383},
|
||||
"antmark@ant.com": {minVersion: "1.1.14", installs: 383},
|
||||
"_flMembers_@free.myformsfinder.com": {minVersion: "7.102.10.4784", installs: 381},
|
||||
"{c36177c0-224a-11da-8cd6-0800200c9a91}": {minVersion: "3.9.85.1-signed.1-signed", installs: 375},
|
||||
"@searchincognito": {minVersion: "0.1.0", installs: 375},
|
||||
"{f13b157f-b174-47e7-a34d-4815ddfdfeb8}": {minVersion: "0.9.89.1-signed.1-signed", installs: 373},
|
||||
"_5eMembers_@www.translationbuddy.com": {minVersion: "7.38.8.45962", installs: 372},
|
||||
"{9c51bd27-6ed8-4000-a2bf-36cb95c0c947}": {minVersion: "11.0.1.1-signed.1-signed", installs: 370},
|
||||
"clickclean@hotcleaner.com": {minVersion: "4.1.1-signed.1-signed", installs: 366},
|
||||
"jid1-xKH0EoS44u1a2w@jetpack": {minVersion: "0.1.1-signed.1-signed", installs: 366},
|
||||
"{c2056674-a37f-4b29-9300-2004759d74fe}": {minVersion: "2.0.0.1090", installs: 361},
|
||||
"newtab-tv-the-extension1@mozilla.com": {minVersion: "0.1.5", installs: 359},
|
||||
"ascsurfingprotectionnew@iobit.com": {minVersion: "2.1.3", installs: 355},
|
||||
"FunTabSafe-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 353},
|
||||
"d.lehr@chello.at": {minVersion: "1.2", installs: 350},
|
||||
"anticontainer@downthemall.net": {minVersion: "1.5", installs: 348},
|
||||
"{F8A55C97-3DB6-4961-A81D-0DE0080E53CB}": {minVersion: "1.0.10", installs: 347},
|
||||
"@FormsApp": {minVersion: "0.2.0", installs: 346},
|
||||
"multilinksplus@hugsmile.eu": {minVersion: "3.9.3", installs: 343},
|
||||
"jid1-KWFaW5zc0EbtBQ@jetpack": {minVersion: "0.2.0", installs: 335},
|
||||
"{e8f509f0-b677-11de-8a39-0800200c9a66}": {minVersion: "1.12.1-signed.1-let-fixed.1-signed", installs: 334},
|
||||
"{37E4D8EA-8BDA-4831-8EA1-89053939A250}": {minVersion: "3.0.0.2.1-signed.1-signed", installs: 333},
|
||||
"{c8d3bc80-0810-4d21-a2c2-be5f2b2832ac}": {minVersion: "0.98", installs: 332},
|
||||
"{cb40da56-497a-4add-955d-3377cae4c33b}": {minVersion: "10.2.0.271", installs: 331},
|
||||
"{5546F97E-11A5-46b0-9082-32AD74AAA920}": {minVersion: "0.76.1-signed.1-signed", installs: 325},
|
||||
"_14Members_@www.totalrecipesearch.com": {minVersion: "7.38.8.45925", installs: 324},
|
||||
"info@mp3it.eu": {minVersion: "1.4.1.1-signed.1-signed", installs: 324},
|
||||
"firefox-autofill@googlegroups.com": {minVersion: "3.6.1-signed.1-signed", installs: 317},
|
||||
"jid1-TQvJxTBYHA8qXg@jetpack": {minVersion: "0.4.1-signed.1-signed", installs: 315},
|
||||
"{8f8fe09b-0bd3-4470-bc1b-8cad42b8203a}": {minVersion: "0.17.1-signed.1-signed", installs: 311},
|
||||
"{D4DD63FA-01E4-46a7-B6B1-EDAB7D6AD389}": {minVersion: "0.9.10.1-signed.1-signed", installs: 311},
|
||||
"{d7f46ca0-899d-11da-a72b-0800200c9a65}": {minVersion: "0.1.2.1-signed.1-signed", installs: 311},
|
||||
"twoo@twoo.com": {minVersion: "1.6.0.1-signed", installs: 303},
|
||||
"_29Members_@www.headlinealley.com": {minVersion: "7.38.8.56537", installs: 302},
|
||||
"_e2Members_@free.coolpopulargames.com": {minVersion: "7.38.8.45873", installs: 300},
|
||||
"TopTVTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 300},
|
||||
"tmbepff@trendmicro.com": {minVersion: "9.2.0.1026", installs: 293},
|
||||
"_2vMembers_@www.dailybibleguide.com": {minVersion: "7.38.8.52880", installs: 289},
|
||||
"{54e46280-0211-11e3-b778-0800200c9a66}": {minVersion: "0.3", installs: 285},
|
||||
"_49Members_@www.utilitychest.com": {minVersion: "7.38.8.45977", installs: 284},
|
||||
"amcontextmenu@loucypher": {minVersion: "0.4.2.1-signed.1-signed", installs: 284},
|
||||
"jid1-r1tDuNiNb4SEww@jetpack": {minVersion: "1.1.2673", installs: 283},
|
||||
"_erMembers_@free.getvideoconvert.com": {minVersion: "7.102.10.5038", installs: 281},
|
||||
"{b1df372d-8b32-4c7d-b6b4-9c5b78cf6fb1}": {minVersion: "0.87.1-signed.1-signed", installs: 281},
|
||||
"jid1-cHKBMlArKdIVEg@jetpack": {minVersion: "1.24.1-signed.1-signed", installs: 281},
|
||||
"@90B817C8-8A5C-413B-9DDD-B2C61ED6E79A": {minVersion: "1.09", installs: 278},
|
||||
"smarterwiki@wikiatic.com": {minVersion: "5.2.1.1-signed.1-signed", installs: 278},
|
||||
"whatsapppanel@alejandrobrizuela.com.ar": {minVersion: "1.1.1.1-signed.1-signed", installs: 277},
|
||||
"lazarus@interclue.com": {minVersion: "2.3.1-signed.1-signed", installs: 275},
|
||||
"{DEDA1132-B316-11DD-8BC1-4E5D56D89593}": {minVersion: "0.18", installs: 274},
|
||||
"_h2Members_@free.calendarspark.com": {minVersion: "7.102.10.3641", installs: 273},
|
||||
"@youtubedownloadere": {minVersion: "0.0.1", installs: 273},
|
||||
"multirevenue@googlemail.com": {minVersion: "6.1.1", installs: 272},
|
||||
"_d9Members_@www.everydaylookup.com": {minVersion: "7.102.10.4140", installs: 271},
|
||||
"belgiumeid@eid.belgium.be": {minVersion: "1.0.21", installs: 271},
|
||||
"{62DD0A97-FDD4-421b-94A5-D1A9434450C7}": {minVersion: "3.1", installs: 270},
|
||||
"the-addon-bar@GeekInTraining-GiT": {minVersion: "3.2.9-compat-fixed-4", installs: 264},
|
||||
"@phextension": {minVersion: "6.0.2", installs: 262},
|
||||
"FunMediaTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 262},
|
||||
"{7f57cf46-4467-4c2d-adfa-0cba7c507e54}": {minVersion: "4.0.1", installs: 259},
|
||||
"safefacebook@bkav": {minVersion: "1.0.4", installs: 255},
|
||||
"content_blocker_6418E0D362104DADA084DC312DFA8ABC@kaspersky.com": {minVersion: "4.5.3.8", installs: 254},
|
||||
"virtual_keyboard_294FF26A1D5B455495946778FDE7CEDB@kaspersky.com": {minVersion: "4.5.3.8", installs: 254},
|
||||
"{B821BF60-5C2D-41EB-92DC-3E4CCD3A22E4}": {minVersion: "4.3.1.10", installs: 252},
|
||||
"@E9438230-A7DF-4D1F-8F2D-CA1D0F0F7924": {minVersion: "1.08.8.66", installs: 252},
|
||||
"jid1-6MGm94JnyY2VkA@jetpack": {minVersion: "2.1.8", installs: 250},
|
||||
"{20a82645-c095-46ed-80e3-08825760534b}": {minVersion: "1.3.1.1-signed", installs: 246},
|
||||
"{a192bf54-089f-4325-ac25-7eafcd17a342}": {minVersion: "3.2", installs: 246},
|
||||
"e389d8c2-5554-4ba2-a36e-ac7a57093130@gmail.com": {minVersion: "1.44.275", installs: 244},
|
||||
"yslow@yahoo-inc.com": {minVersion: "3.1.8.1-signed.1-signed", installs: 244},
|
||||
"avg@safeguard": {minVersion: "19.6.0.592", installs: 243},
|
||||
"@windscribeff": {minVersion: "0.1.43", installs: 242},
|
||||
"jid1-PBNne26X1Kn6hQ@jetpack": {minVersion: "3.3.3", installs: 240},
|
||||
"{53A03D43-5363-4669-8190-99061B2DEBA5}": {minVersion: "1.5.14", installs: 239},
|
||||
"@offersolymp": {minVersion: "0.0.2", installs: 238},
|
||||
"firefox@dotvpn.com": {minVersion: "1.0.2", installs: 238},
|
||||
"{62760FD6-B943-48C9-AB09-F99C6FE96088}": {minVersion: "4.2.9", installs: 236},
|
||||
"jid1-sNL73VCI4UB0Fw@jetpack": {minVersion: "2.1.4", installs: 236},
|
||||
"low_quality_flash@pie2k.com": {minVersion: "0.2.1-signed.1-signed", installs: 236},
|
||||
"jid1-l6VQSR2FeKnliQ@jetpack": {minVersion: "2.0.1-signed", installs: 235},
|
||||
"_5zMembers_@www.couponxplorer.com": {minVersion: "7.102.10.3738", installs: 234},
|
||||
"adonis.cuhk@gmail.com": {minVersion: "1.8.9.1-signed.1-signed", installs: 234},
|
||||
"_e1Members_@free.actionclassicgames.com": {minVersion: "7.38.8.45834", installs: 232},
|
||||
"{ea4637dc-e014-4c17-9c2c-879322d23268}": {minVersion: "2.1.1-signed.1-signed", installs: 229},
|
||||
"{4DC70064-89E2-4a55-8FC6-E8CDEAE3618C}": {minVersion: "0.7.7.1-signed.1-signed", installs: 228},
|
||||
"odyssey_crypto_control@odysseytec.com": {minVersion: "3.5", installs: 228},
|
||||
"seostatus@rubyweb": {minVersion: "1.5.9.1-signed.1-signed", installs: 228},
|
||||
"_apMembers_@free.puzzlegamesdaily.com": {minVersion: "7.102.10.4865", installs: 227},
|
||||
"@safesearchincognito": {minVersion: "0.1.8", installs: 226},
|
||||
"jid1-HfCj61J5q2gaGQ@jetpack": {minVersion: "1.0.3", installs: 224},
|
||||
"@stopads": {minVersion: "0.0.4", installs: 224},
|
||||
"dta3noaero@vano": {minVersion: "1.0.1", installs: 224},
|
||||
"rainbow@colors.org": {minVersion: "1.6.1-signed.1-signed", installs: 223},
|
||||
"{146f1820-2b0d-49ef-acbf-d85a6986e10c}": {minVersion: "0.1.9.3.1-signed.1-signed", installs: 222},
|
||||
"{b2bfe60c-eef8-4e20-8334-c53afdc1ffdd}": {minVersion: "3.2", installs: 222},
|
||||
"{b7870b41-bfb3-44cd-8cc2-e392e51b0874}": {minVersion: "3.8", installs: 222},
|
||||
"printPages2Pdf@reinhold.ripper": {minVersion: "0.1.9.3.1-signed", installs: 221},
|
||||
"YouTubetoALL@ALLPlayer.org": {minVersion: "0.8.5.1-signed.1-signed", installs: 221},
|
||||
"{7a526449-3a92-426f-8ca4-47439918f2b1}": {minVersion: "3.2", installs: 219},
|
||||
"jdwimqhayu@yahoo.com": {minVersion: "0.0.0.6", installs: 219},
|
||||
"{54FBE89E-C878-46bb-A064-AB327EE26EBC}": {minVersion: "3.8", installs: 214},
|
||||
"modernDownloadManager@teo.pl": {minVersion: "0.2.2", installs: 214},
|
||||
"{eb8fff7e-1dce-4f3f-a51d-d9513ed6bab4}": {minVersion: "3.8", installs: 211},
|
||||
"jid0-YQz0l1jthOIz179ehuitYAOdBEs@jetpack": {minVersion: "2.0.2", installs: 211},
|
||||
"{7e80e173-7e63-464e-8252-fe170b15c15a}": {minVersion: "2.3", installs: 210},
|
||||
"{35d6291e-1d4b-f9b4-c52f-77e6410d1326}": {minVersion: "4.11.1.0", installs: 209},
|
||||
"{3c59c791-aeec-44bb-af60-ff112eea18e3}": {minVersion: "3.2", installs: 209},
|
||||
"{90477448-b59c-48cd-98af-6a298cbc15d2}": {minVersion: "3.8", installs: 209},
|
||||
"{24d26487-6274-48b1-b500-22f24884f971}": {minVersion: "2.3", installs: 208},
|
||||
"{b7389dbc-6646-412f-bbd5-53168ee68a98}": {minVersion: "49", installs: 208},
|
||||
"{22181a4d-af90-4ca3-a569-faed9118d6bc}": {minVersion: "11.0.0.1181", installs: 207},
|
||||
"printpdf@pavlov.net": {minVersion: "0.76.1-signed.1-signed", installs: 207},
|
||||
"@com.virtualjame.disableads": {minVersion: "0.1.0", installs: 206},
|
||||
"{9AA46F4F-4DC7-4c06-97AF-6665170634FE}": {minVersion: "1.11.6.1-signed.1-signed", installs: 205},
|
||||
"tinyjsdebugger@enigmail.net": {minVersion: "1.1.5", installs: 204},
|
||||
"_foMembers_@free.flightsearchapp.com": {minVersion: "7.102.10.4176", installs: 202},
|
||||
"jid1-rs90nxQtPi3Asg@jetpack": {minVersion: "1.8.1-signed.1-signed", installs: 201},
|
||||
"vlcplaylist@helgatauscher.de": {minVersion: "0.8.1-signed.1-signed", installs: 201},
|
||||
"jid1-G80Ec8LLEbK5fQ@jetpack": {minVersion: "1.3.8", installs: 200},
|
||||
"_gpMembers_@free.mymapswizard.com": {minVersion: "7.102.10.4775", installs: 199},
|
||||
"BestMediaTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 199},
|
||||
"info@convert2mp3.net": {minVersion: "2.5.1-signed.1-signed", installs: 199},
|
||||
"partnerdefaults@mozilla.com": {minVersion: "1.0.1", installs: 199},
|
||||
"qwantcomforfirefox@jetpack": {minVersion: "3.0.28", installs: 199},
|
||||
"{65e41d20-f092-41b7-bb83-c6e8a9ab0f57}": {minVersion: "1.2.6", installs: 198},
|
||||
"amznUWL2@amazon.com": {minVersion: "1.11", installs: 197},
|
||||
"{1b80ae74-4912-44fc-9f27-30f9252a5ad7}": {minVersion: "2.3", installs: 197},
|
||||
"{c9b4cd26-6f0e-4972-a9e0-8b77e811aa8f}": {minVersion: "2.3", installs: 197},
|
||||
"shopcbtoolbar2@befrugal.com": {minVersion: "2013.3.23.1", installs: 197},
|
||||
"trafficlight@bitdefender.com": {minVersion: "0.2.23.1-signed.1-signed", installs: 197},
|
||||
"webrank-toolbar@probcomp.com": {minVersion: "4.4.1.1-signed.1-signed", installs: 197},
|
||||
"_4lMembers_@www.bibletriviatime.com": {minVersion: "7.102.10.4330", installs: 196},
|
||||
"xthunder@lshai.com": {minVersion: "1.3.4.1-signed.1-signed", installs: 196},
|
||||
"extension@hidemyass.com": {minVersion: "1.3.2", installs: 195},
|
||||
"jid1-MIAJd5BiK7V4Pw@jetpack": {minVersion: "0.9.1-signed.1-signed", installs: 195},
|
||||
"{51aa69f8-8825-4def-916a-a766c5e3c0fd}": {minVersion: "3.8", installs: 194},
|
||||
"{2bc72c53-9bde-4db2-8479-eda9a5e71f4e}": {minVersion: "3.2", installs: 193},
|
||||
"{a95d8332-e4b4-6e7f-98ac-20b733364387}": {minVersion: "1.0.5", installs: 191},
|
||||
"ocr@babylon.com": {minVersion: "1.1", installs: 191},
|
||||
"{d3b9472c-f8b1-4a10-935b-1087bac8417f}": {minVersion: "3.8", installs: 189},
|
||||
"windowpromo@dm73.net": {minVersion: "1.6", installs: 188},
|
||||
"alldownloader@link64": {minVersion: "1.00.17.1-signed.1-signed", installs: 187},
|
||||
"{3e0e7d2a-070f-4a47-b019-91fe5385ba79}": {minVersion: "3.6.5.2", installs: 185},
|
||||
"jid1-vFmnfCkyf5VeSA@jetpack": {minVersion: "0.4.0", installs: 185},
|
||||
"@greatdealz": {minVersion: "0.0.3", installs: 184},
|
||||
"superstart@enjoyfreeware.org": {minVersion: "7.4.0.1-signed", installs: 183},
|
||||
"{c2fc3c2b-a65a-453c-bf95-101fde56ed1d}": {minVersion: "2.3", installs: 182},
|
||||
"{53152e75-fd90-472f-9d30-5cba3679eab9}": {minVersion: "48.3", installs: 180},
|
||||
"jid0-raWjElI57dRa4jx9CCiYm5qZUQU@jetpack": {minVersion: "3.0.12.1.1-signed.1-signed", installs: 180},
|
||||
"_ivMembers_@free.simplepictureedit.com": {minVersion: "7.102.10.14166", installs: 179},
|
||||
"jid1-wKRSK9TpFpr9Hw@jetpack": {minVersion: "0.92", installs: 179},
|
||||
"emailExtractor@penzil.com": {minVersion: "1.3.1-signed.1-signed", installs: 178},
|
||||
"{60B7679C-BED9-11E5-998D-8526BB8E7F8B}": {minVersion: "6.3", installs: 177},
|
||||
"@pdfit": {minVersion: "0.1.9", installs: 177},
|
||||
"jid1-6AyZ1PQXsR9LgQ@jetpack": {minVersion: "0.2.1", installs: 177},
|
||||
"_6oMembers_@free.heroicplay.com": {minVersion: "7.38.8.46626", installs: 175},
|
||||
"{4BBDD651-70CF-4821-84F8-2B918CF89CA3}": {minVersion: "8.9.3.1", installs: 173},
|
||||
"jid1-GeRCnsiDhZiTvA@jetpack": {minVersion: "1.0.3", installs: 172},
|
||||
"jid0-zs24wecdcQo0Lp18D7QOV4WSZFo@jetpack": {minVersion: "0.2.1-signed.1-signed", installs: 171},
|
||||
"{c50ca3c4-5656-43c2-a061-13e717f73fc8}": {minVersion: "5.0.1.48.1-signed.1-signed", installs: 170},
|
||||
"selenium_ide_buttons@egarracingteam.com.ar": {minVersion: "1.2.0.1-signed.1-signed", installs: 170},
|
||||
"WSVCU@Wondershare.com": {minVersion: "7.1.0", installs: 169},
|
||||
"{4cc4a13b-94a6-7568-370d-5f9de54a9c7f}": {minVersion: "2.7.1-signed.1-signed", installs: 168},
|
||||
"{aa84ce40-4253-a00a-8cd6-0800200f9a67}": {minVersion: "3.12.0", installs: 168},
|
||||
"FasterFox_Lite@BigRedBrent": {minVersion: "3.9.9Lite.1-signed.1-signed", installs: 167},
|
||||
"{6cc0f0f7-a6e2-4834-9682-24de2229b51e}": {minVersion: "23.6", installs: 166},
|
||||
"{b749fc7c-e949-447f-926c-3f4eed6accfe}": {minVersion: "0.7.1.1.1-signed.1-signed", installs: 166},
|
||||
"@mendeleyimporter": {minVersion: "1.6.8", installs: 166},
|
||||
"ALone-live@ya.ru": {minVersion: "1.4.11", installs: 166},
|
||||
"{4093c4de-454a-4329-8aff-c6b0b123c386}": {minVersion: "0.8.14.1-signed.1-signed", installs: 165},
|
||||
"cookiemgr@jayapal.com": {minVersion: "5.12", installs: 164},
|
||||
"touchenex@raon.co.kr": {minVersion: "1.0.1.11", installs: 163},
|
||||
"{b0e1b4a6-2c6f-4e99-94f2-8e625d7ae255}": {minVersion: "3.5.0.1-signed.1-signed", installs: 162},
|
||||
"isreaditlater@ideashower.com": {minVersion: "3.0.6.1-signed", installs: 161},
|
||||
"safesearchplus@avira.com": {minVersion: "1.4.1.371", installs: 161},
|
||||
"_e0Members_@www.downshotfree.com": {minVersion: "7.102.10.3833", installs: 159},
|
||||
"LDSI_plashcor@gmail.com": {minVersion: "1.1.0.3", installs: 159},
|
||||
"jid1-9ETkKdBARv7Iww@jetpack": {minVersion: "0.20.1-signed.1-signed", installs: 157},
|
||||
"jid1-CGxMej0nDJTjwQ@jetpack": {minVersion: "1.0.1-signed.1-signed", installs: 157},
|
||||
"{00f7ab9f-62f4-4145-b2f9-38d579d639f6}": {minVersion: "49", installs: 156},
|
||||
"googledictionary@toptip.ca": {minVersion: "7.5", installs: 156},
|
||||
"shopearn@prodege.com": {minVersion: "219", installs: 156},
|
||||
"fvdmedia@gmail.com": {minVersion: "11.0.1", installs: 155},
|
||||
"magicplayer_unlisted@acestream.org": {minVersion: "1.1.42", installs: 155},
|
||||
"{0538E3E3-7E9B-4d49-8831-A227C80A7AD3}": {minVersion: "2.2.2.1-signed.1-let-fixed.1-signed", installs: 154},
|
||||
"{73007fef-a6e0-47d3-b4e7-dfc116ed6f65}": {minVersion: "1.15.1-signed.1-signed", installs: 153},
|
||||
"{cd617372-6743-4ee4-bac4-fbf60f35719e}": {minVersion: "2.0.1-signed.1-signed", installs: 152},
|
||||
"TopSecurityTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 152},
|
||||
"jid1-hDf2iQXGiUjzGQ@jetpack": {minVersion: "2.5.0", installs: 151},
|
||||
"_dnMembers_@www.free.webmailworld.com": {minVersion: "7.102.10.5052", installs: 149},
|
||||
"jid1-rrMTK7JqsxNOeQ@jetpack": {minVersion: "2.1.0", installs: 149},
|
||||
"jid1-XgC5trUcILmXBw@jetpack": {minVersion: "2.0.3", installs: 149},
|
||||
"online_banking_69A4E213815F42BD863D889007201D82@kaspersky.com": {minVersion: "4.5.3.8", installs: 148},
|
||||
"jid1-AXn9cXcB4fD1QQ@jetpack": {minVersion: "0.7.4", installs: 148},
|
||||
"feedly@devhd": {minVersion: "16.0.528.1-signed.1-signed", installs: 147},
|
||||
"{6E727987-C8EA-44DA-8749-310C0FBE3C3E}": {minVersion: "2.0.0.11", installs: 146},
|
||||
"{1082eb84-f0f2-11e5-8e18-9bb85ab7992e}": {minVersion: "1.07", installs: 146},
|
||||
"{c151d79e-e61b-4a90-a887-5a46d38fba99}": {minVersion: "2.8.8", installs: 146},
|
||||
"public.proartex@gmail.com": {minVersion: "1.1.3", installs: 145},
|
||||
"jid1-8J7ayxTha4KqKQ@jetpack": {minVersion: "1.1.1-signed.1-signed", installs: 144},
|
||||
"stealthyextension@gmail.com": {minVersion: "3.0.1.1-signed", installs: 144},
|
||||
"_beMembers_@free.dailylocalguide.com": {minVersion: "7.38.9.7920", installs: 143},
|
||||
"mytube@ashishmishra.in": {minVersion: "0.979.1-signed.1-signed", installs: 142},
|
||||
"@A3592ADB-854A-443A-854E-EB92130D470D": {minVersion: "1.08.8.88", installs: 139},
|
||||
"FunkyTVTabs-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 139},
|
||||
"jid1-QpHD8URtZWJC2A@jetpack": {minVersion: "4.3.0", installs: 138},
|
||||
"savedeo-video-downloader@fczbkk.com": {minVersion: "0.4.1.1-signed.1-signed", installs: 137},
|
||||
"toolbar@shopathome.com": {minVersion: "8.20.3.1", installs: 137},
|
||||
"_dyMembers_@www.dezipper.com": {minVersion: "7.102.10.3775", installs: 135},
|
||||
"jid0-zXo3XFGyiDalgkeEO4UYJTUwo2I@jetpack": {minVersion: "1.0.0", installs: 134},
|
||||
"{d57c9ff1-6389-48fc-b770-f78bd89b6e8a}": {minVersion: "1.46.1-signed.1-signed", installs: 133},
|
||||
"@searchlock-fx": {minVersion: "1.1.6", installs: 133},
|
||||
"dm@jetpack": {minVersion: "0.0.2", installs: 133},
|
||||
"proxyselector@mozilla.org": {minVersion: "1.31.1-signed.1-signed", installs: 133},
|
||||
"{065829BC-17B5-4C0B-9429-3173C361092E}": {minVersion: "1.0.8", installs: 132},
|
||||
"{ada4b710-8346-4b82-8199-5de2b400a6ae}": {minVersion: "2.1.5.5.3", installs: 132},
|
||||
"readable@evernote.com": {minVersion: "10.2.1.7.1-signed", installs: 131},
|
||||
"{d48a39ba-8f80-4fce-8ee1-bc710561c55d}": {minVersion: "3.1.0.1-signed.1-signed", installs: 131},
|
||||
"autorefresh@plugin": {minVersion: "1.0.2.1-signed.1-signed", installs: 130},
|
||||
"SafeBrowseSearch-the-extension1@mozilla.com": {minVersion: "0.1.2", installs: 130},
|
||||
"browsermodulecorp@browcorporation.org": {minVersion: "2.3", installs: 129},
|
||||
"wisestamp@wisestamp.com": {minVersion: "4.14.20", installs: 127},
|
||||
"_63Members_@www.aplusgamer.com": {minVersion: "7.38.8.45832", installs: 126},
|
||||
"bestproxyswitcher@bestproxyswitcher.com": {minVersion: "3.4.6.1-signed.1-signed", installs: 126},
|
||||
"jid1-AVgCeF1zoVzMjA@jetpack": {minVersion: "0.9.5.6", installs: 126},
|
||||
"{ce7e73df-6a44-4028-8079-5927a588c948}": {minVersion: "1.1.4", installs: 125},
|
||||
"{E71B541F-5E72-5555-A47C-E47863195841}": {minVersion: "3.0.3", installs: 125},
|
||||
"{F5DDF39C-9293-4d5e-9AA8-E04E6DD5E9B4}": {minVersion: "1.6.3.1-signed.1-signed", installs: 125},
|
||||
"@simplepopupblocker": {minVersion: "1.2.1", installs: 125},
|
||||
"commonfix@mozillaonline.com": {minVersion: "0.13", installs: 125},
|
||||
"searchme@mybrowserbar.com": {minVersion: "2.8", installs: 125},
|
||||
"_4wMembers_@www.retrogamer.com": {minVersion: "7.38.8.46604", installs: 124},
|
||||
"{71328583-3CA7-4809-B4BA-570A85818FBB}": {minVersion: "0.8.6.3.1-let-fixed", installs: 123},
|
||||
"dmremote@westbyte.com": {minVersion: "1.9.3", installs: 123},
|
||||
"@google-translate-menu": {minVersion: "1.0.1", installs: 122},
|
||||
"_aaMembers_@free.eliteunzip.com": {minVersion: "7.39.8.50909", installs: 121},
|
||||
"{8620c15f-30dc-4dba-a131-7c5d20cf4a29}": {minVersion: "3.9", installs: 121},
|
||||
"{eb4b28c8-7f2d-4327-a00c-40de4299ba44}": {minVersion: "1.7", installs: 121},
|
||||
"flashlight@stephennolan.com.au": {minVersion: "1.2.1-signed.1-signed", installs: 121},
|
||||
"useragentoverrider@qixinglu.com": {minVersion: "0.4.1", installs: 121},
|
||||
"{1B33E42F-EF14-4cd3-B6DC-174571C4349C}": {minVersion: "4.7", installs: 120},
|
||||
"_dxMembers_@www.download-freemaps.com": {minVersion: "7.38.8.46371", installs: 120},
|
||||
"{95ab36d4-fb6f-47b0-8b8d-e5f3bd547953}": {minVersion: "4.20.13.1-signed.1-signed", installs: 120},
|
||||
"FirefoxAddon@similarWeb.com": {minVersion: "4.0.6", installs: 120},
|
||||
"flashstopper@byo.co.il": {minVersion: "1.4.2", installs: 120},
|
||||
"{15e67a59-bd3d-49ae-90dd-b3d3fd14c2ed}": {minVersion: "1.0.3.1-signed.1-signed", installs: 119},
|
||||
"{c37bac34-849a-4d28-be41-549b2c76c64e}": {minVersion: "2.6", installs: 119},
|
||||
"{03B08592-E5B4-45ff-A0BE-C1D975458688}": {minVersion: "1.1.1-signed.1-signed", installs: 118},
|
||||
"newtabgoogle@graememcc.co.uk": {minVersion: "1.0.2.1-signed.1-signed", installs: 118},
|
||||
"SocialNewtabs-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 118},
|
||||
"@kikikokicicidada": {minVersion: "2.1.2", installs: 117},
|
||||
"{9D6218B8-03C7-4b91-AA43-680B305DD35C}": {minVersion: "4.0.5", installs: 116},
|
||||
"extension@one-tab.com": {minVersion: "1.17.0", installs: 116},
|
||||
"{22119944-ED35-4ab1-910B-E619EA06A115}": {minVersion: "7.9.21.5", installs: 115},
|
||||
"admin@hide-my-ip.org": {minVersion: "9.6.3", installs: 115},
|
||||
"bdwteffv19@bitdefender.com": {minVersion: "2.2.1", installs: 115},
|
||||
"exif_viewer@mozilla.doslash.org": {minVersion: "2.00.1-signed.1-signed", installs: 115},
|
||||
"MyStartab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 115},
|
||||
"coralietab@mozdev.org": {minVersion: "2.04.20110724.1-signed.1-signed", installs: 113},
|
||||
"gaurangnshah@gmail.com": {minVersion: "1.3.2.1-signed.1-signed", installs: 113},
|
||||
"ImagePicker@topolog.org": {minVersion: "1.9.4", installs: 113},
|
||||
"{d49a148e-817e-4025-bee3-5d541376de3b}": {minVersion: "3.1.1-signed.1-signed", installs: 112},
|
||||
"firebug@tools.sitepoint.com": {minVersion: "1.6.1-signed.1-signed", installs: 111},
|
||||
"add-to-searchbox@maltekraus.de": {minVersion: "2.9", installs: 110},
|
||||
"captiondownloader@hiephm.com": {minVersion: "2.3.1-signed.1-signed", installs: 110},
|
||||
"jid1-LYopfl0r00ZV5k@jetpack": {minVersion: "1.0.1-signed.1-signed", installs: 110},
|
||||
"{7CA9CF31-1C73-46CD-8377-85AB71EA771F}": {minVersion: "5.0.12", installs: 109},
|
||||
"jid1-HdwPLukcGQeOSh@jetpack": {minVersion: "1.2.3", installs: 108},
|
||||
"{0AA9101C-D3C1-4129-A9B7-D778C6A17F82}": {minVersion: "2.09.1-signed", installs: 107},
|
||||
"CookiesIE@yahoo.com": {minVersion: "1.0.1-signed.1-signed", installs: 107},
|
||||
"selenium-expert_selenium-ide@Samit.Badle": {minVersion: "0.25.1-signed.1-signed", installs: 107},
|
||||
"{19EB90DC-A456-458b-8AAC-616D91AAFCE1}": {minVersion: "1.0.1-signed", installs: 105},
|
||||
"application@itineraire.info": {minVersion: "4.5.0", installs: 105},
|
||||
"rest-easy@quickmediasolutions.com": {minVersion: "0.3.1.1-signed", installs: 105},
|
||||
"TopSocialHub-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 105},
|
||||
"{7affbfae-c4e2-4915-8c0f-00fa3ec610a1}": {minVersion: "6.36.32", installs: 104},
|
||||
"azhang@cloudacl.com": {minVersion: "0.19.6.9.1-signed.1-signed", installs: 104},
|
||||
"FunCyberTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 104},
|
||||
"SkipScreen@SkipScreen": {minVersion: "0.7.2.1-signed.1-signed", installs: 104},
|
||||
"toolbar@seomoz.org": {minVersion: "3.1.18", installs: 104},
|
||||
"{8b86149f-01fb-4842-9dd8-4d7eb02fd055}": {minVersion: "0.26.1-signed.1-signed", installs: 103},
|
||||
"fbp@fbpurity.com": {minVersion: "9.3.2.1-signed", installs: 103},
|
||||
"jid1-V8ev2melBDV3qQ@jetpack": {minVersion: "1.0.12.1-signed.1-signed", installs: 103},
|
||||
"_fvMembers_@free.directionsace.com": {minVersion: "7.102.10.3790", installs: 102},
|
||||
"{b6b1a201-b252-484f-b9fe-68efbb273fbd}": {minVersion: "1.10.1-signed.1-signed", installs: 102},
|
||||
"flashfirebug@o-minds.com": {minVersion: "4.9.1", installs: 102},
|
||||
"_ebMembers_@download.metrohotspot.com": {minVersion: "7.102.10.4735", installs: 101},
|
||||
"{2e17e2b2-b8d4-4a67-8d7b-fafa6cc9d1d0}": {minVersion: "1.2.7.0.1-signed.1-signed", installs: 101},
|
||||
"{ea61041c-1e22-4400-99a0-aea461e69d04}": {minVersion: "0.2.4.1-signed.1-signed", installs: 101},
|
||||
"rapportive@rapportive.com": {minVersion: "1.4.0.1.1-signed.1-signed", installs: 101},
|
||||
"_dvMembers_@www.testinetspeed.com": {minVersion: "7.38.8.45918", installs: 100},
|
||||
"{9aad3da6-6c46-4ef0-9109-6df5eaaf597c}": {minVersion: "1.4.1.1-signed.1-signed", installs: 100},
|
||||
"{c2b1f3ae-5cd5-49b7-8a0c-2c3bcbbbb294}": {minVersion: "1.1.1-signed.1-signed", installs: 100},
|
||||
"jid0-w1UVmoLd6VGudaIERuRJCPQx1dQ@jetpack": {minVersion: "1.6.8.1-signed", installs: 100},
|
||||
"_cxMembers_@www.autopcbackup.com": {minVersion: "7.102.10.3597", installs: 99},
|
||||
"vpn@hide-my-ip.org": {minVersion: "10.6.2", installs: 99},
|
||||
"{1a5dabbd-0e74-41da-b532-a364bb552cab}": {minVersion: "1.0.9.1-signed", installs: 98},
|
||||
"FirePHPExtension-Build@firephp.org": {minVersion: "0.7.4.1-signed.1-signed", installs: 98},
|
||||
"jid1-UXDr6c69BeyPVw@jetpack": {minVersion: "0.8.2", installs: 98},
|
||||
"TopSafeTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 98},
|
||||
"{3b56bcc7-54e5-44a2-9b44-66c3ef58c13e}": {minVersion: "0.9.7.4", installs: 97},
|
||||
"autoreload@yz.com": {minVersion: "1.21", installs: 97},
|
||||
"manish.p05@gmail.com": {minVersion: "12.9", installs: 97},
|
||||
"videoresumer@jetpack": {minVersion: "1.1.4", installs: 97},
|
||||
"@Radio": {minVersion: "0.2.0", installs: 96},
|
||||
"_hfMembers_@free.everydaymanuals.com": {minVersion: "7.102.10.4142", installs: 96},
|
||||
"jid0-jJRRRBMgoShUhb07IvnxTBAl29w@jetpack": {minVersion: "2.0.4", installs: 96},
|
||||
"rikaichan-jpen@polarcloud.com": {minVersion: "2.01.160101", installs: 96},
|
||||
"{7c6cdf7c-8ea8-4be7-ae5a-0b3effe14d66}": {minVersion: "49.1", installs: 95},
|
||||
"{FDBAD97E-A258-4fe3-9CF6-60CF386C4422}": {minVersion: "2.0.1.6", installs: 95},
|
||||
"intgcal@egarracingteam.com.ar": {minVersion: "1.5.1", installs: 95},
|
||||
"MediaNewTab-the-extension1@mozilla.com": {minVersion: "0.1.6", installs: 95},
|
||||
"{9EB34849-81D3-4841-939D-666D522B889A}": {minVersion: "2.4.0.157", installs: 94},
|
||||
"{158d7cb3-7039-4a75-8e0b-3bd0a464edd2}": {minVersion: "2.7.1-signed.1-signed", installs: 94},
|
||||
"jid1-ach2kaGSshPJCg@jetpack": {minVersion: "0.1.1-signed.1-signed", installs: 94},
|
||||
"jid1-cwbvBTE216jjpg@jetpack": {minVersion: "2.1.0.1-signed.1-signed", installs: 94},
|
||||
"{f36c6cd1-da73-491d-b290-8fc9115bfa55}": {minVersion: "3.0.9.1-signed.1-let-fixed.1-signed", installs: 93},
|
||||
"dmpluginff@westbyte.com": {minVersion: "1.4.12", installs: 93},
|
||||
"firefox@serptrends.com": {minVersion: "0.8.14", installs: 93},
|
||||
"panel-plugin@effectivemeasure.com": {minVersion: "4.0.0", installs: 93},
|
||||
"_evMembers_@www.free.bestbackground.com": {minVersion: "7.102.10.3607", installs: 92},
|
||||
"canitbecheaper@trafficbroker.co.uk": {minVersion: "3.9.78", installs: 92},
|
||||
"favorites_selenium-ide@Samit.Badle": {minVersion: "2.0.1-signed.1-signed", installs: 92},
|
||||
"{5F590AA2-1221-4113-A6F4-A4BB62414FAC}": {minVersion: "0.45.8.20130519.3.1-signed.1-signed", installs: 90},
|
||||
"{3e9bb2a7-62ca-4efa-a4e6-f6f6168a652d}": {minVersion: "2.7.7.1-signed.1-signed", installs: 90},
|
||||
"{ab4b5718-3998-4a2c-91ae-18a7c2db513e}": {minVersion: "1.2.0.1-signed.1-signed", installs: 90},
|
||||
"2020Player_WEB@2020Technologies.com": {minVersion: "5.0.94.0", installs: 90},
|
||||
"translator@dontfollowme.net": {minVersion: "2.0.5", installs: 90},
|
||||
"YouTubeAutoReplay@arikv.com": {minVersion: "3.3.1-signed.1-signed", installs: 90},
|
||||
"{a949831f-d9c0-45ae-8c60-91c2a86fbfb6}": {minVersion: "0.2.1-signed.1-signed", installs: 89},
|
||||
"@vpn-unlimited-secure-proxy": {minVersion: "4.4", installs: 89},
|
||||
"jid1-JcGokIiQyjoBAQ@jetpack": {minVersion: "0.6.1-signed.1-signed", installs: 89},
|
||||
"_73Members_@www.easyhomedecorating.com": {minVersion: "7.102.10.4129", installs: 88},
|
||||
"{065ee92a-ad57-42a2-b6d5-466b6fd8e24d}": {minVersion: "0.11.6.1-signed.1-signed", installs: 88},
|
||||
"{455D905A-D37C-4643-A9E2-F6FEFAA0424A}": {minVersion: "0.8.17.1-signed.1-signed", installs: 88},
|
||||
"{7eb3f691-25b4-4a85-9038-9e57e2bcd537}": {minVersion: "0.4.4.1-signed.1-signed", installs: 88},
|
||||
"FunSocialTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 88},
|
||||
"Lucifox@lucidor.org": {minVersion: "0.9.13", installs: 88},
|
||||
"YourMediaTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 88},
|
||||
"youtube-video-player@lejenome.me": {minVersion: "0.2.38.1-signed.1-signed", installs: 88},
|
||||
"_hgMembers_@free.atozmanuals.com": {minVersion: "7.102.10.3604", installs: 87},
|
||||
"abb-acer@amazon.com": {minVersion: "10.161.13.1002", installs: 87},
|
||||
"gmail_panel@alejandrobrizuela.com.ar": {minVersion: "1.2.0", installs: 87},
|
||||
"izer@camelcamelcamel.com": {minVersion: "2.8.2", installs: 87},
|
||||
"tvnewtab-the-extension1@mozilla.com": {minVersion: "0.1.5", installs: 87},
|
||||
"vlc_shortcut@kosan.kosan": {minVersion: "0.8.3.0", installs: 87},
|
||||
"youtubeunblocker@unblocker.yt": {minVersion: "0.6.20", installs: 86},
|
||||
"email@jetpack": {minVersion: "0.0.16", installs: 86},
|
||||
"extensions@gismeteo.com": {minVersion: "5.1.0.2", installs: 86},
|
||||
"idaremote@westbyte.com": {minVersion: "1.6.3", installs: 86},
|
||||
"{725fc0a6-1f6b-4cf9-ae17-748d111dc16d}": {minVersion: "1.1.0", installs: 85},
|
||||
"jid1-461B0PwxL3oTt1@jetpack": {minVersion: "0.2.1-signed.1-signed", installs: 85},
|
||||
"webdavlauncher@benryan.com": {minVersion: "1.1.0", installs: 85},
|
||||
"jid1-ZM3BerwS6FsQAg@jetpack": {minVersion: "0.4.1-signed", installs: 84},
|
||||
"_fwMembers_@free.howtosuite.com": {minVersion: "7.102.10.4280", installs: 84},
|
||||
"{023e9ca0-63f3-47b1-bcb2-9badf9d9ef28}": {minVersion: "4.4.3.1-signed.1-signed", installs: 84},
|
||||
"{25A1388B-6B18-46c3-BEBA-A81915D0DE8F}": {minVersion: "1.7.8.5.1-signed.1-signed", installs: 84},
|
||||
"{75493B06-1504-4976-9A55-B6FE240FF0BF}": {minVersion: "3.4.0.0", installs: 84},
|
||||
"facepaste.firefox.addon@azabani.com": {minVersion: "2.91", installs: 84},
|
||||
"jid1-cplLTTY501TB2Q@jetpack": {minVersion: "0.5.1", installs: 84},
|
||||
"_d1Members_@free.mysocialshortcut.com": {minVersion: "7.102.10.4792", installs: 83},
|
||||
"{761a54f1-8ccf-4112-9e48-dbf72adf6244}": {minVersion: "2.3.1-signed.1-signed", installs: 83},
|
||||
"{BBB77B49-9FF4-4d5c-8FE2-92B1D6CD696C}": {minVersion: "2.0.0.1083", installs: 83},
|
||||
"{a3a5c777-f583-4fef-9380-ab4add1bc2a2}": {minVersion: "2.1.4", installs: 82},
|
||||
"{eb80b076-a444-444c-a590-5aee5d977d80}": {minVersion: "2.6.18", installs: 82},
|
||||
"KVAllmytube@KeepVid.com": {minVersion: "4.10.0", installs: 82},
|
||||
"lumerias-instagram@lumerias.com": {minVersion: "1.3", installs: 82},
|
||||
"omnibar@ajitk.com": {minVersion: "0.7.28.20141004.1-signed.1-signed", installs: 81},
|
||||
"@autofillanyforms-easytatkal": {minVersion: "7.51.0", installs: 81},
|
||||
"@youtuberightclick": {minVersion: "0.0.3", installs: 81},
|
||||
"autoproxy@autoproxy.org": {minVersion: "0.4b2.2013051811.1-signed.1-let-fixed.1-signed", installs: 80},
|
||||
"{e33788ea-0bb9-4502-9c77-bdc551afc8ad}": {minVersion: "1.0.4", installs: 80},
|
||||
"dmmm@westbyte.com": {minVersion: "1.3.4", installs: 80},
|
||||
"easycopy@smokyink.com": {minVersion: "2.7.0", installs: 80},
|
||||
"jid1-LelsJ0Oz0rt71A@jetpack": {minVersion: "2.0.0", installs: 80},
|
||||
"_f7Members_@download.smsfrombrowser.com": {minVersion: "7.38.8.45917", installs: 79},
|
||||
"{6614d11d-d21d-b211-ae23-815234e1ebb5}": {minVersion: "3.9.13", installs: 79},
|
||||
"FunTvTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 79},
|
||||
"{4204c864-50bf-467a-95b3-0912b7f15869}": {minVersion: "1.2.00.1-signed.1-signed", installs: 78},
|
||||
"{987311C6-B504-4aa2-90BF-60CC49808D42}": {minVersion: "3.1-signed.1-signed", installs: 78},
|
||||
"uploader@adblockfilters.mozdev.org": {minVersion: "2.1.1-signed.1-let-fixed.1-signed", installs: 77},
|
||||
"PageRank@addonfactory.in": {minVersion: "2.0.1-signed.1-signed", installs: 77},
|
||||
"restartbutton@strk.jp": {minVersion: "0.1.5.1-signed.1-signed", installs: 77},
|
||||
"text2voice@vik.josh": {minVersion: "1.15", installs: 77},
|
||||
"_dpMembers_@free.findyourmaps.com": {minVersion: "7.102.10.4185", installs: 76},
|
||||
"53ffxtbr@www.dailyfitnesscenter.com": {minVersion: "7.36.8.15623", installs: 76},
|
||||
"gary@builtwith.com": {minVersion: "1.9.6.1-signed.1-signed", installs: 76},
|
||||
"idamm@westbyte.com": {minVersion: "1.3.2", installs: 76},
|
||||
"jid1-3gu11JeYBiIuJA@jetpack": {minVersion: "3.1.1", installs: 76},
|
||||
"jid1-zV8eHYwTDNUtwQ@jetpack": {minVersion: "1.0.4", installs: 76},
|
||||
"nst@neiron.ru": {minVersion: "7.3.0.2", installs: 76},
|
||||
"service@touchpdf.com": {minVersion: "1.15.1-signed.1-signed", installs: 76},
|
||||
"{02450954-cdd9-410f-b1da-db804e18c671}": {minVersion: "0.96.3.1-signed.1-signed", installs: 75},
|
||||
"{4176DFF4-4698-11DE-BEEB-45DA55D89593}": {minVersion: "0.8.50.1-signed.1-signed", installs: 75},
|
||||
"{DAD0F81A-CF67-4eed-98D6-26F6E47274CA}": {minVersion: "1.8.1-signed.1-signed", installs: 75},
|
||||
"dealxplorermysites770@yahoo.com": {minVersion: "0.0.0.1", installs: 75},
|
||||
"firefox@online-convert.com": {minVersion: "1.4.1-signed.1-signed", installs: 75},
|
||||
"jid1-zmgYgiQPXJtjNA@jetpack": {minVersion: "1.23", installs: 75},
|
||||
"_evMembers_@www.bestbackground.com": {minVersion: "7.38.9.7654", installs: 74},
|
||||
"dmbarff@westbyte.com": {minVersion: "1.5.11", installs: 74},
|
||||
"inf@youtube-mp3.video": {minVersion: "0.1", installs: 74},
|
||||
"jid1-e7w0SHT82Gv9qA@jetpack": {minVersion: "1.2", installs: 74},
|
||||
"NewTabTVCool-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 74},
|
||||
"{E173B749-DB5B-4fd2-BA0E-94ECEA0CA55B}": {minVersion: "7.4.1-signed", installs: 73},
|
||||
"{9BAE5926-8513-417d-8E47-774955A7C60D}": {minVersion: "1.1.1d.1-signed.1-signed", installs: 73},
|
||||
"{3cc6c6ba-654c-417e-a8af-6997ac388ae1}": {minVersion: "49", installs: 72},
|
||||
"{daf44bf7-a45e-4450-979c-91cf07434c3d}": {minVersion: "2.0.5", installs: 72},
|
||||
"{dbac9680-d559-4cd4-9765-059879e8c467}": {minVersion: "5.0.5", installs: 72},
|
||||
"application@recettes.net": {minVersion: "4.5.0", installs: 72},
|
||||
"idapluginff@westbyte.com": {minVersion: "1.5.9", installs: 71},
|
||||
"imgflashblocker@shimon.chohen": {minVersion: "0.7.1-signed.1-signed", installs: 71},
|
||||
"inspector@mozilla.org": {minVersion: "2.0.16.1-signed", installs: 71},
|
||||
"jid1-ReWlW1efOwaQJQ@jetpack": {minVersion: "1.1.2", installs: 71},
|
||||
"youtubedownloader@trafficterminal.com": {minVersion: "1.0.1.1-signed.1-signed", installs: 71},
|
||||
"FavIconReloader@mozilla.org": {minVersion: "0.8.1-signed", installs: 70},
|
||||
"_2bMembers_@www.bettercareersearch.com": {minVersion: "7.38.8.45828", installs: 70},
|
||||
"{5e594888-3e8e-47da-b2c6-b0b545112f84}": {minVersion: "1.3.18", installs: 70},
|
||||
"@greatdealzu": {minVersion: "0.0.3", installs: 70},
|
||||
"86ffxtbr@download.yourvideochat.com": {minVersion: "7.36.8.15938", installs: 70},
|
||||
"google@hitachi.com": {minVersion: "0.3.1-signed.1-signed", installs: 70},
|
||||
"{6e84150a-d526-41f1-a480-a67d3fed910d}": {minVersion: "1.5.6.1-signed.1-signed", installs: 69},
|
||||
"firepicker@thedarkone": {minVersion: "1.4.3.1-signed.1-signed", installs: 69},
|
||||
"jid0-AocRXUCRsLTCYvn6bgJERnwfuqw@jetpack": {minVersion: "2.8.3.1-signed.1-signed", installs: 69},
|
||||
"nortonsecurity@symantec.com": {minVersion: "7.2.0f90", installs: 69},
|
||||
"{ef4e370e-d9f0-4e00-b93e-a4f274cfdd5a}": {minVersion: "1.4.10.1-signed", installs: 68},
|
||||
"{d4e0dc9c-c356-438e-afbe-dca439f4399d}": {minVersion: "49.1", installs: 68},
|
||||
"{E6C93316-271E-4b3d-8D7E-FE11B4350AEB}": {minVersion: "2.1.25.1-signed.1-signed", installs: 68},
|
||||
"{fa8476cf-a98c-4e08-99b4-65a69cb4b7d4}": {minVersion: "1.7.6.1", installs: 68},
|
||||
"simplesiteblocker@example.com": {minVersion: "1.1.1-signed.1-signed", installs: 68},
|
||||
"_fpMembers_@free.passwordlogic.com": {minVersion: "7.102.10.4853", installs: 67},
|
||||
"{6e764c17-863a-450f-bdd0-6772bd5aaa18}": {minVersion: "1.0.3.1-signed.1-signed", installs: 67},
|
||||
"adbeaver@adbeaver.org": {minVersion: "0.7.2.9", installs: 67},
|
||||
"application2@allo-pages.fr": {minVersion: "4.5.0", installs: 67},
|
||||
"arf3@getcartt.com": {minVersion: "1.2.3", installs: 67},
|
||||
"clearcache@michel.de.almeida": {minVersion: "2.0.1.1-signed.1-signed", installs: 67},
|
||||
"fbmessengerpanel@alejandrobrizuela.com.ar": {minVersion: "1.0.3.1-signed.1-signed", installs: 67},
|
||||
"tilt@mozilla.com": {minVersion: "1.0.1.1-signed.1-signed", installs: 67},
|
||||
"toolbar_AVIRA-V7@apn.ask.com": {minVersion: "127.25", installs: 67},
|
||||
"{524B8EF8-C312-11DB-8039-536F56D89593}": {minVersion: "4.91.0.0", installs: 66},
|
||||
"{9d1f059c-cada-4111-9696-41a62d64e3ba}": {minVersion: "0.17.0.1", installs: 66},
|
||||
"{B068AC18-0121-4e67-9A7E-6386F93F4F7A}": {minVersion: "2.4", installs: 66},
|
||||
"@lottadealsun": {minVersion: "0.0.1", installs: 66},
|
||||
"@thebestyoutubedownloader": {minVersion: "1.0.0.7", installs: 66},
|
||||
"TopSocialTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 66},
|
||||
"{11483926-db67-4190-91b1-ef20fcec5f33}": {minVersion: "0.4.9.1", installs: 65},
|
||||
"{8AA36F4F-6DC7-4c06-77AF-5035170634FE}": {minVersion: "2016.9.16", installs: 65},
|
||||
"dam@tensons.com": {minVersion: "5.0.7", installs: 65},
|
||||
"jid1-D7momAzRw417Ag@jetpack": {minVersion: "4.5.13", installs: 65},
|
||||
"support@videoadd.ru": {minVersion: "2.8.1.1-signed.1-signed", installs: 65},
|
||||
"{95322c08-05ff-4f3c-85fd-8ceb821988dd}": {minVersion: "49", installs: 64},
|
||||
"AllMyTube@Wondershare.com": {minVersion: "4.9.1", installs: 64},
|
||||
"azan-times@hamid.net": {minVersion: "1.2.3.1-signed.1-signed", installs: 64},
|
||||
"check-compatibility@dactyl.googlecode.com": {minVersion: "1.3.1-signed.1-signed", installs: 64},
|
||||
"ifamebook@stormvision.it": {minVersion: "4.03.1-signed", installs: 64},
|
||||
"jid1-vRJA7N8VwBoiXw@jetpack": {minVersion: "1.1.1.1-signed", installs: 64},
|
||||
"{04426594-bce6-4705-b811-bcdba2fd9c7b}": {minVersion: "1.7.1-signed.1-signed", installs: 63},
|
||||
"{f3f219f9-cbce-467e-b8fe-6e076d29665c}": {minVersion: "50", installs: 63},
|
||||
"fireforce@scrt.ch": {minVersion: "2.2.1-signed.1-signed", installs: 63},
|
||||
"FunkyMediaTab-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 63},
|
||||
"TopTabTV-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 63},
|
||||
"{018f3160-1a6f-4650-84fd-aad8c13609c8}": {minVersion: "0.1.1-signed.1-signed", installs: 62},
|
||||
"{2e710e6b-5e9d-44ba-8f4e-09a040978b49}": {minVersion: "1.7", installs: 62},
|
||||
"{c1970c0d-dbe6-4d91-804f-c9c0de643a57}": {minVersion: "1.3.2.13.1-signed.1-signed", installs: 62},
|
||||
"{c9b4529a-eeba-4e48-976e-f3d3f9026e04}": {minVersion: "1.1.1-signed.1-signed", installs: 62},
|
||||
"{df4e4df5-5cb7-46b0-9aef-6c784c3249f8}": {minVersion: "1.3.0.1-signed.1-signed", installs: 62},
|
||||
"@stremio": {minVersion: "1.0.2", installs: 62},
|
||||
"application@les-pages.com": {minVersion: "4.4.0", installs: 62},
|
||||
"ffvkontaktemusic@chupakabr.ru": {minVersion: "2.2.1-signed.1-signed", installs: 62},
|
||||
"firefinder@robertnyman.com": {minVersion: "1.4.1-signed.1-signed", installs: 62},
|
||||
"formhistory@yahoo.com": {minVersion: "1.4.0.6", installs: 62},
|
||||
"fxclickonce@rushyo.com": {minVersion: "0.1.1-signed.1-signed", installs: 62},
|
||||
"gmail@borsosfisoft.com": {minVersion: "1.0.1.1-signed.1-signed", installs: 62},
|
||||
"HighlightedTextToFile@bobbyrne01.org": {minVersion: "2.7.1", installs: 62},
|
||||
"jid1-n85lxPv1NAWVTQ@jetpack": {minVersion: "0.96.1.1-signed", installs: 62},
|
||||
"ramback@pavlov.net": {minVersion: "1.0.1-signed.1-signed", installs: 62},
|
||||
"VacuumPlacesImproved@lultimouomo-gmail.com": {minVersion: "1.2.1-signed.1-signed", installs: 62},
|
||||
"@News": {minVersion: "0.2.0", installs: 61},
|
||||
"{45d8ff86-d909-11db-9705-005056c00008}": {minVersion: "1.3.4.8", installs: 61},
|
||||
"{686fc9c5-c339-43db-b93a-5181a217f9a6}": {minVersion: "1.11", installs: 61},
|
||||
"{ea2b95c2-9be8-48ed-bdd1-5fcd2ad0ff99}": {minVersion: "0.3.8.1.1-signed.1-signed", installs: 61},
|
||||
"@chomikuj": {minVersion: "1.2.0", installs: 61},
|
||||
"avg@wtu3": {minVersion: "3.7.0.0", installs: 61},
|
||||
"jid1-f7dnBeTj8ElpWQ@jetpack": {minVersion: "1.34.1-signed.1-signed", installs: 61},
|
||||
"jid1-OY8Xu5BsKZQa6A@jetpack": {minVersion: "2.0.21", installs: 61},
|
||||
"jid1-u9RbFp9JcoEGGw@jetpack": {minVersion: "1.2.2.1-signed.1-signed", installs: 61},
|
||||
"plugin@okta.com": {minVersion: "5.8.0", installs: 61},
|
||||
"showpassword@pratikpoddar": {minVersion: "1.7.1-signed.1-signed", installs: 61},
|
||||
"IGF.F3@igufei.com": {minVersion: "3.2.11", installs: 60},
|
||||
"{12b6fdcd-4423-4276-82a3-73fdbff5f7e4}": {minVersion: "50", installs: 60},
|
||||
"{8F6A6FD9-0619-459f-B9D0-81DE065D4E21}": {minVersion: "1.13", installs: 60},
|
||||
"jid1-mW7iuA66Ny8Ziw@jetpack": {minVersion: "0.9.1-signed.1-signed", installs: 60},
|
||||
"nishan.naseer.googimagesearch@gmail.com": {minVersion: "0.5.1-signed.1-signed", installs: 60},
|
||||
"quicksearch@yandex.ru": {minVersion: "1.0.13", installs: 60},
|
||||
"{902D2C4A-457A-4EF9-AD43-7014562929FF}": {minVersion: "0.6.4", installs: 59},
|
||||
"@yset": {minVersion: "0.0.10", installs: 59},
|
||||
"csscoverage@spaghetticoder.org": {minVersion: "0.3.4.1-signed.1-signed", installs: 59},
|
||||
"dgnria2@nuance.com": {minVersion: "15.00.000.058", installs: 59},
|
||||
"firequery@binaryage.com": {minVersion: "2.0.4", installs: 59},
|
||||
"IBM-cck@firefox-extensions.ibm.com": {minVersion: "2.3.0", installs: 59},
|
||||
"trackmenot@mrl.nyu.edu": {minVersion: "0.9.2", installs: 59},
|
||||
"_chMembers_@free.discoverancestry.com": {minVersion: "7.102.10.3818", installs: 58},
|
||||
"{338e0b96-2285-4424-b4c8-e25560750fa3}": {minVersion: "3.1-signed.1-signed", installs: 58},
|
||||
"{8b5bea8c-6194-4c7c-a440-d5ca181480c3}": {minVersion: "1.500.000.11", installs: 58},
|
||||
"{e30e9060-21d5-11e3-8224-0800200c9a66}": {minVersion: "1.2.12", installs: 58},
|
||||
"LDshowpicture_plashcor@gmail.com": {minVersion: "3.2", installs: 58},
|
||||
"open.about.permissions@jasnapaka.com": {minVersion: "1.2.1-signed.1-signed", installs: 58},
|
||||
"sqlime@security.compass": {minVersion: "0.4.7.1-signed.1-signed", installs: 58},
|
||||
"@jetpack-easy-turism2": {minVersion: "7.1.0", installs: 57},
|
||||
"check4change-owner@mozdev.org": {minVersion: "1.9.8.1", installs: 57},
|
||||
"jid1-SDFC9fEAZRW7ab@jetpack": {minVersion: "0.1.3.1-signed.1-signed", installs: 57},
|
||||
"linkgopher@oooninja.com": {minVersion: "1.3.3.1-signed.1-signed", installs: 57},
|
||||
"pixelperfectplugin@openhouseconcepts.com": {minVersion: "2.0.14", installs: 57},
|
||||
"YoutubeDownloader@huangho.net76.net": {minVersion: "1.6.5.1-signed.1-signed", installs: 57},
|
||||
"lwthemes-manager@loucypher": {minVersion: "0.2.1-signed.1-let-fixed.1-signed", installs: 56},
|
||||
"_eiMembers_@www.100sofrecipes.com": {minVersion: "7.102.10.3580", installs: 56},
|
||||
"{068c594c-1a69-4f51-888d-1e231eac59a3}": {minVersion: "1", installs: 56},
|
||||
"{139C4B80-60ED-11E4-80EC-84041E5D46B0}": {minVersion: "1.3", installs: 56},
|
||||
"{4c7097f7-08f2-4ef2-9b9f-f95fa4cbb064}": {minVersion: "1.1", installs: 56},
|
||||
"{776f38cb-6255-4b92-b5cf-e5c71ff2b688}": {minVersion: "1.6", installs: 56},
|
||||
"{79c50f9a-2ffe-4ee0-8a37-fae4f5dacd4f}": {minVersion: "5.1.3", installs: 56},
|
||||
"{8BCA0E8A-E57B-425b-A05B-CD3868EB577E}": {minVersion: "1.4.1-signed.1-signed", installs: 56},
|
||||
"easycopypaste@everhelper.me": {minVersion: "1.1.0.1-signed.1-signed", installs: 56},
|
||||
"NoiaFoxoption@davidvincent.tld": {minVersion: "3.0.2.1-signed", installs: 56},
|
||||
"r2d2b2g@mozilla.org": {minVersion: "4.0.4.1-signed", installs: 56},
|
||||
"TFToolbarX@torrent-finder": {minVersion: "1.3.1.1-signed.1-signed", installs: 56},
|
||||
"{E4091D66-127C-11DB-903A-DE80D2EFDFE8}": {minVersion: "1.6.5.5.1-signed.1-signed", installs: 55},
|
||||
"downintab@max.max": {minVersion: "1.00.1-signed.1-signed", installs: 55},
|
||||
"flv2mp3@hotger.com": {minVersion: "2.3.2-signed", installs: 55},
|
||||
"ISVCU@iSkysoft.com": {minVersion: "5.1.0", installs: 55},
|
||||
"jid1-n5ARdBzHkUEdAA@jetpack": {minVersion: "3.8.7", installs: 55},
|
||||
"rpnetdownloadhelper@gmail.com": {minVersion: "3.0.1-signed.1-signed", installs: 55},
|
||||
"shpassword@shpassword.fr": {minVersion: "0.3.1-signed.1-signed", installs: 55},
|
||||
"snt@simplenewtab.com": {minVersion: "1.3", installs: 55},
|
||||
"admin@djamol.com": {minVersion: "4.31.1-signed.1-signed", installs: 54},
|
||||
"{22870005-adef-4c9d-ae36-d0e1f2f27e5a}": {minVersion: "0.4.0.9.1.1-signed.1-signed", installs: 54},
|
||||
"{DBBB3167-6E81-400f-BBFD-BD8921726F52}": {minVersion: "7125.2016.0115.2213", installs: 54},
|
||||
"{e4f94d1e-2f53-401e-8885-681602c0ddd8}": {minVersion: "1.0.1-signed.1-signed", installs: 54},
|
||||
"{FBF6D7FB-F305-4445-BB3D-FEF66579A033}": {minVersion: "6", installs: 54},
|
||||
"5aa55fd5-6e61-4896-b186-fdc6f298ec92@mozilla": {minVersion: "0.1.2.1-signed", installs: 54},
|
||||
"fireml@sirma.bg": {minVersion: "1.1.11.1-signed.1-signed", installs: 54},
|
||||
"info@priceblink.com": {minVersion: "4.8", installs: 54},
|
||||
"jid0-f82gosWvE8oeGQt6WDCGRF1Dy7Q@jetpack": {minVersion: "1.0.003", installs: 54},
|
||||
"jid0-hyjN250ZzTOOX3evFwwAQBxE4ik@jetpack": {minVersion: "6.0.1-signed.1-signed", installs: 54},
|
||||
"restart@restart.org": {minVersion: "0.5.1-signed.1-signed", installs: 54},
|
||||
"webmaster@keep-tube.com": {minVersion: "1.2.1-signed.1-signed", installs: 54},
|
||||
"eliteproxyswitcher@my-proxy.com": {minVersion: "1.2.0.2.1-signed.1-signed", installs: 53},
|
||||
"foxfilter@inspiredeffect.net": {minVersion: "7.7.1-signed.1-signed", installs: 53},
|
||||
"searchprivacy@searchprivacy.co": {minVersion: "1.15", installs: 53},
|
||||
"SignPlugin@pekao.pl": {minVersion: "1.4.0.73", installs: 53},
|
||||
"{15fe27f3-e5ab-2d59-4c5c-dadc7945bdbd}": {minVersion: "2.1.1.1-signed.1-signed", installs: 52},
|
||||
"ipfuck@p4ul.info": {minVersion: "1.2.1.1-signed.1-signed", installs: 52},
|
||||
"jyboy.yy@gmail.com": {minVersion: "1.0.4.1-signed.1-signed", installs: 52},
|
||||
"MySafeTabs-the-extension1@mozilla.com": {minVersion: "0.1.9", installs: 52},
|
||||
"saiful.neo@gmail.com": {minVersion: "3.0.1-signed.1-signed", installs: 52},
|
||||
"sendtokindle@amazon.com": {minVersion: "1.0.2.76", installs: 52},
|
||||
"smile1Button@amazon.com": {minVersion: "1.0.1-signed.1-signed", installs: 52},
|
||||
"whodeletedme@deleted.io": {minVersion: "0.3.3", installs: 52},
|
||||
"{C0CB8BA3-6C1B-47e8-A6AB-1FAB889562D9}": {minVersion: "0.7.6", installs: 51},
|
||||
"@irctctatkal": {minVersion: "2.0.0", installs: 51},
|
||||
"antgroup@antdownloadmanager.com": {minVersion: "0.1.7", installs: 51},
|
||||
"downloadplan@firefoxmania.uci.cu": {minVersion: "1.3.1-signed.1-signed", installs: 51},
|
||||
"jid1-AoXeeOB4j7kFdA@jetpack": {minVersion: "8.1", installs: 51},
|
||||
"memoryrestart@teamextension.com": {minVersion: "1.18.1-signed.1-signed", installs: 51},
|
||||
"multifox-toolbar-button@rbaldwin": {minVersion: "4.28.1-signed.1-signed", installs: 51},
|
||||
"soaclient@santoso": {minVersion: "0.2.1-signed.1-signed", installs: 51},
|
||||
"speeddns@gmail.com": {minVersion: "0.2.1-signed.1-signed", installs: 51},
|
||||
"windowandtablimiter@weintraut.net": {minVersion: "4.28.1-signed.1-signed", installs: 51},
|
||||
"@Recipes": {minVersion: "0.2.0", installs: 50},
|
||||
"{e6a9a96e-4a08-4719-b9bd-0e91c35aaabc}": {minVersion: "1.3.1.1-signed.1-signed", installs: 50},
|
||||
"autopager@mozilla.org": {minVersion: "0.8.0.10.1-signed.1-signed", installs: 50},
|
||||
"btpersonas@brandthunder.com": {minVersion: "2.0.4.7", installs: 50},
|
||||
"gdrivepanel@alejandrobrizuela.com.ar": {minVersion: "1.0.2.1-signed.1-signed", installs: 50},
|
||||
"jid1-m3kqTBs1zKXXaA@jetpack": {minVersion: "0.2.6.1-signed.1-signed", installs: 50},
|
||||
"qqmail_plugin_for_firefox@tencent.com": {minVersion: "1.0.0.22", installs: 50},
|
||||
"savefileto@mozdev.org": {minVersion: "2.5.5", installs: 50},
|
||||
"seodoctor@prelovac.com": {minVersion: "1.6.5.1-signed.1-signed", installs: 50},
|
||||
"support@todoist.com": {minVersion: "4.0.5", installs: 50},
|
||||
"toolbar_TeoMediaTB@apn.ask.com": {minVersion: "135.3", installs: 50},
|
||||
"txftn@tencent.com": {minVersion: "1.0.0.7", installs: 50},
|
||||
};
|
||||
|
||||
// ================== END OF LIST FOR 51 ======================
|
||||
|
||||
// We use these named policies to correlate the telemetry
|
||||
// data with them, in order to understand how each set
|
||||
// is behaving in the wild.
|
||||
const RolloutPolicy = {
|
||||
// Used during 48 Beta cycle
|
||||
"2a": { addons: set2, webextensions: true },
|
||||
"2b": { addons: set2, webextensions: false },
|
||||
|
||||
// Set agreed for Release 49
|
||||
"49a": { addons: set49Release, webextensions: true },
|
||||
"49b": { addons: set49Release, webextensions: false },
|
||||
|
||||
// Smaller set that can be used for Release 49
|
||||
"49limiteda": { addons: set49PaneOnly, webextensions: true },
|
||||
"49limitedb": { addons: set49PaneOnly, webextensions: false },
|
||||
|
||||
// Beta testing on 50
|
||||
"50allmpc": { addons: [], webextensions: true, mpc: true },
|
||||
|
||||
// Beta testing on 51
|
||||
"51alladdons": { addons: [], webextensions: true, alladdons: true },
|
||||
|
||||
// 51 release
|
||||
"51set1": { addonsv2: set51Release, installs: 50, webextensions: true, mpc: true },
|
||||
"51set2": { addonsv2: set51Release, installs: 100, webextensions: true, mpc: true },
|
||||
"51set3": { addonsv2: set51Release, installs: 300, webextensions: true, mpc: true },
|
||||
"51set4": { addonsv2: set51Release, installs: 1000, webextensions: true, mpc: true },
|
||||
|
||||
// ESR
|
||||
"esrA": { addons: [], mpc: true, webextensions: true },
|
||||
"esrB": { addons: [], mpc: true, webextensions: false },
|
||||
"esrC": { addons: [], mpc: false, webextensions: true },
|
||||
|
||||
"xpcshell-test": { addons: [ADDONS.test1, ADDONS.test2], webextensions: false },
|
||||
};
|
||||
|
||||
Object.defineProperty(this, "isAddonPartOfE10SRollout", {
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
value: function isAddonPartOfE10SRollout(aAddon) {
|
||||
let blocklist = Preferences.get(PREF_E10S_ADDON_BLOCKLIST, "");
|
||||
let policyId = Preferences.get(PREF_E10S_ADDON_POLICY, "");
|
||||
|
||||
if (!policyId || !RolloutPolicy.hasOwnProperty(policyId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (blocklist && blocklist.indexOf(aAddon.id) > -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let policy = RolloutPolicy[policyId];
|
||||
|
||||
if (aAddon.mpcOptedOut == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (policy.alladdons) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (policy.webextensions && aAddon.type == "webextension") {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (policy.mpc && aAddon.multiprocessCompatible) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (policy.addonsv2) {
|
||||
if (aAddon.id in policy.addonsv2) {
|
||||
let rolloutAddon = policy.addonsv2[aAddon.id];
|
||||
|
||||
if (rolloutAddon.installs >= policy.installs &&
|
||||
Services.vc.compare(aAddon.version, rolloutAddon.minVersion) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let rolloutAddon of policy.addons) {
|
||||
if (aAddon.id == rolloutAddon.id &&
|
||||
Services.vc.compare(aAddon.version, rolloutAddon.minVersion) >= 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
});
|
||||
|
|
@ -1,699 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [];
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
/* globals AddonManagerPrivate*/
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Preferences.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
/* globals OS*/
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/GMPUtils.jsm");
|
||||
/* globals EME_ADOBE_ID, GMP_PLUGIN_IDS, GMPPrefs, GMPUtils, OPEN_H264_ID, WIDEVINE_ID */
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(
|
||||
this, "setTimeout", "resource://gre/modules/Timer.jsm");
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
const STRING_TYPE_NAME = "type.%ID%.name";
|
||||
|
||||
const SEC_IN_A_DAY = 24 * 60 * 60;
|
||||
// How long to wait after a user enabled EME before attempting to download CDMs.
|
||||
const GMP_CHECK_DELAY = 10 * 1000; // milliseconds
|
||||
|
||||
const NS_GRE_DIR = "GreD";
|
||||
const CLEARKEY_PLUGIN_ID = "gmp-clearkey";
|
||||
const CLEARKEY_VERSION = "0.1";
|
||||
|
||||
const GMP_LICENSE_INFO = "gmp_license_info";
|
||||
const GMP_PRIVACY_INFO = "gmp_privacy_info";
|
||||
const GMP_LEARN_MORE = "learn_more_label";
|
||||
|
||||
const GMP_PLUGINS = [
|
||||
{
|
||||
id: OPEN_H264_ID,
|
||||
name: "openH264_name",
|
||||
description: "openH264_description2",
|
||||
// The following licenseURL is part of an awful hack to include the OpenH264
|
||||
// license without having bug 624602 fixed yet, and intentionally ignores
|
||||
// localisation.
|
||||
licenseURL: "chrome://mozapps/content/extensions/OpenH264-license.txt",
|
||||
homepageURL: "http://www.openh264.org/",
|
||||
optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul",
|
||||
},
|
||||
{
|
||||
id: EME_ADOBE_ID,
|
||||
name: "eme-adobe_name",
|
||||
description: "eme-adobe_description",
|
||||
// The following learnMoreURL is another hack to be able to support a SUMO page for this
|
||||
// feature.
|
||||
get learnMoreURL() {
|
||||
return Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
|
||||
},
|
||||
licenseURL: "http://help.adobe.com/en_US/primetime/drm/HTML5_CDM_EULA/index.html",
|
||||
homepageURL: "http://help.adobe.com/en_US/primetime/drm/HTML5_CDM",
|
||||
optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul",
|
||||
isEME: true,
|
||||
},
|
||||
{
|
||||
id: WIDEVINE_ID,
|
||||
name: "widevine_description",
|
||||
// Describe the purpose of both CDMs in the same way.
|
||||
description: "eme-adobe_description",
|
||||
licenseURL: "https://www.google.com/policies/privacy/",
|
||||
homepageURL: "https://www.widevine.com/",
|
||||
optionsURL: "chrome://mozapps/content/extensions/gmpPrefs.xul",
|
||||
isEME: true
|
||||
}];
|
||||
XPCOMUtils.defineConstant(this, "GMP_PLUGINS", GMP_PLUGINS);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
|
||||
() => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
|
||||
XPCOMUtils.defineLazyGetter(this, "gmpService",
|
||||
() => Cc["@mozilla.org/gecko-media-plugin-service;1"].getService(Ci.mozIGeckoMediaPluginChromeService));
|
||||
|
||||
var messageManager = Cc["@mozilla.org/globalmessagemanager;1"]
|
||||
.getService(Ci.nsIMessageListenerManager);
|
||||
|
||||
var gLogger;
|
||||
var gLogAppenderDump = null;
|
||||
|
||||
function configureLogging() {
|
||||
if (!gLogger) {
|
||||
gLogger = Log.repository.getLogger("Toolkit.GMP");
|
||||
gLogger.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
|
||||
}
|
||||
gLogger.level = GMPPrefs.get(GMPPrefs.KEY_LOGGING_LEVEL, Log.Level.Warn);
|
||||
|
||||
let logDumping = GMPPrefs.get(GMPPrefs.KEY_LOGGING_DUMP, false);
|
||||
if (logDumping != !!gLogAppenderDump) {
|
||||
if (logDumping) {
|
||||
gLogAppenderDump = new Log.DumpAppender(new Log.BasicFormatter());
|
||||
gLogger.addAppender(gLogAppenderDump);
|
||||
} else {
|
||||
gLogger.removeAppender(gLogAppenderDump);
|
||||
gLogAppenderDump = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The GMPWrapper provides the info for the various GMP plugins to public
|
||||
* callers through the API.
|
||||
*/
|
||||
function GMPWrapper(aPluginInfo) {
|
||||
this._plugin = aPluginInfo;
|
||||
this._log =
|
||||
Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
|
||||
"GMPWrapper(" +
|
||||
this._plugin.id + ") ");
|
||||
Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
|
||||
this._plugin.id),
|
||||
this.onPrefEnabledChanged, this);
|
||||
Preferences.observe(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
this._plugin.id),
|
||||
this.onPrefVersionChanged, this);
|
||||
if (this._plugin.isEME) {
|
||||
Preferences.observe(GMPPrefs.KEY_EME_ENABLED,
|
||||
this.onPrefEMEGlobalEnabledChanged, this);
|
||||
messageManager.addMessageListener("EMEVideo:ContentMediaKeysRequest", this);
|
||||
}
|
||||
}
|
||||
|
||||
GMPWrapper.prototype = {
|
||||
// An active task that checks for plugin updates and installs them.
|
||||
_updateTask: null,
|
||||
_gmpPath: null,
|
||||
_isUpdateCheckPending: false,
|
||||
|
||||
optionsType: AddonManager.OPTIONS_TYPE_INLINE,
|
||||
get optionsURL() { return this._plugin.optionsURL; },
|
||||
|
||||
set gmpPath(aPath) { this._gmpPath = aPath; },
|
||||
get gmpPath() {
|
||||
if (!this._gmpPath && this.isInstalled) {
|
||||
this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
this._plugin.id,
|
||||
GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
null, this._plugin.id));
|
||||
}
|
||||
return this._gmpPath;
|
||||
},
|
||||
|
||||
get id() { return this._plugin.id; },
|
||||
get type() { return "plugin"; },
|
||||
get isGMPlugin() { return true; },
|
||||
get name() { return this._plugin.name; },
|
||||
get creator() { return null; },
|
||||
get homepageURL() { return this._plugin.homepageURL; },
|
||||
|
||||
get description() { return this._plugin.description; },
|
||||
get fullDescription() { return this._plugin.fullDescription; },
|
||||
|
||||
get version() { return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION, null,
|
||||
this._plugin.id); },
|
||||
|
||||
get isActive() {
|
||||
return !this.appDisabled &&
|
||||
!this.userDisabled &&
|
||||
!GMPUtils.isPluginHidden(this._plugin);
|
||||
},
|
||||
get appDisabled() {
|
||||
if (this._plugin.isEME && !GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
|
||||
// If "media.eme.enabled" is false, all EME plugins are disabled.
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
get userDisabled() {
|
||||
return !GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ENABLED, true, this._plugin.id);
|
||||
},
|
||||
set userDisabled(aVal) { GMPPrefs.set(GMPPrefs.KEY_PLUGIN_ENABLED,
|
||||
aVal === false,
|
||||
this._plugin.id); },
|
||||
|
||||
get blocklistState() { return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; },
|
||||
get size() { return 0; },
|
||||
get scope() { return AddonManager.SCOPE_APPLICATION; },
|
||||
get pendingOperations() { return AddonManager.PENDING_NONE; },
|
||||
|
||||
get operationsRequiringRestart() { return AddonManager.OP_NEEDS_RESTART_NONE },
|
||||
|
||||
get permissions() {
|
||||
let permissions = 0;
|
||||
if (!this.appDisabled) {
|
||||
permissions |= AddonManager.PERM_CAN_UPGRADE;
|
||||
permissions |= this.userDisabled ? AddonManager.PERM_CAN_ENABLE :
|
||||
AddonManager.PERM_CAN_DISABLE;
|
||||
}
|
||||
return permissions;
|
||||
},
|
||||
|
||||
get updateDate() {
|
||||
let time = Number(GMPPrefs.get(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, null,
|
||||
this._plugin.id));
|
||||
if (!isNaN(time) && this.isInstalled) {
|
||||
return new Date(time * 1000)
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
get isCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get isPlatformCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get providesUpdatesSecurely() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get foreignInstall() {
|
||||
return false;
|
||||
},
|
||||
|
||||
isCompatibleWith: function(aAppVersion, aPlatformVersion) {
|
||||
return true;
|
||||
},
|
||||
|
||||
get applyBackgroundUpdates() {
|
||||
if (!GMPPrefs.isSet(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id)) {
|
||||
return AddonManager.AUTOUPDATE_DEFAULT;
|
||||
}
|
||||
|
||||
return GMPPrefs.get(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id) ?
|
||||
AddonManager.AUTOUPDATE_ENABLE : AddonManager.AUTOUPDATE_DISABLE;
|
||||
},
|
||||
|
||||
set applyBackgroundUpdates(aVal) {
|
||||
if (aVal == AddonManager.AUTOUPDATE_DEFAULT) {
|
||||
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, this._plugin.id);
|
||||
} else if (aVal == AddonManager.AUTOUPDATE_ENABLE) {
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id);
|
||||
} else if (aVal == AddonManager.AUTOUPDATE_DISABLE) {
|
||||
GMPPrefs.set(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, false, this._plugin.id);
|
||||
}
|
||||
},
|
||||
|
||||
findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
|
||||
this._log.trace("findUpdates() - " + this._plugin.id + " - reason=" +
|
||||
aReason);
|
||||
|
||||
AddonManagerPrivate.callNoUpdateListeners(this, aListener);
|
||||
|
||||
if (aReason === AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
|
||||
if (!AddonManager.shouldAutoUpdate(this)) {
|
||||
this._log.trace("findUpdates() - " + this._plugin.id +
|
||||
" - no autoupdate");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
let secSinceLastCheck =
|
||||
Date.now() / 1000 - Preferences.get(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
|
||||
if (secSinceLastCheck <= SEC_IN_A_DAY) {
|
||||
this._log.trace("findUpdates() - " + this._plugin.id +
|
||||
" - last check was less then a day ago");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
} else if (aReason !== AddonManager.UPDATE_WHEN_USER_REQUESTED) {
|
||||
this._log.trace("findUpdates() - " + this._plugin.id +
|
||||
" - the given reason to update is not supported");
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
if (this._updateTask !== null) {
|
||||
this._log.trace("findUpdates() - " + this._plugin.id +
|
||||
" - update task already running");
|
||||
return this._updateTask;
|
||||
}
|
||||
|
||||
this._updateTask = Task.spawn(function*() {
|
||||
this._log.trace("findUpdates() - updateTask");
|
||||
try {
|
||||
let installManager = new GMPInstallManager();
|
||||
let res = yield installManager.checkForAddons();
|
||||
let update = res.gmpAddons.find(addon => addon.id === this._plugin.id);
|
||||
if (update && update.isValid && !update.isInstalled) {
|
||||
this._log.trace("findUpdates() - found update for " +
|
||||
this._plugin.id + ", installing");
|
||||
yield installManager.installAddon(update);
|
||||
} else {
|
||||
this._log.trace("findUpdates() - no updates for " + this._plugin.id);
|
||||
}
|
||||
this._log.info("findUpdates() - updateTask succeeded for " +
|
||||
this._plugin.id);
|
||||
} catch (e) {
|
||||
this._log.error("findUpdates() - updateTask for " + this._plugin.id +
|
||||
" threw", e);
|
||||
throw e;
|
||||
} finally {
|
||||
this._updateTask = null;
|
||||
return true;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return this._updateTask;
|
||||
},
|
||||
|
||||
get pluginMimeTypes() { return []; },
|
||||
get pluginLibraries() {
|
||||
if (this.isInstalled) {
|
||||
let path = this.version;
|
||||
return [path];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
get pluginFullpath() {
|
||||
if (this.isInstalled) {
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
this._plugin.id,
|
||||
this.version);
|
||||
return [path];
|
||||
}
|
||||
return [];
|
||||
},
|
||||
|
||||
get isInstalled() {
|
||||
return this.version && this.version.length > 0;
|
||||
},
|
||||
|
||||
_handleEnabledChanged: function() {
|
||||
this._log.info("_handleEnabledChanged() id=" +
|
||||
this._plugin.id + " isActive=" + this.isActive);
|
||||
|
||||
AddonManagerPrivate.callAddonListeners(this.isActive ?
|
||||
"onEnabling" : "onDisabling",
|
||||
this, false);
|
||||
if (this._gmpPath) {
|
||||
if (this.isActive) {
|
||||
this._log.info("onPrefEnabledChanged() - adding gmp directory " +
|
||||
this._gmpPath);
|
||||
gmpService.addPluginDirectory(this._gmpPath);
|
||||
} else {
|
||||
this._log.info("onPrefEnabledChanged() - removing gmp directory " +
|
||||
this._gmpPath);
|
||||
gmpService.removePluginDirectory(this._gmpPath);
|
||||
}
|
||||
}
|
||||
AddonManagerPrivate.callAddonListeners(this.isActive ?
|
||||
"onEnabled" : "onDisabled",
|
||||
this);
|
||||
},
|
||||
|
||||
onPrefEMEGlobalEnabledChanged: function() {
|
||||
this._log.info("onPrefEMEGlobalEnabledChanged() id=" + this._plugin.id +
|
||||
" appDisabled=" + this.appDisabled + " isActive=" + this.isActive +
|
||||
" hidden=" + GMPUtils.isPluginHidden(this._plugin));
|
||||
|
||||
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this,
|
||||
["appDisabled"]);
|
||||
// If EME or the GMP itself are disabled, uninstall the GMP.
|
||||
// Otherwise, check for updates, so we download and install the GMP.
|
||||
if (this.appDisabled) {
|
||||
this.uninstallPlugin();
|
||||
} else if (!GMPUtils.isPluginHidden(this._plugin)) {
|
||||
AddonManagerPrivate.callInstallListeners("onExternalInstall", null, this,
|
||||
null, false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalled", this);
|
||||
this.checkForUpdates(GMP_CHECK_DELAY);
|
||||
}
|
||||
if (!this.userDisabled) {
|
||||
this._handleEnabledChanged();
|
||||
}
|
||||
},
|
||||
|
||||
checkForUpdates: function(delay) {
|
||||
if (this._isUpdateCheckPending) {
|
||||
return;
|
||||
}
|
||||
this._isUpdateCheckPending = true;
|
||||
GMPPrefs.reset(GMPPrefs.KEY_UPDATE_LAST_CHECK, null);
|
||||
// Delay this in case the user changes his mind and doesn't want to
|
||||
// enable EME after all.
|
||||
setTimeout(() => {
|
||||
if (!this.appDisabled) {
|
||||
let gmpInstallManager = new GMPInstallManager();
|
||||
// We don't really care about the results, if someone is interested
|
||||
// they can check the log.
|
||||
gmpInstallManager.simpleCheckAndInstall().then(null, () => {});
|
||||
}
|
||||
this._isUpdateCheckPending = false;
|
||||
}, delay);
|
||||
},
|
||||
|
||||
receiveMessage: function({target: browser, data: data}) {
|
||||
this._log.trace("receiveMessage() data=" + data);
|
||||
let parsedData;
|
||||
try {
|
||||
parsedData = JSON.parse(data);
|
||||
} catch (ex) {
|
||||
this._log.error("Malformed EME video message with data: " + data);
|
||||
return;
|
||||
}
|
||||
let {status: status, keySystem: keySystem} = parsedData;
|
||||
if (status == "cdm-not-installed") {
|
||||
this.checkForUpdates(0);
|
||||
}
|
||||
},
|
||||
|
||||
onPrefEnabledChanged: function() {
|
||||
if (!this._plugin.isEME || !this.appDisabled) {
|
||||
this._handleEnabledChanged();
|
||||
}
|
||||
},
|
||||
|
||||
onPrefVersionChanged: function() {
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
|
||||
if (this._gmpPath) {
|
||||
this._log.info("onPrefVersionChanged() - unregistering gmp directory " +
|
||||
this._gmpPath);
|
||||
gmpService.removeAndDeletePluginDirectory(this._gmpPath, true /* can defer */);
|
||||
}
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", this);
|
||||
|
||||
AddonManagerPrivate.callInstallListeners("onExternalInstall", null, this,
|
||||
null, false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
|
||||
this._gmpPath = null;
|
||||
if (this.isInstalled) {
|
||||
this._gmpPath = OS.Path.join(OS.Constants.Path.profileDir,
|
||||
this._plugin.id,
|
||||
GMPPrefs.get(GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
null, this._plugin.id));
|
||||
}
|
||||
if (this._gmpPath && this.isActive) {
|
||||
this._log.info("onPrefVersionChanged() - registering gmp directory " +
|
||||
this._gmpPath);
|
||||
gmpService.addPluginDirectory(this._gmpPath);
|
||||
}
|
||||
AddonManagerPrivate.callAddonListeners("onInstalled", this);
|
||||
},
|
||||
|
||||
uninstallPlugin: function() {
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
|
||||
if (this.gmpPath) {
|
||||
this._log.info("uninstallPlugin() - unregistering gmp directory " +
|
||||
this.gmpPath);
|
||||
gmpService.removeAndDeletePluginDirectory(this.gmpPath);
|
||||
}
|
||||
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_VERSION, this.id);
|
||||
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_ABI, this.id);
|
||||
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, this.id);
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", this);
|
||||
},
|
||||
|
||||
shutdown: function() {
|
||||
Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
|
||||
this._plugin.id),
|
||||
this.onPrefEnabledChanged, this);
|
||||
Preferences.ignore(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
|
||||
this._plugin.id),
|
||||
this.onPrefVersionChanged, this);
|
||||
if (this._plugin.isEME) {
|
||||
Preferences.ignore(GMPPrefs.KEY_EME_ENABLED,
|
||||
this.onPrefEMEGlobalEnabledChanged, this);
|
||||
messageManager.removeMessageListener("EMEVideo:ContentMediaKeysRequest", this);
|
||||
}
|
||||
return this._updateTask;
|
||||
},
|
||||
|
||||
_arePluginFilesOnDisk: function() {
|
||||
let fileExists = function(aGmpPath, aFileName) {
|
||||
let f = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
let path = OS.Path.join(aGmpPath, aFileName);
|
||||
f.initWithPath(path);
|
||||
return f.exists();
|
||||
};
|
||||
|
||||
let id = this._plugin.id.substring(4);
|
||||
let libName = AppConstants.DLL_PREFIX + id + AppConstants.DLL_SUFFIX;
|
||||
let infoName;
|
||||
if (this._plugin.id == WIDEVINE_ID) {
|
||||
infoName = "manifest.json";
|
||||
} else {
|
||||
infoName = id + ".info";
|
||||
}
|
||||
|
||||
return fileExists(this.gmpPath, libName) &&
|
||||
fileExists(this.gmpPath, infoName) &&
|
||||
(this._plugin.id != EME_ADOBE_ID || fileExists(this.gmpPath, id + ".voucher"));
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
if (!this.isInstalled) {
|
||||
// Not installed -> Valid.
|
||||
return {
|
||||
installed: false,
|
||||
valid: true
|
||||
};
|
||||
}
|
||||
|
||||
let abi = GMPPrefs.get(GMPPrefs.KEY_PLUGIN_ABI, UpdateUtils.ABI, this._plugin.id);
|
||||
if (abi != UpdateUtils.ABI) {
|
||||
// ABI doesn't match. Possibly this is a profile migrated across platforms
|
||||
// or from 32 -> 64 bit.
|
||||
return {
|
||||
installed: true,
|
||||
mismatchedABI: true,
|
||||
valid: false
|
||||
};
|
||||
}
|
||||
|
||||
// Installed -> Check if files are missing.
|
||||
let filesOnDisk = this._arePluginFilesOnDisk();
|
||||
return {
|
||||
installed: true,
|
||||
valid: filesOnDisk
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
var GMPProvider = {
|
||||
get name() { return "GMPProvider"; },
|
||||
|
||||
_plugins: null,
|
||||
|
||||
startup: function() {
|
||||
configureLogging();
|
||||
this._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
|
||||
"GMPProvider.");
|
||||
this.buildPluginList();
|
||||
this.ensureProperCDMInstallState();
|
||||
|
||||
Preferences.observe(GMPPrefs.KEY_LOG_BASE, configureLogging);
|
||||
|
||||
for (let [id, plugin] of this._plugins) {
|
||||
let wrapper = plugin.wrapper;
|
||||
let gmpPath = wrapper.gmpPath;
|
||||
let isEnabled = wrapper.isActive;
|
||||
this._log.trace("startup - enabled=" + isEnabled + ", gmpPath=" +
|
||||
gmpPath);
|
||||
|
||||
if (gmpPath && isEnabled) {
|
||||
let validation = wrapper.validate();
|
||||
if (validation.mismatchedABI) {
|
||||
this._log.info("startup - gmp " + plugin.id +
|
||||
" mismatched ABI, uninstalling");
|
||||
wrapper.uninstallPlugin();
|
||||
continue;
|
||||
}
|
||||
if (!validation.valid) {
|
||||
this._log.info("startup - gmp " + plugin.id +
|
||||
" invalid, uninstalling");
|
||||
wrapper.uninstallPlugin();
|
||||
continue;
|
||||
}
|
||||
this._log.info("startup - adding gmp directory " + gmpPath);
|
||||
try {
|
||||
gmpService.addPluginDirectory(gmpPath);
|
||||
} catch (e) {
|
||||
if (e.name != 'NS_ERROR_NOT_AVAILABLE')
|
||||
throw e;
|
||||
this._log.warn("startup - adding gmp directory failed with " +
|
||||
e.name + " - sandboxing not available?", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let greDir = Services.dirsvc.get(NS_GRE_DIR,
|
||||
Ci.nsILocalFile);
|
||||
let clearkeyPath = OS.Path.join(greDir.path,
|
||||
CLEARKEY_PLUGIN_ID,
|
||||
CLEARKEY_VERSION);
|
||||
this._log.info("startup - adding clearkey CDM directory " +
|
||||
clearkeyPath);
|
||||
gmpService.addPluginDirectory(clearkeyPath);
|
||||
} catch (e) {
|
||||
this._log.warn("startup - adding clearkey CDM failed", e);
|
||||
}
|
||||
},
|
||||
|
||||
shutdown: function() {
|
||||
this._log.trace("shutdown");
|
||||
Preferences.ignore(GMPPrefs.KEY_LOG_BASE, configureLogging);
|
||||
|
||||
let shutdownTask = Task.spawn(function*() {
|
||||
this._log.trace("shutdown - shutdownTask");
|
||||
let shutdownSucceeded = true;
|
||||
|
||||
for (let plugin of this._plugins.values()) {
|
||||
try {
|
||||
yield plugin.wrapper.shutdown();
|
||||
} catch (e) {
|
||||
shutdownSucceeded = false;
|
||||
}
|
||||
}
|
||||
|
||||
this._plugins = null;
|
||||
|
||||
if (!shutdownSucceeded) {
|
||||
throw new Error("Shutdown failed");
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
return shutdownTask;
|
||||
},
|
||||
|
||||
getAddonByID: function(aId, aCallback) {
|
||||
if (!this.isEnabled) {
|
||||
aCallback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
let plugin = this._plugins.get(aId);
|
||||
if (plugin && !GMPUtils.isPluginHidden(plugin)) {
|
||||
aCallback(plugin.wrapper);
|
||||
} else {
|
||||
aCallback(null);
|
||||
}
|
||||
},
|
||||
|
||||
getAddonsByTypes: function(aTypes, aCallback) {
|
||||
if (!this.isEnabled ||
|
||||
(aTypes && aTypes.indexOf("plugin") < 0)) {
|
||||
aCallback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
let results = Array.from(this._plugins.values())
|
||||
.filter(p => !GMPUtils.isPluginHidden(p))
|
||||
.map(p => p.wrapper);
|
||||
|
||||
aCallback(results);
|
||||
},
|
||||
|
||||
get isEnabled() {
|
||||
return GMPPrefs.get(GMPPrefs.KEY_PROVIDER_ENABLED, false);
|
||||
},
|
||||
|
||||
generateFullDescription: function(aPlugin) {
|
||||
let rv = [];
|
||||
for (let [urlProp, labelId] of [["learnMoreURL", GMP_LEARN_MORE],
|
||||
["licenseURL", aPlugin.id == WIDEVINE_ID ?
|
||||
GMP_PRIVACY_INFO : GMP_LICENSE_INFO]]) {
|
||||
if (aPlugin[urlProp]) {
|
||||
let label = pluginsBundle.GetStringFromName(labelId);
|
||||
rv.push(`<xhtml:a href="${aPlugin[urlProp]}" target="_blank">${label}</xhtml:a>.`);
|
||||
}
|
||||
}
|
||||
return rv.length ? rv.join("<xhtml:br /><xhtml:br />") : undefined;
|
||||
},
|
||||
|
||||
buildPluginList: function() {
|
||||
this._plugins = new Map();
|
||||
for (let aPlugin of GMP_PLUGINS) {
|
||||
let plugin = {
|
||||
id: aPlugin.id,
|
||||
name: pluginsBundle.GetStringFromName(aPlugin.name),
|
||||
description: pluginsBundle.GetStringFromName(aPlugin.description),
|
||||
homepageURL: aPlugin.homepageURL,
|
||||
optionsURL: aPlugin.optionsURL,
|
||||
wrapper: null,
|
||||
isEME: aPlugin.isEME,
|
||||
};
|
||||
plugin.fullDescription = this.generateFullDescription(aPlugin);
|
||||
plugin.wrapper = new GMPWrapper(plugin);
|
||||
this._plugins.set(plugin.id, plugin);
|
||||
}
|
||||
},
|
||||
|
||||
ensureProperCDMInstallState: function() {
|
||||
if (!GMPPrefs.get(GMPPrefs.KEY_EME_ENABLED, true)) {
|
||||
for (let [id, plugin] of this._plugins) {
|
||||
if (plugin.isEME && plugin.wrapper.isInstalled) {
|
||||
gmpService.addPluginDirectory(plugin.wrapper.gmpPath);
|
||||
plugin.wrapper.uninstallPlugin();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
AddonManagerPrivate.registerProvider(GMPProvider, [
|
||||
new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
|
||||
STRING_TYPE_NAME,
|
||||
AddonManager.VIEW_TYPE_LIST, 6000,
|
||||
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE)
|
||||
]);
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LightweightThemeImageOptimizer"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
|
||||
const ORIGIN_TOP_RIGHT = 1;
|
||||
const ORIGIN_BOTTOM_LEFT = 2;
|
||||
|
||||
this.LightweightThemeImageOptimizer = {
|
||||
optimize: function(aThemeData, aScreen) {
|
||||
let data = Object.assign({}, aThemeData);
|
||||
if (!data.headerURL) {
|
||||
return data;
|
||||
}
|
||||
|
||||
data.headerURL = ImageCropper.getCroppedImageURL(
|
||||
data.headerURL, aScreen, ORIGIN_TOP_RIGHT);
|
||||
|
||||
if (data.footerURL) {
|
||||
data.footerURL = ImageCropper.getCroppedImageURL(
|
||||
data.footerURL, aScreen, ORIGIN_BOTTOM_LEFT);
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
purge: function() {
|
||||
let dir = FileUtils.getDir("ProfD", ["lwtheme"]);
|
||||
dir.followLinks = false;
|
||||
try {
|
||||
dir.remove(true);
|
||||
} catch (e) {}
|
||||
}
|
||||
};
|
||||
|
||||
Object.freeze(LightweightThemeImageOptimizer);
|
||||
|
||||
var ImageCropper = {
|
||||
_inProgress: {},
|
||||
|
||||
getCroppedImageURL: function(aImageURL, aScreen, aOrigin) {
|
||||
// We can crop local files, only.
|
||||
if (!aImageURL.startsWith("file://")) {
|
||||
return aImageURL;
|
||||
}
|
||||
|
||||
// Generate the cropped image's file name using its
|
||||
// base name and the current screen size.
|
||||
let uri = Services.io.newURI(aImageURL, null, null);
|
||||
let file = uri.QueryInterface(Ci.nsIFileURL).file;
|
||||
|
||||
// Make sure the source file exists.
|
||||
if (!file.exists()) {
|
||||
return aImageURL;
|
||||
}
|
||||
|
||||
let fileName = file.leafName + "-" + aScreen.width + "x" + aScreen.height;
|
||||
let croppedFile = FileUtils.getFile("ProfD", ["lwtheme", fileName]);
|
||||
|
||||
// If we have a local file that is not in progress, return it.
|
||||
if (croppedFile.exists() && !(croppedFile.path in this._inProgress)) {
|
||||
let fileURI = Services.io.newFileURI(croppedFile);
|
||||
|
||||
// Copy the query part to avoid wrong caching.
|
||||
fileURI.QueryInterface(Ci.nsIURL).query = uri.query;
|
||||
return fileURI.spec;
|
||||
}
|
||||
|
||||
// Crop the given image in the background.
|
||||
this._crop(uri, croppedFile, aScreen, aOrigin);
|
||||
|
||||
// Return the original image while we're waiting for the cropped version
|
||||
// to be written to disk.
|
||||
return aImageURL;
|
||||
},
|
||||
|
||||
_crop: function(aURI, aTargetFile, aScreen, aOrigin) {
|
||||
let inProgress = this._inProgress;
|
||||
inProgress[aTargetFile.path] = true;
|
||||
|
||||
function resetInProgress() {
|
||||
delete inProgress[aTargetFile.path];
|
||||
}
|
||||
|
||||
ImageFile.read(aURI, function(aInputStream, aContentType) {
|
||||
if (aInputStream && aContentType) {
|
||||
let image = ImageTools.decode(aInputStream, aContentType);
|
||||
if (image && image.width && image.height) {
|
||||
let stream = ImageTools.encode(image, aScreen, aOrigin, aContentType);
|
||||
if (stream) {
|
||||
ImageFile.write(aTargetFile, stream, resetInProgress);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetInProgress();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var ImageFile = {
|
||||
read: function(aURI, aCallback) {
|
||||
this._netUtil.asyncFetch({
|
||||
uri: aURI,
|
||||
loadUsingSystemPrincipal: true,
|
||||
contentPolicyType: Ci.nsIContentPolicy.TYPE_INTERNAL_IMAGE
|
||||
}, function(aInputStream, aStatus, aRequest) {
|
||||
if (Components.isSuccessCode(aStatus) && aRequest instanceof Ci.nsIChannel) {
|
||||
let channel = aRequest.QueryInterface(Ci.nsIChannel);
|
||||
aCallback(aInputStream, channel.contentType);
|
||||
} else {
|
||||
aCallback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
write: function(aFile, aInputStream, aCallback) {
|
||||
let fos = FileUtils.openSafeFileOutputStream(aFile);
|
||||
this._netUtil.asyncCopy(aInputStream, fos, function(aResult) {
|
||||
FileUtils.closeSafeFileOutputStream(fos);
|
||||
|
||||
// Remove the file if writing was not successful.
|
||||
if (!Components.isSuccessCode(aResult)) {
|
||||
try {
|
||||
aFile.remove(false);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
aCallback();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(ImageFile, "_netUtil",
|
||||
"resource://gre/modules/NetUtil.jsm", "NetUtil");
|
||||
|
||||
var ImageTools = {
|
||||
decode: function(aInputStream, aContentType) {
|
||||
let outParam = {value: null};
|
||||
|
||||
try {
|
||||
this._imgTools.decodeImageData(aInputStream, aContentType, outParam);
|
||||
} catch (e) {}
|
||||
|
||||
return outParam.value;
|
||||
},
|
||||
|
||||
encode: function(aImage, aScreen, aOrigin, aContentType) {
|
||||
let stream;
|
||||
let width = Math.min(aImage.width, aScreen.width);
|
||||
let height = Math.min(aImage.height, aScreen.height);
|
||||
let x = aOrigin == ORIGIN_TOP_RIGHT ? aImage.width - width : 0;
|
||||
|
||||
try {
|
||||
stream = this._imgTools.encodeCroppedImage(aImage, aContentType, x, 0,
|
||||
width, height);
|
||||
} catch (e) {}
|
||||
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(ImageTools, "_imgTools",
|
||||
"@mozilla.org/image/tools;1", "imgITools");
|
||||
|
||||
|
|
@ -1,600 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
this.EXPORTED_SYMBOLS = [];
|
||||
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
/* globals AddonManagerPrivate*/
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
const STRING_TYPE_NAME = "type.%ID%.name";
|
||||
const LIST_UPDATED_TOPIC = "plugins-list-updated";
|
||||
const FLASH_MIME_TYPE = "application/x-shockwave-flash";
|
||||
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
const LOGGER_ID = "addons.plugins";
|
||||
|
||||
// Create a new logger for use by the Addons Plugin Provider
|
||||
// (Requires AddonManager.jsm)
|
||||
var logger = Log.repository.getLogger(LOGGER_ID);
|
||||
|
||||
function getIDHashForString(aStr) {
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
|
||||
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
hasher.init(Ci.nsICryptoHash.MD5);
|
||||
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsIStringInputStream);
|
||||
stringStream.data = aStr ? aStr : "null";
|
||||
hasher.updateFromStream(stringStream, -1);
|
||||
|
||||
// convert the binary hash data to a hex string.
|
||||
let binary = hasher.finish(false);
|
||||
let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
|
||||
hash = hash.join("").toLowerCase();
|
||||
return "{" + hash.substr(0, 8) + "-" +
|
||||
hash.substr(8, 4) + "-" +
|
||||
hash.substr(12, 4) + "-" +
|
||||
hash.substr(16, 4) + "-" +
|
||||
hash.substr(20) + "}";
|
||||
}
|
||||
|
||||
var PluginProvider = {
|
||||
get name() {
|
||||
return "PluginProvider";
|
||||
},
|
||||
|
||||
// A dictionary mapping IDs to names and descriptions
|
||||
plugins: null,
|
||||
|
||||
startup: function() {
|
||||
Services.obs.addObserver(this, LIST_UPDATED_TOPIC, false);
|
||||
Services.obs.addObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the application is shutting down. Only necessary for tests
|
||||
* to be able to simulate a shutdown.
|
||||
*/
|
||||
shutdown: function() {
|
||||
this.plugins = null;
|
||||
Services.obs.removeObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
|
||||
Services.obs.removeObserver(this, LIST_UPDATED_TOPIC);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case AddonManager.OPTIONS_NOTIFICATION_DISPLAYED:
|
||||
this.getAddonByID(aData, function(plugin) {
|
||||
if (!plugin)
|
||||
return;
|
||||
|
||||
let libLabel = aSubject.getElementById("pluginLibraries");
|
||||
libLabel.textContent = plugin.pluginLibraries.join(", ");
|
||||
|
||||
let typeLabel = aSubject.getElementById("pluginMimeTypes"), types = [];
|
||||
for (let type of plugin.pluginMimeTypes) {
|
||||
let extras = [type.description.trim(), type.suffixes].
|
||||
filter(x => x).join(": ");
|
||||
types.push(type.type + (extras ? " (" + extras + ")" : ""));
|
||||
}
|
||||
typeLabel.textContent = types.join(",\n");
|
||||
let showProtectedModePref = canDisableFlashProtectedMode(plugin);
|
||||
aSubject.getElementById("pluginEnableProtectedMode")
|
||||
.setAttribute("collapsed", showProtectedModePref ? "" : "true");
|
||||
});
|
||||
break;
|
||||
case LIST_UPDATED_TOPIC:
|
||||
if (this.plugins)
|
||||
this.updatePluginList();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a PluginWrapper for a plugin object.
|
||||
*/
|
||||
buildWrapper: function(aPlugin) {
|
||||
return new PluginWrapper(aPlugin.id,
|
||||
aPlugin.name,
|
||||
aPlugin.description,
|
||||
aPlugin.tags);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get an Addon with a particular ID.
|
||||
*
|
||||
* @param aId
|
||||
* The ID of the add-on to retrieve
|
||||
* @param aCallback
|
||||
* A callback to pass the Addon to
|
||||
*/
|
||||
getAddonByID: function(aId, aCallback) {
|
||||
if (!this.plugins)
|
||||
this.buildPluginList();
|
||||
|
||||
if (aId in this.plugins)
|
||||
aCallback(this.buildWrapper(this.plugins[aId]));
|
||||
else
|
||||
aCallback(null);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get Addons of a particular type.
|
||||
*
|
||||
* @param aTypes
|
||||
* An array of types to fetch. Can be null to get all types.
|
||||
* @param callback
|
||||
* A callback to pass an array of Addons to
|
||||
*/
|
||||
getAddonsByTypes: function(aTypes, aCallback) {
|
||||
if (aTypes && aTypes.indexOf("plugin") < 0) {
|
||||
aCallback([]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.plugins)
|
||||
this.buildPluginList();
|
||||
|
||||
let results = [];
|
||||
|
||||
for (let id in this.plugins)
|
||||
this.getAddonByID(id, (addon) => results.push(addon));
|
||||
|
||||
aCallback(results);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get Addons that have pending operations.
|
||||
*
|
||||
* @param aTypes
|
||||
* An array of types to fetch. Can be null to get all types
|
||||
* @param aCallback
|
||||
* A callback to pass an array of Addons to
|
||||
*/
|
||||
getAddonsWithOperationsByTypes: function(aTypes, aCallback) {
|
||||
aCallback([]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called to get the current AddonInstalls, optionally restricting by type.
|
||||
*
|
||||
* @param aTypes
|
||||
* An array of types or null to get all types
|
||||
* @param aCallback
|
||||
* A callback to pass the array of AddonInstalls to
|
||||
*/
|
||||
getInstallsByTypes: function(aTypes, aCallback) {
|
||||
aCallback([]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds a list of the current plugins reported by the plugin host
|
||||
*
|
||||
* @return a dictionary of plugins indexed by our generated ID
|
||||
*/
|
||||
getPluginList: function() {
|
||||
let tags = Cc["@mozilla.org/plugin/host;1"].
|
||||
getService(Ci.nsIPluginHost).
|
||||
getPluginTags({});
|
||||
|
||||
let list = {};
|
||||
let seenPlugins = {};
|
||||
for (let tag of tags) {
|
||||
if (!(tag.name in seenPlugins))
|
||||
seenPlugins[tag.name] = {};
|
||||
if (!(tag.description in seenPlugins[tag.name])) {
|
||||
let plugin = {
|
||||
id: getIDHashForString(tag.name + tag.description),
|
||||
name: tag.name,
|
||||
description: tag.description,
|
||||
tags: [tag]
|
||||
};
|
||||
|
||||
seenPlugins[tag.name][tag.description] = plugin;
|
||||
list[plugin.id] = plugin;
|
||||
}
|
||||
else {
|
||||
seenPlugins[tag.name][tag.description].tags.push(tag);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the list of known plugins from the plugin host
|
||||
*/
|
||||
buildPluginList: function() {
|
||||
this.plugins = this.getPluginList();
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the plugins from the plugin host by comparing the current plugins
|
||||
* to the last known list sending out any necessary API notifications for
|
||||
* changes.
|
||||
*/
|
||||
updatePluginList: function() {
|
||||
let newList = this.getPluginList();
|
||||
|
||||
let lostPlugins = Object.keys(this.plugins).filter(id => !(id in newList)).
|
||||
map(id => this.buildWrapper(this.plugins[id]));
|
||||
let newPlugins = Object.keys(newList).filter(id => !(id in this.plugins)).
|
||||
map(id => this.buildWrapper(newList[id]));
|
||||
let matchedIDs = Object.keys(newList).filter(id => id in this.plugins);
|
||||
|
||||
// The plugin host generates new tags for every plugin after a scan and
|
||||
// if the plugin's filename has changed then the disabled state won't have
|
||||
// been carried across, send out notifications for anything that has
|
||||
// changed (see bug 830267).
|
||||
let changedWrappers = [];
|
||||
for (let id of matchedIDs) {
|
||||
let oldWrapper = this.buildWrapper(this.plugins[id]);
|
||||
let newWrapper = this.buildWrapper(newList[id]);
|
||||
|
||||
if (newWrapper.isActive != oldWrapper.isActive) {
|
||||
AddonManagerPrivate.callAddonListeners(newWrapper.isActive ?
|
||||
"onEnabling" : "onDisabling",
|
||||
newWrapper, false);
|
||||
changedWrappers.push(newWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
// Notify about new installs
|
||||
for (let plugin of newPlugins) {
|
||||
AddonManagerPrivate.callInstallListeners("onExternalInstall", null,
|
||||
plugin, null, false);
|
||||
AddonManagerPrivate.callAddonListeners("onInstalling", plugin, false);
|
||||
}
|
||||
|
||||
// Notify for any plugins that have vanished.
|
||||
for (let plugin of lostPlugins)
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalling", plugin, false);
|
||||
|
||||
this.plugins = newList;
|
||||
|
||||
// Signal that new installs are complete
|
||||
for (let plugin of newPlugins)
|
||||
AddonManagerPrivate.callAddonListeners("onInstalled", plugin);
|
||||
|
||||
// Signal that enables/disables are complete
|
||||
for (let wrapper of changedWrappers) {
|
||||
AddonManagerPrivate.callAddonListeners(wrapper.isActive ?
|
||||
"onEnabled" : "onDisabled",
|
||||
wrapper);
|
||||
}
|
||||
|
||||
// Signal that uninstalls are complete
|
||||
for (let plugin of lostPlugins)
|
||||
AddonManagerPrivate.callAddonListeners("onUninstalled", plugin);
|
||||
}
|
||||
};
|
||||
|
||||
function isFlashPlugin(aPlugin) {
|
||||
for (let type of aPlugin.pluginMimeTypes) {
|
||||
if (type.type == FLASH_MIME_TYPE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Protected mode is win32-only, not win64
|
||||
function canDisableFlashProtectedMode(aPlugin) {
|
||||
return isFlashPlugin(aPlugin) && Services.appinfo.XPCOMABI == "x86-msvc";
|
||||
}
|
||||
|
||||
const wrapperMap = new WeakMap();
|
||||
let pluginFor = wrapper => wrapperMap.get(wrapper);
|
||||
|
||||
/**
|
||||
* The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to
|
||||
* public callers through the API.
|
||||
*/
|
||||
function PluginWrapper(id, name, description, tags) {
|
||||
wrapperMap.set(this, { id, name, description, tags });
|
||||
}
|
||||
|
||||
PluginWrapper.prototype = {
|
||||
get id() {
|
||||
return pluginFor(this).id;
|
||||
},
|
||||
|
||||
get type() {
|
||||
return "plugin";
|
||||
},
|
||||
|
||||
get name() {
|
||||
return pluginFor(this).name;
|
||||
},
|
||||
|
||||
get creator() {
|
||||
return null;
|
||||
},
|
||||
|
||||
get description() {
|
||||
return pluginFor(this).description.replace(/<\/?[a-z][^>]*>/gi, " ");
|
||||
},
|
||||
|
||||
get version() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
return tag.version;
|
||||
},
|
||||
|
||||
get homepageURL() {
|
||||
let { description } = pluginFor(this);
|
||||
if (/<A\s+HREF=[^>]*>/i.test(description))
|
||||
return /<A\s+HREF=["']?([^>"'\s]*)/i.exec(description)[1];
|
||||
return null;
|
||||
},
|
||||
|
||||
get isActive() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
return !tag.blocklisted && !tag.disabled;
|
||||
},
|
||||
|
||||
get appDisabled() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
return tag.blocklisted;
|
||||
},
|
||||
|
||||
get userDisabled() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
if (tag.disabled)
|
||||
return true;
|
||||
|
||||
if ((Services.prefs.getBoolPref("plugins.click_to_play") && tag.clicktoplay) ||
|
||||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
|
||||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE)
|
||||
return AddonManager.STATE_ASK_TO_ACTIVATE;
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
set userDisabled(val) {
|
||||
let previousVal = this.userDisabled;
|
||||
if (val === previousVal)
|
||||
return val;
|
||||
|
||||
let { tags } = pluginFor(this);
|
||||
|
||||
for (let tag of tags) {
|
||||
if (val === true)
|
||||
tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
|
||||
else if (val === false)
|
||||
tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
|
||||
else if (val == AddonManager.STATE_ASK_TO_ACTIVATE)
|
||||
tag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY;
|
||||
}
|
||||
|
||||
// If 'userDisabled' was 'true' and we're going to a state that's not
|
||||
// that, we're enabling, so call those listeners.
|
||||
if (previousVal === true && val !== true) {
|
||||
AddonManagerPrivate.callAddonListeners("onEnabling", this, false);
|
||||
AddonManagerPrivate.callAddonListeners("onEnabled", this);
|
||||
}
|
||||
|
||||
// If 'userDisabled' was not 'true' and we're going to a state where
|
||||
// it is, we're disabling, so call those listeners.
|
||||
if (previousVal !== true && val === true) {
|
||||
AddonManagerPrivate.callAddonListeners("onDisabling", this, false);
|
||||
AddonManagerPrivate.callAddonListeners("onDisabled", this);
|
||||
}
|
||||
|
||||
// If the 'userDisabled' value involved AddonManager.STATE_ASK_TO_ACTIVATE,
|
||||
// call the onPropertyChanged listeners.
|
||||
if (previousVal == AddonManager.STATE_ASK_TO_ACTIVATE ||
|
||||
val == AddonManager.STATE_ASK_TO_ACTIVATE) {
|
||||
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]);
|
||||
}
|
||||
|
||||
return val;
|
||||
},
|
||||
|
||||
get blocklistState() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
return bs.getPluginBlocklistState(tag);
|
||||
},
|
||||
|
||||
get blocklistURL() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
let bs = Cc["@mozilla.org/extensions/blocklist;1"].
|
||||
getService(Ci.nsIBlocklistService);
|
||||
return bs.getPluginBlocklistURL(tag);
|
||||
},
|
||||
|
||||
get size() {
|
||||
function getDirectorySize(aFile) {
|
||||
let size = 0;
|
||||
let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
|
||||
let entry;
|
||||
while ((entry = entries.nextFile)) {
|
||||
if (entry.isSymlink() || !entry.isDirectory())
|
||||
size += entry.fileSize;
|
||||
else
|
||||
size += getDirectorySize(entry);
|
||||
}
|
||||
entries.close();
|
||||
return size;
|
||||
}
|
||||
|
||||
let size = 0;
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
for (let tag of pluginFor(this).tags) {
|
||||
file.initWithPath(tag.fullpath);
|
||||
if (file.isDirectory())
|
||||
size += getDirectorySize(file);
|
||||
else
|
||||
size += file.fileSize;
|
||||
}
|
||||
return size;
|
||||
},
|
||||
|
||||
get pluginLibraries() {
|
||||
let libs = [];
|
||||
for (let tag of pluginFor(this).tags)
|
||||
libs.push(tag.filename);
|
||||
return libs;
|
||||
},
|
||||
|
||||
get pluginFullpath() {
|
||||
let paths = [];
|
||||
for (let tag of pluginFor(this).tags)
|
||||
paths.push(tag.fullpath);
|
||||
return paths;
|
||||
},
|
||||
|
||||
get pluginMimeTypes() {
|
||||
let types = [];
|
||||
for (let tag of pluginFor(this).tags) {
|
||||
let mimeTypes = tag.getMimeTypes({});
|
||||
let mimeDescriptions = tag.getMimeDescriptions({});
|
||||
let extensions = tag.getExtensions({});
|
||||
for (let i = 0; i < mimeTypes.length; i++) {
|
||||
let type = {};
|
||||
type.type = mimeTypes[i];
|
||||
type.description = mimeDescriptions[i];
|
||||
type.suffixes = extensions[i];
|
||||
|
||||
types.push(type);
|
||||
}
|
||||
}
|
||||
return types;
|
||||
},
|
||||
|
||||
get installDate() {
|
||||
let date = 0;
|
||||
for (let tag of pluginFor(this).tags) {
|
||||
date = Math.max(date, tag.lastModifiedTime);
|
||||
}
|
||||
return new Date(date);
|
||||
},
|
||||
|
||||
get scope() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
let path = tag.fullpath;
|
||||
// Plugins inside the application directory are in the application scope
|
||||
let dir = Services.dirsvc.get("APlugns", Ci.nsIFile);
|
||||
if (path.startsWith(dir.path))
|
||||
return AddonManager.SCOPE_APPLICATION;
|
||||
|
||||
// Plugins inside the profile directory are in the profile scope
|
||||
dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
if (path.startsWith(dir.path))
|
||||
return AddonManager.SCOPE_PROFILE;
|
||||
|
||||
// Plugins anywhere else in the user's home are in the user scope,
|
||||
// but not all platforms have a home directory.
|
||||
try {
|
||||
dir = Services.dirsvc.get("Home", Ci.nsIFile);
|
||||
if (path.startsWith(dir.path))
|
||||
return AddonManager.SCOPE_USER;
|
||||
} catch (e) {
|
||||
if (!e.result || e.result != Components.results.NS_ERROR_FAILURE)
|
||||
throw e;
|
||||
// Do nothing: missing "Home".
|
||||
}
|
||||
|
||||
// Any other locations are system scope
|
||||
return AddonManager.SCOPE_SYSTEM;
|
||||
},
|
||||
|
||||
get pendingOperations() {
|
||||
return AddonManager.PENDING_NONE;
|
||||
},
|
||||
|
||||
get operationsRequiringRestart() {
|
||||
return AddonManager.OP_NEEDS_RESTART_NONE;
|
||||
},
|
||||
|
||||
get permissions() {
|
||||
let { tags: [tag] } = pluginFor(this);
|
||||
let permissions = 0;
|
||||
if (tag.isEnabledStateLocked) {
|
||||
return permissions;
|
||||
}
|
||||
if (!this.appDisabled) {
|
||||
|
||||
if (this.userDisabled !== true)
|
||||
permissions |= AddonManager.PERM_CAN_DISABLE;
|
||||
|
||||
let blocklistState = this.blocklistState;
|
||||
let isCTPBlocklisted =
|
||||
(blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE ||
|
||||
blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
|
||||
|
||||
if (this.userDisabled !== AddonManager.STATE_ASK_TO_ACTIVATE &&
|
||||
(Services.prefs.getBoolPref("plugins.click_to_play") ||
|
||||
isCTPBlocklisted)) {
|
||||
permissions |= AddonManager.PERM_CAN_ASK_TO_ACTIVATE;
|
||||
}
|
||||
|
||||
if (this.userDisabled !== false && !isCTPBlocklisted) {
|
||||
permissions |= AddonManager.PERM_CAN_ENABLE;
|
||||
}
|
||||
}
|
||||
return permissions;
|
||||
},
|
||||
|
||||
get optionsType() {
|
||||
if (canDisableFlashProtectedMode(this)) {
|
||||
return AddonManager.OPTIONS_TYPE_INLINE;
|
||||
}
|
||||
return AddonManager.OPTIONS_TYPE_INLINE_INFO;
|
||||
},
|
||||
|
||||
get optionsURL() {
|
||||
return "chrome://mozapps/content/extensions/pluginPrefs.xul";
|
||||
},
|
||||
|
||||
get updateDate() {
|
||||
return this.installDate;
|
||||
},
|
||||
|
||||
get isCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get isPlatformCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get providesUpdatesSecurely() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get foreignInstall() {
|
||||
return true;
|
||||
},
|
||||
|
||||
isCompatibleWith: function(aAppVersion, aPlatformVersion) {
|
||||
return true;
|
||||
},
|
||||
|
||||
findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
|
||||
if ("onNoCompatibilityUpdateAvailable" in aListener)
|
||||
aListener.onNoCompatibilityUpdateAvailable(this);
|
||||
if ("onNoUpdateAvailable" in aListener)
|
||||
aListener.onNoUpdateAvailable(this);
|
||||
if ("onUpdateFinished" in aListener)
|
||||
aListener.onUpdateFinished(this);
|
||||
}
|
||||
};
|
||||
|
||||
AddonManagerPrivate.registerProvider(PluginProvider, [
|
||||
new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
|
||||
STRING_TYPE_NAME,
|
||||
AddonManager.VIEW_TYPE_LIST, 6000,
|
||||
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE)
|
||||
]);
|
||||
|
|
@ -1,467 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
const LOCAL_EME_SOURCES = [{
|
||||
"id": "gmp-eme-adobe",
|
||||
"src": "chrome://global/content/gmp-sources/eme-adobe.json"
|
||||
}, {
|
||||
"id": "gmp-gmpopenh264",
|
||||
"src": "chrome://global/content/gmp-sources/openh264.json"
|
||||
}, {
|
||||
"id": "gmp-widevinecdm",
|
||||
"src": "chrome://global/content/gmp-sources/widevinecdm.json"
|
||||
}];
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
|
||||
|
||||
Cu.importGlobalProperties(["XMLHttpRequest"]);
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/CertUtils.jsm");
|
||||
/* globals checkCert, BadCertHandler*/
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/NetUtil.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
/* globals GMPPrefs */
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "GMPPrefs",
|
||||
"resource://gre/modules/GMPUtils.jsm");
|
||||
|
||||
/* globals OS */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
|
||||
"resource://gre/modules/UpdateUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest",
|
||||
"resource://gre/modules/ServiceRequest.jsm");
|
||||
|
||||
// This exists so that tests can override the XHR behaviour for downloading
|
||||
// the addon update XML file.
|
||||
var CreateXHR = function() {
|
||||
return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
||||
createInstance(Ci.nsISupports);
|
||||
}
|
||||
|
||||
var logger = Log.repository.getLogger("addons.productaddons");
|
||||
|
||||
/**
|
||||
* Number of milliseconds after which we need to cancel `downloadXML`.
|
||||
*
|
||||
* Bug 1087674 suggests that the XHR we use in `downloadXML` may
|
||||
* never terminate in presence of network nuisances (e.g. strange
|
||||
* antivirus behavior). This timeout is a defensive measure to ensure
|
||||
* that we fail cleanly in such case.
|
||||
*/
|
||||
const TIMEOUT_DELAY_MS = 20000;
|
||||
// Chunk size for the incremental downloader
|
||||
const DOWNLOAD_CHUNK_BYTES_SIZE = 300000;
|
||||
// Incremental downloader interval
|
||||
const DOWNLOAD_INTERVAL = 0;
|
||||
// How much of a file to read into memory at a time for hashing
|
||||
const HASH_CHUNK_SIZE = 8192;
|
||||
|
||||
/**
|
||||
* Gets the status of an XMLHttpRequest either directly or from its underlying
|
||||
* channel.
|
||||
*
|
||||
* @param request
|
||||
* The XMLHttpRequest.
|
||||
* @return an integer status value.
|
||||
*/
|
||||
function getRequestStatus(request) {
|
||||
let status = null;
|
||||
try {
|
||||
status = request.status;
|
||||
}
|
||||
catch (e) {
|
||||
}
|
||||
|
||||
if (status != null) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return request.channel.QueryInterface(Ci.nsIRequest).status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads an XML document from a URL optionally testing the SSL certificate
|
||||
* for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to the DOM document downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let request = CreateXHR();
|
||||
// This is here to let unit test code override XHR
|
||||
if (request.wrappedJSObject) {
|
||||
request = request.wrappedJSObject;
|
||||
}
|
||||
request.open("GET", url, true);
|
||||
request.channel.notificationCallbacks = new BadCertHandler(allowNonBuiltIn);
|
||||
// Prevent the request from reading from the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
// Prevent the request from writing to the cache.
|
||||
request.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
|
||||
// Use conservative TLS settings. See bug 1325501.
|
||||
// TODO move to ServiceRequest.
|
||||
if (request.channel instanceof Ci.nsIHttpChannelInternal) {
|
||||
request.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
|
||||
}
|
||||
request.timeout = TIMEOUT_DELAY_MS;
|
||||
|
||||
request.overrideMimeType("text/xml");
|
||||
// The Cache-Control header is only interpreted by proxies and the
|
||||
// final destination. It does not help if a resource is already
|
||||
// cached locally.
|
||||
request.setRequestHeader("Cache-Control", "no-cache");
|
||||
// HTTP/1.0 servers might not implement Cache-Control and
|
||||
// might only implement Pragma: no-cache
|
||||
request.setRequestHeader("Pragma", "no-cache");
|
||||
|
||||
let fail = (event) => {
|
||||
let request = event.target;
|
||||
let status = getRequestStatus(request);
|
||||
let message = "Failed downloading XML, status: " + status + ", reason: " + event.type;
|
||||
logger.warn(message);
|
||||
let ex = new Error(message);
|
||||
ex.status = status;
|
||||
reject(ex);
|
||||
};
|
||||
|
||||
let success = (event) => {
|
||||
logger.info("Completed downloading document");
|
||||
let request = event.target;
|
||||
|
||||
try {
|
||||
checkCert(request.channel, allowNonBuiltIn, allowedCerts);
|
||||
} catch (ex) {
|
||||
logger.error("Request failed certificate checks: " + ex);
|
||||
ex.status = getRequestStatus(request);
|
||||
reject(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(request.responseXML);
|
||||
};
|
||||
|
||||
request.addEventListener("error", fail, false);
|
||||
request.addEventListener("abort", fail, false);
|
||||
request.addEventListener("timeout", fail, false);
|
||||
request.addEventListener("load", success, false);
|
||||
|
||||
logger.info("sending request to: " + url);
|
||||
request.send(null);
|
||||
});
|
||||
}
|
||||
|
||||
function downloadJSON(uri) {
|
||||
logger.info("fetching config from: " + uri);
|
||||
return new Promise((resolve, reject) => {
|
||||
let xmlHttp = new ServiceRequest({mozAnon: true});
|
||||
|
||||
xmlHttp.onload = function(aResponse) {
|
||||
resolve(JSON.parse(this.responseText));
|
||||
};
|
||||
|
||||
xmlHttp.onerror = function(e) {
|
||||
reject("Fetching " + uri + " results in error code: " + e.target.status);
|
||||
};
|
||||
|
||||
xmlHttp.open("GET", uri);
|
||||
xmlHttp.overrideMimeType("application/json");
|
||||
xmlHttp.send();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parses a list of add-ons from a DOM document.
|
||||
*
|
||||
* @param document
|
||||
* The DOM document to parse.
|
||||
* @return null if there is no <addons> element otherwise an object containing
|
||||
* an array of the addons listed and a field notifying whether the
|
||||
* fallback was used.
|
||||
*/
|
||||
function parseXML(document) {
|
||||
// Check that the root element is correct
|
||||
if (document.documentElement.localName != "updates") {
|
||||
throw new Error("got node name: " + document.documentElement.localName +
|
||||
", expected: updates");
|
||||
}
|
||||
|
||||
// Check if there are any addons elements in the updates element
|
||||
let addons = document.querySelector("updates:root > addons");
|
||||
if (!addons) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let results = [];
|
||||
let addonList = document.querySelectorAll("updates:root > addons > addon");
|
||||
for (let addonElement of addonList) {
|
||||
let addon = {};
|
||||
|
||||
for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) {
|
||||
if (addonElement.hasAttribute(name)) {
|
||||
addon[name] = addonElement.getAttribute(name);
|
||||
}
|
||||
}
|
||||
addon.size = Number(addon.size) || undefined;
|
||||
|
||||
results.push(addon);
|
||||
}
|
||||
|
||||
return {
|
||||
usedFallback: false,
|
||||
gmpAddons: results
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If downloading from the network fails (AUS server is down),
|
||||
* load the sources from local build configuration.
|
||||
*/
|
||||
function downloadLocalConfig() {
|
||||
|
||||
if (!GMPPrefs.get(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
|
||||
logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
|
||||
return Promise.resolve({usedFallback: true, gmpAddons: []});
|
||||
}
|
||||
|
||||
return Promise.all(LOCAL_EME_SOURCES.map(conf => {
|
||||
return downloadJSON(conf.src).then(addons => {
|
||||
|
||||
let platforms = addons.vendors[conf.id].platforms;
|
||||
let target = Services.appinfo.OS + "_" + UpdateUtils.ABI;
|
||||
let details = null;
|
||||
|
||||
while (!details) {
|
||||
if (!(target in platforms)) {
|
||||
// There was no matching platform so return false, this addon
|
||||
// will be filtered from the results below
|
||||
logger.info("no details found for: " + target);
|
||||
return false;
|
||||
}
|
||||
// Field either has the details of the binary or is an alias
|
||||
// to another build target key that does
|
||||
if (platforms[target].alias) {
|
||||
target = platforms[target].alias;
|
||||
} else {
|
||||
details = platforms[target];
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("found plugin: " + conf.id);
|
||||
return {
|
||||
"id": conf.id,
|
||||
"URL": details.fileUrl,
|
||||
"hashFunction": addons.hashFunction,
|
||||
"hashValue": details.hashValue,
|
||||
"version": addons.vendors[conf.id].version,
|
||||
"size": details.filesize
|
||||
};
|
||||
});
|
||||
})).then(addons => {
|
||||
|
||||
// Some filters may not match this platform so
|
||||
// filter those out
|
||||
addons = addons.filter(x => x !== false);
|
||||
|
||||
return {
|
||||
usedFallback: true,
|
||||
gmpAddons: addons
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads file from a URL using XHR.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @return a promise that resolves to the path of a temporary file or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
function downloadFile(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let xhr = new XMLHttpRequest();
|
||||
xhr.onload = function(response) {
|
||||
logger.info("downloadXHR File download. status=" + xhr.status);
|
||||
if (xhr.status != 200 && xhr.status != 206) {
|
||||
reject(Components.Exception("File download failed", xhr.status));
|
||||
return;
|
||||
}
|
||||
Task.spawn(function* () {
|
||||
let f = yield OS.File.openUnique(OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon"));
|
||||
let path = f.path;
|
||||
logger.info(`Downloaded file will be saved to ${path}`);
|
||||
yield f.file.close();
|
||||
yield OS.File.writeAtomic(path, new Uint8Array(xhr.response));
|
||||
return path;
|
||||
}).then(resolve, reject);
|
||||
};
|
||||
|
||||
let fail = (event) => {
|
||||
let request = event.target;
|
||||
let status = getRequestStatus(request);
|
||||
let message = "Failed downloading via XHR, status: " + status + ", reason: " + event.type;
|
||||
logger.warn(message);
|
||||
let ex = new Error(message);
|
||||
ex.status = status;
|
||||
reject(ex);
|
||||
};
|
||||
xhr.addEventListener("error", fail);
|
||||
xhr.addEventListener("abort", fail);
|
||||
|
||||
xhr.responseType = "arraybuffer";
|
||||
try {
|
||||
xhr.open("GET", url);
|
||||
// Use conservative TLS settings. See bug 1325501.
|
||||
// TODO move to ServiceRequest.
|
||||
if (xhr.channel instanceof Ci.nsIHttpChannelInternal) {
|
||||
xhr.channel.QueryInterface(Ci.nsIHttpChannelInternal).beConservative = true;
|
||||
}
|
||||
xhr.send(null);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a string containing binary values to hex.
|
||||
*/
|
||||
function binaryToHex(input) {
|
||||
let result = "";
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
let hex = input.charCodeAt(i).toString(16);
|
||||
if (hex.length == 1) {
|
||||
hex = "0" + hex;
|
||||
}
|
||||
result += hex;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the hash of a file.
|
||||
*
|
||||
* @param hashFunction
|
||||
* The type of hash function to use, must be supported by nsICryptoHash.
|
||||
* @param path
|
||||
* The path of the file to hash.
|
||||
* @return a promise that resolves to hash of the file or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
var computeHash = Task.async(function*(hashFunction, path) {
|
||||
let file = yield OS.File.open(path, { existing: true, read: true });
|
||||
try {
|
||||
let hasher = Cc["@mozilla.org/security/hash;1"].
|
||||
createInstance(Ci.nsICryptoHash);
|
||||
hasher.initWithString(hashFunction);
|
||||
|
||||
let bytes;
|
||||
do {
|
||||
bytes = yield file.read(HASH_CHUNK_SIZE);
|
||||
hasher.update(bytes, bytes.length);
|
||||
} while (bytes.length == HASH_CHUNK_SIZE);
|
||||
|
||||
return binaryToHex(hasher.finish(false));
|
||||
}
|
||||
finally {
|
||||
yield file.close();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Verifies that a downloaded file matches what was expected.
|
||||
*
|
||||
* @param properties
|
||||
* The properties to check, `size` and `hashFunction` with `hashValue`
|
||||
* are supported. Any properties missing won't be checked.
|
||||
* @param path
|
||||
* The path of the file to check.
|
||||
* @return a promise that resolves if the file matched or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
var verifyFile = Task.async(function*(properties, path) {
|
||||
if (properties.size !== undefined) {
|
||||
let stat = yield OS.File.stat(path);
|
||||
if (stat.size != properties.size) {
|
||||
throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
if (properties.hashFunction !== undefined) {
|
||||
let expectedDigest = properties.hashValue.toLowerCase();
|
||||
let digest = yield computeHash(properties.hashFunction, path);
|
||||
if (digest != expectedDigest) {
|
||||
throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest + "`.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const ProductAddonChecker = {
|
||||
/**
|
||||
* Downloads a list of add-ons from a URL optionally testing the SSL
|
||||
* certificate for certain attributes.
|
||||
*
|
||||
* @param url
|
||||
* The url to download from.
|
||||
* @param allowNonBuiltIn
|
||||
* Whether to trust SSL certificates without a built-in CA issuer.
|
||||
* @param allowedCerts
|
||||
* The list of certificate attributes to match the SSL certificate
|
||||
* against or null to skip checks.
|
||||
* @return a promise that resolves to an object containing the list of add-ons
|
||||
* and whether the local fallback was used, or rejects with a JS
|
||||
* exception in case of error.
|
||||
*/
|
||||
getProductAddonList: function(url, allowNonBuiltIn = false, allowedCerts = null) {
|
||||
if (!GMPPrefs.get(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
|
||||
logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
|
||||
return Promise.resolve({usedFallback: true, gmpAddons: []});
|
||||
}
|
||||
|
||||
return downloadXML(url, allowNonBuiltIn, allowedCerts)
|
||||
.then(parseXML)
|
||||
.catch(downloadLocalConfig);
|
||||
},
|
||||
|
||||
/**
|
||||
* Downloads an add-on to a local file and checks that it matches the expected
|
||||
* file. The caller is responsible for deleting the temporary file returned.
|
||||
*
|
||||
* @param addon
|
||||
* The addon to download.
|
||||
* @return a promise that resolves to the temporary file downloaded or rejects
|
||||
* with a JS exception in case of error.
|
||||
*/
|
||||
downloadAddon: Task.async(function*(addon) {
|
||||
let path = yield downloadFile(addon.URL);
|
||||
try {
|
||||
yield verifyFile(addon, path);
|
||||
return path;
|
||||
}
|
||||
catch (e) {
|
||||
yield OS.File.remove(path);
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
var hunspell, dir;
|
||||
|
||||
function startup(data) {
|
||||
hunspell = Components.classes["@mozilla.org/spellchecker/engine;1"]
|
||||
.getService(Components.interfaces.mozISpellCheckingEngine);
|
||||
dir = data.installPath.clone();
|
||||
dir.append("dictionaries");
|
||||
hunspell.addDirectory(dir);
|
||||
}
|
||||
|
||||
function shutdown() {
|
||||
hunspell.removeDirectory(dir);
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Extension.jsm");
|
||||
|
||||
var extension;
|
||||
|
||||
const BOOTSTRAP_REASON_TO_STRING_MAP = {
|
||||
1: "APP_STARTUP",
|
||||
2: "APP_SHUTDOWN",
|
||||
3: "ADDON_ENABLE",
|
||||
4: "ADDON_DISABLE",
|
||||
5: "ADDON_INSTALL",
|
||||
6: "ADDON_UNINSTALL",
|
||||
7: "ADDON_UPGRADE",
|
||||
8: "ADDON_DOWNGRADE",
|
||||
}
|
||||
|
||||
function install(data, reason)
|
||||
{
|
||||
}
|
||||
|
||||
function startup(data, reason)
|
||||
{
|
||||
extension = new Extension(data, BOOTSTRAP_REASON_TO_STRING_MAP[reason]);
|
||||
extension.startup();
|
||||
}
|
||||
|
||||
function shutdown(data, reason)
|
||||
{
|
||||
extension.shutdown();
|
||||
}
|
||||
|
||||
function uninstall(data, reason)
|
||||
{
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,36 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXTRA_JS_MODULES.addons += [
|
||||
'AddonLogging.jsm',
|
||||
'AddonRepository.jsm',
|
||||
'AddonRepository_SQLiteMigrator.jsm',
|
||||
'AddonUpdateChecker.jsm',
|
||||
'APIExtensionBootstrap.js',
|
||||
'Content.js',
|
||||
'E10SAddonsRollout.jsm',
|
||||
'GMPProvider.jsm',
|
||||
'LightweightThemeImageOptimizer.jsm',
|
||||
'ProductAddonChecker.jsm',
|
||||
'SpellCheckDictionaryBootstrap.js',
|
||||
'WebExtensionBootstrap.js',
|
||||
'XPIProvider.jsm',
|
||||
'XPIProviderUtils.js',
|
||||
]
|
||||
|
||||
TESTING_JS_MODULES += [
|
||||
'AddonTestUtils.jsm',
|
||||
]
|
||||
|
||||
# Don't ship unused providers on Android
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
|
||||
EXTRA_JS_MODULES.addons += [
|
||||
'PluginProvider.jsm',
|
||||
]
|
||||
|
||||
EXTRA_PP_JS_MODULES.addons += [
|
||||
'AddonConstants.jsm',
|
||||
]
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
toolkit.jar:
|
||||
#ifndef MOZ_FENNEC
|
||||
% content mozapps %content/mozapps/
|
||||
* content/mozapps/extensions/extensions.xul (content/extensions.xul)
|
||||
content/mozapps/extensions/extensions.css (content/extensions.css)
|
||||
content/mozapps/extensions/extensions.js (content/extensions.js)
|
||||
* content/mozapps/extensions/extensions.xml (content/extensions.xml)
|
||||
content/mozapps/extensions/updateinfo.xsl (content/updateinfo.xsl)
|
||||
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/blocklist.xul (content/blocklist.xul)
|
||||
content/mozapps/extensions/blocklist.js (content/blocklist.js)
|
||||
content/mozapps/extensions/blocklist.css (content/blocklist.css)
|
||||
content/mozapps/extensions/blocklist.xml (content/blocklist.xml)
|
||||
* 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)
|
||||
content/mozapps/extensions/newaddon.xul (content/newaddon.xul)
|
||||
content/mozapps/extensions/newaddon.js (content/newaddon.js)
|
||||
content/mozapps/extensions/pluginPrefs.xul (content/pluginPrefs.xul)
|
||||
content/mozapps/extensions/gmpPrefs.xul (content/gmpPrefs.xul)
|
||||
content/mozapps/extensions/OpenH264-license.txt (content/OpenH264-license.txt)
|
||||
#endif
|
||||
content/mozapps/extensions/setting.xml (content/setting.xml)
|
||||
content/mozapps/xpinstall/xpinstallConfirm.xul (content/xpinstallConfirm.xul)
|
||||
content/mozapps/xpinstall/xpinstallConfirm.js (content/xpinstallConfirm.js)
|
||||
content/mozapps/xpinstall/xpinstallConfirm.css (content/xpinstallConfirm.css)
|
||||
content/mozapps/xpinstall/xpinstallItem.xml (content/xpinstallItem.xml)
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
SPHINX_TREES['addon-manager'] = 'docs'
|
||||
|
||||
if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
|
||||
DEFINES['MOZ_FENNEC'] = True
|
||||
|
||||
DIRS += ['internal']
|
||||
TEST_DIRS += ['test']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'amIAddonManager.idl',
|
||||
'amIAddonPathService.idl',
|
||||
'amIWebInstaller.idl',
|
||||
'amIWebInstallListener.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'extensions'
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'addonManager.js',
|
||||
'amContentHandler.js',
|
||||
'amInstallTrigger.js',
|
||||
'amWebAPI.js',
|
||||
'amWebInstallListener.js',
|
||||
'nsBlocklistService.js',
|
||||
'nsBlocklistServiceContent.js',
|
||||
]
|
||||
|
||||
EXTRA_PP_COMPONENTS += [
|
||||
'extensions.manifest',
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'AddonManager.jsm',
|
||||
'ChromeManifestParser.jsm',
|
||||
'DeferredSave.jsm',
|
||||
'LightweightThemeManager.jsm',
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'AddonContentPolicy.h',
|
||||
'AddonManagerWebAPI.h',
|
||||
'AddonPathService.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AddonContentPolicy.cpp',
|
||||
'AddonManagerWebAPI.cpp',
|
||||
'AddonPathService.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/dom/base',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Add-ons Manager')
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,113 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cr = Components.results;
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const kMissingAPIMessage = "Unsupported blocklist call in the child process."
|
||||
|
||||
/*
|
||||
* A lightweight blocklist proxy for the content process that traps plugin
|
||||
* related blocklist checks and forwards them to the parent. This interface is
|
||||
* primarily designed to insure overlays work.. it does not control plugin
|
||||
* or addon loading.
|
||||
*/
|
||||
|
||||
function Blocklist() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
Blocklist.prototype = {
|
||||
classID: Components.ID("{e0a106ed-6ad4-47a4-b6af-2f1c8aa4712d}"),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsIBlocklistService]),
|
||||
|
||||
init: function() {
|
||||
Services.cpmm.addMessageListener("Blocklist:blocklistInvalidated", this);
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
Services.cpmm.removeMessageListener("Blocklist:blocklistInvalidated", this);
|
||||
Services.obs.removeObserver(this, "xpcom-shutdown", false);
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "xpcom-shutdown":
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
// Message manager message handlers
|
||||
receiveMessage: function(aMsg) {
|
||||
switch (aMsg.name) {
|
||||
case "Blocklist:blocklistInvalidated":
|
||||
Services.obs.notifyObservers(null, "blocklist-updated", null);
|
||||
Services.cpmm.sendAsyncMessage("Blocklist:content-blocklist-updated");
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unknown blocklist message received from content: " + aMsg.name);
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* A helper that queries key data from a plugin or addon object
|
||||
* and generates a simple data wrapper suitable for ipc. We hand
|
||||
* these directly to the nsBlockListService in the parent which
|
||||
* doesn't query for much.. allowing us to get away with this.
|
||||
*/
|
||||
flattenObject: function(aTag) {
|
||||
// Based on debugging the nsBlocklistService, these are the props the
|
||||
// parent side will check on our objects.
|
||||
let props = ["name", "description", "filename", "version"];
|
||||
let dataWrapper = {};
|
||||
for (let prop of props) {
|
||||
dataWrapper[prop] = aTag[prop];
|
||||
}
|
||||
return dataWrapper;
|
||||
},
|
||||
|
||||
// We support the addon methods here for completeness, but content currently
|
||||
// only calls getPluginBlocklistState.
|
||||
|
||||
isAddonBlocklisted: function(aAddon, aAppVersion, aToolkitVersion) {
|
||||
return true;
|
||||
},
|
||||
|
||||
getAddonBlocklistState: function(aAddon, aAppVersion, aToolkitVersion) {
|
||||
return Components.interfaces.nsIBlocklistService.STATE_BLOCKED;
|
||||
},
|
||||
|
||||
// There are a few callers in layout that rely on this.
|
||||
getPluginBlocklistState: function(aPluginTag, aAppVersion, aToolkitVersion) {
|
||||
return Services.cpmm.sendSyncMessage("Blocklist:getPluginBlocklistState", {
|
||||
addonData: this.flattenObject(aPluginTag),
|
||||
appVersion: aAppVersion,
|
||||
toolkitVersion: aToolkitVersion
|
||||
})[0];
|
||||
},
|
||||
|
||||
getAddonBlocklistURL: function(aAddon, aAppVersion, aToolkitVersion) {
|
||||
throw new Error(kMissingAPIMessage);
|
||||
},
|
||||
|
||||
getPluginBlocklistURL: function(aPluginTag) {
|
||||
throw new Error(kMissingAPIMessage);
|
||||
},
|
||||
|
||||
getPluginInfoURL: function(aPluginTag) {
|
||||
throw new Error(kMissingAPIMessage);
|
||||
}
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Blocklist]);
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// This file is a test-only JSM containing utility methods for
|
||||
// interacting with the add-ons manager.
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = [
|
||||
"AddonManagerTesting",
|
||||
];
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
|
||||
this.AddonManagerTesting = {
|
||||
/**
|
||||
* Get the add-on that is specified by its ID.
|
||||
*
|
||||
* @return {Promise<Object>} A promise that resolves returning the found addon or null
|
||||
* if it is not found.
|
||||
*/
|
||||
getAddonById: function (id) {
|
||||
return new Promise(resolve => AddonManager.getAddonByID(id, addon => resolve(addon)));
|
||||
},
|
||||
|
||||
/**
|
||||
* Uninstall an add-on that is specified by its ID.
|
||||
*
|
||||
* The returned promise resolves on successful uninstall and rejects
|
||||
* if the add-on is not unknown.
|
||||
*
|
||||
* @return Promise<restartRequired>
|
||||
*/
|
||||
uninstallAddonByID: function (id) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getAddonByID(id, (addon) => {
|
||||
if (!addon) {
|
||||
deferred.reject(new Error("Add-on is not known: " + id));
|
||||
return;
|
||||
}
|
||||
|
||||
let listener = {
|
||||
onUninstalling: function (addon, needsRestart) {
|
||||
if (addon.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (needsRestart) {
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve(true);
|
||||
}
|
||||
},
|
||||
|
||||
onUninstalled: function (addon) {
|
||||
if (addon.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.resolve(false);
|
||||
},
|
||||
|
||||
onOperationCancelled: function (addon) {
|
||||
if (addon.id != id) {
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.removeAddonListener(listener);
|
||||
deferred.reject(new Error("Uninstall cancelled."));
|
||||
},
|
||||
};
|
||||
|
||||
AddonManager.addAddonListener(listener);
|
||||
addon.uninstall();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Install an XPI add-on from a URL.
|
||||
*
|
||||
* @return Promise<addon>
|
||||
*/
|
||||
installXPIFromURL: function (url, hash, name, iconURL, version) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getInstallForURL(url, (install) => {
|
||||
let fail = () => { deferred.reject(new Error("Add-on install failed.")) };
|
||||
|
||||
let listener = {
|
||||
onDownloadCancelled: fail,
|
||||
onDownloadFailed: fail,
|
||||
onInstallCancelled: fail,
|
||||
onInstallFailed: fail,
|
||||
onInstallEnded: function (install, addon) {
|
||||
deferred.resolve(addon);
|
||||
},
|
||||
};
|
||||
|
||||
install.addListener(listener);
|
||||
install.install();
|
||||
}, "application/x-xpinstall", hash, name, iconURL, version);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
};
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
ADDONSRC = $(srcdir)/addons
|
||||
TESTROOT = $(CURDIR)/$(DEPTH)/_tests/xpcshell/$(relativesrcdir)
|
||||
TESTXPI = $(TESTROOT)/xpcshell/addons
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
rm -rf $(TESTXPI)
|
||||
$(NSINSTALL) -D $(TESTXPI)
|
||||
if [ -d $(ADDONSRC) ]; then \
|
||||
$(EXIT_ON_ERROR) \
|
||||
for dir in $(ADDONSRC)/*; do \
|
||||
base=`basename $$dir` ; \
|
||||
(cd $$dir && zip -qr $(TESTXPI)/$$base.xpi *) \
|
||||
done \
|
||||
fi
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>hardblock@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Hardblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>hardblock@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Hardblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>hardblock@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Hardblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>regexpblock@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>RegExp-blocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>regexpblock@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>RegExp-blocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>regexpblock@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>RegExp-blocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock1@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock1@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock1@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock2@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock2@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock2@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock3@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock3@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock3@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock4@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock4@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock4@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock5@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:internalName>test/1.0</em:internalName>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock5@tests.mozilla.org</em:id>
|
||||
<em:version>2.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:internalName>test/1.0</em:internalName>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>softblock5@tests.mozilla.org</em:id>
|
||||
<em:version>3.0</em:version>
|
||||
<em:name>Softblocked add-on</em:name>
|
||||
<em:internalName>test/1.0</em:internalName>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var seenGlobals = new Set();
|
||||
var scope = this;
|
||||
function checkGlobal(name, type) {
|
||||
if (scope[name] && typeof(scope[name]) == type)
|
||||
seenGlobals.add(name);
|
||||
}
|
||||
|
||||
var wrapped = {};
|
||||
Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals", null);
|
||||
for (let [name, type] of wrapped.expectedGlobals) {
|
||||
checkGlobal(name, type);
|
||||
}
|
||||
|
||||
function install(data, reason) {
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
Services.obs.notifyObservers({
|
||||
wrappedJSObject: seenGlobals
|
||||
}, "bootstrap-seen-globals", null);
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>bootstrap_globals@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test Bootstrap Globals</em:name>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>min1max1@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test minVersion 1 maxVersion 1</em:name>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>min1max2@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>2</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test minVersion 1 maxVersion 2</em:name>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>min1max3@tests.mozilla.org</em:id>
|
||||
<em:version>1.0</em:version>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||
<em:minVersion>1</em:minVersion>
|
||||
<em:maxVersion>3</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>Test minVersion 1 maxVersion 3</em:name>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue