Bug 1561435 - Format toolkit/mozapps/, a=automatic-formatting

# ignore-this-changeset

Differential Revision: https://phabricator.services.mozilla.com/D36057

--HG--
extra : source : fc2db4029f7eeae4942b42ca0b23873f150012b2
This commit is contained in:
Victor Porof 2019-07-05 11:17:14 +02:00
Родитель b503616295
Коммит 584e273f90
496 изменённых файлов: 37528 добавлений и 19672 удалений

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

@ -28,16 +28,18 @@ const LAST_DIR_PREF = "browser.download.lastDir";
const SAVE_PER_SITE_PREF = LAST_DIR_PREF + ".savePerSite";
const nsIFile = Ci.nsIFile;
var EXPORTED_SYMBOLS = [ "DownloadLastDir" ];
var EXPORTED_SYMBOLS = ["DownloadLastDir"];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
let nonPrivateLoadContext = Cu.createLoadContext();
let privateLoadContext = Cu.createPrivateLoadContext();
var observer = {
QueryInterface: ChromeUtils.generateQI(["nsIObserver",
"nsISupportsWeakReference"]),
QueryInterface: ChromeUtils.generateQI([
"nsIObserver",
"nsISupportsWeakReference",
]),
observe(aSubject, aTopic, aData) {
switch (aTopic) {
@ -46,12 +48,14 @@ var observer = {
break;
case "browser:purge-session-history":
gDownloadLastDirFile = null;
if (Services.prefs.prefHasUserValue(LAST_DIR_PREF))
if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) {
Services.prefs.clearUserPref(LAST_DIR_PREF);
}
// Ensure that purging session history causes both the session-only PB cache
// and persistent prefs to be cleared.
let cps2 = Cc["@mozilla.org/content-pref/service;1"].
getService(Ci.nsIContentPrefService2);
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
cps2.removeByName(LAST_DIR_PREF, nonPrivateLoadContext);
cps2.removeByName(LAST_DIR_PREF, privateLoadContext);
@ -82,14 +86,13 @@ function isContentPrefEnabled() {
var gDownloadLastDirFile = readLastDirPref();
function DownloadLastDir(aWindow) {
let loadContext = aWindow.docShell
.QueryInterface(Ci.nsILoadContext);
let loadContext = aWindow.docShell.QueryInterface(Ci.nsILoadContext);
// Need this in case the real thing has gone away by the time we need it.
// We only care about the private browsing state. All the rest of the
// load context isn't of interest to the content pref service.
this.fakeContext = loadContext.usePrivateBrowsing ?
privateLoadContext :
nonPrivateLoadContext;
this.fakeContext = loadContext.usePrivateBrowsing
? privateLoadContext
: nonPrivateLoadContext;
}
DownloadLastDir.prototype = {
@ -97,19 +100,25 @@ DownloadLastDir.prototype = {
return this.fakeContext.usePrivateBrowsing;
},
// compat shims
get file() { return this._getLastFile(); },
set file(val) { this.setFile(null, val); },
get file() {
return this._getLastFile();
},
set file(val) {
this.setFile(null, val);
},
cleanupPrivateFile() {
gDownloadLastDirFile = null;
},
_getLastFile() {
if (gDownloadLastDirFile && !gDownloadLastDirFile.exists())
if (gDownloadLastDirFile && !gDownloadLastDirFile.exists()) {
gDownloadLastDirFile = null;
}
if (this.isPrivate()) {
if (!gDownloadLastDirFile)
if (!gDownloadLastDirFile) {
gDownloadLastDirFile = readLastDirPref();
}
return gDownloadLastDirFile;
}
return readLastDirPref();
@ -123,18 +132,20 @@ DownloadLastDir.prototype = {
}
let uri = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
let cps2 = Cc["@mozilla.org/content-pref/service;1"]
.getService(Ci.nsIContentPrefService2);
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
let result = null;
cps2.getByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext, {
handleResult: aResult => result = aResult,
handleResult: aResult => (result = aResult),
handleCompletion(aReason) {
let file = plainPrefFile;
if (aReason == Ci.nsIContentPrefCallback2.COMPLETE_OK &&
result instanceof Ci.nsIContentPref) {
if (
aReason == Ci.nsIContentPrefCallback2.COMPLETE_OK &&
result instanceof Ci.nsIContentPref
) {
try {
file = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(result.value);
} catch (e) {
file = plainPrefFile;
@ -148,18 +159,21 @@ DownloadLastDir.prototype = {
setFile(aURI, aFile) {
if (aURI && isContentPrefEnabled()) {
let uri = aURI instanceof Ci.nsIURI ? aURI.spec : aURI;
let cps2 = Cc["@mozilla.org/content-pref/service;1"]
.getService(Ci.nsIContentPrefService2);
if (aFile instanceof Ci.nsIFile)
let cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
Ci.nsIContentPrefService2
);
if (aFile instanceof Ci.nsIFile) {
cps2.set(uri, LAST_DIR_PREF, aFile.path, this.fakeContext);
else
} else {
cps2.removeByDomainAndName(uri, LAST_DIR_PREF, this.fakeContext);
}
}
if (this.isPrivate()) {
if (aFile instanceof Ci.nsIFile)
if (aFile instanceof Ci.nsIFile) {
gDownloadLastDirFile = aFile.clone();
else
} else {
gDownloadLastDirFile = null;
}
} else if (aFile instanceof Ci.nsIFile) {
Services.prefs.setComplexValue(LAST_DIR_PREF, nsIFile, aFile);
} else if (Services.prefs.prefHasUserValue(LAST_DIR_PREF)) {

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

@ -5,7 +5,7 @@
"use strict";
var EXPORTED_SYMBOLS = [ "DownloadUtils" ];
var EXPORTED_SYMBOLS = ["DownloadUtils"];
/**
* This module provides the DownloadUtils object which contains useful methods
@ -37,20 +37,26 @@ var EXPORTED_SYMBOLS = [ "DownloadUtils" ];
* convertTimeUnits(double aSecs)
*/
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "PluralForm",
"resource://gre/modules/PluralForm.jsm");
ChromeUtils.defineModuleGetter(
this,
"PluralForm",
"resource://gre/modules/PluralForm.jsm"
);
const MS_PER_DAY = 24 * 60 * 60 * 1000;
var localeNumberFormatCache = new Map();
function getLocaleNumberFormat(fractionDigits) {
if (!localeNumberFormatCache.has(fractionDigits)) {
localeNumberFormatCache.set(fractionDigits,
new Services.intl.NumberFormat(undefined,
{ maximumFractionDigits: fractionDigits,
minimumFractionDigits: fractionDigits }));
localeNumberFormatCache.set(
fractionDigits,
new Services.intl.NumberFormat(undefined, {
maximumFractionDigits: fractionDigits,
minimumFractionDigits: fractionDigits,
})
);
}
return localeNumberFormatCache.get(fractionDigits);
}
@ -85,7 +91,7 @@ Object.defineProperty(this, "gBundle", {
enumerable: true,
get() {
delete this.gBundle;
return this.gBundle = Services.strings.createBundle(kDownloadProperties);
return (this.gBundle = Services.strings.createBundle(kDownloadProperties));
},
});
@ -109,18 +115,33 @@ var DownloadUtils = {
* Last time remaining in seconds or Infinity for unknown
* @return A pair: [download status text, new value of "last seconds"]
*/
getDownloadStatus: function DU_getDownloadStatus(aCurrBytes, aMaxBytes,
aSpeed, aLastSec) {
let [transfer, timeLeft, newLast, normalizedSpeed]
= this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
getDownloadStatus: function DU_getDownloadStatus(
aCurrBytes,
aMaxBytes,
aSpeed,
aLastSec
) {
let [
transfer,
timeLeft,
newLast,
normalizedSpeed,
] = this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
let [rate, unit] = DownloadUtils.convertByteUnits(normalizedSpeed);
let status;
if (rate === "Infinity") {
// Infinity download speed doesn't make sense. Show a localized phrase instead.
let params = [transfer, gBundle.GetStringFromName(gStr.infiniteRate), timeLeft];
status = gBundle.formatStringFromName(gStr.statusFormatInfiniteRate, params);
let params = [
transfer,
gBundle.GetStringFromName(gStr.infiniteRate),
timeLeft,
];
status = gBundle.formatStringFromName(
gStr.statusFormatInfiniteRate,
params
);
} else {
let params = [transfer, rate, unit, timeLeft];
status = gBundle.formatStringFromName(gStr.statusFormat, params);
@ -144,11 +165,18 @@ var DownloadUtils = {
* Last time remaining in seconds or Infinity for unknown
* @return A pair: [download status text, new value of "last seconds"]
*/
getDownloadStatusNoRate:
function DU_getDownloadStatusNoRate(aCurrBytes, aMaxBytes, aSpeed,
aLastSec) {
let [transfer, timeLeft, newLast]
= this._deriveTransferRate(aCurrBytes, aMaxBytes, aSpeed, aLastSec);
getDownloadStatusNoRate: function DU_getDownloadStatusNoRate(
aCurrBytes,
aMaxBytes,
aSpeed,
aLastSec
) {
let [transfer, timeLeft, newLast] = this._deriveTransferRate(
aCurrBytes,
aMaxBytes,
aSpeed,
aLastSec
);
let params = [transfer, timeLeft];
let status = gBundle.formatStringFromName(gStr.statusFormatNoRate, params);
@ -169,19 +197,25 @@ var DownloadUtils = {
* @return A triple: [amount transferred string, time remaining string,
* new value of "last seconds"]
*/
_deriveTransferRate: function DU__deriveTransferRate(aCurrBytes,
aMaxBytes, aSpeed,
aLastSec) {
if (aMaxBytes == null)
_deriveTransferRate: function DU__deriveTransferRate(
aCurrBytes,
aMaxBytes,
aSpeed,
aLastSec
) {
if (aMaxBytes == null) {
aMaxBytes = -1;
if (aSpeed == null)
}
if (aSpeed == null) {
aSpeed = -1;
if (aLastSec == null)
}
if (aLastSec == null) {
aLastSec = Infinity;
}
// Calculate the time remaining if we have valid values
let seconds = (aSpeed > 0) && (aMaxBytes > 0) ?
(aMaxBytes - aCurrBytes) / aSpeed : -1;
let seconds =
aSpeed > 0 && aMaxBytes > 0 ? (aMaxBytes - aCurrBytes) / aSpeed : -1;
let transfer = DownloadUtils.getTransferTotal(aCurrBytes, aMaxBytes);
let [timeLeft, newLast] = DownloadUtils.getTimeLeft(seconds, aLastSec);
@ -200,8 +234,9 @@ var DownloadUtils = {
* @return The transfer progress text
*/
getTransferTotal: function DU_getTransferTotal(aCurrBytes, aMaxBytes) {
if (aMaxBytes == null)
if (aMaxBytes == null) {
aMaxBytes = -1;
}
let [progress, progressUnits] = DownloadUtils.convertByteUnits(aCurrBytes);
let [total, totalUnits] = DownloadUtils.convertByteUnits(aMaxBytes);
@ -210,25 +245,13 @@ var DownloadUtils = {
let name, values;
if (aMaxBytes < 0) {
name = gStr.transferNoTotal;
values = [
progress,
progressUnits,
];
values = [progress, progressUnits];
} else if (progressUnits == totalUnits) {
name = gStr.transferSameUnits;
values = [
progress,
total,
totalUnits,
];
values = [progress, total, totalUnits];
} else {
name = gStr.transferDiffUnits;
values = [
progress,
progressUnits,
total,
totalUnits,
];
values = [progress, progressUnits, total, totalUnits];
}
return gBundle.formatStringFromName(name, values);
@ -248,20 +271,25 @@ var DownloadUtils = {
*/
getTimeLeft: function DU_getTimeLeft(aSeconds, aLastSec) {
let nf = new Services.intl.NumberFormat();
if (aLastSec == null)
if (aLastSec == null) {
aLastSec = Infinity;
}
if (aSeconds < 0)
if (aSeconds < 0) {
return [gBundle.GetStringFromName(gStr.timeUnknown), aLastSec];
}
// Try to find a cached lastSec for the given second
aLastSec = gCachedLast.reduce((aResult, aItem) =>
aItem[0] == aSeconds ? aItem[1] : aResult, aLastSec);
aLastSec = gCachedLast.reduce(
(aResult, aItem) => (aItem[0] == aSeconds ? aItem[1] : aResult),
aLastSec
);
// Add the current second/lastSec pair unless we have too many
gCachedLast.push([aSeconds, aLastSec]);
if (gCachedLast.length > kCachedLastMaxSize)
if (gCachedLast.length > kCachedLastMaxSize) {
gCachedLast.shift();
}
// Apply smoothing only if the new time isn't a huge change -- e.g., if the
// new time is more than half the previous time; this is useful for
@ -270,13 +298,14 @@ var DownloadUtils = {
// Apply hysteresis to favor downward over upward swings
// 30% of down and 10% of up (exponential smoothing)
let diff = aSeconds - aLastSec;
aSeconds = aLastSec + (diff < 0 ? .3 : .1) * diff;
aSeconds = aLastSec + (diff < 0 ? 0.3 : 0.1) * diff;
// If the new time is similar, reuse something close to the last seconds,
// but subtract a little to provide forward progress
let diffPct = diff / aLastSec * 100;
if (Math.abs(diff) < 5 || Math.abs(diffPct) < 5)
aSeconds = aLastSec - (diff < 0 ? .4 : .2);
let diffPct = (diff / aLastSec) * 100;
if (Math.abs(diff) < 5 || Math.abs(diffPct) < 5) {
aSeconds = aLastSec - (diff < 0 ? 0.4 : 0.2);
}
}
// Decide what text to show for the time
@ -286,23 +315,29 @@ var DownloadUtils = {
timeLeft = gBundle.GetStringFromName(gStr.timeFewSeconds);
} else {
// Convert the seconds into its two largest units to display
let [time1, unit1, time2, unit2] =
DownloadUtils.convertTimeUnits(aSeconds);
let [time1, unit1, time2, unit2] = DownloadUtils.convertTimeUnits(
aSeconds
);
let pair1 =
gBundle.formatStringFromName(gStr.timePair, [nf.format(time1), unit1]);
let pair2 =
gBundle.formatStringFromName(gStr.timePair, [nf.format(time2), unit2]);
let pair1 = gBundle.formatStringFromName(gStr.timePair, [
nf.format(time1),
unit1,
]);
let pair2 = gBundle.formatStringFromName(gStr.timePair, [
nf.format(time2),
unit2,
]);
// Only show minutes for under 1 hour unless there's a few minutes left;
// or the second pair is 0.
if ((aSeconds < 3600 && time1 >= 4) || time2 == 0) {
timeLeft = gBundle.formatStringFromName(gStr.timeLeftSingle,
[pair1]);
timeLeft = gBundle.formatStringFromName(gStr.timeLeftSingle, [pair1]);
} else {
// We've got 2 pairs of times to display
timeLeft = gBundle.formatStringFromName(gStr.timeLeftDouble,
[pair1, pair2]);
timeLeft = gBundle.formatStringFromName(gStr.timeLeftDouble, [
pair1,
pair2,
]);
}
}
@ -342,23 +377,27 @@ var DownloadUtils = {
timeStyle: "short",
});
dateTimeCompact = dts.format(aDate);
} else if (today - aDate < (MS_PER_DAY)) {
} else if (today - aDate < MS_PER_DAY) {
// After yesterday started, show yesterday
dateTimeCompact = gBundle.GetStringFromName(gStr.yesterday);
} else if (today - aDate < (6 * MS_PER_DAY)) {
} else if (today - aDate < 6 * MS_PER_DAY) {
// After last week started, show day of week
dateTimeCompact = aDate.toLocaleDateString(undefined, { weekday: "long" });
dateTimeCompact = aDate.toLocaleDateString(undefined, {
weekday: "long",
});
} else {
// Show month/day
dateTimeCompact = aDate.toLocaleString(undefined, {
month: "long",
day: "numeric",
month: "long",
day: "numeric",
});
}
const dtOptions = { dateStyle: "long", timeStyle: "short" };
dateTimeFull =
new Services.intl.DateTimeFormat(undefined, dtOptions).format(aDate);
dateTimeFull = new Services.intl.DateTimeFormat(
undefined,
dtOptions
).format(aDate);
return [dateTimeCompact, dateTimeFull];
},
@ -372,8 +411,9 @@ var DownloadUtils = {
* @return A pair: [display host for the URI string, full host name]
*/
getURIHost: function DU_getURIHost(aURIString) {
let idnService = Cc["@mozilla.org/network/idn-service;1"].
getService(Ci.nsIIDNService);
let idnService = Cc["@mozilla.org/network/idn-service;1"].getService(
Ci.nsIIDNService
);
// Get a URI that knows about its components
let uri;
@ -384,8 +424,9 @@ var DownloadUtils = {
}
// Get the inner-most uri for schemes like jar:
if (uri instanceof Ci.nsINestedURI)
if (uri instanceof Ci.nsINestedURI) {
uri = uri.innermostURI;
}
let fullHost;
try {
@ -414,8 +455,7 @@ var DownloadUtils = {
fullHost = displayHost;
} else if (displayHost.length == 0) {
// Got nothing; show the scheme (data: about: moz-icon:)
displayHost =
gBundle.formatStringFromName(gStr.doneScheme, [uri.scheme]);
displayHost = gBundle.formatStringFromName(gStr.doneScheme, [uri.scheme]);
fullHost = displayHost;
} else if (uri.port != -1) {
// Tack on the port if it's not the default port
@ -440,7 +480,7 @@ var DownloadUtils = {
// Convert to next unit if it needs 4 digits (after rounding), but only if
// we know the name of the next unit
while ((aBytes >= 999.5) && (unitIndex < gStr.units.length - 1)) {
while (aBytes >= 999.5 && unitIndex < gStr.units.length - 1) {
aBytes /= 1024;
unitIndex++;
}
@ -448,7 +488,7 @@ var DownloadUtils = {
// Get rid of insignificant bits by truncating to 1 or 0 decimal points
// 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235
// added in bug 462064: (unitIndex != 0) makes sure that no decimal digit for bytes appears when aBytes < 100
let fractionDigits = (aBytes > 0) && (aBytes < 100) && (unitIndex != 0) ? 1 : 0;
let fractionDigits = aBytes > 0 && aBytes < 100 && unitIndex != 0 ? 1 : 0;
// Don't try to format Infinity values using NumberFormat.
if (aBytes === Infinity) {
@ -479,7 +519,7 @@ var DownloadUtils = {
// Keep converting to the next unit while we have units left and the
// current one isn't the largest unit possible
while ((unitIndex < timeSize.length) && (time >= timeSize[unitIndex])) {
while (unitIndex < timeSize.length && time >= timeSize[unitIndex]) {
time /= timeSize[unitIndex];
scale *= timeSize[unitIndex];
unitIndex++;
@ -492,8 +532,9 @@ var DownloadUtils = {
let nextIndex = unitIndex - 1;
// Convert the extra time to the next largest unit
for (let index = 0; index < nextIndex; index++)
for (let index = 0; index < nextIndex; index++) {
extra /= timeSize[index];
}
let value2 = convertTimeUnitsValue(extra);
let units2 = convertTimeUnitsUnits(value2, nextIndex);
@ -524,10 +565,14 @@ function convertTimeUnitsValue(aTime) {
*/
function convertTimeUnitsUnits(aTime, aIndex) {
// Negative index would be an invalid unit, so just give empty
if (aIndex < 0)
if (aIndex < 0) {
return "";
}
return PluralForm.get(aTime, gBundle.GetStringFromName(gStr.timeUnits[aIndex]));
return PluralForm.get(
aTime,
gBundle.GetStringFromName(gStr.timeUnits[aIndex])
);
}
/**

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -5,9 +5,11 @@
ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm", this);
const UCT_URI = "chrome://mozapps/content/downloads/unknownContentType.xul";
const LOAD_URI = "http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.txt";
const LOAD_URI =
"http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.txt";
const DIALOG_DELAY = Services.prefs.getIntPref("security.dialog_enable_delay") + 200;
const DIALOG_DELAY =
Services.prefs.getIntPref("security.dialog_enable_delay") + 200;
let UCTObserver = {
opened: PromiseUtils.defer(),
@ -18,12 +20,16 @@ let UCTObserver = {
switch (aTopic) {
case "domwindowopened":
win.addEventListener("load", function onLoad(event) {
// Let the dialog initialize
SimpleTest.executeSoon(function() {
UCTObserver.opened.resolve(win);
});
}, {once: true});
win.addEventListener(
"load",
function onLoad(event) {
// Let the dialog initialize
SimpleTest.executeSoon(function() {
UCTObserver.opened.resolve(win);
});
},
{ once: true }
);
break;
case "domwindowclosed":
@ -45,39 +51,42 @@ function waitDelay(delay) {
add_task(async function test_unknownContentType_delayedbutton() {
Services.ww.registerNotification(UCTObserver);
await BrowserTestUtils.withNewTab({
gBrowser,
url: LOAD_URI,
}, async function() {
let uctWindow = await UCTObserver.opened.promise;
let ok = uctWindow.document.documentElement.getButton("accept");
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: LOAD_URI,
},
async function() {
let uctWindow = await UCTObserver.opened.promise;
let ok = uctWindow.document.documentElement.getButton("accept");
SimpleTest.is(ok.disabled, true, "button started disabled");
SimpleTest.is(ok.disabled, true, "button started disabled");
await waitDelay(DIALOG_DELAY);
await waitDelay(DIALOG_DELAY);
SimpleTest.is(ok.disabled, false, "button was enabled");
SimpleTest.is(ok.disabled, false, "button was enabled");
let focusOutOfDialog = SimpleTest.promiseFocus(window);
window.focus();
await focusOutOfDialog;
let focusOutOfDialog = SimpleTest.promiseFocus(window);
window.focus();
await focusOutOfDialog;
SimpleTest.is(ok.disabled, true, "button was disabled");
SimpleTest.is(ok.disabled, true, "button was disabled");
let focusOnDialog = SimpleTest.promiseFocus(uctWindow);
uctWindow.focus();
await focusOnDialog;
let focusOnDialog = SimpleTest.promiseFocus(uctWindow);
uctWindow.focus();
await focusOnDialog;
SimpleTest.is(ok.disabled, true, "button remained disabled");
SimpleTest.is(ok.disabled, true, "button remained disabled");
await waitDelay(DIALOG_DELAY);
SimpleTest.is(ok.disabled, false, "button re-enabled after delay");
await waitDelay(DIALOG_DELAY);
SimpleTest.is(ok.disabled, false, "button re-enabled after delay");
uctWindow.document.documentElement.cancelDialog();
await UCTObserver.closed.promise;
uctWindow.document.documentElement.cancelDialog();
await UCTObserver.closed.promise;
Services.ww.unregisterNotification(UCTObserver);
uctWindow = null;
UCTObserver = null;
});
Services.ww.unregisterNotification(UCTObserver);
uctWindow = null;
UCTObserver = null;
}
);
});

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

@ -11,15 +11,19 @@
const UCT_URI = "chrome://mozapps/content/downloads/unknownContentType.xul";
let tests = [
{ // This URL will trigger the simple UI, where only the Save an Cancel buttons are available
url: "http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.pif",
{
// This URL will trigger the simple UI, where only the Save an Cancel buttons are available
url:
"http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.pif",
elements: {
basicBox: { collapsed: false },
normalBox: { collapsed: true },
},
},
{ // This URL will trigger the full UI
url: "http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.txt",
{
// This URL will trigger the full UI
url:
"http://mochi.test:8888/browser/toolkit/mozapps/downloads/tests/browser/unknownContentType_dialog_layout_data.txt",
elements: {
basicBox: { collapsed: true },
normalBox: { collapsed: false },
@ -38,12 +42,16 @@ add_task(async function test_unknownContentType_dialog_layout() {
switch (aTopic) {
case "domwindowopened":
win.addEventListener("load", function onLoad(event) {
// Let the dialog initialize
SimpleTest.executeSoon(function() {
UCTObserver.opened.resolve(win);
});
}, {once: true});
win.addEventListener(
"load",
function onLoad(event) {
// Let the dialog initialize
SimpleTest.executeSoon(function() {
UCTObserver.opened.resolve(win);
});
},
{ once: true }
);
break;
case "domwindowclosed":
@ -56,27 +64,37 @@ add_task(async function test_unknownContentType_dialog_layout() {
};
Services.ww.registerNotification(UCTObserver);
await BrowserTestUtils.withNewTab({
gBrowser,
url: test.url,
}, async function() {
let uctWindow = await UCTObserver.opened.promise;
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: test.url,
},
async function() {
let uctWindow = await UCTObserver.opened.promise;
for (let [id, props] of Object.entries(test.elements)) {
let elem = uctWindow.dialog.dialogElement(id);
for (let [prop, value] of Object.entries(props)) {
SimpleTest.is(elem[prop], value,
"Element with id " + id + " has property " +
prop + " set to " + value);
for (let [id, props] of Object.entries(test.elements)) {
let elem = uctWindow.dialog.dialogElement(id);
for (let [prop, value] of Object.entries(props)) {
SimpleTest.is(
elem[prop],
value,
"Element with id " +
id +
" has property " +
prop +
" set to " +
value
);
}
}
}
let focusOnDialog = SimpleTest.promiseFocus(uctWindow);
uctWindow.focus();
await focusOnDialog;
let focusOnDialog = SimpleTest.promiseFocus(uctWindow);
uctWindow.focus();
await focusOnDialog;
uctWindow.document.documentElement.cancelDialog();
uctWindow = null;
Services.ww.unregisterNotification(UCTObserver);
});
uctWindow.document.documentElement.cancelDialog();
uctWindow = null;
Services.ww.unregisterNotification(UCTObserver);
}
);
}
});

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

@ -1,4 +1,4 @@
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
registerCleanupFunction(function() {
Services.obs.notifyObservers(null, "quit-application");

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

@ -2,9 +2,13 @@
* 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 {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
const { DownloadUtils } = ChromeUtils.import(
"resource://gre/modules/DownloadUtils.jsm"
);
const gDecimalSymbol = Number(5.4).toLocaleString().match(/\D/);
const gDecimalSymbol = Number(5.4)
.toLocaleString()
.match(/\D/);
function _(str) {
return str.replace(/\./g, gDecimalSymbol);
}
@ -23,7 +27,19 @@ function testTransferTotal(aCurrBytes, aMaxBytes, aTransfer) {
// Get the em-dash character because typing it directly here doesn't work :(
var gDash = DownloadUtils.getDownloadStatus(0)[0].match(/left (.) 0 bytes/)[1];
var gVals = [0, 100, 2345, 55555, 982341, 23194134, 1482, 58, 9921949201, 13498132, Infinity];
var gVals = [
0,
100,
2345,
55555,
982341,
23194134,
1482,
58,
9921949201,
13498132,
Infinity,
];
function testStatus(aFunc, aCurr, aMore, aRate, aTest) {
dump("Status Test: " + [aCurr, aMore, aRate, aTest] + "\n");
@ -34,18 +50,30 @@ function testStatus(aFunc, aCurr, aMore, aRate, aTest) {
let [status, last] = aFunc(curr, max, speed);
if (0) {
dump("testStatus(" + aCurr + ", " + aMore + ", " + aRate + ", [\"" +
status.replace(gDash, "--") + "\", " + last.toFixed(3) + "]);\n");
dump(
"testStatus(" +
aCurr +
", " +
aMore +
", " +
aRate +
', ["' +
status.replace(gDash, "--") +
'", ' +
last.toFixed(3) +
"]);\n"
);
}
// Make sure the status text matches
Assert.equal(status, _(aTest[0].replace(/--/, gDash)));
// Make sure the lastSeconds matches
if (last == Infinity)
if (last == Infinity) {
Assert.equal(last, aTest[1]);
else
Assert.ok(Math.abs(last - aTest[1]) < .1);
} else {
Assert.ok(Math.abs(last - aTest[1]) < 0.1);
}
}
function testURI(aURI, aDisp, aHost) {
@ -58,7 +86,6 @@ function testURI(aURI, aDisp, aHost) {
Assert.equal(host, aHost);
}
function testGetReadableDates(aDate, aCompactValue) {
const now = new Date(2000, 11, 31, 11, 59, 59);
@ -69,35 +96,52 @@ function testGetReadableDates(aDate, aCompactValue) {
function testAllGetReadableDates() {
// This test cannot depend on the current date and time, or the date format.
// It depends on being run with the English localization, however.
const today_11_30 = new Date(2000, 11, 31, 11, 30, 15);
const today_12_30 = new Date(2000, 11, 31, 12, 30, 15);
const today_11_30 = new Date(2000, 11, 31, 11, 30, 15);
const today_12_30 = new Date(2000, 11, 31, 12, 30, 15);
const yesterday_11_30 = new Date(2000, 11, 30, 11, 30, 15);
const yesterday_12_30 = new Date(2000, 11, 30, 12, 30, 15);
const twodaysago = new Date(2000, 11, 29, 11, 30, 15);
const sixdaysago = new Date(2000, 11, 25, 11, 30, 15);
const sevendaysago = new Date(2000, 11, 24, 11, 30, 15);
const twodaysago = new Date(2000, 11, 29, 11, 30, 15);
const sixdaysago = new Date(2000, 11, 25, 11, 30, 15);
const sevendaysago = new Date(2000, 11, 24, 11, 30, 15);
let cDtf = Services.intl.DateTimeFormat;
testGetReadableDates(today_11_30,
(new cDtf(undefined, {timeStyle: "short"})).format(today_11_30));
testGetReadableDates(today_12_30,
(new cDtf(undefined, {timeStyle: "short"})).format(today_12_30));
testGetReadableDates(
today_11_30,
new cDtf(undefined, { timeStyle: "short" }).format(today_11_30)
);
testGetReadableDates(
today_12_30,
new cDtf(undefined, { timeStyle: "short" }).format(today_12_30)
);
testGetReadableDates(yesterday_11_30, "Yesterday");
testGetReadableDates(yesterday_12_30, "Yesterday");
testGetReadableDates(twodaysago,
twodaysago.toLocaleDateString(undefined, { weekday: "long" }));
testGetReadableDates(sixdaysago,
sixdaysago.toLocaleDateString(undefined, { weekday: "long" }));
testGetReadableDates(sevendaysago,
sevendaysago.toLocaleDateString(undefined, { month: "long" }) + " " +
sevendaysago.getDate().toString().padStart(2, "0"));
testGetReadableDates(
twodaysago,
twodaysago.toLocaleDateString(undefined, { weekday: "long" })
);
testGetReadableDates(
sixdaysago,
sixdaysago.toLocaleDateString(undefined, { weekday: "long" })
);
testGetReadableDates(
sevendaysago,
sevendaysago.toLocaleDateString(undefined, { month: "long" }) +
" " +
sevendaysago
.getDate()
.toString()
.padStart(2, "0")
);
let [, dateTimeFull] = DownloadUtils.getReadableDates(today_11_30);
const dtOptions = { dateStyle: "long", timeStyle: "short" };
Assert.equal(dateTimeFull, (new cDtf(undefined, dtOptions)).format(today_11_30));
Assert.equal(
dateTimeFull,
new cDtf(undefined, dtOptions).format(today_11_30)
);
}
function run_test() {
@ -132,47 +176,137 @@ function run_test() {
// First, test with rates, via getDownloadStatus...
let statusFunc = DownloadUtils.getDownloadStatus.bind(DownloadUtils);
testStatus(statusFunc, 2, 1, 7, ["A few seconds left -- 2.3 of 2.4 KB (58 bytes/sec)", 1.724]);
testStatus(statusFunc, 1, 2, 6, ["A few seconds left -- 100 bytes of 2.4 KB (1.4 KB/sec)", 1.582]);
testStatus(statusFunc, 4, 3, 9, ["A few seconds left -- 959 KB of 1.0 MB (12.9 MB/sec)", 0.004]);
testStatus(statusFunc, 2, 3, 8, ["A few seconds left -- 2.3 of 56.5 KB (9.2 GB/sec)", 0.000]);
testStatus(statusFunc, 2, 1, 7, [
"A few seconds left -- 2.3 of 2.4 KB (58 bytes/sec)",
1.724,
]);
testStatus(statusFunc, 1, 2, 6, [
"A few seconds left -- 100 bytes of 2.4 KB (1.4 KB/sec)",
1.582,
]);
testStatus(statusFunc, 4, 3, 9, [
"A few seconds left -- 959 KB of 1.0 MB (12.9 MB/sec)",
0.004,
]);
testStatus(statusFunc, 2, 3, 8, [
"A few seconds left -- 2.3 of 56.5 KB (9.2 GB/sec)",
0.0,
]);
testStatus(statusFunc, 8, 4, 3, ["17s left -- 9.2 of 9.2 GB (54.3 KB/sec)", 17.682]);
testStatus(statusFunc, 1, 3, 2, ["23s left -- 100 bytes of 54.4 KB (2.3 KB/sec)", 23.691]);
testStatus(statusFunc, 9, 3, 2, ["23s left -- 12.9 of 12.9 MB (2.3 KB/sec)", 23.691]);
testStatus(statusFunc, 5, 6, 7, ["25s left -- 22.1 of 22.1 MB (58 bytes/sec)", 25.552]);
testStatus(statusFunc, 8, 4, 3, [
"17s left -- 9.2 of 9.2 GB (54.3 KB/sec)",
17.682,
]);
testStatus(statusFunc, 1, 3, 2, [
"23s left -- 100 bytes of 54.4 KB (2.3 KB/sec)",
23.691,
]);
testStatus(statusFunc, 9, 3, 2, [
"23s left -- 12.9 of 12.9 MB (2.3 KB/sec)",
23.691,
]);
testStatus(statusFunc, 5, 6, 7, [
"25s left -- 22.1 of 22.1 MB (58 bytes/sec)",
25.552,
]);
testStatus(statusFunc, 3, 9, 3, ["4m left -- 54.3 KB of 12.9 MB (54.3 KB/sec)", 242.969]);
testStatus(statusFunc, 2, 3, 1, ["9m left -- 2.3 of 56.5 KB (100 bytes/sec)", 555.550]);
testStatus(statusFunc, 4, 3, 7, ["15m left -- 959 KB of 1.0 MB (58 bytes/sec)", 957.845]);
testStatus(statusFunc, 5, 3, 7, ["15m left -- 22.1 of 22.2 MB (58 bytes/sec)", 957.845]);
testStatus(statusFunc, 3, 9, 3, [
"4m left -- 54.3 KB of 12.9 MB (54.3 KB/sec)",
242.969,
]);
testStatus(statusFunc, 2, 3, 1, [
"9m left -- 2.3 of 56.5 KB (100 bytes/sec)",
555.55,
]);
testStatus(statusFunc, 4, 3, 7, [
"15m left -- 959 KB of 1.0 MB (58 bytes/sec)",
957.845,
]);
testStatus(statusFunc, 5, 3, 7, [
"15m left -- 22.1 of 22.2 MB (58 bytes/sec)",
957.845,
]);
testStatus(statusFunc, 1, 9, 2, ["1h 35m left -- 100 bytes of 12.9 MB (2.3 KB/sec)", 5756.133]);
testStatus(statusFunc, 2, 9, 6, ["2h 31m left -- 2.3 KB of 12.9 MB (1.4 KB/sec)", 9108.051]);
testStatus(statusFunc, 2, 4, 1, ["2h 43m left -- 2.3 of 962 KB (100 bytes/sec)", 9823.410]);
testStatus(statusFunc, 6, 4, 7, ["4h 42m left -- 1.4 of 961 KB (58 bytes/sec)", 16936.914]);
testStatus(statusFunc, 1, 9, 2, [
"1h 35m left -- 100 bytes of 12.9 MB (2.3 KB/sec)",
5756.133,
]);
testStatus(statusFunc, 2, 9, 6, [
"2h 31m left -- 2.3 KB of 12.9 MB (1.4 KB/sec)",
9108.051,
]);
testStatus(statusFunc, 2, 4, 1, [
"2h 43m left -- 2.3 of 962 KB (100 bytes/sec)",
9823.41,
]);
testStatus(statusFunc, 6, 4, 7, [
"4h 42m left -- 1.4 of 961 KB (58 bytes/sec)",
16936.914,
]);
testStatus(statusFunc, 6, 9, 1, ["1d 13h left -- 1.4 KB of 12.9 MB (100 bytes/sec)", 134981.320]);
testStatus(statusFunc, 3, 8, 3, ["2d 1h left -- 54.3 KB of 9.2 GB (54.3 KB/sec)", 178596.872]);
testStatus(statusFunc, 1, 8, 6, ["77d 11h left -- 100 bytes of 9.2 GB (1.4 KB/sec)", 6694972.470]);
testStatus(statusFunc, 6, 8, 7, ["1,979d 22h left -- 1.4 KB of 9.2 GB (58 bytes/sec)", 171068089.672]);
testStatus(statusFunc, 6, 9, 1, [
"1d 13h left -- 1.4 KB of 12.9 MB (100 bytes/sec)",
134981.32,
]);
testStatus(statusFunc, 3, 8, 3, [
"2d 1h left -- 54.3 KB of 9.2 GB (54.3 KB/sec)",
178596.872,
]);
testStatus(statusFunc, 1, 8, 6, [
"77d 11h left -- 100 bytes of 9.2 GB (1.4 KB/sec)",
6694972.47,
]);
testStatus(statusFunc, 6, 8, 7, [
"1,979d 22h left -- 1.4 KB of 9.2 GB (58 bytes/sec)",
171068089.672,
]);
testStatus(statusFunc, 0, 0, 5, ["Unknown time left -- 0 of 0 bytes (22.1 MB/sec)", Infinity]);
testStatus(statusFunc, 0, 6, 0, ["Unknown time left -- 0 bytes of 1.4 KB (0 bytes/sec)", Infinity]);
testStatus(statusFunc, 6, 6, 0, ["Unknown time left -- 1.4 of 2.9 KB (0 bytes/sec)", Infinity]);
testStatus(statusFunc, 8, 5, 0, ["Unknown time left -- 9.2 of 9.3 GB (0 bytes/sec)", Infinity]);
testStatus(statusFunc, 0, 0, 5, [
"Unknown time left -- 0 of 0 bytes (22.1 MB/sec)",
Infinity,
]);
testStatus(statusFunc, 0, 6, 0, [
"Unknown time left -- 0 bytes of 1.4 KB (0 bytes/sec)",
Infinity,
]);
testStatus(statusFunc, 6, 6, 0, [
"Unknown time left -- 1.4 of 2.9 KB (0 bytes/sec)",
Infinity,
]);
testStatus(statusFunc, 8, 5, 0, [
"Unknown time left -- 9.2 of 9.3 GB (0 bytes/sec)",
Infinity,
]);
// With rate equal to Infinity
testStatus(statusFunc, 0, 0, 10, ["Unknown time left -- 0 of 0 bytes (Really fast)", Infinity]);
testStatus(statusFunc, 1, 2, 10, ["A few seconds left -- 100 bytes of 2.4 KB (Really fast)", 0]);
testStatus(statusFunc, 0, 0, 10, [
"Unknown time left -- 0 of 0 bytes (Really fast)",
Infinity,
]);
testStatus(statusFunc, 1, 2, 10, [
"A few seconds left -- 100 bytes of 2.4 KB (Really fast)",
0,
]);
// Now test without rates, via getDownloadStatusNoRate.
statusFunc = DownloadUtils.getDownloadStatusNoRate.bind(DownloadUtils);
testStatus(statusFunc, 2, 1, 7, ["A few seconds left -- 2.3 of 2.4 KB", 1.724]);
testStatus(statusFunc, 1, 2, 6, ["A few seconds left -- 100 bytes of 2.4 KB", 1.582]);
testStatus(statusFunc, 4, 3, 9, ["A few seconds left -- 959 KB of 1.0 MB", 0.004]);
testStatus(statusFunc, 2, 3, 8, ["A few seconds left -- 2.3 of 56.5 KB", 0.000]);
testStatus(statusFunc, 2, 1, 7, [
"A few seconds left -- 2.3 of 2.4 KB",
1.724,
]);
testStatus(statusFunc, 1, 2, 6, [
"A few seconds left -- 100 bytes of 2.4 KB",
1.582,
]);
testStatus(statusFunc, 4, 3, 9, [
"A few seconds left -- 959 KB of 1.0 MB",
0.004,
]);
testStatus(statusFunc, 2, 3, 8, [
"A few seconds left -- 2.3 of 56.5 KB",
0.0,
]);
testStatus(statusFunc, 8, 4, 3, ["17s left -- 9.2 of 9.2 GB", 17.682]);
testStatus(statusFunc, 1, 3, 2, ["23s left -- 100 bytes of 54.4 KB", 23.691]);
@ -180,29 +314,67 @@ function run_test() {
testStatus(statusFunc, 5, 6, 7, ["25s left -- 22.1 of 22.1 MB", 25.552]);
testStatus(statusFunc, 3, 9, 3, ["4m left -- 54.3 KB of 12.9 MB", 242.969]);
testStatus(statusFunc, 2, 3, 1, ["9m left -- 2.3 of 56.5 KB", 555.550]);
testStatus(statusFunc, 2, 3, 1, ["9m left -- 2.3 of 56.5 KB", 555.55]);
testStatus(statusFunc, 4, 3, 7, ["15m left -- 959 KB of 1.0 MB", 957.845]);
testStatus(statusFunc, 5, 3, 7, ["15m left -- 22.1 of 22.2 MB", 957.845]);
testStatus(statusFunc, 1, 9, 2, ["1h 35m left -- 100 bytes of 12.9 MB", 5756.133]);
testStatus(statusFunc, 2, 9, 6, ["2h 31m left -- 2.3 KB of 12.9 MB", 9108.051]);
testStatus(statusFunc, 2, 4, 1, ["2h 43m left -- 2.3 of 962 KB", 9823.410]);
testStatus(statusFunc, 1, 9, 2, [
"1h 35m left -- 100 bytes of 12.9 MB",
5756.133,
]);
testStatus(statusFunc, 2, 9, 6, [
"2h 31m left -- 2.3 KB of 12.9 MB",
9108.051,
]);
testStatus(statusFunc, 2, 4, 1, ["2h 43m left -- 2.3 of 962 KB", 9823.41]);
testStatus(statusFunc, 6, 4, 7, ["4h 42m left -- 1.4 of 961 KB", 16936.914]);
testStatus(statusFunc, 6, 9, 1, ["1d 13h left -- 1.4 KB of 12.9 MB", 134981.320]);
testStatus(statusFunc, 3, 8, 3, ["2d 1h left -- 54.3 KB of 9.2 GB", 178596.872]);
testStatus(statusFunc, 1, 8, 6, ["77d 11h left -- 100 bytes of 9.2 GB", 6694972.470]);
testStatus(statusFunc, 6, 8, 7, ["1,979d 22h left -- 1.4 KB of 9.2 GB", 171068089.672]);
testStatus(statusFunc, 6, 9, 1, [
"1d 13h left -- 1.4 KB of 12.9 MB",
134981.32,
]);
testStatus(statusFunc, 3, 8, 3, [
"2d 1h left -- 54.3 KB of 9.2 GB",
178596.872,
]);
testStatus(statusFunc, 1, 8, 6, [
"77d 11h left -- 100 bytes of 9.2 GB",
6694972.47,
]);
testStatus(statusFunc, 6, 8, 7, [
"1,979d 22h left -- 1.4 KB of 9.2 GB",
171068089.672,
]);
testStatus(statusFunc, 0, 0, 5, ["Unknown time left -- 0 of 0 bytes", Infinity]);
testStatus(statusFunc, 0, 6, 0, ["Unknown time left -- 0 bytes of 1.4 KB", Infinity]);
testStatus(statusFunc, 6, 6, 0, ["Unknown time left -- 1.4 of 2.9 KB", Infinity]);
testStatus(statusFunc, 8, 5, 0, ["Unknown time left -- 9.2 of 9.3 GB", Infinity]);
testStatus(statusFunc, 0, 0, 5, [
"Unknown time left -- 0 of 0 bytes",
Infinity,
]);
testStatus(statusFunc, 0, 6, 0, [
"Unknown time left -- 0 bytes of 1.4 KB",
Infinity,
]);
testStatus(statusFunc, 6, 6, 0, [
"Unknown time left -- 1.4 of 2.9 KB",
Infinity,
]);
testStatus(statusFunc, 8, 5, 0, [
"Unknown time left -- 9.2 of 9.3 GB",
Infinity,
]);
testURI("http://www.mozilla.org/", "mozilla.org", "www.mozilla.org");
testURI("http://www.city.mikasa.hokkaido.jp/", "city.mikasa.hokkaido.jp", "www.city.mikasa.hokkaido.jp");
testURI(
"http://www.city.mikasa.hokkaido.jp/",
"city.mikasa.hokkaido.jp",
"www.city.mikasa.hokkaido.jp"
);
testURI("data:text/html,Hello World", "data resource", "data resource");
testURI("jar:http://www.mozilla.com/file!/magic", "mozilla.com", "www.mozilla.com");
testURI(
"jar:http://www.mozilla.com/file!/magic",
"mozilla.com",
"www.mozilla.com"
);
testURI("file:///C:/Cool/Stuff/", "local file", "local file");
// Don't test for moz-icon if we don't have a protocol handler for it (e.g. b2g):
if ("@mozilla.org/network/protocol;1?name=moz-icon" in Cc) {

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

@ -1,13 +1,15 @@
/* 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/. */
* 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/. */
/**
* Test bug 448344 to make sure when we're in low minutes, we show both minutes
* and seconds; but continue to show only minutes when we have plenty.
*/
const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
const { DownloadUtils } = ChromeUtils.import(
"resource://gre/modules/DownloadUtils.jsm"
);
/**
* Print some debug message to the console. All arguments will be printed,

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

@ -8,17 +8,21 @@
* "last time".
*/
const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
const { DownloadUtils } = ChromeUtils.import(
"resource://gre/modules/DownloadUtils.jsm"
);
function run_test() {
// Simulate having multiple downloads requesting time left
let downloadTimes = {};
for (let time of [1, 30, 60, 3456, 9999])
for (let time of [1, 30, 60, 3456, 9999]) {
downloadTimes[time] = DownloadUtils.getTimeLeft(time)[0];
}
// Pretend we're a download status bar also asking for a time left, but we're
// using a different "last sec". We need to make sure we get the same time.
let lastSec = 314;
for (let [time, text] of Object.entries(downloadTimes))
for (let [time, text] of Object.entries(downloadTimes)) {
Assert.equal(DownloadUtils.getTimeLeft(time, lastSec)[0], text);
}
}

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

@ -7,17 +7,27 @@
* methods provide the same result.
*/
const {DownloadUtils} = ChromeUtils.import("resource://gre/modules/DownloadUtils.jsm");
const { DownloadUtils } = ChromeUtils.import(
"resource://gre/modules/DownloadUtils.jsm"
);
function run_test() {
Assert.equal(DownloadUtils.getDownloadStatus(1000, null, null, null) + "",
DownloadUtils.getDownloadStatus(1000) + "");
Assert.equal(DownloadUtils.getDownloadStatus(1000, null, null) + "",
DownloadUtils.getDownloadStatus(1000, null) + "");
Assert.equal(
DownloadUtils.getDownloadStatus(1000, null, null, null) + "",
DownloadUtils.getDownloadStatus(1000) + ""
);
Assert.equal(
DownloadUtils.getDownloadStatus(1000, null, null) + "",
DownloadUtils.getDownloadStatus(1000, null) + ""
);
Assert.equal(DownloadUtils.getTransferTotal(1000, null) + "",
DownloadUtils.getTransferTotal(1000) + "");
Assert.equal(
DownloadUtils.getTransferTotal(1000, null) + "",
DownloadUtils.getTransferTotal(1000) + ""
);
Assert.equal(DownloadUtils.getTimeLeft(1000, null) + "",
DownloadUtils.getTimeLeft(1000) + "");
Assert.equal(
DownloadUtils.getTimeLeft(1000, null) + "",
DownloadUtils.getTimeLeft(1000) + ""
);
}

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

@ -2,13 +2,15 @@
* 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 EXPORTED_SYMBOLS = [ "AbuseReporter", "AbuseReportError" ];
const EXPORTED_SYMBOLS = ["AbuseReporter", "AbuseReportError"];
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
Cu.importGlobalProperties(["fetch"]);
const PREF_ABUSE_REPORT_URL = "extensions.abuseReport.url";
const PREF_ABUSE_REPORT_URL = "extensions.abuseReport.url";
// Maximum length of the string properties sent to the API endpoint.
const MAX_STRING_LENGTH = 255;
@ -24,7 +26,11 @@ XPCOMUtils.defineLazyModuleGetters(this, {
Services: "resource://gre/modules/Services.jsm",
});
XPCOMUtils.defineLazyPreferenceGetter(this, "ABUSE_REPORT_URL", PREF_ABUSE_REPORT_URL);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"ABUSE_REPORT_URL",
PREF_ABUSE_REPORT_URL
);
const PRIVATE_REPORT_PROPS = Symbol("privateReportProps");
@ -44,8 +50,7 @@ class AbuseReportError extends Error {
throw new Error(`Unknown AbuseReportError type "${errorType}"`);
}
let message = errorInfo ?
`${errorType} - ${errorInfo}` : errorType;
let message = errorInfo ? `${errorType} - ${errorInfo}` : errorType;
super(message);
this.name = "AbuseReportError";
@ -93,7 +98,7 @@ const AbuseReporter = {
* An instance of the AbuseReport class, which represent an ongoing
* report.
*/
async createAbuseReport(addonId, {reportEntryPoint} = {}) {
async createAbuseReport(addonId, { reportEntryPoint } = {}) {
const addon = await AddonManager.getAddonByID(addonId);
if (!addon) {
@ -128,7 +133,7 @@ const AbuseReporter = {
* An object that contains the collected details.
*/
async getReportData(addon) {
const truncateString = (text) =>
const truncateString = text =>
typeof text == "string" ? text.slice(0, MAX_STRING_LENGTH) : text;
const data = {
@ -136,7 +141,8 @@ const AbuseReporter = {
addon_version: addon.version,
addon_name: truncateString(addon.name),
addon_summary: truncateString(addon.description),
addon_install_origin: addon.sourceURI && truncateString(addon.sourceURI.spec),
addon_install_origin:
addon.sourceURI && truncateString(addon.sourceURI.spec),
install_date: addon.installDate && addon.installDate.toISOString(),
};
@ -145,7 +151,7 @@ const AbuseReporter = {
// https://addons-server.readthedocs.io/en/latest/topics/api/abuse.html).
let install_method = "other";
if (addon.installTelemetryInfo) {
const {source, method} = addon.installTelemetryInfo;
const { source, method } = addon.installTelemetryInfo;
switch (source) {
case "enterprise-policy":
case "file-url":
@ -248,7 +254,7 @@ const AbuseReporter = {
* A string that identify how the report has been triggered.
*/
class AbuseReport {
constructor({addon, createErrorType, reportData, reportEntryPoint}) {
constructor({ addon, createErrorType, reportData, reportEntryPoint }) {
this[PRIVATE_REPORT_PROPS] = {
aborted: false,
abortController: new AbortController(),
@ -259,7 +265,7 @@ class AbuseReport {
}
recordTelemetry(errorType) {
const {addon, reportEntryPoint} = this;
const { addon, reportEntryPoint } = this;
AMTelemetry.recordReportEvent({
addonId: addon.id,
addonType: addon.type,
@ -282,15 +288,13 @@ class AbuseReport {
* It rejects with an AbuseReportError if the report couldn't be
* submitted for a known reason (or another Error type otherwise).
*/
async submit({reason, message}) {
const {
aborted, abortController,
reportData,
reportEntryPoint,
} = this[PRIVATE_REPORT_PROPS];
async submit({ reason, message }) {
const { aborted, abortController, reportData, reportEntryPoint } = this[
PRIVATE_REPORT_PROPS
];
// Record telemetry event and throw an AbuseReportError.
const rejectReportError = async (errorType, {response} = {}) => {
const rejectReportError = async (errorType, { response } = {}) => {
this.recordTelemetry(errorType);
let errorInfo;
@ -325,7 +329,7 @@ class AbuseReport {
method: "POST",
credentials: "omit",
referrerPolicy: "no-referrer",
headers: {"Content-Type": "application/json"},
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
...reportData,
report_entry_point: reportEntryPoint,
@ -355,22 +359,22 @@ class AbuseReport {
}
if (response.status >= 400 && response.status < 500) {
return rejectReportError("ERROR_CLIENT", {response});
return rejectReportError("ERROR_CLIENT", { response });
}
if (response.status >= 500 && response.status < 600) {
return rejectReportError("ERROR_SERVER", {response});
return rejectReportError("ERROR_SERVER", { response });
}
// We got an unexpected HTTP status code.
return rejectReportError("ERROR_UNKNOWN", {response});
return rejectReportError("ERROR_UNKNOWN", { response });
}
/**
* Abort the report submission.
*/
abort() {
const {abortController} = this[PRIVATE_REPORT_PROPS];
const { abortController } = this[PRIVATE_REPORT_PROPS];
abortController.abort();
this[PRIVATE_REPORT_PROPS].aborted = true;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -31,6 +31,6 @@ var LightweightThemeManager = {
},
get themeData() {
return _fallbackThemeData || {theme: null};
return _fallbackThemeData || { theme: null };
},
};

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

@ -10,43 +10,58 @@
"use strict";
ChromeUtils.defineModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
ChromeUtils.defineModuleGetter(
this,
"AppConstants",
"resource://gre/modules/AppConstants.jsm"
);
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyPreferenceGetter(this, "separatePrivilegedMozillaWebContentProcess",
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", false);
XPCOMUtils.defineLazyPreferenceGetter(this, "extensionsWebAPITesting",
"extensions.webapi.testing", false);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"separatePrivilegedMozillaWebContentProcess",
"browser.tabs.remote.separatePrivilegedMozillaWebContentProcess",
false
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"extensionsWebAPITesting",
"extensions.webapi.testing",
false
);
// The old XPInstall error codes
const EXECUTION_ERROR = -203;
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 USER_CANCELLED = -210;
const DOWNLOAD_ERROR = -228;
const UNSUPPORTED_TYPE = -244;
const SUCCESS = 0;
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
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 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";
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var gSingleton = null;
var AddonManager, AddonManagerPrivate;
function amManager() {
({AddonManager, AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"));
({ AddonManager, AddonManagerPrivate } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
));
Services.mm.loadFrameScript(CHILD_SCRIPT, true, true);
Services.mm.addMessageListener(MSG_INSTALL_ENABLED, this);
@ -81,14 +96,7 @@ amManager.prototype = {
installAddonFromWebpage(aPayload, aBrowser, aCallback) {
let retval = true;
const {
mimetype,
triggeringPrincipal,
hash,
icon,
name,
uri,
} = aPayload;
const { mimetype, triggeringPrincipal, hash, icon, name, uri } = aPayload;
if (!AddonManager.isInstallAllowed(mimetype, triggeringPrincipal)) {
aCallback = null;
@ -132,10 +140,11 @@ amManager.prototype = {
},
onDownloadFailed(aInstall) {
if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE) {
callCallback(CANT_READ_ARCHIVE);
else
} else {
callCallback(DOWNLOAD_ERROR);
}
},
onInstallFailed(aInstall) {
@ -148,7 +157,12 @@ amManager.prototype = {
});
}
AddonManager.installAddonFromWebpage(mimetype, aBrowser, triggeringPrincipal, aInstall);
AddonManager.installAddonFromWebpage(
mimetype,
aBrowser,
triggeringPrincipal,
aInstall
);
});
return retval;
@ -165,7 +179,7 @@ amManager.prototype = {
_addAddonListener(target) {
if (!this.addonListeners.has(target)) {
let handler = (event, id) => {
target.sendAsyncMessage(MSG_ADDON_EVENT, {event, id});
target.sendAsyncMessage(MSG_ADDON_EVENT, { event, id });
};
let listener = {
onEnabling: addon => handler("onEnabling", addon.id),
@ -176,7 +190,8 @@ amManager.prototype = {
onInstalled: addon => handler("onInstalled", addon.id),
onUninstalling: addon => handler("onUninstalling", addon.id),
onUninstalled: addon => handler("onUninstalled", addon.id),
onOperationCancelled: addon => handler("onOperationCancelled", addon.id),
onOperationCancelled: addon =>
handler("onOperationCancelled", addon.id),
};
this.addonListeners.set(target, listener);
AddonManager.addAddonListener(listener);
@ -222,20 +237,24 @@ amManager.prototype = {
}
case MSG_PROMISE_REQUEST: {
if (!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess && aMessage.target &&
aMessage.target.remoteType != null && aMessage.target.remoteType !== "privilegedmozilla") {
if (
!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess &&
aMessage.target &&
aMessage.target.remoteType != null &&
aMessage.target.remoteType !== "privilegedmozilla"
) {
return undefined;
}
let mm = aMessage.target.messageManager;
let resolve = (value) => {
let resolve = value => {
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
callbackID: payload.callbackID,
resolve: value,
});
};
let reject = (value) => {
let reject = value => {
mm.sendAsyncMessage(MSG_PROMISE_RESULT, {
callbackID: payload.callbackID,
reject: value,
@ -244,7 +263,10 @@ amManager.prototype = {
let API = AddonManager.webAPI;
if (payload.type in API) {
API[payload.type](aMessage.target, ...payload.args).then(resolve, reject);
API[payload.type](aMessage.target, ...payload.args).then(
resolve,
reject
);
} else {
reject("Unknown Add-on API request.");
}
@ -252,9 +274,13 @@ amManager.prototype = {
}
case MSG_INSTALL_CLEANUP: {
if (!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess && aMessage.target &&
aMessage.target.remoteType != null && aMessage.target.remoteType !== "privilegedmozilla") {
if (
!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess &&
aMessage.target &&
aMessage.target.remoteType != null &&
aMessage.target.remoteType !== "privilegedmozilla"
) {
return undefined;
}
@ -263,9 +289,13 @@ amManager.prototype = {
}
case MSG_ADDON_EVENT_REQ: {
if (!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess && aMessage.target &&
aMessage.target.remoteType != null && aMessage.target.remoteType !== "privilegedmozilla") {
if (
!extensionsWebAPITesting &&
separatePrivilegedMozillaWebContentProcess &&
aMessage.target &&
aMessage.target.remoteType != null &&
aMessage.target.remoteType !== "privilegedmozilla"
) {
return undefined;
}
@ -292,18 +322,24 @@ amManager.prototype = {
classID: Components.ID("{4399533d-08d1-458c-a87a-235f74451cfa}"),
_xpcom_factory: {
createInstance(aOuter, aIid) {
if (aOuter != null)
throw Components.Exception("Component does not support aggregation",
Cr.NS_ERROR_NO_AGGREGATION);
if (aOuter != null) {
throw Components.Exception(
"Component does not support aggregation",
Cr.NS_ERROR_NO_AGGREGATION
);
}
if (!gSingleton)
if (!gSingleton) {
gSingleton = new amManager();
}
return gSingleton.QueryInterface(aIid);
},
},
QueryInterface: ChromeUtils.generateQI([Ci.amIAddonManager,
Ci.nsITimerCallback,
Ci.nsIObserver]),
QueryInterface: ChromeUtils.generateQI([
Ci.amIAddonManager,
Ci.nsITimerCallback,
Ci.nsIObserver,
]),
};
const BLOCKLIST_JSM = "resource://gre/modules/Blocklist.jsm";
@ -319,7 +355,8 @@ BlocklistService.prototype = {
STATE_SOFTBLOCKED: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
STATE_BLOCKED: Ci.nsIBlocklistService.STATE_BLOCKED,
STATE_OUTDATED: Ci.nsIBlocklistService.STATE_OUTDATED,
STATE_VULNERABLE_UPDATE_AVAILABLE: Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE,
STATE_VULNERABLE_UPDATE_AVAILABLE:
Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE,
STATE_VULNERABLE_NO_UPDATE: Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE,
get isLoaded() {
@ -331,12 +368,18 @@ BlocklistService.prototype = {
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
}
if (Cu.isModuleLoaded(BLOCKLIST_JSM)) {
return Blocklist.getPluginBlocklistState(plugin, appVersion, toolkitVersion);
return Blocklist.getPluginBlocklistState(
plugin,
appVersion,
toolkitVersion
);
}
// Blocklist module isn't loaded yet. Queue the query until it is.
let request = {plugin, appVersion, toolkitVersion};
let promise = new Promise(resolve => { request.resolve = resolve; });
let request = { plugin, appVersion, toolkitVersion };
let promise = new Promise(resolve => {
request.resolve = resolve;
});
this.pluginQueries.push(request);
return promise;
@ -351,9 +394,11 @@ BlocklistService.prototype = {
},
classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
Ci.nsIBlocklistService,
Ci.nsITimerCallback]),
QueryInterface: ChromeUtils.generateQI([
Ci.nsIObserver,
Ci.nsIBlocklistService,
Ci.nsITimerCallback,
]),
};
// eslint-disable-next-line no-unused-vars

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

@ -7,10 +7,9 @@
const XPI_CONTENT_TYPE = "application/x-xpinstall";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
function amContentHandler() {
}
function amContentHandler() {}
amContentHandler.prototype = {
/**
@ -24,24 +23,27 @@ amContentHandler.prototype = {
* The nsIRequest dealing with the content
*/
handleContent(aMimetype, aContext, aRequest) {
if (aMimetype != XPI_CONTENT_TYPE)
if (aMimetype != XPI_CONTENT_TYPE) {
throw Cr.NS_ERROR_WONT_HANDLE_CONTENT;
}
if (!(aRequest instanceof Ci.nsIChannel))
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)
let callbacks = aRequest.notificationCallbacks
? aRequest.notificationCallbacks
: aRequest.loadGroup.notificationCallbacks;
if (callbacks) {
window = callbacks.getInterface(Ci.nsIDOMWindow);
}
aRequest.cancel(Cr.NS_BINDING_ABORTED);
const {triggeringPrincipal} = aRequest.loadInfo;
const { triggeringPrincipal } = aRequest.loadInfo;
let sourceHost;
@ -69,8 +71,12 @@ amContentHandler.prototype = {
// in-content UI page, walk up to find the first frame element in a chrome
// privileged document
let element = window.frameElement;
while (element && !element.ownerDocument.nodePrincipal.isSystemPrincipal)
while (
element &&
!element.ownerDocument.nodePrincipal.isSystemPrincipal
) {
element = element.ownerGlobal.frameElement;
}
if (element) {
let listener = Cc["@mozilla.org/addons/integration;1"].getService();

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

@ -4,19 +4,23 @@
"use strict";
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {Preferences} = ChromeUtils.import("resource://gre/modules/Preferences.jsm");
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Preferences } = ChromeUtils.import(
"resource://gre/modules/Preferences.jsm"
);
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
const XPINSTALL_MIMETYPE = "application/x-xpinstall";
const XPINSTALL_MIMETYPE = "application/x-xpinstall";
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
const MSG_INSTALL_ENABLED = "WebInstallerIsInstallEnabled";
const MSG_INSTALL_ADDON = "WebInstallerInstallAddonFromWebpage";
const MSG_INSTALL_CALLBACK = "WebInstallerInstallCallback";
var log = Log.repository.getLogger("AddonManager.InstallTrigger");
log.level = Log.Level[Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"];
log.level =
Log.Level[
Preferences.get("extensions.logging.enabled", false) ? "Warn" : "Trace"
];
function CallbackObject(id, callback, mediator) {
this.id = id;
@ -73,8 +77,12 @@ RemoteMediator.prototype = {
// in-content UI page, walk up to find the first frame element in a chrome
// privileged document
let element = window.frameElement;
while (element && !element.ownerDocument.nodePrincipal.isSystemPrincipal)
while (
element &&
!element.ownerDocument.nodePrincipal.isSystemPrincipal
) {
element = element.ownerGlobal.frameElement;
}
if (element) {
let listener = Cc["@mozilla.org/addons/integration;1"].getService();
@ -93,8 +101,9 @@ RemoteMediator.prototype = {
},
_addCallback(callback) {
if (!callback || typeof callback != "function")
if (!callback || typeof callback != "function") {
return -1;
}
let callbackID = this._windowID + "-" + ++this._lastCallbackID;
let callbackObject = new CallbackObject(callbackID, callback, this);
@ -105,9 +114,7 @@ RemoteMediator.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsISupportsWeakReference]),
};
function InstallTrigger() {
}
function InstallTrigger() {}
InstallTrigger.prototype = {
// We've declared ourselves as providing the nsIDOMGlobalPropertyInitializer
@ -148,12 +155,16 @@ InstallTrigger.prototype = {
item = { URL: item };
}
if (!item.URL) {
throw new this._window.Error("Missing URL property for '" + keys[0] + "'");
throw new this._window.Error(
"Missing URL property for '" + keys[0] + "'"
);
}
let url = this._resolveURL(item.URL);
if (!this._checkLoadURIFromScript(url)) {
throw new this._window.Error("Insufficient permissions to install: " + url.spec);
throw new this._window.Error(
"Insufficient permissions to install: " + url.spec
);
}
let iconUrl = null;
@ -182,15 +193,18 @@ InstallTrigger.prototype = {
sourceHost,
};
return this._mediator.install(installData, this._principal, callback, this._window);
return this._mediator.install(
installData,
this._principal,
callback,
this._window
);
},
startSoftwareUpdate(url, flags) {
let filename = Services.io.newURI(url)
.QueryInterface(Ci.nsIURL)
.filename;
let filename = Services.io.newURI(url).QueryInterface(Ci.nsIURL).filename;
let args = {};
args[filename] = { "URL": url };
args[filename] = { URL: url };
return this.install(args);
},
@ -205,9 +219,11 @@ InstallTrigger.prototype = {
_checkLoadURIFromScript(uri) {
let secman = Services.scriptSecurityManager;
try {
secman.checkLoadURIWithPrincipal(this._principal,
uri,
secman.DISALLOW_INHERIT_PRINCIPAL);
secman.checkLoadURIWithPrincipal(
this._principal,
uri,
secman.DISALLOW_INHERIT_PRINCIPAL
);
return true;
} catch (e) {
return false;

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

@ -4,19 +4,28 @@
"use strict";
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
"extensions.webextPermissionPrompts", false);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"WEBEXT_PERMISSION_PROMPTS",
"extensions.webextPermissionPrompts",
false
);
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 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) {
@ -51,7 +60,9 @@ class APIBroker {
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}`);
let err = new Error(
`Got install event for unknown install ${payload.id}`
);
Cu.reportError(err);
return;
}
@ -80,10 +91,10 @@ class APIBroker {
this._eventListener = callback;
if (callback) {
this.mm.addMessageListener(MSG_ADDON_EVENT, this);
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, {enabled: true});
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});
this.mm.sendAsyncMessage(MSG_ADDON_EVENT_REQ, { enabled: false });
}
}
@ -234,7 +245,8 @@ class WebAPI extends APIObject {
// Provide the host from which the amWebAPI is being called
// (so that we can detect if the API is being used from the disco pane,
// AMO, testpilot or another unknown webpage).
sourceHost: this.window.document.nodePrincipal.URI &&
sourceHost:
this.window.document.nodePrincipal.URI &&
this.window.document.nodePrincipal.URI.host,
};
installOptions.triggeringPrincipal = this.window.document.nodePrincipal;
@ -269,6 +281,10 @@ class WebAPI extends APIObject {
}
}
}
WebAPI.prototype.QueryInterface = ChromeUtils.generateQI(["nsIDOMGlobalPropertyInitializer"]);
WebAPI.prototype.classID = Components.ID("{8866d8e3-4ea5-48b7-a891-13ba0ac15235}");
WebAPI.prototype.QueryInterface = ChromeUtils.generateQI([
"nsIDOMGlobalPropertyInitializer",
]);
WebAPI.prototype.classID = Components.ID(
"{8866d8e3-4ea5-48b7-a891-13ba0ac15235}"
);
var EXPORTED_SYMBOLS = ["WebAPI"];

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -9,19 +9,28 @@
* isDisabledUnsigned, loadReleaseNotes, openOptionsInTab,
* promiseEvent, shouldShowPermissionsPrompt, showPermissionsPrompt */
const {AddonSettings} =
ChromeUtils.import("resource://gre/modules/addons/AddonSettings.jsm");
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const { AddonSettings } = ChromeUtils.import(
"resource://gre/modules/addons/AddonSettings.jsm"
);
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const HTML_NS = "http://www.w3.org/1999/xhtml";
XPCOMUtils.defineLazyPreferenceGetter(
this, "WEBEXT_PERMISSION_PROMPTS",
"extensions.webextPermissionPrompts", false);
this,
"WEBEXT_PERMISSION_PROMPTS",
"extensions.webextPermissionPrompts",
false
);
ChromeUtils.defineModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
ChromeUtils.defineModuleGetter(
this,
"Extension",
"resource://gre/modules/Extension.jsm"
);
function getBrowserElement() {
return window.docShell.chromeEventHandler;
@ -29,7 +38,7 @@ function getBrowserElement() {
function promiseEvent(event, target, capture = false) {
return new Promise(resolve => {
target.addEventListener(event, resolve, {capture, once: true});
target.addEventListener(event, resolve, { capture, once: true });
});
}
@ -38,7 +47,7 @@ function attachUpdateHandler(install) {
return;
}
install.promptHandler = (info) => {
install.promptHandler = info => {
let oldPerms = info.existingAddon.userPermissions;
if (!oldPerms) {
// Updating from a legacy add-on, let it proceed
@ -78,7 +87,7 @@ function attachUpdateHandler(install) {
}
async function loadReleaseNotes(uri) {
const res = await fetch(uri.spec, {credentials: "omit"});
const res = await fetch(uri.spec, { credentials: "omit" });
if (!res.ok) {
throw new Error("Error loading release notes");
@ -88,8 +97,9 @@ async function loadReleaseNotes(uri) {
const text = await res.text();
// Setup the content sanitizer.
const ParserUtils = Cc["@mozilla.org/parserutils;1"]
.getService(Ci.nsIParserUtils);
const ParserUtils = Cc["@mozilla.org/parserutils;1"].getService(
Ci.nsIParserUtils
);
const flags =
ParserUtils.SanitizerDropMedia |
ParserUtils.SanitizerDropNonCSSPresentation |
@ -117,12 +127,12 @@ function shouldShowPermissionsPrompt(addon) {
return false;
}
const {origins, permissions} = addon.userPermissions;
const { origins, permissions } = addon.userPermissions;
return origins.length > 0 || permissions.length > 0;
}
function showPermissionsPrompt(addon) {
return new Promise((resolve) => {
return new Promise(resolve => {
const permissions = addon.userPermissions;
const target = getBrowserElement();
@ -130,10 +140,13 @@ function showPermissionsPrompt(addon) {
// The user has just enabled a sideloaded extension, if the permission
// can be changed for the extension, show the post-install panel to
// give the user that opportunity.
if (addon.permissions &
AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS) {
Services.obs.notifyObservers({addon, target},
"webextension-install-notify");
if (
addon.permissions & AddonManager.PERM_CAN_CHANGE_PRIVATEBROWSING_ACCESS
) {
Services.obs.notifyObservers(
{ addon, target },
"webextension-install-notify"
);
}
resolve();
};
@ -182,8 +195,9 @@ function isCorrectlySigned(addon) {
}
function isDisabledUnsigned(addon) {
let signingRequired = (addon.type == "locale") ?
AddonSettings.LANGPACKS_REQUIRE_SIGNING :
AddonSettings.REQUIRE_SIGNING;
let signingRequired =
addon.type == "locale"
? AddonSettings.LANGPACKS_REQUIRE_SIGNING
: AddonSettings.REQUIRE_SIGNING;
return signingRequired && !isCorrectlySigned(addon);
}

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

@ -3,10 +3,16 @@
/* globals MozXULElement, Services, useHtmlViews, getHtmlBrowser, htmlBrowserLoaded */
{
const ABUSE_REPORT_ENABLED = Services.prefs.getBoolPref("extensions.abuseReport.enabled", false);
const ABUSE_REPORT_FRAME_URL = "chrome://mozapps/content/extensions/abuse-report-frame.html";
const ABUSE_REPORT_ENABLED = Services.prefs.getBoolPref(
"extensions.abuseReport.enabled",
false
);
const ABUSE_REPORT_FRAME_URL =
"chrome://mozapps/content/extensions/abuse-report-frame.html";
const fm = Services.focus;
const {AbuseReporter} = ChromeUtils.import("resource://gre/modules/AbuseReporter.jsm");
const { AbuseReporter } = ChromeUtils.import(
"resource://gre/modules/AbuseReporter.jsm"
);
class AddonAbuseReportsXULFrame extends MozXULElement {
constructor() {
@ -33,7 +39,9 @@
const browser = this.querySelector("browser");
this.promiseBrowserLoaded = new Promise(resolve => {
browser.addEventListener("load", () => resolve(browser), {once: true});
browser.addEventListener("load", () => resolve(browser), {
once: true,
});
});
document.addEventListener("focus", this);
@ -88,15 +96,19 @@
}
forwardEvent(evt) {
this.dispatchEvent(new CustomEvent(evt.type, {detail: evt.detail}));
this.dispatchEvent(new CustomEvent(evt.type, { detail: evt.detail }));
}
async openReport({addonId, reportEntryPoint}) {
async openReport({ addonId, reportEntryPoint }) {
if (this.report) {
throw new Error("Ignoring new abuse report request. AbuseReport panel already open");
throw new Error(
"Ignoring new abuse report request. AbuseReport panel already open"
);
} else {
try {
this.report = await AbuseReporter.createAbuseReport(addonId, {reportEntryPoint});
this.report = await AbuseReporter.createAbuseReport(addonId, {
reportEntryPoint,
});
this.update();
} catch (err) {
// Log the complete error in the console.
@ -105,9 +117,14 @@
// panel an error message-bar is created on the HTML about:addons page.
const win = await this.promiseHtmlAboutAddons;
win.document.dispatchEvent(
new CustomEvent("abuse-report:create-error", {detail: {
addonId, addon: err.addon, errorType: err.errorType,
}}));
new CustomEvent("abuse-report:create-error", {
detail: {
addonId,
addon: err.addon,
errorType: err.errorType,
},
})
);
}
}
}
@ -139,9 +156,9 @@
}
async update() {
const {report} = this;
const { report } = this;
if (report && report.addon && !report.errorType) {
const {addon, reportEntryPoint} = this.report;
const { addon, reportEntryPoint } = this.report;
this.addonId = addon.id;
this.reportEntryPoint = reportEntryPoint;
@ -149,9 +166,15 @@
// embedded in the XUL browser.
this.promiseAbuseReport.then(abuseReport => {
this.hidden = false;
abuseReport.addEventListener("abuse-report:updated", this, {once: true});
abuseReport.addEventListener("abuse-report:submit", this, {once: true});
abuseReport.addEventListener("abuse-report:cancel", this, {once: true});
abuseReport.addEventListener("abuse-report:updated", this, {
once: true,
});
abuseReport.addEventListener("abuse-report:submit", this, {
once: true,
});
abuseReport.addEventListener("abuse-report:cancel", this, {
once: true,
});
abuseReport.setAbuseReport(report);
// Hide the content of the underlying about:addons page from
// screen readers.
@ -171,16 +194,27 @@
// Move the focus back to the top level window.
fm.moveFocus(window, null, fm.MOVEFOCUS_ROOT, fm.FLAG_BYKEY);
this.promiseAbuseReport.then(abuseReport => {
abuseReport.removeEventListener("abuse-report:updated", this, {once: true});
abuseReport.removeEventListener("abuse-report:submit", this, {once: true});
abuseReport.removeEventListener("abuse-report:cancel", this, {once: true});
abuseReport.setAbuseReport(null);
}, err => {
console.error("promiseAbuseReport rejected", err);
}).then(() => {
this.dispatchEvent(new CustomEvent("abuse-report:frame-hidden"));
});
this.promiseAbuseReport
.then(
abuseReport => {
abuseReport.removeEventListener("abuse-report:updated", this, {
once: true,
});
abuseReport.removeEventListener("abuse-report:submit", this, {
once: true,
});
abuseReport.removeEventListener("abuse-report:cancel", this, {
once: true,
});
abuseReport.setAbuseReport(null);
},
err => {
console.error("promiseAbuseReport rejected", err);
}
)
.then(() => {
this.dispatchEvent(new CustomEvent("abuse-report:frame-hidden"));
});
}
}
@ -233,14 +267,17 @@
// the custom XUL WebComponent and append it to the XUL stack element
// (if not registered the element will be just a dummy hidden box)
if (useHtmlViews && ABUSE_REPORT_ENABLED) {
customElements.define("addon-abuse-report-xulframe", AddonAbuseReportsXULFrame);
customElements.define(
"addon-abuse-report-xulframe",
AddonAbuseReportsXULFrame
);
}
// Helper method exported into the about:addons global, used to open the
// abuse report panel from outside of the about:addons page
// (e.g. triggered from the browserAction context menu).
window.openAbuseReport = ({addonId, reportEntryPoint}) => {
window.openAbuseReport = ({ addonId, reportEntryPoint }) => {
const frame = document.querySelector("addon-abuse-report-xulframe");
frame.openReport({addonId, reportEntryPoint});
frame.openReport({ addonId, reportEntryPoint });
};
}

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

@ -5,55 +5,58 @@
"use strict";
ChromeUtils.defineModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
const showOnAnyType = () => false;
const hideOnAnyType = () => true;
const hideOnThemeType = (addonType) => addonType === "theme";
const hideOnThemeType = addonType => addonType === "theme";
// The reasons string used as a key in this Map is expected to stay in sync
// with the reasons string used in the "abuseReports.ftl" locale file and
// the suggestions templates included in abuse-reports-xulframe.html.
const ABUSE_REASONS = window.ABUSE_REPORT_REASONS = {
"damage": {
const ABUSE_REASONS = (window.ABUSE_REPORT_REASONS = {
damage: {
isExampleHidden: showOnAnyType,
isReasonHidden: hideOnThemeType,
},
"spam": {
spam: {
isExampleHidden: showOnAnyType,
isReasonHidden: showOnAnyType,
},
"settings": {
settings: {
hasSuggestions: true,
isExampleHidden: hideOnAnyType,
isReasonHidden: hideOnThemeType,
},
"deceptive": {
deceptive: {
isExampleHidden: showOnAnyType,
isReasonHidden: showOnAnyType,
},
"broken": {
broken: {
hasAddonTypeL10nId: true,
hasAddonTypeSuggestionTemplate: true,
hasSuggestions: true,
isExampleHidden: hideOnThemeType,
isReasonHidden: showOnAnyType,
},
"policy": {
policy: {
hasSuggestions: true,
isExampleHidden: hideOnAnyType,
isReasonHidden: showOnAnyType,
},
"unwanted": {
unwanted: {
isExampleHidden: showOnAnyType,
isReasonHidden: hideOnThemeType,
},
"other": {
other: {
isExampleHidden: hideOnAnyType,
isReasonHidden: showOnAnyType,
},
};
});
function getReasonL10nId(reason, addonType) {
let l10nId = `abuse-report-${reason}-reason`;
@ -101,9 +104,9 @@ const LEARNMORE_LINKS = {
function formatLearnMoreURLs(containerEl) {
for (const [linkClass, linkInfo] of Object.entries(LEARNMORE_LINKS)) {
for (const element of containerEl.querySelectorAll(linkClass)) {
const baseURL = linkInfo.baseURL ?
Services.urlFormatter.formatURL(linkInfo.baseURL) :
Services.urlFormatter.formatURLPref("app.support.baseURL");
const baseURL = linkInfo.baseURL
? Services.urlFormatter.formatURL(linkInfo.baseURL)
: Services.urlFormatter.formatURLPref("app.support.baseURL");
element.href = baseURL + linkInfo.path;
}
@ -114,7 +117,7 @@ function formatLearnMoreURLs(containerEl) {
function defineElementSelectorsGetters(object, propsMap) {
const props = Object.entries(propsMap).reduce((acc, entry) => {
const [name, selector] = entry;
acc[name] = {get: () => object.querySelector(selector)};
acc[name] = { get: () => object.querySelector(selector) };
return acc;
}, {});
Object.defineProperties(object, props);
@ -127,7 +130,7 @@ function defineElementAttributesProperties(object, propsMap) {
const [name, attr] = entry;
acc[name] = {
get: () => object.getAttribute(attr),
set: (value) => {
set: value => {
object.setAttribute(attr, value);
},
};
@ -148,7 +151,7 @@ function getElements(containerEl, propsMap) {
}
function dispatchCustomEvent(el, eventName, detail) {
el.dispatchEvent(new CustomEvent(eventName, {detail}));
el.dispatchEvent(new CustomEvent(eventName, { detail }));
}
// This WebComponent extends the li item to represent an abuse report reason
@ -177,7 +180,7 @@ class AbuseReasonListItem extends HTMLLIElement {
return;
}
const {reason, checked, addonType} = this;
const { reason, checked, addonType } = this;
this.textContent = "";
const content = document.importNode(this.template.content, true);
@ -186,7 +189,7 @@ class AbuseReasonListItem extends HTMLLIElement {
const reasonId = `abuse-reason-${reason}`;
const reasonInfo = ABUSE_REASONS[reason] || {};
const {labelEl, descriptionEl, radioEl} = getElements(content, {
const { labelEl, descriptionEl, radioEl } = getElements(content, {
labelEl: "label",
descriptionEl: ".reason-description",
radioEl: "input[type=radio]",
@ -200,13 +203,17 @@ class AbuseReasonListItem extends HTMLLIElement {
// This reason has a different localized description based on the
// addon type.
document.l10n.setAttributes(
descriptionEl, getReasonL10nId(reason, addonType));
descriptionEl,
getReasonL10nId(reason, addonType)
);
// Show the reason example if supported for the addon type.
if (!reasonInfo.isExampleHidden(addonType)) {
const exampleEl = content.querySelector(".reason-example");
document.l10n.setAttributes(
exampleEl, `abuse-report-${reason}-example`);
exampleEl,
`abuse-report-${reason}-example`
);
exampleEl.hidden = false;
}
}
@ -240,12 +247,12 @@ class AbuseReasonsPanel extends HTMLElement {
return;
}
const {addonType} = this;
const { addonType } = this;
this.textContent = "";
const content = document.importNode(this.template.content, true);
const {titleEl, listEl} = getElements(content, {
const { titleEl, listEl } = getElements(content, {
titleEl: ".abuse-report-title",
listEl: "ul.abuse-report-reasons",
});
@ -255,8 +262,8 @@ class AbuseReasonsPanel extends HTMLElement {
// Create the randomized list of reasons.
const reasons = Object.keys(ABUSE_REASONS)
.filter(reason => reason !== "other")
.sort(() => Math.random() - 0.5);
.filter(reason => reason !== "other")
.sort(() => Math.random() - 0.5);
for (const reason of reasons) {
const reasonInfo = ABUSE_REASONS[reason];
@ -299,7 +306,7 @@ class AbuseReasonSuggestions extends HTMLElement {
}
update() {
const {addonType, extensionSupportURL, reason} = this;
const { addonType, extensionSupportURL, reason } = this;
if (!addonType) {
return;
@ -359,7 +366,7 @@ class AbuseSubmitPanel extends HTMLElement {
if (!this.isConnected || !this.addonType) {
return;
}
const {addonType, reason, _suggestions, _title} = this;
const { addonType, reason, _suggestions, _title } = this;
document.l10n.setAttributes(_title, getReasonL10nId(reason, addonType));
_suggestions.reason = reason;
_suggestions.addonType = addonType;
@ -435,8 +442,7 @@ class AbuseReport extends HTMLElement {
this.handleKeyboardNavigation(evt);
break;
case "click":
if (evt.target === this._iconClose ||
evt.target === this._btnCancel) {
if (evt.target === this._iconClose || evt.target === this._btnCancel) {
// NOTE: clear the focus on the clicked element to ensure that
// -moz-focusring pseudo class is not still set on the element
// when the panel is going to be shown again (See Bug 1560949).
@ -468,8 +474,12 @@ class AbuseReport extends HTMLElement {
}
handleKeyboardNavigation(evt) {
if (evt.keyCode !== evt.DOM_VK_TAB ||
evt.altKey || evt.controlKey || evt.metaKey) {
if (
evt.keyCode !== evt.DOM_VK_TAB ||
evt.altKey ||
evt.controlKey ||
evt.metaKey
) {
return;
}
@ -497,8 +507,11 @@ class AbuseReport extends HTMLElement {
evt.stopImmediatePropagation();
const chromeWin = window.windowRoot.ownerGlobal;
Services.focus.moveFocus(
chromeWin, null,
Services.MOVEFOCUS_BACKWARD, Services.focus.FLAG_BYKEY);
chromeWin,
null,
Services.MOVEFOCUS_BACKWARD,
Services.focus.FLAG_BYKEY
);
}
}
@ -535,9 +548,11 @@ class AbuseReport extends HTMLElement {
_linkAddonAuthor.href = this.authorURL || this.homepageURL;
_linkAddonAuthor.textContent = this.authorName;
document.l10n.setAttributes(_linkAddonAuthor.parentNode,
"abuse-report-addon-authored-by",
{"author-name": this.authorName});
document.l10n.setAttributes(
_linkAddonAuthor.parentNode,
"abuse-report-addon-authored-by",
{ "author-name": this.authorName }
);
_addonIconElement.setAttribute("src", this.iconURL);
@ -551,7 +566,8 @@ class AbuseReport extends HTMLElement {
this.focus();
dispatchCustomEvent(this, "abuse-report:updated", {
addonId, panel: "reasons",
addonId,
panel: "reasons",
});
}
@ -573,11 +589,11 @@ class AbuseReport extends HTMLElement {
return;
}
if (this._reasonsPanel.hidden) {
const {_textarea} = this;
const { _textarea } = this;
_textarea.focus();
_textarea.select();
} else {
const {_radioCheckedReason} = this;
const { _radioCheckedReason } = this;
if (_radioCheckedReason) {
_radioCheckedReason.focus();
}
@ -617,7 +633,8 @@ class AbuseReport extends HTMLElement {
// Adjust the focused element when switching to the submit panel.
this.focus();
dispatchCustomEvent(this, "abuse-report:updated", {
addonId: this.addonId, panel: "submit",
addonId: this.addonId,
panel: "submit",
});
}
@ -632,7 +649,8 @@ class AbuseReport extends HTMLElement {
// Adjust the focused element when switching back to the list of reasons.
this.focus();
dispatchCustomEvent(this, "abuse-report:updated", {
addonId: this.addonId, panel: "reasons",
addonId: this.addonId,
panel: "reasons",
});
}
@ -657,18 +675,18 @@ class AbuseReport extends HTMLElement {
}
get homepageURL() {
const {addon} = this;
return addon && addon.homepageURL || this.authorURL || "";
const { addon } = this;
return (addon && addon.homepageURL) || this.authorURL || "";
}
get authorName() {
// The author name may be missing on some of the test extensions
// (or for temporarily installed add-ons).
return this.addonCreator && this.addonCreator.name || "";
return (this.addonCreator && this.addonCreator.name) || "";
}
get authorURL() {
return this.addonCreator && this.addonCreator.url || "";
return (this.addonCreator && this.addonCreator.url) || "";
}
get iconURL() {
@ -676,7 +694,7 @@ class AbuseReport extends HTMLElement {
}
get supportURL() {
return this.addon && this.addon.supportURL || this.homepageURL || "";
return (this.addon && this.addon.supportURL) || this.homepageURL || "";
}
get message() {
@ -692,14 +710,21 @@ class AbuseReport extends HTMLElement {
}
}
customElements.define("abuse-report-reason-listitem",
AbuseReasonListItem, {extends: "li"});
customElements.define("abuse-report-reason-suggestions",
AbuseReasonSuggestions);
customElements.define("abuse-report-reason-listitem", AbuseReasonListItem, {
extends: "li",
});
customElements.define(
"abuse-report-reason-suggestions",
AbuseReasonSuggestions
);
customElements.define("abuse-report-reasons-panel", AbuseReasonsPanel);
customElements.define("abuse-report-submit-panel", AbuseSubmitPanel);
customElements.define("addon-abuse-report", AbuseReport);
window.addEventListener("load", () => {
document.body.prepend(document.createElement("addon-abuse-report"));
}, {once: true});
window.addEventListener(
"load",
() => {
document.body.prepend(document.createElement("addon-abuse-report"));
},
{ once: true }
);

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

@ -14,66 +14,88 @@
// Message Bars definitions.
const ABUSE_REPORT_MESSAGE_BARS = {
// Idle message-bar (used while the submission is still ongoing).
"submitting": {id: "submitting", actions: ["cancel"]},
submitting: { id: "submitting", actions: ["cancel"] },
// Submitted report message-bar.
"submitted": {
id: "submitted", actionAddonTypeSuffix: true,
actions: ["remove", "keep"], dismissable: true,
submitted: {
id: "submitted",
actionAddonTypeSuffix: true,
actions: ["remove", "keep"],
dismissable: true,
},
// Submitted report message-bar (with no remove actions).
"submitted-no-remove-action": {
id: "submitted-noremove", dismissable: true,
id: "submitted-noremove",
dismissable: true,
},
// Submitted report and remove addon message-bar.
"submitted-and-removed": {
id: "removed", addonTypeSuffix: true, dismissable: true,
id: "removed",
addonTypeSuffix: true,
dismissable: true,
},
// The "aborted report" message bar is rendered as a generic informative one,
// because aborting a report is triggered by a user choice.
"ERROR_ABORTED_SUBMIT": {
id: "aborted", type: "generic", dismissable: true,
ERROR_ABORTED_SUBMIT: {
id: "aborted",
type: "generic",
dismissable: true,
},
// Errors message bars.
"ERROR_ADDON_NOTFOUND": {
id: "error", type: "error", dismissable: true,
ERROR_ADDON_NOTFOUND: {
id: "error",
type: "error",
dismissable: true,
},
"ERROR_CLIENT": {
id: "error", type: "error", dismissable: true,
ERROR_CLIENT: {
id: "error",
type: "error",
dismissable: true,
},
"ERROR_NETWORK": {
id: "error", actions: ["retry", "cancel"], type: "error",
ERROR_NETWORK: {
id: "error",
actions: ["retry", "cancel"],
type: "error",
},
"ERROR_RECENT_SUBMIT": {
id: "error-recent-submit", actions: ["retry", "cancel"], type: "error",
ERROR_RECENT_SUBMIT: {
id: "error-recent-submit",
actions: ["retry", "cancel"],
type: "error",
},
"ERROR_SERVER": {
id: "error", actions: ["retry", "cancel"], type: "error",
ERROR_SERVER: {
id: "error",
actions: ["retry", "cancel"],
type: "error",
},
"ERROR_UNKNOWN": {
id: "error", actions: ["retry", "cancel"], type: "error",
ERROR_UNKNOWN: {
id: "error",
actions: ["retry", "cancel"],
type: "error",
},
};
function openAbuseReport({addonId, reportEntryPoint}) {
document.dispatchEvent(new CustomEvent("abuse-report:new", {
detail: {addonId, reportEntryPoint},
}));
function openAbuseReport({ addonId, reportEntryPoint }) {
document.dispatchEvent(
new CustomEvent("abuse-report:new", {
detail: { addonId, reportEntryPoint },
})
);
}
// Helper function used to create abuse report message bars in the
// HTML about:addons page.
function createReportMessageBar(
definitionId, {addonId, addonName, addonType},
{onclose, onaction} = {}
definitionId,
{ addonId, addonName, addonType },
{ onclose, onaction } = {}
) {
const getMessageL10n = (id) => `abuse-report-messagebar-${id}`;
const getActionL10n = (action) => getMessageL10n(`action-${action}`);
const getMessageL10n = id => `abuse-report-messagebar-${id}`;
const getActionL10n = action => getMessageL10n(`action-${action}`);
const barInfo = ABUSE_REPORT_MESSAGE_BARS[definitionId];
if (!barInfo) {
throw new Error(`message-bar definition not found: ${definitionId}`);
}
const {id, dismissable, actions, type} = barInfo;
const { id, dismissable, actions, type } = barInfo;
const messageEl = document.createElement("span");
// The message element includes an addon-name span (also filled by
@ -86,18 +108,22 @@ function createReportMessageBar(
document.l10n.setAttributes(
messageEl,
getMessageL10n(barInfo.addonTypeSuffix ? `${id}-${addonType}` : id),
{"addon-name": addonName || addonId});
{ "addon-name": addonName || addonId }
);
const barActions = actions ? actions.map(action => {
// Some of the message bars require a different per addonType
// Fluent id for their actions.
const actionId = barInfo.actionAddonTypeSuffix ?
`${action}-${addonType}` : action;
const buttonEl = document.createElement("button");
buttonEl.addEventListener("click", () => onaction && onaction(action));
document.l10n.setAttributes(buttonEl, getActionL10n(actionId));
return buttonEl;
}) : [];
const barActions = actions
? actions.map(action => {
// Some of the message bars require a different per addonType
// Fluent id for their actions.
const actionId = barInfo.actionAddonTypeSuffix
? `${action}-${addonType}`
: action;
const buttonEl = document.createElement("button");
buttonEl.addEventListener("click", () => onaction && onaction(action));
document.l10n.setAttributes(buttonEl, getActionL10n(actionId));
return buttonEl;
})
: [];
const messagebar = document.createElement("message-bar");
messagebar.setAttribute("type", type || "generic");
@ -105,35 +131,40 @@ function createReportMessageBar(
messagebar.setAttribute("dismissable", "");
}
messagebar.append(messageEl, ...barActions);
messagebar.addEventListener("message-bar:close", onclose, {once: true});
messagebar.addEventListener("message-bar:close", onclose, { once: true });
document.getElementById("abuse-reports-messages").append(messagebar);
document.dispatchEvent(new CustomEvent("abuse-report:new-message-bar", {
detail: {definitionId, messagebar},
}));
document.dispatchEvent(
new CustomEvent("abuse-report:new-message-bar", {
detail: { definitionId, messagebar },
})
);
return messagebar;
}
async function submitReport({report, reason, message}) {
const {addon} = report;
async function submitReport({ report, reason, message }) {
const { addon } = report;
const addonId = addon.id;
const addonName = addon.name;
const addonType = addon.type;
// Create a message bar while we are still submitting the report.
const mbSubmitting = createReportMessageBar(
"submitting", {addonId, addonName, addonType}, {
onaction: (action) => {
"submitting",
{ addonId, addonName, addonType },
{
onaction: action => {
if (action === "cancel") {
report.abort();
mbSubmitting.remove();
}
},
});
}
);
try {
await report.submit({reason, message});
await report.submit({ reason, message });
mbSubmitting.remove();
// Create a submitted message bar when the submission has been
@ -152,53 +183,67 @@ async function submitReport({report, reason, message}) {
barId = "submitted";
}
const mbInfo = createReportMessageBar(barId, {
addonId, addonName, addonType,
}, {
onaction: (action) => {
mbInfo.remove();
// action "keep" doesn't require any further action,
// just handle "remove".
if (action === "remove") {
report.addon.uninstall(true);
}
const mbInfo = createReportMessageBar(
barId,
{
addonId,
addonName,
addonType,
},
});
{
onaction: action => {
mbInfo.remove();
// action "keep" doesn't require any further action,
// just handle "remove".
if (action === "remove") {
report.addon.uninstall(true);
}
},
}
);
} catch (err) {
// Log the complete error in the console.
console.error("Error submitting abuse report for", addonId, err);
mbSubmitting.remove();
// The report has a submission error, create a error message bar which
// may optionally allow the user to retry to submit the same report.
const barId = err.errorType in ABUSE_REPORT_MESSAGE_BARS ?
err.errorType : "ERROR_UNKNOWN";
const barId =
err.errorType in ABUSE_REPORT_MESSAGE_BARS
? err.errorType
: "ERROR_UNKNOWN";
const mbError = createReportMessageBar(barId, {
addonId, addonName, addonType,
}, {
onaction: (action) => {
mbError.remove();
switch (action) {
case "retry":
submitReport({report, reason, message});
break;
case "cancel":
report.abort();
break;
}
const mbError = createReportMessageBar(
barId,
{
addonId,
addonName,
addonType,
},
});
{
onaction: action => {
mbError.remove();
switch (action) {
case "retry":
submitReport({ report, reason, message });
break;
case "cancel":
report.abort();
break;
}
},
}
);
}
}
document.addEventListener("abuse-report:submit", ({detail}) => {
document.addEventListener("abuse-report:submit", ({ detail }) => {
submitReport(detail);
});
document.addEventListener("abuse-report:create-error", ({detail}) => {
const {addonId, addon, errorType} = detail;
const barId = errorType in ABUSE_REPORT_MESSAGE_BARS ?
errorType : "ERROR_UNKNOWN";
document.addEventListener("abuse-report:create-error", ({ detail }) => {
const { addonId, addon, errorType } = detail;
const barId =
errorType in ABUSE_REPORT_MESSAGE_BARS ? errorType : "ERROR_UNKNOWN";
createReportMessageBar(barId, {
addonId,
addonName: addon && addon.name,

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

@ -9,7 +9,7 @@
/* global MozXULElement */
/* exported init, finish */
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
var gArgs;
@ -18,17 +18,27 @@ function init() {
var hasSoftBlocks = false;
gArgs = window.arguments[0].wrappedJSObject;
document.addEventListener("dialogaccept", function() { finish(true); });
document.addEventListener("dialogcancel", function() { finish(false); });
document.addEventListener("dialogaccept", function() {
finish(true);
});
document.addEventListener("dialogcancel", function() {
finish(false);
});
// 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 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"));
cancelButton.setAttribute(
"label",
bundle.GetStringFromName("restartLaterButton")
);
cancelButton.setAttribute(
"accesskey",
bundle.GetStringFromName("restartLaterButton.accesskey")
);
var richlist = document.getElementById("addonList");
var list = gArgs.list;
@ -58,14 +68,24 @@ function init() {
fragment.pack = "end";
if (listItem.blocked) {
fragment.appendChild(MozXULElement.parseXULToFragment(`
fragment.appendChild(
MozXULElement.parseXULToFragment(
`
<label class="blockedLabel" value="&blocklist.blocked.label;"/>
`, ["chrome://mozapps/locale/extensions/blocklist.dtd"]));
`,
["chrome://mozapps/locale/extensions/blocklist.dtd"]
)
);
hasHardBlocks = true;
} else {
fragment.appendChild(MozXULElement.parseXULToFragment(`
fragment.appendChild(
MozXULElement.parseXULToFragment(
`
<checkbox class="disableCheckbox" checked="true" label="&blocklist.checkbox.label;"/>
`, ["chrome://mozapps/locale/extensions/blocklist.dtd"]));
`,
["chrome://mozapps/locale/extensions/blocklist.dtd"]
)
);
hasSoftBlocks = true;
}
@ -74,18 +94,21 @@ function init() {
richlist.appendChild(item);
}
if (hasHardBlocks && hasSoftBlocks)
if (hasHardBlocks && hasSoftBlocks) {
document.getElementById("bothMessage").hidden = false;
else if (hasHardBlocks)
} else if (hasHardBlocks) {
document.getElementById("hardBlockMessage").hidden = false;
else
} 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");
var url = Services.urlFormatter.formatURLPref(
"extensions.blocklist.detailsURL"
);
link.setAttribute("href", url);
}
}
@ -95,7 +118,8 @@ function finish(shouldRestartNow) {
var list = gArgs.list;
var items = document.getElementById("addonList").childNodes;
for (let i = 0; i < list.length; i++) {
if (!list[i].blocked)
if (!list[i].blocked) {
list[i].disable = items[i].querySelector(".disableCheckbox").checked;
}
}
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -10,7 +10,7 @@ class MessageBarStackElement extends HTMLElement {
constructor() {
super();
this._observer = null;
const shadowRoot = this.attachShadow({mode: "open"});
const shadowRoot = this.attachShadow({ mode: "open" });
shadowRoot.append(this.constructor.template.content.cloneNode(true));
}
@ -24,9 +24,9 @@ class MessageBarStackElement extends HTMLElement {
this._observer = new MutationObserver(() => {
this._observer.disconnect();
this.closeMessageBars();
this._observer.observe(this, {childList: true});
this._observer.observe(this, { childList: true });
});
this._observer.observe(this, {childList: true});
this._observer.observe(this, { childList: true });
}
disconnectedCallback() {
@ -35,7 +35,7 @@ class MessageBarStackElement extends HTMLElement {
}
closeMessageBars() {
const {maxMessageBarCount} = this;
const { maxMessageBarCount } = this;
if (maxMessageBarCount > 1) {
// Remove the older message bars if the stack reached the
// maximum number of message bars allowed.
@ -78,11 +78,12 @@ class MessageBarStackElement extends HTMLElement {
class MessageBarElement extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({mode: "open"});
const shadowRoot = this.attachShadow({ mode: "open" });
const content = this.constructor.template.content.cloneNode(true);
shadowRoot.append(content);
this._closeIcon.addEventListener(
"click", () => this.remove(), {once: true});
this._closeIcon.addEventListener("click", () => this.remove(), {
once: true,
});
}
disconnectedCallback() {

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

@ -31,7 +31,7 @@
class NamedDeckButton extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.attachShadow({ mode: "open" });
// Include styles inline to avoid a FOUC.
let style = document.createElement("style");
style.textContent = `
@ -72,11 +72,11 @@ class NamedDeckButton extends HTMLElement {
connectedCallback() {
this.setSelectedFromDeck();
document.addEventListener("view-changed", this, {capture: true});
document.addEventListener("view-changed", this, { capture: true });
}
disconnectedCallback() {
document.removeEventListener("view-changed", this, {capture: true});
document.removeEventListener("view-changed", this, { capture: true });
}
get deckId() {
@ -95,7 +95,7 @@ class NamedDeckButton extends HTMLElement {
if (e.type == "view-changed" && e.target.id == this.deckId) {
this.setSelectedFromDeck();
} else if (e.type == "click") {
let {deck} = this;
let { deck } = this;
if (deck) {
deck.selectedViewName = this.name;
}
@ -115,7 +115,7 @@ class NamedDeckButton extends HTMLElement {
}
setSelectedFromDeck() {
let {deck} = this;
let { deck } = this;
this.selected = deck && deck.selectedViewName == this.name;
}
}
@ -154,7 +154,7 @@ class NamedDeck extends HTMLElement {
constructor() {
super();
this.attachShadow({mode: "open"});
this.attachShadow({ mode: "open" });
// Create a slot for the visible content.
let selectedSlot = document.createElement("slot");
@ -178,7 +178,7 @@ class NamedDeck extends HTMLElement {
this.selectedViewName = firstView.getAttribute("name");
}
}
this.observer.observe(this, {childList: true});
this.observer.observe(this, { childList: true });
}
disconnectedCallback() {
@ -208,7 +208,7 @@ class NamedDeck extends HTMLElement {
* is shown.
*/
_setSelectedViewAttributes() {
let {selectedViewName} = this;
let { selectedViewName } = this;
for (let view of this.children) {
if (view.getAttribute("name") == selectedViewName) {
view.slot = "selected";

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

@ -5,32 +5,42 @@
"use strict";
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const { AddonManager } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
const PREFS = {
"pluginFlashBlockingCheckbox":
{ pref: "plugins.flashBlock.enabled", invert: false },
"pluginEnableProtectedModeCheckbox":
{ pref: "dom.ipc.plugins.flash.disable-protected-mode", invert: true },
pluginFlashBlockingCheckbox: {
pref: "plugins.flashBlock.enabled",
invert: false,
},
pluginEnableProtectedModeCheckbox: {
pref: "dom.ipc.plugins.flash.disable-protected-mode",
invert: true,
},
};
async function renderPluginMetadata(id) {
let plugin = await AddonManager.getAddonByID(id);
if (!plugin)
if (!plugin) {
return;
}
let libLabel = document.getElementById("pluginLibraries");
libLabel.textContent = plugin.pluginLibraries.join(", ");
let typeLabel = document.getElementById("pluginMimeTypes"), types = [];
let typeLabel = document.getElementById("pluginMimeTypes"),
types = [];
for (let type of plugin.pluginMimeTypes) {
let extras = [type.description.trim(), type.suffixes].
filter(x => x).join(": ");
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);
document.getElementById("pluginEnableProtectedMode")
document
.getElementById("pluginEnableProtectedMode")
.setAttribute("collapsed", showProtectedModePref ? "" : "true");
}
@ -48,8 +58,10 @@ function init() {
var prefVal = Services.prefs.getBoolPref(PREFS[id].pref);
checkbox.checked = PREFS[id].invert ? !prefVal : prefVal;
checkbox.addEventListener("command", () => {
Services.prefs.setBoolPref(PREFS[id].pref,
PREFS[id].invert ? !checkbox.checked : checkbox.checked);
Services.prefs.setBoolPref(
PREFS[id].pref,
PREFS[id].invert ? !checkbox.checked : checkbox.checked
);
});
}
}

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

@ -5,7 +5,9 @@
"use strict";
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
@ -24,14 +26,18 @@ let shortcutKeyMap = new Map();
const templates = {};
function loadTemplates() {
if (templatesLoaded) return;
if (templatesLoaded) {
return;
}
templatesLoaded = true;
templates.card = document.getElementById("card-template");
templates.row = document.getElementById("shortcut-row-template");
templates.noAddons = document.getElementById("shortcuts-no-addons");
templates.expandRow = document.getElementById("expand-row-template");
templates.noShortcutAddons = document.getElementById("shortcuts-no-commands-template");
templates.noShortcutAddons = document.getElementById(
"shortcuts-no-commands-template"
);
}
function extensionForAddonId(id) {
@ -44,7 +50,7 @@ let builtInNames = new Map([
["_execute_page_action", "shortcuts-pageAction"],
["_execute_sidebar_action", "shortcuts-sidebarAction"],
]);
let getCommandDescriptionId = (command) => {
let getCommandDescriptionId = command => {
if (!command.description && builtInNames.has(command.name)) {
return builtInNames.get(command.name);
}
@ -52,18 +58,75 @@ let getCommandDescriptionId = (command) => {
};
const _functionKeys = [
"F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
"F1",
"F2",
"F3",
"F4",
"F5",
"F6",
"F7",
"F8",
"F9",
"F10",
"F11",
"F12",
];
const functionKeys = new Set(_functionKeys);
const validKeys = new Set([
"Home", "End", "PageUp", "PageDown", "Insert", "Delete",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"Home",
"End",
"PageUp",
"PageDown",
"Insert",
"Delete",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
..._functionKeys,
"MediaNextTrack", "MediaPlayPause", "MediaPrevTrack", "MediaStop",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"Up", "Down", "Left", "Right",
"Comma", "Period", "Space",
"MediaNextTrack",
"MediaPlayPause",
"MediaPrevTrack",
"MediaStop",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"Up",
"Down",
"Left",
"Right",
"Comma",
"Period",
"Space",
]);
/**
@ -157,18 +220,25 @@ function getShortcutValue(shortcut) {
let error;
function setError(input, messageId, args) {
if (!error) error = document.querySelector(".error-message");
if (!error) {
error = document.querySelector(".error-message");
}
let {x, y, height} = input.getBoundingClientRect();
let { x, y, height } = input.getBoundingClientRect();
error.style.top = `${y + window.scrollY + height - 5}px`;
error.style.left = `${x}px`;
document.l10n.setAttributes(
error.querySelector(".error-message-label"), messageId, args);
error.querySelector(".error-message-label"),
messageId,
args
);
error.style.visibility = "visible";
}
function inputBlurred(e) {
if (!error) error = document.querySelector(".error-message");
if (!error) {
error = document.querySelector(".error-message");
}
error.style.visibility = "hidden";
e.target.value = getShortcutValue(e.target.getAttribute("shortcut"));
@ -272,7 +342,9 @@ function onShortcutChange(e) {
// Check if shortcut is already assigned.
if (shortcutKeyMap.has(shortcutString)) {
setError(input, "shortcuts-exists", {addon: getAddonName(shortcutString)});
setError(input, "shortcuts-exists", {
addon: getAddonName(shortcutString),
});
break;
} else {
// Update the shortcut if it isn't reserved or assigned.
@ -292,10 +364,11 @@ function onShortcutChange(e) {
input.blur();
break;
case ShortcutUtils.MODIFIER_REQUIRED:
if (AppConstants.platform == "macosx")
if (AppConstants.platform == "macosx") {
setError(input, "shortcuts-modifier-mac");
else
} else {
setError(input, "shortcuts-modifier-other");
}
break;
case ShortcutUtils.INVALID_COMBINATION:
setError(input, "shortcuts-invalid");
@ -326,11 +399,13 @@ async function renderAddons(addons) {
let extension = extensionForAddonId(addon.id);
// Skip this extension if it isn't a webextension.
if (!extension) continue;
if (!extension) {
continue;
}
if (extension.shortcuts) {
let card = document.importNode(
templates.card.content, true).firstElementChild;
let card = document.importNode(templates.card.content, true)
.firstElementChild;
let icon = AddonManager.getPreferredIconURL(addon, 24, window);
card.setAttribute("addon-id", addon.id);
card.setAttribute("addon-name", addon.name);
@ -342,21 +417,24 @@ async function renderAddons(addons) {
// Sort the commands so the ones with shortcuts are at the top.
commands.sort((a, b) => {
// Boolean compare the shortcuts to see if they're both set or unset.
if (!a.shortcut == !b.shortcut)
if (!a.shortcut == !b.shortcut) {
return 0;
if (a.shortcut)
}
if (a.shortcut) {
return -1;
}
return 1;
});
let {limit, allowOver} = COLLAPSE_OPTIONS;
let { limit, allowOver } = COLLAPSE_OPTIONS;
let willHideCommands = commands.length > limit + allowOver;
let firstHiddenInput;
for (let i = 0; i < commands.length; i++) {
let command = commands[i];
let row = document.importNode(templates.row.content, true).firstElementChild;
let row = document.importNode(templates.row.content, true)
.firstElementChild;
if (willHideCommands && i >= limit) {
row.setAttribute("hide-before-expand", "true");
@ -391,14 +469,14 @@ async function renderAddons(addons) {
let row = document.importNode(templates.expandRow.content, true);
let button = row.querySelector(".expand-button");
let numberToShow = commands.length - limit;
let setLabel = (type) => {
let setLabel = type => {
document.l10n.setAttributes(button, `shortcuts-card-${type}-button`, {
numberToShow,
});
};
setLabel("expand");
button.addEventListener("click", (event) => {
button.addEventListener("click", event => {
let expanded = card.hasAttribute("expanded");
if (expanded) {
card.removeAttribute("expanded");

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

@ -4,7 +4,9 @@
"use strict";
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
@ -38,30 +40,33 @@ XPCOMUtils.defineLazyGetter(this, "PLATFORM", () => {
return platform;
});
var EXPORTED_SYMBOLS = [ "AddonRepository" ];
var EXPORTED_SYMBOLS = ["AddonRepository"];
Cu.importGlobalProperties(["fetch"]);
const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
const PREF_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
const PREF_GETADDONS_DB_SCHEMA = "extensions.getAddons.databaseSchema";
const PREF_GET_LANGPACKS = "extensions.getAddons.langpacks.url";
const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
const PREF_GETADDONS_CACHE_ID_ENABLED =
"extensions.%ID%.getAddons.cache.enabled";
const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
const PREF_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
const PREF_GETADDONS_BROWSESEARCHRESULTS =
"extensions.getAddons.search.browseURL";
const PREF_GETADDONS_DB_SCHEMA = "extensions.getAddons.databaseSchema";
const PREF_GET_LANGPACKS = "extensions.getAddons.langpacks.url";
const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
const PREF_METADATA_UPDATETHRESHOLD_SEC = "extensions.getAddons.cache.updateThreshold";
const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
const PREF_METADATA_UPDATETHRESHOLD_SEC =
"extensions.getAddons.cache.updateThreshold";
const DEFAULT_METADATA_UPDATETHRESHOLD_SEC = 172800; // two days
const DEFAULT_CACHE_TYPES = "extension,theme,locale,dictionary";
const FILE_DATABASE = "addons.json";
const DB_SCHEMA = 5;
const DB_MIN_JSON_SCHEMA = 5;
const DB_BATCH_TIMEOUT_MS = 50;
const FILE_DATABASE = "addons.json";
const DB_SCHEMA = 5;
const DB_MIN_JSON_SCHEMA = 5;
const DB_BATCH_TIMEOUT_MS = 50;
const BLANK_DB = function() {
return {
@ -71,9 +76,9 @@ const BLANK_DB = function() {
};
};
const TOOLKIT_ID = "toolkit@mozilla.org";
const TOOLKIT_ID = "toolkit@mozilla.org";
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
const LOGGER_ID = "addons.repository";
// Create a new logger for use by the Addons Repository
@ -81,25 +86,30 @@ const LOGGER_ID = "addons.repository";
var logger = Log.repository.getLogger(LOGGER_ID);
function convertHTMLToPlainText(html) {
if (!html)
if (!html) {
return html;
var converter = Cc["@mozilla.org/widget/htmlformatconverter;1"].
createInstance(Ci.nsIFormatConverter);
}
var converter = Cc[
"@mozilla.org/widget/htmlformatconverter;1"
].createInstance(Ci.nsIFormatConverter);
var input = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
var input = Cc["@mozilla.org/supports-string;1"].createInstance(
Ci.nsISupportsString
);
input.data = html.replace(/\n/g, "<br>");
var output = {};
converter.convert("text/html", input, "text/unicode", output);
if (output.value instanceof Ci.nsISupportsString)
if (output.value instanceof Ci.nsISupportsString) {
return output.value.data.replace(/\r\n/g, "\n");
}
return html;
}
async function getAddonsToCache(aIds) {
let types = Preferences.get(PREF_GETADDONS_CACHE_TYPES) || DEFAULT_CACHE_TYPES;
let types =
Preferences.get(PREF_GETADDONS_CACHE_TYPES) || DEFAULT_CACHE_TYPES;
types = types.split(",");
@ -109,8 +119,9 @@ async function getAddonsToCache(aIds) {
for (let [i, addon] of addons.entries()) {
var preference = PREF_GETADDONS_CACHE_ID_ENABLED.replace("%ID%", aIds[i]);
// If the preference doesn't exist caching is enabled by default
if (!Preferences.get(preference, true))
if (!Preferences.get(preference, true)) {
continue;
}
// The add-ons manager may not know about this ID yet if it is a pending
// install. In that case we'll just cache it regardless
@ -190,7 +201,7 @@ AddonSearchResult.prototype = {
return this.icons && this.icons[32];
},
/**
/**
* The URLs of the add-on's icons, as an object with icon size as key
*/
icons: null,
@ -255,9 +266,9 @@ AddonSearchResult.prototype = {
for (let property of Object.keys(this)) {
let value = this[property];
if (property.startsWith("_") ||
typeof(value) === "function")
if (property.startsWith("_") || typeof value === "function") {
continue;
}
try {
switch (property) {
@ -279,8 +290,9 @@ AddonSearchResult.prototype = {
for (let property of Object.keys(this._unsupportedProperties)) {
let value = this._unsupportedProperties[property];
if (!property.startsWith("_"))
if (!property.startsWith("_")) {
json[property] = value;
}
}
return json;
@ -305,7 +317,7 @@ var AddonRepository = {
*/
get homepageURL() {
let url = this._formatURLPref(PREF_GETADDONS_BROWSEADDONS, {});
return (url != null) ? url : "about:blank";
return url != null ? url : "about:blank";
},
/**
@ -320,7 +332,7 @@ var AddonRepository = {
let url = this._formatURLPref(PREF_GETADDONS_BROWSESEARCHRESULTS, {
TERMS: aSearchTerms,
});
return (url != null) ? url : "about:blank";
return url != null ? url : "about:blank";
},
/**
@ -346,8 +358,11 @@ var AddonRepository = {
},
isMetadataStale() {
let threshold = Services.prefs.getIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC, DEFAULT_METADATA_UPDATETHRESHOLD_SEC);
return (this.metadataAge() > threshold);
let threshold = Services.prefs.getIntPref(
PREF_METADATA_UPDATETHRESHOLD_SEC,
DEFAULT_METADATA_UPDATETHRESHOLD_SEC
);
return this.metadataAge() > threshold;
},
/**
@ -421,7 +436,8 @@ var AddonRepository = {
*/
_clearCache() {
return AddonDatabase.delete().then(() =>
AddonManagerPrivate.updateAddonRepositoryData());
AddonManagerPrivate.updateAddonRepositoryData()
);
},
/**
@ -442,7 +458,7 @@ var AddonRepository = {
* the API call(s).
*/
_fetchPaged(ids, pref, handler) {
let startURL = this._formatURLPref(pref, {IDS: ids.join(",")});
let startURL = this._formatURLPref(pref, { IDS: ids.join(",") });
let results = [];
let idCheck = ids.map(id => {
if (id.startsWith("rta:")) {
@ -451,9 +467,9 @@ var AddonRepository = {
return id;
});
const fetchNextPage = (url) => {
const fetchNextPage = url => {
return new Promise((resolve, reject) => {
let request = new ServiceRequest({mozAnon: true});
let request = new ServiceRequest({ mozAnon: true });
request.mozBackgroundRequest = true;
request.open("GET", url, true);
request.responseType = "json";
@ -472,7 +488,9 @@ var AddonRepository = {
}
try {
let newResults = handler(response.results).filter(e => idCheck.includes(e.id));
let newResults = handler(response.results).filter(e =>
idCheck.includes(e.id)
);
results.push(...newResults);
} catch (err) {
reject(err);
@ -500,9 +518,9 @@ var AddonRepository = {
* @returns {array<AddonSearchResult>}
*/
async getAddonsByIDs(aIDs) {
return this._fetchPaged(aIDs, PREF_GETADDONS_BYIDS,
results => results.map(
entry => this._parseAddon(entry)));
return this._fetchPaged(aIDs, PREF_GETADDONS_BYIDS, results =>
results.map(entry => this._parseAddon(entry))
);
},
/**
@ -521,17 +539,23 @@ var AddonRepository = {
async _getFullData(aIDs) {
let metadataPromise = this.getAddonsByIDs(aIDs, false);
let overridesPromise = this._fetchPaged(aIDs, PREF_COMPAT_OVERRIDES,
results => results.map(
entry => this._parseCompatEntry(entry)));
let addons = [], overrides = [];
let overridesPromise = this._fetchPaged(
aIDs,
PREF_COMPAT_OVERRIDES,
results => results.map(entry => this._parseCompatEntry(entry))
);
let addons = [],
overrides = [];
try {
[addons, overrides] = await Promise.all([metadataPromise, overridesPromise]);
[addons, overrides] = await Promise.all([
metadataPromise,
overridesPromise,
]);
} catch (err) {
logger.error(`Error in addon metadata check: ${err.message}`);
}
return {addons, overrides};
return { addons, overrides };
},
/**
@ -542,7 +566,9 @@ var AddonRepository = {
* The array of add-on ids to add to the cache
*/
async cacheAddons(aIds) {
logger.debug("cacheAddons: enabled " + this.cacheEnabled + " IDs " + aIds.toSource());
logger.debug(
"cacheAddons: enabled " + this.cacheEnabled + " IDs " + aIds.toSource()
);
if (!this.cacheEnabled) {
return [];
}
@ -554,7 +580,7 @@ var AddonRepository = {
return [];
}
let {addons, overrides} = await this._getFullData(ids);
let { addons, overrides } = await this._getFullData(ids);
await AddonDatabase.update(addons, overrides);
return Array.from(addons.values());
@ -587,7 +613,7 @@ var AddonRepository = {
return;
}
let {addons, overrides} = await this._getFullData(addonsToCache);
let { addons, overrides } = await this._getFullData(addonsToCache);
AddonDatabase.repopulate(addons, overrides);
@ -643,7 +669,9 @@ var AddonRepository = {
}
if (Array.isArray(aEntry.authors)) {
let authors = aEntry.authors.map(author => new AddonManagerPrivate.AddonAuthor(author.name, author.url));
let authors = aEntry.authors.map(
author => new AddonManagerPrivate.AddonAuthor(author.name, author.url)
);
if (authors.length > 0) {
addon.creator = authors[0];
addon.developers = authors.slice(1);
@ -652,16 +680,19 @@ var AddonRepository = {
if (typeof aEntry.previews == "object") {
addon.screenshots = aEntry.previews.map(shot => {
let safeSize = orig => Array.isArray(orig) && orig.length >= 2 ? orig : [null, null];
let safeSize = orig =>
Array.isArray(orig) && orig.length >= 2 ? orig : [null, null];
let imageSize = safeSize(shot.image_size);
let thumbSize = safeSize(shot.thumbnail_size);
return new AddonManagerPrivate.AddonScreenshot(shot.image_url,
imageSize[0],
imageSize[1],
shot.thumbnail_url,
thumbSize[0],
thumbSize[1],
shot.caption);
return new AddonManagerPrivate.AddonScreenshot(
shot.image_url,
imageSize[0],
imageSize[1],
shot.thumbnail_url,
thumbSize[0],
thumbSize[1],
shot.caption
);
});
}
@ -705,7 +736,9 @@ var AddonRepository = {
return null;
}
let override = new AddonManagerPrivate.AddonCompatibilityOverride("incompatible");
let override = new AddonManagerPrivate.AddonCompatibilityOverride(
"incompatible"
);
override.minVersion = range.addon_min_version;
override.maxVersion = range.addon_max_version;
@ -726,7 +759,9 @@ var AddonRepository = {
}
if (!override.appID) {
logger.debug("Compatibility override is missing a valid application range.");
logger.debug(
"Compatibility override is missing a valid application range."
);
continue;
}
@ -748,8 +783,9 @@ var AddonRepository = {
}
url = url.replace(/%([A-Z_]+)%/g, function(aMatch, aKey) {
return (aKey in aSubstitutions) ? encodeURIComponent(aSubstitutions[aKey])
: aMatch;
return aKey in aSubstitutions
? encodeURIComponent(aSubstitutions[aKey])
: aMatch;
});
return Services.urlFormatter.formatURL(url);
@ -757,21 +793,26 @@ var AddonRepository = {
// Find a AddonCompatibilityOverride that matches a given aAddonVersion and
// application/platform version.
findMatchingCompatOverride(aAddonVersion,
aCompatOverrides,
aAppVersion,
aPlatformVersion) {
findMatchingCompatOverride(
aAddonVersion,
aCompatOverrides,
aAppVersion,
aPlatformVersion
) {
for (let override of aCompatOverrides) {
let appVersion = null;
if (override.appID == TOOLKIT_ID)
if (override.appID == TOOLKIT_ID) {
appVersion = aPlatformVersion || Services.appinfo.platformVersion;
else
} else {
appVersion = aAppVersion || Services.appinfo.version;
}
if (Services.vc.compare(override.minVersion, aAddonVersion) <= 0 &&
Services.vc.compare(aAddonVersion, override.maxVersion) <= 0 &&
Services.vc.compare(override.appMinVersion, appVersion) <= 0 &&
Services.vc.compare(appVersion, override.appMaxVersion) <= 0) {
if (
Services.vc.compare(override.minVersion, aAddonVersion) <= 0 &&
Services.vc.compare(aAddonVersion, override.maxVersion) <= 0 &&
Services.vc.compare(override.appMinVersion, appVersion) <= 0 &&
Services.vc.compare(appVersion, override.appMaxVersion) <= 0
) {
return override;
}
}
@ -787,7 +828,7 @@ var AddonRepository = {
// http://addons-server.readthedocs.io/en/latest/topics/api/addons.html#language-tools
let url = this._formatURLPref(PREF_GET_LANGPACKS);
let response = await fetch(url, {credentials: "omit"});
let response = await fetch(url, { credentials: "omit" });
if (!response.ok) {
throw new Error("fetching available language packs failed");
}
@ -796,13 +837,18 @@ var AddonRepository = {
let result = [];
for (let entry of data.results) {
if (!entry.current_compatible_version ||
!entry.current_compatible_version.files) {
continue;
if (
!entry.current_compatible_version ||
!entry.current_compatible_version.files
) {
continue;
}
for (let file of entry.current_compatible_version.files) {
if (file.platform == "all" || file.platform == Services.appinfo.OS.toLowerCase()) {
if (
file.platform == "all" ||
file.platform == Services.appinfo.OS.toLowerCase()
) {
result.push({
target_locale: entry.target_locale,
url: file.url,
@ -840,66 +886,69 @@ var AddonDatabase = {
openConnection() {
if (!this.connectionPromise) {
this.connectionPromise = (async () => {
let inputDB, schema;
let inputDB, schema;
try {
let data = await OS.File.read(this.jsonFile, { encoding: "utf-8"});
inputDB = JSON.parse(data);
try {
let data = await OS.File.read(this.jsonFile, { encoding: "utf-8" });
inputDB = JSON.parse(data);
if (!inputDB.hasOwnProperty("addons") ||
!Array.isArray(inputDB.addons)) {
throw new Error("No addons array.");
}
if (
!inputDB.hasOwnProperty("addons") ||
!Array.isArray(inputDB.addons)
) {
throw new Error("No addons array.");
}
if (!inputDB.hasOwnProperty("schema")) {
throw new Error("No schema specified.");
}
if (!inputDB.hasOwnProperty("schema")) {
throw new Error("No schema specified.");
}
schema = parseInt(inputDB.schema, 10);
schema = parseInt(inputDB.schema, 10);
if (!Number.isInteger(schema) ||
schema < DB_MIN_JSON_SCHEMA) {
throw new Error("Invalid schema value.");
}
} catch (e) {
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
logger.debug("No " + FILE_DATABASE + " found.");
} else {
logger.error(`Malformed ${FILE_DATABASE}: ${e} - resetting to empty`);
}
if (!Number.isInteger(schema) || schema < DB_MIN_JSON_SCHEMA) {
throw new Error("Invalid schema value.");
}
} catch (e) {
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
logger.debug("No " + FILE_DATABASE + " found.");
} else {
logger.error(
`Malformed ${FILE_DATABASE}: ${e} - resetting to empty`
);
}
// Create a blank addons.json file
this.save();
// Create a blank addons.json file
this.save();
Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
this._loaded = true;
return this.DB;
}
Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
this._loaded = true;
return this.DB;
}
Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
Services.prefs.setIntPref(PREF_GETADDONS_DB_SCHEMA, DB_SCHEMA);
// Convert the addon and compat override objects as necessary
// and store them in our in-memory copy of the database.
for (let addon of inputDB.addons) {
let id = addon.id;
// Convert the addon and compat override objects as necessary
// and store them in our in-memory copy of the database.
for (let addon of inputDB.addons) {
let id = addon.id;
let entry = this._parseAddon(addon);
this.DB.addons.set(id, entry);
let entry = this._parseAddon(addon);
this.DB.addons.set(id, entry);
if (entry.compatibilityOverrides) {
this.DB.compatOverrides.set(id, entry.compatibilityOverrides);
}
}
if (entry.compatibilityOverrides) {
this.DB.compatOverrides.set(id, entry.compatibilityOverrides);
}
}
if (inputDB.compatOverrides) {
for (let entry of inputDB.compatOverrides) {
this.DB.compatOverrides.set(entry.id, entry.compatRanges);
}
}
if (inputDB.compatOverrides) {
for (let entry of inputDB.compatOverrides) {
this.DB.compatOverrides.set(entry.id, entry.compatRanges);
}
}
this._loaded = true;
return this.DB;
})();
return this.DB;
})();
}
return this.connectionPromise;
@ -949,9 +998,13 @@ var AddonDatabase = {
// shutdown(true) never rejects
this._deleting = this.shutdown(true)
.then(() => OS.File.remove(this.jsonFile, {}))
.catch(error => logger.error("Unable to delete Addon Repository file " +
this.jsonFile, error))
.then(() => this._deleting = null)
.catch(error =>
logger.error(
"Unable to delete Addon Repository file " + this.jsonFile,
error
)
)
.then(() => (this._deleting = null))
.then(aCallback);
return this._deleting;
@ -965,20 +1018,26 @@ var AddonDatabase = {
};
for (let [id, overrides] of this.DB.compatOverrides.entries()) {
json.compatOverrides.push({id, compatRanges: overrides});
json.compatOverrides.push({ id, compatRanges: overrides });
}
await OS.File.writeAtomic(this.jsonFile, JSON.stringify(json),
{tmpPath: `${this.jsonFile}.tmp`});
await OS.File.writeAtomic(this.jsonFile, JSON.stringify(json), {
tmpPath: `${this.jsonFile}.tmp`,
});
},
save() {
if (!this._saveTask) {
this._saveTask = new DeferredTask(() => this._saveNow(), DB_BATCH_TIMEOUT_MS);
this._saveTask = new DeferredTask(
() => this._saveNow(),
DB_BATCH_TIMEOUT_MS
);
if (!this._blockerAdded) {
AsyncShutdown.profileBeforeChange.addBlocker(
"Flush AddonRepository", () => this.flush());
"Flush AddonRepository",
() => this.flush()
);
this._blockerAdded = true;
}
}
@ -1041,7 +1100,9 @@ var AddonDatabase = {
this._update(aAddons, aCompatOverrides);
let now = Math.round(Date.now() / 1000);
logger.debug("Cache repopulated, setting " + PREF_METADATA_LASTUPDATE + " to " + now);
logger.debug(
"Cache repopulated, setting " + PREF_METADATA_LASTUPDATE + " to " + now
);
Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, now);
},
@ -1090,19 +1151,24 @@ var AddonDatabase = {
* @return Returns an AddonSearchResult object.
*/
_parseAddon(aObj) {
if (aObj instanceof AddonSearchResult)
if (aObj instanceof AddonSearchResult) {
return aObj;
}
let id = aObj.id;
if (!aObj.id)
if (!aObj.id) {
return null;
}
let addon = new AddonSearchResult(id);
for (let expectedProperty of Object.keys(AddonSearchResult.prototype)) {
if (!(expectedProperty in aObj) ||
typeof(aObj[expectedProperty]) === "function")
if (
!(expectedProperty in aObj) ||
typeof aObj[expectedProperty] === "function"
) {
continue;
}
let value = aObj[expectedProperty];
@ -1113,9 +1179,7 @@ var AddonDatabase = {
break;
case "creator":
addon.creator = value
? this._makeDeveloper(value)
: null;
addon.creator = value ? this._makeDeveloper(value) : null;
break;
case "updateDate":
@ -1123,21 +1187,27 @@ var AddonDatabase = {
break;
case "developers":
if (!addon.developers) addon.developers = [];
if (!addon.developers) {
addon.developers = [];
}
for (let developer of value) {
addon.developers.push(this._makeDeveloper(developer));
}
break;
case "screenshots":
if (!addon.screenshots) addon.screenshots = [];
if (!addon.screenshots) {
addon.screenshots = [];
}
for (let screenshot of value) {
addon.screenshots.push(this._makeScreenshot(screenshot));
}
break;
case "icons":
if (!addon.icons) addon.icons = {};
if (!addon.icons) {
addon.icons = {};
}
for (let size of Object.keys(aObj.icons)) {
addon.icons[size] = aObj.icons[size];
}
@ -1150,7 +1220,9 @@ var AddonDatabase = {
addon[expectedProperty] = value;
}
} catch (ex) {
logger.warn("Error in parsing property value for " + expectedProperty + " | " + ex);
logger.warn(
"Error in parsing property value for " + expectedProperty + " | " + ex
);
}
// delete property from obj to indicate we've already
@ -1165,7 +1237,7 @@ var AddonDatabase = {
// The properties will be merged in the same object
// prior to being written back through toJSON.
for (let remainingProperty of Object.keys(aObj)) {
switch (typeof(aObj[remainingProperty])) {
switch (typeof aObj[remainingProperty]) {
case "boolean":
case "number":
case "string":
@ -1176,9 +1248,10 @@ var AddonDatabase = {
continue;
}
if (!remainingProperty.startsWith("_"))
if (!remainingProperty.startsWith("_")) {
addon._unsupportedProperties[remainingProperty] =
aObj[remainingProperty];
}
}
return addon;
@ -1214,7 +1287,14 @@ var AddonDatabase = {
let thumbnailWidth = aObj.thumbnailWidth;
let thumbnailHeight = aObj.thumbnailHeight;
let caption = aObj.caption;
return new AddonManagerPrivate.AddonScreenshot(url, width, height, thumbnailURL,
thumbnailWidth, thumbnailHeight, caption);
return new AddonManagerPrivate.AddonScreenshot(
url,
width,
height,
thumbnailURL,
thumbnailWidth,
thumbnailHeight,
caption
);
},
};

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

@ -4,10 +4,14 @@
"use strict";
var EXPORTED_SYMBOLS = [ "AddonSettings" ];
var EXPORTED_SYMBOLS = ["AddonSettings"];
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
const PREF_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
const PREF_LANGPACK_SIGNATURES = "extensions.langpacks.signatures.required";
@ -30,15 +34,27 @@ if (AppConstants.MOZ_REQUIRE_SIGNING && !Cu.isInAutomation) {
makeConstant("REQUIRE_SIGNING", true);
makeConstant("LANGPACKS_REQUIRE_SIGNING", true);
} else {
XPCOMUtils.defineLazyPreferenceGetter(AddonSettings, "REQUIRE_SIGNING",
PREF_SIGNATURES_REQUIRED, false);
XPCOMUtils.defineLazyPreferenceGetter(AddonSettings, "LANGPACKS_REQUIRE_SIGNING",
PREF_LANGPACK_SIGNATURES, false);
XPCOMUtils.defineLazyPreferenceGetter(
AddonSettings,
"REQUIRE_SIGNING",
PREF_SIGNATURES_REQUIRED,
false
);
XPCOMUtils.defineLazyPreferenceGetter(
AddonSettings,
"LANGPACKS_REQUIRE_SIGNING",
PREF_LANGPACK_SIGNATURES,
false
);
}
if (AppConstants.MOZ_ALLOW_LEGACY_EXTENSIONS || Cu.isInAutomation) {
XPCOMUtils.defineLazyPreferenceGetter(AddonSettings, "ALLOW_LEGACY_EXTENSIONS",
PREF_ALLOW_LEGACY, true);
XPCOMUtils.defineLazyPreferenceGetter(
AddonSettings,
"ALLOW_LEGACY_EXTENSIONS",
PREF_ALLOW_LEGACY,
true
);
} else {
makeConstant("ALLOW_LEGACY_EXTENSIONS", false);
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -9,36 +9,56 @@
"use strict";
var EXPORTED_SYMBOLS = [ "AddonUpdateChecker" ];
var EXPORTED_SYMBOLS = ["AddonUpdateChecker"];
const TIMEOUT = 60 * 1000;
const TOOLKIT_ID = "toolkit@mozilla.org";
const TIMEOUT = 60 * 1000;
const TOOLKIT_ID = "toolkit@mozilla.org";
const PREF_UPDATE_REQUIREBUILTINCERTS = "extensions.update.requireBuiltInCerts";
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AddonManagerPrivate",
"resource://gre/modules/AddonManager.jsm");
ChromeUtils.defineModuleGetter(this, "AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm");
ChromeUtils.defineModuleGetter(this, "Blocklist",
"resource://gre/modules/Blocklist.jsm");
ChromeUtils.defineModuleGetter(this, "CertUtils",
"resource://gre/modules/CertUtils.jsm");
ChromeUtils.defineModuleGetter(this, "ServiceRequest",
"resource://gre/modules/ServiceRequest.jsm");
ChromeUtils.defineModuleGetter(
this,
"AddonManager",
"resource://gre/modules/AddonManager.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AddonManagerPrivate",
"resource://gre/modules/AddonManager.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"AddonRepository",
"resource://gre/modules/addons/AddonRepository.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"Blocklist",
"resource://gre/modules/Blocklist.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"CertUtils",
"resource://gre/modules/CertUtils.jsm"
);
ChromeUtils.defineModuleGetter(
this,
"ServiceRequest",
"resource://gre/modules/ServiceRequest.jsm"
);
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Log } = ChromeUtils.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);
const updateTypeHistogram = Services.telemetry.getHistogramById("EXTENSION_UPDATE_TYPE");
const updateTypeHistogram = Services.telemetry.getHistogramById(
"EXTENSION_UPDATE_TYPE"
);
/**
* Sanitizes the update URL in an update item, as returned by
@ -64,18 +84,25 @@ function sanitizeUpdateURL(aUpdate, aRequest, aHashPattern, aHashString) {
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);
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}).`);
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;
}
@ -96,34 +123,45 @@ function sanitizeUpdateURL(aUpdate, aRequest, aHashPattern, aHashString) {
*/
function parseJSONManifest(aId, aRequest, aManifestData) {
let TYPE_CHECK = {
"array": val => Array.isArray(val),
"object": val => val && typeof val == "object" && !Array.isArray(val),
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))
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})`);
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.`);
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");
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");
@ -148,13 +186,16 @@ function parseJSONManifest(aId, aRequest, aManifestData) {
logger.debug(`Found an update entry for ${aId} version ${version}`);
let applications = getProperty(update, "applications", "object",
{ gecko: {} });
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}");
logger.debug(
"gecko not in application entry, skipping update of ${addon}"
);
continue;
}
@ -162,8 +203,12 @@ function parseJSONManifest(aId, aRequest, aManifestData) {
let appEntry = {
id: TOOLKIT_ID,
minVersion: getProperty(app, "strict_min_version", "string",
AddonManagerPrivate.webExtensionsMinPlatformVersion),
minVersion: getProperty(
app,
"strict_min_version",
"string",
AddonManagerPrivate.webExtensionsMinPlatformVersion
),
maxVersion: "*",
};
@ -179,8 +224,11 @@ function parseJSONManifest(aId, aRequest, aManifestData) {
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");
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");
@ -196,8 +244,9 @@ function parseJSONManifest(aId, aRequest, aManifestData) {
// Note: This currently only has any effect on legacy extensions (mainly
// those used in tests), since WebExtensions cannot yet specify app-specific
// compatibility ranges.
result.targetApplications.push(Object.assign({}, appEntry,
{id: Services.appinfo.ID}));
result.targetApplications.push(
Object.assign({}, appEntry, { id: Services.appinfo.ID })
);
// The JSON update protocol requires an SHA-2 hash. RDF still
// supports SHA-1, for compatibility reasons.
@ -224,13 +273,18 @@ function UpdateParser(aId, aUrl, aObserver) {
this.observer = aObserver;
this.url = aUrl;
let requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, true);
let requireBuiltIn = Services.prefs.getBoolPref(
PREF_UPDATE_REQUIREBUILTINCERTS,
true
);
logger.debug("Requesting " + aUrl);
try {
this.request = new ServiceRequest({mozAnon: true});
this.request = new ServiceRequest({ mozAnon: true });
this.request.open("GET", this.url, true);
this.request.channel.notificationCallbacks = new CertUtils.BadCertHandler(!requireBuiltIn);
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;
@ -259,7 +313,10 @@ UpdateParser.prototype = {
this.request = null;
this._doneAt = new Error("place holder");
let requireBuiltIn = Services.prefs.getBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, true);
let requireBuiltIn = Services.prefs.getBoolPref(
PREF_UPDATE_REQUIREBUILTINCERTS,
true
);
try {
CertUtils.checkCert(request.channel, !requireBuiltIn);
@ -277,8 +334,14 @@ UpdateParser.prototype = {
let channel = request.channel;
if (channel instanceof Ci.nsIHttpChannel && !channel.requestSucceeded) {
logger.warn("Request failed: " + this.url + " - " + channel.responseStatus +
": " + channel.responseStatusText);
logger.warn(
"Request failed: " +
this.url +
" - " +
channel.responseStatus +
": " +
channel.responseStatusText
);
this.notifyError(AddonManager.ERROR_DOWNLOAD_ERROR);
return;
}
@ -301,7 +364,10 @@ UpdateParser.prototype = {
logger.warn("onUpdateCheckComplete notification failed", e);
}
} else {
logger.warn("onUpdateCheckComplete may not properly cancel", new Error("stack marker"));
logger.warn(
"onUpdateCheckComplete may not properly cancel",
new Error("stack marker")
);
}
},
@ -324,9 +390,14 @@ UpdateParser.prototype = {
} 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);
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");
@ -386,30 +457,44 @@ UpdateParser.prototype = {
* 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) {
function matchesVersions(
aUpdate,
aAppVersion,
aPlatformVersion,
aIgnoreMaxVersion,
aIgnoreStrictCompat,
aCompatOverrides
) {
if (aCompatOverrides) {
let override = AddonRepository.findMatchingCompatOverride(aUpdate.version,
aCompatOverrides,
aAppVersion,
aPlatformVersion);
if (override && override.type == "incompatible")
let override = AddonRepository.findMatchingCompatOverride(
aUpdate.version,
aCompatOverrides,
aAppVersion,
aPlatformVersion
);
if (override && override.type == "incompatible") {
return false;
}
}
if (aUpdate.strictCompatibility && !aIgnoreStrictCompat)
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));
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));
result =
Services.vc.compare(aPlatformVersion, app.minVersion) >= 0 &&
(aIgnoreMaxVersion ||
Services.vc.compare(aPlatformVersion, app.maxVersion) <= 0);
}
}
return result;
@ -437,24 +522,40 @@ var AddonUpdateChecker = {
* Ignore strictCompatibility when testing if an update matches. Optional.
* @return an update object if one matches or null if not
*/
getCompatibilityUpdate(aUpdates, aVersion, aIgnoreCompatibility,
aAppVersion, aPlatformVersion,
aIgnoreMaxVersion, aIgnoreStrictCompat) {
if (!aAppVersion)
getCompatibilityUpdate(
aUpdates,
aVersion,
aIgnoreCompatibility,
aAppVersion,
aPlatformVersion,
aIgnoreMaxVersion,
aIgnoreStrictCompat
) {
if (!aAppVersion) {
aAppVersion = Services.appinfo.version;
if (!aPlatformVersion)
}
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)
if (id == Services.appinfo.ID || id == TOOLKIT_ID) {
return update;
}
}
} else if (matchesVersions(update, aAppVersion, aPlatformVersion,
aIgnoreMaxVersion, aIgnoreStrictCompat)) {
} else if (
matchesVersions(
update,
aAppVersion,
aPlatformVersion,
aIgnoreMaxVersion,
aIgnoreStrictCompat
)
) {
return update;
}
}
@ -479,25 +580,46 @@ var AddonUpdateChecker = {
* Array of AddonCompatibilityOverride to take into account. Optional.
* @return an update object if one matches or null if not
*/
async getNewestCompatibleUpdate(aUpdates, aAppVersion, aPlatformVersion,
aIgnoreMaxVersion, aIgnoreStrictCompat,
aCompatOverrides) {
if (!aAppVersion)
async getNewestCompatibleUpdate(
aUpdates,
aAppVersion,
aPlatformVersion,
aIgnoreMaxVersion,
aIgnoreStrictCompat,
aCompatOverrides
) {
if (!aAppVersion) {
aAppVersion = Services.appinfo.version;
if (!aPlatformVersion)
}
if (!aPlatformVersion) {
aPlatformVersion = Services.appinfo.platformVersion;
}
let newest = null;
for (let update of aUpdates) {
if (!update.updateURL)
if (!update.updateURL) {
continue;
let state = await Blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
}
let state = await 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)) {
}
if (
(newest == null ||
Services.vc.compare(newest.version, update.version) < 0) &&
matchesVersions(
update,
aAppVersion,
aPlatformVersion,
aIgnoreMaxVersion,
aIgnoreStrictCompat,
aCompatOverrides
)
) {
newest = update;
}
}

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

@ -7,24 +7,25 @@
"use strict";
(function() {
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const MSG_JAR_FLUSH = "AddonJarFlush";
const MSG_MESSAGE_MANAGER_CACHES_FLUSH = "AddonMessageManagerCachesFlush";
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) {
Services.obs.notifyObservers(null, "flush-cache-entry", message.data);
});
// Propagate message manager caches flush notifications across processes.
addMessageListener(MSG_MESSAGE_MANAGER_CACHES_FLUSH, function() {
Services.obs.notifyObservers(null, "message-manager-flush-caches");
});
try {
if (
Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT
) {
// Propagate JAR cache flush notifications across process boundaries.
addMessageListener(MSG_JAR_FLUSH, function(message) {
Services.obs.notifyObservers(null, "flush-cache-entry", message.data);
});
// Propagate message manager caches flush notifications across processes.
addMessageListener(MSG_MESSAGE_MANAGER_CACHES_FLUSH, function() {
Services.obs.notifyObservers(null, "message-manager-flush-caches");
});
}
} catch (e) {
Cu.reportError(e);
}
} catch (e) {
Cu.reportError(e);
}
})();

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

@ -6,65 +6,85 @@
var EXPORTED_SYMBOLS = [];
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const {AddonManager, AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { AddonManager, AddonManagerPrivate } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
/* globals AddonManagerPrivate*/
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
/* globals OS*/
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
// These symbols are, unfortunately, accessed via the module global from
// tests, and therefore cannot be lexical definitions.
var {GMPPrefs, GMPUtils, OPEN_H264_ID, WIDEVINE_ID} = ChromeUtils.import("resource://gre/modules/GMPUtils.jsm");
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
var { GMPPrefs, GMPUtils, OPEN_H264_ID, WIDEVINE_ID } = ChromeUtils.import(
"resource://gre/modules/GMPUtils.jsm"
);
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
ChromeUtils.defineModuleGetter(
this, "GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm");
this,
"GMPInstallManager",
"resource://gre/modules/GMPInstallManager.jsm"
);
ChromeUtils.defineModuleGetter(
this, "setTimeout", "resource://gre/modules/Timer.jsm");
this,
"setTimeout",
"resource://gre/modules/Timer.jsm"
);
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
const URI_EXTENSION_STRINGS =
"chrome://mozapps/locale/extensions/extensions.properties";
const SEC_IN_A_DAY = 24 * 60 * 60;
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 GMP_CHECK_DELAY = 10 * 1000; // milliseconds
const XHTML = "http://www.w3.org/1999/xhtml";
const NS_GRE_DIR = "GreD";
const CLEARKEY_PLUGIN_ID = "gmp-clearkey";
const CLEARKEY_VERSION = "0.1";
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_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",
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: "https://www.openh264.org/",
licenseURL: "chrome://mozapps/content/extensions/OpenH264-license.txt",
homepageURL: "https://www.openh264.org/",
},
{
id: WIDEVINE_ID,
name: "widevine_description",
id: WIDEVINE_ID,
name: "widevine_description",
// Describe the purpose of both CDMs in the same way.
description: "cdm_description2",
licenseURL: "https://www.google.com/policies/privacy/",
homepageURL: "https://www.widevine.com/",
isEME: true,
}];
description: "cdm_description2",
licenseURL: "https://www.google.com/policies/privacy/",
homepageURL: "https://www.widevine.com/",
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));
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 gLogger;
var gLogAppenderDump = null;
@ -88,8 +108,6 @@ function configureLogging() {
}
}
/**
* The GMPWrapper provides the info for the various GMP plugins to public
* callers through the API.
@ -97,16 +115,20 @@ function configureLogging() {
function GMPWrapper(aPluginInfo, aRawPluginInfo) {
this._plugin = aPluginInfo;
this._rawPlugin = aRawPluginInfo;
this._log =
Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
"GMPWrapper(" +
this._plugin.id + ") ");
Services.prefs.addObserver(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
this._plugin.id),
this, true);
Services.prefs.addObserver(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
this._plugin.id),
this, true);
this._log = Log.repository.getLoggerWithMessagePrefix(
"Toolkit.GMP",
"GMPWrapper(" + this._plugin.id + ") "
);
Services.prefs.addObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id),
this,
true
);
Services.prefs.addObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id),
this,
true
);
if (this._plugin.isEME) {
Services.prefs.addObserver(GMPPrefs.KEY_EME_ENABLED, this, true);
Services.mm.addMessageListener("EMEVideo:ContentMediaKeysRequest", this);
@ -114,41 +136,67 @@ function GMPWrapper(aPluginInfo, aRawPluginInfo) {
}
GMPWrapper.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
QueryInterface: ChromeUtils.generateQI([
Ci.nsIObserver,
Ci.nsISupportsWeakReference,
]),
// An active task that checks for plugin updates and installs them.
_updateTask: null,
_gmpPath: null,
_isUpdateCheckPending: false,
set gmpPath(aPath) { this._gmpPath = aPath; },
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.getString(GMPPrefs.KEY_PLUGIN_VERSION,
null, this._plugin.id));
this._gmpPath = OS.Path.join(
OS.Constants.Path.profileDir,
this._plugin.id,
GMPPrefs.getString(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 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 null; },
get description() {
return this._plugin.description;
},
get fullDescription() {
return null;
},
getFullDescription(doc) {
let plugin = this._rawPlugin;
let frag = doc.createDocumentFragment();
for (let [urlProp, labelId] of [["learnMoreURL", GMP_LEARN_MORE],
["licenseURL", this.id == WIDEVINE_ID ?
GMP_PRIVACY_INFO : GMP_LICENSE_INFO]]) {
for (let [urlProp, labelId] of [
["learnMoreURL", GMP_LEARN_MORE],
[
"licenseURL",
this.id == WIDEVINE_ID ? GMP_PRIVACY_INFO : GMP_LICENSE_INFO,
],
]) {
if (plugin[urlProp]) {
let a = doc.createElementNS(XHTML, "a");
a.href = plugin[urlProp];
@ -156,8 +204,10 @@ GMPWrapper.prototype = {
a.textContent = pluginsBundle.GetStringFromName(labelId);
if (frag.childElementCount) {
frag.append(doc.createElementNS(XHTML, "br"),
doc.createElementNS(XHTML, "br"));
frag.append(
doc.createElementNS(XHTML, "br"),
doc.createElementNS(XHTML, "br")
);
}
frag.append(a);
}
@ -167,16 +217,25 @@ GMPWrapper.prototype = {
},
get version() {
return GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id);
return GMPPrefs.getString(
GMPPrefs.KEY_PLUGIN_VERSION,
null,
this._plugin.id
);
},
get isActive() {
return !this.appDisabled &&
!this.userDisabled &&
!GMPUtils.isPluginHidden(this._plugin);
return (
!this.appDisabled &&
!this.userDisabled &&
!GMPUtils.isPluginHidden(this._plugin)
);
},
get appDisabled() {
if (this._plugin.isEME && !GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)) {
if (
this._plugin.isEME &&
!GMPPrefs.getBool(GMPPrefs.KEY_EME_ENABLED, true)
) {
// If "media.eme.enabled" is false, all EME plugins are disabled.
return true;
}
@ -184,10 +243,18 @@ GMPWrapper.prototype = {
},
get userDisabled() {
return !GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_ENABLED, true, this._plugin.id);
return !GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_ENABLED,
true,
this._plugin.id
);
},
set userDisabled(aVal) {
GMPPrefs.setBool(GMPPrefs.KEY_PLUGIN_ENABLED, aVal === false, this._plugin.id);
GMPPrefs.setBool(
GMPPrefs.KEY_PLUGIN_ENABLED,
aVal === false,
this._plugin.id
);
},
async enable() {
@ -197,26 +264,38 @@ GMPWrapper.prototype = {
this.userDisabled = true;
},
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 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 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;
permissions |= this.userDisabled
? AddonManager.PERM_CAN_ENABLE
: AddonManager.PERM_CAN_DISABLE;
}
return permissions;
},
get updateDate() {
let time = Number(GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 0,
this._plugin.id));
let time = Number(
GMPPrefs.getInt(GMPPrefs.KEY_PLUGIN_LAST_UPDATE, 0, this._plugin.id)
);
if (this.isInstalled) {
return new Date(time * 1000);
}
@ -240,7 +319,7 @@ GMPWrapper.prototype = {
},
get installTelemetryInfo() {
return {source: "gmp-plugin"};
return { source: "gmp-plugin" };
},
isCompatibleWith(aAppVersion, aPlatformVersion) {
@ -252,8 +331,13 @@ GMPWrapper.prototype = {
return AddonManager.AUTOUPDATE_DEFAULT;
}
return GMPPrefs.getBool(GMPPrefs.KEY_PLUGIN_AUTOUPDATE, true, this._plugin.id) ?
AddonManager.AUTOUPDATE_ENABLE : AddonManager.AUTOUPDATE_DISABLE;
return GMPPrefs.getBool(
GMPPrefs.KEY_PLUGIN_AUTOUPDATE,
true,
this._plugin.id
)
? AddonManager.AUTOUPDATE_ENABLE
: AddonManager.AUTOUPDATE_DISABLE;
},
set applyBackgroundUpdates(aVal) {
@ -267,34 +351,44 @@ GMPWrapper.prototype = {
},
findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
this._log.trace("findUpdates() - " + this._plugin.id + " - reason=" +
aReason);
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");
this._log.trace(
"findUpdates() - " + this._plugin.id + " - no autoupdate"
);
return Promise.resolve(false);
}
let secSinceLastCheck =
Date.now() / 1000 - Services.prefs.getIntPref(GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
Date.now() / 1000 -
Services.prefs.getIntPref(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");
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");
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");
this._log.trace(
"findUpdates() - " + this._plugin.id + " - update task already running"
);
return this._updateTask;
}
@ -305,17 +399,23 @@ GMPWrapper.prototype = {
let res = await 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");
this._log.trace(
"findUpdates() - found update for " +
this._plugin.id +
", installing"
);
await installManager.installAddon(update);
} else {
this._log.trace("findUpdates() - no updates for " + this._plugin.id);
}
this._log.info("findUpdates() - updateTask succeeded 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);
this._log.error(
"findUpdates() - updateTask for " + this._plugin.id + " threw",
e
);
throw e;
} finally {
this._updateTask = null;
@ -326,7 +426,9 @@ GMPWrapper.prototype = {
return this._updateTask;
},
get pluginMimeTypes() { return []; },
get pluginMimeTypes() {
return [];
},
get pluginLibraries() {
if (this.isInstalled) {
let path = this.version;
@ -336,9 +438,11 @@ GMPWrapper.prototype = {
},
get pluginFullpath() {
if (this.isInstalled) {
let path = OS.Path.join(OS.Constants.Path.profileDir,
this._plugin.id,
this.version);
let path = OS.Path.join(
OS.Constants.Path.profileDir,
this._plugin.id,
this.version
);
return [path];
}
return [];
@ -349,42 +453,64 @@ GMPWrapper.prototype = {
},
_handleEnabledChanged() {
this._log.info("_handleEnabledChanged() id=" +
this._plugin.id + " isActive=" + this.isActive);
this._log.info(
"_handleEnabledChanged() id=" +
this._plugin.id +
" isActive=" +
this.isActive
);
AddonManagerPrivate.callAddonListeners(this.isActive ?
"onEnabling" : "onDisabling",
this, false);
AddonManagerPrivate.callAddonListeners(
this.isActive ? "onEnabling" : "onDisabling",
this,
false
);
if (this._gmpPath) {
if (this.isActive) {
this._log.info("onPrefEnabledChanged() - adding gmp directory " +
this._gmpPath);
this._log.info(
"onPrefEnabledChanged() - adding gmp directory " + this._gmpPath
);
gmpService.addPluginDirectory(this._gmpPath);
} else {
this._log.info("onPrefEnabledChanged() - removing gmp directory " +
this._gmpPath);
this._log.info(
"onPrefEnabledChanged() - removing gmp directory " + this._gmpPath
);
gmpService.removePluginDirectory(this._gmpPath);
}
}
AddonManagerPrivate.callAddonListeners(this.isActive ?
"onEnabled" : "onDisabled",
this);
AddonManagerPrivate.callAddonListeners(
this.isActive ? "onEnabled" : "onDisabled",
this
);
},
onPrefEMEGlobalEnabledChanged() {
this._log.info("onPrefEMEGlobalEnabledChanged() id=" + this._plugin.id +
" appDisabled=" + this.appDisabled + " isActive=" + this.isActive +
" hidden=" + GMPUtils.isPluginHidden(this._plugin));
this._log.info(
"onPrefEMEGlobalEnabledChanged() id=" +
this._plugin.id +
" appDisabled=" +
this.appDisabled +
" isActive=" +
this.isActive +
" hidden=" +
GMPUtils.isPluginHidden(this._plugin)
);
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this,
["appDisabled"]);
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.callInstallListeners(
"onExternalInstall",
null,
this,
null,
false
);
AddonManagerPrivate.callAddonListeners("onInstalling", this, false);
AddonManagerPrivate.callAddonListeners("onInstalled", this);
this.checkForUpdates(GMP_CHECK_DELAY);
@ -413,7 +539,7 @@ GMPWrapper.prototype = {
}, delay);
},
receiveMessage({target: browser, data: data}) {
receiveMessage({ target: browser, data: data }) {
this._log.trace("receiveMessage() data=" + data);
let parsedData;
try {
@ -422,7 +548,7 @@ GMPWrapper.prototype = {
this._log.error("Malformed EME video message with data: " + data);
return;
}
let {status} = parsedData;
let { status } = parsedData;
if (status == "cdm-not-installed") {
this.checkForUpdates(0);
}
@ -437,25 +563,36 @@ GMPWrapper.prototype = {
onPrefVersionChanged() {
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this._gmpPath) {
this._log.info("onPrefVersionChanged() - unregistering gmp directory " +
this._gmpPath);
gmpService.removeAndDeletePluginDirectory(this._gmpPath, true /* can defer */);
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.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.getString(GMPPrefs.KEY_PLUGIN_VERSION,
null, this._plugin.id));
this._gmpPath = OS.Path.join(
OS.Constants.Path.profileDir,
this._plugin.id,
GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_VERSION, null, this._plugin.id)
);
}
if (this._gmpPath && this.isActive) {
this._log.info("onPrefVersionChanged() - registering gmp directory " +
this._gmpPath);
this._log.info(
"onPrefVersionChanged() - registering gmp directory " + this._gmpPath
);
gmpService.addPluginDirectory(this._gmpPath);
}
AddonManagerPrivate.callAddonListeners("onInstalled", this);
@ -463,9 +600,15 @@ GMPWrapper.prototype = {
observe(subject, topic, pref) {
if (topic == "nsPref:changed") {
if (pref == GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id)) {
if (
pref ==
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id)
) {
this.onPrefEnabledChanged();
} else if (pref == GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id)) {
} else if (
pref ==
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id)
) {
this.onPrefVersionChanged();
} else if (pref == GMPPrefs.KEY_EME_ENABLED) {
this.onPrefEMEGlobalEnabledChanged();
@ -476,8 +619,9 @@ GMPWrapper.prototype = {
uninstallPlugin() {
AddonManagerPrivate.callAddonListeners("onUninstalling", this, false);
if (this.gmpPath) {
this._log.info("uninstallPlugin() - unregistering gmp directory " +
this.gmpPath);
this._log.info(
"uninstallPlugin() - unregistering gmp directory " + this.gmpPath
);
gmpService.removeAndDeletePluginDirectory(this.gmpPath);
}
GMPPrefs.reset(GMPPrefs.KEY_PLUGIN_VERSION, this.id);
@ -487,13 +631,20 @@ GMPWrapper.prototype = {
},
shutdown() {
Services.prefs.removeObserver(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED,
this._plugin.id), this);
Services.prefs.removeObserver(GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION,
this._plugin.id), this);
Services.prefs.removeObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_ENABLED, this._plugin.id),
this
);
Services.prefs.removeObserver(
GMPPrefs.getPrefKey(GMPPrefs.KEY_PLUGIN_VERSION, this._plugin.id),
this
);
if (this._plugin.isEME) {
Services.prefs.removeObserver(GMPPrefs.KEY_EME_ENABLED, this);
Services.mm.removeMessageListener("EMEVideo:ContentMediaKeysRequest", this);
Services.mm.removeMessageListener(
"EMEVideo:ContentMediaKeysRequest",
this
);
}
return this._updateTask;
},
@ -515,8 +666,9 @@ GMPWrapper.prototype = {
infoName = id + ".info";
}
return fileExists(this.gmpPath, libName) &&
fileExists(this.gmpPath, infoName);
return (
fileExists(this.gmpPath, libName) && fileExists(this.gmpPath, infoName)
);
},
validate() {
@ -529,8 +681,11 @@ GMPWrapper.prototype = {
}
let expectedABI = GMPUtils._expectedABI(this._plugin);
let abi = GMPPrefs.getString(GMPPrefs.KEY_PLUGIN_ABI, expectedABI,
this._plugin.id);
let abi = GMPPrefs.getString(
GMPPrefs.KEY_PLUGIN_ABI,
expectedABI,
this._plugin.id
);
if (abi != expectedABI) {
// ABI doesn't match. Possibly this is a profile migrated across platforms
// or from 32 -> 64 bit.
@ -551,14 +706,18 @@ GMPWrapper.prototype = {
};
var GMPProvider = {
get name() { return "GMPProvider"; },
get name() {
return "GMPProvider";
},
_plugins: null,
startup() {
configureLogging();
this._log = Log.repository.getLoggerWithMessagePrefix("Toolkit.GMP",
"GMPProvider.");
this._log = Log.repository.getLoggerWithMessagePrefix(
"Toolkit.GMP",
"GMPProvider."
);
this.buildPluginList();
this.ensureProperCDMInstallState();
@ -568,20 +727,23 @@ var GMPProvider = {
let wrapper = plugin.wrapper;
let gmpPath = wrapper.gmpPath;
let isEnabled = wrapper.isActive;
this._log.trace("startup - enabled=" + isEnabled + ", gmpPath=" +
gmpPath);
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");
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");
this._log.info(
"startup - gmp " + plugin.id + " invalid, uninstalling"
);
wrapper.uninstallPlugin();
continue;
}
@ -589,26 +751,31 @@ var GMPProvider = {
try {
gmpService.addPluginDirectory(gmpPath);
} catch (e) {
if (e.name != "NS_ERROR_NOT_AVAILABLE")
if (e.name != "NS_ERROR_NOT_AVAILABLE") {
throw e;
this._log.warn("startup - adding gmp directory failed with " +
e.name + " - sandboxing not available?", 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.nsIFile);
let greDir = Services.dirsvc.get(NS_GRE_DIR, Ci.nsIFile);
let path = greDir.path;
if (GMPUtils._isWindowsOnARM64()) {
path = OS.Path.join(path, "i686");
}
let clearkeyPath = OS.Path.join(path,
CLEARKEY_PLUGIN_ID,
CLEARKEY_VERSION);
this._log.info("startup - adding clearkey CDM directory " +
clearkeyPath);
let clearkeyPath = OS.Path.join(
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);
@ -654,8 +821,7 @@ var GMPProvider = {
},
async getAddonsByTypes(aTypes) {
if (!this.isEnabled ||
(aTypes && !aTypes.includes("plugin"))) {
if (!this.isEnabled || (aTypes && !aTypes.includes("plugin"))) {
return [];
}
@ -700,8 +866,12 @@ var GMPProvider = {
};
AddonManagerPrivate.registerProvider(GMPProvider, [
new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
"type.plugin.name",
AddonManager.VIEW_TYPE_LIST, 6000,
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE),
new AddonManagerPrivate.AddonType(
"plugin",
URI_EXTENSION_STRINGS,
"type.plugin.name",
AddonManager.VIEW_TYPE_LIST,
6000,
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE
),
]);

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

@ -8,17 +8,23 @@
var EXPORTED_SYMBOLS = [];
const {AddonManager, AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
const { AddonManager, AddonManagerPrivate } = ChromeUtils.import(
"resource://gre/modules/AddonManager.jsm"
);
/* globals AddonManagerPrivate*/
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
ChromeUtils.defineModuleGetter(this, "Blocklist",
"resource://gre/modules/Blocklist.jsm");
ChromeUtils.defineModuleGetter(
this,
"Blocklist",
"resource://gre/modules/Blocklist.jsm"
);
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
const LIST_UPDATED_TOPIC = "plugins-list-updated";
const URI_EXTENSION_STRINGS =
"chrome://mozapps/locale/extensions/extensions.properties";
const LIST_UPDATED_TOPIC = "plugins-list-updated";
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
const LOGGER_ID = "addons.plugins";
// Create a new logger for use by the Addons Plugin Provider
@ -48,10 +54,11 @@ var PluginProvider = {
async observe(aSubject, aTopic, aData) {
switch (aTopic) {
case LIST_UPDATED_TOPIC:
if (this.plugins)
this.updatePluginList();
break;
case LIST_UPDATED_TOPIC:
if (this.plugins) {
this.updatePluginList();
}
break;
}
},
@ -59,10 +66,12 @@ var PluginProvider = {
* Creates a PluginWrapper for a plugin object.
*/
buildWrapper(aPlugin) {
return new PluginWrapper(aPlugin.id,
aPlugin.name,
aPlugin.description,
aPlugin.tags);
return new PluginWrapper(
aPlugin.id,
aPlugin.name,
aPlugin.description,
aPlugin.tags
);
},
/**
@ -72,11 +81,13 @@ var PluginProvider = {
* The ID of the add-on to retrieve
*/
async getAddonByID(aId) {
if (!this.plugins)
if (!this.plugins) {
this.buildPluginList();
}
if (aId in this.plugins)
if (aId in this.plugins) {
return this.buildWrapper(this.plugins[aId]);
}
return null;
},
@ -91,11 +102,13 @@ var PluginProvider = {
return [];
}
if (!this.plugins)
if (!this.plugins) {
this.buildPluginList();
}
return Promise.all(Object.keys(this.plugins).map(
id => this.getAddonByID(id)));
return Promise.all(
Object.keys(this.plugins).map(id => this.getAddonByID(id))
);
},
/**
@ -114,15 +127,16 @@ var PluginProvider = {
* @return a dictionary of plugins indexed by our generated ID
*/
getPluginList() {
let tags = Cc["@mozilla.org/plugin/host;1"].
getService(Ci.nsIPluginHost).
getPluginTags();
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))
if (!(tag.name in seenPlugins)) {
seenPlugins[tag.name] = {};
}
if (!(tag.description in seenPlugins[tag.name])) {
let plugin = {
id: tag.name + tag.description,
@ -156,10 +170,12 @@ var PluginProvider = {
updatePluginList() {
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 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
@ -172,40 +188,51 @@ var PluginProvider = {
let newWrapper = this.buildWrapper(newList[id]);
if (newWrapper.isActive != oldWrapper.isActive) {
AddonManagerPrivate.callAddonListeners(newWrapper.isActive ?
"onEnabling" : "onDisabling",
newWrapper, false);
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.callInstallListeners(
"onExternalInstall",
null,
plugin,
null,
false
);
AddonManagerPrivate.callAddonListeners("onInstalling", plugin, false);
}
// Notify for any plugins that have vanished.
for (let plugin of lostPlugins)
for (let plugin of lostPlugins) {
AddonManagerPrivate.callAddonListeners("onUninstalling", plugin, false);
}
this.plugins = newList;
// Signal that new installs are complete
for (let plugin of newPlugins)
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);
AddonManagerPrivate.callAddonListeners(
wrapper.isActive ? "onEnabled" : "onDisabled",
wrapper
);
}
// Signal that uninstalls are complete
for (let plugin of lostPlugins)
for (let plugin of lostPlugins) {
AddonManagerPrivate.callAddonListeners("onUninstalled", plugin);
}
},
};
@ -242,35 +269,48 @@ PluginWrapper.prototype = {
},
get version() {
let { tags: [tag] } = pluginFor(this);
let {
tags: [tag],
} = pluginFor(this);
return tag.version;
},
get homepageURL() {
let { description } = pluginFor(this);
if (/<A\s+HREF=[^>]*>/i.test(description))
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);
let {
tags: [tag],
} = pluginFor(this);
return !tag.blocklisted && !tag.disabled;
},
get appDisabled() {
let { tags: [tag] } = pluginFor(this);
let {
tags: [tag],
} = pluginFor(this);
return tag.blocklisted;
},
get userDisabled() {
let { tags: [tag] } = pluginFor(this);
if (tag.disabled)
let {
tags: [tag],
} = pluginFor(this);
if (tag.disabled) {
return true;
}
if (tag.clicktoplay ||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
if (
tag.clicktoplay ||
this.blocklistState ==
Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE
) {
return AddonManager.STATE_ASK_TO_ACTIVATE;
}
@ -283,18 +323,20 @@ PluginWrapper.prototype = {
val = AddonManager.STATE_ASK_TO_ACTIVATE;
}
if (val === previousVal)
if (val === previousVal) {
return val;
}
let { tags } = pluginFor(this);
for (let tag of tags) {
if (val === true)
if (val === true) {
tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
else if (val === false)
} else if (val === false) {
tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED;
else if (val == AddonManager.STATE_ASK_TO_ACTIVATE)
} 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
@ -313,9 +355,13 @@ PluginWrapper.prototype = {
// 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"]);
if (
previousVal == AddonManager.STATE_ASK_TO_ACTIVATE ||
val == AddonManager.STATE_ASK_TO_ACTIVATE
) {
AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, [
"userDisabled",
]);
}
return val;
@ -329,26 +375,32 @@ PluginWrapper.prototype = {
},
get blocklistState() {
let { tags: [tag] } = pluginFor(this);
let {
tags: [tag],
} = pluginFor(this);
return tag.blocklistState;
},
async getBlocklistURL() {
let { tags: [tag] } = pluginFor(this);
let {
tags: [tag],
} = pluginFor(this);
return Blocklist.getPluginBlockURL(tag);
},
get pluginLibraries() {
let libs = [];
for (let tag of pluginFor(this).tags)
for (let tag of pluginFor(this).tags) {
libs.push(tag.filename);
}
return libs;
},
get pluginFullpath() {
let paths = [];
for (let tag of pluginFor(this).tags)
for (let tag of pluginFor(this).tags) {
paths.push(tag.fullpath);
}
return paths;
},
@ -379,27 +431,33 @@ PluginWrapper.prototype = {
},
get scope() {
let { tags: [tag] } = pluginFor(this);
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))
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))
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))
if (path.startsWith(dir.path)) {
return AddonManager.SCOPE_USER;
}
} catch (e) {
if (!e.result || e.result != Cr.NS_ERROR_FAILURE)
if (!e.result || e.result != Cr.NS_ERROR_FAILURE) {
throw e;
}
// Do nothing: missing "Home".
}
@ -416,14 +474,17 @@ PluginWrapper.prototype = {
},
get permissions() {
let { tags: [tag] } = pluginFor(this);
let {
tags: [tag],
} = pluginFor(this);
let permissions = 0;
if (tag.isEnabledStateLocked) {
return permissions;
}
if (!this.appDisabled) {
if (this.userDisabled !== true)
if (this.userDisabled !== true) {
permissions |= AddonManager.PERM_CAN_DISABLE;
}
if (this.userDisabled !== AddonManager.STATE_ASK_TO_ACTIVATE) {
permissions |= AddonManager.PERM_CAN_ASK_TO_ACTIVATE;
@ -431,9 +492,14 @@ PluginWrapper.prototype = {
let blocklistState = this.blocklistState;
let isCTPBlocklisted =
(blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE ||
blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
if (this.userDisabled !== false && !isCTPBlocklisted && !this.isFlashPlugin) {
blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE ||
blocklistState ==
Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE;
if (
this.userDisabled !== false &&
!isCTPBlocklisted &&
!this.isFlashPlugin
) {
permissions |= AddonManager.PERM_CAN_ENABLE;
}
}
@ -445,7 +511,10 @@ PluginWrapper.prototype = {
},
get optionsURL() {
return "chrome://mozapps/content/extensions/pluginPrefs.xul#id=" + encodeURIComponent(this.id);
return (
"chrome://mozapps/content/extensions/pluginPrefs.xul#id=" +
encodeURIComponent(this.id)
);
},
get updateDate() {
@ -469,7 +538,7 @@ PluginWrapper.prototype = {
},
get installTelemetryInfo() {
return {source: "plugin"};
return { source: "plugin" };
},
isCompatibleWith(aAppVersion, aPlatformVersion) {
@ -477,12 +546,15 @@ PluginWrapper.prototype = {
},
findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
if ("onNoCompatibilityUpdateAvailable" in aListener)
if ("onNoCompatibilityUpdateAvailable" in aListener) {
aListener.onNoCompatibilityUpdateAvailable(this);
if ("onNoUpdateAvailable" in aListener)
}
if ("onNoUpdateAvailable" in aListener) {
aListener.onNoUpdateAvailable(this);
if ("onUpdateFinished" in aListener)
}
if ("onUpdateFinished" in aListener) {
aListener.onUpdateFinished(this);
}
},
get isFlashPlugin() {
@ -491,8 +563,12 @@ PluginWrapper.prototype = {
};
AddonManagerPrivate.registerProvider(PluginProvider, [
new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS,
"type.plugin.name",
AddonManager.VIEW_TYPE_LIST, 6000,
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE),
new AddonManagerPrivate.AddonType(
"plugin",
URI_EXTENSION_STRINGS,
"type.plugin.name",
AddonManager.VIEW_TYPE_LIST,
6000,
AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE
),
]);

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

@ -6,35 +6,51 @@
/* exported ProductAddonChecker */
const LOCAL_GMP_SOURCES = [{
"id": "gmp-gmpopenh264",
"src": "chrome://global/content/gmp-sources/openh264.json",
}, {
"id": "gmp-widevinecdm",
"src": "chrome://global/content/gmp-sources/widevinecdm.json",
}];
const LOCAL_GMP_SOURCES = [
{
id: "gmp-gmpopenh264",
src: "chrome://global/content/gmp-sources/openh264.json",
},
{
id: "gmp-widevinecdm",
src: "chrome://global/content/gmp-sources/widevinecdm.json",
},
];
var EXPORTED_SYMBOLS = [ "ProductAddonChecker" ];
var EXPORTED_SYMBOLS = ["ProductAddonChecker"];
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {Log} = ChromeUtils.import("resource://gre/modules/Log.jsm");
const {CertUtils} = ChromeUtils.import("resource://gre/modules/CertUtils.jsm");
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
const { CertUtils } = ChromeUtils.import(
"resource://gre/modules/CertUtils.jsm"
);
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
/* globals GMPPrefs */
ChromeUtils.defineModuleGetter(this, "GMPPrefs",
"resource://gre/modules/GMPUtils.jsm");
ChromeUtils.defineModuleGetter(
this,
"GMPPrefs",
"resource://gre/modules/GMPUtils.jsm"
);
/* globals OS */
ChromeUtils.defineModuleGetter(this, "UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm");
ChromeUtils.defineModuleGetter(
this,
"UpdateUtils",
"resource://gre/modules/UpdateUtils.jsm"
);
ChromeUtils.defineModuleGetter(this, "ServiceRequest",
"resource://gre/modules/ServiceRequest.jsm");
ChromeUtils.defineModuleGetter(
this,
"ServiceRequest",
"resource://gre/modules/ServiceRequest.jsm"
);
// This exists so that tests can override the XHR behaviour for downloading
// the addon update XML file.
@ -68,8 +84,7 @@ function getRequestStatus(request) {
let status = null;
try {
status = request.status;
} catch (e) {
}
} catch (e) {}
if (status != null) {
return status;
@ -100,7 +115,9 @@ function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
request = request.wrappedJSObject;
}
request.open("GET", url, true);
request.channel.notificationCallbacks = new CertUtils.BadCertHandler(allowNonBuiltIn);
request.channel.notificationCallbacks = new CertUtils.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.
@ -110,7 +127,9 @@ function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
// 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.channel.QueryInterface(
Ci.nsIHttpChannelInternal
).beConservative = true;
}
request.timeout = TIMEOUT_DELAY_MS;
@ -123,17 +142,18 @@ function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
// might only implement Pragma: no-cache
request.setRequestHeader("Pragma", "no-cache");
let fail = (event) => {
let fail = event => {
let request = event.target;
let status = getRequestStatus(request);
let message = "Failed downloading XML, status: " + status + ", reason: " + event.type;
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) => {
let success = event => {
logger.info("Completed downloading document");
let request = event.target;
@ -162,7 +182,7 @@ function downloadXML(url, allowNonBuiltIn = false, allowedCerts = null) {
function downloadJSON(uri) {
logger.info("fetching config from: " + uri);
return new Promise((resolve, reject) => {
let xmlHttp = new ServiceRequest({mozAnon: true});
let xmlHttp = new ServiceRequest({ mozAnon: true });
xmlHttp.onload = function(aResponse) {
resolve(JSON.parse(this.responseText));
@ -178,7 +198,6 @@ function downloadJSON(uri) {
});
}
/**
* Parses a list of add-ons from a DOM document.
*
@ -191,8 +210,11 @@ function downloadJSON(uri) {
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");
throw new Error(
"got node name: " +
document.documentElement.localName +
", expected: updates"
);
}
// Check if there are any addons elements in the updates element
@ -206,7 +228,14 @@ function parseXML(document) {
for (let addonElement of addonList) {
let addon = {};
for (let name of ["id", "URL", "hashFunction", "hashValue", "version", "size"]) {
for (let name of [
"id",
"URL",
"hashFunction",
"hashValue",
"version",
"size",
]) {
if (addonElement.hasAttribute(name)) {
addon[name] = addonElement.getAttribute(name);
}
@ -229,42 +258,44 @@ function parseXML(document) {
function downloadLocalConfig() {
if (!GMPPrefs.getBool(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
return Promise.resolve({usedFallback: true, gmpAddons: []});
return Promise.resolve({ usedFallback: true, gmpAddons: [] });
}
return Promise.all(LOCAL_GMP_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;
return Promise.all(
LOCAL_GMP_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;
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];
}
}
// 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 => {
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);
@ -294,7 +325,9 @@ function downloadFile(url) {
return;
}
(async function() {
let f = await OS.File.openUnique(OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon"));
let f = await OS.File.openUnique(
OS.Path.join(OS.Constants.Path.tmpDir, "tmpaddon")
);
let path = f.path;
logger.info(`Downloaded file will be saved to ${path}`);
await f.file.close();
@ -303,10 +336,14 @@ function downloadFile(url) {
})().then(resolve, reject);
};
let fail = (event) => {
let fail = event => {
let request = event.target;
let status = getRequestStatus(request);
let message = "Failed downloading via XHR, status: " + status + ", reason: " + event.type;
let message =
"Failed downloading via XHR, status: " +
status +
", reason: " +
event.type;
logger.warn(message);
let ex = new Error(message);
ex.status = status;
@ -321,7 +358,9 @@ function downloadFile(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.channel.QueryInterface(
Ci.nsIHttpChannelInternal
).beConservative = true;
}
xhr.send(null);
} catch (ex) {
@ -358,8 +397,9 @@ function binaryToHex(input) {
var computeHash = async function(hashFunction, path) {
let file = await OS.File.open(path, { existing: true, read: true });
try {
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
let hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
Ci.nsICryptoHash
);
hasher.initWithString(hashFunction);
let bytes;
@ -389,7 +429,13 @@ var verifyFile = async function(properties, path) {
if (properties.size !== undefined) {
let stat = await OS.File.stat(path);
if (stat.size != properties.size) {
throw new Error("Downloaded file was " + stat.size + " bytes but expected " + properties.size + " bytes.");
throw new Error(
"Downloaded file was " +
stat.size +
" bytes but expected " +
properties.size +
" bytes."
);
}
}
@ -397,7 +443,9 @@ var verifyFile = async function(properties, path) {
let expectedDigest = properties.hashValue.toLowerCase();
let digest = await computeHash(properties.hashFunction, path);
if (digest != expectedDigest) {
throw new Error("Hash was `" + digest + "` but expected `" + expectedDigest + "`.");
throw new Error(
"Hash was `" + digest + "` but expected `" + expectedDigest + "`."
);
}
}
};
@ -421,7 +469,7 @@ const ProductAddonChecker = {
getProductAddonList(url, allowNonBuiltIn = false, allowedCerts = null) {
if (!GMPPrefs.getBool(GMPPrefs.KEY_UPDATE_ENABLED, true)) {
logger.info("Updates are disabled via media.gmp-manager.updateEnabled");
return Promise.resolve({usedFallback: true, gmpAddons: []});
return Promise.resolve({ usedFallback: true, gmpAddons: [] });
}
return downloadXML(url, allowNonBuiltIn, allowedCerts)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -5,7 +5,9 @@
// Test plugin blocking.
const gHttpTestRoot = "http://127.0.0.1:8888/" + RELATIVE_DIR + "/";
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
function updateBlocklist(aURL, aCallback) {
var observer = function() {
@ -16,19 +18,24 @@ function updateBlocklist(aURL, aCallback) {
if (Services.prefs.getBoolPref("extensions.blocklist.useXML", true)) {
info("Loading plugin data " + aURL + " using xml implementation.");
Services.prefs.setCharPref("extensions.blocklist.url", aURL);
var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
.getService(Ci.nsITimerCallback);
var blocklistNotifier = Cc[
"@mozilla.org/extensions/blocklist;1"
].getService(Ci.nsITimerCallback);
blocklistNotifier.notify(null);
} else {
info("Loading plugin data " + aURL + " using remote settings.");
if (aURL.endsWith("blockNoPlugins.xml")) {
AddonTestUtils.loadBlocklistRawData({plugins: []});
AddonTestUtils.loadBlocklistRawData({ plugins: [] });
} else if (aURL.endsWith("blockPluginHard.xml")) {
AddonTestUtils.loadBlocklistRawData({plugins: [{
matchFilename: "libnptest\\.so|nptest\\.dll|Test\\.plugin",
versionRange: [{"severity": "2"}],
blockID: "p9999",
}]});
AddonTestUtils.loadBlocklistRawData({
plugins: [
{
matchFilename: "libnptest\\.so|nptest\\.dll|Test\\.plugin",
versionRange: [{ severity: "2" }],
blockID: "p9999",
},
],
});
} else {
ok(false, "Should never be asked to update to unknown blocklist data.");
}
@ -38,7 +45,9 @@ function updateBlocklist(aURL, aCallback) {
var _originalBlocklistURL = null;
function setAndUpdateBlocklist(aURL, aCallback) {
if (!_originalBlocklistURL) {
_originalBlocklistURL = Services.prefs.getCharPref("extensions.blocklist.url");
_originalBlocklistURL = Services.prefs.getCharPref(
"extensions.blocklist.url"
);
}
updateBlocklist(aURL, aCallback);
}
@ -48,23 +57,37 @@ function resetBlocklist() {
}
function getXULPluginUI(plugin, anonid) {
if (plugin.openOrClosedShadowRoot &&
plugin.openOrClosedShadowRoot.isUAWidget()) {
if (
plugin.openOrClosedShadowRoot &&
plugin.openOrClosedShadowRoot.isUAWidget()
) {
return plugin.openOrClosedShadowRoot.getElementById(anonid);
}
return plugin.ownerDocument.
getAnonymousElementByAttribute(plugin, "anonid", anonid);
return plugin.ownerDocument.getAnonymousElementByAttribute(
plugin,
"anonid",
anonid
);
}
function assertPluginActiveState({managerWindow, pluginId, expectedActivateState}) {
function assertPluginActiveState({
managerWindow,
pluginId,
expectedActivateState,
}) {
let pluginEl = get_addon_element(managerWindow, pluginId);
ok(pluginEl, `Got the about:addon entry for "${pluginId}"`);
if (managerWindow.useHtmlViews) {
const pluginOptions = pluginEl.querySelector("plugin-options");
const pluginCheckedItem = pluginOptions.querySelector("panel-item[checked]");
is(pluginCheckedItem.getAttribute("action"), expectedActivateState,
`plugin should have ${expectedActivateState} state selected`);
const pluginCheckedItem = pluginOptions.querySelector(
"panel-item[checked]"
);
is(
pluginCheckedItem.getAttribute("action"),
expectedActivateState,
`plugin should have ${expectedActivateState} state selected`
);
} else {
// Assertions for the XUL about:addons views.
pluginEl.parentNode.ensureElementIsVisible(pluginEl);
@ -74,13 +97,23 @@ function assertPluginActiveState({managerWindow, pluginId, expectedActivateState
is_element_hidden(disableButton, "disable button should be hidden");
let menu = getXULPluginUI(pluginEl, "state-menulist");
is_element_visible(menu, "state menu should be visible");
let activateItem = getXULPluginUI(pluginEl, `${expectedActivateState}-menuitem`);
ok(activateItem, `Got a menu item for the ${expectedActivateState} plugin activate state`);
is(menu.selectedItem, activateItem, `state menu should have '${expectedActivateState}' selected`);
let activateItem = getXULPluginUI(
pluginEl,
`${expectedActivateState}-menuitem`
);
ok(
activateItem,
`Got a menu item for the ${expectedActivateState} plugin activate state`
);
is(
menu.selectedItem,
activateItem,
`state menu should have '${expectedActivateState}' selected`
);
}
}
function setPluginActivateState({managerWindow, pluginId, activateState}) {
function setPluginActivateState({ managerWindow, pluginId, activateState }) {
let pluginEl = get_addon_element(managerWindow, pluginId);
ok(pluginEl, `Got the about:addon entry for "${pluginId}"`);
@ -92,14 +125,17 @@ function setPluginActivateState({managerWindow, pluginId, activateState}) {
} else {
// Activate plugin on the XUL about:addons views.
let activateItem = getXULPluginUI(pluginEl, `${activateState}-menuitem`);
ok(activateItem, `Got a menu item for the ${activateState} plugin activate state`);
ok(
activateItem,
`Got a menu item for the ${activateState} plugin activate state`
);
let menu = getXULPluginUI(pluginEl, "state-menulist");
menu.selectedItem = activateItem;
activateItem.doCommand();
}
}
async function assertPluginAppDisabled({managerWindow, pluginId}) {
async function assertPluginAppDisabled({ managerWindow, pluginId }) {
const pluginEl = get_addon_element(managerWindow, pluginId);
ok(pluginEl, `Got the about:addon entry for "${pluginId}"`);
@ -109,12 +145,19 @@ async function assertPluginAppDisabled({managerWindow, pluginId}) {
pluginOptions.querySelector("panel-list").open = true;
// tests all buttons disabled (besides the checked one and the expand action)
// are expected to be disabled if locked is true.
for (const item of pluginOptions.querySelectorAll("panel-item:not([hidden])")) {
for (const item of pluginOptions.querySelectorAll(
"panel-item:not([hidden])"
)) {
const actionName = item.getAttribute("action");
if (!item.hasAttribute("checked") && actionName !== "expand" &&
actionName !== "preferences") {
ok(item.shadowRoot.querySelector("button").disabled,
`Plugin action "${actionName}" should be disabled`);
if (
!item.hasAttribute("checked") &&
actionName !== "expand" &&
actionName !== "preferences"
) {
ok(
item.shadowRoot.querySelector("button").disabled,
`Plugin action "${actionName}" should be disabled`
);
}
}
pluginOptions.querySelector("panel-list").open = false;
@ -165,11 +208,18 @@ async function test_CTP_plugins(aboutAddonsType) {
expectedActivateState: "ask-to-activate",
});
let pluginTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html");
let pluginTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
gHttpTestRoot + "plugin_test.html"
);
let pluginBrowser = pluginTab.linkedBrowser;
let condition = () => PopupNotifications.getNotification("click-to-play-plugins", pluginBrowser);
await BrowserTestUtils.waitForCondition(condition, "part4: should have a click-to-play notification");
let condition = () =>
PopupNotifications.getNotification("click-to-play-plugins", pluginBrowser);
await BrowserTestUtils.waitForCondition(
condition,
"part4: should have a click-to-play notification"
);
BrowserTestUtils.removeTab(pluginTab);
@ -179,14 +229,22 @@ async function test_CTP_plugins(aboutAddonsType) {
activateState: "always-activate",
});
pluginTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html");
pluginTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
gHttpTestRoot + "plugin_test.html"
);
await ContentTask.spawn(pluginTab.linkedBrowser, null, async function() {
let testPlugin = content.document.getElementById("test");
ok(testPlugin, "part5: should have a plugin element in the page");
let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
let objLoadingContent = testPlugin.QueryInterface(
Ci.nsIObjectLoadingContent
);
let condition = () => objLoadingContent.activated;
await ContentTaskUtils.waitForCondition(condition, "part5: waited too long for plugin to activate");
await ContentTaskUtils.waitForCondition(
condition,
"part5: waited too long for plugin to activate"
);
ok(objLoadingContent.activated, "part6: plugin should be activated");
});
@ -198,13 +256,18 @@ async function test_CTP_plugins(aboutAddonsType) {
activateState: "never-activate",
});
pluginTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html");
pluginTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
gHttpTestRoot + "plugin_test.html"
);
pluginBrowser = pluginTab.linkedBrowser;
await ContentTask.spawn(pluginTab.linkedBrowser, null, async function() {
let testPlugin = content.document.getElementById("test");
ok(testPlugin, "part7: should have a plugin element in the page");
let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
let objLoadingContent = testPlugin.QueryInterface(
Ci.nsIObjectLoadingContent
);
ok(!objLoadingContent.activated, "part7: plugin should not be activated");
});
@ -224,15 +287,23 @@ async function test_CTP_plugins(aboutAddonsType) {
activateState: "always-activate",
});
pluginTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html");
pluginTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
gHttpTestRoot + "plugin_test.html"
);
pluginBrowser = pluginTab.linkedBrowser;
await ContentTask.spawn(pluginTab.linkedBrowser, null, async function() {
let testPlugin = content.document.getElementById("test");
ok(testPlugin, "part9: should have a plugin element in the page");
let objLoadingContent = testPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
let objLoadingContent = testPlugin.QueryInterface(
Ci.nsIObjectLoadingContent
);
let condition = () => objLoadingContent.activated;
await ContentTaskUtils.waitForCondition(condition, "part9: waited too long for plugin to activate");
await ContentTaskUtils.waitForCondition(
condition,
"part9: waited too long for plugin to activate"
);
ok(objLoadingContent.activated, "part10: plugin should be activated");
});
@ -244,11 +315,18 @@ async function test_CTP_plugins(aboutAddonsType) {
activateState: "ask-to-activate",
});
pluginTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, gHttpTestRoot + "plugin_test.html");
pluginTab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
gHttpTestRoot + "plugin_test.html"
);
pluginBrowser = pluginTab.linkedBrowser;
condition = () => PopupNotifications.getNotification("click-to-play-plugins", pluginBrowser);
await BrowserTestUtils.waitForCondition(condition, "part11: should have a click-to-play notification");
condition = () =>
PopupNotifications.getNotification("click-to-play-plugins", pluginBrowser);
await BrowserTestUtils.waitForCondition(
condition,
"part11: should have a click-to-play notification"
);
BrowserTestUtils.removeTab(pluginTab);
await close_manager(managerWindow);
@ -321,7 +399,7 @@ async function checkPlugins() {
set: [["extensions.htmlaboutaddons.enabled", false]],
});
managerWindow = await open_manager("addons://list/plugin");
await assertPluginAppDisabled({managerWindow, pluginId: testPluginId});
await assertPluginAppDisabled({ managerWindow, pluginId: testPluginId });
await close_manager(managerWindow);
info("Test blocklisted plugin actions disabled in HTML about:addons");
@ -329,6 +407,6 @@ async function checkPlugins() {
set: [["extensions.htmlaboutaddons.enabled", true]],
});
managerWindow = await open_manager("addons://list/plugin");
await assertPluginAppDisabled({managerWindow, pluginId: testPluginId});
await assertPluginAppDisabled({ managerWindow, pluginId: testPluginId });
await close_manager(managerWindow);
}

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

@ -3,7 +3,9 @@
"use strict";
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm"
);
// Whitelist rejections related to closing an about:debugging too soon after it has been
// just opened in a new tab and loaded.
PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
@ -50,7 +52,7 @@ add_task(async function testAboutDebugging() {
let aboutDebuggingTab = gBrowser.selectedTab;
if (newAboutAddons) {
const {AboutDebugging} = aboutDebuggingTab.linkedBrowser.contentWindow;
const { AboutDebugging } = aboutDebuggingTab.linkedBrowser.contentWindow;
// Avoid test failures due to closing the about:debugging tab
// while it is still initializing.
info("Wait until about:debugging actions are finished");
@ -63,7 +65,8 @@ add_task(async function testAboutDebugging() {
info("Re-open about:debugging");
let switched = TestUtils.waitForCondition(
() => gBrowser.selectedTab == aboutDebuggingTab);
() => gBrowser.selectedTab == aboutDebuggingTab
);
debugAddonsBtn.doCommand();
await switched;

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

@ -1,62 +1,83 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const URI_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
const URI_BLOCKLIST_DIALOG =
"chrome://mozapps/content/extensions/blocklist.xul";
// This tests that the blocklist dialog still affects soft-blocked add-ons
// if the user clicks the "Restart Later" button. It also ensures that the
// "Cancel" button is correctly renamed (to "Restart Later").
var args = {
restart: false,
list: [{
name: "Bug 523784 softblocked addon",
version: "1",
icon: "chrome://global/skin/plugins/pluginGeneric.svg",
disable: false,
blocked: false,
url: "http://example.com/bug523784_1",
}],
list: [
{
name: "Bug 523784 softblocked addon",
version: "1",
icon: "chrome://global/skin/plugins/pluginGeneric.svg",
disable: false,
blocked: false,
url: "http://example.com/bug523784_1",
},
],
};
function test() {
waitForExplicitFinish();
let windowObserver = function(aSubject, aTopic, aData) {
if (aTopic != "domwindowopened")
if (aTopic != "domwindowopened") {
return;
}
Services.ww.unregisterNotification(windowObserver);
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function() {
executeSoon(() => bug523784_test1(win));
}, {once: true});
win.addEventListener(
"load",
function() {
executeSoon(() => bug523784_test1(win));
},
{ once: true }
);
};
Services.ww.registerNotification(windowObserver);
args.wrappedJSObject = args;
Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
"chrome,centerscreen,dialog,titlebar", args);
Services.ww.openWindow(
null,
URI_BLOCKLIST_DIALOG,
"",
"chrome,centerscreen,dialog,titlebar",
args
);
}
function bug523784_test1(win) {
let bundle = Services.strings.
createBundle("chrome://mozapps/locale/update/updates.properties");
let bundle = Services.strings.createBundle(
"chrome://mozapps/locale/update/updates.properties"
);
let cancelButton = win.document.documentElement.getButton("cancel");
let moreInfoLink = win.document.getElementById("moreInfo");
is(cancelButton.getAttribute("label"),
bundle.GetStringFromName("restartLaterButton"),
"Text should be changed on Cancel button");
is(cancelButton.getAttribute("accesskey"),
bundle.GetStringFromName("restartLaterButton.accesskey"),
"Accesskey should also be changed on Cancel button");
is(moreInfoLink.getAttribute("href"),
"http://example.com/bug523784_1",
"More Info link should link to a detailed blocklist page.");
is(
cancelButton.getAttribute("label"),
bundle.GetStringFromName("restartLaterButton"),
"Text should be changed on Cancel button"
);
is(
cancelButton.getAttribute("accesskey"),
bundle.GetStringFromName("restartLaterButton.accesskey"),
"Accesskey should also be changed on Cancel button"
);
is(
moreInfoLink.getAttribute("href"),
"http://example.com/bug523784_1",
"More Info link should link to a detailed blocklist page."
);
let windowObserver = function(aSubject, aTopic, aData) {
if (aTopic != "domwindowclosed")
if (aTopic != "domwindowclosed") {
return;
}
Services.ww.unregisterNotification(windowObserver);
@ -72,22 +93,31 @@ function bug523784_test1(win) {
function bug523784_test2(win) {
let windowObserver = function(aSubject, aTopic, aData) {
if (aTopic != "domwindowopened")
if (aTopic != "domwindowopened") {
return;
}
Services.ww.unregisterNotification(windowObserver);
let win = aSubject.QueryInterface(Ci.nsIDOMWindow);
win.addEventListener("load", function() {
executeSoon(function() {
let moreInfoLink = win.document.getElementById("moreInfo");
let cancelButton = win.document.documentElement.getButton("cancel");
is(moreInfoLink.getAttribute("href"),
Services.urlFormatter.formatURLPref("extensions.blocklist.detailsURL"),
"More Info link should link to the general blocklist page.");
cancelButton.doCommand();
executeSoon(finish);
});
}, {once: true});
win.addEventListener(
"load",
function() {
executeSoon(function() {
let moreInfoLink = win.document.getElementById("moreInfo");
let cancelButton = win.document.documentElement.getButton("cancel");
is(
moreInfoLink.getAttribute("href"),
Services.urlFormatter.formatURLPref(
"extensions.blocklist.detailsURL"
),
"More Info link should link to the general blocklist page."
);
cancelButton.doCommand();
executeSoon(finish);
});
},
{ once: true }
);
};
Services.ww.registerNotification(windowObserver);
@ -111,6 +141,11 @@ function bug523784_test2(win) {
});
args.wrappedJSObject = args;
Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
"chrome,centerscreen,dialog,titlebar", args);
Services.ww.openWindow(
null,
URI_BLOCKLIST_DIALOG,
"",
"chrome,centerscreen,dialog,titlebar",
args
);
}

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

@ -20,39 +20,59 @@ async function test() {
var addonPrefsURI = CHROMEROOT + "addon_prefs.xul";
var gProvider = new MockProvider();
gProvider.createAddons([{
id: "test1@tests.mozilla.org",
name: "Test add-on 1",
description: "foo",
},
{
id: "test2@tests.mozilla.org",
name: "Test add-on 2",
description: "bar",
optionsURL: addonPrefsURI,
}]);
gProvider.createAddons([
{
id: "test1@tests.mozilla.org",
name: "Test add-on 1",
description: "foo",
},
{
id: "test2@tests.mozilla.org",
name: "Test add-on 2",
description: "bar",
optionsURL: addonPrefsURI,
},
]);
let aManager = await open_manager("addons://list/extension");
var addonList = aManager.document.getElementById("addon-list");
for (var addonItem of addonList.childNodes) {
if (addonItem.hasAttribute("name") &&
addonItem.getAttribute("name") == "Test add-on 1")
if (
addonItem.hasAttribute("name") &&
addonItem.getAttribute("name") == "Test add-on 1"
) {
break;
}
}
var prefsBtn = aManager.document.getAnonymousElementByAttribute(addonItem,
"anonid",
"preferences-btn");
is(prefsBtn.hidden, true, "Prefs button should be hidden for addon with no optionsURL set");
var prefsBtn = aManager.document.getAnonymousElementByAttribute(
addonItem,
"anonid",
"preferences-btn"
);
is(
prefsBtn.hidden,
true,
"Prefs button should be hidden for addon with no optionsURL set"
);
for (addonItem of addonList.childNodes) {
if (addonItem.hasAttribute("name") &&
addonItem.getAttribute("name") == "Test add-on 2")
if (
addonItem.hasAttribute("name") &&
addonItem.getAttribute("name") == "Test add-on 2"
) {
break;
}
}
prefsBtn = aManager.document.getAnonymousElementByAttribute(addonItem,
"anonid",
"preferences-btn");
is(prefsBtn.hidden, true, "Prefs button should not be shown for addon with just an optionsURL set");
prefsBtn = aManager.document.getAnonymousElementByAttribute(
addonItem,
"anonid",
"preferences-btn"
);
is(
prefsBtn.hidden,
true,
"Prefs button should not be shown for addon with just an optionsURL set"
);
close_manager(aManager, finish);
}

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

@ -5,7 +5,9 @@
// Simulates quickly switching between different list views to verify that only
// the last selected is displayed
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm"
);
PromiseTestUtils.whitelistRejectionsGlobally(/this\._errorLink/);

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

@ -14,11 +14,19 @@ async function checkInstallConfirmation(...names) {
let observer = {
observe(aSubject, aTopic, aData) {
var installInfo = aSubject.wrappedJSObject;
isnot(installInfo.browser, null, "Notification should have non-null browser");
Assert.deepEqual(installInfo.installs[0].installTelemetryInfo, {
source: "about:addons",
method: "install-from-file",
}, "Got the expected installTelemetryInfo");
isnot(
installInfo.browser,
null,
"Notification should have non-null browser"
);
Assert.deepEqual(
installInfo.installs[0].installTelemetryInfo,
{
source: "about:addons",
method: "install-from-file",
},
"Got the expected installTelemetryInfo"
);
notificationCount++;
},
};
@ -34,7 +42,9 @@ async function checkInstallConfirmation(...names) {
info(`Saw install for ${name}`);
if (results.length < names.length) {
info(`Waiting for installs for ${names.filter(n => !results.includes(n))}`);
info(
`Waiting for installs for ${names.filter(n => !results.includes(n))}`
);
promise = promisePopupNotificationShown("addon-webext-permissions");
}
@ -43,7 +53,11 @@ async function checkInstallConfirmation(...names) {
Assert.deepEqual(results.sort(), names.sort(), "Got expected installs");
is(notificationCount, names.length, `Saw ${names.length} addon-install-started notification`);
is(
notificationCount,
names.length,
`Saw ${names.length} addon-install-started notification`
);
Services.obs.removeObserver(observer, "addon-install-started");
}
@ -51,9 +65,9 @@ add_task(async function test_install_from_file() {
gManagerWindow = await open_manager("addons://list/extension");
var filePaths = [
get_addon_file_url("browser_dragdrop1.xpi"),
get_addon_file_url("browser_dragdrop2.xpi"),
];
get_addon_file_url("browser_dragdrop1.xpi"),
get_addon_file_url("browser_dragdrop2.xpi"),
];
for (let uri of filePaths) {
ok(uri.file != null, `Should have file for ${uri.spec}`);
ok(uri.file instanceof Ci.nsIFile, `Should have nsIFile for ${uri.spec}`);
@ -62,7 +76,10 @@ add_task(async function test_install_from_file() {
// Set handler that executes the core test after the window opens,
// and resolves the promise when the window closes
let pInstallURIClosed = checkInstallConfirmation("Drag Drop test 1", "Drag Drop test 2");
let pInstallURIClosed = checkInstallConfirmation(
"Drag Drop test 1",
"Drag Drop test 2"
);
gManagerWindow.gViewController.doCommand("cmd_installFromFile");

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

@ -17,7 +17,11 @@ async function test() {
aWindow = await open_manager(null);
utils = new CategoryUtilities(aWindow);
is(utils.selectedCategory, "plugin", "Should have shown the plugins category");
is(
utils.selectedCategory,
"plugin",
"Should have shown the plugins category"
);
// Open the extensions category
utils.openType("extension", async function() {
@ -26,7 +30,11 @@ async function test() {
aWindow = await open_manager(null);
utils = new CategoryUtilities(aWindow);
is(utils.selectedCategory, "extension", "Should have shown the extensions category");
is(
utils.selectedCategory,
"extension",
"Should have shown the extensions category"
);
close_manager(aWindow, finish);
});
});

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

@ -37,6 +37,6 @@ function f_key_test() {
}
function slash_key_test() {
EventUtils.synthesizeKey("/", { }, gManagerWindow);
EventUtils.synthesizeKey("/", {}, gManagerWindow);
is(focusCount, 2, "Search box should have been focused due to the / key");
}

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

@ -8,10 +8,12 @@
var gManagerWindow;
var gCategoryUtilities;
var gProvider;
var gInstallProperties = [{
name: "Locale Category Test",
type: "locale",
}];
var gInstallProperties = [
{
name: "Locale Category Test",
type: "locale",
},
];
var gInstall;
var gExpectedCancel = false;
var gTestInstallListener = {
@ -92,4 +94,3 @@ add_test(async function() {
gExpectedCancel = true;
gInstall.cancel();
});

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

@ -9,10 +9,12 @@ var gManagerWindow;
var gCategoryUtilities;
var gProvider;
var gInstall;
var gInstallProperties = [{
name: "Locale Category Test",
type: "locale",
}];
var gInstallProperties = [
{
name: "Locale Category Test",
type: "locale",
},
];
async function test() {
try {
@ -50,7 +52,12 @@ function check_hidden(aExpectedHidden) {
is(hidden, !!aExpectedHidden, "Should have correct hidden state");
}
async function run_open_test(aTestSetup, aLoadHidden, aInitializedHidden, aSelected) {
async function run_open_test(
aTestSetup,
aLoadHidden,
aInitializedHidden,
aSelected
) {
function loadCallback(aManagerWindow) {
gManagerWindow = aManagerWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
@ -60,7 +67,7 @@ async function run_open_test(aTestSetup, aLoadHidden, aInitializedHidden, aSelec
async function run() {
await open_manager(null, null, loadCallback);
check_hidden(aInitializedHidden);
var selected = (gCategoryUtilities.selectedCategory == "locale");
var selected = gCategoryUtilities.selectedCategory == "locale";
is(selected, !!aSelected, "Should have correct selected state");
run_next_test();
@ -70,7 +77,6 @@ async function run_open_test(aTestSetup, aLoadHidden, aInitializedHidden, aSelec
aTestSetup ? aTestSetup(run) : run();
}
// Tests that the locale category is hidden when there are no locales installed
add_test(function() {
run_open_test(null, true, true);
@ -124,4 +130,3 @@ add_test(function() {
gInstall.cancel();
run_open_test(null, false, true);
});

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

@ -18,22 +18,26 @@ SpecialPowers.pushPrefEnv({
add_task(async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "extension@tests.mozilla.org",
name: "Extension 1",
type: "extension",
version: "123",
}, {
id: "theme@tests.mozilla.org",
name: "Theme 2",
type: "theme",
version: "456",
}, {
id: "lwtheme@personas.mozilla.org",
name: "Persona 3",
type: "theme",
version: "789",
}]);
gProvider.createAddons([
{
id: "extension@tests.mozilla.org",
name: "Extension 1",
type: "extension",
version: "123",
},
{
id: "theme@tests.mozilla.org",
name: "Theme 2",
type: "theme",
version: "456",
},
{
id: "lwtheme@personas.mozilla.org",
name: "Persona 3",
type: "theme",
version: "789",
},
]);
gManagerWindow = await open_manager();
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
@ -44,7 +48,11 @@ function get(aId) {
}
function get_node(parent, anonid) {
return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
return parent.ownerDocument.getAnonymousElementByAttribute(
parent,
"anonid",
anonid
);
}
function open_details(aList, aItem, aCallback) {

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

@ -32,12 +32,14 @@ async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
@ -48,22 +50,24 @@ async function test() {
run_next_test();
}
function end_test() {
close_manager(gManagerWindow, finish);
}
function wait_for_popup(aCallback) {
if (gUtilsMenu.state == "open") {
aCallback();
return;
}
gUtilsMenu.addEventListener("popupshown", function() {
info("Utilities menu shown");
aCallback();
}, {once: true});
gUtilsMenu.addEventListener(
"popupshown",
function() {
info("Utilities menu shown");
aCallback();
},
{ once: true }
);
}
function wait_for_hide(aCallback) {
@ -72,38 +76,63 @@ function wait_for_hide(aCallback) {
return;
}
gUtilsMenu.addEventListener("popuphidden", function() {
info("Utilities menu hidden");
aCallback();
}, {once: true});
gUtilsMenu.addEventListener(
"popuphidden",
function() {
info("Utilities menu hidden");
aCallback();
},
{ once: true }
);
}
add_test(function() {
gSetDefault = gManagerWindow.document.getElementById("utils-autoUpdateDefault");
gResetToAutomatic = gManagerWindow.document.getElementById("utils-resetAddonUpdatesToAutomatic");
gResetToManual = gManagerWindow.document.getElementById("utils-resetAddonUpdatesToManual");
gSetDefault = gManagerWindow.document.getElementById(
"utils-autoUpdateDefault"
);
gResetToAutomatic = gManagerWindow.document.getElementById(
"utils-resetAddonUpdatesToAutomatic"
);
gResetToManual = gManagerWindow.document.getElementById(
"utils-resetAddonUpdatesToManual"
);
info("Ensuring default prefs are set to true");
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, true);
Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, true);
wait_for_popup(function() {
is(gSetDefault.getAttribute("checked"), "true",
"#1 Set Default menuitem should be checked");
is_element_visible(gResetToAutomatic,
"#1 Reset to Automatic menuitem should be visible");
is_element_hidden(gResetToManual,
"#1 Reset to Manual menuitem should be hidden");
is(
gSetDefault.getAttribute("checked"),
"true",
"#1 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#1 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#1 Reset to Manual menuitem should be hidden"
);
var listener = {
onPropertyChanged(aAddon, aProperties) {
AddonManager.removeAddonListener(listener);
is(aAddon.id, gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon");
ok(!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array");
is(aAddon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default");
is(
aAddon.id,
gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon"
);
ok(
!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array"
);
is(
aAddon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default"
);
info("Setting Addon.applyBackgroundUpdates back to disabled");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
@ -114,115 +143,162 @@ add_test(function() {
AddonManager.addAddonListener(listener);
info("Clicking Reset to Automatic menuitem");
EventUtils.synthesizeMouseAtCenter(gResetToAutomatic, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gResetToAutomatic, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
info("Disabling extensions.update.enabled");
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
wait_for_popup(function() {
isnot(gSetDefault.getAttribute("checked"), "true",
"#2 Set Default menuitem should not be checked");
is_element_visible(gResetToAutomatic,
"#2 Reset to Automatic menuitem should be visible");
is_element_hidden(gResetToManual,
"#2 Reset to Manual menuitem should be hidden");
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#2 Set Default menuitem should not be checked"
);
is_element_visible(
gResetToAutomatic,
"#2 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#2 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to reenable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test");
ok(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test");
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
ok(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test"
);
info("Disabling both extensions.update.enabled and extensions.update.autoUpdateDefault");
info(
"Disabling both extensions.update.enabled and extensions.update.autoUpdateDefault"
);
Services.prefs.setBoolPref(PREF_UPDATE_ENABLED, false);
Services.prefs.setBoolPref(PREF_AUTOUPDATE_DEFAULT, false);
wait_for_popup(function() {
isnot(gSetDefault.getAttribute("checked"), "true",
"#3 Set Default menuitem should not be checked");
is_element_hidden(gResetToAutomatic,
"#3 Reset to automatic menuitem should be hidden");
is_element_visible(gResetToManual,
"#3 Reset to manual menuitem should be visible");
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#3 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#3 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#3 Reset to manual menuitem should be visible"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to reenable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test");
ok(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test");
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
ok(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
"extensions.update.autoUpdateDefault should be true after the previous test"
);
info("clicking the button to disable extensions.update.autoUpdateDefault");
wait_for_popup(function() {
is(gSetDefault.getAttribute("checked"), "true",
"#4 Set Default menuitem should be checked");
is_element_visible(gResetToAutomatic,
"#4 Reset to Automatic menuitem should be visible");
is_element_hidden(gResetToManual,
"#4 Reset to Manual menuitem should be hidden");
is(
gSetDefault.getAttribute("checked"),
"true",
"#4 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#4 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#4 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem to disable");
EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
ok(Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test");
is(Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT), false,
"extensions.update.autoUpdateDefault should be false after the previous test");
ok(
Services.prefs.getBoolPref(PREF_UPDATE_ENABLED),
"extensions.update.enabled should be true after the previous test"
);
is(
Services.prefs.getBoolPref(PREF_AUTOUPDATE_DEFAULT),
false,
"extensions.update.autoUpdateDefault should be false after the previous test"
);
wait_for_popup(function() {
isnot(gSetDefault.getAttribute("checked"), "true",
"#5 Set Default menuitem should not be checked");
is_element_hidden(gResetToAutomatic,
"#5 Reset to automatic menuitem should be hidden");
is_element_visible(gResetToManual,
"#5 Reset to manual menuitem should be visible");
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#5 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#5 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#5 Reset to manual menuitem should be visible"
);
var listener = {
onPropertyChanged(aAddon, aProperties) {
AddonManager.removeAddonListener(listener);
is(aAddon.id, gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon");
ok(!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array");
is(aAddon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default");
is(
aAddon.id,
gProvider.addons[0].id,
"Should get onPropertyChanged event for correct addon"
);
ok(
!("applyBackgroundUpdates" in aProperties),
"Should have gotten applyBackgroundUpdates in properties array"
);
is(
aAddon.applyBackgroundUpdates,
AddonManager.AUTOUPDATE_DEFAULT,
"Addon.applyBackgroundUpdates should have been reset to default"
);
info("Setting Addon.applyBackgroundUpdates back to disabled");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
@ -233,42 +309,54 @@ add_test(function() {
AddonManager.addAddonListener(listener);
info("Clicking Reset to Manual menuitem");
EventUtils.synthesizeMouseAtCenter(gResetToManual, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gResetToManual, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
wait_for_popup(function() {
isnot(gSetDefault.getAttribute("checked"), "true",
"#6 Set Default menuitem should not be checked");
is_element_hidden(gResetToAutomatic,
"#6 Reset to automatic menuitem should be hidden");
is_element_visible(gResetToManual,
"#6 Reset to manual menuitem should be visible");
isnot(
gSetDefault.getAttribute("checked"),
"true",
"#6 Set Default menuitem should not be checked"
);
is_element_hidden(
gResetToAutomatic,
"#6 Reset to automatic menuitem should be hidden"
);
is_element_visible(
gResetToManual,
"#6 Reset to manual menuitem should be visible"
);
wait_for_hide(run_next_test);
info("Clicking Set Default menuitem");
EventUtils.synthesizeMouseAtCenter(gSetDefault, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gSetDefault, {}, gManagerWindow);
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});
add_test(function() {
wait_for_popup(function() {
is(gSetDefault.getAttribute("checked"), "true",
"#7 Set Default menuitem should be checked");
is_element_visible(gResetToAutomatic,
"#7 Reset to Automatic menuitem should be visible");
is_element_hidden(gResetToManual,
"#7 Reset to Manual menuitem should be hidden");
is(
gSetDefault.getAttribute("checked"),
"true",
"#7 Set Default menuitem should be checked"
);
is_element_visible(
gResetToAutomatic,
"#7 Reset to Automatic menuitem should be visible"
);
is_element_hidden(
gResetToManual,
"#7 Reset to Manual menuitem should be hidden"
);
wait_for_hide(run_next_test);
@ -276,6 +364,5 @@ add_test(function() {
});
info("Opening utilities menu");
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gUtilsBtn, {}, gManagerWindow);
});

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

@ -20,65 +20,76 @@ async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
}, {
id: "addon2@tests.mozilla.org",
name: "addon 2",
version: "2.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
}, {
id: "addon3@tests.mozilla.org",
name: "addon 3",
version: "3.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
{
id: "addon2@tests.mozilla.org",
name: "addon 2",
version: "2.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
{
id: "addon3@tests.mozilla.org",
name: "addon 3",
version: "3.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
]);
let aWindow = await open_manager("addons://updates/available");
gManagerWindow = aWindow;
run_next_test();
}
function end_test() {
close_manager(gManagerWindow, finish);
}
add_test(function() {
var list = gManagerWindow.document.getElementById("updates-list");
is(list.childNodes.length, 0, "Available updates list should be empty");
var emptyNotice = gManagerWindow.document.getElementById("empty-availableUpdates-msg");
var emptyNotice = gManagerWindow.document.getElementById(
"empty-availableUpdates-msg"
);
is_element_visible(emptyNotice, "Empty notice should be visible");
var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
var updateSelected = gManagerWindow.document.getElementById(
"update-selected-btn"
);
is_element_hidden(updateSelected, "Update Selected button should be hidden");
info("Adding updates");
gProvider.createInstalls([{
name: "addon 1",
version: "1.1",
existingAddon: gProvider.addons[0],
}, {
name: "addon 2",
version: "2.1",
existingAddon: gProvider.addons[1],
}, {
name: "addon 3",
version: "3.1",
existingAddon: gProvider.addons[2],
}]);
gProvider.createInstalls([
{
name: "addon 1",
version: "1.1",
existingAddon: gProvider.addons[0],
},
{
name: "addon 2",
version: "2.1",
existingAddon: gProvider.addons[1],
},
{
name: "addon 3",
version: "3.1",
existingAddon: gProvider.addons[2],
},
]);
function wait_for_refresh() {
if (list.childNodes.length == 3 &&
list.childNodes[0].mManualUpdate &&
list.childNodes[1].mManualUpdate &&
list.childNodes[2].mManualUpdate) {
if (
list.childNodes.length == 3 &&
list.childNodes[0].mManualUpdate &&
list.childNodes[1].mManualUpdate &&
list.childNodes[2].mManualUpdate
) {
run_next_test();
} else {
info("Waiting for pane to refresh");
@ -89,7 +100,6 @@ add_test(function() {
setTimeout(wait_for_refresh, 10);
});
add_test(function() {
var list = gManagerWindow.document.getElementById("updates-list");
is(list.childNodes.length, 3, "Available updates list should have 2 items");
@ -101,51 +111,119 @@ add_test(function() {
var item3 = get_addon_element(gManagerWindow, "addon3@tests.mozilla.org");
isnot(item3, null, "Item for addon3@tests.mozilla.org should be in list");
var emptyNotice = gManagerWindow.document.getElementById("empty-availableUpdates-msg");
var emptyNotice = gManagerWindow.document.getElementById(
"empty-availableUpdates-msg"
);
is_element_hidden(emptyNotice, "Empty notice should be hidden");
var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
is_element_visible(updateSelected, "Update Selected button should be visible");
is(updateSelected.disabled, false, "Update Selected button should be enabled by default");
var updateSelected = gManagerWindow.document.getElementById(
"update-selected-btn"
);
is_element_visible(
updateSelected,
"Update Selected button should be visible"
);
is(
updateSelected.disabled,
false,
"Update Selected button should be enabled by default"
);
is(item1._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon1");
is(item2._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon2");
is(item3._includeUpdate.checked, true, "Include Update checkbox should be checked by default for addon3");
is(
item1._includeUpdate.checked,
true,
"Include Update checkbox should be checked by default for addon1"
);
is(
item2._includeUpdate.checked,
true,
"Include Update checkbox should be checked by default for addon2"
);
is(
item3._includeUpdate.checked,
true,
"Include Update checkbox should be checked by default for addon3"
);
info("Unchecking Include Update checkbox for addon1");
EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
is(item1._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon1");
is(updateSelected.disabled, false, "Update Selected button should still be enabled");
EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item1._includeUpdate.checked,
false,
"Include Update checkbox should now be be unchecked for addon1"
);
is(
updateSelected.disabled,
false,
"Update Selected button should still be enabled"
);
info("Unchecking Include Update checkbox for addon2");
EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
is(item2._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon2");
is(updateSelected.disabled, false, "Update Selected button should still be enabled");
EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item2._includeUpdate.checked,
false,
"Include Update checkbox should now be be unchecked for addon2"
);
is(
updateSelected.disabled,
false,
"Update Selected button should still be enabled"
);
info("Unchecking Include Update checkbox for addon3");
EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
is(item3._includeUpdate.checked, false, "Include Update checkbox should now be be unchecked for addon3");
is(updateSelected.disabled, true, "Update Selected button should now be disabled");
EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item3._includeUpdate.checked,
false,
"Include Update checkbox should now be be unchecked for addon3"
);
is(
updateSelected.disabled,
true,
"Update Selected button should now be disabled"
);
info("Checking Include Update checkbox for addon2");
EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, { }, gManagerWindow);
is(item2._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon2");
is(updateSelected.disabled, false, "Update Selected button should now be enabled");
EventUtils.synthesizeMouse(item2._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item2._includeUpdate.checked,
true,
"Include Update checkbox should now be be checked for addon2"
);
is(
updateSelected.disabled,
false,
"Update Selected button should now be enabled"
);
info("Checking Include Update checkbox for addon3");
EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, { }, gManagerWindow);
is(item3._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon3");
is(updateSelected.disabled, false, "Update Selected button should now be enabled");
EventUtils.synthesizeMouse(item3._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item3._includeUpdate.checked,
true,
"Include Update checkbox should now be be checked for addon3"
);
is(
updateSelected.disabled,
false,
"Update Selected button should now be enabled"
);
var installCount = 0;
var listener = {
onDownloadStarted(aInstall) {
isnot(aInstall.existingAddon.id, "addon1@tests.mozilla.org", "Should not have seen a download start for addon1");
isnot(
aInstall.existingAddon.id,
"addon1@tests.mozilla.org",
"Should not have seen a download start for addon1"
);
},
onInstallEnded(aInstall) {
if (++installCount < 2)
if (++installCount < 2) {
return;
}
gProvider.installs[0].removeTestListener(listener);
gProvider.installs[1].removeTestListener(listener);
@ -154,9 +232,21 @@ add_test(function() {
// Installs are started synchronously so by the time an executeSoon is
// executed all installs that are going to start will have started
executeSoon(function() {
is(gProvider.installs[0].state, AddonManager.STATE_AVAILABLE, "addon1 should not have been upgraded");
is(gProvider.installs[1].state, AddonManager.STATE_INSTALLED, "addon2 should have been upgraded");
is(gProvider.installs[2].state, AddonManager.STATE_INSTALLED, "addon3 should have been upgraded");
is(
gProvider.installs[0].state,
AddonManager.STATE_AVAILABLE,
"addon1 should not have been upgraded"
);
is(
gProvider.installs[1].state,
AddonManager.STATE_INSTALLED,
"addon2 should have been upgraded"
);
is(
gProvider.installs[2].state,
AddonManager.STATE_INSTALLED,
"addon3 should have been upgraded"
);
run_next_test();
});
@ -166,22 +256,39 @@ add_test(function() {
gProvider.installs[1].addTestListener(listener);
gProvider.installs[2].addTestListener(listener);
info("Clicking Update Selected button");
EventUtils.synthesizeMouseAtCenter(updateSelected, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(updateSelected, {}, gManagerWindow);
});
add_test(function() {
var updateSelected = gManagerWindow.document.getElementById("update-selected-btn");
is(updateSelected.disabled, true, "Update Selected button should be disabled");
var updateSelected = gManagerWindow.document.getElementById(
"update-selected-btn"
);
is(
updateSelected.disabled,
true,
"Update Selected button should be disabled"
);
var item1 = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
isnot(item1, null, "Item for addon1@tests.mozilla.org should be in list");
is(item1._includeUpdate.checked, false, "Include Update checkbox should not have changed");
is(
item1._includeUpdate.checked,
false,
"Include Update checkbox should not have changed"
);
info("Checking Include Update checkbox for addon1");
EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, { }, gManagerWindow);
is(item1._includeUpdate.checked, true, "Include Update checkbox should now be be checked for addon1");
is(updateSelected.disabled, false, "Update Selected button should now not be disabled");
EventUtils.synthesizeMouse(item1._includeUpdate, 2, 2, {}, gManagerWindow);
is(
item1._includeUpdate.checked,
true,
"Include Update checkbox should now be be checked for addon1"
);
is(
updateSelected.disabled,
false,
"Update Selected button should now not be disabled"
);
run_next_test();
});

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

@ -28,8 +28,16 @@ async function open_details(aId, aType, aCallback) {
while (item) {
if ("mAddon" in item && item.mAddon.id == aId) {
list.ensureElementIsVisible(item);
EventUtils.synthesizeMouseAtCenter(item, { clickCount: 1 }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(
item,
{ clickCount: 1 },
gManagerWindow
);
EventUtils.synthesizeMouseAtCenter(
item,
{ clickCount: 2 },
gManagerWindow
);
wait_for_view_load(gManagerWindow, aCallback);
return;
}
@ -49,12 +57,17 @@ function get_list_view_warning_node() {
item = item.nextSibling;
}
ok(found, "Test add-on node should have been found.");
return item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
return item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"warning"
);
}
function get_detail_view_warning_node(aManagerWindow) {
if (aManagerWindow)
if (aManagerWindow) {
return aManagerWindow.document.getElementById("detail-warning");
}
return undefined;
}
@ -63,13 +76,15 @@ async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "Test add-on",
description: "A test add-on",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "Test add-on",
description: "A test add-on",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
},
]);
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
@ -88,7 +103,11 @@ add_test(async function() {
Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, true);
let warning_node = get_list_view_warning_node();
is_element_visible(warning_node, "Warning message should be visible");
is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
is(
warning_node.textContent,
"Test add-on is incompatible with " + gApp + " " + gVersion + ".",
"Warning message should be correct"
);
run_next_test();
});
@ -96,7 +115,11 @@ add_test(function() {
open_details("addon1@tests.mozilla.org", "extension", function() {
let warning_node = get_detail_view_warning_node(gManagerWindow);
is_element_visible(warning_node, "Warning message should be visible");
is(warning_node.textContent, "Test add-on is incompatible with " + gApp + " " + gVersion + ".", "Warning message should be correct");
is(
warning_node.textContent,
"Test add-on is incompatible with " + gApp + " " + gVersion + ".",
"Warning message should be correct"
);
Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, false);
run_next_test();
});
@ -107,7 +130,11 @@ add_test(async function() {
await gCategoryUtilities.openType("extension");
let warning_node = get_list_view_warning_node();
is_element_visible(warning_node, "Warning message should be visible");
is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct");
is(
warning_node.textContent,
"Test add-on is known to cause security or stability issues.",
"Warning message should be correct"
);
run_next_test();
});
@ -115,7 +142,11 @@ add_test(function() {
open_details("addon1@tests.mozilla.org", "extension", function() {
let warning_node = get_detail_view_warning_node(gManagerWindow);
is_element_visible(warning_node, "Warning message should be visible");
is(warning_node.textContent, "Test add-on is known to cause security or stability issues.", "Warning message should be correct");
is(
warning_node.textContent,
"Test add-on is known to cause security or stability issues.",
"Warning message should be correct"
);
run_next_test();
});
});

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

@ -4,117 +4,169 @@
// Bug 591465 - Context menu of add-ons miss context related state change entries
var gManagerWindow;
var gProvider;
var gContextMenu;
add_task(async function setup() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
}, {
id: "addon2@tests.mozilla.org",
name: "addon 2",
version: "1.0",
_userDisabled: true,
}, {
id: "theme1@tests.mozilla.org",
name: "theme 1",
version: "1.0",
type: "theme",
}, {
id: "theme2@tests.mozilla.org",
name: "theme 2",
version: "1.0",
type: "theme",
_userDisabled: true,
}, {
id: "theme3@tests.mozilla.org",
name: "theme 3",
version: "1.0",
type: "theme",
permissions: 0,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
},
{
id: "addon2@tests.mozilla.org",
name: "addon 2",
version: "1.0",
_userDisabled: true,
},
{
id: "theme1@tests.mozilla.org",
name: "theme 1",
version: "1.0",
type: "theme",
},
{
id: "theme2@tests.mozilla.org",
name: "theme 2",
version: "1.0",
type: "theme",
_userDisabled: true,
},
{
id: "theme3@tests.mozilla.org",
name: "theme 3",
version: "1.0",
type: "theme",
permissions: 0,
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
gContextMenu = aWindow.document.getElementById("addonitem-popup");
});
function check_contextmenu(
aIsTheme,
aIsEnabled,
aIsRemote,
aIsDetails,
aIsSingleItemCase
) {
if (aIsTheme || aIsEnabled || aIsRemote) {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_enableItem"),
"'Enable' should be hidden"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_enableItem"),
"'Enable' should be visible"
);
}
function check_contextmenu(aIsTheme, aIsEnabled, aIsRemote, aIsDetails, aIsSingleItemCase) {
if (aIsTheme || aIsEnabled || aIsRemote)
is_element_hidden(gManagerWindow.document.getElementById("menuitem_enableItem"),
"'Enable' should be hidden");
else
is_element_visible(gManagerWindow.document.getElementById("menuitem_enableItem"),
"'Enable' should be visible");
if (aIsTheme || !aIsEnabled || aIsRemote) {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_disableItem"),
"'Disable' should be hidden"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_disableItem"),
"'Disable' should be visible"
);
}
if (aIsTheme || !aIsEnabled || aIsRemote)
is_element_hidden(gManagerWindow.document.getElementById("menuitem_disableItem"),
"'Disable' should be hidden");
else
is_element_visible(gManagerWindow.document.getElementById("menuitem_disableItem"),
"'Disable' should be visible");
if (!aIsTheme || aIsEnabled || aIsRemote || aIsSingleItemCase) {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_enableTheme"),
"'Wear Theme' should be hidden"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_enableTheme"),
"'Wear Theme' should be visible"
);
}
if (!aIsTheme || aIsEnabled || aIsRemote || aIsSingleItemCase)
is_element_hidden(gManagerWindow.document.getElementById("menuitem_enableTheme"),
"'Wear Theme' should be hidden");
else
is_element_visible(gManagerWindow.document.getElementById("menuitem_enableTheme"),
"'Wear Theme' should be visible");
if (!aIsTheme || !aIsEnabled || aIsRemote || aIsSingleItemCase) {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_disableTheme"),
"'Stop Wearing Theme' should be hidden"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_disableTheme"),
"'Stop Wearing Theme' should be visible"
);
}
if (!aIsTheme || !aIsEnabled || aIsRemote || aIsSingleItemCase)
is_element_hidden(gManagerWindow.document.getElementById("menuitem_disableTheme"),
"'Stop Wearing Theme' should be hidden");
else
is_element_visible(gManagerWindow.document.getElementById("menuitem_disableTheme"),
"'Stop Wearing Theme' should be visible");
if (aIsRemote) {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_installItem"),
"'Install' should be visible"
);
} else {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_installItem"),
"'Install' should be hidden"
);
}
if (aIsRemote)
is_element_visible(gManagerWindow.document.getElementById("menuitem_installItem"),
"'Install' should be visible");
else
is_element_hidden(gManagerWindow.document.getElementById("menuitem_installItem"),
"'Install' should be hidden");
if (aIsDetails) {
is_element_hidden(
gManagerWindow.document.getElementById("menuitem_showDetails"),
"'Show More Information' should be hidden in details view"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("menuitem_showDetails"),
"'Show More Information' should be visible in list view"
);
}
if (aIsDetails)
is_element_hidden(gManagerWindow.document.getElementById("menuitem_showDetails"),
"'Show More Information' should be hidden in details view");
else
is_element_visible(gManagerWindow.document.getElementById("menuitem_showDetails"),
"'Show More Information' should be visible in list view");
if (aIsSingleItemCase)
is_element_hidden(gManagerWindow.document.getElementById("addonitem-menuseparator"),
"Menu separator should be hidden with only one menu item");
else
is_element_visible(gManagerWindow.document.getElementById("addonitem-menuseparator"),
"Menu separator should be visible with multiple menu items");
if (aIsSingleItemCase) {
is_element_hidden(
gManagerWindow.document.getElementById("addonitem-menuseparator"),
"Menu separator should be hidden with only one menu item"
);
} else {
is_element_visible(
gManagerWindow.document.getElementById("addonitem-menuseparator"),
"Menu separator should be visible with multiple menu items"
);
}
}
add_test(function() {
var el = get_addon_element(gManagerWindow, "addon1@tests.mozilla.org");
isnot(el, null, "Should have found addon element");
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, true, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, true, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on enabled extension item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
@ -122,17 +174,27 @@ add_test(async function() {
isnot(el, null, "Should have found addon element");
await el.mAddon.disable();
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, false, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, false, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on newly disabled extension item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
@ -140,156 +202,251 @@ add_test(async function() {
isnot(el, null, "Should have found addon element");
await el.mAddon.enable();
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, true, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, true, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on newly enabled extension item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(function() {
var el = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, false, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, false, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on disabled extension item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://list/theme");
await wait_for_view_load(gManagerWindow);
var el = get_addon_element(gManagerWindow, "theme1@tests.mozilla.org");
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(true, true, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(true, true, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on enabled theme item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(function() {
var el = get_addon_element(gManagerWindow, "theme2@tests.mozilla.org");
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(true, false, false, false, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(true, false, false, false, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on disabled theme item");
el.parentNode.ensureElementIsVisible(el);
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://detail/addon1@tests.mozilla.org");
await wait_for_view_load(gManagerWindow);
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, true, false, true, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, true, false, true, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on enabled extension, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
var el = gManagerWindow.document.querySelector(
"#detail-view .detail-view-container"
);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://detail/addon2@tests.mozilla.org");
await wait_for_view_load(gManagerWindow);
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(false, false, false, true, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(false, false, false, true, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on disabled extension, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
var el = gManagerWindow.document.querySelector(
"#detail-view .detail-view-container"
);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://detail/theme1@tests.mozilla.org");
await wait_for_view_load(gManagerWindow);
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(true, true, false, true, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(true, true, false, true, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on enabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
var el = gManagerWindow.document.querySelector(
"#detail-view .detail-view-container"
);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://detail/theme2@tests.mozilla.org");
await wait_for_view_load(gManagerWindow);
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(true, false, false, true, false);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(true, false, false, true, false);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu on disabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
var el = gManagerWindow.document.querySelector(
"#detail-view .detail-view-container"
);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_test(async function() {
gManagerWindow.loadView("addons://detail/theme3@tests.mozilla.org");
await wait_for_view_load(gManagerWindow);
gContextMenu.addEventListener("popupshown", function() {
check_contextmenu(true, true, false, true, true);
gContextMenu.addEventListener(
"popupshown",
function() {
check_contextmenu(true, true, false, true, true);
gContextMenu.hidePopup();
run_next_test();
}, {once: true});
gContextMenu.hidePopup();
run_next_test();
},
{ once: true }
);
info("Opening context menu with single menu item on enabled theme, in detail view");
var el = gManagerWindow.document.querySelector("#detail-view .detail-view-container");
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
info(
"Opening context menu with single menu item on enabled theme, in detail view"
);
var el = gManagerWindow.document.querySelector(
"#detail-view .detail-view-container"
);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
add_task(function end_test() {
return close_manager(gManagerWindow, finish);
});

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

@ -38,12 +38,14 @@ SpecialPowers.pushPrefEnv({
async function test() {
waitForExplicitFinish();
gProvider = new MockProvider(true, [{
id: "mock-addon",
name: "Mock Add-ons",
uiPriority: 4500,
flags: AddonManager.TYPE_UI_VIEW_LIST,
}]);
gProvider = new MockProvider(true, [
{
id: "mock-addon",
name: "Mock Add-ons",
uiPriority: 4500,
flags: AddonManager.TYPE_UI_VIEW_LIST,
},
]);
let aWindow = await open_manager(VIEW_ID);
gManagerWindow = aWindow;
@ -64,18 +66,25 @@ function check_list(aItem) {
// Check state of the empty notice
let emptyNotice = gManagerWindow.document.getElementById(EMPTY_ID);
ok(emptyNotice != null, "Should have found the empty notice");
is(!emptyNotice.hidden, (aItem == null), "Empty notice should be showing if list empty");
is(
!emptyNotice.hidden,
aItem == null,
"Empty notice should be showing if list empty"
);
// Check the children of the list
let list = gManagerWindow.document.getElementById(LIST_ID);
is(list.childNodes.length, aItem ? 1 : 0, "Should get expected number of items in list");
is(
list.childNodes.length,
aItem ? 1 : 0,
"Should get expected number of items in list"
);
if (aItem != null) {
let itemName = list.firstChild.getAttribute("name");
is(itemName, aItem.name, "List item should have correct name");
}
}
// Test that the empty notice is showing and no items are showing in list
add_test(function() {
check_list(null);
@ -160,4 +169,3 @@ add_test(async function() {
check_list(gItem);
run_next_test();
});

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

@ -26,7 +26,11 @@ function end_test() {
add_test(async function() {
let aManager = await open_manager("addons://detail/foo");
gCategoryUtilities = new CategoryUtilities(aManager);
is(gCategoryUtilities.selectedCategory, "discover", "Should fall back to the discovery pane");
is(
gCategoryUtilities.selectedCategory,
"discover",
"Should fall back to the discovery pane"
);
close_manager(aManager, run_next_test);
});
@ -34,15 +38,21 @@ add_test(async function() {
// Also test that opening directly to an add-on that does exist doesn't break
// and selects the right category
add_test(async function() {
new MockProvider().createAddons([{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
}]);
new MockProvider().createAddons([
{
id: "addon1@tests.mozilla.org",
name: "addon 1",
version: "1.0",
},
]);
let aManager = await open_manager("addons://detail/addon1@tests.mozilla.org");
gCategoryUtilities = new CategoryUtilities(aManager);
is(gCategoryUtilities.selectedCategory, "extension", "Should have selected the right category");
is(
gCategoryUtilities.selectedCategory,
"extension",
"Should have selected the right category"
);
close_manager(aManager, run_next_test);
});

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

@ -17,8 +17,11 @@ async function test() {
Services.prefs.clearUserPref(PREF_UI_LASTCATEGORY);
aWindow = await open_manager(null);
is(new CategoryUtilities(aWindow).selectedCategory, "discover",
"Should have loaded the right view");
is(
new CategoryUtilities(aWindow).selectedCategory,
"discover",
"Should have loaded the right view"
);
close_manager(aWindow, finish);
}

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

@ -8,7 +8,10 @@ async function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
ok(AddonManager.strictCompatibility, "Strict compatibility should be enabled");
ok(
AddonManager.strictCompatibility,
"Strict compatibility should be enabled"
);
let aAddons = await AddonManager.getAllAddons();
aAddons.sort(function compareTypeName(a, b) {
@ -18,10 +21,14 @@ async function test() {
let allCompatible = true;
for (let a of aAddons) {
// Ignore plugins.
if (a.type == "plugin" || a.id == "workerbootstrap-test@mozilla.org")
if (a.type == "plugin" || a.id == "workerbootstrap-test@mozilla.org") {
continue;
}
ok(a.isCompatible, a.type + " " + a.name + " " + a.version + " should be compatible");
ok(
a.isCompatible,
a.type + " " + a.name + " " + a.version + " should be compatible"
);
allCompatible = allCompatible && a.isCompatible;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -23,23 +23,29 @@ SpecialPowers.pushPrefEnv({
var gProgressListener = {
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
// Only care about the network stop status events
if (!(aStateFlags & (Ci.nsIWebProgressListener.STATE_IS_NETWORK)) ||
!(aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP)))
if (
!(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) ||
!(aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
) {
return;
}
if (gLoadCompleteCallback)
if (gLoadCompleteCallback) {
executeSoon(gLoadCompleteCallback);
}
gLoadCompleteCallback = null;
},
onLocationChange() { },
onSecurityChange() { },
onProgressChange() { },
onStatusChange() { },
onContentBlockingEvent() { },
onLocationChange() {},
onSecurityChange() {},
onProgressChange() {},
onStatusChange() {},
onContentBlockingEvent() {},
QueryInterface: ChromeUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
QueryInterface: ChromeUtils.generateQI([
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference,
]),
};
function test() {
@ -52,23 +58,26 @@ function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "Test add-on 1",
type: "extension",
version: "2.2",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
userDisabled: false,
}, {
id: "addon3@tests.mozilla.org",
name: "Test add-on 3",
type: "theme",
version: "1.2b1",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
userDisabled: true,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "Test add-on 1",
type: "extension",
version: "2.2",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_SOFTBLOCKED,
userDisabled: false,
},
{
id: "addon3@tests.mozilla.org",
name: "Test add-on 3",
type: "theme",
version: "1.2b1",
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
userDisabled: true,
},
]);
run_next_test();
}
@ -78,26 +87,34 @@ function end_test() {
}
function getURL(aBrowser) {
if (gManagerWindow.document.getElementById("discover-view").selectedPanel !=
aBrowser)
if (
gManagerWindow.document.getElementById("discover-view").selectedPanel !=
aBrowser
) {
return null;
}
var url = aBrowser.currentURI.spec;
var pos = url.indexOf("#");
if (pos != -1)
if (pos != -1) {
return url.substring(0, pos);
}
return url;
}
function getHash(aBrowser) {
if (gManagerWindow.document.getElementById("discover-view").selectedPanel !=
aBrowser)
if (
gManagerWindow.document.getElementById("discover-view").selectedPanel !=
aBrowser
) {
return null;
}
var url = aBrowser.currentURI.spec;
var pos = url.indexOf("#");
if (pos != -1)
if (pos != -1) {
return decodeURIComponent(url.substring(pos + 1));
}
return null;
}
@ -113,14 +130,22 @@ async function testHash(aBrowser, aTestAddonVisible) {
is(typeof data, "object", "Hash should be a JS object");
// Ensure that at least the test add-ons are present
if (aTestAddonVisible[0])
if (aTestAddonVisible[0]) {
ok("addon1@tests.mozilla.org" in data, "Test add-on 1 should be listed");
else
ok(!("addon1@tests.mozilla.org" in data), "Test add-on 1 should not be listed");
if (aTestAddonVisible[1])
} else {
ok(
!("addon1@tests.mozilla.org" in data),
"Test add-on 1 should not be listed"
);
}
if (aTestAddonVisible[1]) {
ok("addon3@tests.mozilla.org" in data, "Test add-on 3 should be listed");
else
ok(!("addon3@tests.mozilla.org" in data), "Test add-on 3 should not be listed");
} else {
ok(
!("addon3@tests.mozilla.org" in data),
"Test add-on 3 should not be listed"
);
}
// Test against all the add-ons the manager knows about since plugins and
// app extensions may exist
@ -128,8 +153,9 @@ async function testHash(aBrowser, aTestAddonVisible) {
for (let addon of aAddons) {
if (!(addon.id in data)) {
// Test add-ons will have shown an error if necessary above
if (addon.id.substring(6) != "@tests.mozilla.org")
if (addon.id.substring(6) != "@tests.mozilla.org") {
ok(false, "Add-on " + addon.id + " was not included in the data");
}
continue;
}
@ -138,25 +164,42 @@ async function testHash(aBrowser, aTestAddonVisible) {
is(addonData.name, addon.name, "Name should be correct");
is(addonData.version, addon.version, "Version should be correct");
is(addonData.type, addon.type, "Type should be correct");
is(addonData.userDisabled, addon.userDisabled, "userDisabled should be correct");
is(addonData.isBlocklisted, addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED, "blocklisted should be correct");
is(addonData.isCompatible, addon.isCompatible, "isCompatible should be correct");
is(
addonData.userDisabled,
addon.userDisabled,
"userDisabled should be correct"
);
is(
addonData.isBlocklisted,
addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED,
"blocklisted should be correct"
);
is(
addonData.isCompatible,
addon.isCompatible,
"isCompatible should be correct"
);
}
}
function isLoading() {
var loading = gManagerWindow.document.getElementById("discover-view").selectedPanel ==
gManagerWindow.document.getElementById("discover-loading");
var loading =
gManagerWindow.document.getElementById("discover-view").selectedPanel ==
gManagerWindow.document.getElementById("discover-loading");
if (loading) {
is_element_visible(gManagerWindow.document.querySelector("#discover-loading .loading"),
"Loading message should be visible when its panel is the selected panel");
is_element_visible(
gManagerWindow.document.querySelector("#discover-loading .loading"),
"Loading message should be visible when its panel is the selected panel"
);
}
return loading;
}
function isError() {
return gManagerWindow.document.getElementById("discover-view").selectedPanel ==
gManagerWindow.document.getElementById("discover-error");
return (
gManagerWindow.document.getElementById("discover-view").selectedPanel ==
gManagerWindow.document.getElementById("discover-error")
);
}
function clickLink(aId, aCallback) {
@ -170,7 +213,7 @@ function clickLink(aId, aCallback) {
};
var link = browser.contentDocument.getElementById(aId);
EventUtils.sendMouseEvent({type: "click"}, link);
EventUtils.sendMouseEvent({ type: "click" }, link);
executeSoon(function() {
ok(isLoading(), "Clicking a link should show the loading pane");
@ -200,13 +243,20 @@ add_test(async function() {
// selected view displays the right url
add_test(async function() {
// Hide one of the test add-ons
Services.prefs.setBoolPref("extensions.addon3@tests.mozilla.org.getAddons.cache.enabled", false);
Services.prefs.setBoolPref(
"extensions.addon3@tests.mozilla.org.getAddons.cache.enabled",
false
);
await open_manager(null, null, function(aWindow) {
gManagerWindow = aWindow;
ok(isLoading(), "Should be loading at first");
});
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
is(
gCategoryUtilities.selectedCategory,
"discover",
"Should have loaded the right view"
);
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
@ -218,7 +268,9 @@ add_test(async function() {
// Tests that loading the add-ons manager with the discovery view as the initial
// view displays the right url
add_test(async function() {
Services.prefs.clearUserPref("extensions.addon3@tests.mozilla.org.getAddons.cache.enabled");
Services.prefs.clearUserPref(
"extensions.addon3@tests.mozilla.org.getAddons.cache.enabled"
);
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
@ -229,7 +281,11 @@ add_test(async function() {
ok(isLoading(), "Should be loading at first");
});
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
is(
gCategoryUtilities.selectedCategory,
"discover",
"Should have loaded the right view"
);
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
@ -261,7 +317,11 @@ add_test(async function() {
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
is(
gCategoryUtilities.selectedCategory,
"discover",
"Should have loaded the right view"
);
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
@ -281,7 +341,11 @@ add_test(async function() {
aWindow = await open_manager("addons://discover/");
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
is(gCategoryUtilities.selectedCategory, "discover", "Should have loaded the right view");
is(
gCategoryUtilities.selectedCategory,
"discover",
"Should have loaded the right view"
);
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
@ -357,7 +421,11 @@ add_test(async function() {
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
await clickLink("link-good");
is(getURL(browser), "https://example.com/" + RELATIVE_DIR + "releaseNotes.xhtml", "Should have loaded the right url");
is(
getURL(browser),
"https://example.com/" + RELATIVE_DIR + "releaseNotes.xhtml",
"Should have loaded the right url"
);
await gCategoryUtilities.openType("extension");
await gCategoryUtilities.openType("discover");
@ -378,10 +446,11 @@ add_test(async function() {
var count = 10;
function clickAgain(aCallback) {
if (count-- == 0)
if (count-- == 0) {
aCallback();
else
} else {
clickLink("link-normal", clickAgain.bind(null, aCallback));
}
}
clickAgain(async function() {
@ -408,13 +477,21 @@ add_test(async function() {
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), TESTROOT + "discovery.html", "Should have loaded the right url");
is(
getURL(browser),
TESTROOT + "discovery.html",
"Should have loaded the right url"
);
await clickLink("link-normal");
is(getURL(browser), MAIN_URL, "Should have loaded the right url");
await clickLink("link-http");
is(getURL(browser), TESTROOT + "discovery.html", "Should have loaded the right url");
is(
getURL(browser),
TESTROOT + "discovery.html",
"Should have loaded the right url"
);
close_manager(gManagerWindow, run_next_test);
});
@ -430,28 +507,40 @@ add_test(async function() {
var browser = gManagerWindow.document.getElementById("discover-browser");
EventUtils.synthesizeMouse(gCategoryUtilities.get("discover"), 2, 2, { }, gManagerWindow);
EventUtils.synthesizeMouse(
gCategoryUtilities.get("discover"),
2,
2,
{},
gManagerWindow
);
// Do this after wait_for_view_load has had a chance to setup its
// listeners.
executeSoon(() => {
ok(isLoading(), "Should be loading");
// This will actually stop the about:blank load
browser.stop();
ok(isLoading(), "Should be loading");
// This will actually stop the about:blank load
browser.stop();
});
await wait_for_view_load(gManagerWindow);
ok(isError(), "Should have shown the error page");
await gCategoryUtilities.openType("extension");
EventUtils.synthesizeMouse(gCategoryUtilities.get("discover"), 2, 2, { }, gManagerWindow);
EventUtils.synthesizeMouse(
gCategoryUtilities.get("discover"),
2,
2,
{},
gManagerWindow
);
// Do this after wait_for_view_load has had a chance to setup its
// listeners.
executeSoon(() => {
ok(isLoading(), "Should be loading");
// This will actually stop the about:blank load
browser.stop();
ok(isLoading(), "Should be loading");
// This will actually stop the about:blank load
browser.stop();
});
await wait_for_view_load(gManagerWindow);
@ -482,43 +571,68 @@ add_test(async function() {
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), url, "Should be able to load the chrome XUL file a second time");
is(
getURL(browser),
url,
"Should be able to load the chrome XUL file a second time"
);
close_manager(gManagerWindow, run_next_test);
});
// Bug 711693 - Send the compatibility mode when loading the Discovery pane
add_test(async function() {
info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'normal'");
Services.prefs.setCharPref(PREF_DISCOVERURL, MAIN_URL + "?mode=%COMPATIBILITY_MODE%");
info(
"Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'normal'"
);
Services.prefs.setCharPref(
PREF_DISCOVERURL,
MAIN_URL + "?mode=%COMPATIBILITY_MODE%"
);
Services.prefs.setBoolPref(PREF_STRICT_COMPAT, false);
let aWindow = await open_manager("addons://discover/");
gManagerWindow = aWindow;
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL + "?mode=normal", "Should have loaded the right url");
is(
getURL(browser),
MAIN_URL + "?mode=normal",
"Should have loaded the right url"
);
close_manager(gManagerWindow, run_next_test);
});
add_test(async function() {
info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'strict'");
info(
"Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'strict'"
);
Services.prefs.setBoolPref(PREF_STRICT_COMPAT, true);
let aWindow = await open_manager("addons://discover/");
gManagerWindow = aWindow;
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL + "?mode=strict", "Should have loaded the right url");
is(
getURL(browser),
MAIN_URL + "?mode=strict",
"Should have loaded the right url"
);
close_manager(gManagerWindow, run_next_test);
});
add_test(async function() {
info("Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'ignore'");
info(
"Test '%COMPATIBILITY_MODE%' in the URL is correctly replaced by 'ignore'"
);
Services.prefs.setBoolPref(PREF_CHECK_COMPATIBILITY, false);
let aWindow = await open_manager("addons://discover/");
gManagerWindow = aWindow;
var browser = gManagerWindow.document.getElementById("discover-browser");
is(getURL(browser), MAIN_URL + "?mode=ignore", "Should have loaded the right url");
is(
getURL(browser),
MAIN_URL + "?mode=ignore",
"Should have loaded the right url"
);
close_manager(gManagerWindow, run_next_test);
});
@ -528,18 +642,32 @@ async function bug_601442_test_elements(visible) {
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
if (visible)
ok(gCategoryUtilities.isTypeVisible("discover"), "Discover category should be visible");
else
ok(!gCategoryUtilities.isTypeVisible("discover"), "Discover category should not be visible");
if (visible) {
ok(
gCategoryUtilities.isTypeVisible("discover"),
"Discover category should be visible"
);
} else {
ok(
!gCategoryUtilities.isTypeVisible("discover"),
"Discover category should not be visible"
);
}
gManagerWindow.loadView("addons://list/dictionary");
let aManager = await wait_for_view_load(gManagerWindow);
var button = aManager.document.getElementById("discover-button-install");
if (visible)
ok(!BrowserTestUtils.is_hidden(button), "Discover button should be visible!");
else
ok(BrowserTestUtils.is_hidden(button), "Discover button should not be visible!");
if (visible) {
ok(
!BrowserTestUtils.is_hidden(button),
"Discover button should be visible!"
);
} else {
ok(
BrowserTestUtils.is_hidden(button),
"Discover button should not be visible!"
);
}
close_manager(gManagerWindow, run_next_test);
}
@ -574,7 +702,11 @@ add_test(async function() {
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
is(gCategoryUtilities.selectedCategory, "extension", "Should be showing the extension view");
is(
gCategoryUtilities.selectedCategory,
"extension",
"Should be showing the extension view"
);
close_manager(gManagerWindow, run_next_test);
Services.prefs.clearUserPref(PREF_DISCOVER_ENABLED);
});

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

@ -1,6 +1,6 @@
"use strict";
const {ClientID} = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
const { ClientID } = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
const MAIN_URL = "https://example.com/" + RELATIVE_DIR + "discovery.html";
@ -36,18 +36,28 @@ function waitForHeader() {
}
add_task(async function setup() {
SpecialPowers.pushPrefEnv({"set": [
[PREF_DISCOVERURL, MAIN_URL],
["datareporting.healthreport.uploadEnabled", true],
["browser.discovery.enabled", true],
]});
SpecialPowers.pushPrefEnv({
set: [
[PREF_DISCOVERURL, MAIN_URL],
["datareporting.healthreport.uploadEnabled", true],
["browser.discovery.enabled", true],
],
});
});
add_task(async function test_no_private_clientid() {
let privateWindow = await BrowserTestUtils.openNewBrowserWindow({private: true});
let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
let [header, manager] = await Promise.all([
waitForHeader(),
open_manager("addons://discover/", undefined, undefined, undefined, privateWindow),
open_manager(
"addons://discover/",
undefined,
undefined,
undefined,
privateWindow
),
]);
ok(PrivateBrowsingUtils.isContentWindowPrivate(manager), "window is private");
is(header, null, "header was not set");

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

@ -12,20 +12,33 @@
// we only need EventUtils.js for a few files which is why we are using loadSubScript.
var gManagerWindow;
var EventUtils = {};
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
Services.scriptloader.loadSubScript(
"chrome://mochikit/content/tests/SimpleTest/EventUtils.js",
EventUtils
);
async function checkInstallConfirmation(...names) {
let notificationCount = 0;
let observer = {
observe(aSubject, aTopic, aData) {
let installInfo = aSubject.wrappedJSObject;
isnot(installInfo.browser, null, "Notification should have non-null browser");
isnot(
installInfo.browser,
null,
"Notification should have non-null browser"
);
is(installInfo.installs.length, 1, "Got one AddonInstall instance as expected");
is(
installInfo.installs.length,
1,
"Got one AddonInstall instance as expected"
);
Assert.deepEqual(installInfo.installs[0].installTelemetryInfo,
{source: "about:addons", method: "drag-and-drop"},
"Got the expected installTelemetryInfo");
Assert.deepEqual(
installInfo.installs[0].installTelemetryInfo,
{ source: "about:addons", method: "drag-and-drop" },
"Got the expected installTelemetryInfo"
);
notificationCount++;
},
@ -42,7 +55,9 @@ async function checkInstallConfirmation(...names) {
info(`Saw install for ${name}`);
if (results.length < names.length) {
info(`Waiting for installs for ${names.filter(n => !results.includes(n))}`);
info(
`Waiting for installs for ${names.filter(n => !results.includes(n))}`
);
promise = promisePopupNotificationShown("addon-webext-permissions");
}
@ -51,7 +66,11 @@ async function checkInstallConfirmation(...names) {
Assert.deepEqual(results.sort(), names.sort(), "Got expected installs");
is(notificationCount, names.length, `Saw ${names.length} addon-install-started notification`);
is(
notificationCount,
names.length,
`Saw ${names.length} addon-install-started notification`
);
Services.obs.removeObserver(observer, "addon-install-started");
}
@ -68,9 +87,13 @@ add_task(async function test_drop_url() {
gManagerWindow = await open_manager("addons://list/extension");
let promise = checkInstallConfirmation("Drag Drop test 1");
let viewContainer = getViewContainer(gManagerWindow);
let effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "text/x-moz-url", data: url}]],
"copy", gManagerWindow);
let effect = EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[[{ type: "text/x-moz-url", data: url }]],
"copy",
gManagerWindow
);
is(effect, "copy", "Drag should be accepted");
await promise;
await close_manager(gManagerWindow);
@ -83,9 +106,13 @@ add_task(async function test_drop_file() {
await wait_for_view_load(gManagerWindow);
let promise = checkInstallConfirmation("Drag Drop test 1");
let viewContainer = getViewContainer(gManagerWindow);
let effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "application/x-moz-file", data: fileurl.file}]],
"copy", gManagerWindow);
let effect = EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[[{ type: "application/x-moz-file", data: fileurl.file }]],
"copy",
gManagerWindow
);
is(effect, "copy", "Drag should be accepted");
await promise;
await close_manager(gManagerWindow);
@ -97,12 +124,21 @@ add_task(async function test_drop_multiple_urls() {
let url2 = TESTROOT2 + "addons/browser_dragdrop2.xpi";
gManagerWindow = await open_manager("addons://list/extension");
await wait_for_view_load(gManagerWindow);
let promise = checkInstallConfirmation("Drag Drop test 1", "Drag Drop test 2");
let promise = checkInstallConfirmation(
"Drag Drop test 1",
"Drag Drop test 2"
);
let viewContainer = getViewContainer(gManagerWindow);
let effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "text/x-moz-url", data: url1}],
[{type: "text/x-moz-url", data: url2}]],
"copy", gManagerWindow);
let effect = EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[
[{ type: "text/x-moz-url", data: url1 }],
[{ type: "text/x-moz-url", data: url2 }],
],
"copy",
gManagerWindow
);
is(effect, "copy", "Drag should be accepted");
await promise;
await close_manager(gManagerWindow);
@ -114,12 +150,21 @@ add_task(async function test_drop_multiple_files() {
let fileurl2 = get_addon_file_url("browser_dragdrop2.xpi");
gManagerWindow = await open_manager("addons://list/extension");
await wait_for_view_load(gManagerWindow);
let promise = checkInstallConfirmation("Drag Drop test 1", "Drag Drop test 2");
let promise = checkInstallConfirmation(
"Drag Drop test 1",
"Drag Drop test 2"
);
let viewContainer = getViewContainer(gManagerWindow);
let effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "application/x-moz-file", data: fileurl1.file}],
[{type: "application/x-moz-file", data: fileurl2.file}]],
"copy", gManagerWindow);
let effect = EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[
[{ type: "application/x-moz-file", data: fileurl1.file }],
[{ type: "application/x-moz-file", data: fileurl2.file }],
],
"copy",
gManagerWindow
);
is(effect, "copy", "Drag should be accepted");
await promise;
await close_manager(gManagerWindow);
@ -131,12 +176,21 @@ add_task(async function test_drop_file_and_url() {
let fileurl = get_addon_file_url("browser_dragdrop2.xpi");
gManagerWindow = await open_manager("addons://list/extension");
await wait_for_view_load(gManagerWindow);
let promise = checkInstallConfirmation("Drag Drop test 1", "Drag Drop test 2");
let promise = checkInstallConfirmation(
"Drag Drop test 1",
"Drag Drop test 2"
);
let viewContainer = getViewContainer(gManagerWindow);
let effect = EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "text/x-moz-url", data: url}],
[{type: "application/x-moz-file", data: fileurl.file}]],
"copy", gManagerWindow);
let effect = EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[
[{ type: "text/x-moz-url", data: url }],
[{ type: "application/x-moz-file", data: fileurl.file }],
],
"copy",
gManagerWindow
);
is(effect, "copy", "Drag should be accepted");
await promise;
await close_manager(gManagerWindow);
@ -152,9 +206,13 @@ add_task(async function test_drop_incompat_file() {
let url = `${TESTROOT}/addons/browser_dragdrop_incompat.xpi`;
let viewContainer = getViewContainer(gManagerWindow);
EventUtils.synthesizeDrop(viewContainer, viewContainer,
[[{type: "text/x-moz-url", data: url}]],
"copy", gManagerWindow);
EventUtils.synthesizeDrop(
viewContainer,
viewContainer,
[[{ type: "text/x-moz-url", data: url }]],
"copy",
gManagerWindow
);
await errorPromise;
ok(true, "Got addon-install-failed event");

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

@ -3,9 +3,11 @@
*/
/*
* Test Permission Popup for Sideloaded Extensions.
*/
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
* Test Permission Popup for Sideloaded Extensions.
*/
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
const ADDON_ID = "addon1@test.mozilla.org";
AddonTestUtils.initMochitest(this);
@ -13,13 +15,26 @@ AddonTestUtils.initMochitest(this);
function assertDisabledSideloadedExtensionElement(managerWindow, addonElement) {
const doc = addonElement.ownerDocument;
if (managerWindow.useHtmlViews) {
const toggleDisabled = addonElement.querySelector('[action="toggle-disabled"]');
is(doc.l10n.getAttributes(toggleDisabled).id, "enable-addon-button",
"Addon toggle-disabled action has the enable label");
const toggleDisabled = addonElement.querySelector(
'[action="toggle-disabled"]'
);
is(
doc.l10n.getAttributes(toggleDisabled).id,
"enable-addon-button",
"Addon toggle-disabled action has the enable label"
);
} else {
let el = doc.getAnonymousElementByAttribute(addonElement, "anonid", "disable-btn");
let el = doc.getAnonymousElementByAttribute(
addonElement,
"anonid",
"disable-btn"
);
is_element_hidden(el, "Disable button not visible.");
el = doc.getAnonymousElementByAttribute(addonElement, "anonid", "enable-btn");
el = doc.getAnonymousElementByAttribute(
addonElement,
"anonid",
"enable-btn"
);
is_element_visible(el, "Enable button visible");
}
}
@ -27,13 +42,26 @@ function assertDisabledSideloadedExtensionElement(managerWindow, addonElement) {
function assertEnabledSideloadedExtensionElement(managerWindow, addonElement) {
const doc = addonElement.ownerDocument;
if (managerWindow.useHtmlViews) {
const toggleDisabled = addonElement.querySelector('[action="toggle-disabled"]');
is(doc.l10n.getAttributes(toggleDisabled).id, "enable-addon-button",
"Addon toggle-disabled action has the enable label");
const toggleDisabled = addonElement.querySelector(
'[action="toggle-disabled"]'
);
is(
doc.l10n.getAttributes(toggleDisabled).id,
"enable-addon-button",
"Addon toggle-disabled action has the enable label"
);
} else {
let el = doc.getAnonymousElementByAttribute(addonElement, "anonid", "disable-btn");
let el = doc.getAnonymousElementByAttribute(
addonElement,
"anonid",
"disable-btn"
);
is_element_hidden(el, "Disable button not visible.");
el = doc.getAnonymousElementByAttribute(addonElement, "anonid", "enable-btn");
el = doc.getAnonymousElementByAttribute(
addonElement,
"anonid",
"enable-btn"
);
is_element_visible(el, "Enable button visible");
}
}
@ -43,8 +71,12 @@ function clickEnableExtension(managerWindow, addonElement) {
addonElement.querySelector('[action="toggle-disabled"]').click();
} else {
const doc = addonElement.ownerDocument;
const el = doc.getAnonymousElementByAttribute(addonElement, "anonid", "enable-btn");
EventUtils.synthesizeMouseAtCenter(el, {clickCount: 1}, managerWindow);
const el = doc.getAnonymousElementByAttribute(
addonElement,
"anonid",
"enable-btn"
);
EventUtils.synthesizeMouseAtCenter(el, { clickCount: 1 }, managerWindow);
}
}
@ -60,17 +92,19 @@ async function test_sideloaded_extension_permissions_prompt() {
let options = {
manifest: {
applications: {gecko: {id: ADDON_ID}},
applications: { gecko: { id: ADDON_ID } },
name: "Test 1",
permissions: ["history", "https://*/*"],
icons: {"64": "foo-icon.png"},
icons: { "64": "foo-icon.png" },
},
};
let xpi = AddonTestUtils.createTempWebExtensionFile(options);
await AddonTestUtils.manuallyInstall(xpi);
let changePromise = new Promise(resolve => ExtensionsUI.once("change", resolve));
let changePromise = new Promise(resolve =>
ExtensionsUI.once("change", resolve)
);
ExtensionsUI._checkForSideloaded();
await changePromise;
@ -88,10 +122,16 @@ async function test_sideloaded_extension_permissions_prompt() {
ok(PopupNotifications.isPanelOpen, "Permission popup should be visible");
panel.secondaryButton.click();
ok(!PopupNotifications.isPanelOpen, "Permission popup should be closed / closing");
ok(
!PopupNotifications.isPanelOpen,
"Permission popup should be closed / closing"
);
addon = await AddonManager.getAddonByID(ADDON_ID);
ok(!addon.seen, "Seen flag should remain false after permissions are refused");
ok(
!addon.seen,
"Seen flag should remain false after permissions are refused"
);
// Test click event on permission accept option.
addon = get_addon_element(manager, ADDON_ID);
@ -105,10 +145,16 @@ async function test_sideloaded_extension_permissions_prompt() {
ok(PopupNotifications.isPanelOpen, "Permission popup should be visible");
let notificationPromise = acceptAppMenuNotificationWhenShown("addon-installed", ADDON_ID);
let notificationPromise = acceptAppMenuNotificationWhenShown(
"addon-installed",
ADDON_ID
);
panel.button.click();
ok(!PopupNotifications.isPanelOpen, "Permission popup should be closed / closing");
ok(
!PopupNotifications.isPanelOpen,
"Permission popup should be closed / closing"
);
await notificationPromise;
addon = await AddonManager.getAddonByID(ADDON_ID);

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

@ -13,8 +13,10 @@ function promiseInstallNotification(aBrowser) {
return;
}
let notification =
PopupNotifications.getNotification(ADDON_INSTALL_ID, aBrowser);
let notification = PopupNotifications.getNotification(
ADDON_INSTALL_ID,
aBrowser
);
if (!notification) {
return;
}
@ -30,16 +32,20 @@ function promiseInstallNotification(aBrowser) {
}
function waitForAnyNewTabAndInstallNotification() {
return new Promise((resolve) => {
gBrowser.tabContainer.addEventListener("TabOpen", function(openEvent) {
let newTab = openEvent.target;
resolve([newTab, promiseInstallNotification(newTab.linkedBrowser)]);
}, {once: true});
return new Promise(resolve => {
gBrowser.tabContainer.addEventListener(
"TabOpen",
function(openEvent) {
let newTab = openEvent.target;
resolve([newTab, promiseInstallNotification(newTab.linkedBrowser)]);
},
{ once: true }
);
});
}
function CheckBrowserInPid(browser, expectedPid, message) {
return ContentTask.spawn(browser, { expectedPid, message }, (arg) => {
return ContentTask.spawn(browser, { expectedPid, message }, arg => {
is(Services.appinfo.processID, arg.expectedPid, arg.message);
});
}
@ -57,27 +63,41 @@ async function testOpenedAndDraggedXPI(aBrowser) {
urlbar.focus();
EventUtils.synthesizeKey("KEY_Enter");
await promiseNotification;
await CheckBrowserInPid(aBrowser, browserPid,
"Check that browser has not switched process.");
await CheckBrowserInPid(
aBrowser,
browserPid,
"Check that browser has not switched process."
);
// No process switch for XPI file:// URI dragged to tab.
let tab = gBrowser.getTabForBrowser(aBrowser);
promiseNotification = promiseInstallNotification(aBrowser);
let effect = EventUtils.synthesizeDrop(tab, tab,
[[{type: "text/uri-list", data: fileurl1.spec}]],
"move");
let effect = EventUtils.synthesizeDrop(
tab,
tab,
[[{ type: "text/uri-list", data: fileurl1.spec }]],
"move"
);
is(effect, "move", "Drag should be accepted");
await promiseNotification;
await CheckBrowserInPid(aBrowser, browserPid,
"Check that browser has not switched process.");
await CheckBrowserInPid(
aBrowser,
browserPid,
"Check that browser has not switched process."
);
// No process switch for two XPI file:// URIs dragged to tab.
promiseNotification = promiseInstallNotification(aBrowser);
let promiseTabAndNotification = waitForAnyNewTabAndInstallNotification();
effect = EventUtils.synthesizeDrop(tab, tab,
[[{type: "text/uri-list", data: fileurl1.spec}],
[{type: "text/uri-list", data: fileurl2.spec}]],
"move");
effect = EventUtils.synthesizeDrop(
tab,
tab,
[
[{ type: "text/uri-list", data: fileurl1.spec }],
[{ type: "text/uri-list", data: fileurl2.spec }],
],
"move"
);
is(effect, "move", "Drag should be accepted");
let [newTab, newTabInstallNotification] = await promiseTabAndNotification;
await promiseNotification;
@ -86,12 +106,18 @@ async function testOpenedAndDraggedXPI(aBrowser) {
}
await newTabInstallNotification;
BrowserTestUtils.removeTab(newTab);
await CheckBrowserInPid(aBrowser, browserPid,
"Check that browser has not switched process.");
await CheckBrowserInPid(
aBrowser,
browserPid,
"Check that browser has not switched process."
);
}
// Test for bug 1175267.
add_task(async function() {
await BrowserTestUtils.withNewTab("http://example.com", testOpenedAndDraggedXPI);
await BrowserTestUtils.withNewTab(
"http://example.com",
testOpenedAndDraggedXPI
);
await BrowserTestUtils.withNewTab("about:robots", testOpenedAndDraggedXPI);
});

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

@ -10,7 +10,8 @@ async function loadDetail(aWindow, id) {
if (aWindow.useHtmlViews) {
let browser = await aWindow.getHtmlBrowser();
let card = browser.contentDocument.querySelector(
`addon-card[addon-id="${id}"]`);
`addon-card[addon-id="${id}"]`
);
EventUtils.synthesizeMouseAtCenter(card, {}, browser.contentWindow);
} else {
let card = aWindow.document.querySelector(`.addon.card[value="${id}"]`);
@ -27,7 +28,7 @@ async function checkCompatibility(hboxSelector, buttonSelector) {
let id = "test@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {applications: {gecko: {id}}},
manifest: { applications: { gecko: { id } } },
useAddonManager: "temporary",
});
await extension.startup();
@ -38,11 +39,23 @@ async function checkCompatibility(hboxSelector, buttonSelector) {
function checkMessage(visible) {
if (visible) {
is_element_visible(hbox, "Check Compatibility warning hbox should be visible");
is_element_visible(button, "Check Compatibility warning button should be visible");
is_element_visible(
hbox,
"Check Compatibility warning hbox should be visible"
);
is_element_visible(
button,
"Check Compatibility warning button should be visible"
);
} else {
is_element_hidden(hbox, "Check Compatibility warning hbox should be hidden");
is_element_hidden(button, "Check Compatibility warning button should be hidden");
is_element_hidden(
hbox,
"Check Compatibility warning hbox should be hidden"
);
is_element_hidden(
button,
"Check Compatibility warning button should be hidden"
);
}
}
@ -63,8 +76,12 @@ async function checkCompatibility(hboxSelector, buttonSelector) {
// Check the button works.
info("Clicking 'Enable' button");
EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
is(AddonManager.checkCompatibility, true, "Check Compatibility pref should be cleared");
EventUtils.synthesizeMouse(button, 2, 2, {}, aWindow);
is(
AddonManager.checkCompatibility,
true,
"Check Compatibility pref should be cleared"
);
checkMessage(false);
await close_manager(aWindow);
@ -80,7 +97,7 @@ async function checkSecurity(hboxSelector, buttonSelector) {
let id = "test-security@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {applications: {gecko: {id}}},
manifest: { applications: { gecko: { id } } },
useAddonManager: "temporary",
});
await extension.startup();
@ -91,11 +108,23 @@ async function checkSecurity(hboxSelector, buttonSelector) {
function checkMessage(visible) {
if (visible) {
is_element_visible(hbox, "Check Update Security warning hbox should be visible");
is_element_visible(button, "Check Update Security warning button should be visible");
is_element_visible(
hbox,
"Check Update Security warning hbox should be visible"
);
is_element_visible(
button,
"Check Update Security warning button should be visible"
);
} else {
is_element_hidden(hbox, "Check Update Security warning hbox should be hidden");
is_element_hidden(button, "Check Update Security warning button should be hidden");
is_element_hidden(
hbox,
"Check Update Security warning hbox should be hidden"
);
is_element_hidden(
button,
"Check Update Security warning button should be hidden"
);
}
}
@ -116,8 +145,12 @@ async function checkSecurity(hboxSelector, buttonSelector) {
// Check the button works.
info("Clicking 'Enable' button");
EventUtils.synthesizeMouse(button, 2, 2, { }, aWindow);
is(Services.prefs.prefHasUserValue(pref), false, "Check Update Security pref should be cleared");
EventUtils.synthesizeMouse(button, 2, 2, {}, aWindow);
is(
Services.prefs.prefHasUserValue(pref),
false,
"Check Update Security pref should be cleared"
);
checkMessage(false);
await close_manager(aWindow);
@ -129,7 +162,7 @@ async function checkSafeMode(hboxSelector) {
let id = "test-safemode@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {applications: {gecko: {id}}},
manifest: { applications: { gecko: { id } } },
useAddonManager: "temporary",
});
await extension.startup();
@ -139,7 +172,10 @@ async function checkSafeMode(hboxSelector) {
function checkMessage(visible) {
if (visible) {
is_element_visible(hbox, "Check safe mode warning hbox should be visible");
is_element_visible(
hbox,
"Check safe mode warning hbox should be visible"
);
} else {
is_element_hidden(hbox, "Check safe mode warning hbox should be hidden");
}
@ -149,7 +185,9 @@ async function checkSafeMode(hboxSelector) {
checkMessage(false);
// Mock safe mode by setting the page attribute.
aWindow.document.getElementById("addons-page").setAttribute("warning", "safemode");
aWindow.document
.getElementById("addons-page")
.setAttribute("warning", "safemode");
// Check detail view.
await loadDetail(aWindow, id);
@ -172,7 +210,8 @@ add_task(async function testCompatCheckXUL() {
});
await checkCompatibility(
"#list-view hbox.global-warning-checkcompatibility",
"#list-view button.global-warning-checkcompatibility");
"#list-view button.global-warning-checkcompatibility"
);
// No popPrefEnv because of bug 1557397.
});
@ -182,7 +221,8 @@ add_task(async function testCompatCheckHTML() {
});
await checkCompatibility(
"#html-view .global-warning-checkcompatibility",
"#html-view .global-warning-checkcompatibility button");
"#html-view .global-warning-checkcompatibility button"
);
// No popPrefEnv because of bug 1557397.
});
@ -192,7 +232,8 @@ add_task(async function testSecurityCheckXUL() {
});
await checkSecurity(
"#list-view hbox.global-warning-updatesecurity",
"#list-view button.global-warning-updatesecurity");
"#list-view button.global-warning-updatesecurity"
);
// No popPrefEnv because of bug 1557397.
});
@ -202,7 +243,8 @@ add_task(async function testSecurityCheckHTML() {
});
await checkSecurity(
"#html-view .global-warning-updatesecurity",
"#html-view .global-warning-updatesecurity button");
"#html-view .global-warning-updatesecurity button"
);
// No popPrefEnv because of bug 1557397.
});

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

@ -5,8 +5,13 @@
"use strict";
ChromeUtils.import("resource://gre/modules/Promise.jsm", this);
const {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
var GMPScope = ChromeUtils.import("resource://gre/modules/addons/GMPProvider.jsm", null);
const { AppConstants } = ChromeUtils.import(
"resource://gre/modules/AppConstants.jsm"
);
var GMPScope = ChromeUtils.import(
"resource://gre/modules/addons/GMPProvider.jsm",
null
);
const TEST_DATE = new Date(2013, 0, 1, 12);
@ -17,10 +22,12 @@ var gMockAddons = [];
for (let plugin of GMPScope.GMP_PLUGINS) {
let mockAddon = Object.freeze({
id: plugin.id,
isValid: true,
isInstalled: false,
isEME: !!(plugin.id == "gmp-widevinecdm" || plugin.id.indexOf("gmp-eme-") == 0),
id: plugin.id,
isValid: true,
isInstalled: false,
isEME: !!(
plugin.id == "gmp-widevinecdm" || plugin.id.indexOf("gmp-eme-") == 0
),
});
gMockAddons.push(mockAddon);
}
@ -30,14 +37,14 @@ var gInstallDeferred = null;
var gPrefs = Services.prefs;
var getKey = GMPScope.GMPPrefs.getPrefKey;
function MockGMPInstallManager() {
}
function MockGMPInstallManager() {}
MockGMPInstallManager.prototype = {
checkForAddons: () => Promise.resolve({
usedFallback: true,
gmpAddons: gMockAddons,
}),
checkForAddons: () =>
Promise.resolve({
usedFallback: true,
gmpAddons: gMockAddons,
}),
installAddon: addon => {
gInstalledAddonId = addon.id;
@ -49,9 +56,17 @@ MockGMPInstallManager.prototype = {
function openDetailsView(aId) {
let view = get_current_view(gManagerWindow);
if (gManagerWindow.useHtmlViews) {
Assert.equal(view.id, "html-view", "Should be in the list view to use this function");
Assert.equal(
view.id,
"html-view",
"Should be in the list view to use this function"
);
} else {
Assert.equal(view.id, "list-view", "Should be in the list view to use this function");
Assert.equal(
view.id,
"list-view",
"Should be in the list view to use this function"
);
}
let item = get_addon_element(gManagerWindow, aId);
@ -67,7 +82,7 @@ function openDetailsView(aId) {
});
}
async function initializeState({useHtmlViews}) {
async function initializeState({ useHtmlViews }) {
await SpecialPowers.pushPrefEnv({
set: [["extensions.htmlaboutaddons.enabled", useHtmlViews]],
});
@ -81,12 +96,24 @@ async function initializeState({useHtmlViews}) {
registerCleanupFunction(async function() {
for (let addon of gMockAddons) {
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id));
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id));
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id));
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id));
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id));
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id)
);
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id)
);
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id)
);
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id)
);
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id)
);
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id)
);
}
gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_LOGGING_DUMP);
gPrefs.clearUserPref(GMPScope.GMPPrefs.KEY_LOGGING_LEVEL);
@ -100,12 +127,30 @@ async function initializeState({useHtmlViews}) {
// disabled.
gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
for (let addon of gMockAddons) {
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
gPrefs.setIntPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id), 0);
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id), false);
gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "");
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id), true);
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id), true);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id),
false
);
gPrefs.setIntPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id),
0
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id),
false
);
gPrefs.setCharPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
""
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id),
true
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id),
true
);
}
await GMPScope.GMPProvider.shutdown();
GMPScope.GMPProvider.startup();
@ -123,27 +168,60 @@ async function testNotInstalledDisabled() {
// Open the options menu (needed to check the disabled buttons).
const pluginOptions = item.querySelector("plugin-options");
pluginOptions.querySelector("panel-list").open = true;
const neverActivate = pluginOptions.querySelector("panel-item[action=never-activate]");
ok(neverActivate.hasAttribute("checked"), "Plugin state should be never-activate");
const neverActivate = pluginOptions.querySelector(
"panel-item[action=never-activate]"
);
ok(
neverActivate.hasAttribute("checked"),
"Plugin state should be never-activate"
);
pluginOptions.querySelector("panel-list").open = false;
} else {
item.parentNode.ensureElementIsVisible(item);
is(item.getAttribute("active"), "false", "Should be disabled");
let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
let el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"warning"
);
is_element_hidden(el, "Warning notification is hidden.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"class",
"disabled-postfix"
);
is_element_visible(el, "disabled-postfix is visible.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"disable-btn"
);
is_element_hidden(el, "Disable button not visible.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"enable-btn"
);
is_element_hidden(el, "Enable button not visible.");
let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
let menu = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"state-menulist"
);
is_element_visible(menu, "State menu should be visible.");
let neverActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "never-activate-menuitem");
is(menu.selectedItem, neverActivate, "Plugin state should be never-activate.");
let neverActivate = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"never-activate-menuitem"
);
is(
menu.selectedItem,
neverActivate,
"Plugin state should be never-activate."
);
}
}
}
@ -167,7 +245,10 @@ async function testNotInstalledDisabledDetails() {
async function testNotInstalled() {
for (let addon of gMockAddons) {
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id),
true
);
let item = get_addon_element(gManagerWindow, addon.id);
Assert.ok(item, "Got add-on element:" + addon.id);
if (gManagerWindow.useHtmlViews) {
@ -175,27 +256,60 @@ async function testNotInstalled() {
// Open the options menu (needed to check the disabled buttons).
const pluginOptions = item.querySelector("plugin-options");
pluginOptions.querySelector("panel-list").open = true;
const alwaysActivate = pluginOptions.querySelector("panel-item[action=always-activate]");
ok(alwaysActivate.hasAttribute("checked"), "Plugin state should be always-activate");
const alwaysActivate = pluginOptions.querySelector(
"panel-item[action=always-activate]"
);
ok(
alwaysActivate.hasAttribute("checked"),
"Plugin state should be always-activate"
);
pluginOptions.querySelector("panel-list").open = false;
} else {
item.parentNode.ensureElementIsVisible(item);
is(item.getAttribute("active"), "true");
let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
let el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"warning"
);
is_element_visible(el, "Warning notification is visible.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"class",
"disabled-postfix"
);
is_element_hidden(el, "disabled-postfix is hidden.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"disable-btn"
);
is_element_hidden(el, "Disable button not visible.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"enable-btn"
);
is_element_hidden(el, "Enable button not visible.");
let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
let menu = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"state-menulist"
);
is_element_visible(menu, "State menu should be visible.");
let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "always-activate-menuitem");
is(menu.selectedItem, alwaysActivate, "Plugin state should be always-activate.");
let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"always-activate-menuitem"
);
is(
menu.selectedItem,
alwaysActivate,
"Plugin state should be always-activate."
);
}
}
}
@ -225,10 +339,18 @@ async function testNotInstalledDetails() {
async function testInstalled() {
for (let addon of gMockAddons) {
gPrefs.setIntPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id),
TEST_DATE.getTime());
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id), false);
gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "1.2.3.4");
gPrefs.setIntPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, addon.id),
TEST_DATE.getTime()
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id),
false
);
gPrefs.setCharPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
"1.2.3.4"
);
let item = get_addon_element(gManagerWindow, addon.id);
Assert.ok(item, "Got add-on element.");
@ -237,27 +359,60 @@ async function testInstalled() {
// Open the options menu (needed to check the disabled buttons).
const pluginOptions = item.querySelector("plugin-options");
pluginOptions.querySelector("panel-list").open = true;
const alwaysActivate = pluginOptions.querySelector("panel-item[action=always-activate]");
ok(alwaysActivate.hasAttribute("checked"), "Plugin state should be always-activate");
const alwaysActivate = pluginOptions.querySelector(
"panel-item[action=always-activate]"
);
ok(
alwaysActivate.hasAttribute("checked"),
"Plugin state should be always-activate"
);
pluginOptions.querySelector("panel-list").open = false;
} else {
item.parentNode.ensureElementIsVisible(item);
is(item.getAttribute("active"), "true");
let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "warning");
let el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"warning"
);
is_element_hidden(el, "Warning notification is hidden.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "class", "disabled-postfix");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"class",
"disabled-postfix"
);
is_element_hidden(el, "disabled-postfix is hidden.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"disable-btn"
);
is_element_hidden(el, "Disable button not visible.");
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
el = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"enable-btn"
);
is_element_hidden(el, "Enable button not visible.");
let menu = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "state-menulist");
let menu = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"state-menulist"
);
is_element_visible(menu, "State menu should be visible.");
let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "always-activate-menuitem");
is(menu.selectedItem, alwaysActivate, "Plugin state should be always-activate.");
let alwaysActivate = item.ownerDocument.getAnonymousElementByAttribute(
item,
"anonid",
"always-activate-menuitem"
);
is(
menu.selectedItem,
alwaysActivate,
"Plugin state should be always-activate."
);
}
}
}
@ -268,8 +423,10 @@ async function testInstalledDetails() {
if (gManagerWindow.useHtmlViews) {
let card = get_addon_element(gManagerWindow, addon.id);
is_element_visible(card.querySelector("[action=update-check]"),
"Find updates link is bisible");
is_element_visible(
card.querySelector("[action=update-check]"),
"Find updates link is bisible"
);
} else {
let doc = gManagerWindow.document;
@ -289,8 +446,14 @@ async function testInstalledDetails() {
};
contextMenu.addEventListener("popupshown", listener);
el = doc.getElementsByClassName("detail-view-container")[0];
EventUtils.synthesizeMouse(el, 4, 4, { }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, { type: "contextmenu", button: 2 }, gManagerWindow);
EventUtils.synthesizeMouse(el, 4, 4, {}, gManagerWindow);
EventUtils.synthesizeMouse(
el,
4,
4,
{ type: "contextmenu", button: 2 },
gManagerWindow
);
});
let menuSep = doc.getElementById("addonitem-menuseparator");
is_element_hidden(menuSep, "Menu separator is hidden.");
@ -311,8 +474,13 @@ async function testInstalledGlobalEmeDisabled() {
// Open the options menu (needed to check the disabled buttons).
const pluginOptions = item.querySelector("plugin-options");
pluginOptions.querySelector("panel-list").open = true;
const askActivate = pluginOptions.querySelector("panel-item[action=ask-to-activate]");
ok(askActivate.shadowRoot.querySelector("button").disabled, "ask-to-activate should be disabled");
const askActivate = pluginOptions.querySelector(
"panel-item[action=ask-to-activate]"
);
ok(
askActivate.shadowRoot.querySelector("button").disabled,
"ask-to-activate should be disabled"
);
pluginOptions.querySelector("panel-list").open = false;
} else {
Assert.ok(!item, "Couldn't get add-on element.");
@ -333,16 +501,23 @@ async function testPreferencesButton() {
];
for (let preferences of prefValues) {
dump("Testing preferences button with pref settings: " +
JSON.stringify(preferences) + "\n");
dump(
"Testing preferences button with pref settings: " +
JSON.stringify(preferences) +
"\n"
);
for (let addon of gMockAddons) {
await close_manager(gManagerWindow);
gManagerWindow = await open_manager();
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
gPrefs.setCharPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
preferences.version);
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id),
preferences.enabled);
gPrefs.setCharPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
preferences.version
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id),
preferences.enabled
);
await gCategoryUtilities.openType("plugin");
let doc = gManagerWindow.document;
@ -352,14 +527,26 @@ async function testPreferencesButton() {
// Open the options menu (needed to check the more options action is enabled).
const pluginOptions = item.querySelector("plugin-options");
pluginOptions.querySelector("panel-list").open = true;
const moreOptions = pluginOptions.querySelector("panel-item[action=expand]");
ok(!moreOptions.shadowRoot.querySelector("button").disabled,
"more options action should be enabled");
const moreOptions = pluginOptions.querySelector(
"panel-item[action=expand]"
);
ok(
!moreOptions.shadowRoot.querySelector("button").disabled,
"more options action should be enabled"
);
moreOptions.click();
} else {
let button = doc.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
let button = doc.getAnonymousElementByAttribute(
item,
"anonid",
"preferences-btn"
);
is_element_visible(button);
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
gManagerWindow
);
}
await wait_for_view_load(gManagerWindow);
@ -392,13 +579,25 @@ async function testUpdateButton() {
let detail = get_addon_element(gManagerWindow, addon.id);
detail.querySelector("[action=update-check]").click();
} else {
let button = doc.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
let button = doc.getAnonymousElementByAttribute(
item,
"anonid",
"preferences-btn"
);
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
gManagerWindow
);
await wait_for_view_load(gManagerWindow);
button = doc.getElementById("detail-findUpdates-btn");
Assert.ok(button != null, "Got detail-findUpdates-btn");
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(
button,
{ clickCount: 1 },
gManagerWindow
);
}
await gInstallDeferred.promise;
@ -414,7 +613,9 @@ async function testUpdateButton() {
async function testEmeSupport() {
for (let addon of gMockAddons) {
gPrefs.clearUserPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id));
gPrefs.clearUserPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id)
);
}
await GMPScope.GMPProvider.shutdown();
GMPScope.GMPProvider.startup();
@ -426,17 +627,23 @@ async function testEmeSupport() {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6")) {
Assert.ok(item, "Adobe EME supported, found add-on element.");
} else {
Assert.ok(!item,
"Adobe EME not supported, couldn't find add-on element.");
Assert.ok(
!item,
"Adobe EME not supported, couldn't find add-on element."
);
}
} else if (addon.id == GMPScope.WIDEVINE_ID) {
if (AppConstants.isPlatformAndVersionAtLeast("win", "6") ||
AppConstants.platform == "macosx" ||
AppConstants.platform == "linux") {
if (
AppConstants.isPlatformAndVersionAtLeast("win", "6") ||
AppConstants.platform == "macosx" ||
AppConstants.platform == "linux"
) {
Assert.ok(item, "Widevine supported, found add-on element.");
} else {
Assert.ok(!item,
"Widevine not supported, couldn't find add-on element.");
Assert.ok(
!item,
"Widevine not supported, couldn't find add-on element."
);
}
} else {
Assert.ok(item, "Found add-on element.");
@ -444,8 +651,14 @@ async function testEmeSupport() {
}
for (let addon of gMockAddons) {
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id), true);
gPrefs.setBoolPref(getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id), true);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id),
true
);
gPrefs.setBoolPref(
getKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id),
true
);
}
await GMPScope.GMPProvider.shutdown();
GMPScope.GMPProvider.startup();
@ -475,9 +688,9 @@ async function test_gmpProvider(initializeStateOptions) {
}
add_task(function test_gmpProvider_on_XUL_aboutaddons() {
return test_gmpProvider({useHtmlViews: false});
return test_gmpProvider({ useHtmlViews: false });
});
add_task(async function test_gmpProvider_on_HTML_aboutaddons() {
return test_gmpProvider({useHtmlViews: true});
return test_gmpProvider({ useHtmlViews: true });
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,10 +1,14 @@
/* eslint max-len: ["error", 80] */
const {AddonTestUtils} =
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm",
{}
);
const {ExtensionPermissions} =
ChromeUtils.import("resource://gre/modules/ExtensionPermissions.jsm", {});
const { ExtensionPermissions } = ChromeUtils.import(
"resource://gre/modules/ExtensionPermissions.jsm",
{}
);
let gProvider;
let promptService;
@ -16,8 +20,9 @@ function getAddonCard(doc, addonId) {
}
function getDetailRows(card) {
return Array.from(card.querySelectorAll(
'[name="details"] .addon-detail-row:not([hidden])'));
return Array.from(
card.querySelectorAll('[name="details"] .addon-detail-row:not([hidden])')
);
}
function checkLabel(row, name) {
@ -28,8 +33,11 @@ function checkLabel(row, name) {
} else {
id = `addon-detail-${name}-label`;
}
is(row.ownerDocument.l10n.getAttributes(row.querySelector("label")).id,
id, `The ${name} label is set`);
is(
row.ownerDocument.l10n.getAttributes(row.querySelector("label")).id,
id,
`The ${name} label is set`
);
}
function checkLink(link, url, text = url) {
@ -39,7 +47,9 @@ function checkLink(link, url, text = url) {
// Check the fluent data.
Assert.deepEqual(
link.ownerDocument.l10n.getAttributes(link),
text, "The fluent data is set correctly");
text,
"The fluent data is set correctly"
);
} else {
// Just check text.
is(link.textContent, text, "The text is set");
@ -61,8 +71,10 @@ function checkOptions(doc, options, expectedOptions) {
is(input.value, expected.value, "The value is right");
is(input.checked, expected.checked, "The checked property is correct");
Assert.deepEqual(
doc.l10n.getAttributes(text), {id: expected.label, args: null},
"The label has the right text");
doc.l10n.getAttributes(text),
{ id: expected.label, args: null },
"The label has the right text"
);
}
}
@ -75,8 +87,10 @@ function assertDeckHeadingHidden(group) {
function assertDeckHeadingButtons(group, visibleButtons) {
ok(!group.hidden, "The tab group is shown");
ok(group.children.length >= visibleButtons.length,
`There should be at least ${visibleButtons.length} buttons`);
ok(
group.children.length >= visibleButtons.length,
`There should be at least ${visibleButtons.length} buttons`
);
for (let button of group.children) {
if (visibleButtons.includes(button.name)) {
ok(!button.hidden, `The ${button.name} is shown`);
@ -93,56 +107,65 @@ async function hasPrivateAllowed(id) {
add_task(async function enableHtmlViews() {
await SpecialPowers.pushPrefEnv({
set: [["extensions.htmlaboutaddons.enabled", true],
["extensions.allowPrivateBrowsingByDefault", false]],
set: [
["extensions.htmlaboutaddons.enabled", true],
["extensions.allowPrivateBrowsingByDefault", false],
],
});
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@mochi.test",
name: "Test add-on 1",
creator: {name: "The creator", url: "http://example.com/me"},
version: "3.1",
description: "Short description",
fullDescription: "Longer description\nWith brs!",
type: "extension",
contributionURL: "http://localhost/contribute",
averageRating: 4.279,
userPermissions: {
origins: ["<all_urls>", "file://*/*"],
permissions: ["alarms", "contextMenus", "tabs", "webNavigation"],
gProvider.createAddons([
{
id: "addon1@mochi.test",
name: "Test add-on 1",
creator: { name: "The creator", url: "http://example.com/me" },
version: "3.1",
description: "Short description",
fullDescription: "Longer description\nWith brs!",
type: "extension",
contributionURL: "http://localhost/contribute",
averageRating: 4.279,
userPermissions: {
origins: ["<all_urls>", "file://*/*"],
permissions: ["alarms", "contextMenus", "tabs", "webNavigation"],
},
reviewCount: 5,
reviewURL: "http://example.com/reviews",
homepageURL: "http://example.com/addon1",
updateDate: new Date("2019-03-07T01:00:00"),
applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE,
},
reviewCount: 5,
reviewURL: "http://example.com/reviews",
homepageURL: "http://example.com/addon1",
updateDate: new Date("2019-03-07T01:00:00"),
applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE,
}, {
id: "addon2@mochi.test",
name: "Test add-on 2",
creator: {name: "I made it"},
description: "Short description",
userPermissions: {
origins: [],
permissions: ["alarms", "contextMenus"],
{
id: "addon2@mochi.test",
name: "Test add-on 2",
creator: { name: "I made it" },
description: "Short description",
userPermissions: {
origins: [],
permissions: ["alarms", "contextMenus"],
},
type: "extension",
},
type: "extension",
}, {
id: "theme1@mochi.test",
name: "Test theme",
creator: {name: "Artist", url: "http://example.com/artist"},
description: "A nice tree",
type: "theme",
screenshots: [{
url: "http://example.com/preview-wide.png",
width: 760,
height: 92,
}, {
url: "http://example.com/preview.png",
width: 680,
height: 92,
}],
}]);
{
id: "theme1@mochi.test",
name: "Test theme",
creator: { name: "Artist", url: "http://example.com/artist" },
description: "A nice tree",
type: "theme",
screenshots: [
{
url: "http://example.com/preview-wide.png",
width: 760,
height: 92,
},
{
url: "http://example.com/preview.png",
width: 680,
height: 92,
},
],
},
]);
promptService = mockPromptService();
});
@ -153,7 +176,7 @@ add_task(async function testOpenDetailView() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
useAddonManager: "temporary",
});
@ -161,7 +184,7 @@ add_task(async function testOpenDetailView() {
let extension2 = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test",
applications: {gecko: {id: id2}},
applications: { gecko: { id: id2 } },
},
useAddonManager: "temporary",
});
@ -182,7 +205,7 @@ add_task(async function testOpenDetailView() {
let card = getAddonCard(doc, id);
ok(!card.querySelector("addon-details"), "The card doesn't have details");
let loaded = waitForViewLoad(win);
EventUtils.synthesizeMouseAtCenter(card, {clickCount: 1}, win);
EventUtils.synthesizeMouseAtCenter(card, { clickCount: 1 }, win);
await loaded;
card = getAddonCard(doc, id);
@ -211,15 +234,30 @@ add_task(async function testOpenDetailView() {
await extension2.unload();
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id2}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id2 },
],
]);
});
@ -229,7 +267,7 @@ add_task(async function testDetailOperations() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
useAddonManager: "temporary",
});
@ -242,7 +280,7 @@ add_task(async function testDetailOperations() {
let card = getAddonCard(doc, id);
ok(!card.querySelector("addon-details"), "The card doesn't have details");
let loaded = waitForViewLoad(win);
EventUtils.synthesizeMouseAtCenter(card, {clickCount: 1}, win);
EventUtils.synthesizeMouseAtCenter(card, { clickCount: 1 }, win);
await loaded;
card = getAddonCard(doc, id);
@ -274,8 +312,9 @@ add_task(async function testDetailOperations() {
// The (disabled) text should be shown now.
Assert.deepEqual(
doc.l10n.getAttributes(name),
{id: "addon-name-disabled", args: {name: "Test"}},
"The name is updated to the disabled text");
{ id: "addon-name-disabled", args: { name: "Test" } },
"The name is updated to the disabled text"
);
// Enable the add-on.
let extensionStarted = AddonTestUtils.promiseWebExtensionStartup(id);
@ -302,11 +341,12 @@ add_task(async function testDetailOperations() {
// We're on the list view now and there's no card for this extension.
const addonList = doc.querySelector("addon-list");
ok(addonList, "There's an addon-list now");
ok(!getAddonCard(doc, id),
"The extension no longer has a card");
ok(!getAddonCard(doc, id), "The extension no longer has a card");
let addon = await AddonManager.getAddonByID(id);
ok(addon && !!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The addon is pending uninstall");
ok(
addon && !!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The addon is pending uninstall"
);
// Ensure that a pending uninstall bar has been created for the
// pending uninstall extension, and pressing the undo button will
@ -326,20 +366,50 @@ add_task(async function testDetailOperations() {
await extension.unload();
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "disable", view: "detail"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "enable"}],
["addonsManager", "action", "aboutAddons", "cancelled",
{type: "extension", addonId: id, action: "uninstall", view: "detail"}],
["addonsManager", "action", "aboutAddons", "accepted",
{type: "extension", addonId: id, action: "uninstall", view: "detail"}],
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "undo", view: "list"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "disable", view: "detail" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "enable" },
],
[
"addonsManager",
"action",
"aboutAddons",
"cancelled",
{ type: "extension", addonId: id, action: "uninstall", view: "detail" },
],
[
"addonsManager",
"action",
"aboutAddons",
"accepted",
{ type: "extension", addonId: id, action: "uninstall", view: "detail" },
],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "undo", view: "list" },
],
]);
});
@ -377,14 +447,19 @@ add_task(async function testFullDetails() {
assertDeckHeadingButtons(details.tabGroup, ["details", "permissions"]);
let desc = details.querySelector(".addon-detail-description");
is(desc.innerHTML, "Longer description<br>With brs!",
"The full description replaces newlines with <br>");
is(
desc.innerHTML,
"Longer description<br>With brs!",
"The full description replaces newlines with <br>"
);
let contrib = details.querySelector(".addon-detail-contribute");
ok(contrib, "The contribution section is visible");
let waitForTab = BrowserTestUtils.waitForNewTab(
gBrowser, "http://localhost/contribute");
gBrowser,
"http://localhost/contribute"
);
contrib.querySelector("button").click();
BrowserTestUtils.removeTab(await waitForTab);
@ -394,9 +469,9 @@ add_task(async function testFullDetails() {
let row = rows.shift();
checkLabel(row, "updates");
let expectedOptions = [
{value: "1", label: "addon-detail-updates-radio-default", checked: false},
{value: "2", label: "addon-detail-updates-radio-on", checked: true},
{value: "0", label: "addon-detail-updates-radio-off", checked: false},
{ value: "1", label: "addon-detail-updates-radio-default", checked: false },
{ value: "2", label: "addon-detail-updates-radio-on", checked: true },
{ value: "0", label: "addon-detail-updates-radio-off", checked: false },
];
let options = row.lastElementChild.querySelectorAll("label");
checkOptions(doc, options, expectedOptions);
@ -409,8 +484,11 @@ add_task(async function testFullDetails() {
row = rows.shift();
ok(row.classList.contains("addon-detail-help-row"), "There's a help row");
ok(!row.hidden, "The help row is shown");
is(doc.l10n.getAttributes(row).id, "addon-detail-private-browsing-help",
"The help row is for private browsing");
is(
doc.l10n.getAttributes(row).id,
"addon-detail-private-browsing-help",
"The help row is for private browsing"
);
// Author.
row = rows.shift();
@ -449,30 +527,36 @@ add_task(async function testFullDetails() {
link = rating.querySelector("a");
checkLink(link, "http://example.com/reviews", {
id: "addon-detail-reviews-link",
args: {numberOfReviews: 5},
args: { numberOfReviews: 5 },
});
// While we are here, let's test edge cases of star ratings.
async function testRating(rating, ratingRounded, expectation) {
starsElem.rating = rating;
await starsElem.ownerDocument.l10n.translateElements([starsElem]);
is(starsElem.ratingBuckets.join(","), expectation,
`Rendering of rating ${rating}`);
is(
starsElem.ratingBuckets.join(","),
expectation,
`Rendering of rating ${rating}`
);
is(starsElem.title, `Rated ${ratingRounded} out of 5`,
"Rendered title must contain at most one fractional digit");
is(
starsElem.title,
`Rated ${ratingRounded} out of 5`,
"Rendered title must contain at most one fractional digit"
);
}
await testRating(0.000, "0", "empty,empty,empty,empty,empty");
await testRating(0.0, "0", "empty,empty,empty,empty,empty");
await testRating(0.123, "0.1", "empty,empty,empty,empty,empty");
await testRating(0.249, "0.2", "empty,empty,empty,empty,empty");
await testRating(0.250, "0.3", "half,empty,empty,empty,empty");
await testRating(0.25, "0.3", "half,empty,empty,empty,empty");
await testRating(0.749, "0.7", "half,empty,empty,empty,empty");
await testRating(0.750, "0.8", "full,empty,empty,empty,empty");
await testRating(1.000, "1", "full,empty,empty,empty,empty");
await testRating(0.75, "0.8", "full,empty,empty,empty,empty");
await testRating(1.0, "1", "full,empty,empty,empty,empty");
await testRating(4.249, "4.2", "full,full,full,full,empty");
await testRating(4.250, "4.3", "full,full,full,full,half");
await testRating(4.25, "4.3", "full,full,full,full,half");
await testRating(4.749, "4.7", "full,full,full,full,half");
await testRating(5.000, "5", "full,full,full,full,full");
await testRating(5.0, "5", "full,full,full,full,full");
// That should've been all the rows.
is(rows.length, 0, "There are no more rows left");
@ -480,11 +564,21 @@ add_task(async function testFullDetails() {
await closeView(win);
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "contribute", view: "detail"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "contribute", view: "detail" },
],
]);
});
@ -524,8 +618,11 @@ add_task(async function testMinimalExtension() {
row = rows.shift();
ok(row.classList.contains("addon-detail-help-row"), "There's a help row");
ok(!row.hidden, "The help row is shown");
is(doc.l10n.getAttributes(row).id, "addon-detail-private-browsing-help",
"The help row is for private browsing");
is(
doc.l10n.getAttributes(row).id,
"addon-detail-private-browsing-help",
"The help row is for private browsing"
);
// Author.
row = rows.shift();
@ -645,7 +742,7 @@ add_task(async function testPrivateBrowsingExtension() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "My PB extension",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
useAddonManager: "permanent",
});
@ -659,7 +756,7 @@ add_task(async function testPrivateBrowsingExtension() {
let card = getAddonCard(doc, id);
let badge = card.querySelector(".addon-badge-private-browsing-allowed");
ok(badge.hidden, "The PB badge is hidden initially");
ok(!await hasPrivateAllowed(id), "PB is not allowed");
ok(!(await hasPrivateAllowed(id)), "PB is not allowed");
// Load the detail view.
let loaded = waitForViewLoad(win);
@ -670,7 +767,7 @@ add_task(async function testPrivateBrowsingExtension() {
card = getAddonCard(doc, id);
badge = card.querySelector(".addon-badge-private-browsing-allowed");
ok(badge.hidden, "The PB badge is hidden on the detail view");
ok(!await hasPrivateAllowed(id), "PB is not allowed");
ok(!(await hasPrivateAllowed(id)), "PB is not allowed");
let pbRow = card.querySelector(".addon-detail-row-private-browsing");
@ -697,7 +794,7 @@ add_task(async function testPrivateBrowsingExtension() {
await updated;
ok(badge.hidden, "The PB badge is hidden");
ok(!await hasPrivateAllowed(id), "PB is disallowed");
ok(!(await hasPrivateAllowed(id)), "PB is disallowed");
// Allow PB.
updated = BrowserTestUtils.waitForEvent(card, "update");
@ -711,28 +808,68 @@ add_task(async function testPrivateBrowsingExtension() {
await extension.unload();
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", "on",
{type: "extension", addonId: id, action: "privateBrowsingAllowed",
view: "detail"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "disable"}],
["addonsManager", "action", "aboutAddons", "off",
{type: "extension", addonId: id, action: "privateBrowsingAllowed",
view: "detail"}],
["addonsManager", "action", "aboutAddons", "on",
{type: "extension", addonId: id, action: "privateBrowsingAllowed",
view: "detail"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
"on",
{
type: "extension",
addonId: id,
action: "privateBrowsingAllowed",
view: "detail",
},
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "disable" },
],
[
"addonsManager",
"action",
"aboutAddons",
"off",
{
type: "extension",
addonId: id,
action: "privateBrowsingAllowed",
view: "detail",
},
],
[
"addonsManager",
"action",
"aboutAddons",
"on",
{
type: "extension",
addonId: id,
action: "privateBrowsingAllowed",
view: "detail",
},
],
]);
});
add_task(async function testInvalidExtension() {
let win = await open_manager("addons://detail/foo");
let categoryUtils = new CategoryUtilities(win);
is(categoryUtils.selectedCategory, "discover",
"Should fall back to the discovery pane");
is(
categoryUtils.selectedCategory,
"discover",
"Should fall back to the discovery pane"
);
ok(!gBrowser.canGoBack, "The view has been replaced");
@ -746,8 +883,11 @@ add_task(async function testInvalidExtensionNoDiscover() {
let win = await open_manager("addons://detail/foo");
let categoryUtils = new CategoryUtilities(win);
is(categoryUtils.selectedCategory, "extension",
"Should fall back to the extension list if discover is disabled");
is(
categoryUtils.selectedCategory,
"extension",
"Should fall back to the extension list if discover is disabled"
);
ok(!gBrowser.canGoBack, "The view has been replaced");
@ -760,7 +900,7 @@ add_task(async function testExternalUninstall() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Remove me",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
useAddonManager: "temporary",
});
@ -795,7 +935,7 @@ add_task(async function testExternalThemeUninstall() {
let id = "remove-theme@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id}},
applications: { gecko: { id } },
name: "Remove theme",
theme: {},
},
@ -832,13 +972,13 @@ add_task(async function testPrivateBrowsingAllowedListView() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Allowed PB extension",
applications: {gecko: {id: "allowed@mochi.test"}},
applications: { gecko: { id: "allowed@mochi.test" } },
},
useAddonManager: "permanent",
});
await extension.startup();
let perms = {permissions: ["internal:privateBrowsingAllowed"], origins: []};
let perms = { permissions: ["internal:privateBrowsingAllowed"], origins: [] };
await ExtensionPermissions.add("allowed@mochi.test", perms);
let addon = await AddonManager.getAddonByID("allowed@mochi.test");
await addon.reload();
@ -867,7 +1007,7 @@ add_task(async function testPermissions() {
await loaded;
card = getAddonCard(doc, id);
let {deck, tabGroup} = card.details;
let { deck, tabGroup } = card.details;
// Check all the deck buttons are hidden.
assertDeckHeadingButtons(tabGroup, ["details", "permissions"]);
@ -885,13 +1025,18 @@ add_task(async function testPermissions() {
for (let name in permissions) {
// Check the permission-info class to make sure it's for a permission.
let row = rows.shift();
ok(row.classList.contains("permission-info"),
`There's a row for ${name}`);
ok(
row.classList.contains("permission-info"),
`There's a row for ${name}`
);
}
} else {
let row = rows.shift();
is(doc.l10n.getAttributes(row).id, "addon-permissions-empty",
"There's a message when no permissions are shown");
is(
doc.l10n.getAttributes(row).id,
"addon-permissions-empty",
"There's a message when no permissions are shown"
);
}
info("Check learn more link");
@ -927,7 +1072,9 @@ add_task(async function testGoBackButton() {
let loadDetailView = () => {
let loaded = waitForViewLoad(win);
getAddonCard(doc, id).querySelector("[action=expand]").click();
getAddonCard(doc, id)
.querySelector("[action=expand]")
.click();
return loaded;
};

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

@ -1,16 +1,13 @@
/* eslint max-len: ["error", 80] */
"use strict";
const {
AddonTestUtils,
} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
const {
ExtensionUtils: {
promiseEvent,
promiseObserved,
},
} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
ExtensionUtils: { promiseEvent, promiseObserved },
} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
// The response to the discovery API, as documented at:
// https://addons-server.readthedocs.io/en/latest/topics/api/discovery.html
@ -23,17 +20,23 @@ const API_RESPONSE_FILE = RELATIVE_DIR + "discovery/api_response.json";
const AMO_TEST_HOST = "rewritten-for-testing.addons.allizom.org";
const ArrayBufferInputStream =
Components.Constructor("@mozilla.org/io/arraybuffer-input-stream;1",
"nsIArrayBufferInputStream", "setData");
const ArrayBufferInputStream = Components.Constructor(
"@mozilla.org/io/arraybuffer-input-stream;1",
"nsIArrayBufferInputStream",
"setData"
);
AddonTestUtils.initMochitest(this);
const amoServer = AddonTestUtils.createHttpServer({hosts: [AMO_TEST_HOST]});
const amoServer = AddonTestUtils.createHttpServer({ hosts: [AMO_TEST_HOST] });
amoServer.registerFile("/png",
FileUtils.getFile("CurWorkD",
`${RELATIVE_DIR}discovery/small-1x1.png`.split("/")));
amoServer.registerFile(
"/png",
FileUtils.getFile(
"CurWorkD",
`${RELATIVE_DIR}discovery/small-1x1.png`.split("/")
)
);
amoServer.registerPathHandler("/dummy", (request, response) => {
response.write("Dummy");
});
@ -55,8 +58,8 @@ function getTestExpectationFromApiResult(result) {
// Read the content of API_RESPONSE_FILE, and replaces any embedded URLs with
// URLs that point to the `amoServer` test server.
async function readAPIResponseFixture() {
let apiText = await OS.File.read(API_RESPONSE_FILE, {encoding: "utf-8"});
apiText = apiText.replace(/\bhttps?:\/\/[^"]+(?=")/g, (url) => {
let apiText = await OS.File.read(API_RESPONSE_FILE, { encoding: "utf-8" });
apiText = apiText.replace(/\bhttps?:\/\/[^"]+(?=")/g, url => {
try {
url = new URL(url);
} catch (e) {
@ -114,8 +117,9 @@ class DiscoveryAPIHandler {
// Retrieve the list of visible action elements inside a document or container.
function getVisibleActions(documentOrElement) {
return Array.from(documentOrElement.querySelectorAll("[action]"))
.filter(elem => elem.offsetWidth && elem.offsetHeight);
return Array.from(documentOrElement.querySelectorAll("[action]")).filter(
elem => elem.offsetWidth && elem.offsetHeight
);
}
function getActionName(actionElement) {
@ -142,7 +146,7 @@ function getCardByAddonId(win, addonId) {
// Wait until the current `<discovery-pane>` element has finished loading its
// cards. This can be used after the cards have been loaded.
function promiseDiscopaneUpdate(win) {
let {cardsReady} = getCardContainer(win);
let { cardsReady } = getCardContainer(win);
ok(cardsReady, "Discovery cards should have started to initialize");
return cardsReady;
}
@ -154,15 +158,20 @@ async function switchToNonDiscoView(win) {
// share the same document.
win.managerWindow.gViewController.loadView("addons://list/extensions");
await wait_for_view_load(win.managerWindow);
ok(win.document.querySelector("addon-list"),
"Should be at the extension list view");
ok(
win.document.querySelector("addon-list"),
"Should be at the extension list view"
);
}
// Switch to the discopane and wait until it has fully rendered, including any
// cards from the discovery API.
async function switchToDiscoView(win) {
is(getDiscoveryElement(win), null,
"Cannot switch to discopane when the discopane is already shown");
is(
getDiscoveryElement(win),
null,
"Cannot switch to discopane when the discopane is already shown"
);
win.managerWindow.gViewController.loadView("addons://discover/");
await wait_for_view_load(win.managerWindow);
await promiseDiscopaneUpdate(win);
@ -192,8 +201,11 @@ async function promiseAddonInstall(amoServer, extensionData) {
amoServer.registerFile("/xpi", xpiFile);
let addonId = extensionData.manifest.applications.gecko.id;
let installedPromise =
waitAppMenuNotificationShown("addon-installed", addonId, true);
let installedPromise = waitAppMenuNotificationShown(
"addon-installed",
addonId,
true
);
if (!extensionData.manifest.theme) {
info(`${description}: Waiting for permission prompt`);
@ -202,8 +214,9 @@ async function promiseAddonInstall(amoServer, extensionData) {
panel.button.click();
} else {
info(`${description}: Waiting for install prompt`);
let panel =
await promisePopupNotificationShown("addon-install-confirmation");
let panel = await promisePopupNotificationShown(
"addon-install-confirmation"
);
panel.button.click();
}
@ -211,11 +224,15 @@ async function promiseAddonInstall(amoServer, extensionData) {
await installedPromise;
let addon = await AddonManager.getAddonByID(addonId);
Assert.deepEqual(addon.installTelemetryInfo, {
// This is the expected source because before the HTML-based discopane,
// "disco" was already used to mark installs from the AMO-hosted discopane.
source: "disco",
}, "The installed add-on should have the expected telemetry info");
Assert.deepEqual(
addon.installTelemetryInfo,
{
// This is the expected source because before the HTML-based discopane,
// "disco" was already used to mark installs from the AMO-hosted discopane.
source: "disco",
},
"The installed add-on should have the expected telemetry info"
);
}
// Install an add-on by clicking on the card.
@ -224,7 +241,8 @@ async function testCardInstall(card) {
Assert.deepEqual(
getVisibleActions(card).map(getActionName),
["install-addon"],
"Should have an Install button before install");
"Should have an Install button before install"
);
let installButton =
card.querySelector("[data-l10n-id='install-extension-button']") ||
@ -237,7 +255,8 @@ async function testCardInstall(card) {
Assert.deepEqual(
getVisibleActions(card).map(getActionName),
["manage-addon"],
"Should have a Manage button after install");
"Should have a Manage button after install"
);
}
// Uninstall the add-on (not via the card, since it has no uninstall button).
@ -246,7 +265,8 @@ async function testAddonUninstall(card) {
Assert.deepEqual(
getVisibleActions(card).map(getActionName),
["manage-addon"],
"Should have a Manage button before uninstall");
"Should have a Manage button before uninstall"
);
let addon = await AddonManager.getAddonByID(card.addonId);
@ -257,14 +277,17 @@ async function testAddonUninstall(card) {
Assert.deepEqual(
getVisibleActions(card).map(getActionName),
["install-addon"],
"Should have an Install button after uninstall");
"Should have an Install button after uninstall"
);
}
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.getAddons.discovery.api_url",
`http://${AMO_TEST_HOST}/discoapi`],
[
"extensions.getAddons.discovery.api_url",
`http://${AMO_TEST_HOST}/discoapi`,
],
// Enable HTML for all because some tests load non-discopane views.
["extensions.htmlaboutaddons.enabled", true],
["extensions.htmlaboutaddons.discover.enabled", true],
@ -293,7 +316,8 @@ add_task(async function discopane_with_real_api_data() {
Assert.deepEqual(
getVisibleActions(win.document).map(getActionName),
[],
"The AMO button should be invisible when the AMO API hasn't responded");
"The AMO button should be invisible when the AMO API hasn't responded"
);
apiHandler.unblockResponses();
await promiseDiscopaneUpdate(win);
@ -306,14 +330,16 @@ add_task(async function discopane_with_real_api_data() {
...new Array(apiResultArray.length).fill("install-addon"),
"open-amo",
],
"All add-on cards should be rendered, with AMO button at the end.");
"All add-on cards should be rendered, with AMO button at the end."
);
let imgCount = await waitForAllImagesLoaded(win);
is(imgCount, apiResultArray.length, "Expected an image for every result");
// Check that the cards have the expected content.
let cards =
Array.from(win.document.querySelectorAll("recommended-addon-card"));
let cards = Array.from(
win.document.querySelectorAll("recommended-addon-card")
);
is(cards.length, apiResultArray.length, "Every API result has a card");
for (let [i, card] of cards.entries()) {
let expectations = getTestExpectationFromApiResult(apiResultArray[i]);
@ -325,26 +351,36 @@ add_task(async function discopane_with_real_api_data() {
};
checkContent(".disco-addon-name", expectations.addonName);
await win.document.l10n.translateFragment(card);
checkContent(".disco-addon-author [data-l10n-name='author']",
expectations.authorName);
checkContent(
".disco-addon-author [data-l10n-name='author']",
expectations.authorName
);
let amoListingLink = card.querySelector(".disco-addon-author a");
ok(amoListingLink.search.includes("utm_source=firefox-browser"),
`Listing link should have attribution parameter, url=${amoListingLink}`);
ok(
amoListingLink.search.includes("utm_source=firefox-browser"),
`Listing link should have attribution parameter, url=${amoListingLink}`
);
let actions = getVisibleActions(card);
is(actions.length, 1, "Card should only have one install button");
let installButton = actions[0];
if (expectations.typeIsTheme) {
// Theme button + screenshot
ok(installButton.matches("[data-l10n-id='install-theme-button'"),
"Has theme install button");
ok(card.querySelector(".card-heading-image").offsetWidth,
"Preview image must be visible");
ok(
installButton.matches("[data-l10n-id='install-theme-button'"),
"Has theme install button"
);
ok(
card.querySelector(".card-heading-image").offsetWidth,
"Preview image must be visible"
);
} else {
// Extension button + extended description.
ok(installButton.matches("[data-l10n-id='install-extension-button'"),
"Has extension install button");
ok(
installButton.matches("[data-l10n-id='install-extension-button'"),
"Has extension install button"
);
checkContent(".disco-description-intro", expectations.editorialHead);
checkContent(".disco-description-main", expectations.editorialBody);
@ -360,8 +396,9 @@ add_task(async function discopane_with_real_api_data() {
if (expectations.dailyUsers) {
Assert.deepEqual(
win.document.l10n.getAttributes(userCountElem),
{id: "user-count", args: {dailyUsers: expectations.dailyUsers}},
"Card count should be rendered");
{ id: "user-count", args: { dailyUsers: expectations.dailyUsers } },
"Card count should be rendered"
);
} else {
is(userCountElem.offsetWidth, 0, "User count element is not visible");
}
@ -380,8 +417,8 @@ add_task(async function discopane_with_real_api_data() {
add_task(async function install_from_discopane() {
const apiText = await readAPIResponseFixture();
const apiResultArray = JSON.parse(apiText).results;
let getAddonIdByAMOAddonType =
type => apiResultArray.find(r => r.addon.type === type).addon.guid;
let getAddonIdByAMOAddonType = type =>
apiResultArray.find(r => r.addon.type === type).addon.guid;
const FIRST_EXTENSION_ID = getAddonIdByAMOAddonType("extension");
const FIRST_THEME_ID = getAddonIdByAMOAddonType("statictheme");
@ -398,7 +435,7 @@ add_task(async function install_from_discopane() {
manifest: {
name: "My Awesome Add-on",
description: "Test extension install button",
applications: {gecko: {id: FIRST_EXTENSION_ID}},
applications: { gecko: { id: FIRST_EXTENSION_ID } },
permissions: ["<all_urls>"],
},
});
@ -410,7 +447,7 @@ add_task(async function install_from_discopane() {
manifest: {
name: "My Fancy Theme",
description: "Test theme install button",
applications: {gecko: {id: FIRST_THEME_ID}},
applications: { gecko: { id: FIRST_THEME_ID } },
theme: {
colors: {
tab_selected: "red",
@ -434,15 +471,34 @@ add_task(async function install_from_discopane() {
...new Array(apiResultArray.length - 2).fill("install-addon"),
"open-amo",
],
"The Install buttons should be replaced with Manage buttons");
"The Install buttons should be replaced with Manage buttons"
);
assertAboutAddonsTelemetryEvents([
["addonsManager", "action", "aboutAddons", null,
{action: "installFromRecommendation", view: "discover",
addonId: FIRST_EXTENSION_ID, type: "extension"}],
["addonsManager", "action", "aboutAddons", null,
{action: "installFromRecommendation", view: "discover",
addonId: FIRST_THEME_ID, type: "theme"}],
[
"addonsManager",
"action",
"aboutAddons",
null,
{
action: "installFromRecommendation",
view: "discover",
addonId: FIRST_EXTENSION_ID,
type: "extension",
},
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{
action: "installFromRecommendation",
view: "discover",
addonId: FIRST_THEME_ID,
type: "theme",
},
],
]);
// End of the testing installation from a card.
@ -451,22 +507,35 @@ add_task(async function install_from_discopane() {
// and in order to be able to force the discovery pane to be rendered again.
let loaded = waitForViewLoad(win);
getCardByAddonId(win, FIRST_EXTENSION_ID)
.querySelector("[action='manage-addon']").click();
.querySelector("[action='manage-addon']")
.click();
await loaded;
{
let addonCard =
win.document.querySelector(
`addon-card[addon-id="${FIRST_EXTENSION_ID}"]`);
let addonCard = win.document.querySelector(
`addon-card[addon-id="${FIRST_EXTENSION_ID}"]`
);
ok(addonCard, "Add-on details should be shown");
ok(addonCard.expanded, "The card should have been expanded");
// TODO bug 1540253: Check that the "recommended" badge is visible.
}
assertAboutAddonsTelemetryEvents([
["addonsManager", "action", "aboutAddons", null,
{action: "manage", view: "discover", addonId: FIRST_EXTENSION_ID,
type: "extension"}],
], {methods: ["action"]});
assertAboutAddonsTelemetryEvents(
[
[
"addonsManager",
"action",
"aboutAddons",
null,
{
action: "manage",
view: "discover",
addonId: FIRST_EXTENSION_ID,
type: "extension",
},
],
],
{ methods: ["action"] }
);
// Now we are going to force an updated rendering and check that the cards are
// in the expected order, and then test uninstallation of the above add-ons.
@ -481,7 +550,8 @@ add_task(async function install_from_discopane() {
"manage-addon",
"open-amo",
],
"Already-installed add-ons should be rendered at the end of the list");
"Already-installed add-ons should be rendered at the end of the list"
);
promiseThemeChange = promiseObserved("lightweight-theme-styling-update");
await testAddonUninstall(getCardByAddonId(win, FIRST_THEME_ID));
@ -503,15 +573,23 @@ add_task(async function discopane_navigate_while_loading() {
let updatePromise = promiseDiscopaneUpdate(win);
let didUpdateDiscopane = false;
updatePromise.then(() => { didUpdateDiscopane = true; });
updatePromise.then(() => {
didUpdateDiscopane = true;
});
// Switch views while the request is pending.
await switchToNonDiscoView(win);
is(didUpdateDiscopane, false,
"discopane should still not be updated because the request is blocked");
is(getDiscoveryElement(win), null,
"Discopane should be removed after switching to the extension list");
is(
didUpdateDiscopane,
false,
"discopane should still not be updated because the request is blocked"
);
is(
getDiscoveryElement(win),
null,
"Discopane should be removed after switching to the extension list"
);
// Release pending requests, to verify that completing the request will not
// cause changes to the visible view. The updatePromise will still resolve
@ -519,10 +597,15 @@ add_task(async function discopane_navigate_while_loading() {
apiHandler.unblockResponses();
await updatePromise;
ok(win.document.querySelector("addon-list"),
"Should still be at the extension list view");
is(getDiscoveryElement(win), null,
"Discopane should not be in the document when it is not the active view");
ok(
win.document.querySelector("addon-list"),
"Should still be at the extension list view"
);
is(
getDiscoveryElement(win),
null,
"Discopane should not be in the document when it is not the active view"
);
is(apiHandler.requestCount, 1, "Discovery API should be fetched once");
@ -563,7 +646,8 @@ add_task(async function discopane_cache_api_responses() {
Assert.deepEqual(
getVisibleActions(win.document).map(getActionName),
["open-amo"],
"The AMO button should be visible even when the response was invalid");
"The AMO button should be visible even when the response was invalid"
);
// Change to a valid response, so that the next response will be cached.
apiHandler.setResponseText(`{"results": []}`);
@ -571,15 +655,21 @@ add_task(async function discopane_cache_api_responses() {
await switchToNonDiscoView(win);
await switchToDiscoView(win); // Request #2
is(apiHandler.requestCount, 2,
"Should fetch new data because an invalid response should not be cached");
is(
apiHandler.requestCount,
2,
"Should fetch new data because an invalid response should not be cached"
);
await switchToNonDiscoView(win);
await switchToDiscoView(win);
await closeView(win);
is(apiHandler.requestCount, 2,
"The previous response was valid and should have been reused");
is(
apiHandler.requestCount,
2,
"The previous response was valid and should have been reused"
);
// Now open a new about:addons page and verify that a new API request is sent.
let anotherWin = await loadInitialView("discover");
@ -593,8 +683,18 @@ add_task(async function discopane_no_cookies() {
let requestPromise = new Promise(resolve => {
amoServer.registerPathHandler("/discoapi", resolve);
});
Services.cookies.add(AMO_TEST_HOST, "/", "name", "value", false, false,
false, Date.now() / 1000 + 600, {}, Ci.nsICookie.SAMESITE_NONE);
Services.cookies.add(
AMO_TEST_HOST,
"/",
"name",
"value",
false,
false,
false,
Date.now() / 1000 + 600,
{},
Ci.nsICookie.SAMESITE_NONE
);
let win = await loadInitialView("discover");
let request = await requestPromise;
ok(!request.hasHeader("Cookie"), "discovery API should not receive cookies");
@ -614,21 +714,25 @@ add_task(async function discopane_interaction_telemetry() {
// Minimal API response to get the link in recommended-addon-card to render.
const DUMMY_EXTENSION_ID = "dummy@extensionid";
const apiResponse = {
results: [{
addon: {
guid: DUMMY_EXTENSION_ID,
type: "extension",
authors: [{
name: "Some author",
}],
url: `http://${AMO_TEST_HOST}/dummy`,
icon_url: `http://${AMO_TEST_HOST}/png`,
results: [
{
addon: {
guid: DUMMY_EXTENSION_ID,
type: "extension",
authors: [
{
name: "Some author",
},
],
url: `http://${AMO_TEST_HOST}/dummy`,
icon_url: `http://${AMO_TEST_HOST}/png`,
},
},
}],
],
};
let apiHandler = new DiscoveryAPIHandler(JSON.stringify(apiResponse));
let expectedAmoUrlFor = (where) => {
let expectedAmoUrlFor = where => {
// eslint-disable-next-line max-len
return `http://${AMO_TEST_HOST}/dummy?utm_source=firefox-browser&utm_medium=firefox-browser&utm_content=${where}`;
};
@ -636,11 +740,15 @@ add_task(async function discopane_interaction_telemetry() {
let testClickInDiscoCard = async (selector, utmContentParam) => {
let tabbrowser = win.windowRoot.ownerGlobal.gBrowser;
let tabPromise = BrowserTestUtils.waitForNewTab(tabbrowser);
getDiscoveryElement(win).querySelector(selector).click();
getDiscoveryElement(win)
.querySelector(selector)
.click();
let tab = await tabPromise;
is(tab.linkedBrowser.currentURI.spec,
expectedAmoUrlFor(utmContentParam),
"Expected URL of new tab");
is(
tab.linkedBrowser.currentURI.spec,
expectedAmoUrlFor(utmContentParam),
"Expected URL of new tab"
);
BrowserTestUtils.removeTab(tab);
};
@ -657,8 +765,8 @@ add_task(async function discopane_interaction_telemetry() {
await testClickInDiscoCard(".disco-addon-author a", "discopane-entry-link");
assertAboutAddonsTelemetryEvents([
["addonsManager", "link", "aboutAddons", "discomore", {view: "discover"}],
["addonsManager", "link", "aboutAddons", "discohome", {view: "discover"}],
["addonsManager", "link", "aboutAddons", "discomore", { view: "discover" }],
["addonsManager", "link", "aboutAddons", "discohome", { view: "discover" }],
]);
is(apiHandler.requestCount, 1, "Discovery API should be fetched once");

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

@ -1,19 +1,18 @@
/* eslint max-len: ["error", 80] */
"use strict";
const {ClientID} = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
const { ClientID } = ChromeUtils.import("resource://gre/modules/ClientID.jsm");
const {
AddonTestUtils,
} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
AddonTestUtils.initMochitest(this);
const server = AddonTestUtils.createHttpServer();
const serverBaseUrl = `http://localhost:${server.identity.primaryPort}/`;
server.registerPathHandler("/sumo/personalized-addons",
(request, response) => {
response.write("This is a SUMO page that explains personalized add-ons.");
});
server.registerPathHandler("/sumo/personalized-addons", (request, response) => {
response.write("This is a SUMO page that explains personalized add-ons.");
});
// Before a discovery API request is triggered, this method should be called.
// Resolves with the value of the "telemetry-client-id" query parameter.
@ -80,8 +79,10 @@ add_task(async function clientid_enabled() {
// TODO: Fix this together with bug 1537933
//
// is(await requestPromise, EXPECTED_CLIENT_ID,
ok(await requestPromise,
"Moz-Client-Id should be set when telemetry & discovery are enabled");
ok(
await requestPromise,
"Moz-Client-Id should be set when telemetry & discovery are enabled"
);
Services.telemetry.clearEvents();
@ -97,9 +98,18 @@ add_task(async function clientid_enabled() {
await closeView(win);
assertAboutAddonsTelemetryEvents([
["addonsManager", "link", "aboutAddons", "disconotice", {view: "discover"}],
], {methods: ["link"]});
assertAboutAddonsTelemetryEvents(
[
[
"addonsManager",
"link",
"aboutAddons",
"disconotice",
{ view: "discover" },
],
],
{ methods: ["link"] }
);
});
// Test that the clientid is not sent when disabled via prefs.
@ -111,25 +121,39 @@ add_task(async function clientid_disabled() {
let requestPromise = promiseOneDiscoveryApiRequest();
let win = await loadInitialView("discover");
ok(!isNoticeVisible(win), "Notice about personalization should be hidden");
is(await requestPromise, null,
"Moz-Client-Id should not be sent when discovery is disabled");
is(
await requestPromise,
null,
"Moz-Client-Id should not be sent when discovery is disabled"
);
await closeView(win);
await SpecialPowers.popPrefEnv();
});
// Test that the clientid is not sent from private windows.
add_task(async function clientid_from_private_window() {
let privateWindow =
await BrowserTestUtils.openNewBrowserWindow({private: true});
let privateWindow = await BrowserTestUtils.openNewBrowserWindow({
private: true,
});
let requestPromise = promiseOneDiscoveryApiRequest();
let managerWindow =
await open_manager("addons://discover/", null, null, null, privateWindow);
ok(PrivateBrowsingUtils.isContentWindowPrivate(managerWindow),
"Addon-manager is in a private window");
let managerWindow = await open_manager(
"addons://discover/",
null,
null,
null,
privateWindow
);
ok(
PrivateBrowsingUtils.isContentWindowPrivate(managerWindow),
"Addon-manager is in a private window"
);
is(await requestPromise, null,
"Moz-Client-Id should not be sent in private windows");
is(
await requestPromise,
null,
"Moz-Client-Id should not be sent in private windows"
);
await close_manager(managerWindow);
await BrowserTestUtils.closeWindow(privateWindow);
@ -153,8 +177,10 @@ add_task(async function clientid_enabled_from_extension_list() {
ok(isNoticeVisible(win), "Notice about personalization should be visible");
ok(await requestPromise,
"Moz-Client-Id should be set when telemetry & discovery are enabled");
ok(
await requestPromise,
"Moz-Client-Id should be set when telemetry & discovery are enabled"
);
// Make sure switching to the theme view doesn't trigger another request.
await switchView(win, "theme");
@ -186,16 +212,18 @@ add_task(async function clientid_enabled_from_theme_list() {
ok(!isNoticeVisible(win), "Notice about personalization should be hidden");
is(await requestPromise, null,
"Moz-Client-Id should not be sent when loading themes initially");
is(
await requestPromise,
null,
"Moz-Client-Id should not be sent when loading themes initially"
);
info("Load the extension list and verify the client ID is now sent");
requestPromise = promiseOneDiscoveryApiRequest();
await switchView(win, "extension");
ok(await requestPromise,
"Moz-Client-Id is now sent for extensions");
ok(await requestPromise, "Moz-Client-Id is now sent for extensions");
await closeView(win);
await SpecialPowers.popPrefEnv();

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

@ -1,9 +1,9 @@
/* eslint max-len: ["error", 80] */
"use strict";
const {
AddonTestUtils,
} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
AddonTestUtils.initMochitest(this);
const server = AddonTestUtils.createHttpServer();
@ -24,19 +24,28 @@ async function checkIfDiscoverVisible(expectVisible) {
let managerWindow = await open_manager(null);
let categoryUtilities = new CategoryUtilities(managerWindow);
is(categoryUtilities.isTypeVisible("discover"), expectVisible,
"Visibility of discopane");
is(
categoryUtilities.isTypeVisible("discover"),
expectVisible,
"Visibility of discopane"
);
await wait_for_view_load(managerWindow);
if (expectVisible) {
is(categoryUtilities.selectedCategory, "discover",
"Expected discopane as the default view");
is(
categoryUtilities.selectedCategory,
"discover",
"Expected discopane as the default view"
);
await requestPromise;
is(requestCount, 1, "Expected discovery API request");
} else {
// The next view (after discopane) is the extension list.
is(categoryUtilities.selectedCategory, "extension",
"Should fall back to another view when the discopane is disabled");
is(
categoryUtilities.selectedCategory,
"extension",
"Should fall back to another view when the discopane is disabled"
);
is(requestCount, 0, "Discovery API should not be requested");
}

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

@ -1,8 +1,8 @@
/* eslint max-len: ["error", 80] */
const {
AddonTestUtils,
} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
AddonTestUtils.initMochitest(this);
@ -45,17 +45,21 @@ add_task(async function enableHtmlViews() {
let extensionsCreated = 0;
function createExtensions(manifestExtras) {
return manifestExtras.map(extra => ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {gecko: {id: `test-${extensionsCreated++}@mochi.test`}},
icons: {
32: "test-icon.png",
return manifestExtras.map(extra =>
ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {
gecko: { id: `test-${extensionsCreated++}@mochi.test` },
},
icons: {
32: "test-icon.png",
},
...extra,
},
...extra,
},
useAddonManager: "temporary",
}));
useAddonManager: "temporary",
})
);
}
add_task(async function testExtensionList() {
@ -63,7 +67,7 @@ add_task(async function testExtensionList() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {gecko: {id}},
applications: { gecko: { id } },
icons: {
32: "test-icon.png",
},
@ -92,30 +96,45 @@ add_task(async function testExtensionList() {
ok(card, "The card is in the enabled section");
// Check the properties of the card.
is(card.querySelector(".addon-name").textContent, "Test extension",
"The name is set");
is(
card.querySelector(".addon-name").textContent,
"Test extension",
"The name is set"
);
let icon = card.querySelector(".addon-icon");
ok(icon.src.endsWith("/test-icon.png"), "The icon is set");
// Disable the extension.
let disableButton = card.querySelector('[action="toggle-disabled"]');
is(doc.l10n.getAttributes(disableButton).id, "disable-addon-button",
"The button has the disable label");
is(
doc.l10n.getAttributes(disableButton).id,
"disable-addon-button",
"The button has the disable label"
);
let disabled = BrowserTestUtils.waitForEvent(list, "move");
disableButton.click();
await disabled;
is(card.parentNode, disabledSection,
"The card is now in the disabled section");
is(
card.parentNode,
disabledSection,
"The card is now in the disabled section"
);
// The disable button is now enable.
is(doc.l10n.getAttributes(disableButton).id, "enable-addon-button",
"The button has the enable label");
is(
doc.l10n.getAttributes(disableButton).id,
"enable-addon-button",
"The button has the enable label"
);
// Remove the add-on.
let removeButton = card.querySelector('[action="remove"]');
is(doc.l10n.getAttributes(removeButton).id, "remove-addon-button",
"The button has the remove label");
is(
doc.l10n.getAttributes(removeButton).id,
"remove-addon-button",
"The button has the remove label"
);
// Remove but cancel.
let cancelled = BrowserTestUtils.waitForEvent(card, "remove-cancelled");
@ -129,8 +148,10 @@ add_task(async function testExtensionList() {
await removed;
addon = await AddonManager.getAddonByID(id);
ok(addon && !!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The addon is pending uninstall");
ok(
addon && !!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The addon is pending uninstall"
);
// Ensure that a pending uninstall bar has been created for the
// pending uninstall extension, and pressing the undo button will
@ -144,7 +165,7 @@ add_task(async function testExtensionList() {
const extension2 = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension 2",
applications: {gecko: {id: "test-2@mochi.test"}},
applications: { gecko: { id: "test-2@mochi.test" } },
icons: {
32: "test-icon.png",
},
@ -154,8 +175,10 @@ add_task(async function testExtensionList() {
await extension2.startup();
await added;
ok(getCardByAddonId(list, extension2.id),
"Got a card added for the second extension");
ok(
getCardByAddonId(list, extension2.id),
"Got a card added for the second extension"
);
info("Uninstall the second test extension and wait for addon card removed");
removed = BrowserTestUtils.waitForEvent(list, "remove");
@ -163,8 +186,10 @@ add_task(async function testExtensionList() {
addon2.uninstall(true);
await removed;
ok(!getCardByAddonId(list, extension2.id),
"Addon card for the second extension removed");
ok(
!getCardByAddonId(list, extension2.id),
"Addon card for the second extension removed"
);
assertHasPendingUninstalls(list, 2);
assertHasPendingUninstallAddon(list, addon2);
@ -177,10 +202,14 @@ add_task(async function testExtensionList() {
info("Wait for the second pending uninstal add-ons startup");
await addon2Started;
ok(getCardByAddonId(disabledSection, addon.id),
"The card for the first extension is in the disabled section");
ok(getCardByAddonId(enabledSection, addon2.id),
"The card for the second extension is in the enabled section");
ok(
getCardByAddonId(disabledSection, addon.id),
"The card for the first extension is in the disabled section"
);
ok(
getCardByAddonId(enabledSection, addon2.id),
"The card for the second extension is in the enabled section"
);
await extension2.unload();
await extension.unload();
@ -190,7 +219,7 @@ add_task(async function testExtensionList() {
const themeXpi = AddonTestUtils.createTempWebExtensionFile({
manifest: {
name: "My theme",
applications: {gecko: {id: "theme@mochi.test"}},
applications: { gecko: { id: "theme@mochi.test" } },
theme: {},
},
});
@ -204,7 +233,7 @@ add_task(async function testExtensionList() {
const xpi = AddonTestUtils.createTempWebExtensionFile({
manifest: {
name: "Test extension 3",
applications: {gecko: {id: "test-3@mochi.test"}},
applications: { gecko: { id: "test-3@mochi.test" } },
icons: {
32: "test-icon.png",
},
@ -214,45 +243,86 @@ add_task(async function testExtensionList() {
added = BrowserTestUtils.waitForEvent(list, "add");
const addon3 = await AddonManager.installTemporaryAddon(xpi);
await added;
ok(getCardByAddonId(list, addon3.id),
"Addon card for the third extension added");
ok(
getCardByAddonId(list, addon3.id),
"Addon card for the third extension added"
);
removed = BrowserTestUtils.waitForEvent(list, "remove");
addon3.uninstall(true);
await removed;
ok(!getCardByAddonId(list, addon3.id),
"Addon card for the third extension removed");
ok(
!getCardByAddonId(list, addon3.id),
"Addon card for the third extension removed"
);
assertHasPendingUninstalls(list, 1);
ok(addon3 && !!(addon3.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The third addon is pending uninstall");
ok(
addon3 && !!(addon3.pendingOperations & AddonManager.PENDING_UNINSTALL),
"The third addon is pending uninstall"
);
await closeView(win);
ok(!await AddonManager.getAddonByID(addon3.id),
"The third addon has been fully uninstalled");
ok(
!(await AddonManager.getAddonByID(addon3.id)),
"The third addon has been fully uninstalled"
);
ok(themeAddon.pendingOperations & AddonManager.PENDING_UNINSTALL,
"The theme addon is pending after the list extension view is closed");
ok(
themeAddon.pendingOperations & AddonManager.PENDING_UNINSTALL,
"The theme addon is pending after the list extension view is closed"
);
await themeAddon.uninstall();
ok(!await AddonManager.getAddonByID(themeAddon.id),
"The theme addon is fully uninstalled");
ok(
!(await AddonManager.getAddonByID(themeAddon.id)),
"The theme addon is fully uninstalled"
);
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, view: "list", action: "disable"}],
["addonsManager", "action", "aboutAddons", "cancelled",
{type: "extension", addonId: id, view: "list", action: "uninstall"}],
["addonsManager", "action", "aboutAddons", "accepted",
{type: "extension", addonId: id, view: "list", action: "uninstall"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, view: "list", action: "undo"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: "test-2@mochi.test", view: "list",
action: "undo"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, view: "list", action: "disable" },
],
[
"addonsManager",
"action",
"aboutAddons",
"cancelled",
{ type: "extension", addonId: id, view: "list", action: "uninstall" },
],
[
"addonsManager",
"action",
"aboutAddons",
"accepted",
{ type: "extension", addonId: id, view: "list", action: "uninstall" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, view: "list", action: "undo" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{
type: "extension",
addonId: "test-2@mochi.test",
view: "list",
action: "undo",
},
],
]);
});
@ -260,7 +330,7 @@ add_task(async function testMouseSupport() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {gecko: {id: "test@mochi.test"}},
applications: { gecko: { id: "test@mochi.test" } },
},
useAddonManager: "temporary",
});
@ -277,7 +347,10 @@ add_task(async function testMouseSupport() {
ok(!panel.open, "The panel is initially closed");
await BrowserTestUtils.synthesizeMouseAtCenter(
menuButton, {type: "mousedown"}, gBrowser.selectedBrowser);
menuButton,
{ type: "mousedown" },
gBrowser.selectedBrowser
);
ok(panel.open, "The panel is now open");
await closeView(win);
@ -288,7 +361,7 @@ add_task(async function testKeyboardSupport() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {gecko: {id: "test@mochi.test"}},
applications: { gecko: { id: "test@mochi.test" } },
},
useAddonManager: "temporary",
});
@ -328,7 +401,7 @@ add_task(async function testKeyboardSupport() {
// Test tabbing out of the menu.
space();
is(moreOptionsMenu.open, true, "The menu is open");
tab({shiftKey: true});
tab({ shiftKey: true });
is(moreOptionsMenu.open, false, "Tabbing away from the menu closes it");
tab();
isFocused(moreOptionsButton, "The button is focused again");
@ -364,8 +437,11 @@ add_task(async function testKeyboardSupport() {
space();
await disabled;
is(moreOptionsMenu.open, false, "The menu is closed");
is(card.parentNode, disabledSection,
"The card is now in the disabled section");
is(
card.parentNode,
disabledSection,
"The card is now in the disabled section"
);
// Open the menu again.
shown = BrowserTestUtils.waitForEvent(moreOptionsMenu, "shown");
@ -389,9 +465,9 @@ add_task(async function testKeyboardSupport() {
add_task(async function testExtensionReordering() {
let extensions = createExtensions([
{name: "Extension One"},
{name: "This is last"},
{name: "An extension, is first"},
{ name: "Extension One" },
{ name: "This is last" },
{ name: "An extension, is first" },
]);
await Promise.all(extensions.map(extension => extension.startup()));
@ -409,11 +485,11 @@ add_task(async function testExtensionReordering() {
is(cards.length, 3, "Each extension has an addon-card");
let order = Array.from(cards).map(card => card.addon.name);
Assert.deepEqual(order, [
"An extension, is first",
"Extension One",
"This is last",
], "The add-ons are sorted by name");
Assert.deepEqual(
order,
["An extension, is first", "Extension One", "This is last"],
"The add-ons are sorted by name"
);
// Disable the second extension.
let disabledSection = getSection(doc, "disabled");
@ -421,45 +497,52 @@ add_task(async function testExtensionReordering() {
// Disable the add-ons in a different order.
let reorderedCards = [cards[1], cards[0], cards[2]];
for (let {addon} of reorderedCards) {
for (let { addon } of reorderedCards) {
let moved = BrowserTestUtils.waitForEvent(list, "move");
await addon.disable();
await moved;
}
order = Array.from(getTestCards(disabledSection))
.map(card => card.addon.name);
Assert.deepEqual(order, [
"An extension, is first",
"Extension One",
"This is last",
], "The add-ons are sorted by name");
order = Array.from(getTestCards(disabledSection)).map(
card => card.addon.name
);
Assert.deepEqual(
order,
["An extension, is first", "Extension One", "This is last"],
"The add-ons are sorted by name"
);
// All of our installed add-ons are disabled, install a new one.
let [newExtension] = createExtensions([{name: "Extension New"}]);
let [newExtension] = createExtensions([{ name: "Extension New" }]);
let added = BrowserTestUtils.waitForEvent(list, "add");
await newExtension.startup();
await added;
let [newCard] = getTestCards(enabledSection);
is(newCard.addon.name, "Extension New",
"The new add-on is in the enabled list");
is(
newCard.addon.name,
"Extension New",
"The new add-on is in the enabled list"
);
// Enable everything again.
for (let {addon} of cards) {
for (let { addon } of cards) {
let moved = BrowserTestUtils.waitForEvent(list, "move");
await addon.enable();
await moved;
}
order = Array.from(getTestCards(enabledSection))
.map(card => card.addon.name);
Assert.deepEqual(order, [
"An extension, is first",
"Extension New",
"Extension One",
"This is last",
], "The add-ons are sorted by name");
order = Array.from(getTestCards(enabledSection)).map(card => card.addon.name);
Assert.deepEqual(
order,
[
"An extension, is first",
"Extension New",
"Extension One",
"This is last",
],
"The add-ons are sorted by name"
);
// Remove the new extension.
let removed = BrowserTestUtils.waitForEvent(list, "remove");
@ -474,7 +557,7 @@ add_task(async function testExtensionReordering() {
add_task(async function testThemeList() {
let theme = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id: "theme@mochi.test"}},
applications: { gecko: { id: "theme@mochi.test" } },
name: "My theme",
theme: {},
},
@ -502,25 +585,39 @@ add_task(async function testThemeList() {
let enabledSection = getSection(doc, "enabled");
let disabledSection = getSection(doc, "disabled");
await TestUtils.waitForCondition(() =>
enabledSection.querySelectorAll("addon-card").length == 1);
await TestUtils.waitForCondition(
() => enabledSection.querySelectorAll("addon-card").length == 1
);
is(card.parentNode, enabledSection,
"The new theme card is in the enabled section");
is(enabledSection.querySelectorAll("addon-card").length,
1, "There is one enabled theme");
is(
card.parentNode,
enabledSection,
"The new theme card is in the enabled section"
);
is(
enabledSection.querySelectorAll("addon-card").length,
1,
"There is one enabled theme"
);
let themesChanged = waitForThemeChange(list);
card.querySelector('[action="toggle-disabled"]').click();
await themesChanged;
await TestUtils.waitForCondition(() =>
enabledSection.querySelectorAll("addon-card").length == 1);
await TestUtils.waitForCondition(
() => enabledSection.querySelectorAll("addon-card").length == 1
);
is(card.parentNode, disabledSection,
"The card is now in the disabled section");
is(enabledSection.querySelectorAll("addon-card").length,
1, "There is one enabled theme");
is(
card.parentNode,
disabledSection,
"The card is now in the disabled section"
);
is(
enabledSection.querySelectorAll("addon-card").length,
1,
"There is one enabled theme"
);
await theme.unload();
await closeView(win);
@ -563,8 +660,9 @@ add_task(async function testBuiltInThemeButtons() {
darkButtons.toggleDisabled.click();
await themesChanged;
await TestUtils.waitForCondition(() =>
enabledSection.querySelectorAll("addon-card").length == 1);
await TestUtils.waitForCondition(
() => enabledSection.querySelectorAll("addon-card").length == 1
);
// Check the buttons.
is(defaultButtons.toggleDisabled.hidden, false, "Enable is visible");
@ -577,8 +675,9 @@ add_task(async function testBuiltInThemeButtons() {
darkButtons.toggleDisabled.click();
await themesChanged;
await TestUtils.waitForCondition(() =>
enabledSection.querySelectorAll("addon-card").length == 1);
await TestUtils.waitForCondition(
() => enabledSection.querySelectorAll("addon-card").length == 1
);
// The themes are back to their starting posititons.
is(defaultTheme.parentNode, enabledSection, "Default is enabled");
@ -597,13 +696,16 @@ add_task(async function testOnlyTypeIsShown() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test extension",
applications: {gecko: {id: "test@mochi.test"}},
applications: { gecko: { id: "test@mochi.test" } },
},
useAddonManager: "temporary",
});
let skipped = BrowserTestUtils.waitForEvent(
list, "skip-add", (e) => e.detail == "type-mismatch");
list,
"skip-add",
e => e.detail == "type-mismatch"
);
await extension.startup();
await skipped;

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

@ -3,18 +3,18 @@
/* eslint max-len: ["error", 80] */
"use strict";
const {
AddonTestUtils,
} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
AddonTestUtils.initMochitest(this);
function makeResult({guid, type}) {
function makeResult({ guid, type }) {
return {
addon: {
authors: [{name: "Some author"}],
authors: [{ name: "Some author" }],
current_version: {
files: [{platform: "all", url: "data:,"}],
files: [{ platform: "all", url: "data:," }],
},
url: "data:,",
guid,
@ -26,10 +26,12 @@ function makeResult({guid, type}) {
function mockResults() {
let types = ["extension", "theme", "extension", "extension", "theme"];
return {
results: types.map((type, i) => makeResult({
guid: `${type}${i}@mochi.test`,
type,
})),
results: types.map((type, i) =>
makeResult({
guid: `${type}${i}@mochi.test`,
type,
})
),
};
}
@ -41,8 +43,10 @@ add_task(async function setup() {
["browser.discovery.enabled", false],
["extensions.htmlaboutaddons.enabled", true],
["extensions.getAddons.discovery.api_url", `data:;base64,${results}`],
["extensions.recommendations.themeRecommendationUrl",
"https://example.com/theme"],
[
"extensions.recommendations.themeRecommendationUrl",
"https://example.com/theme",
],
],
});
});
@ -62,7 +66,6 @@ function checkExtraContents(doc, type, opts = {}) {
is_element_visible(footer, "The footer is visible");
if (type == "extension") {
ok(taarNotice, "There is a TAAR notice");
if (showAmoButton) {
@ -81,26 +84,40 @@ function checkExtraContents(doc, type, opts = {}) {
if (showThemeRecommendationFooter) {
is_element_visible(
themeRecommendationFooter, "There's a theme recommendation footer");
themeRecommendationFooter,
"There's a theme recommendation footer"
);
is_element_visible(themeRecommendationLink, "There's a link to the theme");
is(themeRecommendationLink.target, "_blank", "The link opens in a new tab");
is(themeRecommendationLink.href, "https://example.com/theme",
"The link goes to the pref's URL");
is(doc.l10n.getAttributes(themeRecommendationFooter).id,
"recommended-theme-1", "The recommendation has the right l10n-id");
is(
themeRecommendationLink.href,
"https://example.com/theme",
"The link goes to the pref's URL"
);
is(
doc.l10n.getAttributes(themeRecommendationFooter).id,
"recommended-theme-1",
"The recommendation has the right l10n-id"
);
} else {
ok(!themeRecommendationFooter || themeRecommendationFooter.hidden,
"There's no theme recommendation");
ok(
!themeRecommendationFooter || themeRecommendationFooter.hidden,
"There's no theme recommendation"
);
}
}
async function installAddon({card, recommendedList, manifestExtra = {}}) {
async function installAddon({ card, recommendedList, manifestExtra = {} }) {
// Install an add-on to hide the card.
let hidden = BrowserTestUtils.waitForEvent(
recommendedList, "card-hidden", false, e => e.detail.card == card);
recommendedList,
"card-hidden",
false,
e => e.detail.card == card
);
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id: card.addonId}},
applications: { gecko: { id: card.addonId } },
...manifestExtra,
},
useAddonManager: "temporary",
@ -110,7 +127,7 @@ async function installAddon({card, recommendedList, manifestExtra = {}}) {
return extension;
}
async function testListRecommendations({type, manifestExtra = {}}) {
async function testListRecommendations({ type, manifestExtra = {} }) {
Services.telemetry.clearEvents();
let win = await loadInitialView(type);
@ -131,13 +148,13 @@ async function testListRecommendations({type, manifestExtra = {}}) {
}
// Install an add-on for the first card, verify it is hidden.
let {addonId} = cards[0];
let { addonId } = cards[0];
ok(addonId, "The card has an addonId");
// Installing the add-on will fail since the URL doesn't point to a valid
// XPI. This will trigger the telemetry though.
let installButton = cards[0].querySelector('[action="install-addon"]');
let {panel} = PopupNotifications;
let { panel } = PopupNotifications;
let popupId = "addon-install-failed-notification";
let failPromise = TestUtils.topicObserved("addon-install-failed");
installButton.click();
@ -151,7 +168,7 @@ async function testListRecommendations({type, manifestExtra = {}}) {
panel.firstElementChild.button.click();
await BrowserTestUtils.waitForPopupEvent(panel, "hidden");
let extension = await installAddon({card: cards[0], recommendedList});
let extension = await installAddon({ card: cards[0], recommendedList });
is_element_hidden(cards[0], "The card is now hidden");
// Switch away and back, there should still be a hidden card.
@ -182,20 +199,28 @@ async function testListRecommendations({type, manifestExtra = {}}) {
await closeView(win);
assertAboutAddonsTelemetryEvents([
["addonsManager", "action", "aboutAddons", null,
{action: "installFromRecommendation", view: "list", addonId, type}],
], {methods: ["action"]});
assertAboutAddonsTelemetryEvents(
[
[
"addonsManager",
"action",
"aboutAddons",
null,
{ action: "installFromRecommendation", view: "list", addonId, type },
],
],
{ methods: ["action"] }
);
}
add_task(async function testExtensionList() {
await testListRecommendations({type: "extension"});
await testListRecommendations({ type: "extension" });
});
add_task(async function testThemeList() {
await testListRecommendations({
type: "theme",
manifestExtra: {theme: {}},
manifestExtra: { theme: {} },
});
});
@ -215,10 +240,11 @@ add_task(async function testInstallAllExtensions() {
is(cards.length, 3, "We found some cards");
let extensions = await Promise.all(
cards.map(card => installAddon({card, recommendedList})));
cards.map(card => installAddon({ card, recommendedList }))
);
// The find more on AMO button is now shown.
checkExtraContents(doc, type, {showAmoButton: true});
checkExtraContents(doc, type, { showAmoButton: true });
// Uninstall one of the extensions, the button should be hidden again.
let extension = extensions.pop();
@ -235,9 +261,7 @@ add_task(async function testInstallAllExtensions() {
add_task(async function testError() {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.getAddons.discovery.api_url", "data:,"],
],
set: [["extensions.getAddons.discovery.api_url", "data:,"]],
});
let win = await loadInitialView("extension");
@ -247,7 +271,7 @@ add_task(async function testError() {
let recommendedList = doc.querySelector("recommended-addon-list");
await recommendedList.cardsReady;
checkExtraContents(doc, "extension", {showAmoButton: true});
checkExtraContents(doc, "extension", { showAmoButton: true });
await closeView(win);
await SpecialPowers.popPrefEnv();
@ -265,7 +289,7 @@ add_task(async function testThemesNoRecommendationUrl() {
let recommendedList = doc.querySelector("recommended-addon-list");
await recommendedList.cardsReady;
checkExtraContents(doc, "theme", {showThemeRecommendationFooter: false});
checkExtraContents(doc, "theme", { showThemeRecommendationFooter: false });
await closeView(win);
await SpecialPowers.popPrefEnv();

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

@ -12,7 +12,7 @@ function clickElement(el) {
el.dispatchEvent(new CustomEvent("click"));
}
function createMessageBar(messageBarStack, {attrs, children, onclose} = {}) {
function createMessageBar(messageBarStack, { attrs, children, onclose } = {}) {
const win = messageBarStack.ownerGlobal;
const messageBar = win.document.createElementNS(HTML_NS, "message-bar");
if (attrs) {
@ -27,16 +27,14 @@ function createMessageBar(messageBarStack, {attrs, children, onclose} = {}) {
messageBar.append(children);
}
}
messageBar.addEventListener("message-bar:close", onclose, {once: true});
messageBar.addEventListener("message-bar:close", onclose, { once: true });
messageBarStack.append(messageBar);
return messageBar;
}
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
set: [
["extensions.htmlaboutaddons.enabled", true],
],
set: [["extensions.htmlaboutaddons.enabled", true]],
});
htmlAboutAddonsWindow = await loadInitialView("extension");
@ -52,11 +50,17 @@ add_task(async function test_message_bar_stack() {
ok(messageBarStack, "Got a message-bar-stack in HTML about:addons page");
is(messageBarStack.maxMessageBarCount, 3,
"Got the expected max-message-bar-count property");
is(
messageBarStack.maxMessageBarCount,
3,
"Got the expected max-message-bar-count property"
);
is(messageBarStack.childElementCount, 0,
"message-bar-stack is initially empty");
is(
messageBarStack.childElementCount,
0,
"message-bar-stack is initially empty"
);
});
add_task(async function test_create_message_bar_create_and_onclose() {
@ -76,16 +80,28 @@ add_task(async function test_create_message_bar_create_and_onclose() {
});
});
is(messageBarStack.childElementCount, 1,
"message-bar-stack has a child element");
is(messageBarStack.firstElementChild, messageBar,
"newly created message-bar added as message-bar-stack child element");
is(
messageBarStack.childElementCount,
1,
"message-bar-stack has a child element"
);
is(
messageBarStack.firstElementChild,
messageBar,
"newly created message-bar added as message-bar-stack child element"
);
const slot = messageBar.shadowRoot.querySelector("slot");
is(slot.assignedNodes()[0], messageEl,
"Got the expected span element assigned to the message-bar slot");
is(slot.assignedNodes()[1], buttonEl,
"Got the expected button element assigned to the message-bar slot");
is(
slot.assignedNodes()[0],
messageEl,
"Got the expected span element assigned to the message-bar slot"
);
is(
slot.assignedNodes()[1],
buttonEl,
"Got the expected button element assigned to the message-bar slot"
);
info("Click the close icon on the newly created message-bar");
clickElement(messageBar.shadowRoot.querySelector("button.close"));
@ -93,8 +109,11 @@ add_task(async function test_create_message_bar_create_and_onclose() {
info("Expect the onclose function to be called");
await onceMessageBarClosed;
is(messageBarStack.childElementCount, 0,
"message-bar-stack has no child elements");
is(
messageBarStack.childElementCount,
0,
"message-bar-stack has no child elements"
);
});
add_task(async function test_max_message_bar_count() {
@ -112,26 +131,34 @@ add_task(async function test_max_message_bar_count() {
});
});
is(messageBarStack.childElementCount, 1,
"message-bar-stack has the expected number of children");
is(
messageBarStack.childElementCount,
1,
"message-bar-stack has the expected number of children"
);
info("Create 3 more message bars");
const allBarsPromises = [];
for (let i = 2; i <= 4; i++) {
allBarsPromises.push(new Promise(resolve => {
createMessageBar(messageBarStack, {
attrs: {dismissable: ""},
children: [messageElement, i],
onclose: resolve,
});
}));
allBarsPromises.push(
new Promise(resolve => {
createMessageBar(messageBarStack, {
attrs: { dismissable: "" },
children: [messageElement, i],
onclose: resolve,
});
})
);
}
info("Expect first message-bar to closed automatically");
await onceMessageBarClosed;
is(messageBarStack.childElementCount, 3,
"message-bar-stack has the expected number of children");
is(
messageBarStack.childElementCount,
3,
"message-bar-stack has the expected number of children"
);
info("Click on close icon for the second message-bar");
clickElement(messageBarStack.firstElementChild._closeIcon);
@ -139,8 +166,11 @@ add_task(async function test_max_message_bar_count() {
info("Expect the second message-bar to be closed");
await allBarsPromises[0];
is(messageBarStack.childElementCount, 2,
"message-bar-stack has the expected number of children");
is(
messageBarStack.childElementCount,
2,
"message-bar-stack has the expected number of children"
);
info("Clear the entire message-bar-stack content");
messageBarStack.textContent = "";
@ -148,6 +178,9 @@ add_task(async function test_max_message_bar_count() {
info("Expect all the created message-bar to be closed automatically");
await Promise.all(allBarsPromises);
is(messageBarStack.childElementCount, 0,
"message-bar-stack has no child elements");
is(
messageBarStack.childElementCount,
0,
"message-bar-stack has no child elements"
);
});

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

@ -12,7 +12,7 @@ add_task(async function enableHtmlViews() {
const DEFAULT_SECTION_NAMES = ["one", "two", "three"];
function makeButton({doc, name, deckId}) {
function makeButton({ doc, name, deckId }) {
let button = doc.createElement("named-deck-button");
button.setAttribute("name", name);
button.deckId = deckId;
@ -20,23 +20,23 @@ function makeButton({doc, name, deckId}) {
return button;
}
function makeSection({doc, name}) {
function makeSection({ doc, name }) {
let view = doc.createElement("section");
view.setAttribute("name", name);
view.textContent = name + name;
return view;
}
function addSection({name, deck, buttons}) {
function addSection({ name, deck, buttons }) {
let doc = deck.ownerDocument;
let button = makeButton({doc, name, deckId: deck.id});
let button = makeButton({ doc, name, deckId: deck.id });
buttons.appendChild(button);
let view = makeSection({doc, name});
let view = makeSection({ doc, name });
deck.appendChild(view);
return {button, view};
return { button, view };
}
async function runTests({deck, buttons}) {
async function runTests({ deck, buttons }) {
const selectedSlot = deck.shadowRoot.querySelector('slot[name="selected"]');
const getButtonByName = name => buttons.querySelector(`[name="${name}"]`);
@ -50,8 +50,11 @@ async function runTests({deck, buttons}) {
is(slottedEls.length, 0, "The deck is empty");
} else {
is(slottedEls.length, 1, "There's one visible view");
is(slottedEls[0].getAttribute("name"), name,
"The correct view is in the slot");
is(
slottedEls[0].getAttribute("name"),
name,
"The correct view is in the slot"
);
}
// Check that the hidden properties are set.
@ -71,8 +74,11 @@ async function runTests({deck, buttons}) {
for (let button of buttons.children) {
let buttonName = button.getAttribute("name");
let selected = buttonName == name;
is(button.hasAttribute("selected"), selected,
`${buttonName} is ${selected ? "selected" : "not selected"}`);
is(
button.hasAttribute("selected"),
selected,
`${buttonName} is ${selected ? "selected" : "not selected"}`
);
}
}
@ -86,7 +92,7 @@ async function runTests({deck, buttons}) {
// Add a new section, nothing changes.
info("Add section last");
let last = addSection({name: "last", deck, buttons});
let last = addSection({ name: "last", deck, buttons });
checkState("three", 4);
// We can switch to the new section.
@ -125,27 +131,29 @@ async function runTests({deck, buttons}) {
info("Add the missing view, it should be shown");
shown = BrowserTestUtils.waitForEvent(selectedSlot, "slotchange");
let viewChangedEvent = false;
let viewChangedFn = () => { viewChangedEvent = true; };
let viewChangedFn = () => {
viewChangedEvent = true;
};
deck.addEventListener("view-changed", viewChangedFn);
addSection({name: "missing", deck, buttons});
addSection({ name: "missing", deck, buttons });
await shown;
deck.removeEventListener("view-changed", viewChangedFn);
ok(!viewChangedEvent, "The view-changed event didn't fire");
checkState("missing", 4);
}
async function setup({doc, beAsync, first}) {
async function setup({ doc, beAsync, first }) {
const deckId = `${first}-first-${beAsync}`;
// Make the deck and buttons.
const deck = doc.createElement("named-deck");
deck.id = deckId;
for (let name of DEFAULT_SECTION_NAMES) {
deck.appendChild(makeSection({doc, name}));
deck.appendChild(makeSection({ doc, name }));
}
const buttons = doc.createElement("div");
for (let name of DEFAULT_SECTION_NAMES) {
buttons.appendChild(makeButton({doc, name, deckId}));
buttons.appendChild(makeButton({ doc, name, deckId }));
}
let ordered;
@ -164,7 +172,7 @@ async function setup({doc, beAsync, first}) {
}
doc.body.appendChild(ordered.shift());
return {deck, buttons};
return { deck, buttons };
}
add_task(async function testNamedDeckAndButtons() {
@ -173,15 +181,15 @@ add_task(async function testNamedDeckAndButtons() {
// Check adding the deck first.
dump("Running deck first tests synchronously");
await runTests(await setup({doc, beAsync: false, first: "deck"}));
await runTests(await setup({ doc, beAsync: false, first: "deck" }));
dump("Running deck first tests asynchronously");
await runTests(await setup({doc, beAsync: true, first: "deck"}));
await runTests(await setup({ doc, beAsync: true, first: "deck" }));
// Check adding the buttons first.
dump("Running buttons first tests synchronously");
await runTests(await setup({doc, beAsync: false, first: "buttons"}));
await runTests(await setup({ doc, beAsync: false, first: "buttons" }));
dump("Running buttons first tests asynchronously");
await runTests(await setup({doc, beAsync: true, first: "buttons"}));
await runTests(await setup({ doc, beAsync: true, first: "buttons" }));
await closeView(win);
});

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

@ -1,9 +1,11 @@
/* eslint max-len: ["error", 80] */
const {AddonTestUtils} =
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const {ExtensionParent} =
ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
const { ExtensionParent } = ChromeUtils.import(
"resource://gre/modules/ExtensionParent.jsm"
);
AddonTestUtils.initMochitest(this);
@ -39,7 +41,7 @@ add_task(async function testInlineOptions() {
let id = "inline@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id}},
applications: { gecko: { id } },
options_ui: {
page: "options.html",
},
@ -94,8 +96,8 @@ add_task(async function testInlineOptions() {
// Verify we're on the preferences tab.
card = doc.querySelector("addon-card");
is(card.addon.id, id, "The right page was loaded");
let {deck, tabGroup} = card.details;
let {selectedViewName} = deck;
let { deck, tabGroup } = card.details;
let { selectedViewName } = deck;
is(selectedViewName, "preferences", "The preferences tab is shown");
info("Check that there are two buttons and they're visible");
@ -110,24 +112,33 @@ add_task(async function testInlineOptions() {
// Check the attributes of the options browser.
let browser = card.querySelector("inline-options-browser browser");
ok(browser, "The visible view has a browser");
is(browser.currentURI.spec, card.addon.optionsURL,
"The browser has the expected options URL");
is(
browser.currentURI.spec,
card.addon.optionsURL,
"The browser has the expected options URL"
);
is(url, card.addon.optionsURL, "Browser has the expected options URL loaded");
let stack = browser.closest("stack");
is(browser.clientWidth, stack.clientWidth,
"Browser should be the same width as its direct parent");
is(
browser.clientWidth,
stack.clientWidth,
"Browser should be the same width as its direct parent"
);
ok(stack.clientWidth > 0, "The stack has a width");
ok(card.querySelector('[action="preferences"]').hidden,
"The preferences option is hidden now");
ok(
card.querySelector('[action="preferences"]').hidden,
"The preferences option is hidden now"
);
let lastHeight;
let waitForHeightChange = () => TestUtils.waitForCondition(() => {
if (browser.clientHeight !== lastHeight) {
lastHeight = browser.clientHeight;
return true;
}
return false;
});
let waitForHeightChange = () =>
TestUtils.waitForCondition(() => {
if (browser.clientHeight !== lastHeight) {
lastHeight = browser.clientHeight;
return true;
}
return false;
});
// The expected heights are 1px taller, to work around bug 1548687.
const EXPECTED_HEIGHT_SHORT = HEIGHT_SHORT + 1;
@ -178,7 +189,7 @@ add_task(async function testCardRerender() {
let id = "rerender@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id}},
applications: { gecko: { id } },
options_ui: {
page: "options.html",
},
@ -210,8 +221,11 @@ add_task(async function testCardRerender() {
card.querySelector('named-deck-button[name="preferences"]').click();
await browserAdded;
is(doc.querySelectorAll("inline-options-browser").length, 1,
"There is 1 inline-options-browser");
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is 1 inline-options-browser"
);
is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
info("Reload the add-on and ensure there's still only one browser");
@ -222,8 +236,11 @@ add_task(async function testCardRerender() {
card.addon.reload();
await updated;
is(doc.querySelectorAll("inline-options-browser").length, 1,
"There is 1 inline-options-browser");
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is 1 inline-options-browser"
);
is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
info("Re-rendering card to ensure a second browser isn't added");
@ -231,19 +248,28 @@ add_task(async function testCardRerender() {
card.render();
await updated;
is(card.details.deck.selectedViewName, "details",
"Rendering reverted to the details view");
is(
card.details.deck.selectedViewName,
"details",
"Rendering reverted to the details view"
);
is(doc.querySelectorAll("inline-options-browser").length, 1,
"There is still only 1 inline-options-browser after re-render");
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is still only 1 inline-options-browser after re-render"
);
is(doc.querySelectorAll("browser").length, 0, "There is no browser");
let newBrowserAdded = waitOptionsBrowserInserted();
card.showPrefs();
await newBrowserAdded;
is(doc.querySelectorAll("inline-options-browser").length, 1,
"There is still only 1 inline-options-browser after opening preferences");
is(
doc.querySelectorAll("inline-options-browser").length,
1,
"There is still only 1 inline-options-browser after opening preferences"
);
is(doc.querySelectorAll("browser").length, 1, "There is 1 browser");
await closeView(win);

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

@ -13,11 +13,11 @@ add_task(async function enableHtmlViews() {
});
});
async function testOptionsInTab({id, options_ui_options}) {
async function testOptionsInTab({ id, options_ui_options }) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Prefs extension",
applications: {gecko: {id}},
applications: { gecko: { id } },
options_ui: {
page: "options.html",
...options_ui_options,
@ -25,7 +25,9 @@ async function testOptionsInTab({id, options_ui_options}) {
},
background() {
browser.test.sendMessage(
"options-url", browser.runtime.getURL("options.html"));
"options-url",
browser.runtime.getURL("options.html")
);
},
files: {
"options.html": `<script src="options.js"></script>`,
@ -86,7 +88,7 @@ async function testOptionsInTab({id, options_ui_options}) {
add_task(async function testPreferencesLink() {
let id = "prefs@mochi.test";
await testOptionsInTab({id, options_ui_options: {open_in_tab: true}});
await testOptionsInTab({ id, options_ui_options: { open_in_tab: true } });
});
add_task(async function testPreferencesInlineDisabled() {
@ -95,7 +97,7 @@ add_task(async function testPreferencesInlineDisabled() {
});
let id = "inline-disabled@mochi.test";
await testOptionsInTab({id, options_ui_options: {}});
await testOptionsInTab({ id, options_ui_options: {} });
await SpecialPowers.popPrefEnv();
});
@ -105,7 +107,7 @@ add_task(async function testNoPreferences() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "No Prefs extension",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
useAddonManager: "temporary",
});

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

@ -12,17 +12,19 @@ add_task(async function enableHtmlViews() {
});
let gProvider = new MockProvider();
gProvider.createAddons([{
id: "no-ask-to-activate@mochi.test",
name: "No ask to activate",
getFullDescription(doc) {
let a = doc.createElement("a");
a.textContent = "A link";
a.href = "http://example.com/no-ask-to-activate";
return a;
gProvider.createAddons([
{
id: "no-ask-to-activate@mochi.test",
name: "No ask to activate",
getFullDescription(doc) {
let a = doc.createElement("a");
a.textContent = "A link";
a.href = "http://example.com/no-ask-to-activate";
return a;
},
type: "plugin",
},
type: "plugin",
}]);
]);
Services.telemetry.clearEvents();
});
@ -42,7 +44,8 @@ add_task(async function testAskToActivate() {
let plugins = await AddonManager.getAddonsByTypes(["plugin"]);
let flash = plugins.find(
plugin => plugin.description == TEST_PLUGIN_DESCRIPTION);
plugin => plugin.description == TEST_PLUGIN_DESCRIPTION
);
let addonId = flash.id;
// Reset to default value.
@ -54,14 +57,19 @@ add_task(async function testAskToActivate() {
let card = doc.querySelector(`addon-card[addon-id="${flash.id}"]`);
let panelItems = card.querySelectorAll("panel-item:not([hidden])");
let actions = Array.from(panelItems).map(item => item.getAttribute("action"));
Assert.deepEqual(actions, [
"ask-to-activate", "never-activate", "preferences", "expand",
], "The panel items are for a plugin");
Assert.deepEqual(
actions,
["ask-to-activate", "never-activate", "preferences", "expand"],
"The panel items are for a plugin"
);
checkItems(panelItems, "ask-to-activate");
is(flash.userDisabled, AddonManager.STATE_ASK_TO_ACTIVATE,
"Flash is ask-to-activate");
is(
flash.userDisabled,
AddonManager.STATE_ASK_TO_ACTIVATE,
"Flash is ask-to-activate"
);
ok(flash.isActive, "Flash is active");
// Switch to never activate.
@ -77,8 +85,11 @@ add_task(async function testAskToActivate() {
card.querySelector("panel-item[action*=ask]").click();
await updated;
checkItems(panelItems, "ask-to-activate");
is(flash.userDisabled, AddonManager.STATE_ASK_TO_ACTIVATE,
"Flash is ask-to-activate");
is(
flash.userDisabled,
AddonManager.STATE_ASK_TO_ACTIVATE,
"Flash is ask-to-activate"
);
ok(flash.isActive, "Flash is active");
// Check the detail view, too.
@ -93,12 +104,22 @@ add_task(async function testAskToActivate() {
await closeView(win);
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "plugin"}],
["addonsManager", "action", "aboutAddons", null,
{type: "plugin", addonId, view: "list", action: "disable"}],
["addonsManager", "view", "aboutAddons", "list", { type: "plugin" }],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "plugin", addonId, view: "list", action: "disable" },
],
// Ask-to-activate doesn't trigger a telemetry event.
["addonsManager", "view", "aboutAddons", "detail",
{type: "plugin", addonId}],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "plugin", addonId },
],
]);
});
@ -127,8 +148,10 @@ add_task(async function testNoAskToActivate() {
// There's no preferences option.
let actions = Array.from(menuItems).map(item => item.getAttribute("action"));
Assert.deepEqual(
actions, ["ask-to-activate", "always-activate", "never-activate", "expand"],
"The panel items are for a plugin");
actions,
["ask-to-activate", "always-activate", "never-activate", "expand"],
"The panel items are for a plugin"
);
// Open the details page.
let loaded = waitForViewLoad(win);
@ -152,8 +175,10 @@ add_task(async function testNoAskToActivate() {
// There's no preferences option, and expand is now hidden.
actions = Array.from(menuItems).map(item => item.getAttribute("action"));
Assert.deepEqual(
actions, ["ask-to-activate", "always-activate", "never-activate"],
"The panel items are for a detail page plugin");
actions,
["ask-to-activate", "always-activate", "never-activate"],
"The panel items are for a detail page plugin"
);
await closeView(win);
});

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

@ -13,57 +13,65 @@ add_task(async function enableHtmlViews() {
});
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon-today-2@mochi.test",
name: "Updated today two",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(6),
}, {
id: "addon-today-3@mochi.test",
name: "Updated today three",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(9),
}, {
id: "addon-today-1@mochi.test",
name: "Updated today",
creator: {name: "The creator"},
version: "3.1",
type: "extension",
releaseNotesURI: "http://example.com/notes.txt",
updateDate: dateHoursAgo(1),
}, {
id: "addon-yesterday-1@mochi.test",
name: "Updated yesterday one",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(15),
}, {
id: "addon-earlier@mochi.test",
name: "Updated earlier",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(49),
}, {
id: "addon-yesterday-2@mochi.test",
name: "Updated yesterday",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(24),
}, {
id: "addon-lastweek@mochi.test",
name: "Updated last week",
creator: {name: "The creator"},
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(192),
}]);
gProvider.createAddons([
{
id: "addon-today-2@mochi.test",
name: "Updated today two",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(6),
},
{
id: "addon-today-3@mochi.test",
name: "Updated today three",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(9),
},
{
id: "addon-today-1@mochi.test",
name: "Updated today",
creator: { name: "The creator" },
version: "3.1",
type: "extension",
releaseNotesURI: "http://example.com/notes.txt",
updateDate: dateHoursAgo(1),
},
{
id: "addon-yesterday-1@mochi.test",
name: "Updated yesterday one",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(15),
},
{
id: "addon-earlier@mochi.test",
name: "Updated earlier",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(49),
},
{
id: "addon-yesterday-2@mochi.test",
name: "Updated yesterday",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(24),
},
{
id: "addon-lastweek@mochi.test",
name: "Updated last week",
creator: { name: "The creator" },
version: "3.3",
type: "extension",
updateDate: dateHoursAgo(192),
},
]);
});
add_task(async function testRecentUpdatesList() {
@ -85,15 +93,22 @@ add_task(async function testRecentUpdatesList() {
.filter(id => id.endsWith("@mochi.test"));
// Verify that the add-ons are in the right order.
Assert.deepEqual(addonsInOrder(), [
"addon-today-1@mochi.test", "addon-today-2@mochi.test",
"addon-today-3@mochi.test", "addon-yesterday-1@mochi.test",
"addon-yesterday-2@mochi.test",
], "The add-ons are in the right order");
Assert.deepEqual(
addonsInOrder(),
[
"addon-today-1@mochi.test",
"addon-today-2@mochi.test",
"addon-today-3@mochi.test",
"addon-yesterday-1@mochi.test",
"addon-yesterday-2@mochi.test",
],
"The add-ons are in the right order"
);
info("Check that release notes are shown on the details page");
let card = list.querySelector(
'addon-card[addon-id="addon-today-1@mochi.test"]');
'addon-card[addon-id="addon-today-1@mochi.test"]'
);
loaded = waitForViewLoad(win);
card.querySelector('[action="expand"]').click();
await loaded;
@ -101,8 +116,10 @@ add_task(async function testRecentUpdatesList() {
card = doc.querySelector("addon-card");
ok(card.expanded, "The card is expanded");
ok(!card.details.tabGroup.hidden, "The tabs are shown");
ok(!card.details.tabGroup.querySelector('[name="release-notes"]').hidden,
"The release notes button is shown");
ok(
!card.details.tabGroup.querySelector('[name="release-notes"]').hidden,
"The release notes button is shown"
);
info("Go back to the recent updates view");
loaded = waitForViewLoad(win);
@ -117,7 +134,7 @@ add_task(async function testRecentUpdatesList() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "New extension",
applications: {gecko: {id: "new@mochi.test"}},
applications: { gecko: { id: "new@mochi.test" } },
},
useAddonManager: "temporary",
});
@ -126,11 +143,18 @@ add_task(async function testRecentUpdatesList() {
await added;
// The new extension should now be at the top of the list.
Assert.deepEqual(addonsInOrder(), [
"new@mochi.test", "addon-today-1@mochi.test", "addon-today-2@mochi.test",
"addon-today-3@mochi.test", "addon-yesterday-1@mochi.test",
"addon-yesterday-2@mochi.test",
], "The new add-on went to the top");
Assert.deepEqual(
addonsInOrder(),
[
"new@mochi.test",
"addon-today-1@mochi.test",
"addon-today-2@mochi.test",
"addon-today-3@mochi.test",
"addon-yesterday-1@mochi.test",
"addon-yesterday-2@mochi.test",
],
"The new add-on went to the top"
);
// Open the detail view for the new add-on.
card = list.querySelector('addon-card[addon-id="new@mochi.test"]');
@ -138,8 +162,11 @@ add_task(async function testRecentUpdatesList() {
card.querySelector('[action="expand"]').click();
await loaded;
is(win.managerWindow.gCategories.selected, "addons://list/extension",
"The extensions category is selected");
is(
win.managerWindow.gCategories.selected,
"addons://list/extension",
"The extensions category is selected"
);
await closeView(win);
await extension.unload();

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

@ -37,7 +37,7 @@ async function checkRecommendedBadge(id, hidden) {
add_task(async function testNotRecommended() {
let id = "not-recommended@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {applications: {gecko: {id}}},
manifest: { applications: { gecko: { id } } },
useAddonManager: "temporary",
});
await extension.startup();
@ -50,12 +50,14 @@ add_task(async function testNotRecommended() {
add_task(async function testRecommended() {
let id = "recommended@mochi.test";
let provider = new MockProvider();
provider.createAddons([{
id,
isRecommended: true,
name: "Recommended",
type: "extension",
}]);
provider.createAddons([
{
id,
isRecommended: true,
name: "Recommended",
type: "extension",
},
]);
await checkRecommendedBadge(id, false);
});

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

@ -1,7 +1,8 @@
/* eslint max-len: ["error", 80] */
const {AddonTestUtils} =
ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
AddonTestUtils.initMochitest(this);
@ -16,7 +17,7 @@ function loadDetailView(win, id) {
let doc = win.document;
let card = doc.querySelector(`addon-card[addon-id="${id}"]`);
let loaded = waitForViewLoad(win);
EventUtils.synthesizeMouseAtCenter(card, {clickCount: 1}, win);
EventUtils.synthesizeMouseAtCenter(card, { clickCount: 1 }, win);
return loaded;
}
@ -25,7 +26,7 @@ add_task(async function testChangeAutoUpdates() {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
name: "Test",
applications: {gecko: {id}},
applications: { gecko: { id } },
},
// Use permanent so the add-on can be updated.
useAddonManager: "permanent",
@ -109,24 +110,54 @@ add_task(async function testChangeAutoUpdates() {
await extension.unload();
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", "enabled",
{type: "extension", addonId: id, action: "setAddonUpdate"}],
["addonsManager", "action", "aboutAddons", "",
{type: "extension", addonId: id, action: "setAddonUpdate"}],
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", "default",
{type: "extension", addonId: id, action: "setAddonUpdate"}],
["addonsManager", "action", "aboutAddons", "enabled",
{type: "extension", addonId: id, action: "setAddonUpdate"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
"enabled",
{ type: "extension", addonId: id, action: "setAddonUpdate" },
],
[
"addonsManager",
"action",
"aboutAddons",
"",
{ type: "extension", addonId: id, action: "setAddonUpdate" },
],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
"default",
{ type: "extension", addonId: id, action: "setAddonUpdate" },
],
[
"addonsManager",
"action",
"aboutAddons",
"enabled",
{ type: "extension", addonId: id, action: "setAddonUpdate" },
],
]);
});
async function setupExtensionWithUpdate(id, {releaseNotes} = {}) {
async function setupExtensionWithUpdate(id, { releaseNotes } = {}) {
await SpecialPowers.pushPrefEnv({
set: [["extensions.checkUpdateSecurity", false]],
});
@ -173,11 +204,13 @@ async function setupExtensionWithUpdate(id, {releaseNotes} = {}) {
AddonTestUtils.registerJSON(server, updatesPath, {
addons: {
[id]: {
updates: [{
version: "2",
update_link: serverHost + xpiFilename,
...releaseNotesExtra,
}],
updates: [
{
version: "2",
update_link: serverHost + xpiFilename,
...releaseNotesExtra,
},
],
},
},
});
@ -204,7 +237,7 @@ function disableAutoUpdates(card) {
ok(!updateCheckButton.hidden, "The button is now visible");
// There shouldn't be an update shown to the user.
assertUpdateState({card, shown: false});
assertUpdateState({ card, shown: false });
}
function checkForUpdate(card, expected) {
@ -223,24 +256,36 @@ function installUpdate(card, expected) {
}
function assertUpdateState({
card, shown, expanded = true, releaseNotes = false,
card,
shown,
expanded = true,
releaseNotes = false,
}) {
let menuButton = card.querySelector(".more-options-button");
ok(menuButton.classList.contains("more-options-button-badged") == shown,
"The menu button is badged");
ok(
menuButton.classList.contains("more-options-button-badged") == shown,
"The menu button is badged"
);
let installButton = card.querySelector('panel-item[action="install-update"]');
ok(installButton.hidden != shown,
`The install button is ${shown ? "hidden" : "shown"}`);
ok(
installButton.hidden != shown,
`The install button is ${shown ? "hidden" : "shown"}`
);
if (expanded) {
let updateCheckButton = card.querySelector('button[action="update-check"]');
ok(updateCheckButton.hidden == shown,
`The update check button is ${shown ? "hidden" : "shown"}`);
ok(
updateCheckButton.hidden == shown,
`The update check button is ${shown ? "hidden" : "shown"}`
);
let {tabGroup} = card.details;
let { tabGroup } = card.details;
is(tabGroup.hidden, false, "The tab group is shown");
let notesBtn = tabGroup.querySelector('[name="release-notes"]');
is(notesBtn.hidden, !releaseNotes,
`The release notes button is ${releaseNotes ? "shown" : "hidden"}`);
is(
notesBtn.hidden,
!releaseNotes,
`The release notes button is ${releaseNotes ? "shown" : "hidden"}`
);
}
}
@ -260,7 +305,7 @@ add_task(async function testUpdateAvailable() {
await checkForUpdate(card, "update-found");
// There should now be an update.
assertUpdateState({card, shown: true});
assertUpdateState({ card, shown: true });
// The version was 1.
let versionRow = card.querySelector(".addon-detail-row-version");
@ -273,7 +318,7 @@ add_task(async function testUpdateAvailable() {
is(versionRow.lastChild.textContent, "2", "The version has updated");
// No update is shown again.
assertUpdateState({card, shown: false});
assertUpdateState({ card, shown: false });
// Check for updates again, there shouldn't be an update.
await checkForUpdate(card, "no-update");
@ -308,14 +353,14 @@ add_task(async function testReleaseNotesLoad() {
await loadDetailView(win, id);
let card = doc.querySelector("addon-card");
let {deck, tabGroup} = card.details;
let { deck, tabGroup } = card.details;
// Disable updates and then check.
disableAutoUpdates(card);
await checkForUpdate(card, "update-found");
// There should now be an update.
assertUpdateState({card, shown: true, releaseNotes: true});
assertUpdateState({ card, shown: true, releaseNotes: true });
info("Check release notes");
let notesBtn = tabGroup.querySelector('[name="release-notes"]');
@ -326,8 +371,11 @@ add_task(async function testReleaseNotesLoad() {
// See bug 1551621 for more info.
EventUtils.synthesizeMouseAtCenter(notesBtn, {}, win);
await loading;
is(doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-loading", "The loading message is shown");
is(
doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-loading",
"The loading message is shown"
);
await loaded;
info("Checking HTML release notes");
let [h1, ul, a] = notes.children;
@ -362,7 +410,9 @@ add_task(async function testReleaseNotesLoad() {
// Load release notes again, verify they weren't loaded.
viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
let notesCached = BrowserTestUtils.waitForEvent(
notes, "release-notes-cached");
notes,
"release-notes-cached"
);
notesBtn.click();
await viewChanged;
await notesCached;
@ -372,29 +422,54 @@ add_task(async function testReleaseNotesLoad() {
await installUpdate(card, "update-installed");
// There's no more update but release notes are still shown.
assertUpdateState({card, shown: false, releaseNotes: true});
assertUpdateState({ card, shown: false, releaseNotes: true });
await closeView(win);
await extension.unload();
assertAboutAddonsTelemetryEvents([
["addonsManager", "view", "aboutAddons", "list", {type: "extension"}],
["addonsManager", "view", "aboutAddons", "detail",
{type: "extension", addonId: id}],
["addonsManager", "action", "aboutAddons", "",
{type: "extension", addonId: id, action: "setAddonUpdate"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "checkForUpdate"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "releaseNotes"}],
["addonsManager", "action", "aboutAddons", null,
{type: "extension", addonId: id, action: "releaseNotes"}],
["addonsManager", "view", "aboutAddons", "list", { type: "extension" }],
[
"addonsManager",
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: id },
],
[
"addonsManager",
"action",
"aboutAddons",
"",
{ type: "extension", addonId: id, action: "setAddonUpdate" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "checkForUpdate" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "releaseNotes" },
],
[
"addonsManager",
"action",
"aboutAddons",
null,
{ type: "extension", addonId: id, action: "releaseNotes" },
],
]);
});
add_task(async function testReleaseNotesError() {
let id = "update-with-notes-error@mochi.test";
let extension = await setupExtensionWithUpdate(id, {releaseNotes: "ERROR"});
let extension = await setupExtensionWithUpdate(id, { releaseNotes: "ERROR" });
let win = await loadInitialView("extension");
let doc = win.document;
@ -402,14 +477,14 @@ add_task(async function testReleaseNotesError() {
await loadDetailView(win, id);
let card = doc.querySelector("addon-card");
let {deck, tabGroup} = card.details;
let { deck, tabGroup } = card.details;
// Disable updates and then check.
disableAutoUpdates(card);
await checkForUpdate(card, "update-found");
// There should now be an update.
assertUpdateState({card, shown: true, releaseNotes: true});
assertUpdateState({ card, shown: true, releaseNotes: true });
info("Check release notes");
let notesBtn = tabGroup.querySelector('[name="release-notes"]');
@ -420,11 +495,17 @@ add_task(async function testReleaseNotesError() {
// See bug 1551621 for more info.
EventUtils.synthesizeMouseAtCenter(notesBtn, {}, win);
await loading;
is(doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-loading", "The loading message is shown");
is(
doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-loading",
"The loading message is shown"
);
await errored;
is(doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-error", "The error message is shown");
is(
doc.l10n.getAttributes(notes.firstElementChild).id,
"release-notes-error",
"The error message is shown"
);
info("Switch away and back to release notes");
// Load details view.
@ -436,7 +517,9 @@ add_task(async function testReleaseNotesError() {
// Load release notes again, verify they weren't loaded.
viewChanged = BrowserTestUtils.waitForEvent(deck, "view-changed");
let notesCached = BrowserTestUtils.waitForEvent(
notes, "release-notes-cached");
notes,
"release-notes-cached"
);
notesBtn.click();
await viewChanged;
await notesCached;
@ -463,7 +546,7 @@ add_task(async function testUpdateCancelled() {
await checkForUpdate(card, "update-found");
// There should now be an update.
assertUpdateState({card, shown: true});
assertUpdateState({ card, shown: true });
// The add-on starts as version 1.
let versionRow = card.querySelector(".addon-detail-row-version");
@ -481,7 +564,7 @@ add_task(async function testUpdateCancelled() {
is(versionRow.lastChild.textContent, "1", "The version hasn't changed");
// The update has been removed.
assertUpdateState({card, shown: false});
assertUpdateState({ card, shown: false });
await closeView(win);
await extension.unload();
@ -498,7 +581,7 @@ add_task(async function testAvailableUpdates() {
let doc = win.document;
let managerDoc = win.managerWindow.document;
let {gCategories} = win.managerWindow;
let { gCategories } = win.managerWindow;
let availableCat = gCategories.get("addons://updates/available");
ok(availableCat.hidden, "Available updates is hidden");
@ -527,13 +610,16 @@ add_task(async function testAvailableUpdates() {
// Each card should have an update.
for (let card of cards) {
assertUpdateState({card, shown: true, expanded: false});
assertUpdateState({ card, shown: true, expanded: false });
}
// Check the detail page for the first add-on.
await loadDetailView(win, ids[0]);
is(gCategories.selected, "addons://list/extension",
"The extensions category is selected");
is(
gCategories.selected,
"addons://list/extension",
"The extensions category is selected"
);
// Go back to the last view.
loaded = waitForViewLoad(win);
@ -541,8 +627,11 @@ add_task(async function testAvailableUpdates() {
await loaded;
// We're back on the updates view.
is(gCategories.selected, "addons://updates/available",
"The available updates category is selected");
is(
gCategories.selected,
"addons://updates/available",
"The available updates category is selected"
);
// Find the cards again.
cards = doc.querySelectorAll("addon-card");
@ -550,23 +639,29 @@ add_task(async function testAvailableUpdates() {
// Install the first update.
await installUpdate(cards[0], "update-installed");
assertUpdateState({card: cards[0], shown: false, expanded: false});
assertUpdateState({ card: cards[0], shown: false, expanded: false });
// The count goes down but the card stays.
is(availableCat.badgeCount, 2, "There are only 2 updates now");
is(doc.querySelectorAll("addon-card").length, 3,
"All 3 cards are still visible on the updates page");
is(
doc.querySelectorAll("addon-card").length,
3,
"All 3 cards are still visible on the updates page"
);
// Install the other two updates.
await installUpdate(cards[1], "update-installed");
assertUpdateState({card: cards[1], shown: false, expanded: false});
assertUpdateState({ card: cards[1], shown: false, expanded: false });
await installUpdate(cards[2], "update-installed");
assertUpdateState({card: cards[2], shown: false, expanded: false});
assertUpdateState({ card: cards[2], shown: false, expanded: false });
// The count goes down but the card stays.
is(availableCat.badgeCount, 0, "There are no more updates");
is(doc.querySelectorAll("addon-card").length, 3,
"All 3 cards are still visible on the updates page");
is(
doc.querySelectorAll("addon-card").length,
3,
"All 3 cards are still visible on the updates page"
);
// Enable global add-on updates again.
AddonManager.autoUpdateDefault = true;

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

@ -6,16 +6,21 @@
let gProvider;
const {
STATE_BLOCKED, STATE_OUTDATED, STATE_SOFTBLOCKED, STATE_VULNERABLE_NO_UPDATE,
STATE_BLOCKED,
STATE_OUTDATED,
STATE_SOFTBLOCKED,
STATE_VULNERABLE_NO_UPDATE,
STATE_VULNERABLE_UPDATE_AVAILABLE,
} = Ci.nsIBlocklistService;
const brandBundle =
Services.strings.createBundle("chrome://branding/locale/brand.properties");
const brandBundle = Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
const appName = brandBundle.GetStringFromName("brandShortName");
const appVersion = Services.appinfo.version;
const SUPPORT_URL = Services.urlFormatter.formatURL(
Services.prefs.getStringPref("app.support.baseURL"));
Services.prefs.getStringPref("app.support.baseURL")
);
add_task(async function setup() {
await SpecialPowers.pushPrefEnv({
@ -32,12 +37,15 @@ async function checkMessageState(id, addonType, expected) {
if (!expected) {
ok(messageBar.hidden, "message is hidden");
} else {
let {linkText, linkUrl, text, type} = expected;
let { linkText, linkUrl, text, type } = expected;
ok(!messageBar.hidden, "message is visible");
is(messageBar.getAttribute("type"), type, "message has the right type");
is(messageBar.querySelector("span").textContent,
text, "message has the right text");
is(
messageBar.querySelector("span").textContent,
text,
"message has the right text"
);
let link = messageBar.querySelector("button");
if (linkUrl) {
@ -76,7 +84,7 @@ async function checkMessageState(id, addonType, expected) {
add_task(async function testNoMessageExtension() {
let id = "no-message@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {applications: {gecko: {id}}},
manifest: { applications: { gecko: { id } } },
useAddonManager: "temporary",
});
await extension.startup();
@ -88,13 +96,15 @@ add_task(async function testNoMessageExtension() {
add_task(async function testNoMessageLangpack() {
let id = "no-message@mochi.test";
gProvider.createAddons([{
appDisabled: true,
id,
name: "Signed Langpack",
signedState: AddonManager.SIGNEDSTATE_SIGNED,
type: "locale",
}]);
gProvider.createAddons([
{
appDisabled: true,
id,
name: "Signed Langpack",
signedState: AddonManager.SIGNEDSTATE_SIGNED,
type: "locale",
},
]);
await checkMessageState(id, "locale", null);
});
@ -102,14 +112,16 @@ add_task(async function testNoMessageLangpack() {
add_task(async function testBlocked() {
let id = "blocked@mochi.test";
let linkUrl = "https://example.com/addon-blocked";
gProvider.createAddons([{
appDisabled: true,
blocklistState: STATE_BLOCKED,
blocklistURL: linkUrl,
id,
isActive: false,
name: "Blocked",
}]);
gProvider.createAddons([
{
appDisabled: true,
blocklistState: STATE_BLOCKED,
blocklistURL: linkUrl,
id,
isActive: false,
name: "Blocked",
},
]);
await checkMessageState(id, "extension", {
linkText: "More Information",
linkUrl,
@ -124,17 +136,20 @@ add_task(async function testUnsignedDisabled() {
});
let id = "unsigned@mochi.test";
gProvider.createAddons([{
appDisabled: true,
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
}]);
gProvider.createAddons([
{
appDisabled: true,
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
},
]);
await checkMessageState(id, "extension", {
linkText: "More Information",
linkUrl: SUPPORT_URL + "unsigned-addons",
text:
"Unsigned could not be verified for use in " + appName +
"Unsigned could not be verified for use in " +
appName +
" and has been disabled.",
type: "error",
});
@ -144,18 +159,21 @@ add_task(async function testUnsignedDisabled() {
add_task(async function testUnsignedLangpackDisabled() {
let id = "unsigned-langpack@mochi.test";
gProvider.createAddons([{
appDisabled: true,
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
type: "locale",
}]);
gProvider.createAddons([
{
appDisabled: true,
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
type: "locale",
},
]);
await checkMessageState(id, "locale", {
linkText: "More Information",
linkUrl: SUPPORT_URL + "unsigned-addons",
text:
"Unsigned could not be verified for use in " + appName +
"Unsigned could not be verified for use in " +
appName +
" and has been disabled.",
type: "error",
});
@ -163,13 +181,15 @@ add_task(async function testUnsignedLangpackDisabled() {
add_task(async function testIncompatible() {
let id = "incompatible@mochi.test";
gProvider.createAddons([{
appDisabled: true,
id,
isActive: false,
isCompatible: false,
name: "Incompatible",
}]);
gProvider.createAddons([
{
appDisabled: true,
id,
isActive: false,
isCompatible: false,
name: "Incompatible",
},
]);
await checkMessageState(id, "extension", {
text:
"Incompatible is incompatible with " + appName + " " + appVersion + ".",
@ -179,16 +199,19 @@ add_task(async function testIncompatible() {
add_task(async function testUnsignedEnabled() {
let id = "unsigned-allowed@mochi.test";
gProvider.createAddons([{
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
}]);
gProvider.createAddons([
{
id,
name: "Unsigned",
signedState: AddonManager.SIGNEDSTATE_MISSING,
},
]);
await checkMessageState(id, "extension", {
linkText: "More Information",
linkUrl: SUPPORT_URL + "unsigned-addons",
text:
"Unsigned could not be verified for use in " + appName +
"Unsigned could not be verified for use in " +
appName +
". Proceed with caution.",
type: "warning",
});
@ -200,17 +223,20 @@ add_task(async function testUnsignedLangpackEnabled() {
});
let id = "unsigned-allowed-langpack@mochi.test";
gProvider.createAddons([{
id,
name: "Unsigned Langpack",
signedState: AddonManager.SIGNEDSTATE_MISSING,
type: "locale",
}]);
gProvider.createAddons([
{
id,
name: "Unsigned Langpack",
signedState: AddonManager.SIGNEDSTATE_MISSING,
type: "locale",
},
]);
await checkMessageState(id, "locale", {
linkText: "More Information",
linkUrl: SUPPORT_URL + "unsigned-addons",
text:
"Unsigned Langpack could not be verified for use in " + appName +
"Unsigned Langpack could not be verified for use in " +
appName +
". Proceed with caution.",
type: "warning",
});
@ -221,14 +247,16 @@ add_task(async function testUnsignedLangpackEnabled() {
add_task(async function testSoftBlocked() {
let id = "softblocked@mochi.test";
let linkUrl = "https://example.com/addon-blocked";
gProvider.createAddons([{
appDisabled: true,
blocklistState: STATE_SOFTBLOCKED,
blocklistURL: linkUrl,
id,
isActive: false,
name: "Soft Blocked",
}]);
gProvider.createAddons([
{
appDisabled: true,
blocklistState: STATE_SOFTBLOCKED,
blocklistURL: linkUrl,
id,
isActive: false,
name: "Soft Blocked",
},
]);
await checkMessageState(id, "extension", {
linkText: "More Information",
linkUrl,
@ -240,12 +268,14 @@ add_task(async function testSoftBlocked() {
add_task(async function testOutdated() {
let id = "outdated@mochi.test";
let linkUrl = "https://example.com/addon-blocked";
gProvider.createAddons([{
blocklistState: STATE_OUTDATED,
blocklistURL: linkUrl,
id,
name: "Outdated",
}]);
gProvider.createAddons([
{
blocklistState: STATE_OUTDATED,
blocklistURL: linkUrl,
id,
name: "Outdated",
},
]);
await checkMessageState(id, "extension", {
linkText: "Update Now",
linkUrl,
@ -257,12 +287,14 @@ add_task(async function testOutdated() {
add_task(async function testVulnerableUpdate() {
let id = "vulnerable-update@mochi.test";
let linkUrl = "https://example.com/addon-blocked";
gProvider.createAddons([{
blocklistState: STATE_VULNERABLE_UPDATE_AVAILABLE,
blocklistURL: linkUrl,
id,
name: "Vulnerable Update",
}]);
gProvider.createAddons([
{
blocklistState: STATE_VULNERABLE_UPDATE_AVAILABLE,
blocklistURL: linkUrl,
id,
name: "Vulnerable Update",
},
]);
await checkMessageState(id, "extension", {
linkText: "Update Now",
linkUrl,
@ -274,12 +306,14 @@ add_task(async function testVulnerableUpdate() {
add_task(async function testVulnerableNoUpdate() {
let id = "vulnerable-no-update@mochi.test";
let linkUrl = "https://example.com/addon-blocked";
gProvider.createAddons([{
blocklistState: STATE_VULNERABLE_NO_UPDATE,
blocklistURL: linkUrl,
id,
name: "Vulnerable No Update",
}]);
gProvider.createAddons([
{
blocklistState: STATE_VULNERABLE_NO_UPDATE,
blocklistURL: linkUrl,
id,
name: "Vulnerable No Update",
},
]);
await checkMessageState(id, "extension", {
linkText: "More Information",
linkUrl,
@ -290,14 +324,16 @@ add_task(async function testVulnerableNoUpdate() {
add_task(async function testPluginInstalling() {
let id = "plugin-installing@mochi.test";
gProvider.createAddons([{
id,
isActive: true,
isGMPlugin: true,
isInstalled: false,
name: "Plugin Installing",
type: "plugin",
}]);
gProvider.createAddons([
{
id,
isActive: true,
isGMPlugin: true,
isInstalled: false,
name: "Plugin Installing",
type: "plugin",
},
]);
await checkMessageState(id, "plugin", {
text: "Plugin Installing will be installed shortly.",
type: "warning",

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

@ -5,7 +5,9 @@
/* globals TestUtils */
let {ExtensionTestCommon} = ChromeUtils.import("resource://testing-common/ExtensionTestCommon.jsm");
let { ExtensionTestCommon } = ChromeUtils.import(
"resource://testing-common/ExtensionTestCommon.jsm"
);
ChromeUtils.import("resource://testing-common/ContentTask.jsm", {});
@ -22,12 +24,13 @@ var gManagerWindow;
var gCategoryUtilities;
function installAddon(details) {
let id = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
.generateUUID().number;
let id = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator)
.generateUUID().number;
if (!details.manifest) {
details.manifest = {};
}
details.manifest.applications = {gecko: {id}};
details.manifest.applications = { gecko: { id } };
let xpi = ExtensionTestCommon.generateXPI(details);
return AddonManager.installTemporaryAddon(xpi).then(addon => {
@ -45,8 +48,8 @@ function installAddon(details) {
add_task(async function() {
gAddon = await installAddon({
manifest: {
"options_ui": {
"page": "options.html",
options_ui: {
page: "options.html",
},
},
@ -80,42 +83,63 @@ add_task(async function() {
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
});
async function openDetailsBrowser(addonId) {
var addon = get_addon_element(gManagerWindow, addonId);
is(addon.mAddon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_BROWSER,
"Options should be inline browser type");
is(
addon.mAddon.optionsType,
AddonManager.OPTIONS_TYPE_INLINE_BROWSER,
"Options should be inline browser type"
);
addon.parentNode.ensureElementIsVisible(addon);
var button = gManagerWindow.document.getAnonymousElementByAttribute(addon, "anonid", "preferences-btn");
var button = gManagerWindow.document.getAnonymousElementByAttribute(
addon,
"anonid",
"preferences-btn"
);
is_element_visible(button, "Preferences button should be visible");
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, gManagerWindow);
await TestUtils.topicObserved(AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
(subject, data) => data == addonId);
await TestUtils.topicObserved(
AddonManager.OPTIONS_NOTIFICATION_DISPLAYED,
(subject, data) => data == addonId
);
is(gManagerWindow.gViewController.currentViewId,
`addons://detail/${encodeURIComponent(addonId)}/preferences`,
"Current view should scroll to preferences");
is(
gManagerWindow.gViewController.currentViewId,
`addons://detail/${encodeURIComponent(addonId)}/preferences`,
"Current view should scroll to preferences"
);
var browser = gManagerWindow.document.querySelector(
"#detail-grid > rows > stack > .inline-options-browser");
"#detail-grid > rows > stack > .inline-options-browser"
);
var rows = browser.parentNode.parentNode;
let url = await ContentTask.spawn(browser, {}, () => content.location.href);
ok(browser, "Grid should have a browser descendant");
is(browser.localName, "browser", "Grid should have a browser descendant");
is(url, addon.mAddon.optionsURL, "Browser has the expected options URL loaded");
is(
url,
addon.mAddon.optionsURL,
"Browser has the expected options URL loaded"
);
is(browser.clientWidth, browser.parentNode.clientWidth,
"Browser should be the same width as its direct parent");
is(browser.clientWidth, rows.clientWidth,
"Browser should be the same width as its rows ancestor");
is(
browser.clientWidth,
browser.parentNode.clientWidth,
"Browser should be the same width as its direct parent"
);
is(
browser.clientWidth,
rows.clientWidth,
"Browser should be the same width as its rows ancestor"
);
button = gManagerWindow.document.getElementById("detail-prefs-btn");
is_element_hidden(button, "Preferences button should not be visible");
@ -123,23 +147,35 @@ async function openDetailsBrowser(addonId) {
return browser;
}
add_task(async function test_inline_browser_addon() {
let browser = await openDetailsBrowser(gAddon.id);
function checkHeights(expected) {
let {clientHeight} = browser;
return ContentTask.spawn(browser, {expected, clientHeight}, ({expected, clientHeight}) => {
let {body} = content.document;
let { clientHeight } = browser;
return ContentTask.spawn(
browser,
{ expected, clientHeight },
({ expected, clientHeight }) => {
let { body } = content.document;
is(body.clientHeight, expected, `Document body should be ${expected}px tall`);
is(body.clientHeight, body.scrollHeight,
"Document body should be tall enough to fit its contents");
is(
body.clientHeight,
expected,
`Document body should be ${expected}px tall`
);
is(
body.clientHeight,
body.scrollHeight,
"Document body should be tall enough to fit its contents"
);
let heightDiff = clientHeight - expected;
ok(heightDiff >= 0 && heightDiff < 50,
`Browser should be slightly taller than the document body (${clientHeight} vs. ${expected})`);
});
let heightDiff = clientHeight - expected;
ok(
heightDiff >= 0 && heightDiff < 50,
`Browser should be slightly taller than the document body (${clientHeight} vs. ${expected})`
);
}
);
}
// Delay long enough to avoid hitting our resize rate limit.
@ -149,7 +185,9 @@ add_task(async function test_inline_browser_addon() {
await checkHeights(300);
info("Increase the document height, and expect the browser to grow correspondingly");
info(
"Increase the document height, and expect the browser to grow correspondingly"
);
await ContentTask.spawn(browser, null, () => {
content.document.body.classList.toggle("bigger");
});
@ -158,7 +196,9 @@ add_task(async function test_inline_browser_addon() {
await checkHeights(600);
info("Decrease the document height, and expect the browser to shrink correspondingly");
info(
"Decrease the document height, and expect the browser to shrink correspondingly"
);
await ContentTask.spawn(browser, null, () => {
content.document.body.classList.toggle("bigger");
});
@ -168,15 +208,14 @@ add_task(async function test_inline_browser_addon() {
await checkHeights(300);
await new Promise(resolve =>
gCategoryUtilities.openType("extension", resolve));
gCategoryUtilities.openType("extension", resolve)
);
browser = gManagerWindow.document.querySelector(
".inline-options-browser");
browser = gManagerWindow.document.querySelector(".inline-options-browser");
is(browser, null, "Options browser should be removed from the document");
});
// Test that loading an add-on with no inline browser works as expected
// after having viewed our main test add-on.
add_task(async function test_plain_addon() {
@ -186,36 +225,43 @@ add_task(async function test_plain_addon() {
addon.parentNode.ensureElementIsVisible(addon);
await EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 1 }, gManagerWindow);
await EventUtils.synthesizeMouseAtCenter(
addon,
{ clickCount: 1 },
gManagerWindow
);
EventUtils.synthesizeMouseAtCenter(addon, { clickCount: 2 }, gManagerWindow);
await BrowserTestUtils.waitForEvent(gManagerWindow, "ViewChanged");
is(gManagerWindow.gViewController.currentViewId,
`addons://detail/${encodeURIComponent(gOtherAddon.id)}`,
"Detail view should be open");
is(
gManagerWindow.gViewController.currentViewId,
`addons://detail/${encodeURIComponent(gOtherAddon.id)}`,
"Detail view should be open"
);
var browser = gManagerWindow.document.querySelector(
"#detail-grid > rows > .inline-options-browser");
"#detail-grid > rows > .inline-options-browser"
);
is(browser, null, "Detail view should have no inline browser");
await new Promise(resolve =>
gCategoryUtilities.openType("extension", resolve));
gCategoryUtilities.openType("extension", resolve)
);
});
// Test that loading the original add-on details successfully creates a
// browser.
add_task(async function test_inline_browser_addon_again() {
let browser = await openDetailsBrowser(gAddon.id);
await new Promise(resolve =>
gCategoryUtilities.openType("extension", resolve));
gCategoryUtilities.openType("extension", resolve)
);
browser = gManagerWindow.document.querySelector(
".inline-options-browser");
browser = gManagerWindow.document.querySelector(".inline-options-browser");
is(browser, null, "Options browser should be removed from the document");
});

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

@ -14,7 +14,8 @@ const SELFSIGNED = "https://self-signed.example.com/";
const UNTRUSTED = "https://untrusted.example.com/";
const EXPIRED = "https://expired.example.com/";
const PREF_INSTALL_REQUIREBUILTINCERTS = "extensions.install.requireBuiltInCerts";
const PREF_INSTALL_REQUIREBUILTINCERTS =
"extensions.install.requireBuiltInCerts";
var gTests = [];
var gStart = 0;
@ -27,8 +28,9 @@ function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
var cos = Cc["@mozilla.org/security/certoverride;1"].
getService(Ci.nsICertOverrideService);
var cos = Cc["@mozilla.org/security/certoverride;1"].getService(
Ci.nsICertOverrideService
);
cos.clearValidityOverride("nocert.example.com", -1);
cos.clearValidityOverride("self-signed.example.com", -1);
cos.clearValidityOverride("untrusted.example.com", -1);
@ -36,16 +38,18 @@ function test() {
try {
Services.prefs.clearUserPref(PREF_INSTALL_REQUIREBUILTINCERTS);
} catch (e) {
}
} catch (e) {}
if (gPendingInstall) {
gTests = [];
ok(false, "Timed out in the middle of downloading " + gPendingInstall.sourceURI.spec);
ok(
false,
"Timed out in the middle of downloading " +
gPendingInstall.sourceURI.spec
);
try {
gPendingInstall.cancel();
} catch (e) {
}
} catch (e) {}
}
});
@ -72,12 +76,15 @@ function run_install_tests(callback) {
let [mainURL, redirectURL, expectedStatus] = gTests.shift();
if (redirectURL) {
var url = mainURL + redirect + redirectURL + xpi;
var message = "Should have seen the right result for an install redirected from " +
mainURL + " to " + redirectURL;
var message =
"Should have seen the right result for an install redirected from " +
mainURL +
" to " +
redirectURL;
} else {
url = mainURL + xpi;
message = "Should have seen the right result for an install from " +
mainURL;
message =
"Should have seen the right result for an install from " + mainURL;
}
let install = await AddonManager.getInstallForURL(url);

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

@ -4,7 +4,10 @@
* and added to the testFns array.
*/
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm", {});
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm",
{}
);
AddonTestUtils.initMochitest(this);
@ -28,7 +31,7 @@ async function installTheme() {
let id = "theme@mochi.test";
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id}},
applications: { gecko: { id } },
manifest_version: 2,
name: "atheme",
description: "wow. such theme.",
@ -45,16 +48,16 @@ async function installTheme() {
async function installExtension(manifest = {}) {
let extension = ExtensionTestUtils.loadExtension({
manifest: {
applications: {gecko: {id: addonId}},
applications: { gecko: { id: addonId } },
manifest_version: 2,
name: "extension",
description: "wow. such extension.",
author: "Code Pusher",
version: "1",
chrome_url_overrides: {newtab: "new.html"},
options_ui: {page: "options.html", open_in_tab: true},
browser_action: {default_popup: "action.html"},
page_action: {default_popup: "action.html"},
chrome_url_overrides: { newtab: "new.html" },
options_ui: { page: "options.html", open_in_tab: true },
browser_action: { default_popup: "action.html" },
page_action: { default_popup: "action.html" },
...manifest,
},
files: {
@ -96,9 +99,15 @@ async function enableAndDisable(doc, row) {
} else {
is(row.getAttribute("active"), "true", "The add-on is enabled");
doc.getAnonymousElementByAttribute(row, "anonid", "disable-btn").click();
await TestUtils.waitForCondition(() => row.getAttribute("active") == "false", "Wait for disable");
await TestUtils.waitForCondition(
() => row.getAttribute("active") == "false",
"Wait for disable"
);
doc.getAnonymousElementByAttribute(row, "anonid", "enable-btn").click();
await TestUtils.waitForCondition(() => row.getAttribute("active") == "true", "Wait for enable");
await TestUtils.waitForCondition(
() => row.getAttribute("active") == "true",
"Wait for enable"
);
}
}
@ -111,17 +120,25 @@ async function removeAddonAndUndo(doc, row) {
row.querySelector('[action="remove"]').click();
await removed;
let undoBanner = doc.querySelector(`message-bar[addon-id="${row.addon.id}"]`);
let undoBanner = doc.querySelector(
`message-bar[addon-id="${row.addon.id}"]`
);
undoBanner.querySelector('[action="undo"]').click();
await TestUtils.waitForCondition(() => getAddonCard(doc, row.addon.id));
} else {
is(row.getAttribute("status"), "installed", "The add-on is installed");
ok(!row.hasAttribute("pending"), "The add-on is not pending");
doc.getAnonymousElementByAttribute(row, "anonid", "remove-btn").click();
await TestUtils.waitForCondition(() => row.getAttribute("pending") == "uninstall", "Wait for uninstall");
await TestUtils.waitForCondition(
() => row.getAttribute("pending") == "uninstall",
"Wait for uninstall"
);
doc.getAnonymousElementByAttribute(row, "anonid", "undo-btn").click();
await TestUtils.waitForCondition(() => !row.hasAttribute("pending"), "Wait for undo");
await TestUtils.waitForCondition(
() => !row.hasAttribute("pending"),
"Wait for undo"
);
}
await started;
}
@ -132,7 +149,11 @@ async function openPrefs(doc, row) {
} else {
let prefsButton;
await TestUtils.waitForCondition(() => {
prefsButton = doc.getAnonymousElementByAttribute(row, "anonid", "preferences-btn");
prefsButton = doc.getAnonymousElementByAttribute(
row,
"anonid",
"preferences-btn"
);
return prefsButton;
});
prefsButton.click();
@ -155,7 +176,11 @@ function changeAutoUpdates(doc) {
autoUpdate.querySelector('[value="0"]').click();
// Check for updates.
let checkForUpdates = doc.getElementById("detail-findUpdates-btn");
is(checkForUpdates.hidden, false, "The check for updates button is visible");
is(
checkForUpdates.hidden,
false,
"The check for updates button is visible"
);
checkForUpdates.click();
// Turn on auto update.
autoUpdate.querySelector('[value="2"]').click();
@ -174,7 +199,11 @@ function clickLinks(doc) {
// Check links.
let creator = doc.getElementById("detail-creator");
let label = doc.getAnonymousElementByAttribute(creator, "anonid", "label");
let link = doc.getAnonymousElementByAttribute(creator, "anonid", "creator-link");
let link = doc.getAnonymousElementByAttribute(
creator,
"anonid",
"creator-link"
);
// Check that clicking the label doesn't trigger a telemetry event.
label.click();
assertTelemetryMatches([]);
@ -198,7 +227,8 @@ async function init(startPage, isHtmlViews) {
await gCategoryUtilities.openType(startPage);
if (isHtmlViews) {
return gManagerWindow.document.getElementById("html-view-browser").contentDocument;
return gManagerWindow.document.getElementById("html-view-browser")
.contentDocument;
}
return gManagerWindow.document;
}
@ -214,10 +244,7 @@ async function setup(isHtmlViews) {
}
async function testBasicViewTelemetry(isHtmlViews) {
let addons = await Promise.all([
installTheme(),
installExtension(),
]);
let addons = await Promise.all([installTheme(), installExtension()]);
let doc = await init("discover", isHtmlViews);
await gCategoryUtilities.openType("theme");
@ -228,13 +255,26 @@ async function testBasicViewTelemetry(isHtmlViews) {
openDetailView(doc, "extension@mochi.test");
await wait_for_view_load(gManagerWindow);
assertTelemetryMatches([
["view", "aboutAddons", "discover"],
["view", "aboutAddons", "list", {type: "theme"}],
["view", "aboutAddons", "detail", {type: "theme", addonId: "theme@mochi.test"}],
["view", "aboutAddons", "list", {type: "extension"}],
["view", "aboutAddons", "detail", {type: "extension", addonId: "extension@mochi.test"}],
], {filterMethods: ["view"]});
assertTelemetryMatches(
[
["view", "aboutAddons", "discover"],
["view", "aboutAddons", "list", { type: "theme" }],
[
"view",
"aboutAddons",
"detail",
{ type: "theme", addonId: "theme@mochi.test" },
],
["view", "aboutAddons", "list", { type: "extension" }],
[
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: "extension@mochi.test" },
],
],
{ filterMethods: ["view"] }
);
await close_manager(gManagerWindow);
await Promise.all(addons.map(addon => addon.unload()));
@ -246,25 +286,53 @@ async function testExtensionEvents(isHtmlViews) {
let doc = await init("extension", isHtmlViews);
// Check/clear the current telemetry.
assertTelemetryMatches([["view", "aboutAddons", "list", {type: "extension"}]],
{filterMethods: ["view"]});
assertTelemetryMatches(
[["view", "aboutAddons", "list", { type: "extension" }]],
{ filterMethods: ["view"] }
);
let row = getAddonCard(doc, addonId);
// Check disable/enable.
await enableAndDisable(doc, row);
assertTelemetryMatches([
["action", "aboutAddons", null, {action: "disable", addonId, type, view: "list"}],
["action", "aboutAddons", null, {action: "enable", addonId, type, view: "list"}],
], {filterMethods: ["action"]});
assertTelemetryMatches(
[
[
"action",
"aboutAddons",
null,
{ action: "disable", addonId, type, view: "list" },
],
[
"action",
"aboutAddons",
null,
{ action: "enable", addonId, type, view: "list" },
],
],
{ filterMethods: ["action"] }
);
// Check remove/undo.
await removeAddonAndUndo(doc, row);
let uninstallValue = isHtmlViews ? "accepted" : null;
assertTelemetryMatches([
["action", "aboutAddons", uninstallValue, {action: "uninstall", addonId, type, view: "list"}],
["action", "aboutAddons", null, {action: "undo", addonId, type, view: "list"}],
], {filterMethods: ["action"]});
assertTelemetryMatches(
[
[
"action",
"aboutAddons",
uninstallValue,
{ action: "uninstall", addonId, type, view: "list" },
],
[
"action",
"aboutAddons",
null,
{ action: "undo", addonId, type, view: "list" },
],
],
{ filterMethods: ["action"] }
);
// Open the preferences page.
let waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
@ -272,25 +340,57 @@ async function testExtensionEvents(isHtmlViews) {
row = getAddonCard(doc, addonId);
await openPrefs(doc, row);
BrowserTestUtils.removeTab(await waitForNewTab);
assertTelemetryMatches([
["action", "aboutAddons", "external", {action: "preferences", type, addonId, view: "list"}],
], {filterMethods: ["action"]});
assertTelemetryMatches(
[
[
"action",
"aboutAddons",
"external",
{ action: "preferences", type, addonId, view: "list" },
],
],
{ filterMethods: ["action"] }
);
// Go to the detail view.
openDetailView(doc, addonId);
await wait_for_view_load(gManagerWindow);
assertTelemetryMatches([
["view", "aboutAddons", "detail", {type, addonId}],
], {filterMethods: ["view"]});
assertTelemetryMatches(
[["view", "aboutAddons", "detail", { type, addonId }]],
{ filterMethods: ["view"] }
);
// Check updates.
changeAutoUpdates(doc);
assertTelemetryMatches([
["action", "aboutAddons", "", {action: "setAddonUpdate", type, addonId, view: "detail"}],
["action", "aboutAddons", null, {action: "checkForUpdate", type, addonId, view: "detail"}],
["action", "aboutAddons", "enabled", {action: "setAddonUpdate", type, addonId, view: "detail"}],
["action", "aboutAddons", "default", {action: "setAddonUpdate", type, addonId, view: "detail"}],
], {filterMethods: ["action"]});
assertTelemetryMatches(
[
[
"action",
"aboutAddons",
"",
{ action: "setAddonUpdate", type, addonId, view: "detail" },
],
[
"action",
"aboutAddons",
null,
{ action: "checkForUpdate", type, addonId, view: "detail" },
],
[
"action",
"aboutAddons",
"enabled",
{ action: "setAddonUpdate", type, addonId, view: "detail" },
],
[
"action",
"aboutAddons",
"default",
{ action: "setAddonUpdate", type, addonId, view: "detail" },
],
],
{ filterMethods: ["action"] }
);
// These links don't actually have a URL, so they don't open a tab. They're only
// shown when there is a URL though.
@ -307,26 +407,50 @@ async function testExtensionEvents(isHtmlViews) {
await openPrefs(doc, row);
BrowserTestUtils.removeTab(await waitForNewTab);
assertTelemetryMatches([
["link", "aboutAddons", "author", {view: "detail"}],
["link", "aboutAddons", "homepage", {view: "detail"}],
["link", "aboutAddons", "rating", {view: "detail"}],
["link", "aboutAddons", "support", {view: "detail"}],
["action", "aboutAddons", "external", {action: "preferences", type, addonId, view: "detail"}],
], {filterMethods: ["action", "link"]});
assertTelemetryMatches(
[
["link", "aboutAddons", "author", { view: "detail" }],
["link", "aboutAddons", "homepage", { view: "detail" }],
["link", "aboutAddons", "rating", { view: "detail" }],
["link", "aboutAddons", "support", { view: "detail" }],
[
"action",
"aboutAddons",
"external",
{ action: "preferences", type, addonId, view: "detail" },
],
],
{ filterMethods: ["action", "link"] }
);
// Update the preferences and check that inline changes.
await gCategoryUtilities.openType("extension");
let upgraded = await installExtension({options_ui: {page: "options.html"}, version: "2"});
let upgraded = await installExtension({
options_ui: { page: "options.html" },
version: "2",
});
row = getAddonCard(doc, addonId);
await openPrefs(doc, row);
await wait_for_view_load(gManagerWindow);
assertTelemetryMatches([
["view", "aboutAddons", "list", {type}],
["action", "aboutAddons", "inline", {action: "preferences", type, addonId, view: "list"}],
["view", "aboutAddons", "detail", {type: "extension", addonId: "extension@mochi.test"}],
], {filterMethods: ["action", "view"]});
assertTelemetryMatches(
[
["view", "aboutAddons", "list", { type }],
[
"action",
"aboutAddons",
"inline",
{ action: "preferences", type, addonId, view: "list" },
],
[
"view",
"aboutAddons",
"detail",
{ type: "extension", addonId: "extension@mochi.test" },
],
],
{ filterMethods: ["action", "view"] }
);
await close_manager(gManagerWindow);
await addon.unload();
@ -342,7 +466,9 @@ async function testGeneralActions(isHtmlViews) {
let recentUpdates = doc.getElementById("utils-viewUpdates");
let debugAddons = doc.getElementById("utils-debugAddons");
let updatePolicy = doc.getElementById("utils-autoUpdateDefault");
let resetUpdatePolicy = doc.getElementById("utils-resetAddonUpdatesToAutomatic");
let resetUpdatePolicy = doc.getElementById(
"utils-resetAddonUpdatesToAutomatic"
);
let manageShortcuts = doc.getElementById("manage-shortcuts");
async function clickInGearMenu(item) {
@ -375,18 +501,51 @@ async function testGeneralActions(isHtmlViews) {
searchBox.doCommand();
BrowserTestUtils.removeTab(await waitForNewTab);
assertTelemetryMatches([
["view", "aboutAddons", "list", {type: "extension"}],
["action", "aboutAddons", null, {action: "checkForUpdates", view: "list"}],
["view", "aboutAddons", "updates", {type: "recent"}],
["action", "aboutAddons", "default,enabled", {action: "setUpdatePolicy", view: "updates"}],
["action", "aboutAddons", "enabled", {action: "setUpdatePolicy", view: "updates"}],
["action", "aboutAddons", null, {action: "resetUpdatePolicy", view: "updates"}],
["view", "aboutAddons", "shortcuts"],
["action", "aboutAddons", null, {action: "checkForUpdates", view: "shortcuts"}],
["link", "aboutAddons", "about:debugging", {view: "shortcuts"}],
["link", "aboutAddons", "search", {view: "shortcuts", type: "shortcuts"}],
], {filterMethods: TELEMETRY_METHODS});
assertTelemetryMatches(
[
["view", "aboutAddons", "list", { type: "extension" }],
[
"action",
"aboutAddons",
null,
{ action: "checkForUpdates", view: "list" },
],
["view", "aboutAddons", "updates", { type: "recent" }],
[
"action",
"aboutAddons",
"default,enabled",
{ action: "setUpdatePolicy", view: "updates" },
],
[
"action",
"aboutAddons",
"enabled",
{ action: "setUpdatePolicy", view: "updates" },
],
[
"action",
"aboutAddons",
null,
{ action: "resetUpdatePolicy", view: "updates" },
],
["view", "aboutAddons", "shortcuts"],
[
"action",
"aboutAddons",
null,
{ action: "checkForUpdates", view: "shortcuts" },
],
["link", "aboutAddons", "about:debugging", { view: "shortcuts" }],
[
"link",
"aboutAddons",
"search",
{ view: "shortcuts", type: "shortcuts" },
],
],
{ filterMethods: TELEMETRY_METHODS }
);
await close_manager(gManagerWindow);
@ -401,10 +560,14 @@ async function testPreferencesLink(isHtmlViews) {
let doc = gManagerWindow.document;
// Open the about:preferences page from about:addons.
let waitForNewTab = BrowserTestUtils.waitForNewTab(gBrowser, "about:preferences");
let waitForNewTab = BrowserTestUtils.waitForNewTab(
gBrowser,
"about:preferences"
);
doc.getElementById("preferencesButton").click();
let tab = await waitForNewTab;
let getAddonsButton = () => tab.linkedBrowser.contentDocument.getElementById("addonsButton");
let getAddonsButton = () =>
tab.linkedBrowser.contentDocument.getElementById("addonsButton");
// Wait for the page to load.
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
@ -415,11 +578,14 @@ async function testPreferencesLink(isHtmlViews) {
// Close the about:preferences tab.
BrowserTestUtils.removeTab(tab);
assertTelemetryMatches([
["view", "aboutAddons", "list", {type: "theme"}],
["link", "aboutAddons", "about:preferences", {view: "list"}],
["link", "aboutPreferences", "about:addons"],
], {filterMethods: ["link", "view"]});
assertTelemetryMatches(
[
["view", "aboutAddons", "list", { type: "theme" }],
["link", "aboutAddons", "about:preferences", { view: "list" }],
["link", "aboutPreferences", "about:addons"],
],
{ filterMethods: ["link", "view"] }
);
await close_manager(gManagerWindow);
}
@ -443,7 +609,7 @@ function addTestTasks(isHtmlViews) {
for (let fn of testFns) {
let localTestFnName = fn.name + (isHtmlViews ? "HTML" : "XUL");
// Get an informative name for the function in stack traces.
let obj = {[localTestFnName]: () => fn(isHtmlViews)};
let obj = { [localTestFnName]: () => fn(isHtmlViews) };
add_task(obj[localTestFnName]);
}
}

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

@ -18,20 +18,22 @@ add_task(async function() {
let provider = new MockProvider();
provider.createAddons([{
id: "signed@tests.mozilla.org",
name: "Signed langpack",
type: "locale",
signedState: AddonManager.SIGNEDSTATE_SIGNED,
isCorrectlySigned: true,
}, {
id: "unsigned@tests.mozilla.org",
name: "Unsigned langpack",
type: "locale",
signedState: AddonManager.SIGNEDSTATE_MISSING,
isCorrectlySigned: false,
}]);
provider.createAddons([
{
id: "signed@tests.mozilla.org",
name: "Signed langpack",
type: "locale",
signedState: AddonManager.SIGNEDSTATE_SIGNED,
isCorrectlySigned: true,
},
{
id: "unsigned@tests.mozilla.org",
name: "Unsigned langpack",
type: "locale",
signedState: AddonManager.SIGNEDSTATE_MISSING,
isCorrectlySigned: false,
},
]);
let mgrWin = await open_manager(null);
@ -58,21 +60,46 @@ add_task(async function() {
errorVisible = false;
}
let warning = mgrWin.document.getAnonymousElementByAttribute(item, "anonid", "warning");
let warningLink = mgrWin.document.getAnonymousElementByAttribute(item, "anonid", "warning-link");
let warning = mgrWin.document.getAnonymousElementByAttribute(
item,
"anonid",
"warning"
);
let warningLink = mgrWin.document.getAnonymousElementByAttribute(
item,
"anonid",
"warning-link"
);
if (warningVisible) {
is_element_visible(warning, `Warning should be visible for ${what}`);
is_element_visible(warningLink, `Warning link should be visible for ${what}`);
is_element_visible(
warningLink,
`Warning link should be visible for ${what}`
);
} else {
is_element_hidden(warning, `Warning should be hidden for ${what}`);
is_element_hidden(warningLink, `Warning link should be hidden for ${what}`);
is_element_hidden(
warningLink,
`Warning link should be hidden for ${what}`
);
}
let error = mgrWin.document.getAnonymousElementByAttribute(item, "anonid", "error");
let errorLink = mgrWin.document.getAnonymousElementByAttribute(item, "anonid", "error-link");
let error = mgrWin.document.getAnonymousElementByAttribute(
item,
"anonid",
"error"
);
let errorLink = mgrWin.document.getAnonymousElementByAttribute(
item,
"anonid",
"error-link"
);
if (errorVisible) {
is_element_visible(error, `Error should be visible for ${what}`);
is_element_visible(errorLink, `Error link should be visible for ${what}`);
is_element_visible(
errorLink,
`Error link should be visible for ${what}`
);
} else {
is_element_hidden(error, `Error should be hidden for ${what}`);
is_element_hidden(errorLink, `Error link should be hidden for ${what}`);

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

@ -5,7 +5,9 @@ SpecialPowers.pushPrefEnv({
});
add_task(async function() {
const INFO_URL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "webextensions";
const INFO_URL =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"webextensions";
// The mochitest framework installs a bunch of legacy extensions.
// Fortunately, the extensions.legacy.exceptions preference exists to
@ -17,7 +19,7 @@ add_task(async function() {
];
let exceptions = Services.prefs.getCharPref("extensions.legacy.exceptions");
exceptions = [ exceptions, ...IGNORE ].join(",");
exceptions = [exceptions, ...IGNORE].join(",");
await SpecialPowers.pushPrefEnv({
set: [
@ -100,12 +102,18 @@ add_task(async function() {
// Initially, we have two good extensions (a webextension and a
// "Mozilla Extensions"-signed extension).
await catUtils.openType("extension");
checkList("addon-list",
["webextension@tests.mozilla.org", "mozilla@tests.mozilla.org"]);
checkList("addon-list", [
"webextension@tests.mozilla.org",
"mozilla@tests.mozilla.org",
]);
let banner = mgrWin.document.getElementById("legacy-extensions-notice");
is_element_hidden(banner, "Warning about legacy extensions should be hidden");
is(mgrWin.gLegacyView._categoryItem.disabled, true, "Legacy category is hidden");
is(
mgrWin.gLegacyView._categoryItem.disabled,
true,
"Legacy category is hidden"
);
// Now add a legacy extension
provider.createAddons(disabledAddon);
@ -120,28 +128,43 @@ add_task(async function() {
await catUtils.openType("plugin");
await catUtils.openType("extension");
checkList("addon-list",
["webextension@tests.mozilla.org", "mozilla@tests.mozilla.org"]);
checkList("addon-list", [
"webextension@tests.mozilla.org",
"mozilla@tests.mozilla.org",
]);
// But now the legacy banner and category should be visible
banner = mgrWin.document.getElementById("legacy-extensions-notice");
is_element_visible(banner, "Warning about legacy extensions should be visible");
is_element_visible(
banner,
"Warning about legacy extensions should be visible"
);
let catItem = mgrWin.gLegacyView._categoryItem;
is(catItem.disabled, false, "Legacy category is visible");
is(catItem.getAttribute("name"), get_string("type.legacy.name"),
"Category label with no unsigned extensions is correct");
is(
catItem.getAttribute("name"),
get_string("type.legacy.name"),
"Category label with no unsigned extensions is correct"
);
// Follow the link to the legacy extensions page
let legacyLink = mgrWin.document.getElementById("legacy-extensions-learnmore-link");
let legacyLink = mgrWin.document.getElementById(
"legacy-extensions-learnmore-link"
);
is_element_visible(legacyLink, "Link to legacy extension is visible");
let loadPromise = new Promise(resolve => wait_for_view_load(mgrWin, resolve, true));
let loadPromise = new Promise(resolve =>
wait_for_view_load(mgrWin, resolve, true)
);
legacyLink.click();
await loadPromise;
is(mgrWin.gViewController.currentViewId, "addons://legacy/",
"Legacy extensions link leads to the correct view");
is(
mgrWin.gViewController.currentViewId,
"addons://legacy/",
"Legacy extensions link leads to the correct view"
);
let link = mgrWin.document.getElementById("legacy-learnmore");
is(link.href, INFO_URL, "Learn more link points to the right place");
@ -152,25 +175,31 @@ add_task(async function() {
// Now add some unsigned addons and flip the signing preference
provider.createAddons(unsignedAddons);
SpecialPowers.pushPrefEnv({
set: [
["xpinstall.signatures.required", true],
],
set: [["xpinstall.signatures.required", true]],
});
// The entry on the left side should now read "Unsupported"
await mgrWin.gLegacyView.refreshVisibility();
is(catItem.disabled, false, "Legacy category is visible");
is(catItem.getAttribute("name"), get_string("type.unsupported.name"),
"Category label with unsigned extensions is correct");
is(
catItem.getAttribute("name"),
get_string("type.unsupported.name"),
"Category label with unsigned extensions is correct"
);
// The main extensions list should still have the original two
// good extensions and the legacy banner.
await catUtils.openType("extension");
checkList("addon-list",
["webextension@tests.mozilla.org", "mozilla@tests.mozilla.org"]);
checkList("addon-list", [
"webextension@tests.mozilla.org",
"mozilla@tests.mozilla.org",
]);
banner = mgrWin.document.getElementById("legacy-extensions-notice");
is_element_visible(banner, "Warning about legacy extensions should be visible");
is_element_visible(
banner,
"Warning about legacy extensions should be visible"
);
// And the legacy pane should show both legacy and unsigned extensions
await catUtils.openType("legacy");
@ -182,9 +211,7 @@ add_task(async function() {
// Disable unsigned extensions
SpecialPowers.pushPrefEnv({
set: [
["xpinstall.signatures.required", false],
],
set: [["xpinstall.signatures.required", false]],
});
await new Promise(executeSoon);
@ -192,8 +219,11 @@ add_task(async function() {
// The name of the pane should go back to "Legacy Extensions"
await mgrWin.gLegacyView.refreshVisibility();
is(catItem.disabled, false, "Legacy category is visible");
is(catItem.getAttribute("name"), get_string("type.legacy.name"),
"Category label with no unsigned extensions is correct");
is(
catItem.getAttribute("name"),
get_string("type.legacy.name"),
"Category label with no unsigned extensions is correct"
);
// The unsigned extension should be present in the main extensions pane
await catUtils.openType("extension");
@ -217,13 +247,14 @@ add_task(async function() {
// now that legacy extensions are enabled, we should jump to the
// regular Extensions list.
await SpecialPowers.pushPrefEnv({
set: [
["extensions.legacy.enabled", true],
],
set: [["extensions.legacy.enabled", true]],
});
mgrWin = await open_manager(null);
is(mgrWin.gViewController.currentViewId, "addons://list/extension",
"addons manager switched to extensions list");
is(
mgrWin.gViewController.currentViewId,
"addons://list/extension",
"addons manager switched to extensions list"
);
await close_manager(mgrWin);
});

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

@ -9,7 +9,9 @@ SpecialPowers.pushPrefEnv({
});
add_task(async function() {
const INFO_URL = Services.urlFormatter.formatURLPref("app.support.baseURL") + "webextensions";
const INFO_URL =
Services.urlFormatter.formatURLPref("app.support.baseURL") +
"webextensions";
const NAMES = {
newTheme: "New LWT",
@ -55,14 +57,19 @@ add_task(async function() {
let document = mgrWin.document;
// First find the entry in the list.
let item = Array.from(document.getElementById("addon-list").childNodes)
.find(i => i.getAttribute("name") == name);
let item = Array.from(
document.getElementById("addon-list").childNodes
).find(i => i.getAttribute("name") == name);
ok(item, `Found ${name} in list`);
item.parentNode.ensureElementIsVisible(item);
// Check the badge
let badge = document.getAnonymousElementByAttribute(item, "anonid", "legacy");
let badge = document.getAnonymousElementByAttribute(
item,
"anonid",
"legacy"
);
if (isLegacy) {
is_element_visible(badge, `Legacy badge is visible for ${name}`);

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,6 +1,8 @@
"use strict";
const {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
const { PromiseTestUtils } = ChromeUtils.import(
"resource://testing-common/PromiseTestUtils.jsm"
);
PromiseTestUtils.whitelistRejectionsGlobally(/Message manager disconnected/);
let gManagerWindow;
@ -30,20 +32,20 @@ add_task(async function testUpdatingCommands() {
let commands = {
commandZero: {},
commandOne: {
suggested_key: {default: "Shift+Alt+7"},
suggested_key: { default: "Shift+Alt+7" },
},
commandTwo: {
description: "Command Two!",
suggested_key: {default: "Alt+4"},
suggested_key: { default: "Alt+4" },
},
_execute_browser_action: {
suggested_key: {default: "Shift+Alt+9"},
suggested_key: { default: "Shift+Alt+9" },
},
};
let extension = ExtensionTestUtils.loadExtension({
manifest: {
commands,
browser_action: {default_popup: "popup.html"},
browser_action: { default_popup: "popup.html" },
},
background() {
browser.commands.onCommand.addListener(commandName => {
@ -60,12 +62,16 @@ add_task(async function testUpdatingCommands() {
async function checkShortcut(name, key, modifiers) {
EventUtils.synthesizeKey(key, modifiers);
let message = await extension.awaitMessage("oncommand");
is(message, name, `Expected onCommand listener to fire with the correct name: ${name}`);
is(
message,
name,
`Expected onCommand listener to fire with the correct name: ${name}`
);
}
// Check that the original shortcuts work.
await checkShortcut("commandOne", "7", {shiftKey: true, altKey: true});
await checkShortcut("commandTwo", "4", {altKey: true});
await checkShortcut("commandOne", "7", { shiftKey: true, altKey: true });
await checkShortcut("commandTwo", "4", { altKey: true });
let doc = await loadShortcutsView();
@ -73,35 +79,48 @@ add_task(async function testUpdatingCommands() {
ok(card, `There is a card for the extension`);
let inputs = card.querySelectorAll(".shortcut-input");
is(inputs.length, Object.keys(commands).length, "There is an input for each command");
is(
inputs.length,
Object.keys(commands).length,
"There is an input for each command"
);
let nameOrder = Array.from(inputs).map(input => input.getAttribute("name"));
Assert.deepEqual(
nameOrder,
["commandOne", "commandTwo", "_execute_browser_action", "commandZero"],
"commandZero should be last since it is unset");
"commandZero should be last since it is unset"
);
let count = 1;
for (let input of inputs) {
// Change the shortcut.
input.focus();
EventUtils.synthesizeKey("8", {shiftKey: true, altKey: true});
EventUtils.synthesizeKey("8", { shiftKey: true, altKey: true });
count++;
// Wait for the shortcut attribute to change.
await BrowserTestUtils.waitForCondition(
() => input.getAttribute("shortcut") == "Alt+Shift+8");
() => input.getAttribute("shortcut") == "Alt+Shift+8"
);
// Check that the change worked (but skip if browserAction).
if (input.getAttribute("name") != "_execute_browser_action") {
await checkShortcut(input.getAttribute("name"), "8", {shiftKey: true, altKey: true});
await checkShortcut(input.getAttribute("name"), "8", {
shiftKey: true,
altKey: true,
});
}
// Change it again so it doesn't conflict with the next command.
input.focus();
EventUtils.synthesizeKey(count.toString(), {shiftKey: true, altKey: true});
EventUtils.synthesizeKey(count.toString(), {
shiftKey: true,
altKey: true,
});
await BrowserTestUtils.waitForCondition(
() => input.getAttribute("shortcut") == `Alt+Shift+${count}`);
() => input.getAttribute("shortcut") == `Alt+Shift+${count}`
);
}
// Check that errors can be shown.
@ -112,7 +131,7 @@ add_task(async function testUpdatingCommands() {
// Try a shortcut with only shift for a modifier.
input.focus();
EventUtils.synthesizeKey("J", {shiftKey: true});
EventUtils.synthesizeKey("J", { shiftKey: true });
let possibleErrors = ["shortcuts-modifier-mac", "shortcuts-modifier-other"];
ok(possibleErrors.includes(label.dataset.l10nId), `The message is set`);
is(error.style.visibility, "visible", "The error is shown");
@ -125,7 +144,7 @@ add_task(async function testUpdatingCommands() {
// Check if assigning already assigned shortcut is prevented.
input.focus();
EventUtils.synthesizeKey("2", {shiftKey: true, altKey: true});
EventUtils.synthesizeKey("2", { shiftKey: true, altKey: true });
is(label.dataset.l10nId, "shortcuts-exists", `The message is set`);
is(error.style.visibility, "visible", "The error is shown");
@ -188,8 +207,10 @@ add_task(async function testExpanding() {
for (let i = 0; i < shortcutRows.length; i++) {
let row = shortcutRows[i];
if (i < visibleCommands) {
ok(getComputedStyle(row).display != "none",
`The first ${visibleCommands} rows are visible`);
ok(
getComputedStyle(row).display != "none",
`The first ${visibleCommands} rows are visible`
);
} else {
is(getComputedStyle(row).display, "none", "The other rows are hidden");
}
@ -203,8 +224,11 @@ add_task(async function testExpanding() {
ok(expandButton, "There is an expand button");
let l10nAttrs = doc.l10n.getAttributes(expandButton);
is(l10nAttrs.id, "shortcuts-card-expand-button", "The expand text is shown");
is(l10nAttrs.args.numberToShow, numCommands - visibleCommands,
"The number to be shown is set on the expand button");
is(
l10nAttrs.args.numberToShow,
numCommands - visibleCommands,
"The number to be shown is set on the expand button"
);
// Expand the card.
expandButton.click();
@ -217,14 +241,18 @@ add_task(async function testExpanding() {
// The collapse text is now shown.
l10nAttrs = doc.l10n.getAttributes(expandButton);
is(l10nAttrs.id, "shortcuts-card-collapse-button", "The colapse text is shown");
is(
l10nAttrs.id,
"shortcuts-card-collapse-button",
"The colapse text is shown"
);
// Collapse the card.
expandButton.click();
ok(!card.hasAttribute("expanded"), "The card is now collapsed again");
assertCollapsedVisibility({collapsed: true});
assertCollapsedVisibility({ collapsed: true });
await closeView();
await extension.unload();

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

@ -23,16 +23,18 @@ async function registerAndStartExtension(mockProvider, ext) {
// the add-on manager, e.g. by passing "useAddonManager" to `loadExtension`.
// "useAddonManager" can however not be used, because the resulting add-ons
// are unsigned, and only add-ons with privileged signatures can be hidden.
mockProvider.createAddons([{
id: extension.id,
name: ext.manifest.name,
type: "extension",
version: "1",
// We use MockProvider because the "hidden" property cannot
// be set when "useAddonManager" is passed to loadExtension.
hidden: ext.manifest.hidden,
isSystem: ext.isSystem,
}]);
mockProvider.createAddons([
{
id: extension.id,
name: ext.manifest.name,
type: "extension",
version: "1",
// We use MockProvider because the "hidden" property cannot
// be set when "useAddonManager" is passed to loadExtension.
hidden: ext.manifest.hidden,
isSystem: ext.isSystem,
},
]);
return extension;
}
@ -46,7 +48,7 @@ function getShortcutByName(doc, extension, name) {
}
function getNoShortcutListItem(doc, extension) {
let {id} = extension;
let { id } = extension;
let li = doc.querySelector(`.shortcuts-no-commands-list [addon-id="${id}"]`);
return li && li.textContent;
}
@ -64,10 +66,15 @@ add_task(async function extension_with_shortcuts() {
await extension.startup();
let doc = await loadShortcutsView();
ok(getShortcutByName(doc, extension, "theShortcut"),
"Extension with shortcuts should have a card");
is(getNoShortcutListItem(doc, extension), null,
"Extension with shortcuts should not be listed");
ok(
getShortcutByName(doc, extension, "theShortcut"),
"Extension with shortcuts should have a card"
);
is(
getNoShortcutListItem(doc, extension),
null,
"Extension with shortcuts should not be listed"
);
await closeShortcutsView(doc);
await extension.unload();
@ -83,10 +90,16 @@ add_task(async function extension_without_shortcuts() {
await extension.startup();
let doc = await loadShortcutsView();
is(getShortcutCard(doc, extension), null,
"Extension without shortcuts should not have a card");
is(getNoShortcutListItem(doc, extension), "no shortcut addon",
"The add-on's name is set in the list");
is(
getShortcutCard(doc, extension),
null,
"Extension without shortcuts should not have a card"
);
is(
getNoShortcutListItem(doc, extension),
"no shortcut addon",
"The add-on's name is set in the list"
);
await closeShortcutsView(doc);
await extension.unload();
@ -114,13 +127,21 @@ add_task(async function hidden_extension() {
let doc = await loadShortcutsView();
ok(getShortcutByName(doc, hiddenExt1, "hiddenShortcut"),
"Hidden extension with shortcuts should have a card");
ok(
getShortcutByName(doc, hiddenExt1, "hiddenShortcut"),
"Hidden extension with shortcuts should have a card"
);
is(getShortcutCard(doc, hiddenExt2), null,
"Hidden extension without shortcuts should not have a card");
is(getNoShortcutListItem(doc, hiddenExt2), null,
"Hidden extension without shortcuts should not be listed");
is(
getShortcutCard(doc, hiddenExt2),
null,
"Hidden extension without shortcuts should not have a card"
);
is(
getNoShortcutListItem(doc, hiddenExt2),
null,
"Hidden extension without shortcuts should not be listed"
);
await closeShortcutsView(doc);
await hiddenExt1.unload();
@ -153,13 +174,21 @@ add_task(async function system_addons_and_shortcuts() {
let doc = await loadShortcutsView();
ok(getShortcutByName(doc, systemExt1, "systemShortcut"),
"System add-on with shortcut should have a card");
ok(
getShortcutByName(doc, systemExt1, "systemShortcut"),
"System add-on with shortcut should have a card"
);
is(getShortcutCard(doc, systemExt2), null,
"System add-on without shortcut should not have a card");
is(getNoShortcutListItem(doc, systemExt2), null,
"System add-on without shortcuts should not be listed");
is(
getShortcutCard(doc, systemExt2),
null,
"System add-on without shortcut should not have a card"
);
is(
getNoShortcutListItem(doc, systemExt2),
null,
"System add-on without shortcuts should not be listed"
);
await closeShortcutsView(doc);
await systemExt1.unload();

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

@ -20,12 +20,14 @@ async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "auto updating addon",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE,
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "auto updating addon",
version: "1.0",
applyBackgroundUpdates: AddonManager.AUTOUPDATE_ENABLE,
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
@ -43,47 +45,72 @@ add_test(function clearOldTelemetry() {
run_next_test();
});
add_test(function() {
gAvailableCategory = gManagerWindow.gCategories.get("addons://updates/available");
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should initially be hidden");
gAvailableCategory = gManagerWindow.gCategories.get(
"addons://updates/available"
);
is(
gCategoryUtilities.isVisible(gAvailableCategory),
false,
"Available Updates category should initially be hidden"
);
gProvider.createAddons([{
id: "addon2@tests.mozilla.org",
name: "manually updating addon",
version: "1.0",
isCompatible: false,
operationsRequiringRestart: 0,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
}]);
gProvider.createAddons([
{
id: "addon2@tests.mozilla.org",
name: "manually updating addon",
version: "1.0",
isCompatible: false,
operationsRequiringRestart: 0,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
applyBackgroundUpdates: AddonManager.AUTOUPDATE_DISABLE,
},
]);
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should still be hidden");
is(
gCategoryUtilities.isVisible(gAvailableCategory),
false,
"Available Updates category should still be hidden"
);
run_next_test();
});
add_test(async function() {
let finished = 0;
function maybeRunNext() {
if (++finished == 2)
if (++finished == 2) {
run_next_test();
}
}
gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should now be visible");
is(gAvailableCategory.badgeCount, 1, "Badge for Available Updates should now be 1");
maybeRunNext();
}, {once: true});
gAvailableCategory.addEventListener(
"CategoryBadgeUpdated",
function() {
is(
gCategoryUtilities.isVisible(gAvailableCategory),
true,
"Available Updates category should now be visible"
);
is(
gAvailableCategory.badgeCount,
1,
"Badge for Available Updates should now be 1"
);
maybeRunNext();
},
{ once: true }
);
await gCategoryUtilities.openType("extension");
gProvider.createInstalls([{
name: "manually updating addon (new and improved!)",
existingAddon: gProvider.addons[1],
version: "1.1",
releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml"),
}]);
gProvider.createInstalls([
{
name: "manually updating addon (new and improved!)",
existingAddon: gProvider.addons[1],
version: "1.1",
releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml"),
},
]);
var item = get_addon_element(gManagerWindow, "addon2@tests.mozilla.org");
get_tooltip_info(item).then(({ version }) => {
@ -92,23 +119,33 @@ add_test(async function() {
});
});
add_test(async function() {
Promise.resolve().then(() => {
EventUtils.synthesizeMouseAtCenter(gAvailableCategory, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(gAvailableCategory, {}, gManagerWindow);
});
await wait_for_view_load(gManagerWindow, null, true);
is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/available", "Available Updates category should now be selected");
is(gManagerWindow.gViewController.currentViewId, "addons://updates/available", "Available Updates view should be the current view");
is(
gManagerWindow.document.getElementById("categories").selectedItem.value,
"addons://updates/available",
"Available Updates category should now be selected"
);
is(
gManagerWindow.gViewController.currentViewId,
"addons://updates/available",
"Available Updates view should be the current view"
);
run_next_test();
});
add_test(async function() {
var list = gManagerWindow.document.getElementById("updates-list");
is(list.itemCount, 1, "Should be 1 available update listed");
var item = list.firstChild;
is(item.mAddon.id, "addon2@tests.mozilla.org", "Update item should be for the manually updating addon");
is(
item.mAddon.id,
"addon2@tests.mozilla.org",
"Update item should be for the manually updating addon"
);
// The item in the list will be checking for update information asynchronously
// so we have to wait for it to complete. Doing the same async request should
@ -120,88 +157,172 @@ add_test(async function() {
add_test(function() {
function checkReleaseNotesTelemetry() {
let snapshot = Services.telemetry.snapshotEvents(
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS, true);
ok(snapshot.parent && snapshot.parent.length > 0, "Got parent telemetry events in the snapshot");
Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS,
true
);
ok(
snapshot.parent && snapshot.parent.length > 0,
"Got parent telemetry events in the snapshot"
);
let releaseNotesEvents = snapshot.parent
.filter(([ts, category, method]) =>
category == "addonsManager" && method == "action")
.filter(
([ts, category, method]) =>
category == "addonsManager" && method == "action"
)
.map(([ts, category, ...rest]) => rest);
Assert.deepEqual(releaseNotesEvents, [
["action", "aboutAddons", null, {
action: "releaseNotes",
type: "extension",
addonId: "addon2@tests.mozilla.org",
view: "updates",
}],
["action", "aboutAddons", null, {
action: "releaseNotes",
type: "extension",
addonId: "addon2@tests.mozilla.org",
view: "updates",
}],
], "The releaseNotes events are tracked");
Assert.deepEqual(
releaseNotesEvents,
[
[
"action",
"aboutAddons",
null,
{
action: "releaseNotes",
type: "extension",
addonId: "addon2@tests.mozilla.org",
view: "updates",
},
],
[
"action",
"aboutAddons",
null,
{
action: "releaseNotes",
type: "extension",
addonId: "addon2@tests.mozilla.org",
view: "updates",
},
],
],
"The releaseNotes events are tracked"
);
}
var list = gManagerWindow.document.getElementById("updates-list");
var item = list.firstChild;
get_tooltip_info(item).then(({ version }) => {
is(version, "1.1", "Update item should have version number of the update");
var postfix = gManagerWindow.document.getAnonymousElementByAttribute(item, "class", "update-postfix");
var postfix = gManagerWindow.document.getAnonymousElementByAttribute(
item,
"class",
"update-postfix"
);
is_element_visible(postfix, "'Update' postfix should be visible");
is_element_visible(item._updateAvailable, "");
is_element_visible(item._relNotesToggle, "Release notes toggle should be visible");
is_element_visible(
item._relNotesToggle,
"Release notes toggle should be visible"
);
is_element_hidden(item._warning, "Incompatible warning should be hidden");
is_element_hidden(item._error, "Blocklist error should be hidden");
info("Opening release notes");
item.addEventListener("RelNotesToggle", function() {
info("Release notes now open");
item.addEventListener(
"RelNotesToggle",
function() {
info("Release notes now open");
is_element_hidden(item._relNotesLoading, "Release notes loading message should be hidden");
is_element_visible(item._relNotesError, "Release notes error message should be visible");
is(item._relNotes.childElementCount, 0, "Release notes should be empty");
is_element_hidden(
item._relNotesLoading,
"Release notes loading message should be hidden"
);
is_element_visible(
item._relNotesError,
"Release notes error message should be visible"
);
is(
item._relNotes.childElementCount,
0,
"Release notes should be empty"
);
info("Closing release notes");
item.addEventListener("RelNotesToggle", function() {
info("Release notes now closed");
info("Setting Release notes URI to something that should load");
gProvider.installs[0].releaseNotesURI = Services.io.newURI(TESTROOT + "releaseNotes.xhtml");
info("Closing release notes");
item.addEventListener(
"RelNotesToggle",
function() {
info("Release notes now closed");
info("Setting Release notes URI to something that should load");
gProvider.installs[0].releaseNotesURI = Services.io.newURI(
TESTROOT + "releaseNotes.xhtml"
);
info("Re-opening release notes");
item.addEventListener("RelNotesToggle", function() {
info("Release notes now open");
info("Re-opening release notes");
item.addEventListener(
"RelNotesToggle",
function() {
info("Release notes now open");
is_element_hidden(item._relNotesLoading, "Release notes loading message should be hidden");
is_element_hidden(item._relNotesError, "Release notes error message should be hidden");
isnot(item._relNotes.childElementCount, 0, "Release notes should have been inserted into container");
is_element_hidden(
item._relNotesLoading,
"Release notes loading message should be hidden"
);
is_element_hidden(
item._relNotesError,
"Release notes error message should be hidden"
);
isnot(
item._relNotes.childElementCount,
0,
"Release notes should have been inserted into container"
);
checkReleaseNotesTelemetry();
checkReleaseNotesTelemetry();
run_next_test();
}, {once: true});
EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
is_element_visible(item._relNotesLoading, "Release notes loading message should be visible");
}, {once: true});
EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
}, {once: true});
EventUtils.synthesizeMouseAtCenter(item._relNotesToggle, { }, gManagerWindow);
is_element_visible(item._relNotesLoading, "Release notes loading message should be visible");
run_next_test();
},
{ once: true }
);
EventUtils.synthesizeMouseAtCenter(
item._relNotesToggle,
{},
gManagerWindow
);
is_element_visible(
item._relNotesLoading,
"Release notes loading message should be visible"
);
},
{ once: true }
);
EventUtils.synthesizeMouseAtCenter(
item._relNotesToggle,
{},
gManagerWindow
);
},
{ once: true }
);
EventUtils.synthesizeMouseAtCenter(
item._relNotesToggle,
{},
gManagerWindow
);
is_element_visible(
item._relNotesLoading,
"Release notes loading message should be visible"
);
});
});
add_test(function() {
var badgeUpdated = false;
var installCompleted = false;
gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
if (installCompleted)
run_next_test();
else
badgeUpdated = true;
}, {once: true});
gAvailableCategory.addEventListener(
"CategoryBadgeUpdated",
function() {
if (installCompleted) {
run_next_test();
} else {
badgeUpdated = true;
}
},
{ once: true }
);
var list = gManagerWindow.document.getElementById("updates-list");
var item = list.firstChild;
@ -212,61 +333,107 @@ add_test(function() {
var listener = {
onInstallStarted() {
info("Install started");
is_element_visible(item._installStatus, "Install progress widget should be visible");
is_element_visible(
item._installStatus,
"Install progress widget should be visible"
);
},
onInstallEnded(...args) {
install.removeTestListener(this);
info("Install ended");
is_element_hidden(item._installStatus, "Install progress widget should be hidden");
is_element_hidden(
item._installStatus,
"Install progress widget should be hidden"
);
if (badgeUpdated)
if (badgeUpdated) {
run_next_test();
else
} else {
installCompleted = true;
}
},
};
install.addTestListener(listener);
EventUtils.synthesizeMouseAtCenter(updateBtn, { }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(updateBtn, {}, gManagerWindow);
});
add_test(async function() {
is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should still be visible");
is(gAvailableCategory.badgeCount, 0, "Badge for Available Updates should now be 0");
is(
gCategoryUtilities.isVisible(gAvailableCategory),
true,
"Available Updates category should still be visible"
);
is(
gAvailableCategory.badgeCount,
0,
"Badge for Available Updates should now be 0"
);
await gCategoryUtilities.openType("extension");
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should be hidden");
is(
gCategoryUtilities.isVisible(gAvailableCategory),
false,
"Available Updates category should be hidden"
);
await close_manager(gManagerWindow);
let aWindow = await open_manager(null);
gManagerWindow = aWindow;
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
gAvailableCategory = gManagerWindow.gCategories.get("addons://updates/available");
gAvailableCategory = gManagerWindow.gCategories.get(
"addons://updates/available"
);
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should be hidden");
is(
gCategoryUtilities.isVisible(gAvailableCategory),
false,
"Available Updates category should be hidden"
);
run_next_test();
});
add_test(function() {
gAvailableCategory.addEventListener("CategoryBadgeUpdated", async function() {
is(gCategoryUtilities.isVisible(gAvailableCategory), true, "Available Updates category should now be visible");
is(gAvailableCategory.badgeCount, 1, "Badge for Available Updates should now be 1");
gAvailableCategory.addEventListener(
"CategoryBadgeUpdated",
async function() {
is(
gCategoryUtilities.isVisible(gAvailableCategory),
true,
"Available Updates category should now be visible"
);
is(
gAvailableCategory.badgeCount,
1,
"Badge for Available Updates should now be 1"
);
gAvailableCategory.addEventListener("CategoryBadgeUpdated", function() {
is(gCategoryUtilities.isVisible(gAvailableCategory), false, "Available Updates category should now be hidden");
gAvailableCategory.addEventListener(
"CategoryBadgeUpdated",
function() {
is(
gCategoryUtilities.isVisible(gAvailableCategory),
false,
"Available Updates category should now be hidden"
);
run_next_test();
}, {once: true});
run_next_test();
},
{ once: true }
);
let aAddon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
}, {once: true});
let aAddon = await AddonManager.getAddonByID("addon2@tests.mozilla.org");
aAddon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
},
{ once: true }
);
gProvider.createInstalls([{
name: "manually updating addon (new and even more improved!)",
existingAddon: gProvider.addons[1],
version: "1.2",
releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml"),
}]);
gProvider.createInstalls([
{
name: "manually updating addon (new and even more improved!)",
existingAddon: gProvider.addons[1],
version: "1.2",
releaseNotesURI: Services.io.newURI(TESTROOT + "thereIsNoFileHere.xhtml"),
},
]);
});

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

@ -4,9 +4,10 @@
// Tests that state menu is displayed correctly (enabled or disabled) in the add-on manager
// when the preference is unlocked / locked
const gIsWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
const gIsLinux = ("@mozilla.org/gnome-gconf-service;1" in Cc) ||
("@mozilla.org/gio-service;1" in Cc);
const gIsWindows = "@mozilla.org/windows-registry-key;1" in Cc;
const gIsLinux =
"@mozilla.org/gnome-gconf-service;1" in Cc ||
"@mozilla.org/gio-service;1" in Cc;
var gManagerWindow;
var gCategoryUtilities;
@ -14,10 +15,12 @@ var gPluginElement;
function getTestPluginPref() {
let prefix = "plugin.state.";
if (gIsWindows)
if (gIsWindows) {
return `${prefix}nptest`;
if (gIsLinux)
}
if (gIsLinux) {
return `${prefix}libnptest`;
}
return `${prefix}test`;
}
@ -55,32 +58,55 @@ function getTestPlugin(aPlugins) {
}
function checkStateMenu(locked) {
Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
"Preference lock state should be correct.");
let menuList = gManagerWindow.document.getAnonymousElementByAttribute(gPluginElement, "anonid", "state-menulist");
Assert.equal(
Services.prefs.prefIsLocked(getTestPluginPref()),
locked,
"Preference lock state should be correct."
);
let menuList = gManagerWindow.document.getAnonymousElementByAttribute(
gPluginElement,
"anonid",
"state-menulist"
);
// State menu should always have a selected item which must be visible
let selectedMenuItem = menuList.querySelector(".addon-control[selected=\"true\"]");
let selectedMenuItem = menuList.querySelector(
'.addon-control[selected="true"]'
);
is_element_visible(menuList, "State menu should be visible.");
Assert.equal(menuList.disabled, locked,
"State menu should" + (locked === true ? "" : " not") + " be disabled.");
Assert.equal(
menuList.disabled,
locked,
"State menu should" + (locked === true ? "" : " not") + " be disabled."
);
is_element_visible(selectedMenuItem, "State menu's selected item should be visible.");
is_element_visible(
selectedMenuItem,
"State menu's selected item should be visible."
);
}
function checkStateMenuDetail(locked) {
Assert.equal(Services.prefs.prefIsLocked(getTestPluginPref()), locked,
"Preference should be " + (locked === true ? "" : "un") + "locked.");
Assert.equal(
Services.prefs.prefIsLocked(getTestPluginPref()),
locked,
"Preference should be " + (locked === true ? "" : "un") + "locked."
);
// open details menu
EventUtils.synthesizeMouseAtCenter(gPluginElement, {}, gManagerWindow);
return new Promise(async resolve => {
await wait_for_view_load(gManagerWindow);
let menuList = gManagerWindow.document.getElementById("detail-state-menulist");
let menuList = gManagerWindow.document.getElementById(
"detail-state-menulist"
);
is_element_visible(menuList, "Details state menu should be visible.");
Assert.equal(menuList.disabled, locked,
"Details state menu enabled state should be correct.");
Assert.equal(
menuList.disabled,
locked,
"Details state menu enabled state should be correct."
);
resolve();
});
}

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

@ -17,23 +17,41 @@ async function test_inline_plugin_prefs() {
ok(testPlugin, "Test Plug-in should exist");
let pluginEl = get_addon_element(gManagerWindow, testPlugin.id);
is(testPlugin.optionsType, AddonManager.OPTIONS_TYPE_INLINE_BROWSER, "Options should be inline type");
is(
testPlugin.optionsType,
AddonManager.OPTIONS_TYPE_INLINE_BROWSER,
"Options should be inline type"
);
let optionsBrowserPromise =
BrowserTestUtils.waitForEvent(pluginEl.ownerDocument, "load", true, event => {
let {target} = event;
return target.currentURI && target.currentURI.spec === testPlugin.optionsURL;
}).then(event => event.target);
let optionsBrowserPromise = BrowserTestUtils.waitForEvent(
pluginEl.ownerDocument,
"load",
true,
event => {
let { target } = event;
return (
target.currentURI && target.currentURI.spec === testPlugin.optionsURL
);
}
).then(event => event.target);
if (gManagerWindow.useHtmlViews) {
pluginEl.querySelector("panel-item[action='preferences']").click();
} else {
pluginEl.parentNode.ensureElementIsVisible(pluginEl);
let button = gManagerWindow.document.getAnonymousElementByAttribute(pluginEl, "anonid", "preferences-btn");
let button = gManagerWindow.document.getAnonymousElementByAttribute(
pluginEl,
"anonid",
"preferences-btn"
);
is_element_visible(button, "Preferences button should be visible");
EventUtils.synthesizeMouseAtCenter(pluginEl, { clickCount: 1 }, gManagerWindow);
EventUtils.synthesizeMouseAtCenter(
pluginEl,
{ clickCount: 1 },
gManagerWindow
);
await TestUtils.topicObserved(AddonManager.OPTIONS_NOTIFICATION_DISPLAYED);
}
@ -49,11 +67,19 @@ async function test_inline_plugin_prefs() {
let pluginLibraries = doc.getElementById("pluginLibraries");
ok(pluginLibraries, "Plugin file name row should be displayed");
// the file name depends on the platform
is(pluginLibraries.textContent, testPlugin.pluginLibraries, "Plugin file name should be displayed");
is(
pluginLibraries.textContent,
testPlugin.pluginLibraries,
"Plugin file name should be displayed"
);
let pluginMimeTypes = doc.getElementById("pluginMimeTypes");
ok(pluginMimeTypes, "Plugin mime type row should be displayed");
is(pluginMimeTypes.textContent, "application/x-test (Test \u2122 mimetype: tst)", "Plugin mime type should be displayed");
is(
pluginMimeTypes.textContent,
"application/x-test (Test \u2122 mimetype: tst)",
"Plugin mime type should be displayed"
);
await close_manager(gManagerWindow);
}

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

@ -11,15 +11,28 @@ function getTestPlugin(aPlugins) {
}
add_task(async function taskCheckPluginPrefsEnabled() {
const [gManagerWindow, plugins] = await Promise.all([open_manager(), AddonManager.getAddonsByTypes(["plugin"])]);
const [gManagerWindow, plugins] = await Promise.all([
open_manager(),
AddonManager.getAddonsByTypes(["plugin"]),
]);
const testPlugin = getTestPlugin(plugins);
const testPluginTag = getTestPluginTag();
Assert.ok(testPluginTag, "Test Plug-in tag should exist");
const initialTestPluginState = testPluginTag.enabledState;
Assert.ok(gManagerWindow.gViewController.commands.cmd_showItemPreferences.isEnabled(testPlugin), "Test Plug-in preferences should be enabled");
Assert.ok(
gManagerWindow.gViewController.commands.cmd_showItemPreferences.isEnabled(
testPlugin
),
"Test Plug-in preferences should be enabled"
);
testPluginTag.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
Assert.ok(gManagerWindow.gViewController.commands.cmd_showItemPreferences.isEnabled(testPlugin), "Test Plug-in preferences should be enabled");
Assert.ok(
gManagerWindow.gViewController.commands.cmd_showItemPreferences.isEnabled(
testPlugin
),
"Test Plug-in preferences should be enabled"
);
testPluginTag.enabledState = initialTestPluginState;
await close_manager(gManagerWindow);

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

@ -13,23 +13,27 @@ async function test() {
gProvider = new MockProvider();
gProvider.createAddons([{
id: "addon1@tests.mozilla.org",
name: "updated 6 hours ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 60 * 60 * 6)),
releaseNotesURI: Services.io.newURI(TESTROOT + "releaseNotes.xhtml"),
}, {
id: "addon2@tests.mozilla.org",
name: "updated 5 seconds ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 5)),
}, {
id: "addon3@tests.mozilla.org",
name: "updated 1 month ago",
version: "1.0",
updateDate: new Date(Date.now() - (1000 * 60 * 60 * 25 * 30)),
}]);
gProvider.createAddons([
{
id: "addon1@tests.mozilla.org",
name: "updated 6 hours ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 60 * 60 * 6),
releaseNotesURI: Services.io.newURI(TESTROOT + "releaseNotes.xhtml"),
},
{
id: "addon2@tests.mozilla.org",
name: "updated 5 seconds ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 5),
},
{
id: "addon3@tests.mozilla.org",
name: "updated 1 month ago",
version: "1.0",
updateDate: new Date(Date.now() - 1000 * 60 * 60 * 25 * 30),
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
@ -42,28 +46,48 @@ async function end_test() {
finish();
}
add_test(function() {
info("Checking menuitem for Recent Updates opens that pane");
var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
is(gCategoryUtilities.isVisible(recentCat), false, "Recent Updates category should initially be hidden");
is(
gCategoryUtilities.isVisible(recentCat),
false,
"Recent Updates category should initially be hidden"
);
var utilsBtn = gManagerWindow.document.getElementById("header-utils-btn");
utilsBtn.addEventListener("popupshown", async function() {
Promise.resolve().then(() => {
var menuitem = gManagerWindow.document.getElementById("utils-viewUpdates");
EventUtils.synthesizeMouse(menuitem, 2, 2, { }, gManagerWindow);
});
await wait_for_view_load(gManagerWindow, null, true);
is(gCategoryUtilities.isVisible(recentCat), true, "Recent Updates category should now be visible");
is(gManagerWindow.document.getElementById("categories").selectedItem.value, "addons://updates/recent", "Recent Updates category should now be selected");
is(gManagerWindow.gViewController.currentViewId, "addons://updates/recent", "Recent Updates view should be the current view");
run_next_test();
}, {once: true});
EventUtils.synthesizeMouse(utilsBtn, 2, 2, { }, gManagerWindow);
utilsBtn.addEventListener(
"popupshown",
async function() {
Promise.resolve().then(() => {
var menuitem = gManagerWindow.document.getElementById(
"utils-viewUpdates"
);
EventUtils.synthesizeMouse(menuitem, 2, 2, {}, gManagerWindow);
});
await wait_for_view_load(gManagerWindow, null, true);
is(
gCategoryUtilities.isVisible(recentCat),
true,
"Recent Updates category should now be visible"
);
is(
gManagerWindow.document.getElementById("categories").selectedItem.value,
"addons://updates/recent",
"Recent Updates category should now be selected"
);
is(
gManagerWindow.gViewController.currentViewId,
"addons://updates/recent",
"Recent Updates view should be the current view"
);
run_next_test();
},
{ once: true }
);
EventUtils.synthesizeMouse(utilsBtn, 2, 2, {}, gManagerWindow);
});
add_test(async function() {
await close_manager(gManagerWindow);
let aWindow = await open_manager(null);
@ -71,7 +95,11 @@ add_test(async function() {
gCategoryUtilities = new CategoryUtilities(gManagerWindow);
var recentCat = gManagerWindow.gCategories.get("addons://updates/recent");
is(gCategoryUtilities.isVisible(recentCat), true, "Recent Updates category should still be visible");
is(
gCategoryUtilities.isVisible(recentCat),
true,
"Recent Updates category should still be visible"
);
run_next_test();
});

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

@ -5,7 +5,9 @@
// Tests that upgrading bootstrapped add-ons behaves correctly while the
// manager is open
const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
const { AddonTestUtils } = ChromeUtils.import(
"resource://testing-common/AddonTestUtils.jsm"
);
const ID = "reinstall@tests.mozilla.org";
const testIdSuffix = "@tests.mozilla.org";
@ -22,8 +24,13 @@ function get_test_items_in_list(aManager) {
continue;
}
if (!item.mAddon || item.mAddon.id.substring(item.mAddon.id.length - testIdSuffix.length) == testIdSuffix)
if (
!item.mAddon ||
item.mAddon.id.substring(item.mAddon.id.length - testIdSuffix.length) ==
testIdSuffix
) {
items.push(item);
}
item = item.nextSibling;
}
@ -31,20 +38,24 @@ function get_test_items_in_list(aManager) {
}
function htmlDoc() {
return gManagerWindow.document.getElementById("html-view-browser").contentDocument;
return gManagerWindow.document.getElementById("html-view-browser")
.contentDocument;
}
function get_list_item_count() {
if (gManagerWindow.useHtmlViews) {
return htmlDoc()
.querySelectorAll(`addon-card[addon-id$="${testIdSuffix}"]`)
return htmlDoc().querySelectorAll(`addon-card[addon-id$="${testIdSuffix}"]`)
.length;
}
return get_test_items_in_list(gManagerWindow).length;
}
function get_node(parent, anonid) {
return parent.ownerDocument.getAnonymousElementByAttribute(parent, "anonid", anonid);
return parent.ownerDocument.getAnonymousElementByAttribute(
parent,
"anonid",
anonid
);
}
function removeItem(item) {
@ -54,17 +65,23 @@ function removeItem(item) {
button.click();
} else {
button = get_node(item, "remove-btn");
EventUtils.synthesizeMouseAtCenter(button, { }, button.ownerGlobal);
EventUtils.synthesizeMouseAtCenter(button, {}, button.ownerGlobal);
}
}
function get_class_node(parent, cls) {
return parent.ownerDocument.getAnonymousElementByAttribute(parent, "class", cls);
return parent.ownerDocument.getAnonymousElementByAttribute(
parent,
"class",
cls
);
}
function hasPendingMessage(item, msg) {
if (gManagerWindow.useHtmlViews) {
let messageBar = htmlDoc().querySelector(`message-bar[addon-id="${item.addon.id}"`);
let messageBar = htmlDoc().querySelector(
`message-bar[addon-id="${item.addon.id}"`
);
is_element_visible(messageBar, msg);
} else {
is_element_visible(get_class_node(item, "pending"), msg);
@ -72,7 +89,10 @@ function hasPendingMessage(item, msg) {
}
async function install_addon(xpi) {
let install = await AddonManager.getInstallForFile(xpi, "application/x-xpinstall");
let install = await AddonManager.getInstallForFile(
xpi,
"application/x-xpinstall"
);
return install.install();
}
@ -90,28 +110,45 @@ async function check_addon(aAddon, aVersion) {
is(version, aVersion, "Version should be correct");
if (gManagerWindow.useHtmlViews) {
const l10nAttrs = item.ownerDocument.l10n.getAttributes(item.querySelector(".addon-name"));
const l10nAttrs = item.ownerDocument.l10n.getAttributes(
item.querySelector(".addon-name")
);
if (aAddon.userDisabled) {
Assert.deepEqual(l10nAttrs, {id: "addon-name-disabled", args: {name: aAddon.name}},
"localized addon name is marked as disabled");
} else {
Assert.deepEqual(l10nAttrs, {id: null, args: null},
"localized addon name is not marked as disabled");
Assert.deepEqual(
l10nAttrs,
{ id: "addon-name-disabled", args: { name: aAddon.name } },
"localized addon name is marked as disabled"
);
} else {
Assert.deepEqual(
l10nAttrs,
{ id: null, args: null },
"localized addon name is not marked as disabled"
);
}
return;
}
if (aAddon.userDisabled) {
is_element_visible(get_class_node(item, "disabled-postfix"), "Disabled postfix should be hidden");
is_element_visible(
get_class_node(item, "disabled-postfix"),
"Disabled postfix should be hidden"
);
} else {
is_element_hidden(get_class_node(item, "disabled-postfix"), "Disabled postfix should be hidden");
is_element_hidden(
get_class_node(item, "disabled-postfix"),
"Disabled postfix should be hidden"
);
}
}
async function wait_for_addon_item_added(addonId) {
if (gManagerWindow.useHtmlViews) {
await BrowserTestUtils.waitForEvent(htmlDoc().querySelector("addon-list"), "add");
await BrowserTestUtils.waitForEvent(
htmlDoc().querySelector("addon-list"),
"add"
);
const item = get_addon_element(gManagerWindow, addonId);
ok(item, `Found addon card for ${addonId}`);
}
@ -119,7 +156,10 @@ async function wait_for_addon_item_added(addonId) {
async function wait_for_addon_item_removed(addonId) {
if (gManagerWindow.useHtmlViews) {
await BrowserTestUtils.waitForEvent(htmlDoc().querySelector("addon-list"), "remove");
await BrowserTestUtils.waitForEvent(
htmlDoc().querySelector("addon-list"),
"remove"
);
const item = get_addon_element(gManagerWindow, addonId);
ok(!item, `There shouldn't be an addon card for ${addonId}`);
}
@ -127,7 +167,10 @@ async function wait_for_addon_item_removed(addonId) {
async function wait_for_addon_item_updated(addonId) {
if (gManagerWindow.useHtmlViews) {
await BrowserTestUtils.waitForEvent(get_addon_element(gManagerWindow, addonId), "update");
await BrowserTestUtils.waitForEvent(
get_addon_element(gManagerWindow, addonId),
"update"
);
}
}
@ -207,7 +250,10 @@ async function test_upgrade_pending_uninstall_v1_to_v2() {
await promiseItemRemoved;
ok(!!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
ok(
!!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"Add-on should be pending uninstall"
);
hasPendingMessage(item, "Pending message should be visible");
promiseItemAdded = wait_for_addon_item_added(ID);
@ -249,7 +295,10 @@ async function test_upgrade_pending_uninstall_disabled_v1_to_v2() {
item.clientTop;
await promiseItemRemoved;
ok(!!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall");
ok(
!!(addon.pendingOperations & AddonManager.PENDING_UNINSTALL),
"Add-on should be pending uninstall"
);
hasPendingMessage(item, "Pending message should be visible");
promiseItemAdded = wait_for_addon_item_added(ID);
@ -279,8 +328,11 @@ async function test_upgrades(useHtmlViews) {
}
gManagerWindow = await open_manager("addons://list/extension");
is(gManagerWindow.useHtmlViews, useHtmlViews,
"Got about:addons window in the expected mode");
is(
gManagerWindow.useHtmlViews,
useHtmlViews,
"Got about:addons window in the expected mode"
);
await test_upgrade_v1_to_v2();
await test_upgrade_disabled_v1_to_v2();
@ -297,14 +349,14 @@ add_task(async function setup() {
xpi1 = await AddonTestUtils.createTempWebExtensionFile({
manifest: {
version: "1.0",
applications: {gecko: {id: ID}},
applications: { gecko: { id: ID } },
},
});
xpi2 = await AddonTestUtils.createTempWebExtensionFile({
manifest: {
version: "2.0",
applications: {gecko: {id: ID}},
applications: { gecko: { id: ID } },
},
});

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

@ -18,168 +18,188 @@ async function test() {
waitForExplicitFinish();
gProvider = new MockProvider();
gProvider.createAddons([{
// enabledInstalled group
// * Enabled
// * Incompatible but enabled because compatibility checking is off
// * Waiting to be installed
// * Waiting to be enabled
id: "test1@tests.mozilla.org",
name: "Test add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
pendingOperations: AddonManager.PENDING_NONE,
}, {
id: "test2@tests.mozilla.org",
name: "a first add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 23, 59, 59),
pendingOperations: AddonManager.PENDING_UPGRADE,
isActive: true,
isCompatible: false,
}, {
id: "test3@tests.mozilla.org",
name: "\u010Cesk\u00FD slovn\u00EDk", // Český slovník
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 1),
pendingOperations: AddonManager.PENDING_INSTALL,
isActive: false,
}, {
id: "test4@tests.mozilla.org",
name: "canadian dictionary",
updateDate: new Date(1970, 0, 1, 0, 0, 0),
description: "foo",
isActive: true,
}, {
id: "test5@tests.mozilla.org",
name: "croatian dictionary",
description: "foo",
updateDate: new Date(2012, 12, 12, 0, 0, 0),
pendingOperations: AddonManager.PENDING_ENABLE,
isActive: false,
}, {
// pendingDisable group
// * Waiting to be disabled
id: "test6@tests.mozilla.org",
name: "orange Add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
isCompatible: false,
isActive: true,
pendingOperations: AddonManager.PENDING_DISABLE,
}, {
id: "test7@tests.mozilla.org",
name: "Blue Add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 23, 59, 59),
isActive: true,
pendingOperations: AddonManager.PENDING_DISABLE,
}, {
id: "test8@tests.mozilla.org",
name: "Green Add-on",
description: "foo",
updateDate: new Date(2010, 4, 3, 0, 0, 1),
pendingOperations: AddonManager.PENDING_DISABLE,
}, {
id: "test9@tests.mozilla.org",
name: "red Add-on",
updateDate: new Date(2011, 4, 1, 0, 0, 0),
description: "foo",
isCompatible: false,
pendingOperations: AddonManager.PENDING_DISABLE,
}, {
id: "test10@tests.mozilla.org",
name: "Purple Add-on",
description: "foo",
updateDate: new Date(2012, 12, 12, 0, 0, 0),
isCompatible: false,
pendingOperations: AddonManager.PENDING_DISABLE,
}, {
// pendingUninstall group
// * Waiting to be removed
id: "test11@tests.mozilla.org",
name: "amber Add-on",
description: "foo",
updateDate: new Date(1978, 4, 2, 0, 0, 0),
isActive: false,
appDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
}, {
id: "test12@tests.mozilla.org",
name: "Salmon Add-on - pending disable",
description: "foo",
updateDate: new Date(2054, 4, 1, 23, 59, 59),
isActive: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
}, {
id: "test13@tests.mozilla.org",
name: "rose Add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 1),
isActive: false,
userDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
}, {
id: "test14@tests.mozilla.org",
name: "Violet Add-on",
updateDate: new Date(2010, 5, 1, 0, 0, 0),
description: "foo",
isActive: false,
appDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
}, {
id: "test15@tests.mozilla.org",
name: "white Add-on",
description: "foo",
updateDate: new Date(2010, 4, 12, 0, 0, 0),
isActive: false,
userDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
}, {
// disabledIncompatibleBlocked group
// * Disabled
// * Incompatible
// * Blocklisted
id: "test16@tests.mozilla.org",
name: "grimsby Add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 0, 0, 0),
isActive: false,
appDisabled: true,
}, {
id: "test17@tests.mozilla.org",
name: "beamsville Add-on",
description: "foo",
updateDate: new Date(2010, 4, 8, 23, 59, 59),
isActive: false,
userDisabled: true,
}, {
id: "test18@tests.mozilla.org",
name: "smithville Add-on",
description: "foo",
updateDate: new Date(2010, 4, 3, 0, 0, 1),
isActive: false,
userDisabled: true,
blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED,
}, {
id: "test19@tests.mozilla.org",
name: "dunnville Add-on",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
description: "foo",
isActive: false,
appDisabled: true,
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
}, {
id: "test20@tests.mozilla.org",
name: "silverdale Add-on",
description: "foo",
updateDate: new Date(2010, 4, 12, 0, 0, 0),
isActive: false,
appDisabled: true,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
}]);
gProvider.createAddons([
{
// enabledInstalled group
// * Enabled
// * Incompatible but enabled because compatibility checking is off
// * Waiting to be installed
// * Waiting to be enabled
id: "test1@tests.mozilla.org",
name: "Test add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
pendingOperations: AddonManager.PENDING_NONE,
},
{
id: "test2@tests.mozilla.org",
name: "a first add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 23, 59, 59),
pendingOperations: AddonManager.PENDING_UPGRADE,
isActive: true,
isCompatible: false,
},
{
id: "test3@tests.mozilla.org",
name: "\u010Cesk\u00FD slovn\u00EDk", // Český slovník
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 1),
pendingOperations: AddonManager.PENDING_INSTALL,
isActive: false,
},
{
id: "test4@tests.mozilla.org",
name: "canadian dictionary",
updateDate: new Date(1970, 0, 1, 0, 0, 0),
description: "foo",
isActive: true,
},
{
id: "test5@tests.mozilla.org",
name: "croatian dictionary",
description: "foo",
updateDate: new Date(2012, 12, 12, 0, 0, 0),
pendingOperations: AddonManager.PENDING_ENABLE,
isActive: false,
},
{
// pendingDisable group
// * Waiting to be disabled
id: "test6@tests.mozilla.org",
name: "orange Add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
isCompatible: false,
isActive: true,
pendingOperations: AddonManager.PENDING_DISABLE,
},
{
id: "test7@tests.mozilla.org",
name: "Blue Add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 23, 59, 59),
isActive: true,
pendingOperations: AddonManager.PENDING_DISABLE,
},
{
id: "test8@tests.mozilla.org",
name: "Green Add-on",
description: "foo",
updateDate: new Date(2010, 4, 3, 0, 0, 1),
pendingOperations: AddonManager.PENDING_DISABLE,
},
{
id: "test9@tests.mozilla.org",
name: "red Add-on",
updateDate: new Date(2011, 4, 1, 0, 0, 0),
description: "foo",
isCompatible: false,
pendingOperations: AddonManager.PENDING_DISABLE,
},
{
id: "test10@tests.mozilla.org",
name: "Purple Add-on",
description: "foo",
updateDate: new Date(2012, 12, 12, 0, 0, 0),
isCompatible: false,
pendingOperations: AddonManager.PENDING_DISABLE,
},
{
// pendingUninstall group
// * Waiting to be removed
id: "test11@tests.mozilla.org",
name: "amber Add-on",
description: "foo",
updateDate: new Date(1978, 4, 2, 0, 0, 0),
isActive: false,
appDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
},
{
id: "test12@tests.mozilla.org",
name: "Salmon Add-on - pending disable",
description: "foo",
updateDate: new Date(2054, 4, 1, 23, 59, 59),
isActive: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
},
{
id: "test13@tests.mozilla.org",
name: "rose Add-on",
description: "foo",
updateDate: new Date(2010, 4, 2, 0, 0, 1),
isActive: false,
userDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
},
{
id: "test14@tests.mozilla.org",
name: "Violet Add-on",
updateDate: new Date(2010, 5, 1, 0, 0, 0),
description: "foo",
isActive: false,
appDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
},
{
id: "test15@tests.mozilla.org",
name: "white Add-on",
description: "foo",
updateDate: new Date(2010, 4, 12, 0, 0, 0),
isActive: false,
userDisabled: true,
pendingOperations: AddonManager.PENDING_UNINSTALL,
},
{
// disabledIncompatibleBlocked group
// * Disabled
// * Incompatible
// * Blocklisted
id: "test16@tests.mozilla.org",
name: "grimsby Add-on",
description: "foo",
updateDate: new Date(2010, 4, 1, 0, 0, 0),
isActive: false,
appDisabled: true,
},
{
id: "test17@tests.mozilla.org",
name: "beamsville Add-on",
description: "foo",
updateDate: new Date(2010, 4, 8, 23, 59, 59),
isActive: false,
userDisabled: true,
},
{
id: "test18@tests.mozilla.org",
name: "smithville Add-on",
description: "foo",
updateDate: new Date(2010, 4, 3, 0, 0, 1),
isActive: false,
userDisabled: true,
blocklistState: Ci.nsIBlocklistService.STATE_OUTDATED,
},
{
id: "test19@tests.mozilla.org",
name: "dunnville Add-on",
updateDate: new Date(2010, 4, 2, 0, 0, 0),
description: "foo",
isActive: false,
appDisabled: true,
isCompatible: false,
blocklistState: Ci.nsIBlocklistService.STATE_NOT_BLOCKED,
},
{
id: "test20@tests.mozilla.org",
name: "silverdale Add-on",
description: "foo",
updateDate: new Date(2010, 4, 12, 0, 0, 0),
isActive: false,
appDisabled: true,
blocklistState: Ci.nsIBlocklistService.STATE_BLOCKED,
},
]);
let aWindow = await open_manager("addons://list/extension");
gManagerWindow = aWindow;
@ -200,8 +220,9 @@ function set_order(aSortBy, aAscending) {
node = node.nextSibling;
}
gManagerWindow.sortElements(elements, ["uiState", aSortBy], aAscending);
for (let element of elements)
for (let element of elements) {
list.appendChild(element);
}
}
function check_order(aExpectedOrder) {
@ -210,12 +231,17 @@ function check_order(aExpectedOrder) {
var node = list.firstChild;
while (node) {
var id = node.getAttribute("value");
if (id && id.endsWith("@tests.mozilla.org"))
if (id && id.endsWith("@tests.mozilla.org")) {
order.push(node.getAttribute("value"));
}
node = node.nextSibling;
}
is(order.toSource(), aExpectedOrder.toSource(), "Should have seen the right order");
is(
order.toSource(),
aExpectedOrder.toSource(),
"Should have seen the right order"
);
}
// Tests that ascending name ordering was the default

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше