!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 //@line 44 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
4 */
5
6 //
7 // TODO:
8 // - better logging
9 //
10
11 const Cc = Components.classes;
12 const Ci = Components.interfaces;
13 const Cr = Components.results;
14
15 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
16
17 const PREF_EM_CHECK_COMPATIBILITY = "extensions.checkCompatibility";
18 const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
19 const PREF_EM_LAST_APP_VERSION = "extensions.lastAppVersion";
20 const PREF_EM_ENABLED_ITEMS = "extensions.enabledItems";
21 const PREF_UPDATE_COUNT = "extensions.update.count";
22 const PREF_UPDATE_DEFAULT_URL = "extensions.update.url";
23 const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons";
24 const PREF_EM_IGNOREMTIMECHANGES = "extensions.ignoreMTimeChanges";
25 const PREF_EM_DISABLEDOBSOLETE = "extensions.disabledObsolete";
26 const PREF_EM_EXTENSION_FORMAT = "extensions.%UUID%.";
27 const PREF_EM_ITEM_UPDATE_ENABLED = "extensions.%UUID%.update.enabled";
28 const PREF_EM_UPDATE_ENABLED = "extensions.update.enabled";
29 const PREF_EM_ITEM_UPDATE_URL = "extensions.%UUID%.update.url";
30 const PREF_EM_DSS_ENABLED = "extensions.dss.enabled";
31 const PREF_DSS_SWITCHPENDING = "extensions.dss.switchPending";
32 const PREF_DSS_SKIN_TO_SELECT = "extensions.lastSelectedSkin";
33 const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
34 const PREF_EM_LOGGING_ENABLED = "extensions.logging.enabled";
35 const PREF_EM_UPDATE_INTERVAL = "extensions.update.interval";
36 const PREF_UPDATE_NOTIFYUSER = "extensions.update.notifyUser";
37 const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
38 const PREF_SELECTED_LOCALE = "general.useragent.locale";
39
40 const DIR_EXTENSIONS = "extensions";
41 const DIR_CHROME = "chrome";
42 const DIR_STAGE = "staged-xpis";
43 const FILE_EXTENSIONS = "extensions.rdf";
44 const FILE_EXTENSION_MANIFEST = "extensions.ini";
45 const FILE_EXTENSIONS_STARTUP_CACHE = "extensions.cache";
46 const FILE_EXTENSIONS_LOG = "extensions.log";
47 const FILE_AUTOREG = ".autoreg";
48 const FILE_INSTALL_MANIFEST = "install.rdf";
49 const FILE_CONTENTS_MANIFEST = "contents.rdf";
50 const FILE_CHROME_MANIFEST = "chrome.manifest";
51
52 const UNKNOWN_XPCOM_ABI = "unknownABI";
53
54 const FILE_DEFAULT_THEME_JAR = "classic.jar";
55 const TOOLKIT_ID = "toolkit@mozilla.org"
56
57 const KEY_PROFILEDIR = "ProfD";
58 const KEY_PROFILEDS = "ProfDS";
59 const KEY_APPDIR = "XCurProcD";
60 const KEY_TEMPDIR = "TmpD";
61
62 const EM_ACTION_REQUESTED_TOPIC = "em-action-requested";
63 const EM_ITEM_INSTALLED = "item-installed";
64 const EM_ITEM_UPGRADED = "item-upgraded";
65 const EM_ITEM_UNINSTALLED = "item-uninstalled";
66 const EM_ITEM_ENABLED = "item-enabled";
67 const EM_ITEM_DISABLED = "item-disabled";
68 const EM_ITEM_CANCEL = "item-cancel-action";
69
70 const OP_NONE = "";
71 const OP_NEEDS_INSTALL = "needs-install";
72 const OP_NEEDS_UPGRADE = "needs-upgrade";
73 const OP_NEEDS_UNINSTALL = "needs-uninstall";
74 const OP_NEEDS_ENABLE = "needs-enable";
75 const OP_NEEDS_DISABLE = "needs-disable";
76
77 const KEY_APP_PROFILE = "app-profile";
78 const KEY_APP_GLOBAL = "app-global";
79 const KEY_APP_SYSTEM_LOCAL = "app-system-local";
80 const KEY_APP_SYSTEM_SHARE = "app-system-share";
81 const KEY_APP_SYSTEM_USER = "app-system-user";
82
83 const CATEGORY_INSTALL_LOCATIONS = "extension-install-locations";
84 const CATEGORY_UPDATE_PARAMS = "extension-update-params";
85
86 const PREFIX_NS_EM = "http://www.mozilla.org/2004/em-rdf#";
87 const PREFIX_NS_CHROME = "http://www.mozilla.org/rdf/chrome#";
88 const PREFIX_ITEM_URI = "urn:mozilla:item:";
89 const PREFIX_EXTENSION = "urn:mozilla:extension:";
90 const PREFIX_THEME = "urn:mozilla:theme:";
91 const RDFURI_INSTALL_MANIFEST_ROOT = "urn:mozilla:install-manifest";
92 const RDFURI_ITEM_ROOT = "urn:mozilla:item:root"
93 const RDFURI_DEFAULT_THEME = "urn:mozilla:item:{972ce4c6-7e08-4474-a285-3208198ce6fd}";
94 const XMLURI_PARSE_ERROR = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
95
96 const URI_GENERIC_ICON_XPINSTALL = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
97 const URI_GENERIC_ICON_THEME = "chrome://mozapps/skin/extensions/themeGeneric.png";
98 const URI_XPINSTALL_CONFIRM_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
99 const URI_EXTENSIONS_PROPERTIES = "chrome://mozapps/locale/extensions/extensions.properties";
100 const URI_BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
101 const URI_DOWNLOADS_PROPERTIES = "chrome://mozapps/locale/downloads/downloads.properties";
102 const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
103 const URI_EXTENSION_LIST_DIALOG = "chrome://mozapps/content/extensions/list.xul";
104
105 const INSTALLERROR_SUCCESS = 0;
106 const INSTALLERROR_INVALID_VERSION = -1;
107 const INSTALLERROR_INVALID_GUID = -2;
108 const INSTALLERROR_INCOMPATIBLE_VERSION = -3;
109 const INSTALLERROR_PHONED_HOME = -4;
110 const INSTALLERROR_INCOMPATIBLE_PLATFORM = -5;
111 const INSTALLERROR_BLOCKLISTED = -6;
112 const INSTALLERROR_INSECURE_UPDATE = -7;
113 const INSTALLERROR_INVALID_MANIFEST = -8;
114 const INSTALLERROR_RESTRICTED = -9;
115
116 const MODE_RDONLY = 0x01;
117 const MODE_WRONLY = 0x02;
118 const MODE_CREATE = 0x08;
119 const MODE_APPEND = 0x10;
120 const MODE_TRUNCATE = 0x20;
121
122 const PERMS_FILE = 0644;
123 const PERMS_DIRECTORY = 0755;
124
125 var gApp = null;
126 var gPref = null;
127 var gRDF = null;
128 var gOS = null;
129 var gBlocklist = null;
130 var gXPCOMABI = null;
131 var gOSTarget = null;
132 var gConsole = null;
133 var gInstallManifestRoot = null;
134 var gVersionChecker = null;
135 var gLoggingEnabled = null;
136 var gCheckCompatibility = true;
137 var gCheckUpdateSecurity = true;
138 var gLocale = "en-US";
139 var gFirstRun = false;
140
141 /**
142 * Valid GUIDs fit this pattern.
143 */
144 var gIDTest = /^(\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}|[a-z0-9-\._]*\@[a-z0-9-\._]+)$/i;
145
146 // shared code for suppressing bad cert dialogs
147 //@line 40 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/shared/src/badCertHandler.js"
148
149 /**
150 * Only allow built-in certs for HTTPS connections. See bug 340198.
151 */
checkCert
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
152 function checkCert(channel) {
153 if (!channel.originalURI.schemeIs("https")) // bypass
154 return;
155
156 const Ci = Components.interfaces;
157 var cert =
158 channel.securityInfo.QueryInterface(Ci.nsISSLStatusProvider).
159 SSLStatus.QueryInterface(Ci.nsISSLStatus).serverCert;
160
161 var issuer = cert.issuer;
162 while (issuer && !cert.equals(issuer)) {
163 cert = issuer;
164 issuer = cert.issuer;
165 }
166
167 if (!issuer || issuer.tokenName != "Builtin Object Token")
168 throw "cert issuer is not built-in";
169 }
170
171 /**
172 * This class implements nsIBadCertListener. It's job is to prevent "bad cert"
173 * security dialogs from being shown to the user. It is better to simply fail
174 * if the certificate is bad. See bug 304286.
175 */
BadCertHandler
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
176 function BadCertHandler() {
177 }
178 BadCertHandler.prototype = {
179
180 // nsIChannelEventSink
onChannelRedirect
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
181 onChannelRedirect: function(oldChannel, newChannel, flags) {
182 // make sure the certificate of the old channel checks out before we follow
183 // a redirect from it. See bug 340198.
184 checkCert(oldChannel);
185 },
186
187 // Suppress any certificate errors
notifyCertProblem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
188 notifyCertProblem: function(socketInfo, status, targetSite) {
189 return true;
190 },
191
192 // Suppress any ssl errors
notifySSLError
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
193 notifySSLError: function(socketInfo, error, targetSite) {
194 return true;
195 },
196
197 // nsIInterfaceRequestor
getInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
198 getInterface: function(iid) {
199 return this.QueryInterface(iid);
200 },
201
202 // nsISupports
QueryInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
203 QueryInterface: function(iid) {
204 if (!iid.equals(Components.interfaces.nsIChannelEventSink) &&
205 !iid.equals(Components.interfaces.nsIBadCertListener2) &&
206 !iid.equals(Components.interfaces.nsISSLErrorListener) &&
207 !iid.equals(Components.interfaces.nsIInterfaceRequestor) &&
208 !iid.equals(Components.interfaces.nsISupports))
209 throw Components.results.NS_ERROR_NO_INTERFACE;
210 return this;
211 }
212 };
213 //@line 188 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
214
215 /**
216 * Creates a Version Checker object.
217 * @returns A handle to the global Version Checker service.
218 */
getVersionChecker
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
219 function getVersionChecker() {
220 if (!gVersionChecker) {
221 gVersionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"].
222 getService(Ci.nsIVersionComparator);
223 }
224 return gVersionChecker;
225 }
226
227 var BundleManager = {
228 /**
229 * Creates and returns a String Bundle at the specified URI
230 * @param bundleURI
231 * The URI of the bundle to load
232 * @returns A nsIStringBundle which was retrieved.
233 */
getBundle
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
234 getBundle: function(bundleURI) {
235 var sbs = Cc["@mozilla.org/intl/stringbundle;1"].
236 getService(Ci.nsIStringBundleService);
237 return sbs.createBundle(bundleURI);
238 },
239
240 _appName: "",
241
242 /**
243 * The Application's display name.
244 */
get_appName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
245 get appName() {
246 if (!this._appName) {
247 var brandBundle = this.getBundle(URI_BRAND_PROPERTIES)
248 this._appName = brandBundle.GetStringFromName("brandShortName");
249 }
250 return this._appName;
251 }
252 };
253
254 ///////////////////////////////////////////////////////////////////////////////
255 //
256 // Utility Functions
257 //
EM_NS
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
258 function EM_NS(property) {
259 return PREFIX_NS_EM + property;
260 }
261
CHROME_NS
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
262 function CHROME_NS(property) {
263 return PREFIX_NS_CHROME + property;
264 }
265
EM_R
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
266 function EM_R(property) {
267 return gRDF.GetResource(EM_NS(property));
268 }
269
EM_L
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
270 function EM_L(literal) {
271 return gRDF.GetLiteral(literal);
272 }
273
EM_I
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
274 function EM_I(integer) {
275 return gRDF.GetIntLiteral(integer);
276 }
277
EM_D
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
278 function EM_D(integer) {
279 return gRDF.GetDateLiteral(integer);
280 }
281
282 /**
283 * Gets a preference value, handling the case where there is no default.
284 * @param func
285 * The name of the preference function to call, on nsIPrefBranch
286 * @param preference
287 * The name of the preference
288 * @param defaultValue
289 * The default value to return in the event the preference has
290 * no setting
291 * @returns The value of the preference, or undefined if there was no
292 * user or default value.
293 */
getPref
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
294 function getPref(func, preference, defaultValue) {
295 try {
296 return gPref[func](preference);
297 }
298 catch (e) {
299 }
300 return defaultValue;
301 }
302
303 /**
304 * Initializes a RDF Container at a URI in a datasource.
305 * @param datasource
306 * The datasource the container is in
307 * @param root
308 * The RDF Resource which is the root of the container.
309 * @returns The nsIRDFContainer, initialized at the root.
310 */
getContainer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
311 function getContainer(datasource, root) {
312 var ctr = Cc["@mozilla.org/rdf/container;1"].
313 createInstance(Ci.nsIRDFContainer);
314 ctr.Init(datasource, root);
315 return ctr;
316 }
317
318 /**
319 * Gets a RDF Resource for item with the given ID
320 * @param id
321 * The GUID of the item to construct a RDF resource to the
322 * active item for
323 * @returns The RDF Resource to the Active item.
324 */
getResourceForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
325 function getResourceForID(id) {
326 return gRDF.GetResource(PREFIX_ITEM_URI + id);
327 }
328
329 /**
330 * Construct a nsIUpdateItem with the supplied metadata
331 * ...
332 */
makeItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
333 function makeItem(id, version, locationKey, minVersion, maxVersion, name,
334 updateURL, updateHash, iconURL, updateRDF, updateKey, type,
335 targetAppID) {
336 var item = new UpdateItem();
337 item.init(id, version, locationKey, minVersion, maxVersion, name,
338 updateURL, updateHash, iconURL, updateRDF, updateKey, type,
339 targetAppID);
340 return item;
341 }
342
343 /**
344 * Gets the specified directory at the specified hierarchy under a
345 * Directory Service key.
346 * @param key
347 * The Directory Service Key to start from
348 * @param pathArray
349 * An array of path components to locate beneath the directory
350 * specified by |key|
351 * @return nsIFile object for the location specified. If the directory
352 * requested does not exist, it is created, along with any
353 * parent directories that need to be created.
354 */
getDir
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
355 function getDir(key, pathArray) {
356 return getDirInternal(key, pathArray, true);
357 }
358
359 /**
360 * Gets the specified directory at the specified hierarchy under a
361 * Directory Service key.
362 * @param key
363 * The Directory Service Key to start from
364 * @param pathArray
365 * An array of path components to locate beneath the directory
366 * specified by |key|
367 * @return nsIFile object for the location specified. If the directory
368 * requested does not exist, it is NOT created.
369 */
getDirNoCreate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
370 function getDirNoCreate(key, pathArray) {
371 return getDirInternal(key, pathArray, false);
372 }
373
374 /**
375 * Gets the specified directory at the specified hierarchy under a
376 * Directory Service key.
377 * @param key
378 * The Directory Service Key to start from
379 * @param pathArray
380 * An array of path components to locate beneath the directory
381 * specified by |key|
382 * @param shouldCreate
383 * true if the directory hierarchy specified in |pathArray|
384 * should be created if it does not exist,
385 * false otherwise.
386 * @return nsIFile object for the location specified.
387 */
getDirInternal
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
388 function getDirInternal(key, pathArray, shouldCreate) {
389 var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
390 getService(Ci.nsIProperties);
391 var dir = fileLocator.get(key, Ci.nsILocalFile);
392 for (var i = 0; i < pathArray.length; ++i) {
393 dir.append(pathArray[i]);
394 if (shouldCreate && !dir.exists())
395 dir.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
396 }
397 dir.followLinks = false;
398 return dir;
399 }
400
401 /**
402 * Gets the file at the specified hierarchy under a Directory Service key.
403 * @param key
404 * The Directory Service Key to start from
405 * @param pathArray
406 * An array of path components to locate beneath the directory
407 * specified by |key|. The last item in this array must be the
408 * leaf name of a file.
409 * @return nsIFile object for the file specified. The file is NOT created
410 * if it does not exist, however all required directories along
411 * the way are.
412 */
getFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
413 function getFile(key, pathArray) {
414 var file = getDir(key, pathArray.slice(0, -1));
415 file.append(pathArray[pathArray.length - 1]);
416 return file;
417 }
418
419 /**
420 * Gets the descriptor of a directory as a relative path to common base
421 * directories (profile, user home, app install dir, etc).
422 *
423 * @param itemLocation
424 * The nsILocalFile representing the item's directory.
425 * @param installLocation the nsIInstallLocation for this item
426 */
getDescriptorFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
427 function getDescriptorFromFile(itemLocation, installLocation) {
428 var baseDir = installLocation.location;
429
430 if (baseDir && baseDir.contains(itemLocation, true)) {
431 return "rel%" + itemLocation.getRelativeDescriptor(baseDir);
432 }
433
434 return "abs%" + itemLocation.persistentDescriptor;
435 }
436
getAbsoluteDescriptor
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
437 function getAbsoluteDescriptor(itemLocation) {
438 return itemLocation.persistentDescriptor;
439 }
440
441 /**
442 * Initializes a Local File object based on a descriptor
443 * provided by "getDescriptorFromFile".
444 *
445 * @param descriptor
446 * The descriptor that locates the directory
447 * @param installLocation
448 * The nsIInstallLocation object for this item.
449 * @returns The nsILocalFile object representing the location of the item
450 */
getFileFromDescriptor
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
451 function getFileFromDescriptor(descriptor, installLocation) {
452 var location = Cc["@mozilla.org/file/local;1"].
453 createInstance(Ci.nsILocalFile);
454
455 var m = descriptor.match(/^(abs|rel)\%(.*)$/);
456 if (!m)
457 throw Cr.NS_ERROR_INVALID_ARG;
458
459 if (m[1] == "rel") {
460 location.setRelativeDescriptor(installLocation.location, m[2]);
461 }
462 else {
463 location.persistentDescriptor = m[2];
464 }
465
466 return location;
467 }
468
469 /**
470 * Determines if a file is an item package - either a XPI or a JAR file.
471 * @param file
472 * The file to check
473 * @returns true if the file is an item package, false otherwise.
474 */
fileIsItemPackage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
475 function fileIsItemPackage(file) {
476 var fileURL = getURIFromFile(file);
477 if (fileURL instanceof Ci.nsIURL)
478 var extension = fileURL.fileExtension.toLowerCase();
479 return extension == "xpi" || extension == "jar";
480 }
481
482 /**
483 * Opens a safe file output stream for writing.
484 * @param file
485 * The file to write to.
486 * @param modeFlags
487 * (optional) File open flags. Can be undefined.
488 * @returns nsIFileOutputStream to write to.
489 */
openSafeFileOutputStream
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
490 function openSafeFileOutputStream(file, modeFlags) {
491 var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
492 createInstance(Ci.nsIFileOutputStream);
493 if (modeFlags === undefined)
494 modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
495 if (!file.exists())
496 file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
497 fos.init(file, modeFlags, PERMS_FILE, 0);
498 return fos;
499 }
500
501 /**
502 * Closes a safe file output stream.
503 * @param stream
504 * The stream to close.
505 */
closeSafeFileOutputStream
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
506 function closeSafeFileOutputStream(stream) {
507 if (stream instanceof Ci.nsISafeOutputStream)
508 stream.finish();
509 else
510 stream.close();
511 }
512
513 /**
514 * Deletes a directory and its children. First it tries nsIFile::Remove(true).
515 * If that fails it will fall back to recursing, setting the appropriate
516 * permissions, and deleting the current entry. This is needed for when we have
517 * rights to delete a directory but there are entries that have a read-only
518 * attribute (e.g. a copy restore from a read-only CD, etc.)
519 * @param dir
520 * A nsIFile for the directory to be deleted
521 */
removeDirRecursive
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
522 function removeDirRecursive(dir) {
523 try {
524 dir.remove(true);
525 return;
526 }
527 catch (e) {
528 }
529
530 var dirEntries = dir.directoryEntries;
531 while (dirEntries.hasMoreElements()) {
532 var entry = dirEntries.getNext().QueryInterface(Ci.nsIFile);
533
534 if (entry.isDirectory()) {
535 removeDirRecursive(entry);
536 }
537 else {
538 entry.permissions = PERMS_FILE;
539 entry.remove(false);
540 }
541 }
542 dir.permissions = PERMS_DIRECTORY;
543 dir.remove(true);
544 }
545
546 /**
547 * Logs a string to the error console.
548 * @param string
549 * The string to write to the error console.
550 */
LOG
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
551 function LOG(string) {
552 if (gLoggingEnabled) {
553 dump("*** " + string + "\n");
554 if (gConsole)
555 gConsole.logStringMessage(string);
556 }
557 }
558
559 /**
560 * Logs a string to the error console and to a permanent log file.
561 * @param string
562 * The string to write out.
563 */
ERROR
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
564 function ERROR(string) {
565 LOG(string);
566 try {
567 var tstamp = new Date();
568 var logfile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_LOG]);
569 var stream = Cc["@mozilla.org/network/file-output-stream;1"].
570 createInstance(Ci.nsIFileOutputStream);
571 stream.init(logfile, 0x02 | 0x08 | 0x10, 0666, 0); // write, create, append
572 var writer = Cc["@mozilla.org/intl/converter-output-stream;1"].
573 createInstance(Ci.nsIConverterOutputStream);
574 writer.init(stream, "UTF-8", 0, 0x0000);
575 string = tstamp.toLocaleFormat("%Y-%m-%d %H:%M:%S - ") + string;
576 writer.writeString(string + "\n");
577 writer.close();
578 }
579 catch (e) { }
580 }
581
582 /**
583 * Randomize the specified file name. Used to force RDF to bypass the cache
584 * when loading certain types of files.
585 * @param fileName
586 * A file name to randomize, e.g. install.rdf
587 * @returns A randomized file name, e.g. install-xyz.rdf
588 */
getRandomFileName
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
589 function getRandomFileName(fileName) {
590 var extensionDelimiter = fileName.lastIndexOf(".");
591 var prefix = fileName.substr(0, extensionDelimiter);
592 var suffix = fileName.substr(extensionDelimiter);
593
594 var characters = "abcdefghijklmnopqrstuvwxyz0123456789";
595 var nameString = prefix + "-";
596 for (var i = 0; i < 3; ++i) {
597 var index = Math.round((Math.random()) * characters.length);
598 nameString += characters.charAt(index);
599 }
600 return nameString + "." + suffix;
601 }
602
603 /**
604 * Get the RDF URI prefix of a nsIUpdateItem type. This function should be used
605 * ONLY to support Firefox 1.0 Update RDF files! Item URIs in the datasource
606 * are NOT prefixed.
607 * @param type
608 * The nsIUpdateItem type to find a RDF URI prefix for
609 * @returns The RDF URI prefix.
610 */
getItemPrefix
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
611 function getItemPrefix(type) {
612 if (type & Ci.nsIUpdateItem.TYPE_EXTENSION)
613 return PREFIX_EXTENSION;
614 else if (type & Ci.nsIUpdateItem.TYPE_THEME)
615 return PREFIX_THEME;
616 return PREFIX_ITEM_URI;
617 }
618
619 /**
620 * Trims a prefix from a string.
621 * @param string
622 * The source string
623 * @param prefix
624 * The prefix to remove.
625 * @returns The suffix (string - prefix)
626 */
stripPrefix
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
627 function stripPrefix(string, prefix) {
628 return string.substr(prefix.length);
629 }
630
631 /**
632 * Gets a File URL spec for a nsIFile
633 * @param file
634 * The file to get a file URL spec to
635 * @returns The file URL spec to the file
636 */
getURLSpecFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
637 function getURLSpecFromFile(file) {
638 var ioServ = Cc["@mozilla.org/network/io-service;1"].
639 getService(Ci.nsIIOService);
640 var fph = ioServ.getProtocolHandler("file")
641 .QueryInterface(Ci.nsIFileProtocolHandler);
642 return fph.getURLSpecFromFile(file);
643 }
644
645 /**
646 * Constructs a URI to a spec.
647 * @param spec
648 * The spec to construct a URI to
649 * @returns The nsIURI constructed.
650 */
newURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
651 function newURI(spec) {
652 var ioServ = Cc["@mozilla.org/network/io-service;1"].
653 getService(Ci.nsIIOService);
654 return ioServ.newURI(spec, null, null);
655 }
656
657 /**
658 * Constructs a File URI to a nsIFile
659 * @param file
660 * The file to construct a File URI to
661 * @returns The file URI to the file
662 */
getURIFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
663 function getURIFromFile(file) {
664 var ioServ = Cc["@mozilla.org/network/io-service;1"].
665 getService(Ci.nsIIOService);
666 return ioServ.newFileURI(file);
667 }
668
669 /**
670 * @returns Whether or not we are currently running in safe mode.
671 */
inSafeMode
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
672 function inSafeMode() {
673 return gApp.inSafeMode;
674 }
675
676 /**
677 * Extract the string value from a RDF Literal or Resource
678 * @param literalOrResource
679 * RDF String Literal or Resource
680 * @returns String value of the literal or resource, or undefined if the object
681 * supplied is not a RDF string literal or resource.
682 */
stringData
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
683 function stringData(literalOrResource) {
684 if (literalOrResource instanceof Ci.nsIRDFLiteral)
685 return literalOrResource.Value;
686 if (literalOrResource instanceof Ci.nsIRDFResource)
687 return literalOrResource.Value;
688 return undefined;
689 }
690
691 /**
692 * Extract the integer value of a RDF Literal
693 * @param literal
694 * nsIRDFInt literal
695 * @return integer value of the literal
696 */
intData
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
697 function intData(literal) {
698 if (literal instanceof Ci.nsIRDFInt)
699 return literal.Value;
700 return undefined;
701 }
702
703 /**
704 * Gets a property from an install manifest.
705 * @param installManifest
706 * An Install Manifest datasource to read from
707 * @param property
708 * The name of a proprety to read (sans EM_NS)
709 * @returns The literal value of the property, or undefined if the property has
710 * no value.
711 */
getManifestProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
712 function getManifestProperty(installManifest, property) {
713 var target = installManifest.GetTarget(gInstallManifestRoot,
714 gRDF.GetResource(EM_NS(property)), true);
715 var val = stringData(target);
716 return val === undefined ? intData(target) : val;
717 }
718
719 /**
720 * Given an Install Manifest Datasource, retrieves the type of item the manifest
721 * describes.
722 * @param installManifest
723 * The Install Manifest Datasource.
724 * @return The nsIUpdateItem type of the item described by the manifest
725 * returns TYPE_EXTENSION if attempts to determine the type fail.
726 */
getAddonTypeFromInstallManifest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
727 function getAddonTypeFromInstallManifest(installManifest) {
728 var target = installManifest.GetTarget(gInstallManifestRoot,
729 gRDF.GetResource(EM_NS("type")), true);
730 if (target) {
731 var type = stringData(target);
732 return type === undefined ? intData(target) : parseInt(type);
733 }
734
735 // Firefox 1.0 and earlier did not support addon-type annotation on the
736 // Install Manifest, so we fall back to a theme-only property to
737 // differentiate.
738 if (getManifestProperty(installManifest, "internalName") !== undefined)
739 return Ci.nsIUpdateItem.TYPE_THEME;
740
741 // If no type is provided, default to "Extension"
742 return Ci.nsIUpdateItem.TYPE_EXTENSION;
743 }
744
745 /**
746 * Shows a message about an incompatible Extension/Theme.
747 * @param installData
748 * An Install Data object from |getInstallData|
749 */
showIncompatibleError
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
750 function showIncompatibleError(installData) {
751 var extensionStrings = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
752 var params = [extensionStrings.GetStringFromName("type-" + installData.type)];
753 var title = extensionStrings.formatStringFromName("incompatibleTitle",
754 params, params.length);
755 params = [installData.name, installData.version, BundleManager.appName,
756 gApp.version];
757 var message = extensionStrings.formatStringFromName("incompatibleMessage",
758 params, params.length);
759 var ps = Cc["@mozilla.org/embedcomp/prompt-service;1"].
760 getService(Ci.nsIPromptService);
761 ps.alert(null, title, message);
762 }
763
764 /**
765 * Shows a message.
766 * @param titleKey
767 * String key of the title string in the Extensions localization file.
768 * @param messageKey
769 * String key of the message string in the Extensions localization file.
770 * @param messageParams
771 * Array of strings to be substituted into |messageKey|. Can be null.
772 */
showMessage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
773 function showMessage(titleKey, titleParams, messageKey, messageParams) {
774 var extensionStrings = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
775 if (titleParams && titleParams.length > 0) {
776 var title = extensionStrings.formatStringFromName(titleKey, titleParams,
777 titleParams.length);
778 }
779 else
780 title = extensionStrings.GetStringFromName(titleKey);
781
782 if (messageParams && messageParams.length > 0) {
783 var message = extensionStrings.formatStringFromName(messageKey, messageParams,
784 messageParams.length);
785 }
786 else
787 message = extensionStrings.GetStringFromName(messageKey);
788 var ps = Cc["@mozilla.org/embedcomp/prompt-service;1"].
789 getService(Ci.nsIPromptService);
790 ps.alert(null, title, message);
791 }
792
793 /**
794 * Shows a dialog for blocklisted items.
795 * @param items
796 * An array of nsIUpdateItems.
797 * @param fromInstall
798 * Whether this is called from an install or from the blocklist
799 * background check.
800 */
showBlocklistMessage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
801 function showBlocklistMessage(items, fromInstall) {
802 var win = null;
803 var params = Cc["@mozilla.org/embedcomp/dialogparam;1"].
804 createInstance(Ci.nsIDialogParamBlock);
805 params.SetInt(0, (fromInstall ? 1 : 0));
806 params.SetInt(1, items.length);
807 params.SetNumberStrings(items.length * 2);
808 for (var i = 0; i < items.length; ++i)
809 params.SetString(i, items[i].name + " " + items[i].version);
810
811 // if this was initiated from an install try to find the appropriate manager
812 if (fromInstall) {
813 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
814 getService(Ci.nsIWindowMediator);
815 win = wm.getMostRecentWindow("Extension:Manager");
816 }
817 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
818 getService(Ci.nsIWindowWatcher);
819 ww.openWindow(win, URI_EXTENSION_LIST_DIALOG, "",
820 "chrome,centerscreen,modal,dialog,titlebar", params);
821 }
822
823 /**
824 * Gets a zip reader for the file specified.
825 * @param zipFile
826 * A ZIP archive to open with a nsIZipReader.
827 * @return A nsIZipReader for the file specified.
828 */
getZipReaderForFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
829 function getZipReaderForFile(zipFile) {
830 try {
831 var zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
832 createInstance(Ci.nsIZipReader);
833 zipReader.open(zipFile);
834 }
835 catch (e) {
836 zipReader.close();
837 throw e;
838 }
839 return zipReader;
840 }
841
842 /**
843 * Extract a RDF file from a ZIP archive to a random location in the system
844 * temp directory.
845 * @param zipFile
846 * A ZIP archive to read from
847 * @param fileName
848 * The name of the file to read from the zip.
849 * @param suppressErrors
850 * Whether or not to report errors.
851 * @return The file created in the temp directory.
852 */
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
853 function extractRDFFileToTempDir(zipFile, fileName, suppressErrors) {
854 var file = getFile(KEY_TEMPDIR, [getRandomFileName(fileName)]);
855 try {
856 var zipReader = getZipReaderForFile(zipFile);
857 zipReader.extract(fileName, file);
858 zipReader.close();
859 }
860 catch (e) {
861 if (!suppressErrors) {
862 showMessage("missingFileTitle", [], "missingFileMessage",
863 [BundleManager.appName, fileName]);
864 throw e;
865 }
866 }
867 return file;
868 }
869
870 /**
871 * Gets an Install Manifest datasource from a file.
872 * @param file
873 * The nsIFile that contains the Install Manifest RDF
874 * @returns The Install Manifest datasource
875 */
getInstallManifest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
876 function getInstallManifest(file) {
877 var uri = getURIFromFile(file);
878 try {
879 var fis = Cc["@mozilla.org/network/file-input-stream;1"].
880 createInstance(Ci.nsIFileInputStream);
881 fis.init(file, -1, -1, false);
882 var bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
883 createInstance(Ci.nsIBufferedInputStream);
884 bis.init(fis, 4096);
885
886 var rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
887 createInstance(Ci.nsIRDFXMLParser)
888 var ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
889 createInstance(Ci.nsIRDFDataSource);
890 var listener = rdfParser.parseAsync(ds, uri);
891 var channel = Cc["@mozilla.org/network/input-stream-channel;1"].
892 createInstance(Ci.nsIInputStreamChannel);
893 channel.setURI(uri);
894 channel.contentStream = bis;
895 channel.QueryInterface(Ci.nsIChannel);
896 channel.contentType = "text/xml";
897
898 listener.onStartRequest(channel, null);
899 try {
900 var pos = 0;
901 var count = bis.available();
902 while (count > 0) {
903 listener.onDataAvailable(channel, null, bis, pos, count);
904 pos += count;
905 count = bis.available();
906 }
907 listener.onStopRequest(channel, null, Components.results.NS_OK);
908 bis.close();
909 fis.close();
910
911 var arcs = ds.ArcLabelsOut(gInstallManifestRoot);
912 if (arcs.hasMoreElements())
913 return ds;
914 }
915 catch (e) {
916 listener.onStopRequest(channel, null, e.result);
917 bis.close();
918 fis.close();
919 }
920 }
921 catch (e) { }
922
923 var url = uri.QueryInterface(Ci.nsIURL);
924 showMessage("malformedTitle", [], "malformedMessage",
925 [BundleManager.appName, url.fileName]);
926 return null;
927 }
928
929 /**
930 * Selects the closest matching localized resource in the given RDF resource
931 * @param aDataSource The datasource to look in
932 * @param aResource The root resource containing the localized sections
933 * @returns The nsIRDFResource of the best em:localized section or null
934 * if no valid match was found
935 */
findClosestLocalizedResource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
936 function findClosestLocalizedResource(aDataSource, aResource) {
937 var localizedProp = EM_R("localized");
938 var localeProp = EM_R("locale");
939
940 // Holds the best matching localized resource
941 var bestmatch = null;
942 // The number of locale parts it matched with
943 var bestmatchcount = 0;
944 // The number of locale parts in the match
945 var bestpartcount = 0;
946
947 var locales = [gLocale.toLowerCase()];
948 /* If the current locale is English then it will find a match if there is
949 a valid match for en-US so no point searching that locale too. */
950 if (locales[0].substring(0, 3) != "en-")
951 locales.push("en-us");
952
953 for each (var locale in locales) {
954 var lparts = locale.split("-");
955 var localizations = aDataSource.GetTargets(aResource, localizedProp, true);
956 while (localizations.hasMoreElements()) {
957 var localized = localizations.getNext().QueryInterface(Ci.nsIRDFNode);
958 var list = aDataSource.GetTargets(localized, localeProp, true);
959 while (list.hasMoreElements()) {
960 var found = stringData(list.getNext().QueryInterface(Ci.nsIRDFNode));
961 if (!found)
962 continue;
963
964 found = found.toLowerCase();
965
966 // Exact match is returned immediately
967 if (locale == found)
968 return localized;
969
970 var fparts = found.split("-");
971 /* If we have found a possible match and this one isn't any longer
972 then we dont need to check further. */
973 if (bestmatch && fparts.length < bestmatchcount)
974 continue;
975
976 // Count the number of parts that match
977 var maxmatchcount = Math.min(fparts.length, lparts.length);
978 var matchcount = 0;
979 while (matchcount < maxmatchcount &&
980 fparts[matchcount] == lparts[matchcount])
981 matchcount++;
982
983 /* If we matched more than the last best match or matched the same and
984 this locale is less specific than the last best match. */
985 if (matchcount > bestmatchcount ||
986 (matchcount == bestmatchcount && fparts.length < bestpartcount)) {
987 bestmatch = localized;
988 bestmatchcount = matchcount;
989 bestpartcount = fparts.length;
990 }
991 }
992 }
993 // If we found a valid match for this locale return it
994 if (bestmatch)
995 return bestmatch;
996 }
997 return null;
998 }
999
1000 /**
1001 * An enumeration of items in a JS array.
1002 * @constructor
1003 */
ArrayEnumerator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1004 function ArrayEnumerator(aItems) {
1005 if (aItems) {
1006 for (var i = 0; i < aItems.length; ++i) {
1007 if (!aItems[i])
1008 aItems.splice(i--, 1);
1009 }
1010 this._contents = aItems;
1011 } else {
1012 this._contents = [];
1013 }
1014 }
1015
1016 ArrayEnumerator.prototype = {
1017 _index: 0,
1018
hasMoreElements
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1019 hasMoreElements: function () {
1020 return this._index < this._contents.length;
1021 },
1022
getNext
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1023 getNext: function () {
1024 return this._contents[this._index++];
1025 }
1026 };
1027
1028 /**
1029 * An enumeration of files in a JS array.
1030 * @param files
1031 * The files to enumerate
1032 * @constructor
1033 */
FileEnumerator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1034 function FileEnumerator(files) {
1035 if (files) {
1036 for (var i = 0; i < files.length; ++i) {
1037 if (!files[i])
1038 files.splice(i--, 1);
1039 }
1040 this._contents = files;
1041 } else {
1042 this._contents = [];
1043 }
1044 }
1045
1046 FileEnumerator.prototype = {
1047 _index: 0,
1048
1049 /**
1050 * Gets the next file in the sequence.
1051 */
get_nextFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1052 get nextFile() {
1053 if (this._index < this._contents.length)
1054 return this._contents[this._index++];
1055 return null;
1056 },
1057
1058 /**
1059 * Stop enumerating. Nothing to do here.
1060 */
close
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1061 close: function() {
1062 }
1063 };
1064
1065 /**
1066 * An object which identifies an Install Location for items, where the location
1067 * relationship is each item living in a directory named with its GUID under
1068 * the directory used when constructing this object.
1069 *
1070 * e.g. <location>\{GUID1}
1071 * <location>\{GUID2}
1072 * <location>\{GUID3}
1073 * ...
1074 *
1075 * @param name
1076 * The string identifier of this Install Location.
1077 * @param location
1078 * The directory that contains the items.
1079 * @constructor
1080 */
DirectoryInstallLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1081 function DirectoryInstallLocation(name, location, restricted, priority, independent) {
1082 this._name = name;
1083 if (location.exists()) {
1084 if (!location.isDirectory())
1085 throw new Error("location must be a directoy!");
1086 }
1087 else {
1088 try {
1089 location.create(Ci.nsILocalFile.DIRECTORY_TYPE, 0775);
1090 }
1091 catch (e) {
1092 ERROR("DirectoryInstallLocation: failed to create location " +
1093 " directory = " + location.path + ", exception = " + e + "\n");
1094 }
1095 }
1096
1097 this._location = location;
1098 this._locationToIDMap = {};
1099 this._restricted = restricted;
1100 this._priority = priority;
1101 this._independent = independent;
1102 }
1103 DirectoryInstallLocation.prototype = {
1104 _name : "",
1105 _location : null,
1106 _locationToIDMap: null,
1107 _restricted : false,
1108 _priority : 0,
1109 _independent : false,
1110 _canAccess : null,
1111
1112 /**
1113 * See nsIExtensionManager.idl
1114 */
get_name
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1115 get name() {
1116 return this._name;
1117 },
1118
1119 /**
1120 * Reads a directory linked to in a file.
1121 * @param file
1122 * The file containing the directory path
1123 * @returns A nsILocalFile object representing the linked directory.
1124 */
_readDirectoryFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1125 _readDirectoryFromFile: function(file) {
1126 var fis = Cc["@mozilla.org/network/file-input-stream;1"].
1127 createInstance(Ci.nsIFileInputStream);
1128 fis.init(file, -1, -1, false);
1129 var line = { value: "" };
1130 if (fis instanceof Ci.nsILineInputStream)
1131 fis.readLine(line);
1132 fis.close();
1133 if (line.value) {
1134 var linkedDirectory = Cc["@mozilla.org/file/local;1"].
1135 createInstance(Ci.nsILocalFile);
1136 try {
1137 linkedDirectory.initWithPath(line.value);
1138 }
1139 catch (e) {
1140 linkedDirectory.setRelativeDescriptor(file.parent, line.value);
1141 }
1142
1143 return linkedDirectory;
1144 }
1145 return null;
1146 },
1147
1148 /**
1149 * See nsIExtensionManager.idl
1150 */
get_itemLocations
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1151 get itemLocations() {
1152 var locations = [];
1153 if (!this._location.exists())
1154 return new FileEnumerator(locations);
1155
1156 try {
1157 var entries = this._location.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
1158 while (true) {
1159 var entry = entries.nextFile;
1160 if (!entry)
1161 break;
1162 entry instanceof Ci.nsILocalFile;
1163 if (!entry.isDirectory() && gIDTest.test(entry.leafName)) {
1164 var linkedDirectory = this._readDirectoryFromFile(entry);
1165 if (linkedDirectory && linkedDirectory.exists() &&
1166 linkedDirectory.isDirectory()) {
1167 locations.push(linkedDirectory);
1168 this._locationToIDMap[linkedDirectory.persistentDescriptor] = entry.leafName;
1169 }
1170 }
1171 else
1172 locations.push(entry);
1173 }
1174 entries.close();
1175 }
1176 catch (e) {
1177 }
1178 return new FileEnumerator(locations);
1179 },
1180
1181 /**
1182 * Retrieves the GUID for an item at the specified location.
1183 * @param file
1184 * The location where an item might live.
1185 * @returns The ID for an item that might live at the location specified.
1186 *
1187 * N.B. This function makes no promises about whether or not this path is
1188 * actually maintained by this Install Location.
1189 */
getIDForLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1190 getIDForLocation: function(file) {
1191 var section = file.leafName;
1192 var filePD = file.persistentDescriptor;
1193 if (filePD in this._locationToIDMap)
1194 section = this._locationToIDMap[filePD];
1195
1196 if (gIDTest.test(section))
1197 return RegExp.$1;
1198 return undefined;
1199 },
1200
1201 /**
1202 * See nsIExtensionManager.idl
1203 */
get_location
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1204 get location() {
1205 return this._location.clone();
1206 },
1207
1208 /**
1209 * See nsIExtensionManager.idl
1210 */
get_restricted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1211 get restricted() {
1212 return this._restricted;
1213 },
1214
1215 /**
1216 * See nsIExtensionManager.idl
1217 */
get_canAccess
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1218 get canAccess() {
1219 if (this._canAccess != null)
1220 return this._canAccess;
1221
1222 var testFile = this.location;
1223 testFile.append("Access Privileges Test");
1224 try {
1225 testFile.createUnique(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
1226 testFile.remove(false);
1227 this._canAccess = true;
1228 }
1229 catch (e) {
1230 this._canAccess = false;
1231 }
1232 return this._canAccess;
1233 },
1234
1235 /**
1236 * See nsIExtensionManager.idl
1237 */
get_priority
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1238 get priority() {
1239 return this._priority;
1240 },
1241
1242 /**
1243 * See nsIExtensionManager.idl
1244 */
getItemLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1245 getItemLocation: function(id) {
1246 var itemLocation = this.location;
1247 itemLocation.append(id);
1248 if (itemLocation.exists() && !itemLocation.isDirectory())
1249 return this._readDirectoryFromFile(itemLocation);
1250 if (!itemLocation.exists() && this.canAccess)
1251 itemLocation.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
1252 return itemLocation;
1253 },
1254
1255 /**
1256 * See nsIExtensionManager.idl
1257 */
itemIsManagedIndependently
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1258 itemIsManagedIndependently: function(id) {
1259 if (this._independent)
1260 return true;
1261 var itemLocation = this.location;
1262 itemLocation.append(id);
1263 return itemLocation.exists() && !itemLocation.isDirectory();
1264 },
1265
1266 /**
1267 * See nsIExtensionManager.idl
1268 */
getItemFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1269 getItemFile: function(id, filePath) {
1270 var itemLocation = this.getItemLocation(id).clone();
1271 var parts = filePath.split("/");
1272 for (var i = 0; i < parts.length; ++i)
1273 itemLocation.append(parts[i]);
1274 return itemLocation;
1275 },
1276
1277 /**
1278 * Stages the specified file for later.
1279 * @param file
1280 * The file to stage
1281 * @param id
1282 * The GUID of the item the file represents
1283 */
stageFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1284 stageFile: function(file, id) {
1285 var stagedFile = this.location;
1286 stagedFile.append(DIR_STAGE);
1287 stagedFile.append(id);
1288 stagedFile.append(file.leafName);
1289
1290 // When an incompatible update is successful the file is already staged
1291 if (stagedFile.equals(file))
1292 return stagedFile;
1293
1294 if (stagedFile.exists())
1295 stagedFile.remove(false);
1296
1297 file.copyTo(stagedFile.parent, stagedFile.leafName);
1298
1299 // If the file has incorrect permissions set, correct them now.
1300 if (!stagedFile.isWritable())
1301 stagedFile.permissions = PERMS_FILE;
1302
1303 return stagedFile;
1304 },
1305
1306 /**
1307 * Returns the most recently staged package (e.g. the last XPI or JAR in a
1308 * directory) for an item and removes items that do not qualify.
1309 * @param id
1310 * The ID of the staged package
1311 * @returns an nsIFile if the package exists otherwise null.
1312 */
getStageFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1313 getStageFile: function(id) {
1314 var stageFile = null;
1315 var stageDir = this.location;
1316 stageDir.append(DIR_STAGE);
1317 stageDir.append(id);
1318 if (!stageDir.exists() || !stageDir.isDirectory())
1319 return null;
1320 try {
1321 var entries = stageDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
1322 while (entries.hasMoreElements()) {
1323 var file = entries.nextFile;
1324 if (!(file instanceof Ci.nsILocalFile))
1325 continue;
1326 if (file.isDirectory())
1327 removeDirRecursive(file);
1328 else if (fileIsItemPackage(file)) {
1329 if (stageFile)
1330 stageFile.remove(false);
1331 stageFile = file;
1332 }
1333 else
1334 file.remove(false);
1335 }
1336 }
1337 catch (e) {
1338 }
1339 if (entries instanceof Ci.nsIDirectoryEnumerator)
1340 entries.close();
1341 return stageFile;
1342 },
1343
1344 /**
1345 * Removes a file from the stage. This cleans up the stage if there is nothing
1346 * else left after the remove operation.
1347 * @param file
1348 * The file to remove.
1349 */
removeFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1350 removeFile: function(file) {
1351 if (file.exists())
1352 file.remove(false);
1353 var parent = file.parent;
1354 var entries = parent.directoryEntries;
1355 try {
1356 // XXXrstrong calling hasMoreElements on a nsIDirectoryEnumerator after
1357 // it has been removed will cause a crash on Mac OS X - bug 292823
1358 while (parent && !parent.equals(this.location) &&
1359 !entries.hasMoreElements()) {
1360 parent.remove(false);
1361 parent = parent.parent;
1362 entries = parent.directoryEntries;
1363 }
1364 if (entries instanceof Ci.nsIDirectoryEnumerator)
1365 entries.close();
1366 }
1367 catch (e) {
1368 ERROR("DirectoryInstallLocation::removeFile: failed to remove staged " +
1369 " directory = " + parent.path + ", exception = " + e + "\n");
1370 }
1371 },
1372
1373 QueryInterface: XPCOMUtils.generateQI([Ci.nsIInstallLocation])
1374 };
1375
1376 //@line 1496 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
1377
1378 /**
1379 * An object which handles the installation of an Extension.
1380 * @constructor
1381 */
Installer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1382 function Installer(ds, id, installLocation, type) {
1383 this._ds = ds;
1384 this._id = id;
1385 this._type = type;
1386 this._installLocation = installLocation;
1387 }
1388 Installer.prototype = {
1389 // Item metadata
1390 _id: null,
1391 _ds: null,
1392 _installLocation: null,
1393 _metadataDS: null,
1394
1395 /**
1396 * Gets the Install Manifest datasource we are installing from.
1397 */
get_metadataDS
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1398 get metadataDS() {
1399 if (!this._metadataDS) {
1400 var metadataFile = this._installLocation
1401 .getItemFile(this._id, FILE_INSTALL_MANIFEST);
1402 if (!metadataFile.exists())
1403 return null;
1404 this._metadataDS = getInstallManifest(metadataFile);
1405 if (!this._metadataDS) {
1406 LOG("Installer::install: metadata datasource for extension " +
1407 this._id + " at " + metadataFile.path + " could not be loaded. " +
1408 " Installation will not proceed.");
1409 }
1410 }
1411 return this._metadataDS;
1412 },
1413
1414 /**
1415 * Installs the Extension
1416 * @param file
1417 * A XPI/JAR file to install from. If this is null or does not exist,
1418 * the item is assumed to be an expanded directory, located at the GUID
1419 * key in the supplied Install Location.
1420 */
installFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1421 installFromFile: function(file) {
1422 // Move files from the staging dir into the extension's final home.
1423 if (file && file.exists()) {
1424 this._installExtensionFiles(file);
1425 }
1426
1427 if (!this.metadataDS)
1428 return;
1429
1430 // Upgrade old-style contents.rdf Chrome Manifests if necessary.
1431 if (this._type == Ci.nsIUpdateItem.TYPE_THEME)
1432 this.upgradeThemeChrome();
1433 else
1434 this.upgradeExtensionChrome();
1435
1436 // Add metadata for the extension to the global extension metadata set
1437 this._ds.addItemMetadata(this._id, this.metadataDS, this._installLocation);
1438 },
1439
1440 /**
1441 * Safely extract the Extension's files into the target folder.
1442 * @param file
1443 * The XPI/JAR file to install from.
1444 */
_installExtensionFiles
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1445 _installExtensionFiles: function(file) {
1446 /**
1447 * Callback for |safeInstallOperation| that performs file level installation
1448 * steps for an Extension.
1449 * @param extensionID
1450 * The GUID of the Extension being installed.
1451 * @param installLocation
1452 * The Install Location where the Extension is being installed.
1453 * @param xpiFile
1454 * The source XPI file that contains the Extension.
1455 */
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1456 function extractExtensionFiles(extensionID, installLocation, xpiFile) {
1457 // Create a logger to log install operations for uninstall. This must be
1458 // created in the |safeInstallOperation| callback, since it creates a file
1459 // in the target directory. If we do this outside of the callback, we may
1460 // be clobbering a file we should not be.
1461 var zipReader = getZipReaderForFile(xpiFile);
1462
1463 // create directories first
1464 var entries = zipReader.findEntries("*/");
1465 while (entries.hasMore()) {
1466 var entryName = entries.getNext();
1467 var target = installLocation.getItemFile(extensionID, entryName);
1468 if (!target.exists()) {
1469 try {
1470 target.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
1471 }
1472 catch (e) {
1473 ERROR("extractExtensionsFiles: failed to create target directory for extraction " +
1474 " file = " + target.path + ", exception = " + e + "\n");
1475 }
1476 }
1477 }
1478
1479 entries = zipReader.findEntries(null);
1480 while (entries.hasMore()) {
1481 var entryName = entries.getNext();
1482 target = installLocation.getItemFile(extensionID, entryName);
1483 if (target.exists())
1484 continue;
1485
1486 try {
1487 target.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
1488 }
1489 catch (e) {
1490 ERROR("extractExtensionsFiles: failed to create target file for extraction " +
1491 " file = " + target.path + ", exception = " + e + "\n");
1492 }
1493 zipReader.extract(entryName, target);
1494 }
1495 zipReader.close();
1496 }
1497
1498 /**
1499 * Callback for |safeInstallOperation| that performs file level installation
1500 * steps for a Theme.
1501 * @param id
1502 * The GUID of the Theme being installed.
1503 * @param installLocation
1504 * The Install Location where the Theme is being installed.
1505 * @param jarFile
1506 * The source JAR file that contains the Theme.
1507 */
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1508 function extractThemeFiles(id, installLocation, jarFile) {
1509 var themeDirectory = installLocation.getItemLocation(id);
1510 var zipReader = getZipReaderForFile(jarFile);
1511
1512 // The only critical file is the install.rdf and we would not have
1513 // gotten this far without one.
1514 var rootFiles = [FILE_INSTALL_MANIFEST, FILE_CHROME_MANIFEST,
1515 "preview.png", "icon.png"];
1516 for (var i = 0; i < rootFiles.length; ++i) {
1517 try {
1518 var target = installLocation.getItemFile(id, rootFiles[i]);
1519 zipReader.extract(rootFiles[i], target);
1520 }
1521 catch (e) {
1522 }
1523 }
1524
1525 var manifestFile = installLocation.getItemFile(id, FILE_CHROME_MANIFEST);
1526 // new theme structure requires a chrome.manifest file
1527 if (manifestFile.exists()) {
1528 var entries = zipReader.findEntries(DIR_CHROME + "/*");
1529 while (entries.hasMore()) {
1530 var entryName = entries.getNext();
1531 if (entryName.charAt(entryName.length - 1) == "/")
1532 continue;
1533 target = installLocation.getItemFile(id, entryName);
1534 try {
1535 target.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
1536 }
1537 catch (e) {
1538 ERROR("extractThemeFiles: failed to create target file for extraction " +
1539 " file = " + target.path + ", exception = " + e + "\n");
1540 }
1541 zipReader.extract(entryName, target);
1542 }
1543 zipReader.close();
1544 }
1545 else { // old theme structure requires only an install.rdf
1546 try {
1547 var contentsManifestFile = installLocation.getItemFile(id, FILE_CONTENTS_MANIFEST);
1548 contentsManifestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
1549 zipReader.extract(FILE_CONTENTS_MANIFEST, contentsManifestFile);
1550 }
1551 catch (e) {
1552 zipReader.close();
1553 ERROR("extractThemeFiles: failed to extract contents.rdf: " + target.path);
1554 throw e; // let the safe-op clean up
1555 }
1556 zipReader.close();
1557 var chromeDir = installLocation.getItemFile(id, DIR_CHROME);
1558 try {
1559 jarFile.copyTo(chromeDir, jarFile.leafName);
1560 }
1561 catch (e) {
1562 ERROR("extractThemeFiles: failed to copy theme JAR file to: " + chromeDir.path);
1563 throw e; // let the safe-op clean up
1564 }
1565
1566 if (!installer.metadataDS && installer._type == Ci.nsIUpdateItem.TYPE_THEME) {
1567 var themeName = extensionStrings.GetStringFromName("incompatibleThemeName");
1568 if (contentsManifestFile && contentsManifestFile.exists()) {
1569 var contentsManifest = gRDF.GetDataSourceBlocking(getURLSpecFromFile(contentsManifestFile));
1570 try {
1571 var ctr = getContainer(contentsManifest,
1572 gRDF.GetResource("urn:mozilla:skin:root"));
1573 var elts = ctr.GetElements();
1574 var nameArc = gRDF.GetResource(CHROME_NS("displayName"));
1575 while (elts.hasMoreElements()) {
1576 var elt = elts.getNext().QueryInterface(Ci.nsIRDFResource);
1577 themeName = stringData(contentsManifest.GetTarget(elt, nameArc, true));
1578 if (themeName)
1579 break;
1580 }
1581 }
1582 catch (e) {
1583 themeName = extensionStrings.GetStringFromName("incompatibleThemeName");
1584 }
1585 }
1586 showIncompatibleError({ name: themeName, version: "",
1587 type: Ci.nsIUpdateItem.TYPE_THEME });
1588 LOG("Theme JAR file: " + jarFile.leafName + " contains an Old-Style " +
1589 "Theme that is not compatible with this version of the software.");
1590 throw new Error("Old Theme"); // let the safe-op clean up
1591 }
1592 }
1593 }
1594
1595 var installer = this;
1596 var callback = extractExtensionFiles;
1597 if (this._type == Ci.nsIUpdateItem.TYPE_THEME)
1598 callback = extractThemeFiles;
1599 safeInstallOperation(this._id, this._installLocation,
1600 { callback: callback, data: file });
1601 },
1602
1603 /**
1604 * Upgrade contents.rdf Chrome Manifests used by this Theme to the new
1605 * chrome.manifest format if necessary.
1606 */
upgradeThemeChrome
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1607 upgradeThemeChrome: function() {
1608 // Use the Chrome Registry API to install the theme there
1609 var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
1610 getService(Ci.nsIToolkitChromeRegistry);
1611 var manifestFile = this._installLocation.getItemFile(this._id, FILE_CHROME_MANIFEST);
1612 if (manifestFile.exists() ||
1613 this._id == stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI))
1614 return;
1615
1616 try {
1617 // creates a chrome manifest for themes
1618 var manifestURI = getURIFromFile(manifestFile);
1619 var chromeDir = this._installLocation.getItemFile(this._id, DIR_CHROME);
1620 // We're relying on the fact that there is only one JAR file
1621 // in the "chrome" directory. This is a hack, but it works.
1622 var entries = chromeDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
1623 var jarFile = entries.nextFile;
1624 if (jarFile) {
1625 var jarFileURI = getURIFromFile(jarFile);
1626 var contentsURI = newURI("jar:" + jarFileURI.spec + "!/");
1627 var contentsFile = this._installLocation.getItemFile(this._id, FILE_CONTENTS_MANIFEST);
1628 var contentsFileURI = getURIFromFile(contentsFile.parent);
1629
1630 cr.processContentsManifest(contentsFileURI, manifestURI, contentsURI, false, true);
1631 }
1632 entries.close();
1633 contentsFile.remove(false);
1634 }
1635 catch (e) {
1636 // Failed to register chrome, for any number of reasons - non-existent
1637 // contents.rdf file at the location specified, malformed contents.rdf,
1638 // etc. Set the pending op to be OP_NEEDS_UNINSTALL so that the
1639 // extension is uninstalled properly during the subsequent uninstall
1640 // pass in |ExtensionManager::_finishOperations|
1641 ERROR("upgradeThemeChrome: failed for theme " + this._id + " - why " +
1642 "not convert to the new chrome.manifest format while you're at it? " +
1643 "Failure exception: " + e);
1644 showMessage("malformedRegistrationTitle", [], "malformedRegistrationMessage",
1645 [BundleManager.appName]);
1646
1647 var stageFile = this._installLocation.getStageFile(this._id);
1648 if (stageFile)
1649 this._installLocation.removeFile(stageFile);
1650
1651 StartupCache.put(this._installLocation, this._id, OP_NEEDS_UNINSTALL, true);
1652 StartupCache.write();
1653 }
1654 },
1655
1656 /**
1657 * Upgrade contents.rdf Chrome Manifests used by this Extension to the new
1658 * chrome.manifest format if necessary.
1659 */
upgradeExtensionChrome
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1660 upgradeExtensionChrome: function() {
1661 // If the extension is aware of the new flat chrome manifests and has
1662 // included one, just use it instead of generating one from the
1663 // install.rdf/contents.rdf data.
1664 var manifestFile = this._installLocation.getItemFile(this._id, FILE_CHROME_MANIFEST);
1665 if (manifestFile.exists())
1666 return;
1667
1668 try {
1669 // Enumerate the metadata datasource files collection and register chrome
1670 // for each file, calling _registerChrome for each.
1671 var chromeDir = this._installLocation.getItemFile(this._id, DIR_CHROME);
1672
1673 if (!manifestFile.parent.exists())
1674 return;
1675
1676 // Even if an extension doesn't have any chrome, we generate an empty
1677 // manifest file so that we don't try to upgrade from the "old-style"
1678 // chrome manifests at every startup.
1679 manifestFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
1680
1681 var manifestURI = getURIFromFile(manifestFile);
1682 var files = this.metadataDS.GetTargets(gInstallManifestRoot, EM_R("file"), true);
1683 while (files.hasMoreElements()) {
1684 var file = files.getNext().QueryInterface(Ci.nsIRDFResource);
1685 var chromeFile = chromeDir.clone();
1686 var fileName = file.Value.substr("urn:mozilla:extension:file:".length, file.Value.length);
1687 chromeFile.append(fileName);
1688
1689 var fileURLSpec = getURLSpecFromFile(chromeFile);
1690 if (!chromeFile.isDirectory()) {
1691 var zipReader = getZipReaderForFile(chromeFile);
1692 fileURLSpec = "jar:" + fileURLSpec + "!/";
1693 var contentsFile = this._installLocation.getItemFile(this._id, FILE_CONTENTS_MANIFEST);
1694 contentsFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
1695 }
1696
1697 var providers = [EM_R("package"), EM_R("skin"), EM_R("locale")];
1698 for (var i = 0; i < providers.length; ++i) {
1699 var items = this.metadataDS.GetTargets(file, providers[i], true);
1700 while (items.hasMoreElements()) {
1701 var item = items.getNext().QueryInterface(Ci.nsIRDFLiteral);
1702 var fileURI = newURI(fileURLSpec + item.Value);
1703 // Extract the contents.rdf files instead of opening them inside of
1704 // the jar. This prevents the jar from being cached by the zip
1705 // reader which will keep the jar in use and prevent deletion.
1706 if (zipReader) {
1707 zipReader.extract(item.Value + FILE_CONTENTS_MANIFEST, contentsFile);
1708 var contentsFileURI = getURIFromFile(contentsFile.parent);
1709 }
1710 else
1711 contentsFileURI = fileURI;
1712
1713 var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
1714 getService(Ci.nsIToolkitChromeRegistry);
1715 cr.processContentsManifest(contentsFileURI, manifestURI, fileURI, true, false);
1716 }
1717 }
1718 if (zipReader) {
1719 zipReader.close();
1720 zipReader = null;
1721 contentsFile.remove(false);
1722 }
1723 }
1724 }
1725 catch (e) {
1726 // Failed to register chrome, for any number of reasons - non-existent
1727 // contents.rdf file at the location specified, malformed contents.rdf,
1728 // etc. Set the pending op to be OP_NEEDS_UNINSTALL so that the
1729 // extension is uninstalled properly during the subsequent uninstall
1730 // pass in |ExtensionManager::_finishOperations|
1731 ERROR("upgradeExtensionChrome: failed for extension " + this._id + " - why " +
1732 "not convert to the new chrome.manifest format while you're at it? " +
1733 "Failure exception: " + e);
1734 showMessage("malformedRegistrationTitle", [], "malformedRegistrationMessage",
1735 [BundleManager.appName]);
1736
1737 var stageFile = this._installLocation.getStageFile(this._id);
1738 if (stageFile)
1739 this._installLocation.removeFile(stageFile);
1740
1741 StartupCache.put(this._installLocation, this._id, OP_NEEDS_UNINSTALL, true);
1742 StartupCache.write();
1743 }
1744 }
1745 };
1746
1747 /**
1748 * Safely attempt to perform a caller-defined install operation for a given
1749 * item ID. Using aggressive success-safety checks, this function will attempt
1750 * to move an existing location for an item aside and then allow installation
1751 * into the appropriate folder. If any operation fails the installation will
1752 * abort and roll back from the moved-aside old version.
1753 * @param itemID
1754 * The GUID of the item to perform the operation on.
1755 * @param installLocation
1756 * The Install Location where the item is installed.
1757 * @param installCallback
1758 * A caller supplied JS object with the following properties:
1759 * "data" A data parameter to be passed to the callback.
1760 * "callback" A function to perform the install operation. This
1761 * function is passed three parameters:
1762 * 1. The GUID of the item being operated on.
1763 * 2. The Install Location where the item is installed.
1764 * 3. The "data" parameter on the installCallback object.
1765 */
safeInstallOperation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1766 function safeInstallOperation(itemID, installLocation, installCallback) {
1767 var movedFiles = [];
1768
1769 /**
1770 * Reverts a deep move by moving backed up files back to their original
1771 * location.
1772 */
rollbackMove
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1773 function rollbackMove()
1774 {
1775 for (var i = 0; i < movedFiles.length; ++i) {
1776 var oldFile = movedFiles[i].oldFile;
1777 var newFile = movedFiles[i].newFile;
1778 try {
1779 newFile.moveTo(oldFile.parent, newFile.leafName);
1780 }
1781 catch (e) {
1782 ERROR("safeInstallOperation: failed to roll back files after an install " +
1783 "operation failed. Failed to roll back: " + newFile.path + " to: " +
1784 oldFile.path + " ... aborting installation.");
1785 throw e;
1786 }
1787 }
1788 }
1789
1790 /**
1791 * Moves a file to a new folder.
1792 * @param file
1793 * The file to move
1794 * @param destination
1795 * The target folder
1796 */
moveFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1797 function moveFile(file, destination) {
1798 try {
1799 var oldFile = file.clone();
1800 file.moveTo(destination, file.leafName);
1801 movedFiles.push({ oldFile: oldFile, newFile: file });
1802 }
1803 catch (e) {
1804 ERROR("safeInstallOperation: failed to back up file: " + file.path + " to: " +
1805 destination.path + " ... rolling back file moves and aborting " +
1806 "installation.");
1807 rollbackMove();
1808 throw e;
1809 }
1810 }
1811
1812 /**
1813 * Moves a directory to a new location. If any part of the move fails,
1814 * files already moved will be rolled back.
1815 * @param sourceDir
1816 * The directory to move
1817 * @param targetDir
1818 * The destination directory
1819 * @param currentDir
1820 * The current directory (a subdirectory of |sourceDir| or
1821 * |sourceDir| itself) we are moving files from.
1822 */
moveDirectory
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1823 function moveDirectory(sourceDir, targetDir, currentDir) {
1824 var entries = currentDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
1825 while (true) {
1826 var entry = entries.nextFile;
1827 if (!entry)
1828 break;
1829 if (entry.isDirectory())
1830 moveDirectory(sourceDir, targetDir, entry);
1831 else if (entry instanceof Ci.nsILocalFile) {
1832 var rd = entry.getRelativeDescriptor(sourceDir);
1833 var destination = targetDir.clone().QueryInterface(Ci.nsILocalFile);
1834 destination.setRelativeDescriptor(targetDir, rd);
1835 moveFile(entry, destination.parent);
1836 }
1837 }
1838 entries.close();
1839 }
1840
1841 /**
1842 * Removes the temporary backup directory where we stored files.
1843 * @param directory
1844 * The backup directory to remove
1845 */
cleanUpTrash
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1846 function cleanUpTrash(directory) {
1847 try {
1848 // Us-generated. Safe.
1849 if (directory && directory.exists())
1850 removeDirRecursive(directory);
1851 }
1852 catch (e) {
1853 ERROR("safeInstallOperation: failed to clean up the temporary backup of the " +
1854 "older version: " + itemLocationTrash.path);
1855 // This is a non-fatal error. Annoying, but non-fatal.
1856 }
1857 }
1858
1859 if (!installLocation.itemIsManagedIndependently(itemID)) {
1860 var itemLocation = installLocation.getItemLocation(itemID);
1861 if (itemLocation.exists()) {
1862 var trashDirName = itemID + "-trash";
1863 var itemLocationTrash = itemLocation.parent.clone();
1864 itemLocationTrash.append(trashDirName);
1865 if (itemLocationTrash.exists()) {
1866 // We can remove recursively here since this is a folder we created, not
1867 // one the user specified. If this fails, it'll throw, and the caller
1868 // should stop installation.
1869 try {
1870 removeDirRecursive(itemLocationTrash);
1871 }
1872 catch (e) {
1873 ERROR("safeFileOperation: failed to remove existing trash directory " +
1874 itemLocationTrash.path + " ... aborting installation.");
1875 throw e;
1876 }
1877 }
1878
1879 // Move the directory that contains the existing version of the item aside,
1880 // into {GUID}-trash. This will throw if there's a failure and the install
1881 // will abort.
1882 moveDirectory(itemLocation, itemLocationTrash, itemLocation);
1883
1884 // Clean up the original location, if necessary. Again, this is a path we
1885 // generated, so it is safe to recursively delete.
1886 try {
1887 removeDirRecursive(itemLocation);
1888 }
1889 catch (e) {
1890 ERROR("safeInstallOperation: failed to clean up item location after its contents " +
1891 "were properly backed up. Failed to clean up: " + itemLocation.path +
1892 " ... rolling back file moves and aborting installation.");
1893 rollbackMove();
1894 cleanUpTrash(itemLocationTrash);
1895 throw e;
1896 }
1897 }
1898 }
1899 else if (installLocation.name == KEY_APP_PROFILE ||
1900 installLocation.name == KEY_APP_GLOBAL ||
1901 installLocation.name == KEY_APP_SYSTEM_USER) {
1902 // Check for a pointer file and move it aside if it exists
1903 var pointerFile = installLocation.location.clone();
1904 pointerFile.append(itemID);
1905 if (pointerFile.exists() && !pointerFile.isDirectory()) {
1906 var trashFileName = itemID + "-trash";
1907 var itemLocationTrash = installLocation.location.clone();
1908 itemLocationTrash.append(trashFileName);
1909 if (itemLocationTrash.exists()) {
1910 // We can remove recursively here since this is a folder we created, not
1911 // one the user specified. If this fails, it'll throw, and the caller
1912 // should stop installation.
1913 try {
1914 removeDirRecursive(itemLocationTrash);
1915 }
1916 catch (e) {
1917 ERROR("safeFileOperation: failed to remove existing trash directory " +
1918 itemLocationTrash.path + " ... aborting installation.");
1919 throw e;
1920 }
1921 }
1922 itemLocationTrash.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
1923 // Move the pointer file to the trash.
1924 moveFile(pointerFile, itemLocationTrash);
1925 }
1926 }
1927
1928 // Now tell the client to do their stuff.
1929 try {
1930 installCallback.callback(itemID, installLocation, installCallback.data);
1931 }
1932 catch (e) {
1933 // This means the install operation failed. Remove everything and roll back.
1934 ERROR("safeInstallOperation: install operation (caller-supplied callback) failed, " +
1935 "rolling back file moves and aborting installation.");
1936 try {
1937 // Us-generated. Safe.
1938 removeDirRecursive(itemLocation);
1939 }
1940 catch (e) {
1941 ERROR("safeInstallOperation: failed to remove the folder we failed to install " +
1942 "an item into: " + itemLocation.path + " -- There is not much to suggest " +
1943 "here... maybe restart and try again?");
1944 cleanUpTrash(itemLocationTrash);
1945 throw e;
1946 }
1947 rollbackMove();
1948 cleanUpTrash(itemLocationTrash);
1949 throw e;
1950 }
1951
1952 // Now, and only now - after everything else has succeeded (against all odds!)
1953 // remove the {GUID}-trash directory where we stashed the old version of the
1954 // item.
1955 cleanUpTrash(itemLocationTrash);
1956 }
1957
1958 /**
1959 * Manages the list of pending operations.
1960 */
1961 var PendingOperations = {
1962 _ops: { },
1963
1964 /**
1965 * Adds an entry to the Pending Operations List
1966 * @param opType
1967 * The type of Operation to be performed
1968 * @param entry
1969 * A JS Object representing the item to be operated on:
1970 * "locationKey" The name of the Install Location where the item
1971 * is installed.
1972 * "id" The GUID of the item.
1973 */
addItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1974 addItem: function(opType, entry) {
1975 if (opType == OP_NONE)
1976 this.clearOpsForItem(entry.id);
1977 else {
1978 if (!(opType in this._ops))
1979 this._ops[opType] = { };
1980 this._ops[opType][entry.id] = entry.locationKey;
1981 }
1982 },
1983
1984 /**
1985 * Removes a Pending Operation from the list
1986 * @param opType
1987 * The type of Operation being removed
1988 * @param id
1989 * The GUID of the item to remove the entry for
1990 */
clearItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1991 clearItem: function(opType, id) {
1992 if (opType in this._ops && id in this._ops[opType])
1993 delete this._ops[opType][id];
1994 },
1995
1996 /**
1997 * Removes all Pending Operation for an item
1998 * @param id
1999 * The ID of the item to remove the entries for
2000 */
clearOpsForItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2001 clearOpsForItem: function(id) {
2002 for (var opType in this._ops) {
2003 if (id in this._ops[opType])
2004 delete this._ops[opType][id];
2005 }
2006 },
2007
2008 /**
2009 * Remove all Pending Operations of a certain type
2010 * @param opType
2011 * The type of Operation to remove all entries for
2012 */
clearItems
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2013 clearItems: function(opType) {
2014 if (opType in this._ops)
2015 delete this._ops[opType];
2016 },
2017
2018 /**
2019 * Get an array of operations of a certain type
2020 * @param opType
2021 * The type of Operation to return a list of
2022 */
getOperations
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2023 getOperations: function(opType) {
2024 if (!(opType in this._ops))
2025 return [];
2026 var ops = [];
2027 for (var id in this._ops[opType])
2028 ops.push( {id: id, locationKey: this._ops[opType][id] } );
2029 return ops;
2030 },
2031
2032 /**
2033 * The total number of Pending Operations, for all types.
2034 */
get_size
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2035 get size() {
2036 var size = 0;
2037 for (var opType in this._ops) {
2038 for (var id in this._ops[opType])
2039 ++size;
2040 }
2041 return size;
2042 }
2043 };
2044
2045 /**
2046 * Manages registered Install Locations
2047 */
2048 var InstallLocations = {
2049 _locations: { },
2050
2051 /**
2052 * A nsISimpleEnumerator of all available Install Locations.
2053 */
get_enumeration
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2054 get enumeration() {
2055 var installLocations = [];
2056 for (var key in this._locations)
2057 installLocations.push(InstallLocations.get(key));
2058 return new ArrayEnumerator(installLocations);
2059 },
2060
2061 /**
2062 * Gets a named Install Location
2063 * @param name
2064 * The name of the Install Location to get
2065 */
get
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2066 get: function(name) {
2067 return name in this._locations ? this._locations[name] : null;
2068 },
2069
2070 /**
2071 * Registers an Install Location
2072 * @param installLocation
2073 * The Install Location to register
2074 */
put
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2075 put: function(installLocation) {
2076 this._locations[installLocation.name] = installLocation;
2077 }
2078 };
2079
2080 /**
2081 * Manages the Startup Cache. The Startup Cache is a representation
2082 * of the contents of extensions.cache, a list of all
2083 * items the Extension System knows about, whether or not they
2084 * are active or visible.
2085 */
2086 var StartupCache = {
2087 /**
2088 * Location Name -> GUID hash of entries from the Startup Cache file
2089 * Each entry has the following properties:
2090 * "descriptor" The location on disk of the item
2091 * "mtime" The time the location was last modified
2092 * "op" Any pending operations on this item.
2093 * "location" The Install Location name where the item is installed.
2094 */
2095 entries: { },
2096
2097 /**
2098 * Puts an entry into the Startup Cache
2099 * @param installLocation
2100 * The Install Location where the item is installed
2101 * @param id
2102 * The GUID of the item
2103 * @param op
2104 * The name of the operation to be performed
2105 * @param shouldCreate
2106 * Whether or not we should create a new entry for this item
2107 * in the cache if one does not already exist.
2108 */
put
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2109 put: function(installLocation, id, op, shouldCreate) {
2110 var itemLocation = installLocation.getItemLocation(id);
2111
2112 var descriptor = null;
2113 var mtime = null;
2114 if (itemLocation) {
2115 itemLocation.QueryInterface(Ci.nsILocalFile);
2116 descriptor = getDescriptorFromFile(itemLocation, installLocation);
2117 if (itemLocation.exists() && itemLocation.isDirectory())
2118 mtime = Math.floor(itemLocation.lastModifiedTime / 1000);
2119 }
2120
2121 this._putRaw(installLocation.name, id, descriptor, mtime, op, shouldCreate);
2122 },
2123
2124 /**
2125 * Private helper function for putting an entry into the Startup Cache
2126 * without relying on the presence of its associated nsIInstallLocation
2127 * instance.
2128 *
2129 * @param key
2130 * The install location name.
2131 * @param id
2132 * The ID of the item.
2133 * @param descriptor
2134 * Value returned from absoluteDescriptor. May be null, in which
2135 * case the descriptor field is not updated.
2136 * @param mtime
2137 * The last modified time of the item. May be null, in which case the
2138 * descriptor field is not updated.
2139 * @param op
2140 * The OP code to store with the entry.
2141 * @param shouldCreate
2142 * Boolean value indicating whether to create or delete the entry.
2143 */
_putRaw
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2144 _putRaw: function(key, id, descriptor, mtime, op, shouldCreate) {
2145 if (!(key in this.entries))
2146 this.entries[key] = { };
2147 if (!(id in this.entries[key]))
2148 this.entries[key][id] = { };
2149 if (shouldCreate) {
2150 if (!this.entries[key][id])
2151 this.entries[key][id] = { };
2152
2153 var entry = this.entries[key][id];
2154
2155 if (descriptor)
2156 entry.descriptor = descriptor;
2157 if (mtime)
2158 entry.mtime = mtime;
2159 entry.op = op;
2160 entry.location = key;
2161 }
2162 else
2163 this.entries[key][id] = null;
2164 },
2165
2166 /**
2167 * Clears an entry from the Startup Cache
2168 * @param installLocation
2169 * The Install Location where item is installed
2170 * @param id
2171 * The GUID of the item.
2172 */
clearEntry
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2173 clearEntry: function(installLocation, id) {
2174 var key = installLocation.name;
2175 if (key in this.entries && id in this.entries[key])
2176 this.entries[key][id] = null;
2177 },
2178
2179 /**
2180 * Get all the startup cache entries for a particular ID.
2181 * @param id
2182 * The GUID of the item to locate.
2183 * @returns An array of Startup Cache entries for the specified ID.
2184 */
findEntries
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2185 findEntries: function(id) {
2186 var entries = [];
2187 for (var key in this.entries) {
2188 if (id in this.entries[key])
2189 entries.push(this.entries[key][id]);
2190 }
2191 return entries;
2192 },
2193
2194 /**
2195 * Read the Item-Change manifest file into a hash of properties.
2196 * The Item-Change manifest currently holds a list of paths, with the last
2197 * mtime for each path, and the GUID of the item at that path.
2198 */
read
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2199 read: function() {
2200 var itemChangeManifest = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_STARTUP_CACHE]);
2201 if (!itemChangeManifest.exists()) {
2202 // There is no change manifest for some reason, either we're in an initial
2203 // state or something went wrong with one of the other files and the
2204 // change manifest was removed. Return an empty dataset and rebuild.
2205 gFirstRun = true;
2206 return;
2207 }
2208 var fis = Cc["@mozilla.org/network/file-input-stream;1"].
2209 createInstance(Ci.nsIFileInputStream);
2210 fis.init(itemChangeManifest, -1, -1, false);
2211 if (fis instanceof Ci.nsILineInputStream) {
2212 var line = { value: "" };
2213 var more = false;
2214 do {
2215 more = fis.readLine(line);
2216 if (line.value) {
2217 // The Item-Change manifest is formatted like so:
2218 // (pd = descriptor)
2219 // location-key\tguid-of-item\tpd-to-extension1\tmtime-of-pd\tpending-op
2220 // location-key\tguid-of-item\tpd-to-extension2\tmtime-of-pd\tpending-op
2221 // ...
2222 // We hash on location-key first, because we don't want to have to
2223 // spin up the main extensions datasource on every start to determine
2224 // the Install Location for an item.
2225 // We hash on guid second, because we want a way to quickly determine
2226 // item GUID during a check loop that runs on every startup.
2227 var parts = line.value.split("\t");
2228 // Silently drop any entries in unknown install locations
2229 if (!InstallLocations.get(parts[0]))
2230 continue;
2231 var op = parts[4];
2232 this._putRaw(parts[0], parts[1], parts[2], parts[3], op, true);
2233 if (op)
2234 PendingOperations.addItem(op, { locationKey: parts[0], id: parts[1] });
2235 }
2236 }
2237 while (more);
2238 }
2239 fis.close();
2240 },
2241
2242 /**
2243 * Writes the Startup Cache to disk
2244 */
write
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2245 write: function() {
2246 var extensionsCacheFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_STARTUP_CACHE]);
2247 var fos = openSafeFileOutputStream(extensionsCacheFile);
2248 for (var locationKey in this.entries) {
2249 for (var id in this.entries[locationKey]) {
2250 var entry = this.entries[locationKey][id];
2251 if (entry) {
2252 try {
2253 var itemLocation = getFileFromDescriptor(entry.descriptor, InstallLocations.get(locationKey));
2254
2255 // Update our knowledge of this item's last-modified-time.
2256 // XXXdarin: this may cause us to miss changes in some cases.
2257 var itemMTime = 0;
2258 if (itemLocation.exists() && itemLocation.isDirectory())
2259 itemMTime = Math.floor(itemLocation.lastModifiedTime / 1000);
2260
2261 // Each line in the startup cache manifest is in this form:
2262 // location-key\tid-of-item\tpd-to-extension1\tmtime-of-pd\tpending-op
2263 var line = locationKey + "\t" + id + "\t" + entry.descriptor + "\t" +
2264 itemMTime + "\t" + entry.op + "\r\n";
2265 fos.write(line, line.length);
2266 }
2267 catch (e) {}
2268 }
2269 }
2270 }
2271 closeSafeFileOutputStream(fos);
2272 }
2273 };
2274
2275 /**
2276 * Installs, manages and tracks compatibility for Extensions and Themes
2277 * @constructor
2278 */
ExtensionManager
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2279 function ExtensionManager() {
2280 gApp = Cc["@mozilla.org/xre/app-info;1"].
2281 getService(Ci.nsIXULAppInfo).QueryInterface(Ci.nsIXULRuntime);
2282 gOSTarget = gApp.OS;
2283 try {
2284 gXPCOMABI = gApp.XPCOMABI;
2285 } catch (ex) {
2286 // Provide a default for gXPCOMABI. It won't be compared to an
2287 // item's metadata (i.e. install.rdf can't specify e.g. WINNT_unknownABI
2288 // as targetPlatform), but it will be displayed in error messages and
2289 // transmitted to update URLs.
2290 gXPCOMABI = UNKNOWN_XPCOM_ABI;
2291 }
2292 gPref = Cc["@mozilla.org/preferences-service;1"].
2293 getService(Ci.nsIPrefBranch2);
2294
2295 gOS = Cc["@mozilla.org/observer-service;1"].
2296 getService(Ci.nsIObserverService);
2297 gOS.addObserver(this, "xpcom-shutdown", false);
2298
2299 gConsole = Cc["@mozilla.org/consoleservice;1"].
2300 getService(Ci.nsIConsoleService);
2301
2302 gRDF = Cc["@mozilla.org/rdf/rdf-service;1"].
2303 getService(Ci.nsIRDFService);
2304 gInstallManifestRoot = gRDF.GetResource(RDFURI_INSTALL_MANIFEST_ROOT);
2305
2306 // Register Global Install Location
2307 var appGlobalExtensions = getDirNoCreate(KEY_APPDIR, [DIR_EXTENSIONS]);
2308 var priority = Ci.nsIInstallLocation.PRIORITY_APP_SYSTEM_GLOBAL;
2309 var globalLocation = new DirectoryInstallLocation(KEY_APP_GLOBAL,
2310 appGlobalExtensions, true,
2311 priority, false);
2312 InstallLocations.put(globalLocation);
2313
2314 // Register App-Profile Install Location
2315 var appProfileExtensions = getDirNoCreate(KEY_PROFILEDS, [DIR_EXTENSIONS]);
2316 var priority = Ci.nsIInstallLocation.PRIORITY_APP_PROFILE;
2317 var profileLocation = new DirectoryInstallLocation(KEY_APP_PROFILE,
2318 appProfileExtensions, false,
2319 priority, false);
2320 InstallLocations.put(profileLocation);
2321
2322 // Register per-user Install Location
2323 try {
2324 var appSystemUExtensions = getDirNoCreate("XREUSysExt", [gApp.ID]);
2325 }
2326 catch(e) { }
2327
2328 if (appSystemUExtensions) {
2329 var priority = Ci.nsIInstallLocation.PRIORITY_APP_SYSTEM_USER;
2330 var systemLocation = new DirectoryInstallLocation(KEY_APP_SYSTEM_USER,
2331 appSystemUExtensions, false,
2332 priority, true);
2333
2334 InstallLocations.put(systemLocation);
2335 }
2336
2337 // Register App-System-Shared Install Location
2338 try {
2339 var appSystemSExtensions = getDirNoCreate("XRESysSExtPD", [gApp.ID]);
2340 }
2341 catch (e) { }
2342
2343 if (appSystemSExtensions) {
2344 var priority = Ci.nsIInstallLocation.PRIORITY_APP_SYSTEM_GLOBAL + 10;
2345 var systemLocation = new DirectoryInstallLocation(KEY_APP_SYSTEM_SHARE,
2346 appSystemSExtensions, true,
2347 priority, true);
2348 InstallLocations.put(systemLocation);
2349 }
2350
2351 // Register App-System-Local Install Location
2352 try {
2353 var appSystemLExtensions = getDirNoCreate("XRESysLExtPD", [gApp.ID]);
2354 }
2355 catch (e) { }
2356
2357 if (appSystemLExtensions) {
2358 var priority = Ci.nsIInstallLocation.PRIORITY_APP_SYSTEM_GLOBAL + 20;
2359 var systemLocation = new DirectoryInstallLocation(KEY_APP_SYSTEM_LOCAL,
2360 appSystemLExtensions, true,
2361 priority, true);
2362 InstallLocations.put(systemLocation);
2363 }
2364
2365 //@line 2499 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
2366
2367 // Register Additional Install Locations
2368 var categoryManager = Cc["@mozilla.org/categorymanager;1"].
2369 getService(Ci.nsICategoryManager);
2370 var locations = categoryManager.enumerateCategory(CATEGORY_INSTALL_LOCATIONS);
2371 while (locations.hasMoreElements()) {
2372 var entry = locations.getNext().QueryInterface(Ci.nsISupportsCString).data;
2373 var contractID = categoryManager.getCategoryEntry(CATEGORY_INSTALL_LOCATIONS, entry);
2374 var location = Cc[contractID].getService(Ci.nsIInstallLocation);
2375 InstallLocations.put(location);
2376 }
2377 }
2378
2379 ExtensionManager.prototype = {
2380 /**
2381 * See nsIObserver.idl
2382 */
observe
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2383 observe: function(subject, topic, data) {
2384 switch (topic) {
2385 case "app-startup":
2386 gOS.addObserver(this, "profile-after-change", false);
2387 gOS.addObserver(this, "quit-application", false);
2388 break;
2389 case "profile-after-change":
2390 this._profileSelected();
2391 break;
2392 case "quit-application-requested":
2393 this._confirmCancelDownloadsOnQuit(subject);
2394 break;
2395 case "offline-requested":
2396 this._confirmCancelDownloadsOnOffline(subject);
2397 break;
2398 case "quit-application":
2399 gOS.removeObserver(this, "profile-after-change");
2400 gOS.removeObserver(this, "quit-application");
2401 break;
2402 case "xpcom-shutdown":
2403 this._shutdown();
2404 break;
2405 case "nsPref:changed":
2406 if (data == PREF_EM_LOGGING_ENABLED)
2407 this._loggingToggled();
2408 else if (data == PREF_EM_CHECK_COMPATIBILITY ||
2409 data == PREF_EM_CHECK_UPDATE_SECURITY)
2410 this._updateAppDisabledState();
2411 else if ((data == PREF_MATCH_OS_LOCALE) || (data == PREF_SELECTED_LOCALE))
2412 this._updateLocale();
2413 break;
2414 }
2415 },
2416
2417 /**
2418 * Refresh the logging enabled global from preferences when the user changes
2419 * the preference settting.
2420 */
_loggingToggled
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2421 _loggingToggled: function() {
2422 gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
2423 },
2424
2425 /**
2426 * Retrieves the current locale
2427 */
_updateLocale
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2428 _updateLocale: function() {
2429 try {
2430 if (gPref.getBoolPref(PREF_MATCH_OS_LOCALE)) {
2431 var localeSvc = Cc["@mozilla.org/intl/nslocaleservice;1"].
2432 getService(Ci.nsILocaleService);
2433 gLocale = localeSvc.getLocaleComponentForUserAgent();
2434 return;
2435 }
2436 }
2437 catch (ex) {
2438 }
2439 gLocale = gPref.getCharPref(PREF_SELECTED_LOCALE);
2440 },
2441
2442 /**
2443 * When a preference is toggled that affects whether an item is usable or not
2444 * we must app-enable or app-disable the item based on the new settings.
2445 */
_updateAppDisabledState
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2446 _updateAppDisabledState: function() {
2447 gCheckCompatibility = getPref("getBoolPref", PREF_EM_CHECK_COMPATIBILITY, true);
2448 gCheckUpdateSecurity = getPref("getBoolPref", PREF_EM_CHECK_UPDATE_SECURITY, true);
2449 var ds = this.datasource;
2450
2451 // Enumerate all items
2452 var ctr = getContainer(ds, ds._itemRoot);
2453 var elements = ctr.GetElements();
2454 while (elements.hasMoreElements()) {
2455 var itemResource = elements.getNext().QueryInterface(Ci.nsIRDFResource);
2456
2457 // App disable or enable items as necessary
2458 // _appEnableItem and _appDisableItem will do nothing if the item is already
2459 // in the right state.
2460 id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
2461 if (this._isUsableItem(id))
2462 this._appEnableItem(id);
2463 else
2464 this._appDisableItem(id);
2465 }
2466 },
2467
2468 /**
2469 * Initialize the system after a profile has been selected.
2470 */
_profileSelected
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2471 _profileSelected: function() {
2472 // Tell the Chrome Registry which Skin to select
2473 try {
2474 if (gPref.getBoolPref(PREF_DSS_SWITCHPENDING)) {
2475 var toSelect = gPref.getCharPref(PREF_DSS_SKIN_TO_SELECT);
2476 gPref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, toSelect);
2477 gPref.clearUserPref(PREF_DSS_SWITCHPENDING);
2478 gPref.clearUserPref(PREF_DSS_SKIN_TO_SELECT);
2479 }
2480 }
2481 catch (e) {
2482 }
2483
2484 if ("nsICrashReporter" in Ci && gApp instanceof Ci.nsICrashReporter) {
2485 // Annotate the crash report with the list of add-ons
2486 try {
2487 try {
2488 gApp.annotateCrashReport("Add-ons", gPref.getCharPref(PREF_EM_ENABLED_ITEMS));
2489 } catch (e) { }
2490 gApp.annotateCrashReport("Theme", gPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN));
2491 }
2492 catch (ex) {
2493 // This will fail in unnofficial builds, ignorable error
2494 }
2495 }
2496
2497 gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
2498 gCheckCompatibility = getPref("getBoolPref", PREF_EM_CHECK_COMPATIBILITY, true);
2499 gCheckUpdateSecurity = getPref("getBoolPref", PREF_EM_CHECK_UPDATE_SECURITY, true);
2500 gPref.addObserver("extensions.", this, false);
2501 gPref.addObserver(PREF_MATCH_OS_LOCALE, this, false);
2502 gPref.addObserver(PREF_SELECTED_LOCALE, this, false);
2503 this._updateLocale();
2504 },
2505
2506 /**
2507 * Notify user that there are new addons updates
2508 */
_showUpdatesWindow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2509 _showUpdatesWindow: function() {
2510 if (!getPref("getBoolPref", PREF_UPDATE_NOTIFYUSER, false))
2511 return;
2512
2513 const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
2514 const EMFEATURES = "chrome,centerscreen,extra-chrome,dialog,resizable,modal";
2515
2516 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
2517 getService(Ci.nsIWindowWatcher);
2518 var param = Cc["@mozilla.org/supports-array;1"].
2519 createInstance(Ci.nsISupportsArray);
2520 var arg = Cc["@mozilla.org/supports-string;1"].
2521 createInstance(Ci.nsISupportsString);
2522 arg.data = "updates-only";
2523 param.AppendElement(arg);
2524 ww.openWindow(null, EMURL, null, EMFEATURES, param);
2525 },
2526
2527 /**
2528 * Clean up on application shutdown to avoid leaks.
2529 */
_shutdown
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2530 _shutdown: function() {
2531 gOS.removeObserver(this, "xpcom-shutdown");
2532
2533 // Release strongly held services.
2534 gOS = null;
2535 if (this._ptr && gRDF) {
2536 gRDF.UnregisterDataSource(this._ptr);
2537 this._ptr = null;
2538 }
2539 gRDF = null;
2540 if (gPref) {
2541 gPref.removeObserver("extensions.", this);
2542 gPref.removeObserver(PREF_MATCH_OS_LOCALE, this);
2543 gPref.removeObserver(PREF_SELECTED_LOCALE, this);
2544 }
2545 gPref = null;
2546 gConsole = null;
2547 gVersionChecker = null;
2548 gInstallManifestRoot = null;
2549 gApp = null;
2550 },
2551
2552 /**
2553 * Check for presence of critical Extension system files. If any is missing,
2554 * delete the others and signal that the system needs to rebuild them all
2555 * from scratch.
2556 * @returns true if any critical file is missing and the system needs to
2557 * be rebuilt, false otherwise.
2558 */
_ensureDatasetIntegrity
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2559 _ensureDatasetIntegrity: function () {
2560 var extensionsDS = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
2561 var extensionsINI = getFile(KEY_PROFILEDIR, [FILE_EXTENSION_MANIFEST]);
2562 var extensionsCache = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS_STARTUP_CACHE]);
2563
2564 var dsExists = extensionsDS.exists();
2565 var iniExists = extensionsINI.exists();
2566 var cacheExists = extensionsCache.exists();
2567
2568 if (dsExists && iniExists && cacheExists)
2569 return false;
2570
2571 // If any of the files are missing, remove the .ini file
2572 if (iniExists)
2573 extensionsINI.remove(false);
2574
2575 // If the extensions datasource is missing remove the .cache file if it exists
2576 if (!dsExists && cacheExists)
2577 extensionsCache.remove(false);
2578
2579 return true;
2580 },
2581
2582 /**
2583 * See nsIExtensionManager.idl
2584 */
start
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2585 start: function(commandLine) {
2586 var isDirty = false;
2587 var forceAutoReg = false;
2588
2589 // Somehow the component list went away, and for that reason the new one
2590 // generated by this function is going to result in a different compreg.
2591 // We must force a restart.
2592 var componentList = getFile(KEY_PROFILEDIR, [FILE_EXTENSION_MANIFEST]);
2593 if (!componentList.exists())
2594 forceAutoReg = true;
2595
2596 // Check for missing manifests - e.g. missing extensions.ini, missing
2597 // extensions.cache, extensions.rdf etc. If any of these files
2598 // is missing then we are in some kind of weird or initial state and need
2599 // to force a regeneration.
2600 if (this._ensureDatasetIntegrity())
2601 isDirty = true;
2602
2603 // Configure any items that are being installed, uninstalled or upgraded
2604 // by being added, removed or modified by another process. We must do this
2605 // on every startup since there is no way we can tell if this has happened
2606 // or not!
2607 if (this._checkForFileChanges())
2608 isDirty = true;
2609
2610 this._showUpdatesWindow();
2611
2612 if (PendingOperations.size != 0)
2613 isDirty = true;
2614
2615 // Extension Changes
2616 if (isDirty) {
2617 var needsRestart = this._finishOperations();
2618
2619 if (forceAutoReg) {
2620 this._extensionListChanged = true;
2621 needsRestart = true;
2622 }
2623 return needsRestart;
2624 }
2625
2626 this._startTimers();
2627
2628 return false;
2629 },
2630
2631 /**
2632 * Begins all background update check timers
2633 */
_startTimers
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2634 _startTimers: function() {
2635 // Register a background update check timer
2636 var tm = Cc["@mozilla.org/updates/timer-manager;1"].
2637 getService(Ci.nsIUpdateTimerManager);
2638 var interval = getPref("getIntPref", PREF_EM_UPDATE_INTERVAL, 86400);
2639 tm.registerTimer("addon-background-update-timer", this, interval);
2640 },
2641
2642 /**
2643 * Notified when a timer fires
2644 * @param timer
2645 * The timer that fired
2646 */
notify
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2647 notify: function(timer) {
2648 if (!getPref("getBoolPref", PREF_EM_UPDATE_ENABLED, true))
2649 return;
2650
2651 var items = this.getItemList(Ci.nsIUpdateItem.TYPE_ANY, { });
2652
2653 var updater = new ExtensionItemUpdater(this);
2654 updater.checkForUpdates(items, items.length,
2655 Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION,
2656 new BackgroundUpdateCheckListener(this.datasource));
2657 },
2658
2659 /**
2660 * See nsIExtensionManager.idl
2661 */
handleCommandLineArgs
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2662 handleCommandLineArgs: function(commandLine) {
2663 try {
2664 var globalExtension = commandLine.handleFlagWithParam("install-global-extension", false);
2665 if (globalExtension) {
2666 var file = commandLine.resolveFile(globalExtension);
2667 this._installGlobalItem(file);
2668 }
2669 var globalTheme = commandLine.handleFlagWithParam("install-global-theme", false);
2670 if (globalTheme) {
2671 file = commandLine.resolveFile(globalTheme);
2672 this._installGlobalItem(file);
2673 }
2674 }
2675 catch (e) {
2676 ERROR("ExtensionManager:handleCommandLineArgs - failure, catching exception - lineno: " +
2677 e.lineNumber + " - file: " + e.fileName + " - " + e);
2678 }
2679 commandLine.preventDefault = true;
2680 },
2681
2682 /**
2683 * Installs an XPI/JAR file into the KEY_APP_GLOBAL install location.
2684 * @param file
2685 * The XPI/JAR file to extract
2686 */
_installGlobalItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2687 _installGlobalItem: function(file) {
2688 if (!file || !file.exists())
2689 throw new Error("Unable to find the file specified on the command line!");
2690 //@line 2829 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
2691 var installManifestFile = extractRDFFileToTempDir(file, FILE_INSTALL_MANIFEST, true);
2692 if (!installManifestFile.exists())
2693 throw new Error("The package is missing an install manifest!");
2694 var installManifest = getInstallManifest(installManifestFile);
2695 installManifestFile.remove(false);
2696 var installData = this._getInstallData(installManifest);
2697 var installer = new Installer(installManifest, installData.id,
2698 InstallLocations.get(KEY_APP_GLOBAL),
2699 installData.type);
2700 installer._installExtensionFiles(file);
2701 if (installData.type == Ci.nsIUpdateItem.TYPE_THEME)
2702 installer.upgradeThemeChrome();
2703 else
2704 installer.upgradeExtensionChrome();
2705 },
2706
2707 /**
2708 * Check to see if a file is a XPI/JAR file that the user dropped into this
2709 * Install Location. (i.e. a XPI that is not a staged XPI from an install
2710 * transaction that is currently in operation).
2711 * @param file
2712 * The XPI/JAR file to configure
2713 * @param location
2714 * The Install Location where this file was found.
2715 * @returns A nsIUpdateItem representing the dropped XPI if this file was a
2716 * XPI/JAR that needs installation, null otherwise.
2717 */
_getItemForDroppedFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2718 _getItemForDroppedFile: function(file, location) {
2719 if (fileIsItemPackage(file)) {
2720 // We know nothing about this item, it is not something we've
2721 // staged in preparation for finalization, so assume it's something
2722 // the user dropped in.
2723 LOG("A Item Package appeared at: " + file.path + " that we know " +
2724 "nothing about, assuming it was dropped in by the user and " +
2725 "configuring for installation now. Location Key: " + location.name);
2726
2727 var installManifestFile = extractRDFFileToTempDir(file, FILE_INSTALL_MANIFEST, true);
2728 if (!installManifestFile.exists())
2729 return null;
2730 var installManifest = getInstallManifest(installManifestFile);
2731 installManifestFile.remove(false);
2732 var ds = this.datasource;
2733 var installData = this._getInstallData(installManifest);
2734 var targetAppInfo = ds.getTargetApplicationInfo(installData.id, installManifest);
2735 return makeItem(installData.id,
2736 installData.version,
2737 location.name,
2738 targetAppInfo ? targetAppInfo.minVersion : "",
2739 targetAppInfo ? targetAppInfo.maxVersion : "",
2740 getManifestProperty(installManifest, "name"),
2741 "", /* XPI Update URL */
2742 "", /* XPI Update Hash */
2743 getManifestProperty(installManifest, "iconURL"),
2744 getManifestProperty(installManifest, "updateURL"),
2745 getManifestProperty(installManifest, "updateKey"),
2746 installData.type,
2747 targetAppInfo ? targetAppInfo.appID : gApp.ID);
2748 }
2749 return null;
2750 },
2751
2752 /**
2753 * Configure an item that was installed or upgraded by another process
2754 * so that |_finishOperations| can properly complete processing and
2755 * registration.
2756 * As this is the only point at which we can reliably know the Install
2757 * Location of this item, we use this as an opportunity to:
2758 * 1. Check that this item is compatible with this Firefox version.
2759 * 2. If it is, configure the item by using the supplied callback.
2760 * We do not do any special handling in the case that the item is
2761 * not compatible with this version other than to simply not register
2762 * it and log that fact - there is no "phone home" check for updates.
2763 * It may or may not make sense to do this, but for now we'll just
2764 * not register.
2765 * @param id
2766 * The GUID of the item to validate and configure.
2767 * @param location
2768 * The Install Location where this item is installed.
2769 * @param callback
2770 * The callback that configures the item for installation upon
2771 * successful validation.
2772 */
installItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2773 installItem: function(id, location, callback) {
2774 // As this is the only pint at which we reliably know the installation
2775 var installRDF = location.getItemFile(id, FILE_INSTALL_MANIFEST);
2776 if (installRDF.exists()) {
2777 LOG("Item Installed/Upgraded at Install Location: " + location.name +
2778 " Item ID: " + id + ", attempting to register...");
2779 var installManifest = getInstallManifest(installRDF);
2780 var installData = this._getInstallData(installManifest);
2781 if (installData.error == INSTALLERROR_SUCCESS) {
2782 LOG("... success, item is compatible");
2783 callback(installManifest, installData.id, location, installData.type);
2784 }
2785 else if (installData.error == INSTALLERROR_INCOMPATIBLE_VERSION) {
2786 LOG("... success, item installed but is not compatible");
2787 callback(installManifest, installData.id, location, installData.type);
2788 this._appDisableItem(id);
2789 }
2790 else if (installData.error == INSTALLERROR_INSECURE_UPDATE) {
2791 LOG("... success, item installed but does not provide updates securely");
2792 callback(installManifest, installData.id, location, installData.type);
2793 this._appDisableItem(id);
2794 }
2795 else if (installData.error == INSTALLERROR_BLOCKLISTED) {
2796 LOG("... success, item installed but is blocklisted");
2797 callback(installManifest, installData.id, location, installData.type);
2798 this._appDisableItem(id);
2799 }
2800 else {
2801 /**
2802 * Turns an error code into a message for logging
2803 * @param error
2804 * an Install Error code
2805 * @returns A string message to be logged.
2806 */
translateErrorMessage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2807 function translateErrorMessage(error) {
2808 switch (error) {
2809 case INSTALLERROR_INVALID_GUID:
2810 return "Invalid GUID";
2811 case INSTALLERROR_INVALID_VERSION:
2812 return "Invalid Version";
2813 case INSTALLERROR_INCOMPATIBLE_PLATFORM:
2814 return "Incompatible Platform";
2815 }
2816 }
2817 LOG("... failure, item is not compatible, error: " +
2818 translateErrorMessage(installData.error));
2819
2820 // Add the item to the Startup Cache anyway, so we don't re-detect it
2821 // every time the app starts.
2822 StartupCache.put(location, id, OP_NONE, true);
2823 }
2824 }
2825 },
2826
2827 /**
2828 * Check for changes to items that were made independently of the Extension
2829 * Manager, e.g. items were added or removed from a Install Location or items
2830 * in an Install Location changed.
2831 */
_checkForFileChanges
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2832 _checkForFileChanges: function() {
2833 var em = this;
2834
2835 /**
2836 * Determines if an item can be used based on whether or not the install
2837 * location of the "item" has an equal or higher priority than the install
2838 * location where another version may live.
2839 * @param id
2840 * The GUID of the item being installed.
2841 * @param location
2842 * The location where an item is to be installed.
2843 * @returns true if the item can be installed at that location, false
2844 * otherwise.
2845 */
canUse
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2846 function canUse(id, location) {
2847 for (var locationKey in StartupCache.entries) {
2848 if (locationKey != location.name &&
2849 id in StartupCache.entries[locationKey]) {
2850 if (StartupCache.entries[locationKey][id]) {
2851 var oldInstallLocation = InstallLocations.get(locationKey);
2852 if (oldInstallLocation.priority <= location.priority)
2853 return false;
2854 }
2855 }
2856 }
2857 return true;
2858 }
2859
2860 /**
2861 * Gets a Dialog Param Block loaded with a set of strings to initialize the
2862 * XPInstall Confirmation Dialog.
2863 * @param strings
2864 * An array of strings
2865 * @returns A nsIDialogParamBlock loaded with the strings and dialog state.
2866 */
getParamBlock
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2867 function getParamBlock(strings) {
2868 var dpb = Cc["@mozilla.org/embedcomp/dialogparam;1"].
2869 createInstance(Ci.nsIDialogParamBlock);
2870 // OK and Cancel Buttons
2871 dpb.SetInt(0, 2);
2872 // Number of Strings
2873 dpb.SetInt(1, strings.length);
2874 dpb.SetNumberStrings(strings.length);
2875 // Add Strings
2876 for (var i = 0; i < strings.length; ++i)
2877 dpb.SetString(i, strings[i]);
2878
2879 var supportsString = Cc["@mozilla.org/supports-string;1"].
2880 createInstance(Ci.nsISupportsString);
2881 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
2882 supportsString.data = bundle.GetStringFromName("droppedInWarning");
2883 var objs = Cc["@mozilla.org/array;1"].
2884 createInstance(Ci.nsIMutableArray);
2885 objs.appendElement(supportsString, false);
2886 dpb.objects = objs;
2887 return dpb;
2888 }
2889
2890 /**
2891 * Installs a set of files which were dropped into an install location by
2892 * the user, only after user confirmation.
2893 * @param droppedInFiles
2894 * An array of JS objects with the following properties:
2895 * "file" The nsILocalFile where the XPI lives
2896 * "location" The Install Location where the XPI was found.
2897 * @param xpinstallStrings
2898 * An array of strings used to initialize the XPInstall Confirm
2899 * dialog.
2900 */
installDroppedInFiles
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
2901 function installDroppedInFiles(droppedInFiles, xpinstallStrings) {
2902 if (droppedInFiles.length == 0)
2903 return;
2904
2905 var dpb = getParamBlock(xpinstallStrings);
2906 var ifptr = Cc["@mozilla.org/supports-interface-pointer;1"].
2907 createInstance(Ci.nsISupportsInterfacePointer);
2908 ifptr.data = dpb;
2909 ifptr.dataIID = Ci.nsIDialogParamBlock;
2910 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
2911 getService(Ci.nsIWindowWatcher);
2912 ww.openWindow(null, URI_XPINSTALL_CONFIRM_DIALOG,
2913 "", "chrome,centerscreen,modal,dialog,titlebar", ifptr);
2914 if (!dpb.GetInt(0)) {
2915 // User said OK - install items
2916 for (var i = 0; i < droppedInFiles.length; ++i) {
2917 em.installItemFromFile(droppedInFiles[i].file,
2918 droppedInFiles[i].location.name);
2919 // We are responsible for cleaning up this file
2920 droppedInFiles[i].file.remove(false);
2921 }
2922 }
2923 else {
2924 for (i = 0; i < droppedInFiles.length; ++i) {
2925 // We are responsible for cleaning up this file
2926 droppedInFiles[i].file.remove(false);
2927 }
2928 }
2929 }
2930
2931 var isDirty = false;
2932 var ignoreMTimeChanges = getPref("getBoolPref", PREF_EM_IGNOREMTIMECHANGES,
2933 false);
2934 StartupCache.read();
2935
2936 // Array of objects with 'location' and 'id' properties to maybe install.
2937 var newItems = [];
2938
2939 var droppedInFiles = [];
2940 var xpinstallStrings = [];
2941
2942 // Enumerate over the install locations from low to high priority. The
2943 // enumeration returned is pre-sorted.
2944 var installLocations = this.installLocations;
2945 while (installLocations.hasMoreElements()) {
2946 var location = installLocations.getNext().QueryInterface(Ci.nsIInstallLocation);
2947
2948 // Hash the set of items actually held by the Install Location.
2949 var actualItems = { };
2950 var entries = location.itemLocations;
2951 while (true) {
2952 var entry = entries.nextFile;
2953 if (!entry)
2954 break;
2955
2956 // Is this location a valid item? It must be a directory, and contain
2957 // an install.rdf manifest:
2958 if (entry.isDirectory()) {
2959 var installRDF = entry.clone();
2960 installRDF.append(FILE_INSTALL_MANIFEST);
2961
2962 var id = location.getIDForLocation(entry);
2963 if (!id || (!installRDF.exists() &&
2964 !location.itemIsManagedIndependently(id)))
2965 continue;
2966
2967 actualItems[id] = entry;
2968 }
2969 else {
2970 // Check to see if this file is a XPI/JAR dropped into this dir
2971 // by the user, installing it if necessary. We do this here rather
2972 // than separately in |_finishOperations| because I don't want to
2973 // walk these lists multiple times on every startup.
2974 var item = this._getItemForDroppedFile(entry, location);
2975 if (item) {
2976 droppedInFiles.push({ file: entry, location: location });
2977 var prettyName = "";
2978 try {
2979 var zipReader = getZipReaderForFile(entry);
2980 var principal = { };
2981 var certPrincipal = zipReader.getCertificatePrincipal(null, principal);
2982 // XXXbz This string could be empty. This needs better
2983 // UI to present principal.value.certificate's subject.
2984 prettyName = principal.value.prettyName;
2985 }
2986 catch (e) { }
2987 if (zipReader)
2988 zipReader.close();
2989 xpinstallStrings = xpinstallStrings.concat([item.name,
2990 getURLSpecFromFile(entry),
2991 item.iconURL,
2992 prettyName]);
2993 isDirty = true;
2994 }
2995 }
2996 }
2997
2998 if (location.name in StartupCache.entries) {
2999 // Look for items that have been uninstalled by removing their directory.
3000 for (var id in StartupCache.entries[location.name]) {
3001 if (!StartupCache.entries[location.name] ||
3002 !StartupCache.entries[location.name][id])
3003 continue;
3004
3005 // Force _finishOperations to run if we have enabled or disabled items.
3006 // XXXdarin this should be unnecessary now that we check
3007 // PendingOperations.size in start()
3008 if (StartupCache.entries[location.name][id].op == OP_NEEDS_ENABLE ||
3009 StartupCache.entries[location.name][id].op == OP_NEEDS_DISABLE)
3010 isDirty = true;
3011
3012 if (!(id in actualItems) &&
3013 StartupCache.entries[location.name][id].op != OP_NEEDS_UNINSTALL &&
3014 StartupCache.entries[location.name][id].op != OP_NEEDS_INSTALL &&
3015 StartupCache.entries[location.name][id].op != OP_NEEDS_UPGRADE) {
3016 // We have an entry for this id in the Extensions database, for this
3017 // install location, but it no longer exists in the Install Location.
3018 // We can infer from this that the item has been removed, so uninstall
3019 // it properly.
3020 if (canUse(id, location)) {
3021 LOG("Item Uninstalled via file removal from: " + StartupCache.entries[location.name][id].descriptor +
3022 " Item ID: " + id + " Location Key: " + location.name + ", uninstalling item.");
3023
3024 // Load the Extensions Datasource and force this item into the visible
3025 // items list if it is not already. This allows us to handle the case
3026 // where there is an entry for an item in the Startup Cache but not
3027 // in the extensions.rdf file - in that case the item will not be in
3028 // the visible list and calls to |getInstallLocation| will mysteriously
3029 // fail.
3030 this.datasource.updateVisibleList(id, location.name, false);
3031 this.uninstallItem(id);
3032 isDirty = true;
3033 }
3034 }
3035 else if (!ignoreMTimeChanges) {
3036 // Look for items whose mtime has changed, and as such we can assume
3037 // they have been "upgraded".
3038 var lf = { path: StartupCache.entries[location.name][id].descriptor };
3039 try {
3040 lf = getFileFromDescriptor(StartupCache.entries[location.name][id].descriptor, location);
3041 }
3042 catch (e) { }
3043
3044 if (lf.exists && lf.exists()) {
3045 var actualMTime = Math.floor(lf.lastModifiedTime / 1000);
3046 if (actualMTime != StartupCache.entries[location.name][id].mtime) {
3047 LOG("Item Location path changed: " + lf.path + " Item ID: " +
3048 id + " Location Key: " + location.name + ", attempting to upgrade item...");
3049 if (canUse(id, location)) {
3050 this.installItem(id, location,
anon:3051:35
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3051 function(installManifest, id, location, type) {
3052 em._upgradeItem(installManifest, id, location,
3053 type);
3054 });
3055 isDirty = true;
3056 }
3057 }
3058 }
3059 else {
3060 isDirty = true;
3061 LOG("Install Location returned a missing or malformed item path! " +
3062 "Item Path: " + lf.path + ", Location Key: " + location.name +
3063 " Item ID: " + id);
3064 if (canUse(id, location)) {
3065 // Load the Extensions Datasource and force this item into the visible
3066 // items list if it is not already. This allows us to handle the case
3067 // where there is an entry for an item in the Startup Cache but not
3068 // in the extensions.rdf file - in that case the item will not be in
3069 // the visible list and calls to |getInstallLocation| will mysteriously
3070 // fail.
3071 this.datasource.updateVisibleList(id, location.name, false);
3072 this.uninstallItem(id);
3073 }
3074 }
3075 }
3076 }
3077 }
3078
3079 // Look for items that have been installed by appearing in the location.
3080 for (var id in actualItems) {
3081 if (!(location.name in StartupCache.entries) ||
3082 !(id in StartupCache.entries[location.name]) ||
3083 !StartupCache.entries[location.name][id]) {
3084 // Remember that we've seen this item
3085 StartupCache.put(location, id, OP_NONE, true);
3086 // Push it on the stack of items to maybe install later
3087 newItems.push({location: location, id: id});
3088 }
3089 }
3090 }
3091
3092 // Process any newly discovered items. We do this here instead of in the
3093 // previous loop so that we can be sure that we have a fully populated
3094 // StartupCache.
3095 for (var i = 0; i < newItems.length; ++i) {
3096 var id = newItems[i].id;
3097 var location = newItems[i].location;
3098 if (canUse(id, location)) {
3099 LOG("Item Installed via directory addition to Install Location: " +
3100 location.name + " Item ID: " + id + ", attempting to register...");
3101 this.installItem(id, location,
anon:3102:25
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3102 function(installManifest, id, location, type) {
3103 em._configureForthcomingItem(installManifest, id, location,
3104 type);
3105 });
3106 // Disable add-ons on install when the InstallDisabled file exists.
3107 // This is so Talkback will be disabled on a subset of installs.
3108 var installDisabled = location.getItemFile(id, "InstallDisabled");
3109 if (installDisabled.exists())
3110 em.disableItem(id);
3111 isDirty = true;
3112 }
3113 }
3114
3115 // Ask the user if they want to install the dropped items, for security
3116 // purposes.
3117 installDroppedInFiles(droppedInFiles, xpinstallStrings);
3118
3119 return isDirty;
3120 },
3121
3122 /**
3123 * Upgrades contents.rdf files to chrome.manifest files for any existing
3124 * Extensions and Themes.
3125 * @returns true if actions were performed that require a restart, false
3126 * otherwise.
3127 */
_upgradeChrome
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3128 _upgradeChrome: function() {
3129 if (inSafeMode())
3130 return false;
3131
3132 var checkForNewChrome = false;
3133 var ds = this.datasource;
3134 // If we have extensions that were installed before the new flat chrome
3135 // manifests, and are still valid, we need to manually create the flat
3136 // manifest files.
3137 var extensions = this._getActiveItems(Ci.nsIUpdateItem.TYPE_EXTENSION +
3138 Ci.nsIUpdateItem.TYPE_LOCALE);
3139 for (var i = 0; i < extensions.length; ++i) {
3140 var e = extensions[i];
3141 var itemLocation = e.location.getItemLocation(e.id);
3142 var manifest = itemLocation.clone();
3143 manifest.append(FILE_CHROME_MANIFEST);
3144 if (!manifest.exists()) {
3145 var installRDF = itemLocation.clone();
3146 installRDF.append(FILE_INSTALL_MANIFEST);
3147 var installLocation = this.getInstallLocation(e.id);
3148 if (installLocation && installRDF.exists()) {
3149 var itemLocation = installLocation.getItemLocation(e.id);
3150 if (itemLocation.exists() && itemLocation.isDirectory()) {
3151 var installer = new Installer(ds, e.id, installLocation,
3152 Ci.nsIUpdateItem.TYPE_EXTENSION);
3153 installer.upgradeExtensionChrome();
3154 }
3155 }
3156 else {
3157 ds.removeItemMetadata(e.id);
3158 ds.removeItemFromContainer(e.id);
3159 }
3160
3161 checkForNewChrome = true;
3162 }
3163 }
3164
3165 var themes = this._getActiveItems(Ci.nsIUpdateItem.TYPE_THEME);
3166 // If we have themes that were installed before the new flat chrome
3167 // manifests, and are still valid, we need to manually create the flat
3168 // manifest files.
3169 for (i = 0; i < themes.length; ++i) {
3170 var item = themes[i];
3171 var itemLocation = item.location.getItemLocation(item.id);
3172 var manifest = itemLocation.clone();
3173 manifest.append(FILE_CHROME_MANIFEST);
3174 if (manifest.exists() ||
3175 item.id == stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI))
3176 continue;
3177
3178 var entries;
3179 try {
3180 var manifestURI = getURIFromFile(manifest);
3181 var chromeDir = itemLocation.clone();
3182 chromeDir.append(DIR_CHROME);
3183
3184 if (!chromeDir.exists() || !chromeDir.isDirectory()) {
3185 ds.removeItemMetadata(item.id);
3186 ds.removeItemFromContainer(item.id);
3187 continue;
3188 }
3189
3190 // We're relying on the fact that there is only one JAR file
3191 // in the "chrome" directory. This is a hack, but it works.
3192 entries = chromeDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
3193 var jarFile = entries.nextFile;
3194 if (jarFile) {
3195 var jarFileURI = getURIFromFile(jarFile);
3196 var contentsURI = newURI("jar:" + jarFileURI.spec + "!/");
3197
3198 // Use the Chrome Registry API to install the theme there
3199 var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
3200 getService(Ci.nsIToolkitChromeRegistry);
3201 cr.processContentsManifest(contentsURI, manifestURI, contentsURI, false, true);
3202 }
3203 entries.close();
3204 }
3205 catch (e) {
3206 ERROR("_upgradeChrome: failed to upgrade contents manifest for " +
3207 "theme: " + item.id + ", exception: " + e + "... The theme will be " +
3208 "disabled.");
3209 this._appDisableItem(item.id);
3210 }
3211 finally {
3212 try {
3213 entries.close();
3214 }
3215 catch (e) {
3216 }
3217 }
3218 checkForNewChrome = true;
3219 }
3220 return checkForNewChrome;
3221 },
3222
_checkForUncoveredItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3223 _checkForUncoveredItem: function(id) {
3224 var ds = this.datasource;
3225 var oldLocation = this.getInstallLocation(id);
3226 var newLocations = [];
3227 for (var locationKey in StartupCache.entries) {
3228 var location = InstallLocations.get(locationKey);
3229 if (id in StartupCache.entries[locationKey] &&
3230 location.priority > oldLocation.priority)
3231 newLocations.push(location);
3232 }
anon:3233:22
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3233 newLocations.sort(function(a, b) { return b.priority - a.priority; });
3234 if (newLocations.length > 0) {
3235 for (var i = 0; i < newLocations.length; ++i) {
3236 // Check to see that the item at the location exists
3237 var installRDF = newLocations[i].getItemFile(id, FILE_INSTALL_MANIFEST);
3238 if (installRDF.exists()) {
3239 // Update the visible item cache so that |_finalizeUpgrade| is properly
3240 // called from |_finishOperations|
3241 var name = newLocations[i].name;
3242 ds.updateVisibleList(id, name, true);
3243 PendingOperations.addItem(OP_NEEDS_UPGRADE,
3244 { locationKey: name, id: id });
3245 PendingOperations.addItem(OP_NEEDS_INSTALL,
3246 { locationKey: name, id: id });
3247 break;
3248 }
3249 else {
3250 // If no item exists at the location specified, remove this item
3251 // from the visible items list and check again.
3252 StartupCache.clearEntry(newLocations[i], id);
3253 ds.updateVisibleList(id, null, true);
3254 }
3255 }
3256 }
3257 else
3258 ds.updateVisibleList(id, null, true);
3259 },
3260
3261 /**
3262 * Finish up pending operations - perform upgrades, installs, enables/disables,
3263 * uninstalls etc.
3264 * @returns true if actions were performed that require a restart, false
3265 * otherwise.
3266 */
_finishOperations
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3267 _finishOperations: function() {
3268 try {
3269 // Stuff has changed, load the Extensions datasource in all its RDFey
3270 // glory.
3271 var ds = this.datasource;
3272 var updatedTargetAppInfos = [];
3273
3274 var needsRestart = false;
3275 var upgrades = [];
3276 var newAddons = [];
3277 var addons = getPref("getCharPref", PREF_EM_NEW_ADDONS_LIST, "");
3278 if (addons != "")
3279 newAddons = addons.split(",");
3280 do {
3281 // Enable and disable during startup so items that are changed in the
3282 // ui can be reset to a no-op.
3283 // Look for extensions that need to be enabled.
3284 var items = PendingOperations.getOperations(OP_NEEDS_ENABLE);
3285 for (var i = items.length - 1; i >= 0; --i) {
3286 var id = items[i].id;
3287 var installLocation = this.getInstallLocation(id);
3288 StartupCache.put(installLocation, id, OP_NONE, true);
3289 PendingOperations.clearItem(OP_NEEDS_ENABLE, id);
3290 needsRestart = true;
3291 }
3292 PendingOperations.clearItems(OP_NEEDS_ENABLE);
3293
3294 // Look for extensions that need to be disabled.
3295 items = PendingOperations.getOperations(OP_NEEDS_DISABLE);
3296 for (i = items.length - 1; i >= 0; --i) {
3297 id = items[i].id;
3298 installLocation = this.getInstallLocation(id);
3299 StartupCache.put(installLocation, id, OP_NONE, true);
3300 PendingOperations.clearItem(OP_NEEDS_DISABLE, id);
3301 needsRestart = true;
3302 }
3303 PendingOperations.clearItems(OP_NEEDS_DISABLE);
3304
3305 // Look for extensions that need to be upgraded. The process here is to
3306 // uninstall the old version of the extension first, then install the
3307 // new version in its place.
3308 items = PendingOperations.getOperations(OP_NEEDS_UPGRADE);
3309 for (i = items.length - 1; i >= 0; --i) {
3310 id = items[i].id;
3311 var newLocation = InstallLocations.get(items[i].locationKey);
3312 // check if there is updated app compatibility info
3313 var newTargetAppInfo = ds.getUpdatedTargetAppInfo(id);
3314 if (newTargetAppInfo)
3315 updatedTargetAppInfos.push(newTargetAppInfo);
3316 this._finalizeUpgrade(id, newLocation);
3317 upgrades.push(id);
3318 }
3319 PendingOperations.clearItems(OP_NEEDS_UPGRADE);
3320
3321 // Install items
3322 items = PendingOperations.getOperations(OP_NEEDS_INSTALL);
3323 for (i = items.length - 1; i >= 0; --i) {
3324 needsRestart = true;
3325 id = items[i].id;
3326 // check if there is updated app compatibility info
3327 newTargetAppInfo = ds.getUpdatedTargetAppInfo(id);
3328 if (newTargetAppInfo)
3329 updatedTargetAppInfos.push(newTargetAppInfo);
3330 this._finalizeInstall(id, null);
3331 if (upgrades.indexOf(id) < 0 && newAddons.indexOf(id) < 0)
3332 newAddons.push(id);
3333 }
3334 PendingOperations.clearItems(OP_NEEDS_INSTALL);
3335
3336 // Look for extensions that need to be removed. This MUST be done after
3337 // the install operations since extensions to be installed may have to be
3338 // uninstalled if there are errors during the installation process!
3339 items = PendingOperations.getOperations(OP_NEEDS_UNINSTALL);
3340 for (i = items.length - 1; i >= 0; --i) {
3341 id = items[i].id;
3342 this._finalizeUninstall(id);
3343 this._checkForUncoveredItem(id);
3344 needsRestart = true;
3345 var pos = newAddons.indexOf(id);
3346 if (pos >= 0)
3347 newAddons.splice(pos, 1);
3348 }
3349 PendingOperations.clearItems(OP_NEEDS_UNINSTALL);
3350
3351 // When there have been operations and all operations have completed.
3352 if (PendingOperations.size == 0) {
3353 // If there is updated app compatibility info update the datasource.
3354 for (i = 0; i < updatedTargetAppInfos.length; ++i)
3355 ds.setTargetApplicationInfo(updatedTargetAppInfos[i].id,
3356 updatedTargetAppInfos[i].targetAppID,
3357 updatedTargetAppInfos[i].minVersion,
3358 updatedTargetAppInfos[i].maxVersion,
3359 null);
3360
3361 // Enumerate all items
3362 var ctr = getContainer(ds, ds._itemRoot);
3363 var elements = ctr.GetElements();
3364 while (elements.hasMoreElements()) {
3365 var itemResource = elements.getNext().QueryInterface(Ci.nsIRDFResource);
3366
3367 // Ensure appDisabled is in the correct state.
3368 id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
3369 if (this._isUsableItem(id))
3370 ds.setItemProperty(id, EM_R("appDisabled"), null);
3371 else
3372 ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
3373
3374 // userDisabled is set based on its value being OP_NEEDS_ENABLE or
3375 // OP_NEEDS_DISABLE. This allows us to have an item to be enabled
3376 // by the app and disabled by the user during a single restart.
3377 var value = stringData(ds.GetTarget(itemResource, EM_R("userDisabled"), true));
3378 if (value == OP_NEEDS_ENABLE)
3379 ds.setItemProperty(id, EM_R("userDisabled"), null);
3380 else if (value == OP_NEEDS_DISABLE)
3381 ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
3382 }
3383 }
3384 }
3385 while (PendingOperations.size > 0);
3386
3387 // Upgrade contents.rdf files to the new chrome.manifest format for
3388 // existing Extensions and Themes
3389 if (this._upgradeChrome()) {
3390 var cr = Cc["@mozilla.org/chrome/chrome-registry;1"].
3391 getService(Ci.nsIChromeRegistry);
3392 cr.checkForNewChrome();
3393 }
3394
3395 // If no additional restart is required, it implies that there are
3396 // no new components that need registering so we can inform the app
3397 // not to do any extra startup checking next time round.
3398 this._updateManifests(needsRestart);
3399
3400 // Remember the list of add-ons that were installed this time around
3401 // unless this was a new profile.
3402 if (!gFirstRun && newAddons.length > 0)
3403 gPref.setCharPref(PREF_EM_NEW_ADDONS_LIST, newAddons.join(","));
3404 }
3405 catch (e) {
3406 ERROR("ExtensionManager:_finishOperations - failure, catching exception - lineno: " +
3407 e.lineNumber + " - file: " + e.fileName + " - " + e);
3408 }
3409 return needsRestart;
3410 },
3411
3412 /**
3413 * Checks to see if there are items that are incompatible with this version
3414 * of the application, disables them to prevent incompatibility problems and
3415 * invokes the Update Wizard to look for newer versions.
3416 * @returns true if there were incompatible items installed and disabled, and
3417 * the application must now be restarted to reinitialize XPCOM,
3418 * false otherwise.
3419 */
checkForMismatches
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3420 checkForMismatches: function() {
3421 // Check to see if the version of the application that is being started
3422 // now is the same one that was started last time.
3423 var currAppVersion = gApp.version;
3424 var lastAppVersion = getPref("getCharPref", PREF_EM_LAST_APP_VERSION, "");
3425 if (currAppVersion == lastAppVersion)
3426 return false;
3427 // With a new profile lastAppVersion doesn't exist yet.
3428 if (!lastAppVersion) {
3429 gPref.setCharPref(PREF_EM_LAST_APP_VERSION, currAppVersion);
3430 return false;
3431 }
3432
3433 // Version mismatch, we have to load the extensions datasource and do
3434 // version checking. Time hit here doesn't matter since this doesn't happen
3435 // all that often.
3436 this._upgradeFromV10();
3437
3438 // Make the extensions datasource consistent if it isn't already.
3439 var isDirty = false;
3440 if (this._ensureDatasetIntegrity())
3441 isDirty = true;
3442
3443 if (this._checkForFileChanges())
3444 isDirty = true;
3445
3446 if (PendingOperations.size != 0)
3447 isDirty = true;
3448
3449 if (isDirty)
3450 this._finishOperations();
3451
3452 var ds = this.datasource;
3453 // During app upgrade cleanup invalid entries in the extensions datasource.
3454 ds.beginUpdateBatch();
3455 var allResources = ds.GetAllResources();
3456 while (allResources.hasMoreElements()) {
3457 var res = allResources.getNext().QueryInterface(Ci.nsIRDFResource);
3458 if (ds.GetTarget(res, EM_R("downloadURL"), true) ||
3459 (!ds.GetTarget(res, EM_R("installLocation"), true) &&
3460 stringData(ds.GetTarget(res, EM_R("appDisabled"), true)) == "true"))
3461 ds.removeDownload(res.Value);
3462 }
3463 ds.endUpdateBatch();
3464
3465 var badItems = [];
3466 var allAppManaged = true;
3467 var ctr = getContainer(ds, ds._itemRoot);
3468 var elements = ctr.GetElements();
3469 while (elements.hasMoreElements()) {
3470 var itemResource = elements.getNext().QueryInterface(Ci.nsIRDFResource);
3471 var id = stripPrefix(itemResource.Value, PREFIX_ITEM_URI);
3472 var location = this.getInstallLocation(id);
3473 if (!location) {
3474 // Item was in an unknown install location
3475 badItems.push(id);
3476 continue;
3477 }
3478
3479 if (ds.getItemProperty(id, "appManaged") == "true") {
3480 // Force an update of the metadata for appManaged extensions since the
3481 // last modified time is not updated for directories on FAT / FAT32
3482 // filesystems when software update applies a new version of the app.
3483 if (location.name == KEY_APP_GLOBAL) {
3484 var installRDF = location.getItemFile(id, FILE_INSTALL_MANIFEST);
3485 if (installRDF.exists()) {
3486 var metadataDS = getInstallManifest(installRDF);
3487 ds.addItemMetadata(id, metadataDS, location);
3488 ds.updateProperty(id, "compatible");
3489 }
3490 }
3491 }
3492 else if (allAppManaged)
3493 allAppManaged = false;
3494
3495 if (ds.getItemProperty(id, "providesUpdatesSecurely") == "false") {
3496 /* It's possible the previous version did not understand updateKeys so
3497 * check if we can import one for this addon from it's manifest. */
3498 installRDF = location.getItemFile(id, FILE_INSTALL_MANIFEST);
3499 if (installRDF.exists()) {
3500 metadataDS = getInstallManifest(installRDF);
3501 var literal = metadataDS.GetTarget(gInstallManifestRoot, EM_R("updateKey"), true);
3502 if (literal && literal instanceof Ci.nsIRDFLiteral)
3503 ds.setItemProperty(id, EM_R("updateKey"), literal);
3504 }
3505 }
3506
3507 // appDisabled is determined by an item being compatible, using secure
3508 // updates, satisfying its dependencies, and not being blocklisted
3509 if (this._isUsableItem(id)) {
3510 if (ds.getItemProperty(id, "appDisabled"))
3511 ds.setItemProperty(id, EM_R("appDisabled"), null);
3512 }
3513 else if (!ds.getItemProperty(id, "appDisabled"))
3514 ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
3515
3516 ds.setItemProperty(id, EM_R("availableUpdateURL"), null);
3517 ds.setItemProperty(id, EM_R("availableUpdateVersion"), null);
3518 }
3519
3520 // Must clean up outside of the loop. Modifying the container while
3521 // iterating its contents is bad.
3522 for (var i = 0; i < badItems.length; i++) {
3523 id = badItems[i];
3524 LOG("Item " + id + " was installed in an unknown location, removing.");
3525 var disabled = ds.getItemProperty(id, "userDisabled") == "true";
3526 // Clean up the datasource
3527 ds.removeCorruptItem(id);
3528 // Check for any unhidden items.
3529 var entries = StartupCache.findEntries(id);
3530 if (entries.length > 0) {
3531 var newLocation = InstallLocations.get(entries[0].location);
3532 for (var j = 1; j < entries.length; j++) {
3533 location = InstallLocations.get(entries[j].location);
3534 if (newLocation.priority < location.priority)
3535 newLocation = location;
3536 }
3537 LOG("Activating item " + id + " in " + newLocation.name);
3538 var em = this;
3539 this.installItem(id, newLocation,
anon:3540:25
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3540 function(installManifest, id, location, type) {
3541 em._configureForthcomingItem(installManifest, id, location,
3542 type);
3543 });
3544 if (disabled)
3545 em.disableItem(id);
3546 }
3547 }
3548
3549 // Update the manifests to reflect the items that were disabled / enabled.
3550 this._updateManifests(true);
3551
3552 // Always check for compatibility updates when upgrading if we have add-ons
3553 // that aren't managed by the application.
3554 if (!allAppManaged)
3555 this._showMismatchWindow();
3556
3557 // Finish any pending upgrades from the compatibility update to avoid an
3558 // additional restart.
3559 if (PendingOperations.size != 0)
3560 this._finishOperations();
3561
3562 // Update the last app version so we don't do this again with this version.
3563 gPref.setCharPref(PREF_EM_LAST_APP_VERSION, currAppVersion);
3564
3565 // Prevent extension update dialog from showing
3566 gPref.setBoolPref(PREF_UPDATE_NOTIFYUSER, false);
3567 return true;
3568 },
3569
3570 /**
3571 * Shows the "Compatibility Updates" UI
3572 */
_showMismatchWindow
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3573 _showMismatchWindow: function(items) {
3574 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
3575 getService(Ci.nsIWindowMediator);
3576 var wizard = wm.getMostRecentWindow("Update:Wizard");
3577 if (wizard)
3578 wizard.focus();
3579 else {
3580 var features = "chrome,centerscreen,dialog,titlebar,modal";
3581 // This *must* be modal so as not to break startup! This code is invoked before
3582 // the main event loop is initiated (via checkForMismatches).
3583 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
3584 getService(Ci.nsIWindowWatcher);
3585 ww.openWindow(null, URI_EXTENSION_UPDATE_DIALOG, "", features, null);
3586 }
3587 },
3588
3589 /*
3590 * Catch all for facilitating a version 1.0 profile upgrade.
3591 * 1) removes the abandoned default theme directory from the profile.
3592 * 2) prepares themes installed with version 1.0 for installation.
3593 * 3) initiates an install to populate the new extensions datasource.
3594 * 4) migrates the disabled attribute from the old datasource.
3595 * 5) migrates the app compatibility info from the old datasource.
3596 */
_upgradeFromV10
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3597 _upgradeFromV10: function() {
3598 var extensionsDS = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
3599 var dsExists = extensionsDS.exists();
3600 // Toolkiit 1.7 profiles (Firefox 1.0, Thunderbird 1.0, etc.) have a default
3601 // theme directory in the profile's extensions directory that will be
3602 // disabled due to having a maxVersion that is incompatible with the
3603 // toolkit 1.8 release of the app.
3604 var profileDefaultTheme = getDirNoCreate(KEY_PROFILEDS, [DIR_EXTENSIONS,
3605 stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI)]);
3606 if (profileDefaultTheme && profileDefaultTheme.exists()) {
3607 removeDirRecursive(profileDefaultTheme);
3608 // Sunbird 0.3a1 didn't move the default theme into the app's extensions
3609 // directory and we can't install it while uninstalling the one in the
3610 // profile directory. If we have a toolkit 1.8 extensions datasource and
3611 // a profile default theme deleting the toolkit 1.8 extensions datasource
3612 // will fix this problem when the datasource is re-created.
3613 if (dsExists)
3614 extensionsDS.remove(false);
3615 }
3616
3617 // return early if the toolkit 1.7 extensions datasource file doesn't exist.
3618 var oldExtensionsFile = getFile(KEY_PROFILEDIR, [DIR_EXTENSIONS, "Extensions.rdf"]);
3619 if (!oldExtensionsFile.exists())
3620 return;
3621
3622 // Sunbird 0.2 used a different GUID for the default theme
3623 profileDefaultTheme = getDirNoCreate(KEY_PROFILEDS, [DIR_EXTENSIONS,
3624 "{8af2d0a7-e394-4de2-ae55-2dae532a7a9b}"]);
3625 if (profileDefaultTheme && profileDefaultTheme.exists())
3626 removeDirRecursive(profileDefaultTheme);
3627
3628 // Firefox 0.9 profiles may have DOMi 1.0 with just an install.rdf
3629 var profileDOMi = getDirNoCreate(KEY_PROFILEDS, [DIR_EXTENSIONS,
3630 "{641d8d09-7dda-4850-8228-ac0ab65e2ac9}"]);
3631 if (profileDOMi && profileDOMi.exists())
3632 removeDirRecursive(profileDOMi);
3633
3634 // return early to avoid migrating data twice if we already have a
3635 // toolkit 1.8 extension datasource.
3636 if (dsExists)
3637 return;
3638
3639 // Prepare themes for installation
3640 // Only enumerate directories in the app-profile and app-global locations.
3641 var locations = [KEY_APP_PROFILE, KEY_APP_GLOBAL];
3642 for (var i = 0; i < locations.length; ++i) {
3643 var location = InstallLocations.get(locations[i]);
3644 if (!location.canAccess)
3645 continue;
3646
3647 var entries = location.itemLocations;
3648 var entry;
3649 while ((entry = entries.nextFile)) {
3650 var installRDF = entry.clone();
3651 installRDF.append(FILE_INSTALL_MANIFEST);
3652
3653 var chromeDir = entry.clone();
3654 chromeDir.append(DIR_CHROME);
3655
3656 // It must be a directory without an install.rdf and it must contain
3657 // a chrome directory
3658 if (!entry.isDirectory() || installRDF.exists() || !chromeDir.exists())
3659 continue;
3660
3661 var chromeEntries = chromeDir.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator);
3662 if (!chromeEntries.hasMoreElements())
3663 continue;
3664
3665 // We're relying on the fact that there is only one JAR file
3666 // in the "chrome" directory. This is a hack, but it works.
3667 var jarFile = chromeEntries.nextFile;
3668 if (jarFile.isDirectory())
3669 continue;
3670 var id = location.getIDForLocation(entry);
3671
3672 try {
3673 var zipReader = getZipReaderForFile(jarFile);
3674 zipReader.extract(FILE_INSTALL_MANIFEST, installRDF);
3675
3676 var contentsManifestFile = location.getItemFile(id, FILE_CONTENTS_MANIFEST);
3677 zipReader.extract(FILE_CONTENTS_MANIFEST, contentsManifestFile);
3678
3679 var rootFiles = ["preview.png", "icon.png"];
3680 for (var i = 0; i < rootFiles.length; ++i) {
3681 try {
3682 var target = location.getItemFile(id, rootFiles[i]);
3683 zipReader.extract(rootFiles[i], target);
3684 }
3685 catch (e) {
3686 }
3687 }
3688 zipReader.close();
3689 }
3690 catch (e) {
3691 ERROR("ExtensionManager:_upgradeFromV10 - failed to extract theme files\r\n" +
3692 "Exception: " + e);
3693 }
3694 }
3695 }
3696
3697 // When upgrading from a version 1.0 profile we need to populate the
3698 // extensions datasource with all items before checking for incompatible
3699 // items since the datasource hasn't been created yet.
3700 var itemsToCheck = [];
3701 if (this._checkForFileChanges()) {
3702 // Create a list of all items that are to be installed so we can migrate
3703 // these items's settings to the new datasource.
3704 var items = PendingOperations.getOperations(OP_NEEDS_INSTALL);
3705 for (i = items.length - 1; i >= 0; --i) {
3706 if (items[i].locationKey == KEY_APP_PROFILE ||
3707 items[i].locationKey == KEY_APP_GLOBAL)
3708 itemsToCheck.push(items[i].id);
3709 }
3710 this._finishOperations();
3711 }
3712
3713 // If there are no items to migrate settings for return early.
3714 if (itemsToCheck.length == 0)
3715 return;
3716
3717 var fileURL = getURLSpecFromFile(oldExtensionsFile);
3718 var oldExtensionsDS = gRDF.GetDataSourceBlocking(fileURL);
3719 var versionChecker = getVersionChecker();
3720 var ds = this.datasource;
3721 var currAppVersion = gApp.version;
3722 var currAppID = gApp.ID;
3723 for (var i = 0; i < itemsToCheck.length; ++i) {
3724 var item = ds.getItemForID(itemsToCheck[i]);
3725 var oldPrefix = (item.type == Ci.nsIUpdateItem.TYPE_EXTENSION) ? PREFIX_EXTENSION : PREFIX_THEME;
3726 var oldRes = gRDF.GetResource(oldPrefix + item.id);
3727 // Disable the item if it was disabled in the version 1.0 extensions
3728 // datasource.
3729 if (oldExtensionsDS.GetTarget(oldRes, EM_R("disabled"), true))
3730 ds.setItemProperty(item.id, EM_R("userDisabled"), EM_L("true"));
3731
3732 // app enable all items. If it is incompatible it will be app disabled
3733 // later on.
3734 ds.setItemProperty(item.id, EM_R("appDisabled"), null);
3735
3736 // if the item is already compatible don't attempt to migrate the
3737 // item's compatibility info
3738 var newRes = getResourceForID(itemsToCheck[i]);
3739 if (ds.isCompatible(ds, newRes))
3740 continue;
3741
3742 var updatedMinVersion = null;
3743 var updatedMaxVersion = null;
3744 var targetApps = oldExtensionsDS.GetTargets(oldRes, EM_R("targetApplication"), true);
3745 while (targetApps.hasMoreElements()) {
3746 var targetApp = targetApps.getNext();
3747 if (targetApp instanceof Ci.nsIRDFResource) {
3748 try {
3749 var foundAppID = stringData(oldExtensionsDS.GetTarget(targetApp, EM_R("id"), true));
3750 // Different target application? (Note: v1.0 didn't support toolkit app ID)
3751 if (foundAppID != currAppID)
3752 continue;
3753
3754 updatedMinVersion = stringData(oldExtensionsDS.GetTarget(targetApp, EM_R("minVersion"), true));
3755 updatedMaxVersion = stringData(oldExtensionsDS.GetTarget(targetApp, EM_R("maxVersion"), true));
3756
3757 // Only set the target app info if the extension's target app info
3758 // in the version 1.0 extensions datasource makes it compatible
3759 if (versionChecker.compare(currAppVersion, updatedMinVersion) >= 0 &&
3760 versionChecker.compare(currAppVersion, updatedMaxVersion) <= 0)
3761 ds.setTargetApplicationInfo(item.id, foundAppID, updatedMinVersion,
3762 updatedMaxVersion, null);
3763
3764 break;
3765 }
3766 catch (e) {
3767 }
3768 }
3769 }
3770 }
3771 },
3772
3773 /**
3774 * Write the Extensions List and the Startup Cache
3775 * @param needsRestart
3776 * true if the application needs to restart again, false otherwise.
3777 */
_updateManifests
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3778 _updateManifests: function(needsRestart) {
3779 // Write the Startup Cache (All Items, visible or not)
3780 StartupCache.write();
3781 // Write the Extensions Locations Manifest (Visible, enabled items)
3782 this._updateExtensionsManifest(needsRestart);
3783 },
3784
3785 /**
3786 * Get a list of items that are currently "active" (turned on) of a specific
3787 * type
3788 * @param type
3789 * The nsIUpdateItem type to return a list of items of
3790 * @returns An array of active items of the specified type.
3791 */
_getActiveItems
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3792 _getActiveItems: function(type) {
3793 var allItems = this.getItemList(type, { });
3794 var activeItems = [];
3795 var ds = this.datasource;
3796 for (var i = 0; i < allItems.length; ++i) {
3797 var item = allItems[i];
3798
3799 var installLocation = this.getInstallLocation(item.id);
3800 // An entry with an invalid install location is not active.
3801 if (!installLocation)
3802 continue;
3803 // An item entry is valid only if it is not disabled, not about to
3804 // be disabled, and not about to be uninstalled.
3805 if (installLocation.name in StartupCache.entries &&
3806 item.id in StartupCache.entries[installLocation.name] &&
3807 StartupCache.entries[installLocation.name][item.id]) {
3808 var op = StartupCache.entries[installLocation.name][item.id].op;
3809 if (op == OP_NEEDS_INSTALL || op == OP_NEEDS_UPGRADE ||
3810 op == OP_NEEDS_UNINSTALL || op == OP_NEEDS_DISABLE)
3811 continue;
3812 }
3813 // Suppress items that have been disabled by the user or the app.
3814 if (ds.getItemProperty(item.id, "isDisabled") != "true")
3815 activeItems.push({ id: item.id, version: item.version,
3816 location: installLocation });
3817 }
3818
3819 return activeItems;
3820 },
3821
3822 /**
3823 * Write the Extensions List
3824 * @param needsRestart
3825 * true if the application needs to restart again, false otherwise.
3826 */
_updateExtensionsManifest
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3827 _updateExtensionsManifest: function(needsRestart) {
3828 // When an operation is performed that requires a component re-registration
3829 // (extension enabled/disabled, installed, uninstalled), we must write the
3830 // set of paths where extensions live so that the startup system can determine
3831 // where additional components, preferences, chrome manifests etc live.
3832 //
3833 // To do this we obtain a list of active extensions and themes and write
3834 // these to the extensions.ini file in the profile directory.
3835 var validExtensions = this._getActiveItems(Ci.nsIUpdateItem.TYPE_ANY -
3836 Ci.nsIUpdateItem.TYPE_THEME);
3837 var validThemes = this._getActiveItems(Ci.nsIUpdateItem.TYPE_THEME);
3838
3839 var extensionsLocationsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSION_MANIFEST]);
3840 var fos = openSafeFileOutputStream(extensionsLocationsFile);
3841
3842 var enabledItems = [];
3843 var extensionSectionHeader = "[ExtensionDirs]\r\n";
3844 fos.write(extensionSectionHeader, extensionSectionHeader.length);
3845 for (var i = 0; i < validExtensions.length; ++i) {
3846 var e = validExtensions[i];
3847 var itemLocation = e.location.getItemLocation(e.id).QueryInterface(Ci.nsILocalFile);
3848 var descriptor = getAbsoluteDescriptor(itemLocation);
3849 var line = "Extension" + i + "=" + descriptor + "\r\n";
3850 fos.write(line, line.length);
3851 enabledItems.push(e.id + ":" + e.version);
3852 }
3853
3854 var themeSectionHeader = "[ThemeDirs]\r\n";
3855 fos.write(themeSectionHeader, themeSectionHeader.length);
3856 for (i = 0; i < validThemes.length; ++i) {
3857 var e = validThemes[i];
3858 var itemLocation = e.location.getItemLocation(e.id).QueryInterface(Ci.nsILocalFile);
3859 var descriptor = getAbsoluteDescriptor(itemLocation);
3860 var line = "Extension" + i + "=" + descriptor + "\r\n";
3861 fos.write(line, line.length);
3862 enabledItems.push(e.id + ":" + e.version);
3863 }
3864
3865 closeSafeFileOutputStream(fos);
3866
3867 // Cache the enabled list for annotating the crash report subsequently
3868 gPref.setCharPref(PREF_EM_ENABLED_ITEMS, enabledItems.join(","));
3869
3870 // Now refresh the compatibility manifest.
3871 this._extensionListChanged = needsRestart;
3872 },
3873
3874 /**
3875 * Say whether or not the Extension List has changed (and thus whether or not
3876 * the system will have to restart the next time it is started).
3877 * @param val
3878 * true if the Extension List has changed, false otherwise.
3879 * @returns |val|
3880 */
set__extensionListChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3881 set _extensionListChanged(val) {
3882 // When an extension has an operation perform on it (e.g. install, upgrade,
3883 // disable, etc.) we are responsible for creating the .autoreg file and
3884 // nsAppRunner is responsible for removing it on restart. At some point it
3885 // may make sense to be able to cancel a registration but for now we only
3886 // create the file.
3887 try {
3888 var autoregFile = getFile(KEY_PROFILEDIR, [FILE_AUTOREG]);
3889 if (val && !autoregFile.exists())
3890 autoregFile.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
3891 }
3892 catch (e) {
3893 }
3894 return val;
3895 },
3896
3897 /**
3898 * Gathers data about an item specified by the supplied Install Manifest
3899 * and determines whether or not it can be installed as-is. It makes this
3900 * determination by validating the item's GUID, Version, and determining
3901 * if it is compatible with this application.
3902 * @param installManifest
3903 * A nsIRDFDataSource representing the Install Manifest of the
3904 * item to be installed.
3905 * @return A JS Object with the following properties:
3906 * "id" The GUID of the Item being installed.
3907 * "version" The Version string of the Item being installed.
3908 * "name" The Name of the Item being installed.
3909 * "type" The nsIUpdateItem type of the Item being installed.
3910 * "targetApps" An array of TargetApplication Info Objects
3911 * with "id", "minVersion" and "maxVersion" properties,
3912 * representing applications targeted by this item.
3913 * "error" The result code:
3914 * INSTALLERROR_SUCCESS
3915 * no error, item can be installed
3916 * INSTALLERROR_INVALID_GUID
3917 * error, GUID is not well-formed
3918 * INSTALLERROR_INVALID_VERSION
3919 * error, Version is not well-formed
3920 * INSTALLERROR_INCOMPATIBLE_VERSION
3921 * error, item is not compatible with this version
3922 * of the application.
3923 * INSTALLERROR_INCOMPATIBLE_PLATFORM
3924 * error, item is not compatible with the operating
3925 * system or ABI the application was built for.
3926 * INSTALLERROR_INSECURE_UPDATE
3927 * error, item has no secure method of providing updates
3928 * INSTALLERROR_BLOCKLISTED
3929 * error, item is blocklisted
3930 */
_getInstallData
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3931 _getInstallData: function(installManifest) {
3932 var installData = { id : "",
3933 version : "",
3934 name : "",
3935 type : 0,
3936 error : INSTALLERROR_SUCCESS,
3937 targetApps : [],
3938 updateURL : "",
3939 updateKey : "",
3940 currentApp : null };
3941
3942 // Fetch properties from the Install Manifest
3943 installData.id = getManifestProperty(installManifest, "id");
3944 installData.version = getManifestProperty(installManifest, "version");
3945 installData.name = getManifestProperty(installManifest, "name");
3946 installData.type = getAddonTypeFromInstallManifest(installManifest);
3947 installData.updateURL= getManifestProperty(installManifest, "updateURL");
3948 installData.updateKey= getManifestProperty(installManifest, "updateKey");
3949
3950 /**
3951 * Reads a property off a Target Application resource
3952 * @param resource
3953 * The RDF Resource for a Target Application
3954 * @param property
3955 * The property (less EM_NS) to read
3956 * @returns The string literal value of the property.
3957 */
readTAProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
3958 function readTAProperty(resource, property) {
3959 return stringData(installManifest.GetTarget(resource, EM_R(property), true));
3960 }
3961
3962 var targetApps = installManifest.GetTargets(gInstallManifestRoot,
3963 EM_R("targetApplication"),
3964 true);
3965 while (targetApps.hasMoreElements()) {
3966 var targetApp = targetApps.getNext();
3967 if (targetApp instanceof Ci.nsIRDFResource) {
3968 try {
3969 var data = { id : readTAProperty(targetApp, "id"),
3970 minVersion: readTAProperty(targetApp, "minVersion"),
3971 maxVersion: readTAProperty(targetApp, "maxVersion") };
3972 installData.targetApps.push(data);
3973 if ((data.id == gApp.ID) ||
3974 (data.id == TOOLKIT_ID) && !installData.currentApp)
3975 installData.currentApp = data;
3976 }
3977 catch (e) {
3978 continue;
3979 }
3980 }
3981 }
3982
3983 // If the item specifies one or more target platforms, make sure our OS/ABI
3984 // combination is in the list - otherwise, refuse to install the item.
3985 var targetPlatforms = null;
3986 try {
3987 targetPlatforms = installManifest.GetTargets(gInstallManifestRoot,
3988 EM_R("targetPlatform"),
3989 true);
3990 } catch(e) {
3991 // No targetPlatform nodes, continue.
3992 }
3993 if (targetPlatforms != null && targetPlatforms.hasMoreElements()) {
3994 var foundMatchingOS = false;
3995 var foundMatchingOSAndABI = false;
3996 var requireABICompatibility = false;
3997 while (targetPlatforms.hasMoreElements()) {
3998 var targetPlatform = stringData(targetPlatforms.getNext());
3999 var os = targetPlatform.split("_")[0];
4000 var index = targetPlatform.indexOf("_");
4001 var abi = index != -1 ? targetPlatform.substr(index + 1) : null;
4002 if (os == gOSTarget) {
4003 foundMatchingOS = true;
4004 // The presence of any ABI part after our OS means ABI is important.
4005 if (abi != null) {
4006 requireABICompatibility = true;
4007 // If we don't know our ABI, we can't be compatible
4008 if (abi == gXPCOMABI && abi != UNKNOWN_XPCOM_ABI) {
4009 foundMatchingOSAndABI = true;
4010 break;
4011 }
4012 }
4013 }
4014 }
4015 if (!foundMatchingOS || (requireABICompatibility && !foundMatchingOSAndABI)) {
4016 installData.error = INSTALLERROR_INCOMPATIBLE_PLATFORM;
4017 return installData;
4018 }
4019 }
4020
4021 // Validate the Item ID
4022 if (!gIDTest.test(installData.id)) {
4023 installData.error = INSTALLERROR_INVALID_GUID;
4024 return installData;
4025 }
4026
4027 // Check that the add-on provides a secure update method.
4028 if (gCheckUpdateSecurity &&
4029 installData.updateURL &&
4030 installData.updateURL.substring(0, 6) != "https:" &&
4031 !installData.updateKey) {
4032 installData.error = INSTALLERROR_INSECURE_UPDATE;
4033 return installData;
4034 }
4035
4036 // Check that the target application range allows compatibility with the app
4037 if (gCheckCompatibility &&
4038 !this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined)) {
4039 installData.error = INSTALLERROR_INCOMPATIBLE_VERSION;
4040 return installData;
4041 }
4042
4043 // Check if the item is blocklisted.
4044 if (!gBlocklist)
4045 gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].
4046 getService(Ci.nsIBlocklistService);
4047 if (gBlocklist.isAddonBlocklisted(installData.id, installData.version,
4048 null, null))
4049 installData.error = INSTALLERROR_BLOCKLISTED;
4050
4051 return installData;
4052 },
4053
4054 /**
4055 * Installs an item from a XPI/JAR file.
4056 * This is the main entry point into the Install system from outside code
4057 * (e.g. XPInstall).
4058 * @param aXPIFile
4059 * The file to install from.
4060 * @param aInstallLocationKey
4061 * The name of the Install Location where this item should be
4062 * installed.
4063 */
installItemFromFile
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4064 installItemFromFile: function(xpiFile, installLocationKey) {
4065 this.installItemFromFileInternal(xpiFile, installLocationKey, null);
4066
4067 // If there are no compatibility checks running and no downloads in
4068 // progress then the install operations are complete.
4069 if (this._compatibilityCheckCount == 0 && this._transactions.length == 0) {
4070 for (var i = 0; i < this._installListeners.length; ++i)
4071 this._installListeners[i].onInstallsCompleted();
4072 }
4073 },
4074
4075 /**
4076 * Installs an item from a XPI/JAR file.
4077 * @param aXPIFile
4078 * The file to install from.
4079 * @param aInstallLocationKey
4080 * The name of the Install Location where this item should be
4081 * installed.
4082 * @param aInstallManifest
4083 * An updated Install Manifest from the Version Update check.
4084 * Can be null when invoked from callers other than the Version
4085 * Update check.
4086 */
installItemFromFileInternal
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4087 installItemFromFileInternal: function(aXPIFile, aInstallLocationKey, aInstallManifest) {
4088 var em = this;
4089 /**
4090 * Gets the Install Location for an Item.
4091 * @param itemID
4092 * The GUID of the item to find an Install Location for.
4093 * @return An object implementing nsIInstallLocation which represents the
4094 * location where the specified item should be installed.
4095 * This can be:
4096 * 1. an object that corresponds to the location key supplied to
4097 * |installItemFromFileInternal|,
4098 * 2. the default install location (the App Profile Extensions Folder)
4099 * if no location key was supplied, or the location key supplied
4100 * was not in the set of registered locations
4101 * 3. null, if the location selected by 1 or 2 above does not support
4102 * installs from XPI/JAR files, or that location is not writable
4103 * with the current access privileges.
4104 */
getInstallLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4105 function getInstallLocation(itemID) {
4106 // Here I use "upgrade" to mean "install a different version of an item".
4107 var installLocation = em.getInstallLocation(itemID);
4108 if (!installLocation) {
4109 // This is not an "upgrade", since we don't have any location data for the
4110 // extension ID specified - that is, it's not in our database.
4111
4112 // Caller supplied a key to a registered location, use that location
4113 // for the installation
4114 installLocation = InstallLocations.get(aInstallLocationKey);
4115 if (installLocation) {
4116 // If the specified location does not have a common metadata location
4117 // (e.g. extensions have no common root, or other location specified
4118 // by the location implementation) - e.g. for a Registry Key enumeration
4119 // location - we cannot install or upgrade using a XPI file, probably
4120 // because these application types will be handling upgrading themselves.
4121 // Just bail.
4122 if (!installLocation.location) {
4123 LOG("Install Location \"" + installLocation.name + "\" does not support " +
4124 "installation of items from XPI/JAR files. You must manage " +
4125 "installation and update of these items yourself.");
4126 installLocation = null;
4127 }
4128 }
4129 else {
4130 // In the absence of a preferred install location, just default to
4131 // the App-Profile
4132 installLocation = InstallLocations.get(KEY_APP_PROFILE);
4133 }
4134 }
4135 else {
4136 // This is an "upgrade", but not through the Update System, because the
4137 // Update code will not let an extension with an incompatible target
4138 // app version range through to this point. This is an "upgrade" in the
4139 // sense that the user found a different version of an installed extension
4140 // and installed it through the web interface, so we have metadata.
4141
4142 // If the location is different, return the preferred location rather than
4143 // the location of the currently installed version, because we may be in
4144 // the situation where an item is being installed into the global app
4145 // dir when there's a version in the profile dir.
4146 if (installLocation.name != aInstallLocationKey)
4147 installLocation = InstallLocations.get(aInstallLocationKey);
4148 }
4149 if (!installLocation.canAccess) {
4150 LOG("Install Location\"" + installLocation.name + "\" cannot be written " +
4151 "to with your access privileges. Installation will not proceed.");
4152 installLocation = null;
4153 }
4154 return installLocation;
4155 }
4156
4157 /**
4158 * Stages a XPI file in the default item location specified by other
4159 * applications when they registered with XulRunner if the item's
4160 * install manifest specified compatibility with them.
4161 */
stageXPIForOtherApps
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4162 function stageXPIForOtherApps(xpiFile, installData) {
4163 for (var i = 0; i < installData.targetApps.length; ++i) {
4164 var targetApp = installData.targetApps[i];
4165 if (targetApp.id != gApp.ID && targetApp.id != TOOLKIT_ID) {
4166 /* XXXben uncomment when this works!
4167 var settingsThingy = Cc[].
4168 getService(Ci.nsIXULRunnerSettingsThingy);
4169 try {
4170 var appPrefix = "SOFTWARE\\Mozilla\\XULRunner\\Applications\\";
4171 var branch = settingsThingy.getBranch(appPrefix + targetApp.id);
4172 var path = branch.getProperty("ExtensionsLocation");
4173 var destination = Cc["@mozilla.org/file/local;1"].
4174 createInstance(Ci.nsILocalFile);
4175 destination.initWithPath(path);
4176 xpiFile.copyTo(file, xpiFile.leafName);
4177 }
4178 catch (e) {
4179 }
4180 */
4181 }
4182 }
4183 }
4184
4185 /**
4186 * Extracts and then starts the install for extensions / themes contained
4187 * within a xpi.
4188 */
installMultiXPI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4189 function installMultiXPI(xpiFile, installData) {
4190 var fileURL = getURIFromFile(xpiFile).QueryInterface(Ci.nsIURL);
4191 if (fileURL.fileExtension.toLowerCase() != "xpi") {
4192 LOG("Invalid File Extension: Item: \"" + fileURL.fileName + "\" has an " +
4193 "invalid file extension. Only xpi file extensions are allowed for " +
4194 "multiple item packages.");
4195 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4196 showMessage("invalidFileExtTitle", [],
4197 "invalidFileExtMessage", [installData.name,
4198 fileURL.fileExtension,
4199 bundle.GetStringFromName("type-" + installData.type)]);
4200 return;
4201 }
4202
4203 try {
4204 var zipReader = getZipReaderForFile(xpiFile);
4205 }
4206 catch (e) {
4207 LOG("installMultiXPI: failed to open xpi file: " + xpiFile.path);
4208 throw e;
4209 }
4210
4211 var searchForEntries = ["*.xpi", "*.jar"];
4212 var files = [];
4213 for (var i = 0; i < searchForEntries.length; ++i) {
4214 var entries = zipReader.findEntries(searchForEntries[i]);
4215 while (entries.hasMore()) {
4216 var entryName = entries.getNext();
4217 var target = getFile(KEY_TEMPDIR, [entryName]);
4218 try {
4219 target.createUnique(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
4220 }
4221 catch (e) {
4222 LOG("installMultiXPI: failed to create target file for extraction " +
4223 " file = " + target.path + ", exception = " + e + "\n");
4224 }
4225 zipReader.extract(entryName, target);
4226 files.push(target);
4227 }
4228 }
4229 zipReader.close();
4230
4231 if (files.length == 0) {
4232 LOG("Multiple Item Package: Item: \"" + fileURL.fileName + "\" does " +
4233 "not contain a valid package to install.");
4234 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4235 showMessage("missingPackageFilesTitle",
4236 [bundle.GetStringFromName("type-" + installData.type)],
4237 "missingPackageFilesMessage", [installData.name,
4238 bundle.GetStringFromName("type-" + installData.type)]);
4239 return;
4240 }
4241
4242 for (i = 0; i < files.length; ++i) {
4243 em.installItemFromFileInternal(files[i], aInstallLocationKey, null);
4244 files[i].remove(false);
4245 }
4246 }
4247
4248 /**
4249 * An observer for the Extension Update System.
4250 * @constructor
4251 */
IncompatibleObserver
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4252 function IncompatibleObserver() {}
4253 IncompatibleObserver.prototype = {
4254 _xpi: null,
4255 _installManifest: null,
4256
4257 /**
4258 * Ask the Extension Update System if there are any version updates for
4259 * this item that will allow it to be compatible with this version of
4260 * the Application.
4261 * @param item
4262 * An nsIUpdateItem representing the item being installed.
4263 * @param installManifest
4264 * The Install Manifest datasource for the item.
4265 * @param xpiFile
4266 * The staged source XPI file that contains the item. Cleaned
4267 * up by this process.
4268 * @param installRDF
4269 * The install.rdf file that was extracted from the xpi.
4270 */
checkForUpdates
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4271 checkForUpdates: function(item, installManifest, xpiFile) {
4272 this._xpi = xpiFile;
4273 this._installManifest = installManifest;
4274
4275 for (var i = 0; i < em._installListeners.length; ++i)
4276 em._installListeners[i].onCompatibilityCheckStarted(item);
4277 em._compatibilityCheckCount++;
4278 em.update([item], 1, Ci.nsIExtensionManager.UPDATE_CHECK_COMPATIBILITY, this);
4279 },
4280
4281 /**
4282 * See nsIExtensionManager.idl
4283 */
onUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4284 onUpdateStarted: function() {
4285 LOG("Phone Home Listener: Update Started");
4286 },
4287
4288 /**
4289 * See nsIExtensionManager.idl
4290 */
onUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4291 onUpdateEnded: function() {
4292 LOG("Phone Home Listener: Update Ended");
4293 },
4294
4295 /**
4296 * See nsIExtensionManager.idl
4297 */
onAddonUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4298 onAddonUpdateStarted: function(addon) {
4299 if (!addon)
4300 throw Cr.NS_ERROR_INVALID_ARG;
4301
4302 LOG("Phone Home Listener: Update For " + addon.id + " started");
4303 em.datasource.addIncompatibleUpdateItem(addon.name, this._xpi.path,
4304 addon.type, addon.version);
4305 },
4306
4307 /**
4308 * See nsIExtensionManager.idl
4309 */
onAddonUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4310 onAddonUpdateEnded: function(addon, status) {
4311 if (!addon)
4312 throw Cr.NS_ERROR_INVALID_ARG;
4313
4314 LOG("Phone Home Listener: Update For " + addon.id + " ended, status = " + status);
4315 em.datasource.removeDownload(this._xpi.path);
4316 LOG("Version Check Phone Home Completed");
4317
4318 for (var i = 0; i < em._installListeners.length; ++i)
4319 em._installListeners[i].onCompatibilityCheckEnded(addon, status);
4320
4321 // Only compatibility updates (e.g. STATUS_VERSIONINFO) are currently
4322 // supported
4323 if (status == Ci.nsIAddonUpdateCheckListener.STATUS_VERSIONINFO) {
4324 em.datasource.setTargetApplicationInfo(addon.id,
4325 addon.targetAppID,
4326 addon.minAppVersion,
4327 addon.maxAppVersion,
4328 this._installManifest);
4329
4330 // Try and install again, but use the updated compatibility DB.
4331 // This will send out an apropriate onInstallEnded notification for us.
4332 em.installItemFromFileInternal(this._xpi, aInstallLocationKey,
4333 this._installManifest);
4334
4335 // Add the updated compatibility info to the datasource if done
4336 if (StartupCache.entries[aInstallLocationKey][addon.id].op == OP_NONE) {
4337 em.datasource.setTargetApplicationInfo(addon.id,
4338 addon.targetAppID,
4339 addon.minAppVersion,
4340 addon.maxAppVersion,
4341 null);
4342 }
4343 else { // needs a restart
4344 // Add updatedMinVersion and updatedMaxVersion so it can be used
4345 // to update the datasource during the installation or upgrade.
4346 em.datasource.setUpdatedTargetAppInfo(addon.id,
4347 addon.targetAppID,
4348 addon.minAppVersion,
4349 addon.maxAppVersion,
4350 null);
4351 }
4352 }
4353 else {
4354 em.datasource.removeDownload(this._xpi.path);
4355 showIncompatibleError(installData);
4356 LOG("Add-on " + addon.id + " is incompatible with " +
4357 BundleManager.appName + " " + gApp.version + ", Toolkit " +
4358 gApp.platformVersion + ". Remote compatibility check did not " +
4359 "resolve this.");
4360
4361 for (var i = 0; i < em._installListeners.length; ++i)
4362 em._installListeners[i].onInstallEnded(addon, INSTALLERROR_INCOMPATIBLE_VERSION);
4363
4364 // We are responsible for cleaning up this file!
4365 InstallLocations.get(aInstallLocationKey).removeFile(this._xpi);
4366 }
4367
4368 em._compatibilityCheckCount--;
4369 // If there are no more compatibility checks running and no downloads in
4370 // progress then the install operations are complete.
4371 if (em._compatibilityCheckCount == 0 && em._transactions.length == 0) {
4372 for (var i = 0; i < em._installListeners.length; ++i)
4373 em._installListeners[i].onInstallsCompleted();
4374 }
4375 },
4376
4377 QueryInterface: XPCOMUtils.generateQI([Ci.nsIAddonUpdateCheckListener])
4378 }
4379
4380 var shouldPhoneHomeIfNecessary = false;
4381 if (!aInstallManifest) {
4382 // If we were not called with an Install Manifest, we were called from
4383 // some other path than the Phone Home system, so we do want to phone
4384 // home if the version is incompatible. As this is the first point in the
4385 // install process we must notify observers here.
4386
4387 var addon = makeItem(getURIFromFile(aXPIFile).spec, "",
4388 aInstallLocationKey, "", "", "",
4389 getURIFromFile(aXPIFile).spec,
4390 "", "", "", "", 0, gApp.id);
4391 for (var i = 0; i < this._installListeners.length; ++i)
4392 this._installListeners[i].onInstallStarted(addon);
4393
4394 shouldPhoneHomeIfNecessary = true;
4395 var installManifest = null;
4396 var installManifestFile = extractRDFFileToTempDir(aXPIFile,
4397 FILE_INSTALL_MANIFEST,
4398 true);
4399 if (installManifestFile.exists()) {
4400 installManifest = getInstallManifest(installManifestFile);
4401 installManifestFile.remove(false);
4402 }
4403 if (!installManifest) {
4404 LOG("The Install Manifest supplied by this item is not well-formed. " +
4405 "Installation will not proceed.");
4406 for (var i = 0; i < this._installListeners.length; ++i)
4407 this._installListeners[i].onInstallEnded(addon, INSTALLERROR_INVALID_MANIFEST);
4408 return;
4409 }
4410 }
4411 else
4412 installManifest = aInstallManifest;
4413
4414 var installData = this._getInstallData(installManifest);
4415 // Recreate the add-on item with the full detail from the install manifest
4416 addon = makeItem(installData.id, installData.version,
4417 aInstallLocationKey,
4418 installData.currentApp ? installData.currentApp.minVersion : "",
4419 installData.currentApp ? installData.currentApp.maxVersion : "",
4420 installData.name,
4421 getURIFromFile(aXPIFile).spec,
4422 "", /* XPI Update Hash */
4423 "", /* Icon URL */
4424 installData.updateURL || "",
4425 installData.updateKey || "",
4426 installData.type,
4427 installData.currentApp ? installData.currentApp.id : "");
4428
4429 switch (installData.error) {
4430 case INSTALLERROR_INCOMPATIBLE_VERSION:
4431 // Since the caller cleans up |aXPIFile|, and we're not yet sure whether or
4432 // not we need it (we may need it if a remote version bump that makes it
4433 // compatible is discovered by the call home) - so we must stage it for
4434 // later ourselves.
4435 if (shouldPhoneHomeIfNecessary && installData.currentApp) {
4436 var installLocation = getInstallLocation(installData.id, aInstallLocationKey);
4437 if (!installLocation)
4438 return;
4439 var stagedFile = installLocation.stageFile(aXPIFile, installData.id);
4440 (new IncompatibleObserver(this)).checkForUpdates(addon, installManifest,
4441 stagedFile);
4442 // Return early to prevent deletion of the install manifest file.
4443 return;
4444 }
4445 else {
4446 // XXXben Look up XULRunnerSettingsThingy to see if there is a registered
4447 // app that can handle this item, if so just stage and don't show
4448 // this error!
4449 showIncompatibleError(installData);
4450 LOG("Add-on " + installData.id + " is incompatible with " +
4451 BundleManager.appName + " " + gApp.version + ", Toolkit " +
4452 gApp.platformVersion + ". Remote compatibility check was not performed.");
4453 }
4454 break;
4455 case INSTALLERROR_SUCCESS:
4456 // Installation of multiple extensions / themes contained within a single xpi.
4457 if (installData.type == Ci.nsIUpdateItem.TYPE_MULTI_XPI) {
4458 installMultiXPI(aXPIFile, installData);
4459 break;
4460 }
4461
4462 // Stage the extension's XPI so it can be extracted at the next restart.
4463 var installLocation = getInstallLocation(installData.id, aInstallLocationKey);
4464 if (!installLocation) {
4465 // No cleanup of any of the staged XPI files should be required here,
4466 // because this should only ever fail on the first recurse through
4467 // this function, BEFORE staging takes place... technically speaking
4468 // a location could become readonly during the phone home process,
4469 // but that's an edge case I don't care about.
4470 for (var i = 0; i < this._installListeners.length; ++i)
4471 this._installListeners[i].onInstallEnded(addon, INSTALLERROR_RESTRICTED);
4472 return;
4473 }
4474
4475 // Stage a copy of the XPI/JAR file for our own evil purposes...
4476 stagedFile = installLocation.stageFile(aXPIFile, installData.id);
4477
4478 var restartRequired = this.installRequiresRestart(installData.id,
4479 installData.type);
4480 // Determine which configuration function to use based on whether or not
4481 // there is data about this item in our datasource already - if there is
4482 // we want to upgrade, otherwise we install fresh.
4483 var ds = this.datasource;
4484 if (installData.id in ds.visibleItems && ds.visibleItems[installData.id]) {
4485 // We enter this function if any data corresponding to an existing GUID
4486 // is found, regardless of its Install Location. We need to check before
4487 // "upgrading" an item that Install Location of the new item is of equal
4488 // or higher priority than the old item, to make sure the datasource only
4489 // ever tracks metadata for active items.
4490 var oldInstallLocation = this.getInstallLocation(installData.id);
4491 if (oldInstallLocation.priority >= installLocation.priority) {
4492 this._upgradeItem(installManifest, installData.id, installLocation,
4493 installData.type);
4494 if (!restartRequired) {
4495 this._finalizeUpgrade(installData.id, installLocation);
4496 this._finalizeInstall(installData.id, stagedFile);
4497 }
4498 }
4499 }
4500 else {
4501 this._configureForthcomingItem(installManifest, installData.id,
4502 installLocation, installData.type);
4503 if (!restartRequired) {
4504 this._finalizeInstall(installData.id, stagedFile);
4505 if (installData.type == Ci.nsIUpdateItem.TYPE_THEME) {
4506 var internalName = this.datasource.getItemProperty(installData.id, "internalName");
4507 if (gPref.getBoolPref(PREF_EM_DSS_ENABLED)) {
4508 gPref.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, internalName);
4509 }
4510 else {
4511 gPref.setBoolPref(PREF_DSS_SWITCHPENDING, true);
4512 gPref.setCharPref(PREF_DSS_SKIN_TO_SELECT, internalName);
4513 }
4514 }
4515 }
4516 }
4517 this._updateManifests(restartRequired);
4518 break;
4519 case INSTALLERROR_INVALID_GUID:
4520 LOG("Invalid GUID: Item has GUID: \"" + installData.id + "\"" +
4521 " which is not well-formed.");
4522 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4523 showMessage("incompatibleTitle",
4524 [bundle.GetStringFromName("type-" + installData.type)],
4525 "invalidGUIDMessage", [installData.name, installData.id]);
4526 break;
4527 case INSTALLERROR_INVALID_VERSION:
4528 LOG("Invalid Version: Item: \"" + installData.id + "\" has version " +
4529 installData.version + " which is not well-formed.");
4530 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4531 showMessage("incompatibleTitle",
4532 [bundle.GetStringFromName("type-" + installData.type)],
4533 "invalidVersionMessage", [installData.name, installData.version]);
4534 break;
4535 case INSTALLERROR_INCOMPATIBLE_PLATFORM:
4536 const osABI = gOSTarget + "_" + gXPCOMABI;
4537 LOG("Incompatible Platform: Item: \"" + installData.id + "\" is not " +
4538 "compatible with '" + osABI + "'.");
4539 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4540 showMessage("incompatibleTitle",
4541 [bundle.GetStringFromName("type-" + installData.type)],
4542 "incompatiblePlatformMessage",
4543 [installData.name, BundleManager.appName, osABI]);
4544 break;
4545 case INSTALLERROR_BLOCKLISTED:
4546 LOG("Blocklisted Item: Item: \"" + installData.id + "\" version " +
4547 installData.version + " was not installed.");
4548 showBlocklistMessage([installData], true);
4549 break;
4550 case INSTALLERROR_INSECURE_UPDATE:
4551 LOG("No secure updates: Item: \"" + installData.id + "\" version " +
4552 installData.version + " was not installed.");
4553 var bundle = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
4554 showMessage("incompatibleTitle",
4555 [bundle.GetStringFromName("type-" + installData.type)],
4556 "insecureUpdateMessage", [installData.name]);
4557 break;
4558 default:
4559 break;
4560 }
4561
4562 // Check to see if this item supports other applications and in that case
4563 // stage the the XPI file in the location specified by those applications.
4564 stageXPIForOtherApps(aXPIFile, installData);
4565
4566 // The install of this item is complete, notify observers
4567 for (var i = 0; i < this._installListeners.length; ++i)
4568 this._installListeners[i].onInstallEnded(addon, installData.error);
4569 },
4570
4571 /**
4572 * Whether or not this type's installation/uninstallation requires
4573 * the application to be restarted.
4574 * @param id
4575 * The GUID of the item
4576 * @param type
4577 * The nsIUpdateItem type of the item
4578 * @returns true if installation of an item of this type requires a
4579 * restart.
4580 */
installRequiresRestart
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4581 installRequiresRestart: function(id, type) {
4582 switch (type) {
4583 case Ci.nsIUpdateItem.TYPE_THEME:
4584 var internalName = this.datasource.getItemProperty(id, "internalName");
4585 var needsRestart = false;
4586 if (gPref.prefHasUserValue(PREF_DSS_SKIN_TO_SELECT))
4587 needsRestart = internalName == gPref.getCharPref(PREF_DSS_SKIN_TO_SELECT);
4588 if (!needsRestart &&
4589 gPref.prefHasUserValue(PREF_GENERAL_SKINS_SELECTEDSKIN))
4590 needsRestart = internalName == gPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
4591 return needsRestart;
4592 }
4593 return ((type & Ci.nsIUpdateItem.TYPE_ADDON) > 0);
4594 },
4595
4596 /**
4597 * Perform initial configuration on an item that has just or will be
4598 * installed. This inserts the item into the appropriate container in the
4599 * datasource, so that the application UI shows the item even if it will
4600 * not actually be installed until the next restart.
4601 * @param installManifest
4602 * The Install Manifest datasource that describes this item.
4603 * @param id
4604 * The GUID of this item.
4605 * @param installLocation
4606 * The Install Location where this item is installed.
4607 * @param type
4608 * The nsIUpdateItem type of this item.
4609 */
_configureForthcomingItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4610 _configureForthcomingItem: function(installManifest, id, installLocation, type) {
4611 var ds = this.datasource;
4612 ds.updateVisibleList(id, installLocation.name, false);
4613
4614 var name = null;
4615 var localized = findClosestLocalizedResource(installManifest, gInstallManifestRoot);
4616 if (localized)
4617 name = installManifest.GetTarget(localized, EM_R("name"), true);
4618 else
4619 name = EM_L(getManifestProperty(installManifest, "name"));
4620
4621 var props = { name : name,
4622 version : EM_L(getManifestProperty(installManifest, "version")),
4623 newVersion : EM_L(getManifestProperty(installManifest, "version")),
4624 installLocation : EM_L(installLocation.name),
4625 type : EM_I(type),
4626 availableUpdateURL : null,
4627 availableUpdateHash : null,
4628 availableUpdateVersion: null,
4629 availableUpdateInfo : null };
4630 for (var p in props)
4631 ds.setItemProperty(id, EM_R(p), props[p]);
4632 ds.updateProperty(id, "availableUpdateURL");
4633
4634 this._setOp(id, OP_NEEDS_INSTALL);
4635
4636 // Insert it into the child list NOW rather than later because:
4637 // - extensions installed using the command line need to be a member
4638 // of a container during the install phase for the code to be able
4639 // to identify profile vs. global
4640 // - extensions installed through the UI should show some kind of
4641 // feedback to indicate their presence is forthcoming (i.e. they
4642 // will be available after a restart).
4643 ds.insertItemIntoContainer(id);
4644
4645 this._notifyAction(id, EM_ITEM_INSTALLED);
4646 },
4647
4648 /**
4649 * Perform configuration on an item that has just or will be upgraded.
4650 * @param installManifest
4651 * The Install Manifest datasource that describes this item.
4652 * @param itemID
4653 * The GUID of this item.
4654 * @param installLocation
4655 * The Install Location where this item is installed.
4656 * @param type
4657 * The nsIUpdateItem type of this item.
4658 */
_upgradeItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4659 _upgradeItem: function (installManifest, id, installLocation, type) {
4660 // Don't change any props that would need to be reset if the install fails.
4661 // They will be reset as appropriate by the upgrade/install process.
4662 var ds = this.datasource;
4663 ds.updateVisibleList(id, installLocation.name, false);
4664 var props = { installLocation : EM_L(installLocation.name),
4665 type : EM_I(type),
4666 newVersion : EM_L(getManifestProperty(installManifest, "version")),
4667 availableUpdateURL : null,
4668 availableUpdateHash : null,
4669 availableUpdateVersion : null,
4670 availableUpdateInfo : null };
4671 for (var p in props)
4672 ds.setItemProperty(id, EM_R(p), props[p]);
4673 ds.updateProperty(id, "availableUpdateURL");
4674
4675 this._setOp(id, OP_NEEDS_UPGRADE);
4676 this._notifyAction(id, EM_ITEM_UPGRADED);
4677 },
4678
4679 /**
4680 * Completes an Extension's installation.
4681 * @param id
4682 * The GUID of the Extension to install.
4683 * @param file
4684 * The XPI/JAR file to install from. If this is null, we try to
4685 * determine the stage file location from the ID.
4686 */
_finalizeInstall
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4687 _finalizeInstall: function(id, file) {
4688 var ds = this.datasource;
4689 var type = ds.getItemProperty(id, "type");
4690 if (id == 0 || id == -1) {
4691 ds.removeCorruptItem(id);
4692 return;
4693 }
4694 var installLocation = this.getInstallLocation(id);
4695 if (!installLocation) {
4696 // If the install location is null, that means we've reached the finalize
4697 // state without the item ever having metadata added for it, which implies
4698 // bogus data in the Startup Cache. Clear the entries and don't do anything
4699 // else.
4700 var entries = StartupCache.findEntries(id);
4701 for (var i = 0; i < entries.length; ++i) {
4702 var location = InstallLocations.get(entries[i].location);
4703 StartupCache.clearEntry(location, id);
4704 PendingOperations.clearItem(OP_NEEDS_INSTALL, id);
4705 }
4706 return;
4707 }
4708 var itemLocation = installLocation.getItemLocation(id);
4709
4710 if (!file && "stageFile" in installLocation)
4711 file = installLocation.getStageFile(id);
4712
4713 // If |file| is null or does not exist, the installer assumes the item is
4714 // a dropped-in directory.
4715 var installer = new Installer(this.datasource, id, installLocation, type);
4716 installer.installFromFile(file);
4717
4718 // If the file was staged, we must clean it up ourselves, otherwise the
4719 // EM caller is responsible for doing so (e.g. XPInstall)
4720 if (file)
4721 installLocation.removeFile(file);
4722
4723 // Clear the op flag from the Startup Cache and Pending Operations sets
4724 StartupCache.put(installLocation, id, OP_NONE, true);
4725 PendingOperations.clearItem(OP_NEEDS_INSTALL, id);
4726 },
4727
4728 /**
4729 * Removes an item's metadata in preparation for an upgrade-install.
4730 * @param id
4731 * The GUID of the item to uninstall.
4732 * @param installLocation
4733 * The nsIInstallLocation of the item
4734 */
_finalizeUpgrade
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4735 _finalizeUpgrade: function(id, installLocation) {
4736 // Retrieve the item properties *BEFORE* we clean the resource!
4737 var ds = this.datasource;
4738
4739 var stagedFile = null;
4740 if ("getStageFile" in installLocation)
4741 stagedFile = installLocation.getStageFile(id);
4742
4743 if (stagedFile)
4744 var installRDF = extractRDFFileToTempDir(stagedFile, FILE_INSTALL_MANIFEST, true);
4745 else
4746 installRDF = installLocation.getItemFile(id, FILE_INSTALL_MANIFEST);
4747 if (installRDF.exists()) {
4748 var installManifest = getInstallManifest(installRDF);
4749 if (installManifest) {
4750 var type = getAddonTypeFromInstallManifest(installManifest);
4751 var userDisabled = ds.getItemProperty(id, "userDisabled") == "true";
4752
4753 // Clean the item resource
4754 ds.removeItemMetadata(id);
4755 // Now set up the properties on the item to mimic an item in its
4756 // "initial state" for installation.
4757 this._configureForthcomingItem(installManifest, id, installLocation,
4758 type);
4759 if (userDisabled)
4760 ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
4761 }
4762 if (stagedFile)
4763 installRDF.remove(false);
4764 }
4765 // Clear the op flag from the Pending Operations set. Do NOT clear op flag in
4766 // the startup cache since this may have been reset to OP_NEEDS_INSTALL by
4767 // |_configureForthcomingItem|.
4768 PendingOperations.clearItem(OP_NEEDS_UPGRADE, id);
4769 },
4770
4771 /**
4772 * Completes an item's uninstallation.
4773 * @param id
4774 * The GUID of the item to uninstall.
4775 */
_finalizeUninstall
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4776 _finalizeUninstall: function(id) {
4777 var ds = this.datasource;
4778
4779 var installLocation = this.getInstallLocation(id);
4780 if (!installLocation.itemIsManagedIndependently(id)) {
4781 try {
4782 // Having a callback that does nothing just causes the directory to be
4783 // removed.
4784 safeInstallOperation(id, installLocation,
callback
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4785 { data: null, callback: function() { } });
4786 }
4787 catch (e) {
4788 ERROR("_finalizeUninstall: failed to remove directory for item: " + id +
4789 " at Install Location: " + installLocation.name + ", rolling back uninstall");
4790 var manifest = installLocation.getItemFile(id, "FILE_INSTALL_MANIFEST");
4791 // If there is no manifest then either the rollback failed, or there was
4792 // no manifest in the first place. Either way this item is now invalid
4793 // and we shouldn't try to re-install it.
4794 if (manifest.exists()) {
4795 // Removal of the files failed, reset the uninstalled flag and rewrite
4796 // the install manifests so this item's components are registered.
4797 // Clear the op flag from the Startup Cache
4798 StartupCache.put(installLocation, id, OP_NONE, true);
4799 var restartRequired = this.installRequiresRestart(id, ds.getItemProperty(id, "type"))
4800 this._updateManifests(restartRequired);
4801 return;
4802 }
4803 }
4804 }
4805 else if (installLocation.name == KEY_APP_PROFILE ||
4806 installLocation.name == KEY_APP_GLOBAL ||
4807 installLocation.name == KEY_APP_SYSTEM_USER) {
4808 // Check for a pointer file and remove it if it exists
4809 var pointerFile = installLocation.location.clone();
4810 pointerFile.append(id);
4811 if (pointerFile.exists() && !pointerFile.isDirectory())
4812 pointerFile.remove(false);
4813 }
4814
4815 // Clean the item resource
4816 ds.removeItemMetadata(id);
4817
4818 // Do this LAST since inferences are made about an item based on
4819 // what container it's in.
4820 ds.removeItemFromContainer(id);
4821
4822 // Clear the op flag from the Startup Cache and the Pending Operations set.
4823 StartupCache.clearEntry(installLocation, id);
4824 PendingOperations.clearItem(OP_NEEDS_UNINSTALL, id);
4825 },
4826
4827 /**
4828 * Uninstalls an item. If the uninstallation cannot be performed immediately
4829 * it is scheduled for the next restart.
4830 * @param id
4831 * The GUID of the item to uninstall.
4832 */
uninstallItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4833 uninstallItem: function(id) {
4834 var ds = this.datasource;
4835 ds.updateDownloadState(PREFIX_ITEM_URI + id, null);
4836 if (!ds.isDownloadItem(id)) {
4837 var opType = ds.getItemProperty(id, "opType");
4838 var installLocation = this.getInstallLocation(id);
4839 // Removes any staged xpis for this item.
4840 if (opType == OP_NEEDS_UPGRADE || opType == OP_NEEDS_INSTALL) {
4841 var stageFile = installLocation.getStageFile(id);
4842 if (stageFile)
4843 installLocation.removeFile(stageFile);
4844 }
4845 // Addons with an opType of OP_NEEDS_INSTALL only have a staged xpi file
4846 // and are removed immediately since the uninstall can't be canceled.
4847 if (opType == OP_NEEDS_INSTALL) {
4848 ds.removeItemMetadata(id);
4849 ds.removeItemFromContainer(id);
4850 ds.updateVisibleList(id, null, true);
4851 StartupCache.clearEntry(installLocation, id);
4852 this._updateManifests(false);
4853 }
4854 else {
4855 if (opType == OP_NEEDS_UPGRADE)
4856 ds.setItemProperty(id, "newVersion", null);
4857 this._setOp(id, OP_NEEDS_UNINSTALL);
4858 var type = ds.getItemProperty(id, "type");
4859 var restartRequired = this.installRequiresRestart(id, type);
4860 if (!restartRequired) {
4861 this._finalizeUninstall(id);
4862 this._updateManifests(restartRequired);
4863 }
4864 }
4865 }
4866 else {
4867 // Bad download entry - uri is url, e.g. "http://www.foo.com/test.xpi"
4868 // ... just remove it from the list.
4869 ds.removeCorruptDLItem(id);
4870 }
4871
4872 this._notifyAction(id, EM_ITEM_UNINSTALLED);
4873 },
4874
4875 /* See nsIExtensionManager.idl */
cancelInstallItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4876 cancelInstallItem: function(id) {
4877 var ds = this.datasource;
4878 var opType = ds.getItemProperty(id, "opType");
4879 if (opType != OP_NEEDS_UPGRADE && opType != OP_NEEDS_INSTALL)
4880 return;
4881
4882 ds.updateDownloadState(PREFIX_ITEM_URI + id, null);
4883 var installLocation = this.getInstallLocation(id);
4884 // Removes any staged xpis for this item.
4885 var stageFile = installLocation.getStageFile(id);
4886 if (stageFile)
4887 installLocation.removeFile(stageFile);
4888 // Addons with an opType of OP_NEEDS_INSTALL only have a staged xpi file
4889 // and just need to be removed completely from the ds.
4890 if (opType == OP_NEEDS_INSTALL) {
4891 ds.removeItemMetadata(id);
4892 ds.removeItemFromContainer(id);
4893 ds.updateVisibleList(id, null, true);
4894 StartupCache.clearEntry(installLocation, id);
4895 this._updateManifests(false);
4896 this._notifyAction(id, EM_ITEM_CANCEL);
4897 }
4898 else {
4899 // Clear upgrade information and reset any request to enable/disable.
4900 ds.setItemProperty(id, EM_R("newVersion"), null);
4901 var appDisabled = ds.getItemProperty(id, "appDisabled");
4902 var userDisabled = ds.getItemProperty(id, "userDisabled");
4903 if (appDisabled == "true" || appDisabled == OP_NONE && userDisabled == OP_NONE) {
4904 this._setOp(id, OP_NONE);
4905 this._notifyAction(id, EM_ITEM_CANCEL);
4906 }
4907 else if (appDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NEEDS_DISABLE) {
4908 this._setOp(id, OP_NEEDS_DISABLE);
4909 this._notifyAction(id, EM_ITEM_DISABLED);
4910 }
4911 else if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE) {
4912 this._setOp(id, OP_NEEDS_ENABLE);
4913 this._notifyAction(id, EM_ITEM_ENABLED);
4914 }
4915 else {
4916 this._setOp(id, OP_NONE);
4917 this._notifyAction(id, EM_ITEM_CANCEL);
4918 }
4919 }
4920 },
4921
4922 /**
4923 * Cancels a pending uninstall of an item
4924 * @param id
4925 * The ID of the item.
4926 */
cancelUninstallItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4927 cancelUninstallItem: function(id) {
4928 var ds = this.datasource;
4929 var appDisabled = ds.getItemProperty(id, "appDisabled");
4930 var userDisabled = ds.getItemProperty(id, "userDisabled");
4931 if (appDisabled == "true" || appDisabled == OP_NONE && userDisabled == OP_NONE) {
4932 this._setOp(id, OP_NONE);
4933 this._notifyAction(id, EM_ITEM_CANCEL);
4934 }
4935 else if (appDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NEEDS_DISABLE) {
4936 this._setOp(id, OP_NEEDS_DISABLE);
4937 this._notifyAction(id, EM_ITEM_DISABLED);
4938 }
4939 else if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE) {
4940 this._setOp(id, OP_NEEDS_ENABLE);
4941 this._notifyAction(id, EM_ITEM_ENABLED);
4942 }
4943 else {
4944 this._setOp(id, OP_NONE);
4945 this._notifyAction(id, EM_ITEM_CANCEL);
4946 }
4947 },
4948
4949 /**
4950 * Sets the pending operation for a visible item.
4951 * @param id
4952 * The GUID of the item
4953 * @param op
4954 * The name of the operation to be performed
4955 */
_setOp
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4956 _setOp: function(id, op) {
4957 var location = this.getInstallLocation(id);
4958 StartupCache.put(location, id, op, true);
4959 PendingOperations.addItem(op, { locationKey: location.name, id: id });
4960 var ds = this.datasource;
4961 if (op == OP_NEEDS_INSTALL || op == OP_NEEDS_UPGRADE)
4962 ds.updateDownloadState(PREFIX_ITEM_URI + id, "success");
4963
4964 ds.updateProperty(id, "opType");
4965 ds.updateProperty(id, "updateable");
4966 ds.updateProperty(id, "satisfiesDependencies");
4967 var restartRequired = this.installRequiresRestart(id, ds.getItemProperty(id, "type"))
4968 this._updateDependentItemsForID(id);
4969 this._updateManifests(restartRequired);
4970 },
4971
4972 /**
4973 * Note on appDisabled and userDisabled property arcs.
4974 * The appDisabled and userDisabled RDF property arcs are used to store
4975 * the pending operation for app disabling and user disabling for an item as
4976 * well as the user and app disabled status after the pending operation has
4977 * been completed upon restart. When the appDisabled value changes the value
4978 * of userDisabled is reset to prevent the state of widgets and status
4979 * messages from being in an incorrect state.
4980 */
4981
4982 /**
4983 * Enables an item for the application (e.g. the item satisfies all
4984 * requirements like app compatibility for it to be enabled). The appDisabled
4985 * property arc will be removed if the item will be app disabled on next
4986 * restart to cancel the app disabled operation for the item otherwise the
4987 * property value will be set to OP_NEEDS_ENABLE. The item's pending
4988 * operations are then evaluated in order to set the operation to perform
4989 * and notify the observers if the operation has been changed.
4990 * See "Note on appDisabled and userDisabled property arcs" above.
4991 * @param id
4992 * The ID of the item to be enabled by the application.
4993 */
_appEnableItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
4994 _appEnableItem: function(id) {
4995 var ds = this.datasource;
4996 var appDisabled = ds.getItemProperty(id, "appDisabled");
4997 if (appDisabled == OP_NONE || appDisabled == OP_NEEDS_ENABLE)
4998 return;
4999
5000 var opType = ds.getItemProperty(id, "opType");
5001 var userDisabled = ds.getItemProperty(id, "userDisabled");
5002 // reset user disabled if it has a pending operation to prevent the ui
5003 // state from getting confused as to an item's current state.
5004 if (userDisabled == OP_NEEDS_DISABLE)
5005 ds.setItemProperty(id, EM_R("userDisabled"), null);
5006 else if (userDisabled == OP_NEEDS_ENABLE)
5007 ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
5008
5009 if (appDisabled == OP_NEEDS_DISABLE)
5010 ds.setItemProperty(id, EM_R("appDisabled"), null);
5011 else if (appDisabled == "true")
5012 ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_ENABLE));
5013
5014 // Don't set a new operation when there is a pending uninstall operation.
5015 if (opType == OP_NEEDS_UNINSTALL) {
5016 this._updateDependentItemsForID(id);
5017 return;
5018 }
5019
5020 var operation, action;
5021 // if this item is already enabled or user disabled don't set a pending
5022 // operation - instead immediately enable it and reset the operation type
5023 // if needed.
5024 if (appDisabled == OP_NEEDS_DISABLE || appDisabled == OP_NONE ||
5025 userDisabled == "true") {
5026 if (opType != OP_NONE) {
5027 operation = OP_NONE;
5028 action = EM_ITEM_CANCEL;
5029 }
5030 }
5031 else {
5032 if (opType != OP_NEEDS_ENABLE) {
5033 operation = OP_NEEDS_ENABLE;
5034 action = EM_ITEM_ENABLED;
5035 }
5036 }
5037
5038 if (action) {
5039 this._setOp(id, operation);
5040 this._notifyAction(id, action);
5041 }
5042 else {
5043 ds.updateProperty(id, "satisfiesDependencies");
5044 this._updateDependentItemsForID(id);
5045 }
5046 },
5047
5048 /**
5049 * Disables an item for the application (e.g. the item doesn't satisfy all
5050 * requirements like app compatibility for it to be enabled). The appDisabled
5051 * property arc will be set to true if the item will be app enabled on next
5052 * restart to cancel the app enabled operation for the item otherwise the
5053 * property value will be set to OP_NEEDS_DISABLE. The item's pending
5054 * operations are then evaluated in order to set the operation to perform
5055 * and notify the observers if the operation has been changed.
5056 * See "Note on appDisabled and userDisabled property arcs" above.
5057 * @param id
5058 * The ID of the item to be disabled by the application.
5059 */
_appDisableItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5060 _appDisableItem: function(id) {
5061 var ds = this.datasource;
5062 var appDisabled = ds.getItemProperty(id, "appDisabled");
5063 if (appDisabled == "true" || appDisabled == OP_NEEDS_DISABLE)
5064 return;
5065
5066 var opType = ds.getItemProperty(id, "opType");
5067 var userDisabled = ds.getItemProperty(id, "userDisabled");
5068
5069 // reset user disabled if it has a pending operation to prevent the ui
5070 // state from getting confused as to an item's current state.
5071 if (userDisabled == OP_NEEDS_DISABLE)
5072 ds.setItemProperty(id, EM_R("userDisabled"), null);
5073 else if (userDisabled == OP_NEEDS_ENABLE)
5074 ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
5075
5076 if (appDisabled == OP_NEEDS_ENABLE || userDisabled == OP_NEEDS_ENABLE ||
5077 ds.getItemProperty(id, "userDisabled") == "true")
5078 ds.setItemProperty(id, EM_R("appDisabled"), EM_L("true"));
5079 else if (appDisabled == OP_NONE)
5080 ds.setItemProperty(id, EM_R("appDisabled"), EM_L(OP_NEEDS_DISABLE));
5081
5082 // Don't set a new operation when there is a pending uninstall operation.
5083 if (opType == OP_NEEDS_UNINSTALL) {
5084 this._updateDependentItemsForID(id);
5085 return;
5086 }
5087
5088 var operation, action;
5089 // if this item is already disabled don't set a pending operation - instead
5090 // immediately disable it and reset the operation type if needed.
5091 if (appDisabled == OP_NEEDS_ENABLE || appDisabled == "true" ||
5092 userDisabled == OP_NEEDS_ENABLE || userDisabled == "true") {
5093 if (opType != OP_NONE) {
5094 operation = OP_NONE;
5095 action = EM_ITEM_CANCEL;
5096 }
5097 }
5098 else {
5099 if (opType != OP_NEEDS_DISABLE) {
5100 operation = OP_NEEDS_DISABLE;
5101 action = EM_ITEM_DISABLED;
5102 }
5103 }
5104
5105 if (action) {
5106 this._setOp(id, operation);
5107 this._notifyAction(id, action);
5108 }
5109 else {
5110 ds.updateProperty(id, "satisfiesDependencies");
5111 this._updateDependentItemsForID(id);
5112 }
5113 },
5114
5115 /**
5116 * Sets an item to be enabled by the user. If the item is already enabled this
5117 * clears the needs-enable operation for the next restart.
5118 * See "Note on appDisabled and userDisabled property arcs" above.
5119 * @param id
5120 * The ID of the item to be enabled by the user.
5121 */
enableItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5122 enableItem: function(id) {
5123 var ds = this.datasource;
5124 var opType = ds.getItemProperty(id, "opType");
5125 var appDisabled = ds.getItemProperty(id, "appDisabled");
5126 var userDisabled = ds.getItemProperty(id, "userDisabled");
5127
5128 var operation, action;
5129 // if this item is already enabled don't set a pending operation - instead
5130 // immediately enable it and reset the operation type if needed.
5131 if (appDisabled == OP_NONE &&
5132 userDisabled == OP_NEEDS_DISABLE || userDisabled == OP_NONE) {
5133 if (userDisabled == OP_NEEDS_DISABLE)
5134 ds.setItemProperty(id, EM_R("userDisabled"), null);
5135 if (opType != OP_NONE) {
5136 operation = OP_NONE;
5137 action = EM_ITEM_CANCEL;
5138 }
5139 }
5140 else {
5141 if (userDisabled == "true")
5142 ds.setItemProperty(id, EM_R("userDisabled"), EM_L(OP_NEEDS_ENABLE));
5143 if (opType != OP_NEEDS_ENABLE) {
5144 operation = OP_NEEDS_ENABLE;
5145 action = EM_ITEM_ENABLED;
5146 }
5147 }
5148
5149 if (action) {
5150 this._setOp(id, operation);
5151 this._notifyAction(id, action);
5152 }
5153 else {
5154 ds.updateProperty(id, "satisfiesDependencies");
5155 this._updateDependentItemsForID(id);
5156 }
5157 },
5158
5159 /**
5160 * Sets an item to be disabled by the user. If the item is already disabled
5161 * this clears the needs-disable operation for the next restart.
5162 * See "Note on appDisabled and userDisabled property arcs" above.
5163 * @param id
5164 * The ID of the item to be disabled by the user.
5165 */
disableItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5166 disableItem: function(id) {
5167 var ds = this.datasource;
5168 var opType = ds.getItemProperty(id, "opType");
5169 var appDisabled = ds.getItemProperty(id, "appDisabled");
5170 var userDisabled = ds.getItemProperty(id, "userDisabled");
5171
5172 var operation, action;
5173 // if this item is already disabled don't set a pending operation - instead
5174 // immediately disable it and reset the operation type if needed.
5175 if (userDisabled == OP_NEEDS_ENABLE || userDisabled == "true" ||
5176 appDisabled == OP_NEEDS_ENABLE) {
5177 if (userDisabled != "true")
5178 ds.setItemProperty(id, EM_R("userDisabled"), EM_L("true"));
5179 if (opType != OP_NONE) {
5180 operation = OP_NONE;
5181 action = EM_ITEM_CANCEL;
5182 }
5183 }
5184 else {
5185 if (userDisabled == OP_NONE)
5186 ds.setItemProperty(id, EM_R("userDisabled"), EM_L(OP_NEEDS_DISABLE));
5187 if (opType != OP_NEEDS_DISABLE) {
5188 operation = OP_NEEDS_DISABLE;
5189 action = EM_ITEM_DISABLED;
5190 }
5191 }
5192
5193 if (action) {
5194 this._setOp(id, operation);
5195 this._notifyAction(id, action);
5196 }
5197 else {
5198 ds.updateProperty(id, "satisfiesDependencies");
5199 this._updateDependentItemsForID(id);
5200 }
5201 },
5202
5203 /**
5204 * Determines whether an item should be disabled by the application.
5205 * @param id
5206 * The ID of the item to check
5207 */
_isUsableItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5208 _isUsableItem: function(id) {
5209 var ds = this.datasource;
5210 /* If we're not compatibility checking or if the item is compatible
5211 * and if it isn't blocklisted and has all dependencies satisfied then
5212 * proceed to the security check */
5213 if ((!gCheckCompatibility || ds.getItemProperty(id, "compatible") == "true") &&
5214 ds.getItemProperty(id, "blocklisted") == "false" &&
5215 ds.getItemProperty(id, "satisfiesDependencies") == "true") {
5216
5217 // appManaged items aren't updated so no need to check update security.
5218 if (ds.getItemProperty(id, "appManaged") == "true")
5219 return true;
5220
5221 /* If we are not ignoring update security then check that the item has
5222 * a secure update mechanism */
5223 return (!gCheckUpdateSecurity ||
5224 ds.getItemProperty(id, "providesUpdatesSecurely") == "true");
5225 }
5226 return false;
5227 },
5228
5229 /**
5230 * Sets an item's dependent items disabled state for the app based on whether
5231 * its dependencies are met and the item is compatible.
5232 * @param id
5233 * The ID of the item whose dependent items will be checked
5234 */
_updateDependentItemsForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5235 _updateDependentItemsForID: function(id) {
5236 var ds = this.datasource;
5237 var dependentItems = this.getDependentItemListForID(id, true, { });
5238 for (var i = 0; i < dependentItems.length; ++i) {
5239 var dependentID = dependentItems[i].id;
5240 ds.updateProperty(dependentID, "satisfiesDependencies");
5241 if (this._isUsableItem(dependentID))
5242 this._appEnableItem(dependentID);
5243 else
5244 this._appDisableItem(dependentID);
5245 }
5246 },
5247
5248 /**
5249 * Notify observers of a change to an item that has been requested by the
5250 * user.
5251 */
_notifyAction
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5252 _notifyAction: function(id, reason) {
5253 gOS.notifyObservers(this.datasource.getItemForID(id),
5254 EM_ACTION_REQUESTED_TOPIC, reason);
5255 },
5256
5257 /**
5258 * See nsIExtensionManager.idl
5259 */
update
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5260 update: function(items, itemCount, updateCheckType, listener) {
5261 for (i = 0; i < itemCount; ++i) {
5262 var currItem = items[i];
5263 if (!currItem)
5264 throw Cr.NS_ERROR_ILLEGAL_VALUE;
5265 }
5266
5267 if (items.length == 0)
5268 items = this.getItemList(Ci.nsIUpdateItem.TYPE_ANY, { });
5269
5270 var updater = new ExtensionItemUpdater(this);
5271 updater.checkForUpdates(items, items.length, updateCheckType, listener);
5272 },
5273
5274
5275 /**
5276 * Checks for changes to the blocklist using the local blocklist file,
5277 * application disables / enables items that have been added / removed from
5278 * the blocklist, and if there are additions to the blocklist this will
5279 * inform the user by displaying a list of the items added.
5280 *
5281 * XXXrstrong - this method is not terribly useful and was added so we can
5282 * trigger this check from the additional timer used by blocklisting.
5283 */
checkForBlocklistChanges
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5284 checkForBlocklistChanges: function() {
5285 var ds = this.datasource;
5286 var items = this.getItemList(Ci.nsIUpdateItem.TYPE_ANY, { });
5287 for (var i = 0; i < items.length; ++i) {
5288 var id = items[i].id;
5289 ds.updateProperty(id, "blocklisted");
5290 if (this._isUsableItem(id))
5291 this._appEnableItem(id);
5292 }
5293
5294 items = ds.getBlocklistedItemList(null, null, Ci.nsIUpdateItem.TYPE_ANY,
5295 false);
5296 for (i = 0; i < items.length; ++i)
5297 this._appDisableItem(items[i].id);
5298
5299 // show the blocklist notification window if there are new blocklist items.
5300 if (items.length > 0)
5301 showBlocklistMessage(items, false);
5302 },
5303
5304 /**
5305 * @returns An enumeration of all registered Install Locations.
5306 */
get_installLocations
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5307 get installLocations () {
5308 return InstallLocations.enumeration;
5309 },
5310
5311 /**
5312 * Gets the Install Location where a visible Item is stored.
5313 * @param id
5314 * The GUID of the item to locate an Install Location for.
5315 * @returns The Install Location object where the item is stored.
5316 */
getInstallLocation
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5317 getInstallLocation: function(id) {
5318 var key = this.datasource.visibleItems[id];
5319 return key ? InstallLocations.get(this.datasource.visibleItems[id]) : null;
5320 },
5321
5322 /**
5323 * Gets a nsIUpdateItem for the item with the specified id.
5324 * @param id
5325 * The GUID of the item to construct a nsIUpdateItem for.
5326 * @returns The nsIUpdateItem representing the item.
5327 */
getItemForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5328 getItemForID: function(id) {
5329 return this.datasource.getItemForID(id);
5330 },
5331
5332 /**
5333 * Retrieves a list of installed nsIUpdateItems of items that are dependent
5334 * on another item.
5335 * @param id
5336 * The ID of the item that other items depend on.
5337 * @param includeDisabled
5338 * Whether to include disabled items in the set returned.
5339 * @param countRef
5340 * The XPCJS reference to the number of items returned.
5341 * @returns An array of installed nsIUpdateItems that depend on the item
5342 * specified by the id parameter.
5343 */
getDependentItemListForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5344 getDependentItemListForID: function(id, includeDisabled, countRef) {
5345 return this.datasource.getDependentItemListForID(id, includeDisabled, countRef);
5346 },
5347
5348 /**
5349 * Retrieves a list of nsIUpdateItems of items matching the specified type.
5350 * @param type
5351 * The type of item to return.
5352 * @param countRef
5353 * The XPCJS reference to the number of items returned.
5354 * @returns An array of nsIUpdateItems matching the id/type filter.
5355 */
getItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5356 getItemList: function(type, countRef) {
5357 return this.datasource.getItemList(type, countRef);
5358 },
5359
5360 /* See nsIExtensionManager.idl */
getIncompatibleItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5361 getIncompatibleItemList: function(id, appVersion, platformVersion, type, includeDisabled,
5362 countRef) {
5363 var items = this.datasource.getIncompatibleItemList(id, appVersion ? appVersion : undefined,
5364 platformVersion ? platformVersion : undefined,
5365 type, includeDisabled);
5366 countRef.value = items.length;
5367 return items;
5368 },
5369
5370 /**
5371 * Move an Item to the index of another item in its container.
5372 * @param movingID
5373 * The ID of the item to be moved.
5374 * @param destinationID
5375 * The ID of an item to move another item to.
5376 */
moveToIndexOf
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5377 moveToIndexOf: function(movingID, destinationID) {
5378 this.datasource.moveToIndexOf(movingID, destinationID);
5379 },
5380
5381 /**
5382 * Sorts addons of the specified type by the specified property starting from
5383 * the top of their container. If the addons are already sorted then no action
5384 * is performed.
5385 * @param type
5386 * The nsIUpdateItem type of the items to sort.
5387 * @param propertyName
5388 * The RDF property name used for sorting.
5389 * @param isAscending
5390 * true to sort ascending and false to sort descending
5391 */
sortTypeByProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5392 sortTypeByProperty: function(type, propertyName, isAscending) {
5393 this.datasource.sortTypeByProperty(type, propertyName, isAscending);
5394 },
5395
5396 /////////////////////////////////////////////////////////////////////////////
5397 // Downloads
5398 _transactions: [],
5399 _downloadCount: 0,
5400 _compatibilityCheckCount: 0,
5401
5402 /**
5403 * Ask the user if they really want to quit the application, since this will
5404 * cancel one or more Extension/Theme downloads.
5405 * @param subject
5406 * A nsISupportsPRBool which this function sets to false if the user
5407 * wishes to cancel all active downloads and quit the application,
5408 * false otherwise.
5409 */
_confirmCancelDownloadsOnQuit
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5410 _confirmCancelDownloadsOnQuit: function(subject) {
5411 // If user has already dismissed quit request, then do nothing
5412 if ((subject instanceof Ci.nsISupportsPRBool) && subject.data)
5413 return;
5414
5415 if (this._downloadCount > 0) {
5416 // The observers will be notified again after this so set the download
5417 // count to 0 to prevent this dialog from being displayed again.
5418 this._downloadCount = 0;
5419 var result;
5420 //@line 5565 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
5421 result = this._confirmCancelDownloads(this._downloadCount,
5422 "quitCancelDownloadsAlertTitle",
5423 "quitCancelDownloadsAlertMsgMacMultiple",
5424 "quitCancelDownloadsAlertMsgMac",
5425 "dontQuitButtonMac");
5426 //@line 5571 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
5427 if (subject instanceof Ci.nsISupportsPRBool)
5428 subject.data = result;
5429 }
5430 },
5431
5432 /**
5433 * Ask the user if they really want to go offline, since this will cancel
5434 * one or more Extension/Theme downloads.
5435 * @param subject
5436 * A nsISupportsPRBool which this function sets to false if the user
5437 * wishes to cancel all active downloads and go offline, false
5438 * otherwise.
5439 */
_confirmCancelDownloadsOnOffline
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5440 _confirmCancelDownloadsOnOffline: function(subject) {
5441 if (this._downloadCount > 0) {
5442 result = this._confirmCancelDownloads(this._downloadCount,
5443 "offlineCancelDownloadsAlertTitle",
5444 "offlineCancelDownloadsAlertMsgMultiple",
5445 "offlineCancelDownloadsAlertMsg",
5446 "dontGoOfflineButton");
5447 if (subject instanceof Ci.nsISupportsPRBool)
5448 subject.data = result;
5449 }
5450 },
5451
5452 /**
5453 * Ask the user whether or not they wish to cancel the Extension/Theme
5454 * downloads which are currently under way.
5455 * @param count
5456 * The number of active downloads.
5457 * @param title
5458 * The key of the title for the message box to be displayed
5459 * @param cancelMessageMultiple
5460 * The key of the message to be displayed in the message box
5461 * when there are > 1 active downloads.
5462 * @param cancelMessageSingle
5463 * The key of the message to be displayed in the message box
5464 * when there is just one active download.
5465 * @param dontCancelButton
5466 * The key of the label to be displayed on the "Don't Cancel
5467 * Downloads" button.
5468 */
_confirmCancelDownloads
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5469 _confirmCancelDownloads: function(count, title, cancelMessageMultiple,
5470 cancelMessageSingle, dontCancelButton) {
5471 var bundle = BundleManager.getBundle(URI_DOWNLOADS_PROPERTIES);
5472 var title = bundle.GetStringFromName(title);
5473 var message, quitButton;
5474 if (count > 1) {
5475 message = bundle.formatStringFromName(cancelMessageMultiple, [count], 1);
5476 quitButton = bundle.formatStringFromName("cancelDownloadsOKTextMultiple", [count], 1);
5477 }
5478 else {
5479 message = bundle.GetStringFromName(cancelMessageSingle);
5480 quitButton = bundle.GetStringFromName("cancelDownloadsOKText");
5481 }
5482 var dontQuitButton = bundle.GetStringFromName(dontCancelButton);
5483
5484 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
5485 getService(Ci.nsIWindowMediator);
5486 var win = wm.getMostRecentWindow("Extension:Manager");
5487 const nsIPromptService = Ci.nsIPromptService;
5488 var ps = Cc["@mozilla.org/embedcomp/prompt-service;1"].
5489 getService(nsIPromptService);
5490 var flags = (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_0) +
5491 (nsIPromptService.BUTTON_TITLE_IS_STRING * nsIPromptService.BUTTON_POS_1);
5492 var rv = ps.confirmEx(win, title, message, flags, quitButton, dontQuitButton, null, null, { });
5493 return rv == 1;
5494 },
5495
5496 /* See nsIExtensionManager.idl */
addDownloads
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5497 addDownloads: function(items, itemCount, manager) {
5498 if (itemCount == 0)
5499 throw Cr.NS_ERROR_ILLEGAL_VALUE;
5500
5501 for (i = 0; i < itemCount; ++i) {
5502 var currItem = items[i];
5503 if (!currItem)
5504 throw Cr.NS_ERROR_ILLEGAL_VALUE;
5505 }
5506
5507 var ds = this.datasource;
5508 // Add observers only if they aren't already added for an active download
5509 if (this._downloadCount == 0) {
5510 gOS.addObserver(this, "offline-requested", false);
5511 gOS.addObserver(this, "quit-application-requested", false);
5512 }
5513 this._downloadCount += itemCount;
5514
5515 var urls = [];
5516 var hashes = [];
5517 var txnID = Math.round(Math.random() * 100);
5518 var txn = new ItemDownloadTransaction(this, txnID);
5519 for (var i = 0; i < itemCount; ++i) {
5520 var currItem = items[i];
5521
5522 txn.addDownload(currItem);
5523 urls.push(currItem.xpiURL);
5524 hashes.push(currItem.xpiHash ? currItem.xpiHash : null);
5525 // if this is an update remove the update metadata to prevent it from
5526 // being updated during an install.
5527 if (!manager) {
5528 var id = currItem.id
5529 ds.setItemProperty(id, EM_R("availableUpdateURL"), null);
5530 ds.setItemProperty(id, EM_R("availableUpdateHash"), null);
5531 ds.setItemProperty(id, EM_R("availableUpdateVersion"), null);
5532 ds.setItemProperty(id, EM_R("availableUpdateInfo"), null);
5533 ds.updateProperty(id, "availableUpdateURL");
5534 ds.updateProperty(id, "updateable");
5535 }
5536 var id = !manager ? PREFIX_ITEM_URI + currItem.id : currItem.xpiURL;
5537 ds.updateDownloadState(id, "waiting");
5538 }
5539 this._transactions.push(txn);
5540
5541 if (manager) {
5542 // XPIManager initiated -- let it know we're ready
5543 manager.observe(txn, "xpinstall-progress", "open");
5544 }
5545 else {
5546 // Initiate an install from chrome
5547 var xpimgr = Cc["@mozilla.org/xpinstall/install-manager;1"].
5548 createInstance(Ci.nsIXPInstallManager);
5549 xpimgr.initManagerWithHashes(urls, hashes, urls.length, txn);
5550 }
5551 },
5552
5553 /**
5554 * Download Operation State has changed from one to another.
5555 *
5556 * The nsIXPIProgressDialog implementation in the download transaction object
5557 * forwards notifications through these methods which we then pass on to any
5558 * front end objects implementing nsIExtensionDownloadListener that
5559 * are listening. We maintain the master state of download operations HERE,
5560 * not in the front end, because if the user closes the extension or theme
5561 * managers during the downloads we need to maintain state and not terminate
5562 * the download/install process.
5563 *
5564 * @param transaction
5565 * The ItemDownloadTransaction object receiving the download
5566 * notifications from XPInstall.
5567 * @param addon
5568 * An object representing nsIUpdateItem for the addon being updated
5569 * @param state
5570 * The state we are entering
5571 * @param value
5572 * ???
5573 */
onStateChange
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5574 onStateChange: function(transaction, addon, state, value) {
5575 var ds = this.datasource;
5576 var id = addon.id != addon.xpiURL ? PREFIX_ITEM_URI + addon.id : addon.xpiURL;
5577 const nsIXPIProgressDialog = Ci.nsIXPIProgressDialog;
5578 switch (state) {
5579 case nsIXPIProgressDialog.DOWNLOAD_START:
5580 ds.updateDownloadState(id, "downloading");
5581 for (var i = 0; i < this._installListeners.length; ++i)
5582 this._installListeners[i].onDownloadStarted(addon);
5583 break;
5584 case nsIXPIProgressDialog.DOWNLOAD_DONE:
5585 for (var i = 0; i < this._installListeners.length; ++i)
5586 this._installListeners[i].onDownloadEnded(addon);
5587 break;
5588 case nsIXPIProgressDialog.INSTALL_START:
5589 ds.updateDownloadState(id, "finishing");
5590 ds.updateDownloadProgress(id, null);
5591 break;
5592 case nsIXPIProgressDialog.INSTALL_DONE:
5593 --this._downloadCount;
5594 // From nsInstall.h
5595 // SUCCESS = 0
5596 // USER_CANCELLED = -210
5597 if (value != 0 && value != -210 && id != addon.xpiURL) {
5598 ds.updateDownloadState(id, "failure");
5599 ds.updateDownloadProgress(id, null);
5600 }
5601 transaction.removeDownload(addon.xpiURL);
5602 // A successful install will be passing notifications via installItemFromFile
5603 if (value != 0) {
5604 for (var i = 0; i < this._installListeners.length; ++i)
5605 this._installListeners[i].onInstallEnded(addon, value);
5606 }
5607 break;
5608 case nsIXPIProgressDialog.DIALOG_CLOSE:
5609 for (var i = 0; i < this._transactions.length; ++i) {
5610 if (this._transactions[i].id == transaction.id) {
5611 this._transactions.splice(i, 1);
5612 // Remove the observers when all transactions have completed.
5613 if (this._transactions.length == 0) {
5614 gOS.removeObserver(this, "offline-requested");
5615 gOS.removeObserver(this, "quit-application-requested");
5616
5617 // If there are no compatibility checks running then the install
5618 // operations are complete.
5619 if (this._compatibilityCheckCount == 0) {
5620 for (var i = 0; i < this._installListeners.length; ++i)
5621 this._installListeners[i].onInstallsCompleted();
5622 }
5623 }
5624 break;
5625 }
5626 }
5627 // Remove any remaining downloads from this transaction
5628 transaction.removeAllDownloads();
5629 break;
5630 }
5631 },
5632
onProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5633 onProgress: function(addon, value, maxValue) {
5634 for (var i = 0; i < this._installListeners.length; ++i)
5635 this._installListeners[i].onDownloadProgress(addon, value, maxValue);
5636
5637 var id = addon.id != addon.xpiURL ? PREFIX_ITEM_URI + addon.id : addon.xpiURL;
5638 var progress = Math.round((value / maxValue) * 100);
5639 this.datasource.updateDownloadProgress(id, progress);
5640 },
5641
5642 _installListeners: [],
addInstallListener
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5643 addInstallListener: function(listener) {
5644 for (var i = 0; i < this._installListeners.length; ++i) {
5645 if (this._installListeners[i] == listener)
5646 return i;
5647 }
5648 this._installListeners.push(listener);
5649 return this._installListeners.length - 1;
5650 },
5651
removeInstallListenerAt
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5652 removeInstallListenerAt: function(index) {
5653 this._installListeners.splice(index, 1);
5654 },
5655
5656 /**
5657 * The Extensions RDF Datasource
5658 */
5659 _ds: null,
5660 _ptr: null,
5661
5662 /**
5663 * Loads the Extensions Datasource. This should not be called unless:
5664 * - a piece of Extensions UI is being shown, or
5665 * - on startup and there has been a change to an Install Location
5666 * ... it should NOT be called on every startup!
5667 */
_ensureDS
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5668 _ensureDS: function() {
5669 if (!this._ds) {
5670 this._ds = new ExtensionsDataSource(this);
5671 if (this._ds) {
5672 this._ds.loadExtensions();
5673 this._ptr = getContainer(this._ds, this._ds._itemRoot).DataSource;
5674 gRDF.RegisterDataSource(this._ptr, true);
5675 }
5676 }
5677 },
5678
5679 /**
5680 * See nsIExtensionManager.idl
5681 */
get_datasource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5682 get datasource() {
5683 this._ensureDS();
5684 return this._ds.QueryInterface(Ci.nsIRDFDataSource);
5685 },
5686
5687 // nsIClassInfo
5688 flags: Ci.nsIClassInfo.SINGLETON,
5689 implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
getHelperForLanguage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5690 getHelperForLanguage: function(language) null,
getInterfaces
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5691 getInterfaces: function(count) {
5692 var interfaces = [Ci.nsIExtensionManager, Ci.nsIObserver];
5693 count.value = interfaces.length;
5694 return interfaces;
5695 },
5696
5697 classDescription: "Extension Manager",
5698 contractID: "@mozilla.org/extensions/manager;1",
5699 classID: Components.ID("{8A115FAA-7DCB-4e8f-979B-5F53472F51CF}"),
5700 _xpcom_categories: [{ category: "app-startup", service: true }],
5701 _xpcom_factory: EmFactory,
5702 QueryInterface: XPCOMUtils.generateQI([Ci.nsIExtensionManager,
5703 Ci.nsITimerCallback,
5704 Ci.nsIObserver,
5705 Ci.nsIClassInfo])
5706 };
5707
5708 /**
5709 * This object implements nsIXPIProgressDialog and represents a collection of
5710 * XPI/JAR download and install operations. There is one
5711 * ItemDownloadTransaction per back-end XPInstallManager object. We maintain
5712 * a collection of separate transaction objects because it's possible to have
5713 * multiple separate XPInstall download/install operations going on
5714 * simultaneously, each with its own XPInstallManager instance. For instance
5715 * you could start downloading two extensions and then download a theme. Each
5716 * of these operations would open the appropriate FE and have to be able to
5717 * track each operation independently.
5718 *
5719 * @constructor
5720 * @param manager
5721 * The extension manager creating this transaction
5722 * @param id
5723 * The integer identifier of this transaction
5724 */
ItemDownloadTransaction
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5725 function ItemDownloadTransaction(manager, id) {
5726 this._manager = manager;
5727 this._downloads = [];
5728 this.id = id;
5729 }
5730 ItemDownloadTransaction.prototype = {
5731 _manager : null,
5732 _downloads : [],
5733 id : -1,
5734
5735 /**
5736 * Add a download to this transaction
5737 * @param addon
5738 * An object implementing nsIUpdateItem for the item to be downloaded
5739 */
addDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5740 addDownload: function(addon) {
5741 this._downloads.push({ addon: addon, waiting: true });
5742 this._manager.datasource.addDownload(addon);
5743 },
5744
5745 /**
5746 * Removes a download from this transaction
5747 * @param url
5748 * The URL to remove
5749 */
removeDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5750 removeDownload: function(url) {
5751 this._manager.datasource.removeDownload(url);
5752 },
5753
5754 /**
5755 * Remove all downloads from this transaction
5756 */
removeAllDownloads
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5757 removeAllDownloads: function() {
5758 for (var i = 0; i < this._downloads.length; ++i) {
5759 var addon = this._downloads[i].addon;
5760 this.removeDownload(addon.xpiURL);
5761 }
5762 },
5763
5764 /**
5765 * Determine if this transaction is handling the download of a url.
5766 * @param url
5767 * The URL to look for
5768 * @returns true if this transaction is downloading the supplied url.
5769 */
containsURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5770 containsURL: function(url) {
5771 for (var i = 0; i < this._downloads.length; ++i) {
5772 if (this._downloads[i].addon.xpiURL == url)
5773 return true;
5774 }
5775 return false;
5776 },
5777
5778 /**
5779 * See nsIXPIProgressDialog.idl
5780 */
onStateChange
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5781 onStateChange: function(index, state, value) {
5782 this._manager.onStateChange(this, this._downloads[index].addon,
5783 state, value);
5784 },
5785
5786 /**
5787 * See nsIXPIProgressDialog.idl
5788 */
onProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5789 onProgress: function(index, value, maxValue) {
5790 this._manager.onProgress(this._downloads[index].addon, value, maxValue);
5791 },
5792
5793 QueryInterface: XPCOMUtils.generateQI([Ci.nsIXPIProgressDialog])
5794 };
5795
5796 /**
5797 * A listener object that watches the background update check and notifies the
5798 * user of any updates found.
5799 */
BackgroundUpdateCheckListener
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5800 function BackgroundUpdateCheckListener(datasource) {
5801 this._emDS = datasource;
5802 }
5803 BackgroundUpdateCheckListener.prototype = {
5804 _updateCount: 0,
5805 _emDS: null,
5806
5807 // nsIObserver implementation
observe
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5808 observe: function(aSubject, aTopic, aData) {
5809 if (aTopic != "alertclickcallback")
5810 return;
5811
5812 var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
5813 getService(Ci.nsIWindowMediator);
5814 var win = wm.getMostRecentWindow("Extension:Manager");
5815 if (win) {
5816 win.focus();
5817 win.showView("updates");
5818 // Don't show the update notification on next startup
5819 gPref.setBoolPref(PREF_UPDATE_NOTIFYUSER, false);
5820 }
5821 else {
5822 const EMURL = "chrome://mozapps/content/extensions/extensions.xul";
5823 const EMFEATURES = "chrome,menubar,extra-chrome,toolbar,dialog=no,resizable";
5824
5825 var ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
5826 getService(Ci.nsIWindowWatcher);
5827 var param = Cc["@mozilla.org/supports-array;1"].
5828 createInstance(Ci.nsISupportsArray);
5829 var arg = Cc["@mozilla.org/supports-string;1"].
5830 createInstance(Ci.nsISupportsString);
5831 arg.data = "updates";
5832 param.AppendElement(arg);
5833 ww.openWindow(null, EMURL, null, EMFEATURES, param);
5834 }
5835 },
5836
5837 // nsIAddonUpdateCheckListener implementation
onUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5838 onUpdateStarted: function() {
5839 },
5840
onUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5841 onUpdateEnded: function() {
5842 if (this._updateCount > 0 && Cc["@mozilla.org/alerts-service;1"]) {
5843 var extensionStrings = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
5844 var title = extensionStrings.GetStringFromName("updateNotificationTitle");
5845 var text;
5846 if (this._updateCount > 1)
5847 text = extensionStrings.formatStringFromName("multipleUpdateNotificationText",
5848 [BundleManager.appName, this._updateCount], 2);
5849 else
5850 text = extensionStrings.formatStringFromName("updateNotificationText",
5851 [BundleManager.appName], 1);
5852
5853 try {
5854 var notifier = Cc["@mozilla.org/alerts-service;1"].
5855 getService(Ci.nsIAlertsService);
5856 notifier.showAlertNotification(URI_GENERIC_ICON_XPINSTALL,
5857 title, text, true, "", this);
5858 }
5859 catch (e) {
5860 LOG("Failed to retrieve alerts service, probably an unsupported " +
5861 "platform - " + e);
5862 }
5863 }
5864 },
5865
onAddonUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5866 onAddonUpdateStarted: function(item) {
5867 },
5868
onAddonUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5869 onAddonUpdateEnded: function(item, status) {
5870 if (status == Ci.nsIAddonUpdateCheckListener.STATUS_UPDATE) {
5871 var lastupdate = this._emDS.getItemProperty(item.id, "availableUpdateVersion");
5872 if (lastupdate != item.version) {
5873 gPref.setBoolPref(PREF_UPDATE_NOTIFYUSER, true);
5874 this._updateCount++;
5875 }
5876 }
5877 }
5878 };
5879
5880
5881 /**
5882 * A listener object to the update check process that routes notifications to
5883 * the right places and keeps the datasource up to date.
5884 */
AddonUpdateCheckListener
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5885 function AddonUpdateCheckListener(listener, datasource) {
5886 this._listener = listener;
5887 this._ds = datasource;
5888 }
5889 AddonUpdateCheckListener.prototype = {
5890 _listener: null,
5891 _ds: null,
5892
onUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5893 onUpdateStarted: function() {
5894 if (this._listener)
5895 this._listener.onUpdateStarted();
5896 this._ds.onUpdateStarted();
5897 },
5898
onUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5899 onUpdateEnded: function() {
5900 if (this._listener)
5901 this._listener.onUpdateEnded();
5902 this._ds.onUpdateEnded();
5903 },
5904
onAddonUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5905 onAddonUpdateStarted: function(addon) {
5906 if (this._listener)
5907 this._listener.onAddonUpdateStarted(addon);
5908 this._ds.onAddonUpdateStarted(addon);
5909 },
5910
onAddonUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5911 onAddonUpdateEnded: function(addon, status) {
5912 if (this._listener)
5913 this._listener.onAddonUpdateEnded(addon, status);
5914 this._ds.onAddonUpdateEnded(addon, status);
5915 }
5916 };
5917
5918 ///////////////////////////////////////////////////////////////////////////////
5919 //
5920 // ExtensionItemUpdater
5921 //
ExtensionItemUpdater
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5922 function ExtensionItemUpdater(aEM)
5923 {
5924 this._emDS = aEM._ds;
5925 this._em = aEM;
5926
5927 getVersionChecker();
5928 }
5929
5930 ExtensionItemUpdater.prototype = {
5931 _emDS : null,
5932 _em : null,
5933 _updateCheckType : 0,
5934 _items : [],
5935 _listener : null,
5936
5937 /* ExtensionItemUpdater
5938 //@line 6108 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
5939 */
checkForUpdates
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5940 checkForUpdates: function(aItems, aItemCount, aUpdateCheckType,
5941 aListener) {
5942 this._listener = new AddonUpdateCheckListener(aListener, this._emDS);
5943 if (this._listener)
5944 this._listener.onUpdateStarted();
5945 this._updateCheckType = aUpdateCheckType;
5946 this._items = aItems;
5947 this._responseCount = aItemCount;
5948
5949 // This is the number of extensions/themes/etc that we found updates for.
5950 this._updateCount = 0;
5951
5952 for (var i = 0; i < aItemCount; ++i) {
5953 var e = this._items[i];
5954 if (this._listener)
5955 this._listener.onAddonUpdateStarted(e);
5956 (new RDFItemUpdater(this)).checkForUpdates(e, aUpdateCheckType);
5957 }
5958
5959 if (this._listener && aItemCount == 0)
5960 this._listener.onUpdateEnded();
5961 },
5962
5963 /////////////////////////////////////////////////////////////////////////////
5964 // ExtensionItemUpdater
_applyVersionUpdates
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
5965 _applyVersionUpdates: function(aLocalItem, aRemoteItem) {
5966 var targetAppInfo = this._emDS.getTargetApplicationInfo(aLocalItem.id, this._emDS);
5967 // If targetAppInfo is null this is for a new install. If the local item's
5968 // maxVersion does not equal the targetAppInfo maxVersion then this is for
5969 // an upgrade. In both of these cases return true if the remotely specified
5970 // maxVersion is greater than the local item's maxVersion.
5971 if (!targetAppInfo ||
5972 gVersionChecker.compare(aLocalItem.maxAppVersion, targetAppInfo.maxVersion) != 0) {
5973 if (gVersionChecker.compare(aLocalItem.maxAppVersion, aRemoteItem.maxAppVersion) < 0)
5974 return true;
5975 else
5976 return false;
5977 }
5978
5979 if (gVersionChecker.compare(targetAppInfo.maxVersion, aRemoteItem.maxAppVersion) < 0) {
5980 // Remotely specified maxVersion is newer than the maxVersion
5981 // for the installed Extension. Apply that change to the datasources.
5982 this._emDS.setTargetApplicationInfo(aLocalItem.id,
5983 aRemoteItem.targetAppID,
5984 aRemoteItem.minAppVersion,
5985 aRemoteItem.maxAppVersion,
5986 null);
5987
5988 // If we got here through |checkForMismatches|, this extension has
5989 // already been disabled, re-enable it.
5990 var op = StartupCache.entries[aLocalItem.installLocationKey][aLocalItem.id].op;
5991 if (op == OP_NEEDS_DISABLE ||
5992 this._emDS.getItemProperty(aLocalItem.id, "appDisabled") == "true")
5993 this._em._appEnableItem(aLocalItem.id);
5994 return true;
5995 }
5996 else if (this._updateCheckType == Ci.nsIExtensionManager.UPDATE_SYNC_COMPATIBILITY)
5997 this._emDS.setTargetApplicationInfo(aLocalItem.id,
5998 aRemoteItem.targetAppID,
5999 aRemoteItem.minAppVersion,
6000 aRemoteItem.maxAppVersion,
6001 null);
6002 return false;
6003 },
6004
6005 /**
6006 * Checks whether a discovered update is valid for install
6007 * @param aLocalItem
6008 * The already installed nsIUpdateItem that the update is for
6009 * @param aRemoteItem
6010 * The nsIUpdateItem we are trying to update to
6011 *
6012 * @returns true if the item is compatible and is not blocklisted.
6013 * false if the item is not compatible or is blocklisted.
6014 */
_isValidUpdate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6015 _isValidUpdate: function _isValidUpdate(aLocalItem, aRemoteItem) {
6016 var appExtensionsVersion = (aRemoteItem.targetAppID != TOOLKIT_ID) ?
6017 gApp.version :
6018 gApp.platformVersion;
6019
6020 var min = aRemoteItem.minAppVersion;
6021 var max = aRemoteItem.maxAppVersion;
6022 // Check if the update will only run on a newer version of the application.
6023 if (!min || gVersionChecker.compare(appExtensionsVersion, min) < 0)
6024 return false;
6025
6026 // Check if the update will only run on an older version of the application.
6027 if (!max || gVersionChecker.compare(appExtensionsVersion, max) > 0)
6028 return false;
6029
6030 if (!gBlocklist)
6031 gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].
6032 getService(Ci.nsIBlocklistService);
6033 if (gBlocklist.isAddonBlocklisted(aLocalItem.id, aRemoteItem.version,
6034 null, null))
6035 return false;
6036
6037 return true;
6038 },
6039
checkForDone
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6040 checkForDone: function(item, status) {
6041 if (this._listener) {
6042 try {
6043 this._listener.onAddonUpdateEnded(item, status);
6044 }
6045 catch (e) {
6046 LOG("ExtensionItemUpdater:checkForDone: Failure in listener's onAddonUpdateEnded: " + e);
6047 }
6048 }
6049 if (--this._responseCount == 0 && this._listener) {
6050 try {
6051 this._listener.onUpdateEnded();
6052 }
6053 catch (e) {
6054 LOG("ExtensionItemUpdater:checkForDone: Failure in listener's onUpdateEnded: " + e);
6055 }
6056 }
6057 },
6058 };
6059
6060 /**
6061 * Replaces %...% strings in an addon url (update and updateInfo) with
6062 * appropriate values.
6063 * @param aItem
6064 * The nsIUpdateItem representing the item
6065 * @param aURI
6066 * The uri to escape
6067 * @param aDS
6068 * The extensions datasource
6069 *
6070 * @returns the appropriately escaped uri.
6071 */
escapeAddonURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6072 function escapeAddonURI(aItem, aURI, aDS)
6073 {
6074 var itemStatus = "userEnabled";
6075 if (aDS.getItemProperty(aItem.id, "userDisabled") == "true" ||
6076 aDS.getItemProperty(aItem.id, "userDisabled") == OP_NEEDS_ENABLE)
6077 itemStatus = "userDisabled";
6078 else if (aDS.getItemProperty(aItem.id, "type") == Ci.nsIUpdateItem.TYPE_THEME) {
6079 var currentSkin = gPref.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN);
6080 if (aDS.getItemProperty(aItem.id, "internalName") != currentSkin)
6081 itemStatus = "userDisabled";
6082 }
6083
6084 if (aDS.getItemProperty(aItem.id, "compatible") == "false")
6085 itemStatus += ",incompatible";
6086 if (aDS.getItemProperty(aItem.id, "blocklisted") == "true")
6087 itemStatus += ",blocklisted";
6088 if (aDS.getItemProperty(aItem.id, "satisfiesDependencies") == "false")
6089 itemStatus += ",needsDependencies";
6090
6091 aURI = aURI.replace(/%ITEM_ID%/g, aItem.id);
6092 aURI = aURI.replace(/%ITEM_VERSION%/g, aItem.version);
6093 aURI = aURI.replace(/%ITEM_MAXAPPVERSION%/g, aItem.maxAppVersion);
6094 aURI = aURI.replace(/%ITEM_STATUS%/g, itemStatus);
6095 aURI = aURI.replace(/%APP_ID%/g, gApp.ID);
6096 aURI = aURI.replace(/%APP_VERSION%/g, gApp.version);
6097 aURI = aURI.replace(/%REQ_VERSION%/g, 1);
6098 aURI = aURI.replace(/%APP_OS%/g, gOSTarget);
6099 aURI = aURI.replace(/%APP_ABI%/g, gXPCOMABI);
6100 aURI = aURI.replace(/%APP_LOCALE%/g, gLocale);
6101
6102 // Replace custom parameters (names of custom parameters must have at
6103 // least 3 characters to prevent lookups for something like %D0%C8)
6104 var catMan = null;
anon:6105:37
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6105 aURI = aURI.replace(/%(\w{3,})%/g, function(match, param) {
6106 if (!catMan) {
6107 catMan = Cc["@mozilla.org/categorymanager;1"].
6108 getService(Ci.nsICategoryManager);
6109 }
6110
6111 try {
6112 var contractID = catMan.getCategoryEntry(CATEGORY_UPDATE_PARAMS, param);
6113 var paramHandler = Cc[contractID].
6114 getService(Ci.nsIPropertyBag2);
6115 return paramHandler.getPropertyAsAString(param);
6116 }
6117 catch(e) {
6118 return match;
6119 }
6120 });
6121
6122 // escape() does not properly encode + symbols in any embedded FVF strings.
6123 return aURI.replace(/\+/g, "%2B");
6124 }
6125
RDFItemUpdater
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6126 function RDFItemUpdater(aUpdater) {
6127 this._updater = aUpdater;
6128 }
6129
6130 RDFItemUpdater.prototype = {
6131 _updater : null,
6132 _updateCheckType : 0,
6133 _item : null,
6134
checkForUpdates
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6135 checkForUpdates: function(aItem, aUpdateCheckType) {
6136 // A preference setting can disable updating for this item
6137 try {
6138 if (!gPref.getBoolPref(PREF_EM_ITEM_UPDATE_ENABLED.replace(/%UUID%/, aItem.id))) {
6139 var status = Ci.nsIAddonUpdateCheckListener.STATUS_DISABLED;
6140 this._updater.checkForDone(aItem, status);
6141 return;
6142 }
6143 }
6144 catch (e) { }
6145
6146 // Items managed by the app are not checked for updates.
6147 var emDS = this._updater._emDS;
6148 if (emDS.getItemProperty(aItem.id, "appManaged") == "true") {
6149 var status = Ci.nsIAddonUpdateCheckListener.STATUS_APP_MANAGED;
6150 this._updater.checkForDone(aItem, status);
6151 return;
6152 }
6153
6154 // Items that have a pending install, uninstall, or upgrade are not checked
6155 // for updates.
6156 var opType = emDS.getItemProperty(aItem.id, "opType");
6157 if (opType) {
6158 var status = Ci.nsIAddonUpdateCheckListener.STATUS_PENDING_OP;
6159 this._updater.checkForDone(aItem, status);
6160 return;
6161 }
6162
6163 var installLocation = InstallLocations.get(emDS.getInstallLocationKey(aItem.id));
6164 // Don't check items for updates that are managed independently
6165 if (installLocation && installLocation.itemIsManagedIndependently(aItem.id)) {
6166 var status = Ci.nsIAddonUpdateCheckListener.STATUS_NOT_MANAGED;
6167 this._updater.checkForDone(aItem, status);
6168 return;
6169 }
6170
6171 // Don't check items for updates if the location can't be written to except
6172 // when performing a version only update.
6173 if ((aUpdateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION) &&
6174 (!installLocation || !installLocation.canAccess)) {
6175 var status = Ci.nsIAddonUpdateCheckListener.STATUS_READ_ONLY;
6176 this._updater.checkForDone(aItem, status);
6177 return;
6178 }
6179
6180 this._updateCheckType = aUpdateCheckType;
6181 this._item = aItem;
6182
6183 // Look for a custom update URI: 1) supplied by a pref, 2) supplied by the
6184 // install manifest, 3) the default configuration
6185 try {
6186 var dsURI = gPref.getComplexValue(PREF_EM_ITEM_UPDATE_URL.replace(/%UUID%/, aItem.id),
6187 Ci.nsIPrefLocalizedString).data;
6188 }
6189 catch (e) { }
6190 if (!dsURI)
6191 dsURI = aItem.updateRDF;
6192 if (!dsURI)
6193 dsURI = gPref.getCharPref(PREF_UPDATE_DEFAULT_URL);
6194
6195 dsURI = escapeAddonURI(aItem, dsURI, emDS);
6196
6197 // Verify that the URI provided is valid
6198 try {
6199 var uri = newURI(dsURI);
6200 }
6201 catch (e) {
6202 LOG("RDFItemUpdater:checkForUpdates: There was an error loading the \r\n" +
6203 " update datasource for: " + dsURI + ", item = " + aItem.id + ", error: " + e);
6204 this._updater.checkForDone(aItem,
6205 Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6206 return;
6207 }
6208
6209 LOG("RDFItemUpdater:checkForUpdates sending a request to server for: " +
6210 uri.spec + ", item = " + aItem.objectSource);
6211
6212 var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
6213 createInstance(Ci.nsIXMLHttpRequest);
6214 request.open("GET", uri.spec, true);
6215 request.channel.notificationCallbacks = new BadCertHandler();
6216 request.overrideMimeType("text/xml");
6217 request.channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
6218
6219 var self = this;
anon:6220:26
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6220 request.onerror = function(event) { self.onXMLError(event, aItem); };
anon:6221:26
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6221 request.onload = function(event) { self.onXMLLoad(event, aItem); };
6222 request.send(null);
6223 },
6224
onXMLLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6225 onXMLLoad: function(aEvent, aItem) {
6226 var request = aEvent.target;
6227 try {
6228 checkCert(request.channel);
6229 }
6230 catch (e) {
6231 // This may be overly restrictive in two cases: corporate installations
6232 // with a corporate update server using an in-house CA cert (installed
6233 // but not "built-in") and lone developers hosting their updates on a
6234 // site with a self-signed cert (permanently accepted, otherwise the
6235 // BadCertHandler would prevent getting this far). Update checks will
6236 // fail in both these scenarios.
6237 // How else can we protect the vast majority of updates served from AMO
6238 // from the spoofing attack described in bug 340198 while allowing those
6239 // other cases? A "hackme" pref? Domain-control certs are cheap, getting
6240 // one should not be a barrier in either case.
6241 LOG("RDFItemUpdater::onXMLLoad: " + e);
6242 this._updater.checkForDone(aItem,
6243 Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6244 return;
6245 }
6246 var responseXML = request.responseXML;
6247
6248 // If the item does not have an update RDF and returns an error it is not
6249 // treated as a failure since all items without an updateURL are checked
6250 // for updates on AMO even if they are not hosted there.
6251 if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
6252 (request.status != 200 && request.status != 0)) {
6253 this._updater.checkForDone(aItem, (aItem.updateRDF ? Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE :
6254 Ci.nsIAddonUpdateCheckListener.STATUS_NONE));
6255 return;
6256 }
6257
6258 var rdfParser = Cc["@mozilla.org/rdf/xml-parser;1"].
6259 createInstance(Ci.nsIRDFXMLParser)
6260 var ds = Cc["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
6261 createInstance(Ci.nsIRDFDataSource);
6262 rdfParser.parseString(ds, request.channel.URI, request.responseText);
6263
6264 this.onDatasourceLoaded(ds, aItem);
6265 },
6266
onXMLError
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6267 onXMLError: function(aEvent, aItem) {
6268 try {
6269 var request = aEvent.target;
6270 // the following may throw (e.g. a local file or timeout)
6271 var status = request.status;
6272 }
6273 catch (e) {
6274 request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
6275 status = request.status;
6276 }
6277 // this can fail when a network connection is not present.
6278 try {
6279 var statusText = request.statusText;
6280 }
6281 catch (e) {
6282 status = 0;
6283 }
6284 // When status is 0 we don't have a valid channel.
6285 if (status == 0)
6286 statusText = "nsIXMLHttpRequest channel unavailable";
6287
6288 LOG("RDFItemUpdater:onError: There was an error loading the \r\n" +
6289 "the update datasource for item " + aItem.id + ", error: " + statusText);
6290 this._updater.checkForDone(aItem,
6291 Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6292 },
6293
onDatasourceLoaded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6294 onDatasourceLoaded: function(aDatasource, aLocalItem) {
6295 /*
6296 //@line 6506 "/Users/sombrero/rev_control/hg/mozilla/toolkit/mozapps/extensions/src/nsExtensionManager.js.in"
6297 */
6298 if (!aDatasource.GetAllResources().hasMoreElements()) {
6299 LOG("RDFItemUpdater:onDatasourceLoaded: Datasource empty.\r\n" +
6300 "If you are an Extension developer and were expecting there to be\r\n" +
6301 "updates, this could mean any number of things, since the RDF system\r\n" +
6302 "doesn't give up much in the way of information when the load fails.\r\n" +
6303 "\r\nTry checking that: \r\n" +
6304 " 1. Your remote RDF file exists at the location.\r\n" +
6305 " 2. Your RDF file is valid XML (starts with <?xml version=\"1.0\"?>\r\n" +
6306 " and loads in Firefox displaying pretty printed like other XML documents\r\n" +
6307 " 3. Your server is sending the data in the correct MIME\r\n" +
6308 " type (text/xml)");
6309 }
6310
6311 // If we have an update key then the update manifest must be signed
6312 if (aLocalItem.updateKey) {
6313 var extensionRes = gRDF.GetResource(getItemPrefix(aLocalItem.type) + aLocalItem.id);
6314 LOG(extensionRes.Value);
6315 var signature = this._getPropertyFromResource(aDatasource, extensionRes, "signature", null);
6316 if (signature) {
6317 var serializer = new RDFSerializer();
6318 try {
6319 var updateString = serializer.serializeResource(aDatasource, extensionRes);
6320 var verifier = Cc["@mozilla.org/security/datasignatureverifier;1"].
6321 getService(Ci.nsIDataSignatureVerifier);
6322 try {
6323 if (!verifier.verifyData(updateString, signature, aLocalItem.updateKey)) {
6324 LOG("RDFItemUpdater:onDatasourceLoaded: Update manifest for " +
6325 aLocalItem.id + " failed signature check.");
6326 this._updater.checkForDone(aLocalItem, Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6327 return;
6328 }
6329 }
6330 catch (e) {
6331 LOG("RDFItemUpdater:onDatasourceLoaded: Failed to verify signature for " +
6332 aLocalItem.id + ". This indicates a malformed update key or signature.");
6333 this._updater.checkForDone(aLocalItem, Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6334 return;
6335 }
6336 }
6337 catch (e) {
6338 LOG("RDFItemUpdater:onDatasourceLoaded: Failed to generate signature " +
6339 "string for " + aLocalItem.id + ". Serializer threw " + e);
6340 this._updater.checkForDone(aLocalItem, Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6341 return;
6342 }
6343 }
6344 else {
6345 LOG("RDFItemUpdater:onDatasourceLoaded: Update manifest for " +
6346 aLocalItem.id + " did not contain a signature.");
6347 this._updater.checkForDone(aLocalItem, Ci.nsIAddonUpdateCheckListener.STATUS_FAILURE);
6348 return;
6349 }
6350 }
6351 /* If there is no updateKey either the update was over SSL, or it is an old
6352 * addon that we are allowing a grace update. */
6353
6354 // Parse the response RDF
6355 var newerItem, sameItem;
6356
6357 // Firefox 1.0PR+ update.rdf format
6358 if (this._updateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION) {
6359 // Look for newer versions of this item, we only do this in "normal"
6360 // mode... see comment by ExtensionItemUpdater_checkForUpdates
6361 // about how we do this in all cases but Install Phone Home - which
6362 // only needs to do a version check.
6363 newerItem = this._parseV20UpdateInfo(aDatasource, aLocalItem,
6364 Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION);
6365
6366 if (newerItem) {
6367 ++this._updater._updateCount;
6368 LOG("RDFItemUpdater:onDatasourceLoaded: Found a newer version of this item:\r\n" +
6369 newerItem.objectSource);
6370 }
6371 }
6372
6373 // Now look for updated version compatibility metadata for the currently
6374 // installed version...
6375 sameItem = this._parseV20UpdateInfo(aDatasource, aLocalItem,
6376 Ci.nsIExtensionManager.UPDATE_CHECK_COMPATIBILITY);
6377
6378 if (sameItem) {
6379 // Install-time updates are not written to the DS because there is no
6380 // entry yet, EM just uses the notifications to ascertain (by hand)
6381 // whether or not there is a remote maxVersion tweak that makes the
6382 // item being installed compatible.
6383 if (!this._updater._applyVersionUpdates(aLocalItem, sameItem))
6384 sameItem = null;
6385 else
6386 LOG("RDFItemUpdater:onDatasourceLoaded: Found info about the installed\r\n" +
6387 "version of this item: " + sameItem.objectSource);
6388 }
6389 var item = null, status = Ci.nsIAddonUpdateCheckListener.STATUS_NONE;
6390 if (this._updateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION
6391 && newerItem) {
6392 item = newerItem;
6393 status = Ci.nsIAddonUpdateCheckListener.STATUS_UPDATE;
6394 }
6395 else if (sameItem) {
6396 item = sameItem;
6397 status = Ci.nsIAddonUpdateCheckListener.STATUS_VERSIONINFO;
6398 }
6399 else {
6400 item = aLocalItem;
6401 status = Ci.nsIAddonUpdateCheckListener.STATUS_NO_UPDATE;
6402 }
6403 // Only one call of this._updater.checkForDone is needed for RDF
6404 // responses, since there is only one response per item.
6405 this._updater.checkForDone(item, status);
6406 },
6407
6408 // Get a compulsory property from a resource. Reports an error if the
6409 // property was not present.
_getPropertyFromResource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6410 _getPropertyFromResource: function(aDataSource, aSourceResource, aProperty, aLocalItem) {
6411 var rv;
6412 try {
6413 var property = gRDF.GetResource(EM_NS(aProperty));
6414 rv = stringData(aDataSource.GetTarget(aSourceResource, property, true));
6415 if (rv === undefined)
6416 throw Cr.NS_ERROR_FAILURE;
6417 }
6418 catch (e) {
6419 // XXXben show console message "aProperty" not found on aSourceResource.
6420 return null;
6421 }
6422 return rv;
6423 },
6424
6425 /**
6426 * Parses the Firefox 1.0RC1+ update manifest format looking for new versions
6427 * of updated compatibility information about the given add-on.
6428 * @param aDataSource
6429 * The update manifest's datasource
6430 * @param aLocalItem
6431 * The nsIUpdateItem representing the add-on being checked for updates.
6432 * @param aUpdateCheckType
6433 * The type of update check being performed. See the constants in
6434 * nsIExtensionManager
6435 * @returns An nsIUpdateItem holding the update's information if a valid
6436 * update is found or null if not.
6437 */
_parseV20UpdateInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6438 _parseV20UpdateInfo: function(aDataSource, aLocalItem, aUpdateCheckType) {
6439 var extensionRes = gRDF.GetResource(getItemPrefix(aLocalItem.type) + aLocalItem.id);
6440
6441 var updatesArc = gRDF.GetResource(EM_NS("updates"));
6442 var updates = aDataSource.GetTarget(extensionRes, updatesArc, true);
6443
6444 try {
6445 updates = updates.QueryInterface(Ci.nsIRDFResource);
6446 }
6447 catch (e) {
6448 LOG("RDFItemUpdater:_parseV20UpdateInfo: No updates were found for:\r\n" +
6449 aLocalItem.id + "\r\n" +
6450 "If you are an Extension developer and were expecting there to be\r\n" +
6451 "updates, this could mean any number of things, since the RDF system\r\n" +
6452 "doesn't give up much in the way of information when the load fails.\r\n" +
6453 "\r\nTry checking that: \r\n" +
6454 " 1. Your RDF File is correct - e.g. check that there is a top level\r\n" +
6455 " RDF Resource with a URI urn:mozilla:extension:{GUID}, and that\r\n" +
6456 " the <em:updates> listed all have matching GUIDs.");
6457 return null;
6458 }
6459
6460 // Track the newest update found
6461 var updatedItem = null;
6462
6463 var cu = Cc["@mozilla.org/rdf/container-utils;1"].
6464 getService(Ci.nsIRDFContainerUtils);
6465 if (cu.IsContainer(aDataSource, updates)) {
6466 var ctr = getContainer(aDataSource, updates);
6467
6468 var versions = ctr.GetElements();
6469 while (versions.hasMoreElements()) {
6470 // There are two different methodologies for collecting version
6471 // information depending on whether or not we've been invoked in
6472 // "version updates only" mode or "version+newest" mode.
6473 var version = versions.getNext().QueryInterface(Ci.nsIRDFResource);
6474 var foundItem = this._parseV20Update(aDataSource, version, aLocalItem,
6475 updatedItem ? updatedItem.version : aLocalItem.version,
6476 aUpdateCheckType);
6477 if (foundItem) {
6478 // When not checking for new versions we can bail out on the first
6479 // result.
6480 if (aUpdateCheckType)
6481 return foundItem;
6482 updatedItem = foundItem;
6483 }
6484 }
6485 }
6486 return updatedItem;
6487 },
6488
6489 /**
6490 * Parses a single version's update entry looking for the best matching
6491 * targetApplication entry.
6492 * @param aDataSource
6493 * The update manifest's datasource
6494 * @param aUpdateResource
6495 * The nsIRDFResource of the update entry.
6496 * @param aLocalItem
6497 * The nsIUpdateItem representing the add-on being checked for updates.
6498 * @param aNewestVersionFound
6499 * When checking for new versions holds the newest version of this
6500 * add-on that we know about. Otherwise holds the current version.
6501 * @param aUpdateCheckType
6502 * The type of update check being performed. See the constants in
6503 * nsIExtensionManager
6504 * @returns An nsIUpdateItem holding the update's information if a valid
6505 * update is found or null if not.
6506 */
_parseV20Update
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6507 _parseV20Update: function(aDataSource, aUpdateResource, aLocalItem, aNewestVersionFound, aUpdateCheckType) {
6508 var version = this._getPropertyFromResource(aDataSource, aUpdateResource,
6509 "version", aLocalItem);
6510 /* If we are looking for new versions then test whether this discovered
6511 * version is greater than any previously found update. Otherwise check
6512 * if this update is for the same version as we have installed. */
6513 var result = gVersionChecker.compare(version, aNewestVersionFound);
6514 if (aUpdateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION ? result <= 0 : result != 0)
6515 return null;
6516
6517 var taArc = gRDF.GetResource(EM_NS("targetApplication"));
6518 var targetApps = aDataSource.GetTargets(aUpdateResource, taArc, true);
6519
6520 // Track the best update we have found so far
6521 var newestUpdateItem = null;
6522 while (targetApps.hasMoreElements()) {
6523 var targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
6524 var appID = this._getPropertyFromResource(aDataSource, targetApp, "id", aLocalItem);
6525 if (appID != gApp.ID && appID != TOOLKIT_ID)
6526 continue;
6527
6528 var updateLink = this._getPropertyFromResource(aDataSource, targetApp, "updateLink", aLocalItem);
6529 var updateHash = this._getPropertyFromResource(aDataSource, targetApp, "updateHash", aLocalItem);
6530 if (aUpdateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION) {
6531 // New version information is useless without a link to get it from
6532 if (!updateLink)
6533 continue;
6534
6535 /* If the update link is non-ssl and we do not have a hash or the hash
6536 * is of an insecure nature then we must ignore this update. Bypass
6537 * this if not checking update security. Currently we only consider
6538 * the sha hashing algorithms as secure. */
6539 if (gCheckUpdateSecurity && updateLink.substring(0, 6) != "https:" &&
6540 (!updateHash || updateHash.substring(0, 3) != "sha")) {
6541 LOG("RDFItemUpdater:_parseV20Update: Update for " + aLocalItem.id +
6542 " at " + updateLink + " ignored because it is insecure. updateLink " +
6543 " must be a https url or an updateHash must be specified.");
6544 continue;
6545 }
6546 }
6547
6548 var updatedItem = makeItem(aLocalItem.id,
6549 version,
6550 aLocalItem.installLocationKey,
6551 this._getPropertyFromResource(aDataSource, targetApp, "minVersion", aLocalItem),
6552 this._getPropertyFromResource(aDataSource, targetApp, "maxVersion", aLocalItem),
6553 aLocalItem.name,
6554 updateLink,
6555 updateHash,
6556 "", /* Icon URL */
6557 "", /* RDF Update URL */
6558 "", /* Update Key */
6559 aLocalItem.type,
6560 appID);
6561
6562 if (this._updater._isValidUpdate(aLocalItem, updatedItem)) {
6563 if (aUpdateCheckType == Ci.nsIExtensionManager.UPDATE_CHECK_NEWVERSION) {
6564 var infourl = this._getPropertyFromResource(aDataSource, targetApp,
6565 "updateInfoURL");
6566 if (infourl)
6567 infourl = EM_L(infourl);
6568 this._updater._emDS.setItemProperty(aLocalItem.id,
6569 EM_R("availableUpdateInfo"),
6570 infourl);
6571 }
6572 if (appID == gApp.ID) {
6573 // App takes precedence over toolkit. If we found the app, bail out.
6574 return updatedItem;
6575 }
6576 newestUpdateItem = updatedItem;
6577 }
6578 }
6579 return newestUpdateItem;
6580 }
6581 };
6582
6583 /**
6584 * A serialisation method for RDF data that produces an identical string
6585 * provided that the RDF assertions match.
6586 * The serialisation is not complete, only assertions stemming from a given
6587 * resource are included, multiple references to the same resource are not
6588 * permitted, and the RDF prolog and epilog are not included.
6589 * RDF Blob and Date literals are not supported.
6590 */
RDFSerializer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6591 function RDFSerializer()
6592 {
6593 this.cUtils = Cc["@mozilla.org/rdf/container-utils;1"].
6594 getService(Ci.nsIRDFContainerUtils);
6595 this.resources = [];
6596 }
6597
6598 RDFSerializer.prototype = {
6599 INDENT: " ", // The indent used for pretty-printing
6600 resources: null, // Array of the resources that have been found
6601
6602 /**
6603 * Escapes characters from a string that should not appear in XML.
6604 * @param string The string to be escaped
6605 * @returns a string with all characters invalid in XML character data
6606 * converted to entity references.
6607 */
escapeEntities
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6608 escapeEntities: function(string)
6609 {
6610 string = string.replace(/&/g, "&");
6611 string = string.replace(/</g, "<");
6612 string = string.replace(/>/g, ">");
6613 string = string.replace(/"/g, """);
6614 return string;
6615 },
6616
6617 /**
6618 * Serializes all the elements of an RDF container.
6619 * @param ds The datasource holding the data
6620 * @param container The RDF container to output the child elements of
6621 * @param indent The current level of indent for pretty-printing
6622 * @returns a string containing the serialized elements.
6623 */
serializeContainerItems
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6624 serializeContainerItems: function(ds, container, indent)
6625 {
6626 var result = "";
6627 var items = container.GetElements();
6628 while (items.hasMoreElements()) {
6629 var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
6630 result += indent + "<RDF:li>\n"
6631 result += this.serializeResource(ds, item, indent + this.INDENT);
6632 result += indent + "</RDF:li>\n"
6633 }
6634 return result;
6635 },
6636
6637 /**
6638 * Serializes all em:* (see EM_NS) properties of an RDF resource except for
6639 * the em:signature property. As this serialization is to be compared against
6640 * the manifest signature it cannot contain the em:signature property itself.
6641 * @param ds The datasource holding the data
6642 * @param resource The RDF resource to output the properties of
6643 * @param indent The current level of indent for pretty-printing
6644 * @returns a string containing the serialized properties.
6645 */
serializeResourceProperties
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6646 serializeResourceProperties: function(ds, resource, indent)
6647 {
6648 var result = "";
6649 var items = [];
6650 var arcs = ds.ArcLabelsOut(resource);
6651 while (arcs.hasMoreElements()) {
6652 var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
6653 if (arc.ValueUTF8.substring(0, PREFIX_NS_EM.length) != PREFIX_NS_EM)
6654 continue;
6655 var prop = arc.ValueUTF8.substring(PREFIX_NS_EM.length);
6656 if (prop == "signature")
6657 continue;
6658
6659 var targets = ds.GetTargets(resource, arc, true);
6660 while (targets.hasMoreElements()) {
6661 var target = targets.getNext();
6662 if (target instanceof Ci.nsIRDFResource) {
6663 var item = indent + "<em:" + prop + ">\n";
6664 item += this.serializeResource(ds, target, indent + this.INDENT);
6665 item += indent + "</em:" + prop + ">\n";
6666 items.push(item);
6667 }
6668 else if (target instanceof Ci.nsIRDFLiteral) {
6669 items.push(indent + "<em:" + prop + ">" + this.escapeEntities(target.Value) + "</em:" + prop + ">\n");
6670 }
6671 else if (target instanceof Ci.nsIRDFInt) {
6672 items.push(indent + "<em:" + prop + " NC:parseType=\"Integer\">" + target.Value + "</em:" + prop + ">\n");
6673 }
6674 else {
6675 throw new Error("Cannot serialize unknown literal type");
6676 }
6677 }
6678 }
6679 items.sort();
6680 result += items.join("");
6681 return result;
6682 },
6683
6684 /**
6685 * Recursively serializes an RDF resource and all resources it links to.
6686 * This will only output EM_NS properties and will ignore any em:signature
6687 * property.
6688 * @param ds The datasource holding the data
6689 * @param resource The RDF resource to serialize
6690 * @param indent The current level of indent for pretty-printing.
6691 * Leave undefined for no indent
6692 * @returns a string containing the serialized resource.
6693 * @throws if the RDF data contains multiple references to the same resource.
6694 */
serializeResource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6695 serializeResource: function(ds, resource, indent)
6696 {
6697 if (this.resources.indexOf(resource) != -1 ) {
6698 // We cannot output multiple references to the same resource.
6699 throw new Error("Cannot serialize multiple references to "+resource.Value);
6700 }
6701 if (indent === undefined)
6702 indent = "";
6703
6704 this.resources.push(resource);
6705 var container = null;
6706 var type = "Description";
6707 if (this.cUtils.IsSeq(ds, resource)) {
6708 type = "Seq";
6709 container = this.cUtils.MakeSeq(ds, resource);
6710 }
6711 else if (this.cUtils.IsAlt(ds, resource)) {
6712 type = "Alt";
6713 container = this.cUtils.MakeAlt(ds, resource);
6714 }
6715 else if (this.cUtils.IsBag(ds, resource)) {
6716 type = "Bag";
6717 container = this.cUtils.MakeBag(ds, resource);
6718 }
6719
6720 var result = indent + "<RDF:" + type;
6721 if (!gRDF.IsAnonymousResource(resource))
6722 result += " about=\"" + this.escapeEntities(resource.ValueUTF8) + "\"";
6723 result += ">\n";
6724
6725 if (container)
6726 result += this.serializeContainerItems(ds, container, indent + this.INDENT);
6727
6728 result += this.serializeResourceProperties(ds, resource, indent + this.INDENT);
6729
6730 result += indent + "</RDF:" + type + ">\n";
6731 return result;
6732 }
6733 }
6734
6735 /**
6736 * A Datasource that holds Extensions.
6737 * - Implements nsIRDFDataSource to drive UI
6738 * - Uses a RDF/XML datasource for storage (this is undesirable)
6739 *
6740 * @constructor
6741 */
ExtensionsDataSource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6742 function ExtensionsDataSource(em) {
6743 this._em = em;
6744
6745 this._itemRoot = gRDF.GetResource(RDFURI_ITEM_ROOT);
6746 this._defaultTheme = gRDF.GetResource(RDFURI_DEFAULT_THEME);
6747 }
6748 ExtensionsDataSource.prototype = {
6749 _inner : null,
6750 _em : null,
6751 _itemRoot : null,
6752 _defaultTheme : null,
6753
6754 /**
6755 * Determines if an item's dependencies are satisfied. An item's dependencies
6756 * are satisifed when all items specified in the item's em:requires arc are
6757 * installed, enabled, and the version is compatible based on the em:requires
6758 * minVersion and maxVersion.
6759 * @param id
6760 * The ID of the item
6761 * @returns true if the item's dependencies are satisfied.
6762 * false if the item's dependencies are not satisfied.
6763 */
satisfiesDependencies
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6764 satisfiesDependencies: function(id) {
6765 var ds = this._inner;
6766 var itemResource = getResourceForID(id);
6767 var targets = ds.GetTargets(itemResource, EM_R("requires"), true);
6768 if (!targets.hasMoreElements())
6769 return true;
6770
6771 getVersionChecker();
6772 var idRes = EM_R("id");
6773 var minVersionRes = EM_R("minVersion");
6774 var maxVersionRes = EM_R("maxVersion");
6775 while (targets.hasMoreElements()) {
6776 var target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
6777 var dependencyID = stringData(ds.GetTarget(target, idRes, true));
6778 var version = null;
6779 version = this.getItemProperty(dependencyID, "version");
6780 if (version) {
6781 var opType = this.getItemProperty(dependencyID, "opType");
6782 if (opType == OP_NEEDS_DISABLE || opType == OP_NEEDS_UNINSTALL)
6783 return false;
6784
6785 if (this.getItemProperty(dependencyID, "userDisabled") == "true" ||
6786 this.getItemProperty(dependencyID, "appDisabled") == "true" ||
6787 this.getItemProperty(dependencyID, "userDisabled") == OP_NEEDS_DISABLE ||
6788 this.getItemProperty(dependencyID, "appDisabled") == OP_NEEDS_DISABLE)
6789 return false;
6790
6791 var minVersion = stringData(ds.GetTarget(target, minVersionRes, true));
6792 var maxVersion = stringData(ds.GetTarget(target, maxVersionRes, true));
6793 var compatible = (gVersionChecker.compare(version, minVersion) >= 0 &&
6794 gVersionChecker.compare(version, maxVersion) <= 0);
6795 if (!compatible)
6796 return false;
6797 }
6798 else {
6799 return false;
6800 }
6801 }
6802
6803 return true;
6804 },
6805
6806 /**
6807 * Determine if an item is compatible
6808 * @param datasource
6809 * The datasource to inspect for compatibility - can be the main
6810 * datasource or an Install Manifest.
6811 * @param source
6812 * The RDF Resource of the item to inspect for compatibility.
6813 * @param appVersion
6814 * The version of the application we are checking for compatibility
6815 * against. If this parameter is undefined, the version of the running
6816 * application is used.
6817 * @param platformVersion
6818 * The version of the toolkit to check compatibility against
6819 * @returns true if the item is compatible with this version of the
6820 * application, false, otherwise.
6821 */
isCompatible
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6822 isCompatible: function (datasource, source, appVersion, platformVersion) {
6823 // The Default Theme is always compatible.
6824 if (source.EqualsNode(this._defaultTheme))
6825 return true;
6826
6827 var appID = gApp.ID;
6828 if (appVersion === undefined)
6829 appVersion = gApp.version;
6830 if (platformVersion === undefined)
6831 var platformVersion = gApp.platformVersion;
6832
6833 var targets = datasource.GetTargets(source, EM_R("targetApplication"), true);
6834 var idRes = EM_R("id");
6835 var minVersionRes = EM_R("minVersion");
6836 var maxVersionRes = EM_R("maxVersion");
6837 var versionChecker = getVersionChecker();
6838 var rv = false;
6839 while (targets.hasMoreElements()) {
6840 var targetApp = targets.getNext().QueryInterface(Ci.nsIRDFResource);
6841 var id = stringData(datasource.GetTarget(targetApp, idRes, true));
6842 var minVersion = stringData(datasource.GetTarget(targetApp, minVersionRes, true));
6843 var maxVersion = stringData(datasource.GetTarget(targetApp, maxVersionRes, true));
6844 if (id == appID) {
6845 rv = (versionChecker.compare(appVersion, minVersion) >= 0) &&
6846 (versionChecker.compare(appVersion, maxVersion) <= 0);
6847 return rv; // App takes precedence over toolkit.
6848 }
6849
6850 if (id == TOOLKIT_ID) {
6851 rv = (versionChecker.compare(platformVersion, minVersion) >= 0) &&
6852 (versionChecker.compare(platformVersion, maxVersion) <= 0);
6853 // Keep looping, in case the app id is later.
6854 }
6855 }
6856 return rv;
6857 },
6858
6859 /**
6860 * Gets a list of items that are incompatible with a specific application version.
6861 * @param appID
6862 * The ID of the application - XXXben unused?
6863 * @param appVersion
6864 * The Version of the application to check for incompatibility against.
6865 * @param platformVersion
6866 * The version of the toolkit to check compatibility against
6867 * @param desiredType
6868 * The nsIUpdateItem type of items to look for
6869 * @param includeDisabled
6870 * Whether or not disabled items should be included in the set returned
6871 * @returns An array of nsIUpdateItems that are incompatible with the application
6872 * ID/Version supplied.
6873 */
getIncompatibleItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6874 getIncompatibleItemList: function(appID, appVersion, platformVersion,
6875 desiredType, includeDisabled) {
6876 var items = [];
6877 var ctr = getContainer(this._inner, this._itemRoot);
6878 var elements = ctr.GetElements();
6879 while (elements.hasMoreElements()) {
6880 var item = elements.getNext().QueryInterface(Ci.nsIRDFResource);
6881 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
6882 var type = this.getItemProperty(id, "type");
6883 // Skip this item if we're not seeking disabled items
6884 if (!includeDisabled && this.getItemProperty(id, "isDisabled") == "true")
6885 continue;
6886
6887 // If the id of this item matches one of the items potentially installed
6888 // with and maintained by this application AND it is installed in the
6889 // global install location (i.e. the place installed by the app installer)
6890 // it is and can be managed by the update file - it's not an item that has
6891 // been manually installed by the user into their profile dir, and as such
6892 // it is always compatible with the next release of the application since
6893 // we will continue to support it.
6894 var locationKey = this.getItemProperty(id, "installLocation");
6895 var appManaged = this.getItemProperty(id, "appManaged") == "true";
6896 if (appManaged && locationKey == KEY_APP_GLOBAL)
6897 continue;
6898
6899 if (type != -1 && (type & desiredType) &&
6900 !this.isCompatible(this, item, appVersion, platformVersion))
6901 items.push(this.getItemForID(id));
6902 }
6903 return items;
6904 },
6905
6906 /**
6907 * Retrieves a list of items that will be blocklisted by the application for
6908 * a specific application or toolkit version.
6909 * @param appVersion
6910 * The Version of the application to check the blocklist against.
6911 * @param platformVersion
6912 * The Version of the toolkit to check the blocklist against.
6913 * @param desiredType
6914 * The nsIUpdateItem type of items to look for
6915 * @param includeAppDisabled
6916 * Whether or not items that are or are already set to be disabled
6917 * by the app on next restart should be included in the set returned
6918 * @returns An array of nsIUpdateItems that are blocklisted with the application
6919 * or toolkit version supplied.
6920 */
getBlocklistedItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6921 getBlocklistedItemList: function(appVersion, platformVersion, desiredType,
6922 includeAppDisabled) {
6923 if (!gBlocklist)
6924 gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].
6925 getService(Ci.nsIBlocklistService);
6926 var items = [];
6927 var ctr = getContainer(this._inner, this._itemRoot);
6928 var elements = ctr.GetElements();
6929 while (elements.hasMoreElements()) {
6930 var item = elements.getNext().QueryInterface(Ci.nsIRDFResource);
6931 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
6932 var type = this.getItemProperty(id, "type");
6933
6934 if (!includeAppDisabled &&
6935 (this.getItemProperty(id, "appDisabled") == "true" ||
6936 this.getItemProperty(id, "appDisabled") == OP_NEEDS_DISABLE))
6937 continue;
6938
6939 var version = this.getItemProperty(id, "version");
6940 if (type != -1 && (type & desiredType) &&
6941 gBlocklist.isAddonBlocklisted(id, version, appVersion, platformVersion))
6942 items.push(this.getItemForID(id));
6943 }
6944 return items;
6945 },
6946
6947 /**
6948 * Gets a list of items of a specific type
6949 * @param desiredType
6950 * The nsIUpdateItem type of items to return
6951 * @param countRef
6952 * The XPCJS reference to the size of the returned array
6953 * @returns An array of nsIUpdateItems, populated only with an item for |id|
6954 * if |id| is non-null, otherwise all items matching the specified
6955 * type.
6956 */
getItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6957 getItemList: function(desiredType, countRef) {
6958 var items = [];
6959 var ctr = getContainer(this, this._itemRoot);
6960 var elements = ctr.GetElements();
6961 while (elements.hasMoreElements()) {
6962 var e = elements.getNext().QueryInterface(Ci.nsIRDFResource);
6963 var eID = stripPrefix(e.Value, PREFIX_ITEM_URI);
6964 var type = this.getItemProperty(eID, "type");
6965 if (type != -1 && type & desiredType)
6966 items.push(this.getItemForID(eID));
6967 }
6968 countRef.value = items.length;
6969 return items;
6970 },
6971
6972 /**
6973 * Retrieves a list of installed nsIUpdateItems of items that are dependent
6974 * on another item.
6975 * @param id
6976 * The ID of the item that other items depend on.
6977 * @param includeDisabled
6978 * Whether to include disabled items in the set returned.
6979 * @param countRef
6980 * The XPCJS reference to the number of items returned.
6981 * @returns An array of installed nsIUpdateItems that depend on the item
6982 * specified by the id parameter.
6983 */
getDependentItemListForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
6984 getDependentItemListForID: function(id, includeDisabled, countRef) {
6985 var items = [];
6986 var ds = this._inner;
6987 var ctr = getContainer(this, this._itemRoot);
6988 var elements = ctr.GetElements();
6989 while (elements.hasMoreElements()) {
6990 var e = elements.getNext().QueryInterface(Ci.nsIRDFResource);
6991 var dependentID = stripPrefix(e.Value, PREFIX_ITEM_URI);
6992 var targets = ds.GetTargets(e, EM_R("requires"), true);
6993 var idRes = EM_R("id");
6994 while (targets.hasMoreElements()) {
6995 var target = targets.getNext().QueryInterface(Ci.nsIRDFResource);
6996 var dependencyID = stringData(ds.GetTarget(target, idRes, true));
6997 if (dependencyID == id) {
6998 if (!includeDisabled && this.getItemProperty(dependentID, "isDisabled") == "true")
6999 continue;
7000 items.push(this.getItemForID(dependentID));
7001 break;
7002 }
7003 }
7004 }
7005 countRef.value = items.length;
7006 return items;
7007 },
7008
7009 /**
7010 * Constructs an nsIUpdateItem for the given item ID
7011 * @param id
7012 * The GUID of the item to construct a nsIUpdateItem for
7013 * @returns The nsIUpdateItem for the id.
7014 */
getItemForID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7015 getItemForID: function(id) {
7016 if (!this.visibleItems[id])
7017 return null;
7018
7019 var r = getResourceForID(id);
7020 if (!r)
7021 return null;
7022
7023 var targetAppInfo = this.getTargetApplicationInfo(id, this);
7024 var updateHash = this.getItemProperty(id, "availableUpdateHash");
7025 return makeItem(id,
7026 this.getItemProperty(id, "version"),
7027 this.getItemProperty(id, "installLocation"),
7028 targetAppInfo ? targetAppInfo.minVersion : "",
7029 targetAppInfo ? targetAppInfo.maxVersion : "",
7030 this.getItemProperty(id, "name"),
7031 this.getItemProperty(id, "availableUpdateURL"),
7032 updateHash ? updateHash : "",
7033 this.getItemProperty(id, "iconURL"),
7034 this.getItemProperty(id, "updateURL"),
7035 this.getItemProperty(id, "updateKey"),
7036 this.getItemProperty(id, "type"),
7037 targetAppInfo ? targetAppInfo.appID : gApp.ID);
7038 },
7039
7040 /**
7041 * Gets the name of the Install Location where an item is installed.
7042 * @param id
7043 * The GUID of the item to locate an Install Location for
7044 * @returns The string name of the Install Location where the item is
7045 * installed.
7046 */
getInstallLocationKey
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7047 getInstallLocationKey: function(id) {
7048 return this.getItemProperty(id, "installLocation");
7049 },
7050
7051 /**
7052 * Sets an RDF property on an item in a datasource. Does not create
7053 * multiple assertions
7054 * @param datasource
7055 * The target datasource where the property should be set
7056 * @param source
7057 * The RDF Resource to set the property on
7058 * @param property
7059 * The RDF Resource of the property to set
7060 * @param newValue
7061 * The RDF Node containing the new property value
7062 */
_setProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7063 _setProperty: function(datasource, source, property, newValue) {
7064 var oldValue = datasource.GetTarget(source, property, true);
7065 if (oldValue) {
7066 if (newValue)
7067 datasource.Change(source, property, oldValue, newValue);
7068 else
7069 datasource.Unassert(source, property, oldValue);
7070 }
7071 else if (newValue)
7072 datasource.Assert(source, property, newValue, true);
7073 },
7074
7075 /**
7076 * Gets the updated target application info if it exists for an item from
7077 * the Extensions datasource during an installation or upgrade.
7078 * @param id
7079 * The ID of the item to discover updated target application info for
7080 * @returns A JS Object with the following properties:
7081 * "id" The id of the item
7082 * "minVersion" The updated minimum version of the target
7083 * application that this item can run in
7084 * "maxVersion" The updated maximum version of the target
7085 * application that this item can run in
7086 */
getUpdatedTargetAppInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7087 getUpdatedTargetAppInfo: function(id) {
7088 // The default theme is always compatible so there is never update info.
7089 if (getResourceForID(id).EqualsNode(this._defaultTheme))
7090 return null;
7091
7092 var appID = gApp.ID;
7093 var r = getResourceForID(id);
7094 var targetApps = this._inner.GetTargets(r, EM_R("targetApplication"), true);
7095 if (!targetApps.hasMoreElements())
7096 targetApps = this._inner.GetTargets(gInstallManifestRoot, EM_R("targetApplication"), true);
7097 var outData = null;
7098 while (targetApps.hasMoreElements()) {
7099 var targetApp = targetApps.getNext();
7100 if (targetApp instanceof Ci.nsIRDFResource) {
7101 try {
7102 var foundAppID = stringData(this._inner.GetTarget(targetApp, EM_R("id"), true));
7103 // Different target application?
7104 if (foundAppID != appID && foundAppID != TOOLKIT_ID)
7105 continue;
7106 var updatedMinVersion = this._inner.GetTarget(targetApp, EM_R("updatedMinVersion"), true);
7107 var updatedMaxVersion = this._inner.GetTarget(targetApp, EM_R("updatedMaxVersion"), true);
7108 if (updatedMinVersion && updatedMaxVersion)
7109 outData = { id : id,
7110 targetAppID : foundAppID,
7111 minVersion : stringData(updatedMinVersion),
7112 maxVersion : stringData(updatedMaxVersion) };
7113 if (foundAppID == appID)
7114 return outData;
7115 }
7116 catch (e) {
7117 continue;
7118 }
7119 }
7120 }
7121 return outData;
7122 },
7123
7124 /**
7125 * Sets the updated target application info for an item in the Extensions
7126 * datasource during an installation or upgrade.
7127 * @param id
7128 * The ID of the item to set updated target application info for
7129 * @param targetAppID
7130 * The target application ID used for checking compatibility for this item.
7131 * @param updatedMinVersion
7132 * The updated minimum version of the target application that this
7133 * item can run in
7134 * @param updatedMaxVersion
7135 * The updated maximum version of the target application that this
7136 * item can run in
7137 *
7138 * @note Add-ons can specify a targetApplication id of toolkit@mozilla.org in
7139 * their install manifest for compatibility with all apps using a
7140 * specific release of the toolkit.
7141 */
setUpdatedTargetAppInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7142 setUpdatedTargetAppInfo: function(id, targetAppID, updatedMinVersion, updatedMaxVersion) {
7143 // The default theme is always compatible so it is never updated.
7144 if (getResourceForID(id).EqualsNode(this._defaultTheme))
7145 return;
7146
7147 // Version/Dependency Info
7148 var updatedMinVersionRes = EM_R("updatedMinVersion");
7149 var updatedMaxVersionRes = EM_R("updatedMaxVersion");
7150
7151 var appID = gApp.ID;
7152 var r = getResourceForID(id);
7153 var targetApps = this._inner.GetTargets(r, EM_R("targetApplication"), true);
7154 // add updatedMinVersion and updatedMaxVersion for an install else an upgrade
7155 if (!targetApps.hasMoreElements()) {
7156 var idRes = EM_R("id");
7157 var targetRes = getResourceForID(id);
7158 var property = EM_R("targetApplication");
7159 var anon = gRDF.GetAnonymousResource();
7160 this._inner.Assert(anon, idRes, EM_L(appID), true);
7161 this._inner.Assert(anon, updatedMinVersionRes, EM_L(updatedMinVersion), true);
7162 this._inner.Assert(anon, updatedMaxVersionRes, EM_L(updatedMaxVersion), true);
7163 this._inner.Assert(targetRes, property, anon, true);
7164 }
7165 else {
7166 while (targetApps.hasMoreElements()) {
7167 var targetApp = targetApps.getNext();
7168 if (targetApp instanceof Ci.nsIRDFResource) {
7169 var foundAppID = stringData(this._inner.GetTarget(targetApp, EM_R("id"), true));
7170 // Different target application?
7171 if (foundAppID != targetAppID)
7172 continue;
7173 this._inner.Assert(targetApp, updatedMinVersionRes, EM_L(updatedMinVersion), true);
7174 this._inner.Assert(targetApp, updatedMaxVersionRes, EM_L(updatedMaxVersion), true);
7175 break;
7176 }
7177 }
7178 }
7179 this.Flush();
7180 },
7181
7182 /**
7183 * Gets the target application info for an item from a datasource.
7184 * @param id
7185 * The GUID of the item to discover target application info for
7186 * @param datasource
7187 * The datasource to look up target application info in
7188 * @returns A JS Object with the following properties:
7189 * "appID" The target application ID used for checking
7190 * compatibility for this item.
7191 * "minVersion" The minimum version of the target application
7192 * that this item can run in
7193 * "maxVersion" The maximum version of the target application
7194 * that this item can run in
7195 * or null, if no target application data exists for the specified
7196 * id in the supplied datasource.
7197 */
getTargetApplicationInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7198 getTargetApplicationInfo: function(id, datasource) {
7199 var appID = gApp.ID;
7200 // The default theme is always compatible.
7201 if (getResourceForID(id).EqualsNode(this._defaultTheme)) {
7202 var ver = gApp.version;
7203 return { appID: appID, minVersion: ver, maxVersion: ver };
7204 }
7205
7206 var r = getResourceForID(id);
7207 var targetApps = datasource.GetTargets(r, EM_R("targetApplication"), true);
7208 if (!targetApps)
7209 return null;
7210
7211 if (!targetApps.hasMoreElements())
7212 targetApps = datasource.GetTargets(gInstallManifestRoot, EM_R("targetApplication"), true);
7213 var outData = null;
7214 while (targetApps.hasMoreElements()) {
7215 var targetApp = targetApps.getNext();
7216 if (targetApp instanceof Ci.nsIRDFResource) {
7217 try {
7218 var foundAppID = stringData(datasource.GetTarget(targetApp, EM_R("id"), true));
7219 // Different target application?
7220 if (foundAppID != appID && foundAppID != TOOLKIT_ID)
7221 continue;
7222
7223 outData = { appID: foundAppID,
7224 minVersion: stringData(datasource.GetTarget(targetApp, EM_R("minVersion"), true)),
7225 maxVersion: stringData(datasource.GetTarget(targetApp, EM_R("maxVersion"), true)) };
7226 if (foundAppID == appID)
7227 return outData;
7228 }
7229 catch (e) {
7230 continue;
7231 }
7232 }
7233 }
7234 return outData;
7235 },
7236
7237 /**
7238 * Sets the target application info for an item in a datasource.
7239 * @param id
7240 * The GUID of the item to discover target application info for
7241 * @param targetAppID
7242 * The target application ID used for checking compatibility for this
7243 * item.
7244 * @param minVersion
7245 * The minimum version of the target application that this item can
7246 * run in
7247 * @param maxVersion
7248 * The maximum version of the target application that this item can
7249 * run in
7250 * @param datasource
7251 * The datasource to look up target application info in
7252 *
7253 * @note Add-ons can specify a targetApplication id of toolkit@mozilla.org in
7254 * their install manifest for compatibility with all apps using a
7255 * specific release of the toolkit.
7256 */
setTargetApplicationInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7257 setTargetApplicationInfo: function(id, targetAppID, minVersion, maxVersion, datasource) {
7258 var targetDataSource = datasource;
7259 if (!targetDataSource)
7260 targetDataSource = this._inner;
7261
7262 var appID = gApp.ID;
7263 var r = getResourceForID(id);
7264 var targetApps = targetDataSource.GetTargets(r, EM_R("targetApplication"), true);
7265 if (!targetApps.hasMoreElements())
7266 targetApps = datasource.GetTargets(gInstallManifestRoot, EM_R("targetApplication"), true);
7267 while (targetApps.hasMoreElements()) {
7268 var targetApp = targetApps.getNext();
7269 if (targetApp instanceof Ci.nsIRDFResource) {
7270 var foundAppID = stringData(targetDataSource.GetTarget(targetApp, EM_R("id"), true));
7271 // Different target application?
7272 if (foundAppID != targetAppID)
7273 continue;
7274
7275 this._setProperty(targetDataSource, targetApp, EM_R("minVersion"), EM_L(minVersion));
7276 this._setProperty(targetDataSource, targetApp, EM_R("maxVersion"), EM_L(maxVersion));
7277
7278 // If we were setting these properties on the main datasource, flush
7279 // it now. (Don't flush changes set on Install Manifests - they are
7280 // fleeting).
7281 if (!datasource)
7282 this.Flush();
7283
7284 break;
7285 }
7286 }
7287 },
7288
7289 /**
7290 * Gets a property of an item
7291 * @param id
7292 * The GUID of the item
7293 * @param property
7294 * The name of the property (excluding EM_NS)
7295 * @returns The literal value of the property, or undefined if there is no
7296 * value.
7297 */
getItemProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7298 getItemProperty: function(id, property) {
7299 var item = getResourceForID(id);
7300 if (!item) {
7301 LOG("getItemProperty failing for lack of an item. This means getResourceForItem \
7302 failed to locate a resource for aItemID (item ID = " + id + ", property = " + property + ")");
7303 }
7304 else
7305 return this._getItemProperty(item, property);
7306 return undefined;
7307 },
7308
7309 /**
7310 * Gets a property of an item resource
7311 * @param itemResource
7312 * The RDF Resource of the item
7313 * @param property
7314 * The name of the property (excluding EM_NS)
7315 * @returns The literal value of the property, or undefined if there is no
7316 * value.
7317 */
_getItemProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7318 _getItemProperty: function(itemResource, property) {
7319 var target = this.GetTarget(itemResource, EM_R(property), true);
7320 var value = stringData(target);
7321 if (value === undefined)
7322 value = intData(target);
7323 return value === undefined ? "" : value;
7324 },
7325
7326 /**
7327 * Sets a property on an item.
7328 * @param id
7329 * The GUID of the item
7330 * @param propertyArc
7331 * The RDF Resource of the property arc
7332 * @param propertyValue
7333 * A nsIRDFLiteral value of the property to be set
7334 */
setItemProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7335 setItemProperty: function (id, propertyArc, propertyValue) {
7336 var item = getResourceForID(id);
7337 this._setProperty(this._inner, item, propertyArc, propertyValue);
7338 this.Flush();
7339 },
7340
7341 /**
7342 * Inserts the RDF resource for an item into a container.
7343 * @param id
7344 * The GUID of the item
7345 */
insertItemIntoContainer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7346 insertItemIntoContainer: function(id) {
7347 // Get the target container and resource
7348 var ctr = getContainer(this._inner, this._itemRoot);
7349 var itemResource = getResourceForID(id);
7350 // Don't bother adding the extension to the list if it's already there.
7351 // (i.e. we're upgrading)
7352 var oldIndex = ctr.IndexOf(itemResource);
7353 if (oldIndex == -1)
7354 ctr.AppendElement(itemResource);
7355 this.Flush();
7356 },
7357
7358 /**
7359 * Removes the RDF resource for an item from its container.
7360 * @param id
7361 * The GUID of the item
7362 */
removeItemFromContainer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7363 removeItemFromContainer: function(id) {
7364 var ctr = getContainer(this._inner, this._itemRoot);
7365 var itemResource = getResourceForID(id);
7366 ctr.RemoveElement(itemResource, true);
7367 this.Flush();
7368 },
7369
7370 /**
7371 * Removes a corrupt item entry from the extension list added due to buggy
7372 * code in previous EM versions!
7373 * @param id
7374 * The GUID of the item
7375 */
removeCorruptItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7376 removeCorruptItem: function(id) {
7377 this.removeItemMetadata(id);
7378 this.removeItemFromContainer(id);
7379 this.visibleItems[id] = null;
7380 },
7381
7382 /**
7383 * Removes a corrupt download entry from the list
7384 * @param uri
7385 * The RDF URI of the item.
7386 * @returns The RDF Resource of the removed entry
7387 */
removeCorruptDLItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7388 removeCorruptDLItem: function(uri) {
7389 var itemResource = gRDF.GetResource(uri);
7390 var ctr = getContainer(this._inner, this._itemRoot);
7391 if (ctr.IndexOf(itemResource) != -1) {
7392 ctr.RemoveElement(itemResource, true);
7393 this._cleanResource(itemResource);
7394 this.Flush();
7395 }
7396 return itemResource;
7397 },
7398
7399 /**
7400 * Copies localized properties from an install manifest to the datasource
7401 *
7402 * @param installManifest
7403 * The Install Manifest datasource we are copying from
7404 * @param source
7405 * The source resource of the localized properties
7406 * @param target
7407 * The target resource to store the localized properties
7408 */
_addLocalizedMetadata
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7409 _addLocalizedMetadata: function(installManifest, sourceRes, targetRes)
7410 {
7411 var singleProps = ["name", "description", "creator", "homepageURL"];
7412
7413 for (var i = 0; i < singleProps.length; ++i) {
7414 var property = EM_R(singleProps[i]);
7415 var literal = installManifest.GetTarget(sourceRes, property, true);
7416 // If literal is null, _setProperty will remove any existing.
7417 this._setProperty(this._inner, targetRes, property, literal);
7418 }
7419
7420 // Assert properties with multiple values
7421 var manyProps = ["developer", "translator", "contributor"];
7422 for (var i = 0; i < manyProps.length; ++i) {
7423 var property = EM_R(manyProps[i]);
7424 var literals = installManifest.GetTargets(sourceRes, property, true);
7425
7426 var oldValues = this._inner.GetTargets(targetRes, property, true);
7427 while (oldValues.hasMoreElements()) {
7428 var oldValue = oldValues.getNext().QueryInterface(Ci.nsIRDFNode);
7429 this._inner.Unassert(targetRes, property, oldValue);
7430 }
7431 while (literals.hasMoreElements()) {
7432 var literal = literals.getNext().QueryInterface(Ci.nsIRDFNode);
7433 this._inner.Assert(targetRes, property, literal, true);
7434 }
7435 }
7436
7437 },
7438
7439 /**
7440 * Copies metadata from an Install Manifest Datasource into the Extensions
7441 * DataSource.
7442 * @param id
7443 * The GUID of the item
7444 * @param installManifest
7445 * The Install Manifest datasource we are copying from
7446 * @param installLocation
7447 * The Install Location of the item.
7448 */
addItemMetadata
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7449 addItemMetadata: function(id, installManifest, installLocation) {
7450 var targetRes = getResourceForID(id);
7451 // Remove any temporary assertions used for the install process
7452 this._setProperty(this._inner, targetRes, EM_R("newVersion"), null);
7453 // Copy the assertions over from the source datasource.
7454 // Assert properties with single values
7455 var singleProps = ["version", "updateURL", "updateService", "optionsURL",
7456 "aboutURL", "iconURL", "internalName", "updateKey"];
7457
7458 // Items installed into restricted Install Locations can also be locked
7459 // (can't be removed or disabled), and hidden (not shown in the UI)
7460 if (installLocation.restricted)
7461 singleProps = singleProps.concat(["locked", "hidden"]);
7462 if (installLocation.name == KEY_APP_GLOBAL)
7463 singleProps = singleProps.concat(["appManaged"]);
7464 for (var i = 0; i < singleProps.length; ++i) {
7465 var property = EM_R(singleProps[i]);
7466 var literal = installManifest.GetTarget(gInstallManifestRoot, property, true);
7467 // If literal is null, _setProperty will remove any existing.
7468 this._setProperty(this._inner, targetRes, property, literal);
7469 }
7470
7471 var localizedProp = EM_R("localized");
7472 var localeProp = EM_R("locale");
7473 // Remove old localized properties
7474 var oldValues = this._inner.GetTargets(targetRes, localizedProp, true);
7475 while (oldValues.hasMoreElements()) {
7476 var oldValue = oldValues.getNext().QueryInterface(Ci.nsIRDFNode);
7477 this._cleanResource(oldValue);
7478 this._inner.Unassert(targetRes, localizedProp, oldValue);
7479 }
7480 // Add each localized property
7481 var localizations = installManifest.GetTargets(gInstallManifestRoot, localizedProp, true);
7482 while (localizations.hasMoreElements()) {
7483 var localization = localizations.getNext().QueryInterface(Ci.nsIRDFResource);
7484 var anon = gRDF.GetAnonymousResource();
7485 var literals = installManifest.GetTargets(localization, localeProp, true);
7486 while (literals.hasMoreElements()) {
7487 var literal = literals.getNext().QueryInterface(Ci.nsIRDFNode);
7488 this._inner.Assert(anon, localeProp, literal, true);
7489 }
7490 this._addLocalizedMetadata(installManifest, localization, anon);
7491 this._inner.Assert(targetRes, localizedProp, anon, true);
7492 }
7493 // Add the fallback properties
7494 this._addLocalizedMetadata(installManifest, gInstallManifestRoot, targetRes);
7495
7496 // Version/Dependency Info
7497 var versionProps = ["targetApplication", "requires"];
7498 var idRes = EM_R("id");
7499 var minVersionRes = EM_R("minVersion");
7500 var maxVersionRes = EM_R("maxVersion");
7501 for (var i = 0; i < versionProps.length; ++i) {
7502 var property = EM_R(versionProps[i]);
7503 var newVersionInfos = installManifest.GetTargets(gInstallManifestRoot, property, true);
7504
7505 var oldVersionInfos = this._inner.GetTargets(targetRes, property, true);
7506 while (oldVersionInfos.hasMoreElements()) {
7507 var oldVersionInfo = oldVersionInfos.getNext().QueryInterface(Ci.nsIRDFResource);
7508 this._cleanResource(oldVersionInfo);
7509 this._inner.Unassert(targetRes, property, oldVersionInfo);
7510 }
7511 while (newVersionInfos.hasMoreElements()) {
7512 var newVersionInfo = newVersionInfos.getNext().QueryInterface(Ci.nsIRDFResource);
7513 var anon = gRDF.GetAnonymousResource();
7514 this._inner.Assert(anon, idRes, installManifest.GetTarget(newVersionInfo, idRes, true), true);
7515 this._inner.Assert(anon, minVersionRes, installManifest.GetTarget(newVersionInfo, minVersionRes, true), true);
7516 this._inner.Assert(anon, maxVersionRes, installManifest.GetTarget(newVersionInfo, maxVersionRes, true), true);
7517 this._inner.Assert(targetRes, property, anon, true);
7518 }
7519 }
7520 this.updateProperty(id, "opType");
7521 this.updateProperty(id, "updateable");
7522 this.Flush();
7523 },
7524
7525 /**
7526 * Strips an item entry of all assertions.
7527 * @param id
7528 * The GUID of the item
7529 */
removeItemMetadata
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7530 removeItemMetadata: function(id) {
7531 var item = getResourceForID(id);
7532 var resources = ["targetApplication", "requires", "localized"];
7533 for (var i = 0; i < resources.length; ++i) {
7534 var targetApps = this._inner.GetTargets(item, EM_R(resources[i]), true);
7535 while (targetApps.hasMoreElements()) {
7536 var targetApp = targetApps.getNext().QueryInterface(Ci.nsIRDFResource);
7537 this._cleanResource(targetApp);
7538 }
7539 }
7540
7541 this._cleanResource(item);
7542 },
7543
7544 /**
7545 * Strips a resource of all outbound assertions. We use methods like this
7546 * since the RDFXMLDatasource will write out all assertions, even if they
7547 * are not connected through our root.
7548 * @param resource
7549 * The resource to clean.
7550 */
_cleanResource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7551 _cleanResource: function(resource) {
7552 // Remove outward arcs
7553 var arcs = this._inner.ArcLabelsOut(resource);
7554 while (arcs.hasMoreElements()) {
7555 var arc = arcs.getNext().QueryInterface(Ci.nsIRDFResource);
7556 var targets = this._inner.GetTargets(resource, arc, true);
7557 while (targets.hasMoreElements()) {
7558 var value = targets.getNext().QueryInterface(Ci.nsIRDFNode);
7559 if (value)
7560 this._inner.Unassert(resource, arc, value);
7561 }
7562 }
7563 },
7564
7565 /**
7566 * Notify views that this propery has changed (this is for properties that
7567 * are implemented by this datasource rather than by the inner in-memory
7568 * datasource and thus do not get free change handling).
7569 * @param id
7570 * The GUID of the item to update the property for.
7571 * @param property
7572 * The property (less EM_NS) to update.
7573 */
updateProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7574 updateProperty: function(id, property) {
7575 var item = getResourceForID(id);
7576 this._updateProperty(item, property);
7577 },
7578
7579 /**
7580 * Notify views that this propery has changed (this is for properties that
7581 * are implemented by this datasource rather than by the inner in-memory
7582 * datasource and thus do not get free change handling). This allows updating
7583 * properties for download items which don't have the em item prefix in there
7584 ( resource value. In most instances updateProperty should be used.
7585 * @param item
7586 * The item to update the property for.
7587 * @param property
7588 * The property (less EM_NS) to update.
7589 */
_updateProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7590 _updateProperty: function(item, property) {
7591 if (item) {
7592 var propertyResource = EM_R(property);
7593 var value = this.GetTarget(item, propertyResource, true);
7594 for (var i = 0; i < this._observers.length; ++i) {
7595 if (value)
7596 this._observers[i].onChange(this, item, propertyResource,
7597 EM_L(""), value);
7598 else
7599 this._observers[i].onUnassert(this, item, propertyResource,
7600 EM_L(""));
7601 }
7602 }
7603 },
7604
7605 /**
7606 * Move an Item to the index of another item in its container.
7607 * @param movingID
7608 * The ID of the item to be moved.
7609 * @param destinationID
7610 * The ID of an item to move another item to.
7611 */
moveToIndexOf
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7612 moveToIndexOf: function(movingID, destinationID) {
7613 var extensions = gRDF.GetResource(RDFURI_ITEM_ROOT);
7614 var ctr = getContainer(this._inner, extensions);
7615 var item = gRDF.GetResource(movingID);
7616 var index = ctr.IndexOf(gRDF.GetResource(destinationID));
7617 if (index == -1)
7618 index = 1; // move to the beginning if destinationID is not found
7619 this._inner.beginUpdateBatch();
7620 ctr.RemoveElement(item, true);
7621 ctr.InsertElementAt(item, index, true);
7622 this._inner.endUpdateBatch();
7623 this.Flush();
7624 },
7625
7626 /**
7627 * Sorts addons of the specified type by the specified property starting from
7628 * the top of their container. If the addons are already sorted then no action
7629 * is performed.
7630 * @param type
7631 * The nsIUpdateItem type of the items to sort.
7632 * @param propertyName
7633 * The RDF property name used for sorting.
7634 * @param isAscending
7635 * true to sort ascending and false to sort descending
7636 */
sortTypeByProperty
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7637 sortTypeByProperty: function(type, propertyName, isAscending) {
7638 var items = [];
7639 var ctr = getContainer(this._inner, this._itemRoot);
7640 var elements = ctr.GetElements();
7641 // Base 0 ordinal for checking against the existing order after sorting
7642 var ordinal = 0;
7643 while (elements.hasMoreElements()) {
7644 var item = elements.getNext().QueryInterface(Ci.nsIRDFResource);
7645 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
7646 var itemType = this.getItemProperty(id, "type");
7647 if (itemType & type) {
7648 items.push({ item : item,
7649 ordinal: ordinal,
7650 sortkey: this.getItemProperty(id, propertyName).toLowerCase() });
7651 ordinal++;
7652 }
7653 }
7654
7655 var direction = isAscending ? 1 : -1;
7656 // Case insensitive sort
compare
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7657 function compare(a, b) {
7658 if (a.sortkey < b.sortkey) return (-1 * direction);
7659 if (a.sortkey > b.sortkey) return (1 * direction);
7660 return 0;
7661 }
7662 items.sort(compare);
7663
7664 // Check if there are any changes in the order of the items
7665 var isDirty = false;
7666 for (var i = 0; i < items.length; i++) {
7667 if (items[i].ordinal != i) {
7668 isDirty = true;
7669 break;
7670 }
7671 }
7672
7673 // If there are no changes then early return to avoid the perf impact
7674 if (!isDirty)
7675 return;
7676
7677 // Reorder the items by moving them to the top of the container
7678 this.beginUpdateBatch();
7679 for (i = 0; i < items.length; i++) {
7680 ctr.RemoveElement(items[i].item, true);
7681 ctr.InsertElementAt(items[i].item, i + 1, true);
7682 }
7683 this.endUpdateBatch();
7684 this.Flush();
7685 },
7686
7687 /**
7688 * Determines if an Item is an active download
7689 * @param id
7690 * The ID of the item. This will be a uri scheme without the
7691 * em item prefix so getProperty shouldn't be used.
7692 * @returns true if the item is an active download, false otherwise.
7693 */
isDownloadItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7694 isDownloadItem: function(id) {
7695 var downloadURL = stringData(this.GetTarget(gRDF.GetResource(id), EM_R("downloadURL"), true));
7696 return downloadURL && downloadURL != "";
7697 },
7698
7699 /**
7700 * Adds an entry representing an active download to the appropriate container
7701 * @param addon
7702 * An object implementing nsIUpdateItem for the addon being
7703 * downloaded.
7704 */
addDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7705 addDownload: function(addon) {
7706 // Updates have already been added to the datasource so we just update the
7707 // download state.
7708 if (addon.id != addon.xpiURL) {
7709 this.updateDownloadState(PREFIX_ITEM_URI + addon.id, "waiting");
7710 return;
7711 }
7712 var res = gRDF.GetResource(addon.xpiURL);
7713 this._setProperty(this._inner, res, EM_R("name"), EM_L(addon.name));
7714 this._setProperty(this._inner, res, EM_R("version"), EM_L(addon.version));
7715 this._setProperty(this._inner, res, EM_R("iconURL"), EM_L(addon.iconURL));
7716 this._setProperty(this._inner, res, EM_R("downloadURL"), EM_L(addon.xpiURL));
7717 this._setProperty(this._inner, res, EM_R("type"), EM_I(addon.type));
7718
7719 var ctr = getContainer(this._inner, this._itemRoot);
7720 if (ctr.IndexOf(res) == -1)
7721 ctr.AppendElement(res);
7722
7723 this.updateDownloadState(addon.xpiURL, "waiting");
7724 this.Flush();
7725 },
7726
7727 /**
7728 * Adds an entry representing an item that is incompatible and is being
7729 * checked for a compatibility update.
7730 * @param name
7731 * The display name of the item being checked
7732 * @param url
7733 * The URL string of the xpi file that has been staged.
7734 * @param type
7735 * The nsIUpdateItem type of the item
7736 * @param version
7737 * The version of the item
7738 */
addIncompatibleUpdateItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7739 addIncompatibleUpdateItem: function(name, url, type, version) {
7740 var iconURL = (type == Ci.nsIUpdateItem.TYPE_THEME) ? URI_GENERIC_ICON_THEME :
7741 URI_GENERIC_ICON_XPINSTALL;
7742 var extensionsStrings = BundleManager.getBundle(URI_EXTENSIONS_PROPERTIES);
7743 var updateMsg = extensionsStrings.formatStringFromName("incompatibleUpdateMessage",
7744 [BundleManager.appName, name], 2)
7745
7746 var res = gRDF.GetResource(url);
7747 this._setProperty(this._inner, res, EM_R("name"), EM_L(name));
7748 this._setProperty(this._inner, res, EM_R("iconURL"), EM_L(iconURL));
7749 this._setProperty(this._inner, res, EM_R("downloadURL"), EM_L(url));
7750 this._setProperty(this._inner, res, EM_R("type"), EM_I(type));
7751 this._setProperty(this._inner, res, EM_R("version"), EM_L(version));
7752 this._setProperty(this._inner, res, EM_R("incompatibleUpdate"), EM_L("true"));
7753 this._setProperty(this._inner, res, EM_R("description"), EM_L(updateMsg));
7754
7755 var ctr = getContainer(this._inner, this._itemRoot);
7756 if (ctr.IndexOf(res) == -1)
7757 ctr.AppendElement(res);
7758
7759 this.updateDownloadState(url, "incompatibleUpdate");
7760 this.Flush();
7761 },
7762
7763 /**
7764 * Removes an active download from the appropriate container
7765 * @param url
7766 * The URL string of the active download to be removed
7767 */
removeDownload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7768 removeDownload: function(url) {
7769 var res = gRDF.GetResource(url);
7770 var ctr = getContainer(this._inner, this._itemRoot);
7771 if (ctr.IndexOf(res) != -1)
7772 ctr.RemoveElement(res, true);
7773 this._cleanResource(res);
7774 this.updateDownloadState(url, null);
7775 this.Flush();
7776 },
7777
7778 /**
7779 * A hash of RDF resource values (e.g. Add-on IDs or XPI URLs) that represent
7780 * installation progress for a single browser session.
7781 */
7782 _progressData: { },
7783
7784 /**
7785 * Updates the install progress data for a given ID (e.g. Add-on IDs or
7786 * XPI URLs).
7787 * @param id
7788 * The URL string of the active download to be removed
7789 * @param state
7790 * The current state in the installation process. If null the object
7791 * is deleted from _progressData.
7792 */
updateDownloadState
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7793 updateDownloadState: function(id, state) {
7794 if (!state) {
7795 if (id in this._progressData)
7796 delete this._progressData[id];
7797 return;
7798 }
7799 else {
7800 if (!(id in this._progressData))
7801 this._progressData[id] = { };
7802 this._progressData[id].state = state;
7803 }
7804 var item = gRDF.GetResource(id);
7805 this._updateProperty(item, "state");
7806 },
7807
updateDownloadProgress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7808 updateDownloadProgress: function(id, progress) {
7809 if (!progress) {
7810 if (!(id in this._progressData))
7811 return;
7812 this._progressData[id].progress = null;
7813 }
7814 else {
7815 if (!(id in this._progressData))
7816 this.updateDownloadState(id, "downloading");
7817
7818 if (this._progressData[id].progress == progress)
7819 return;
7820
7821 this._progressData[id].progress = progress;
7822 }
7823 var item = gRDF.GetResource(id);
7824 this._updateProperty(item, "progress");
7825 },
7826
7827 /**
7828 * A GUID->location-key hash of items that are visible to the application.
7829 * These are items that show up in the Extension/Themes etc UI. If there is
7830 * an instance of the same item installed in Install Locations of differing
7831 * profiles, the item at the highest priority location will appear in this
7832 * list.
7833 */
7834 visibleItems: { },
7835
7836 /**
7837 * Walk the list of installed items and determine what the visible list is,
7838 * based on which items are visible at the highest priority locations.
7839 */
_buildVisibleItemList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7840 _buildVisibleItemList: function() {
7841 var ctr = getContainer(this, this._itemRoot);
7842 var items = ctr.GetElements();
7843 while (items.hasMoreElements()) {
7844 var item = items.getNext().QueryInterface(Ci.nsIRDFResource);
7845 // Resource URIs adopt the format: location-key,item-id
7846 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
7847 this.visibleItems[id] = this.getItemProperty(id, "installLocation");
7848 }
7849 },
7850
7851 /**
7852 * Updates an item's location in the visible item list.
7853 * @param id
7854 * The GUID of the item to update
7855 * @param locationKey
7856 * The name of the Install Location where the item is installed.
7857 * @param forceReplace
7858 * true if the new location should be used, regardless of its
7859 * priority relationship to existing entries, false if the location
7860 * should only be updated if its priority is lower than the existing
7861 * value.
7862 */
updateVisibleList
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7863 updateVisibleList: function(id, locationKey, forceReplace) {
7864 if (id in this.visibleItems && this.visibleItems[id]) {
7865 var oldLocation = InstallLocations.get(this.visibleItems[id]);
7866 var newLocation = InstallLocations.get(locationKey);
7867 if (forceReplace || newLocation.priority < oldLocation.priority)
7868 this.visibleItems[id] = locationKey;
7869 }
7870 else
7871 this.visibleItems[id] = locationKey;
7872 },
7873
7874 /**
7875 * Load the Extensions Datasource from disk.
7876 */
loadExtensions
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7877 loadExtensions: function() {
7878 var extensionsFile = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
7879 try {
7880 this._inner = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
7881 }
7882 catch (e) {
7883 ERROR("Datasource::loadExtensions: removing corrupted extensions datasource " +
7884 " file = " + extensionsFile.path + ", exception = " + e + "\n");
7885 extensionsFile.remove(false);
7886 return;
7887 }
7888
7889 var cu = Cc["@mozilla.org/rdf/container-utils;1"].
7890 getService(Ci.nsIRDFContainerUtils);
7891 cu.MakeSeq(this._inner, this._itemRoot);
7892
7893 this._buildVisibleItemList();
7894 },
7895
7896 /**
7897 * See nsIExtensionManager.idl
7898 */
onUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7899 onUpdateStarted: function() {
7900 LOG("Datasource: Update Started");
7901 },
7902
7903 /**
7904 * See nsIExtensionManager.idl
7905 */
onUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7906 onUpdateEnded: function() {
7907 LOG("Datasource: Update Ended");
7908 },
7909
7910 /**
7911 * See nsIExtensionManager.idl
7912 */
onAddonUpdateStarted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7913 onAddonUpdateStarted: function(addon) {
7914 if (!addon)
7915 throw Cr.NS_ERROR_INVALID_ARG;
7916
7917 LOG("Datasource: Addon Update Started: " + addon.id);
7918 this.updateProperty(addon.id, "availableUpdateURL");
7919 },
7920
7921 /**
7922 * See nsIExtensionManager.idl
7923 */
onAddonUpdateEnded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7924 onAddonUpdateEnded: function(addon, status) {
7925 if (!addon)
7926 throw Cr.NS_ERROR_INVALID_ARG;
7927
7928 LOG("Datasource: Addon Update Ended: " + addon.id + ", status: " + status);
7929 var url = null, hash = null, version = null;
7930 var updateAvailable = status == Ci.nsIAddonUpdateCheckListener.STATUS_UPDATE;
7931 if (updateAvailable) {
7932 url = EM_L(addon.xpiURL);
7933 if (addon.xpiHash)
7934 hash = EM_L(addon.xpiHash);
7935 version = EM_L(addon.version);
7936 }
7937 this.setItemProperty(addon.id, EM_R("availableUpdateURL"), url);
7938 this.setItemProperty(addon.id, EM_R("availableUpdateHash"), hash);
7939 this.setItemProperty(addon.id, EM_R("availableUpdateVersion"), version);
7940 this.updateProperty(addon.id, "availableUpdateURL");
7941 },
7942
7943 /////////////////////////////////////////////////////////////////////////////
7944 // nsIRDFDataSource
get_URI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7945 get URI() {
7946 return "rdf:extensions";
7947 },
7948
GetSource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7949 GetSource: function(property, target, truthValue) {
7950 return this._inner.GetSource(property, target, truthValue);
7951 },
7952
GetSources
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7953 GetSources: function(property, target, truthValue) {
7954 return this._inner.GetSources(property, target, truthValue);
7955 },
7956
7957 /**
7958 * Gets an URL to a theme's image file
7959 * @param item
7960 * The RDF Resource representing the item
7961 * @param fileName
7962 * The file to locate a URL for
7963 * @param fallbackURL
7964 * If the location fails, supply this URL instead
7965 * @returns An RDF Resource to the URL discovered, or the fallback
7966 * if the discovery failed.
7967 */
_getThemeImageURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7968 _getThemeImageURL: function(item, fileName, fallbackURL) {
7969 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
7970 var installLocation = this._em.getInstallLocation(id);
7971 var file = installLocation.getItemFile(id, fileName)
7972 if (file.exists())
7973 return gRDF.GetResource(getURLSpecFromFile(file));
7974
7975 if (id == stripPrefix(RDFURI_DEFAULT_THEME, PREFIX_ITEM_URI)) {
7976 var jarFile = getFile(KEY_APPDIR, [DIR_CHROME, FILE_DEFAULT_THEME_JAR]);
7977 var url = "jar:" + getURLSpecFromFile(jarFile) + "!/" + fileName;
7978 return gRDF.GetResource(url);
7979 }
7980
7981 return fallbackURL ? gRDF.GetResource(fallbackURL) : null;
7982 },
7983
7984 /**
7985 * Get the em:iconURL property (icon url of the item)
7986 */
_rdfGet_iconURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
7987 _rdfGet_iconURL: function(item, property) {
7988 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
7989 var type = this.getItemProperty(id, "type");
7990 if (type & Ci.nsIUpdateItem.TYPE_THEME)
7991 return this._getThemeImageURL(item, "icon.png", URI_GENERIC_ICON_THEME);
7992
7993 if (inSafeMode())
7994 return gRDF.GetResource(URI_GENERIC_ICON_XPINSTALL);
7995
7996 var hasIconURL = this._inner.hasArcOut(item, property);
7997 // If the addon doesn't have an IconURL property or it is disabled use the
7998 // generic icon URL instead.
7999 if (!hasIconURL || this.getItemProperty(id, "isDisabled") == "true")
8000 return gRDF.GetResource(URI_GENERIC_ICON_XPINSTALL);
8001 var iconURL = stringData(this._inner.GetTarget(item, property, true));
8002 try {
8003 var uri = newURI(iconURL);
8004 var scheme = uri.scheme;
8005 // Only allow chrome URIs or when installing http(s) URIs.
8006 if (scheme == "chrome" || (scheme == "http" || scheme == "https") &&
8007 this._inner.hasArcOut(item, EM_R("downloadURL")))
8008 return null;
8009 }
8010 catch (e) {
8011 }
8012 // Use a generic icon URL for addons that have an invalid iconURL.
8013 return gRDF.GetResource(URI_GENERIC_ICON_XPINSTALL);
8014 },
8015
8016 /**
8017 * Get the em:previewImage property (preview image of the item)
8018 */
_rdfGet_previewImage
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8019 _rdfGet_previewImage: function(item, property) {
8020 var type = this.getItemProperty(stripPrefix(item.Value, PREFIX_ITEM_URI), "type");
8021 if (type != -1 && type & Ci.nsIUpdateItem.TYPE_THEME)
8022 return this._getThemeImageURL(item, "preview.png", null);
8023 return null;
8024 },
8025
8026 /**
8027 * If we're in safe mode, the item is disabled by the user or app, or the
8028 * item is to be upgraded force the generic about dialog for the item.
8029 */
_rdfGet_aboutURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8030 _rdfGet_aboutURL: function(item, property) {
8031 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8032 if (inSafeMode() || this.getItemProperty(id, "isDisabled") == "true" ||
8033 this.getItemProperty(id, "opType") == OP_NEEDS_UPGRADE)
8034 return EM_L("");
8035
8036 return null;
8037 },
8038
_rdfGet_installDate
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8039 _rdfGet_installDate: function(item, property) {
8040 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8041 var key = this.getItemProperty(id, "installLocation");
8042 if (key && key in StartupCache.entries && id in StartupCache.entries[key] &&
8043 StartupCache.entries[key][id] && StartupCache.entries[key][id].mtime)
8044 return EM_D(StartupCache.entries[key][id].mtime * 1000000);
8045 return null;
8046 },
8047
8048 /**
8049 * Get the em:compatible property (whether or not this item is compatible)
8050 */
_rdfGet_compatible
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8051 _rdfGet_compatible: function(item, property) {
8052 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8053 var targetAppInfo = this.getTargetApplicationInfo(id, this);
8054 if (!targetAppInfo) {
8055 // When installing a new addon targetAppInfo does not exist yet
8056 if (this.getItemProperty(id, "opType") == OP_NEEDS_INSTALL)
8057 return EM_L("true");
8058 return EM_L("false");
8059 }
8060
8061 getVersionChecker();
8062 var appVersion = targetAppInfo.appID == TOOLKIT_ID ? gApp.platformVersion : gApp.version;
8063 if (gVersionChecker.compare(targetAppInfo.maxVersion, appVersion) < 0 ||
8064 gVersionChecker.compare(appVersion, targetAppInfo.minVersion) < 0) {
8065 // OK, this item is incompatible.
8066 return EM_L("false");
8067 }
8068 return EM_L("true");
8069 },
8070
8071 /**
8072 * Get the providesUpdatesSecurely property (whether or not this item has a
8073 * secure update mechanism)
8074 */
_rdfGet_providesUpdatesSecurely
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8075 _rdfGet_providesUpdatesSecurely: function(item, property) {
8076 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8077 if (this.getItemProperty(id, "updateKey") ||
8078 !this.getItemProperty(id, "updateURL") ||
8079 this.getItemProperty(id, "updateURL").substring(0, 6) == "https:")
8080 return EM_L("true");
8081 return EM_L("false");
8082 },
8083
8084 /**
8085 * Get the em:blocklisted property (whether or not this item is blocklisted)
8086 */
_rdfGet_blocklisted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8087 _rdfGet_blocklisted: function(item, property) {
8088 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8089 var version = this.getItemProperty(id, "version");
8090 if (!gBlocklist)
8091 gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].
8092 getService(Ci.nsIBlocklistService);
8093 if (gBlocklist.isAddonBlocklisted(id, version, null, null))
8094 return EM_L("true");
8095
8096 return EM_L("false");
8097 },
8098
8099 /**
8100 * Get the em:state property (represents the current phase of an install).
8101 */
_rdfGet_state
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8102 _rdfGet_state: function(item, property) {
8103 var id = item.Value;
8104 if (id in this._progressData)
8105 return EM_L(this._progressData[id].state);
8106 return null;
8107 },
8108
8109 /**
8110 * Get the em:progress property from the _progressData js object. By storing
8111 * progress which is updated repeastedly during a download we avoid
8112 * repeastedly writing it to the rdf file.
8113 */
_rdfGet_progress
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8114 _rdfGet_progress: function(item, property) {
8115 var id = item.Value;
8116 if (id in this._progressData)
8117 return EM_I(this._progressData[id].progress);
8118 return null;
8119 },
8120
8121 /**
8122 * Get the em:appManaged property. This prevents extensions from hiding
8123 * extensions installed into locations other than the app-global location.
8124 */
_rdfGet_appManaged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8125 _rdfGet_appManaged: function(item, property) {
8126 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8127 var locationKey = this.getItemProperty(id, "installLocation");
8128 if (locationKey != KEY_APP_GLOBAL)
8129 return EM_L("false");
8130 return null;
8131 },
8132
8133 /**
8134 * Get the em:hidden property. This prevents extensions from hiding
8135 * extensions installed into locations other than restricted locations.
8136 */
_rdfGet_hidden
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8137 _rdfGet_hidden: function(item, property) {
8138 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8139 var installLocation = InstallLocations.get(this.getInstallLocationKey(id));
8140 if (!installLocation || !installLocation.restricted)
8141 return EM_L("false");
8142 return null;
8143 },
8144
8145 /**
8146 * Get the em:locked property. This prevents extensions from locking
8147 * extensions installed into locations other than restricted locations.
8148 */
_rdfGet_locked
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8149 _rdfGet_locked: function(item, property) {
8150 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8151 var installLocation = InstallLocations.get(this.getInstallLocationKey(id));
8152 if (!installLocation || !installLocation.restricted)
8153 return EM_L("false");
8154 return null;
8155 },
8156
8157 /**
8158 * Get the em:satisfiesDependencies property - literal string "false" for
8159 * dependencies not satisfied (e.g. dependency disabled, incorrect version,
8160 * not installed etc.), and literal string "true" for dependencies satisfied.
8161 */
_rdfGet_satisfiesDependencies
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8162 _rdfGet_satisfiesDependencies: function(item, property) {
8163 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8164 if (this.satisfiesDependencies(id))
8165 return EM_L("true");
8166 return EM_L("false");
8167 },
8168
8169 /**
8170 * Get the em:opType property (controls widget state for the EM UI)
8171 * from the Startup Cache (e.g. extensions.cache)
8172 */
_rdfGet_opType
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8173 _rdfGet_opType: function(item, property) {
8174 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8175 var key = this.getItemProperty(id, "installLocation");
8176 if (key in StartupCache.entries && id in StartupCache.entries[key] &&
8177 StartupCache.entries[key][id] && StartupCache.entries[key][id].op != OP_NONE)
8178 return EM_L(StartupCache.entries[key][id].op);
8179 return null;
8180 },
8181
8182 /**
8183 * Gets a localizable property. Install Manifests are generally only in one
8184 * language, however an item can customize by providing localized prefs in
8185 * the form:
8186 *
8187 * extensions.{GUID}.[name|description|creator|homepageURL]
8188 *
8189 * to specify localized text for each of these properties.
8190 */
_getLocalizablePropertyValue
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8191 _getLocalizablePropertyValue: function(item, property) {
8192 // These are localizable properties that a language pack supplied by the
8193 // Extension may override.
8194 var prefName = PREF_EM_EXTENSION_FORMAT.replace(/%UUID%/,
8195 stripPrefix(item.Value, PREFIX_ITEM_URI)) +
8196 stripPrefix(property.Value, PREFIX_NS_EM);
8197 try {
8198 var value = gPref.getComplexValue(prefName,
8199 Ci.nsIPrefLocalizedString);
8200 if (value.data)
8201 return EM_L(value.data);
8202 }
8203 catch (e) {
8204 }
8205
8206 var localized = findClosestLocalizedResource(this._inner, item);
8207 if (localized) {
8208 var value = this._inner.GetTarget(localized, property, true);
8209 return value ? value : EM_L("");
8210 }
8211 return null;
8212 },
8213
8214 /**
8215 * Get the em:name property (name of the item)
8216 */
_rdfGet_name
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8217 _rdfGet_name: function(item, property) {
8218 return this._getLocalizablePropertyValue(item, property);
8219 },
8220
8221 /**
8222 * Get the em:description property (description of the item)
8223 */
_rdfGet_description
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8224 _rdfGet_description: function(item, property) {
8225 return this._getLocalizablePropertyValue(item, property);
8226 },
8227
8228 /**
8229 * Get the em:creator property (creator of the item)
8230 */
_rdfGet_creator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8231 _rdfGet_creator: function(item, property) {
8232 return this._getLocalizablePropertyValue(item, property);
8233 },
8234
8235 /**
8236 * Get the em:homepageURL property (homepage URL of the item)
8237 */
_rdfGet_homepageURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8238 _rdfGet_homepageURL: function(item, property) {
8239 return this._getLocalizablePropertyValue(item, property);
8240 },
8241
_rdfGet_availableUpdateInfo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8242 _rdfGet_availableUpdateInfo: function(item, property) {
8243 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8244 var uri = stringData(this._inner.GetTarget(item, EM_R("availableUpdateInfo"), true));
8245 if (uri) {
8246 uri = escapeAddonURI(this.getItemForID(id), uri, this);
8247 return EM_L(uri);
8248 }
8249 return null;
8250 },
8251
8252 /**
8253 * Get the em:isDisabled property. This will be true if the item has a
8254 * appDisabled or a userDisabled property that is true or OP_NEEDS_ENABLE.
8255 */
_rdfGet_isDisabled
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8256 _rdfGet_isDisabled: function(item, property) {
8257 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8258 if (this.getItemProperty(id, "userDisabled") == "true" ||
8259 this.getItemProperty(id, "appDisabled") == "true" ||
8260 this.getItemProperty(id, "userDisabled") == OP_NEEDS_ENABLE ||
8261 this.getItemProperty(id, "appDisabled") == OP_NEEDS_ENABLE)
8262 return EM_L("true");
8263 return EM_L("false");
8264 },
8265
_rdfGet_addonID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8266 _rdfGet_addonID: function(item, property) {
8267 var id = this._inner.GetTarget(item, EM_R("downloadURL"), true) ? item.Value :
8268 stripPrefix(item.Value, PREFIX_ITEM_URI);
8269 return EM_L(id);
8270 },
8271
8272 /**
8273 * Get the em:updateable property - this specifies whether the item is
8274 * allowed to be updated
8275 */
_rdfGet_updateable
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8276 _rdfGet_updateable: function(item, property) {
8277 var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
8278 var opType = this.getItemProperty(id, "opType");
8279 if (opType != OP_NONE || this.getItemProperty(id, "appManaged") == "true")
8280 return EM_L("false");
8281
8282 if (getPref("getBoolPref", (PREF_EM_ITEM_UPDATE_ENABLED.replace(/%UUID%/, id), false)) == true)
8283 return EM_L("false");
8284
8285 var installLocation = InstallLocations.get(this.getInstallLocationKey(id));
8286 if (!installLocation || !installLocation.canAccess)
8287 return EM_L("false");
8288
8289 return EM_L("true");
8290 },
8291
8292 /**
8293 * See nsIRDFDataSource.idl
8294 */
GetTarget
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8295 GetTarget: function(source, property, truthValue) {
8296 if (!source)
8297 return null;
8298
8299 var target = null;
8300 var getter = "_rdfGet_" + stripPrefix(property.Value, PREFIX_NS_EM);
8301 if (getter in this)
8302 target = this[getter](source, property);
8303
8304 return target || this._inner.GetTarget(source, property, truthValue);
8305 },
8306
8307 /**
8308 * Gets an enumeration of values of a localizable property. Install Manifests
8309 * are generally only in one language, however an item can customize by
8310 * providing localized prefs in the form:
8311 *
8312 * extensions.{GUID}.[contributor].1
8313 * extensions.{GUID}.[contributor].2
8314 * extensions.{GUID}.[contributor].3
8315 * ...
8316 *
8317 * to specify localized text for each of these properties.
8318 */
_getLocalizablePropertyValues
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8319 _getLocalizablePropertyValues: function(item, property) {
8320 // These are localizable properties that a language pack supplied by the
8321 // Extension may override.
8322 var values = [];
8323 var prefName = PREF_EM_EXTENSION_FORMAT.replace(/%UUID%/,
8324 stripPrefix(item.Value, PREFIX_ITEM_URI)) +
8325 stripPrefix(property.Value, PREFIX_NS_EM);
8326 var i = 0;
8327 while (true) {
8328 try {
8329 var value = gPref.getComplexValue(prefName + "." + ++i,
8330 Ci.nsIPrefLocalizedString);
8331 if (value.data)
8332 values.push(EM_L(value.data));
8333 }
8334 catch (e) {
8335 try {
8336 var value = gPref.getComplexValue(prefName,
8337 Ci.nsIPrefLocalizedString);
8338 if (value.data)
8339 values.push(EM_L(value.data));
8340 }
8341 catch (e) {
8342 }
8343 break;
8344 }
8345 }
8346 if (values.length > 0)
8347 return values;
8348
8349 var localized = findClosestLocalizedResource(this._inner, item);
8350 if (localized) {
8351 var targets = this._inner.GetTargets(localized, property, true);
8352 while (targets.hasMoreElements())
8353 values.push(targets.getNext());
8354 return values;
8355 }
8356 return null;
8357 },
8358
8359 /**
8360 * Get the em:developer property (developers of the extension)
8361 */
_rdfGets_developer
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8362 _rdfGets_developer: function(item, property) {
8363 return this._getLocalizablePropertyValues(item, property);
8364 },
8365
8366 /**
8367 * Get the em:translator property (translators of the extension)
8368 */
_rdfGets_translator
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8369 _rdfGets_translator: function(item, property) {
8370 return this._getLocalizablePropertyValues(item, property);
8371 },
8372
8373 /**
8374 * Get the em:contributor property (contributors to the extension)
8375 */
_rdfGets_contributor
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8376 _rdfGets_contributor: function(item, property) {
8377 return this._getLocalizablePropertyValues(item, property);
8378 },
8379
8380 /**
8381 * See nsIRDFDataSource.idl
8382 */
GetTargets
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8383 GetTargets: function(source, property, truthValue) {
8384 if (!source)
8385 return null;
8386
8387 var ary = null;
8388 var propertyName = stripPrefix(property.Value, PREFIX_NS_EM);
8389 var getter = "_rdfGets_" + propertyName;
8390 if (getter in this)
8391 ary = this[getter](source, property);
8392 else {
8393 // The template builder calls GetTargets when single value properties
8394 // are used in a triple.
8395 getter = "_rdfGet_" + propertyName;
8396 if (getter in this)
8397 ary = [ this[getter](source, property) ];
8398 }
8399
8400 return ary ? new ArrayEnumerator(ary)
8401 : this._inner.GetTargets(source, property, truthValue);
8402 },
8403
Assert
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8404 Assert: function(source, property, target, truthValue) {
8405 this._inner.Assert(source, property, target, truthValue);
8406 },
8407
Unassert
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8408 Unassert: function(source, property, target) {
8409 this._inner.Unassert(source, property, target);
8410 },
8411
Change
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8412 Change: function(source, property, oldTarget, newTarget) {
8413 this._inner.Change(source, property, oldTarget, newTarget);
8414 },
8415
Move
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8416 Move: function(oldSource, newSource, property, target) {
8417 this._inner.Move(oldSource, newSource, property, target);
8418 },
8419
HasAssertion
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8420 HasAssertion: function(source, property, target, truthValue) {
8421 if (!source || !property || !target)
8422 return false;
8423
8424 var getter = "_rdfGet_" + stripPrefix(property.Value, PREFIX_NS_EM);
8425 if (getter in this)
8426 return this[getter](source, property) == target;
8427 return this._inner.HasAssertion(source, property, target, truthValue);
8428 },
8429
8430 _observers: [],
AddObserver
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8431 AddObserver: function(observer) {
8432 for (var i = 0; i < this._observers.length; ++i) {
8433 if (this._observers[i] == observer)
8434 return;
8435 }
8436 this._observers.push(observer);
8437 this._inner.AddObserver(observer);
8438 },
8439
RemoveObserver
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8440 RemoveObserver: function(observer) {
8441 for (var i = 0; i < this._observers.length; ++i) {
8442 if (this._observers[i] == observer)
8443 this._observers.splice(i, 1);
8444 }
8445 this._inner.RemoveObserver(observer);
8446 },
8447
ArcLabelsIn
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8448 ArcLabelsIn: function(node) {
8449 return this._inner.ArcLabelsIn(node);
8450 },
8451
ArcLabelsOut
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8452 ArcLabelsOut: function(source) {
8453 return this._inner.ArcLabelsOut(source);
8454 },
8455
GetAllResources
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8456 GetAllResources: function() {
8457 return this._inner.GetAllResources();
8458 },
8459
IsCommandEnabled
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8460 IsCommandEnabled: function(sources, command, arguments) {
8461 return this._inner.IsCommandEnabled(sources, command, arguments);
8462 },
8463
DoCommand
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8464 DoCommand: function(sources, command, arguments) {
8465 this._inner.DoCommand(sources, command, arguments);
8466 },
8467
GetAllCmds
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8468 GetAllCmds: function(source) {
8469 return this._inner.GetAllCmds(source);
8470 },
8471
hasArcIn
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8472 hasArcIn: function(node, arc) {
8473 return this._inner.hasArcIn(node, arc);
8474 },
8475
hasArcOut
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8476 hasArcOut: function(source, arc) {
8477 return this._inner.hasArcOut(source, arc);
8478 },
8479
beginUpdateBatch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8480 beginUpdateBatch: function() {
8481 return this._inner.beginUpdateBatch();
8482 },
8483
endUpdateBatch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8484 endUpdateBatch: function() {
8485 return this._inner.endUpdateBatch();
8486 },
8487
8488 /**
8489 * See nsIRDFRemoteDataSource.idl
8490 */
get_loaded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8491 get loaded() {
8492 throw Cr.NS_ERROR_NOT_IMPLEMENTED;
8493 },
8494
Init
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8495 Init: function(uri) {
8496 },
8497
Refresh
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8498 Refresh: function(blocking) {
8499 },
8500
Flush
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8501 Flush: function() {
8502 if (this._inner instanceof Ci.nsIRDFRemoteDataSource)
8503 this._inner.Flush();
8504 },
8505
FlushTo
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8506 FlushTo: function(uri) {
8507 },
8508
8509 QueryInterface: XPCOMUtils.generateQI([Ci.nsIRDFDataSource,
8510 Ci.nsIRDFRemoteDataSource])
8511 };
8512
UpdateItem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8513 function UpdateItem () {}
8514 UpdateItem.prototype = {
8515 /**
8516 * See nsIUpdateService.idl
8517 */
init
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8518 init: function(id, version, installLocationKey, minAppVersion, maxAppVersion,
8519 name, downloadURL, xpiHash, iconURL, updateURL, updateKey, type,
8520 targetAppID) {
8521 this._id = id;
8522 this._version = version;
8523 this._installLocationKey = installLocationKey;
8524 this._minAppVersion = minAppVersion;
8525 this._maxAppVersion = maxAppVersion;
8526 this._name = name;
8527 this._downloadURL = downloadURL;
8528 this._xpiHash = xpiHash;
8529 this._iconURL = iconURL;
8530 this._updateURL = updateURL;
8531 this._updateKey = updateKey;
8532 this._type = type;
8533 this._targetAppID = targetAppID;
8534 },
8535
8536 /**
8537 * See nsIUpdateService.idl
8538 */
get_id
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8539 get id() { return this._id; },
get_version
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8540 get version() { return this._version; },
get_installLocationKey
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8541 get installLocationKey(){ return this._installLocationKey;},
get_minAppVersion
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8542 get minAppVersion() { return this._minAppVersion; },
get_maxAppVersion
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8543 get maxAppVersion() { return this._maxAppVersion; },
get_name
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8544 get name() { return this._name; },
get_xpiURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8545 get xpiURL() { return this._downloadURL; },
get_xpiHash
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8546 get xpiHash() { return this._xpiHash; },
get_iconURL
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8547 get iconURL() { return this._iconURL },
get_updateRDF
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8548 get updateRDF() { return this._updateURL; },
get_updateKey
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8549 get updateKey() { return this._updateKey; },
get_type
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8550 get type() { return this._type; },
get_targetAppID
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8551 get targetAppID() { return this._targetAppID; },
8552
8553 /**
8554 * See nsIUpdateService.idl
8555 */
get_objectSource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8556 get objectSource() {
8557 return { id : this._id,
8558 version : this._version,
8559 installLocationKey : this._installLocationKey,
8560 minAppVersion : this._minAppVersion,
8561 maxAppVersion : this._maxAppVersion,
8562 name : this._name,
8563 xpiURL : this._downloadURL,
8564 xpiHash : this._xpiHash,
8565 iconURL : this._iconURL,
8566 updateRDF : this._updateURL,
8567 updateKey : this._updateKey,
8568 type : this._type,
8569 targetAppID : this._targetAppID
8570 }.toSource();
8571 },
8572
8573 classDescription: "Update Item",
8574 contractID: "@mozilla.org/updates/item;1",
8575 classID: Components.ID("{F3294B1C-89F4-46F8-98A0-44E1EAE92518}"),
8576 QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdateItem])
8577 };
8578
8579 var gEmSingleton = null;
8580 var EmFactory = {
createInstance
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8581 createInstance: function(outer, iid) {
8582 if (outer != null)
8583 throw Cr.NS_ERROR_NO_AGGREGATION;
8584
8585 if (!gEmSingleton)
8586 gEmSingleton = new ExtensionManager();
8587 return gEmSingleton.QueryInterface(iid);
8588 }
8589 };
8590
DatasourceModule
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8591 function DatasourceModule() {}
8592 DatasourceModule.prototype = {
8593 classDescription: "Extension Manager Data Source",
8594 contractID: "@mozilla.org/rdf/datasource;1?name=extensions",
8595 classID: Components.ID("{69BB8313-2D4F-45EC-97E0-D39DA58ECCE9}"),
8596 _xpcom_factory: {
createInstance
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8597 createInstance: function() Cc[ExtensionManager.prototype.contractID].
8598 getService(Ci.nsIExtensionManager).datasource
8599 }
8600 };
8601
8602
NSGetModule
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
8603 function NSGetModule(compMgr, fileSpec)
8604 XPCOMUtils.generateModule([ExtensionManager, DatasourceModule, UpdateItem]);