1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
AddonManager: "resource://gre/modules/AddonManager.sys.mjs",
});
export const NormandyAddonManager = {
async downloadAndInstall({
createError,
extensionDetails,
applyNormandyChanges,
undoNormandyChanges,
onInstallStarted,
reportError,
}) {
const { extension_id, hash, hash_algorithm, version, xpi } =
extensionDetails;
const downloadDeferred = Promise.withResolvers();
const installDeferred = Promise.withResolvers();
const install = await lazy.AddonManager.getInstallForURL(xpi, {
hash: `${hash_algorithm}:${hash}`,
telemetryInfo: { source: "internal" },
});
const listener = {
onInstallStarted(cbInstall) {
const versionMatches = cbInstall.addon.version === version;
const idMatches = cbInstall.addon.id === extension_id;
if (!versionMatches || !idMatches) {
installDeferred.reject(createError("metadata-mismatch"));
return false; // cancel the installation, server metadata does not match downloaded add-on
}
if (onInstallStarted) {
return onInstallStarted(cbInstall, installDeferred);
}
return true;
},
onDownloadFailed() {
downloadDeferred.reject(
createError("download-failure", {
detail: lazy.AddonManager.errorToString(install.error),
})
);
},
onDownloadEnded() {
downloadDeferred.resolve();
return false; // temporarily pause installation for Normandy bookkeeping
},
onInstallFailed() {
installDeferred.reject(
createError("install-failure", {
detail: lazy.AddonManager.errorToString(install.error),
})
);
},
onInstallEnded() {
installDeferred.resolve();
},
};
install.addListener(listener);
// Download the add-on
try {
install.install();
await downloadDeferred.promise;
} catch (err) {
reportError(err);
install.removeListener(listener);
throw err;
}
// Complete any book-keeping
try {
await applyNormandyChanges(install);
} catch (err) {
reportError(err);
install.removeListener(listener);
install.cancel();
throw err;
}
// Finish paused installation
try {
install.install();
await installDeferred.promise;
} catch (err) {
reportError(err);
install.removeListener(listener);
await undoNormandyChanges();
throw err;
}
install.removeListener(listener);
return [install.addon.id, install.addon.version];
},
};
|