summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/webextensions/test/xpcshell
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2018-02-10 02:51:36 -0500
committerMatt A. Tobin <email@mattatobin.com>2018-02-10 02:51:36 -0500
commit37d5300335d81cecbecc99812747a657588c63eb (patch)
tree765efa3b6a56bb715d9813a8697473e120436278 /toolkit/mozapps/webextensions/test/xpcshell
parentb2bdac20c02b12f2057b9ef70b0a946113a00e00 (diff)
parent4fb11cd5966461bccc3ed1599b808237be6b0de9 (diff)
downloadUXP-37d5300335d81cecbecc99812747a657588c63eb.tar
UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.gz
UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.lz
UXP-37d5300335d81cecbecc99812747a657588c63eb.tar.xz
UXP-37d5300335d81cecbecc99812747a657588c63eb.zip
Merge branch 'ext-work'
Diffstat (limited to 'toolkit/mozapps/webextensions/test/xpcshell')
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/.eslintrc.js7
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/BootstrapMonitor.jsm30
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_change.xml31
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update1.rdf144
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update2.rdf144
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update3.rdf144
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/app_update.xml62
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/manual_update.xml27
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_block.xml18
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_empty.xml7
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_start.xml30
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_warn.xml33
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/corrupt.xpi1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpibin0 -> 633 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpibin0 -> 197 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/bootstrap.js1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/install.rdf28
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/pluginInfoURL_block.xml45
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.xml3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad2.xml3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/empty.xml5
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/good.xml11
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/missing.xml3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpibin0 -> 452 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/bootstrap.js29
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/install.rdf24
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/test.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/bootstrap.js29
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/install.rdf24
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/test.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpibin0 -> 5151 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpibin0 -> 5298 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpibin0 -> 5158 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpibin0 -> 4471 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpibin0 -> 4433 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpibin0 -> 4474 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpibin0 -> 4436 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpibin0 -> 4487 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpibin0 -> 6443 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpibin0 -> 6563 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpibin0 -> 6425 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpibin0 -> 2436 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/install.rdf23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/test.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/install.rdf23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/test.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpibin0 -> 5161 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpibin0 -> 5150 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpibin0 -> 5149 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpibin0 -> 5155 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpibin0 -> 4627 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpibin0 -> 4634 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpibin0 -> 1156 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpibin0 -> 691 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/bootstrap.js1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpibin0 -> 4692 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpibin0 -> 4808 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpibin0 -> 4695 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpibin0 -> 4692 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpibin0 -> 4695 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpibin0 -> 4697 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpibin0 -> 4689 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpibin0 -> 4691 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpibin0 -> 4693 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpibin0 -> 4692 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpibin0 -> 4691 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpibin0 -> 4708 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpibin0 -> 4682 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpibin0 -> 4675 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpibin0 -> 5090 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpibin0 -> 4706 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpibin0 -> 5095 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpibin0 -> 4701 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpibin0 -> 5117 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpibin0 -> 4716 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpibin0 -> 5098 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpibin0 -> 4707 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpibin0 -> 735 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository.xml820
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_cache.xml182
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_empty.xml3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_failed.xml21
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml187
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_backgroundupdate.rdf70
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml21
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_prefs_1.xml28
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_regexp_1.xml20
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716.rdf181
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716_2.rdf23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug324121.rdf91
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug393285.xml30
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug394300.rdf159
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug424262.xml185
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_app.xml333
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_toolkit.xml208
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug468528.xml15
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_1.rdf17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_2.rdf17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_3.rdf17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_4.rdf17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_5.rdf17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_1.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_2.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_3.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_4.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_5.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_1.xml17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_2.xml10
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_empty.xml4
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml13
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml13
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpibin0 -> 458 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpibin0 -> 458 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpibin0 -> 577 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug542391.rdf25
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug554133.xml292
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug619730.xml7
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_bug655254.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_compatoverrides.xml228
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_corrupt.rdf44
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_complete/bootstrap.js24
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_defer/bootstrap.js34
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_ignore/bootstrap.js26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.json11
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.json11
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.json11
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_dictionary.rdf65
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/bootstrap.js21
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/install.rdf23
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist.xml304
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist2.xml31
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml783
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml32
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_1.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_2.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_3.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_install.rdf63
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_install.xml53
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate.rdf125
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate4.rdf46
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_no_update.json7
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/ancient.xml8
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/new.xml8
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/old.xml8
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtp.xml26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml10
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_proxy/bootstrap.js1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_softblocked1.xml9
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_sourceURI.xml18
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_temporary/bootstrap.js1
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_update.json215
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_update.rdf270
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_update.xml26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_update_multi.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.json327
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.rdf419
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_normal.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_strict.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/test_updateid.rdf26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpibin0 -> 452 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpibin0 -> 4182 bytes
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/head_addons.js1345
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/head_unpack.js3
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository.js625
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_cache.js704
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_compatmode.js90
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_ChromeManifestParser.js108
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_DeferredSave.js549
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_LightweightThemeManager.js598
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_ProductAddonChecker.js244
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_XPIStates.js299
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_XPIcancel.js66
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_addon_path_service.js38
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_asyncBlocklistLoad.js44
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_backgroundupdate.js126
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bad_json.js54
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_badschema.js404
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_gfx.js157
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_metadata_filters.js147
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_prefs.js148
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_regexp.js114
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_blocklistchange.js1305
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap.js1403
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_const.js17
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_globals.js37
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_resource.js56
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901.js35
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901_2.js60
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug299716.js208
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug299716_2.js50
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug324121.js178
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug335238.js173
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug371495.js35
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug384052.js103
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug393285.js316
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug394300.js56
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug397778.js117
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug406118.js155
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug424262.js62
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug425657.js27
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug430120.js135
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug449027.js429
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug455906.js517
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug465190.js39
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug468528.js58
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1_strictcompat.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_2.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3.js95
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3_strictcompat.js94
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_4.js92
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_1.js59
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_2.js41
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_3.js139
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug521905.js59
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug526598.js54
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug541420.js37
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug542391.js464
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug554133.js86
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug559800.js71
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug563256.js259
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug564030.js63
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug566626.js112
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug567184.js53
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug569138.js147
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug570173.js61
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug576735.js66
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug587088.js174
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug594058.js88
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug595081.js27
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug595573.js40
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug596607.js147
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug616841.js26
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug619730.js64
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug620837.js145
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug655254.js164
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug659772.js340
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug675371.js91
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug740612.js40
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug753900.js86
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug757663.js112
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_bug953156.js51
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_cache_certdb.js82
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_cacheflush.js127
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_checkCompatibility_themeOverride.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_checkcompatibility.js196
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_childprocess.js21
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_compatoverrides.js259
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_corrupt.js406
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_corrupt_strictcompat.js405
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_corruptfile.js83
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_dataDirectory.js50
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_default_providers_pref.js13
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_delay_update.js260
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_delay_update_webextension.js344
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_dependencies.js144
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_dictionary.js811
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_disable.js194
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_distribution.js273
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_dss.js824
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_duplicateplugins.js187
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_e10s_restartless.js429
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_error.js90
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_experiment.js131
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_ext_management.js137
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_filepointer.js403
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_fuel.js165
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_general.js58
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_getresource.js94
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Device.js96
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_DriverNew.js92
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js123
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js89
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OK.js94
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OS.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js95
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js95
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js96
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Vendor.js93
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Version.js145
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_prefs.js135
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_gmpProvider.js416
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_hasbinarycomponents.js82
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_hotfix.js309
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_hotfix_cert.js167
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_install.js1843
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_install_from_sources.js80
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_install_icons.js61
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_install_strictcompat.js1726
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_isDebuggable.js36
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_isReady.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_json_updatecheck.js372
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_langpack.js339
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_locale.js149
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_locked.js544
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_locked2.js297
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_locked_strictcompat.js567
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_manifest.js562
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_mapURIToAddonID.js347
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_metadata_update.js159
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate1.js231
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate2.js267
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate3.js229
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate4.js321
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate5.js139
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrateAddonRepository.js127
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_migrate_max_version.js103
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_multiprocessCompatible.js120
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_no_addons.js98
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_nodisable_hidden.js107
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_onPropertyChanged_appDisabled.js66
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_overrideblocklist.js200
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_pass_symbol.js43
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_permissions.js86
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_permissions_prefs.js74
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_pluginBlocklistCtp.js182
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_pluginInfoURL.js90
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_pluginchange.js283
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_plugins.js210
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_pref_properties.js221
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_provider_markSafe.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_provider_shutdown.js99
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_shutdown.js64
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_startup.js55
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_proxies.js240
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_proxy.js106
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_registry.js158
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_reload.js235
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_safemode.js115
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_schema_change.js317
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_seen.js211
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_seen_newprofile.js41
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_shutdown.js85
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_inject.js382
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_install.js265
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_long.js49
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_migrate.js194
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_multi.js55
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_updatepref.js136
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_signed_verify.js234
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_softblocked.js109
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_sourceURI.js66
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_startup.js932
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_strictcompatibility.js203
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_switch_os.js52
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_syncGUID.js156
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_system_delay_update.js461
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_system_reset.js418
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_system_update.js788
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_targetPlatforms.js146
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_temporary.js760
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_theme.js1139
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_types.js65
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_undothemeuninstall.js423
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_undouninstall.js792
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_uninstall.js216
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_update.js1398
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_updateCancel.js138
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_update_compatmode.js184
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_update_ignorecompat.js107
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_update_strictcompat.js1126
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_update_webextensions.js248
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_updatecheck.js236
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_updateid.js86
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_upgrade.js206
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_upgrade_strictcompat.js209
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_webextension.js421
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_webextension_embedded.js306
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_webextension_icons.js169
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_webextension_install.js478
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/test_webextension_paths.js55
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/xpcshell-shared.ini334
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/xpcshell-unpack.ini12
-rw-r--r--toolkit/mozapps/webextensions/test/xpcshell/xpcshell.ini50
390 files changed, 57814 insertions, 0 deletions
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/.eslintrc.js b/toolkit/mozapps/webextensions/test/xpcshell/.eslintrc.js
new file mode 100644
index 000000000..ba65517f9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = { // eslint-disable-line no-undef
+ "extends": [
+ "../../../../../testing/xpcshell/xpcshell.eslintrc.js"
+ ]
+};
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/BootstrapMonitor.jsm b/toolkit/mozapps/webextensions/test/xpcshell/data/BootstrapMonitor.jsm
new file mode 100644
index 000000000..7c1e4aa9d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/BootstrapMonitor.jsm
@@ -0,0 +1,30 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+this.EXPORTED_SYMBOLS = [ "monitor" ];
+
+function notify(event, originalMethod, data, reason) {
+ let info = {
+ event,
+ data: Object.assign({}, data, {
+ installPath: data.installPath.path,
+ resourceURI: data.resourceURI.spec,
+ }),
+ reason
+ };
+
+ let subject = {wrappedJSObject: {data}};
+
+ Services.obs.notifyObservers(subject, "bootstrapmonitor-event", JSON.stringify(info));
+
+ // If the bootstrap scope already declares a method call it
+ if (originalMethod)
+ originalMethod(data, reason);
+}
+
+// Allows a simple one-line bootstrap script:
+// Components.utils.import("resource://xpcshelldata/bootstrapmonitor.jsm").monitor(this);
+this.monitor = function(scope) {
+ for (let event of ["install", "startup", "shutdown", "uninstall"]) {
+ scope[event] = notify.bind(null, event, scope[event]);
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_change.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_change.xml
new file mode 100644
index 000000000..a229a653a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_change.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="3"/>
+ </emItem>
+ <!-- Two RegExp matches, so test flags work - first shouldn't match. -->
+ <emItem id="/^RegExp/">
+ <versionRange severity="1" minVersion="2" maxVersion="3"/>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange severity="2" minVersion="2" maxVersion="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update1.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update1.rdf
new file mode 100644
index 000000000..588290968
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update1.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_2.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update2.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update2.rdf
new file mode 100644
index 000000000..5c3747f5f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update2.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>3</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_3.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update3.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update3.rdf
new file mode 100644
index 000000000..d60708414
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update3.rdf
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:softblock1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft2_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft3_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:softblock4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft4_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:theme:softblock5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_soft5_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:hardblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_hard1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:regexpblock@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>4</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>*</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/blocklist_regexp1_1.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/app_update.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/app_update.xml
new file mode 100644
index 000000000..85a66fe55
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/app_update.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/^RegExp/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml
new file mode 100644
index 000000000..87011cd39
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist"/>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml
new file mode 100644
index 000000000..867a34255
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org"/>
+ <emItem id="/^RegExp/">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="/^RegExp/i"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/manual_update.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/manual_update.xml
new file mode 100644
index 000000000..df9276525
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/manual_update.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock2@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock3@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock4@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="softblock5@tests.mozilla.org">
+ <versionRange severity="1" minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="hardblock@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2"/>
+ </emItem>
+ <emItem id="/^RegExp/i">
+ <versionRange minVersion="1" maxVersion="2"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_block.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_block.xml
new file mode 100644
index 000000000..1f673ef2f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_block.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_1@tests.mozilla.org" blockID="test_bug455906_1@tests.mozilla.org"/>
+ <emItem id="test_bug455906_2@tests.mozilla.org" blockID="test_bug455906_2@tests.mozilla.org"/>
+ <emItem id="test_bug455906_3@tests.mozilla.org" blockID="test_bug455906_3@tests.mozilla.org"/>
+ <emItem id="test_bug455906_4@tests.mozilla.org" blockID="test_bug455906_4@tests.mozilla.org"/>
+ <emItem id="test_bug455906_5@tests.mozilla.org" blockID="test_bug455906_5@tests.mozilla.org"/>
+ <emItem id="test_bug455906_6@tests.mozilla.org" blockID="test_bug455906_6@tests.mozilla.org"/>
+ <emItem id="test_bug455906_7@tests.mozilla.org" blockID="test_bug455906_7@tests.mozilla.org"/>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="test_bug455906_plugin">
+ <match name="name" exp="^test_bug455906"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_empty.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_empty.xml
new file mode 100644
index 000000000..88d22f281
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_empty.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="dummy_bug455906_2@tests.mozilla.org"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_start.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_start.xml
new file mode 100644
index 000000000..daba6f4c1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_start.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_4@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_5@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ <emItem id="test_bug455906_6@tests.mozilla.org">
+ <versionRange severity="2"/>
+ </emItem>
+ <emItem id="dummy_bug455906_1@tests.mozilla.org"/>
+ </emItems>
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_4$"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_5$"/>
+ <versionRange severity="1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906_6$"/>
+ <versionRange severity="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_warn.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_warn.xml
new file mode 100644
index 000000000..232fd0d07
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_warn.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug455906_1@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_2@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_3@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_4@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_5@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_6@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ <emItem id="test_bug455906_7@tests.mozilla.org">
+ <versionRange severity="-1"/>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug455906"/>
+ <versionRange severity="-1"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/corrupt.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/corrupt.xpi
new file mode 100644
index 000000000..35d7bd5e5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/corrupt.xpi
@@ -0,0 +1 @@
+This is a corrupt zip file
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpi
new file mode 100644
index 000000000..0c30989aa
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpi
new file mode 100644
index 000000000..74ed2b817
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/install.rdf
new file mode 100644
index 000000000..f02a3869c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/install.rdf
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bootstrap1@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Bootstrap 1</em:name>
+ <em:description>Test Description</em:description>
+
+ <em:iconURL>chrome://foo/skin/icon.png</em:iconURL>
+ <em:aboutURL>chrome://foo/content/about.xul</em:aboutURL>
+ <em:optionsURL>chrome://foo/content/options.xul</em:optionsURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/pluginInfoURL_block.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/pluginInfoURL_block.xml
new file mode 100644
index 000000000..75e252a46
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/pluginInfoURL_block.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ </emItems>
+ <pluginItems>
+ <pluginItem blockID="test_plugin_wInfoURL">
+ <match name="name" exp="^test_with_infoURL"/>
+ <match name="version" exp="^5"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="*"/>
+ </targetApplication>
+ </versionRange>
+ <infoURL>http://test.url.com/</infoURL>
+ </pluginItem>
+ <pluginItem blockID="test_plugin_wAltInfoURL">
+ <match name="name" exp="^test_with_altInfoURL"/>
+ <match name="version" exp="^5"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="*"/>
+ </targetApplication>
+ </versionRange>
+ <infoURL>http://alt.test.url.com/</infoURL>
+ </pluginItem>
+ <pluginItem blockID="test_plugin_noInfoURL">
+ <match name="name" exp="^test_no_infoURL"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="*"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem blockID="test_plugin_newVersion">
+ <match name="name" exp="^test_newVersion"/>
+ <infoURL>http://test.url2.com/</infoURL>
+ <versionRange minVersion="1" maxVersion="2">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="*"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.txt
new file mode 100644
index 000000000..f17f98b15
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.txt
@@ -0,0 +1 @@
+Not an xml file! \ No newline at end of file
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.xml
new file mode 100644
index 000000000..0e3d415c4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<foobar></barfoo> \ No newline at end of file
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad2.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad2.xml
new file mode 100644
index 000000000..55ad1c7d5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad2.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<test></test>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/empty.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/empty.xml
new file mode 100644
index 000000000..42cb20bd0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/empty.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<updates>
+ <addons></addons>
+</updates>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/good.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/good.xml
new file mode 100644
index 000000000..e1da86fa5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/good.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<updates>
+ <addons>
+ <addon id="test1" URL="http://example.com/test1.xpi"/>
+ <addon id="test2" URL="http://example.com/test2.xpi" hashFunction="md5" hashValue="djhfgsjdhf"/>
+ <addon id="test3" URL="http://example.com/test3.xpi" version="1.0" size="45"/>
+ <addon id="test4"/>
+ <addon URL="http://example.com/test5.xpi"/>
+ </addons>
+</updates>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/missing.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/missing.xml
new file mode 100644
index 000000000..8c9501478
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/missing.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<updates></updates>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpi
new file mode 100644
index 000000000..51b00475a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/bootstrap.js
new file mode 100644
index 000000000..0cf01d319
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/bootstrap.js
@@ -0,0 +1,29 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const VERSION = 1;
+
+// Test steps chain from pref observers on *_reason,
+// so always set that last
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", data.oldVersion);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", data.oldVersion);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/install.rdf
new file mode 100644
index 000000000..9d08c357c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>test@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Add-on</em:name>
+ <em:updateURL>http://localhost:4444/update.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/test.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/test.txt
new file mode 100644
index 000000000..11686f61c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/test.txt
@@ -0,0 +1 @@
+This test file can be altered to break signing checks.
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/bootstrap.js
new file mode 100644
index 000000000..718c82be4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/bootstrap.js
@@ -0,0 +1,29 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const VERSION = 2;
+
+// Test steps chain from pref observers on *_reason,
+// so always set that last
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", data.oldVersion);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", VERSION);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", data.oldVersion);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", data.newVersion);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/install.rdf
new file mode 100644
index 000000000..0a345dd92
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>test@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+ <em:bootstrap>true</em:bootstrap>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Add-on</em:name>
+ <em:updateURL>http://localhost:4444/update.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>4</em:minVersion>
+ <em:maxVersion>6</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/test.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/test.txt
new file mode 100644
index 000000000..11686f61c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/test.txt
@@ -0,0 +1 @@
+This test file can be altered to break signing checks.
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpi
new file mode 100644
index 000000000..9d6f0c708
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpi
new file mode 100644
index 000000000..4496a90cf
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpi
new file mode 100644
index 000000000..e61e3c721
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpi
new file mode 100644
index 000000000..1682a7506
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpi
new file mode 100644
index 000000000..cd67e25fc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpi
new file mode 100644
index 000000000..e4040a274
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpi
new file mode 100644
index 000000000..ca453b9d5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpi
new file mode 100644
index 000000000..69579d2dc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpi
new file mode 100644
index 000000000..6e23eb214
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpi
new file mode 100644
index 000000000..0ba0f30d1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpi
new file mode 100644
index 000000000..33101f63c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi
new file mode 100644
index 000000000..3146870d8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/install.rdf
new file mode 100644
index 000000000..97ae60988
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>test@tests.mozilla.org</em:id>
+ <em:version>1.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Add-on</em:name>
+ <em:updateURL>http://localhost:4444/update.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/test.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/test.txt
new file mode 100644
index 000000000..11686f61c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/test.txt
@@ -0,0 +1 @@
+This test file can be altered to break signing checks.
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/install.rdf
new file mode 100644
index 000000000..df2fd8081
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>test@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Test Add-on</em:name>
+ <em:updateURL>http://localhost:4444/update.rdf</em:updateURL>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>4</em:minVersion>
+ <em:maxVersion>6</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/test.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/test.txt
new file mode 100644
index 000000000..11686f61c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/test.txt
@@ -0,0 +1 @@
+This test file can be altered to break signing checks.
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpi
new file mode 100644
index 000000000..ec38fcc65
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpi
new file mode 100644
index 000000000..fc2842399
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpi
new file mode 100644
index 000000000..327c8a187
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpi
new file mode 100644
index 000000000..efad21d1b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpi
new file mode 100644
index 000000000..d6ddbcec3
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpi
new file mode 100644
index 000000000..5898d83e4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpi
new file mode 100644
index 000000000..9d50f0825
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpi
new file mode 100644
index 000000000..6ba1efd72
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpi
new file mode 100644
index 000000000..2fc2fd189
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi
new file mode 100644
index 000000000..e7e50c8ea
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpi
new file mode 100644
index 000000000..a858cf74a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpi
new file mode 100644
index 000000000..911632e49
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpi
new file mode 100644
index 000000000..102a053bb
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpi
new file mode 100644
index 000000000..295e77611
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpi
new file mode 100644
index 000000000..954995619
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpi
new file mode 100644
index 000000000..dc8632aef
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpi
new file mode 100644
index 000000000..3f818172a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpi
new file mode 100644
index 000000000..1f70b1a75
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpi
new file mode 100644
index 000000000..fc636e97f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpi
new file mode 100644
index 000000000..ff620966d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpi
new file mode 100644
index 000000000..e474dbd59
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpi
new file mode 100644
index 000000000..1ccde90c5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpi
new file mode 100644
index 000000000..94d9e47d2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpi
new file mode 100644
index 000000000..28c8561c6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpi
new file mode 100644
index 000000000..daf55c0d4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpi
new file mode 100644
index 000000000..75cacbbc8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpi
new file mode 100644
index 000000000..2eb6b7fc9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpi
new file mode 100644
index 000000000..fb588b3e0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpi
new file mode 100644
index 000000000..7a5eb265d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpi
new file mode 100644
index 000000000..dc6749355
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpi
new file mode 100644
index 000000000..3c673ac2e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository.xml
new file mode 100644
index 000000000..0bebca2c1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository.xml
@@ -0,0 +1,820 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <!-- Passes all requirements -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <status id="8">Preliminarily Reviewed</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that a negative rating is ignored -->
+ <rating>-2</rating>
+ <!-- Test that a <reviews> with a blank review URL is ignored -->
+ <reviews num=" 1111 "> </reviews>
+ <!-- Test that a negative total_downloads is ignored -->
+ <total_downloads>-2</total_downloads>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+
+ <!-- Passes requirements. Tests optional attributes. Also tests that
+ integer properties that are NaN in the XML are ignored -->
+ <addon>
+ <name>PASS</name>
+ <!-- Test that extensions pass -->
+ <type id="1">Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.2</version>
+ <authors>
+ <!-- Test that the first author becomes the creator,
+ and the second one is a developer -->
+ <author>
+ <name>Test Creator 2</name>
+ <link>http://localhost:%PORT%/creator2.html</link>
+ </author>
+ <author>
+ <name>Test Developer 2</name>
+ <link>http://localhost:%PORT%/developer2.html</link>
+ </author>
+ </authors>
+ <summary>&lt;h1&gt;Test Summary 2&lt;/h1&gt;&lt;p&gt;paragraph&lt;/p&gt;</summary>
+ <description>Test Description 2&lt;br&gt;newline</description>
+ <developer_comments>Test Developer
+ Comments 2</developer_comments>
+ <eula>Test EULA 2</eula>
+ <icon size="64">http://localhost:%PORT%/icon2-64.png</icon>
+ <icon size="48">http://localhost:%PORT%/icon2-48.png</icon>
+ <icon size="32">http://localhost:%PORT%/icon2-32.png</icon>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that multiple preview images are correctly parsed -->
+ <previews>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full1-2.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail1-2.png</thumbnail>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full2-2.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail2-2.png</thumbnail>
+ <caption>Caption 2</caption>
+ </preview>
+ </previews>
+ <rating>NaN</rating>
+ <!-- Test that learnmore is used as the add-on's homepageURL
+ if there is no homepage defined -->
+ <learnmore>http://localhost:%PORT%/learnmore2.html</learnmore>
+ <homepage/>
+ <support>http://localhost:%PORT%/support2.html</support>
+ <contribution_data>
+ <link>http://localhost:%PORT%/contribution2.html</link>
+ <meet_developers>http://localhost:%PORT%/meetDevelopers2.html</meet_developers>
+ </contribution_data>
+ <reviews num="NaN">http://localhost:%PORT%/review2.html</reviews>
+ <total_downloads>NaN</total_downloads>
+ <weekly_downloads>NaN</weekly_downloads>
+ <daily_users>NaN</daily_users>
+ <last_updated epoch="NaN">Not an acual date</last_updated>
+ <install size="NaN" os="ALL">http://localhost:%PORT%/test2.xpi</install>
+ </addon>
+
+ <!-- Passes requirements. Tests optional attributes with extra whitespace. -->
+ <addon>
+ <name> PASS </name>
+ <!-- Test that themes pass -->
+ <type id=" 2 ">Theme</type>
+ <guid> test3@tests.mozilla.org </guid>
+ <version> 1.3 </version>
+ <authors>
+ <!-- Test that authors with blank names are ignored -->
+ <author>
+ <name> </name>
+ <link> http://localhost:%PORT%/ignore3.html </link>
+ </author>
+ <!-- Test that authors with blank links are ignored -->
+ <author>
+ <name> Test Creator Ignore </name>
+ <link> </link>
+ </author>
+ <author>
+ <name> Test Creator 3 </name>
+ <link> http://localhost:%PORT%/creator3.html </link>
+ </author>
+ <author>
+ <name> First Test Developer 3 </name>
+ <link> http://localhost:%PORT%/developer1-3.html </link>
+ </author>
+ <author>
+ <name> </name>
+ <link> </link>
+ </author>
+ <author>
+ <name> Second Test Developer 3 </name>
+ <link> http://localhost:%PORT%/developer2-3.html </link>
+ </author>
+ </authors>
+ <summary> Test Summary 3 </summary>
+ <description> Test Description 3&lt;br&gt;&lt;ul&gt;&lt;li&gt;List item 1&lt;li&gt;List item 2&lt;/ul&gt; </description>
+ <developer_comments> Test Developer Comments 3 </developer_comments>
+ <eula> Test EULA 3 </eula>
+ <icon size="32"> http://localhost:%PORT%/icon3.png </icon>
+ <status id=" 8 ">Preliminarily Reviewed</status>
+ <!-- Test that an incompatible + compatible application list passes -->
+ <compatible_applications>
+ <application>
+ <appID> unknown@tests.mozilla.org </appID>
+ <min_version> 1 </min_version>
+ <max_version> 1 </max_version>
+ </application>
+ <application>
+ <appID> xpcshell@tests.mozilla.org </appID>
+ <min_version> 1 </min_version>
+ <max_version> 1 </max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that primary images appear first in the add-on's screenshots array -->
+ <previews>
+ <preview primary=" 0 ">
+ <full type=" image/png "> http://localhost:%PORT%/full2-3.png </full>
+ <caption> Caption 2 - 3 </caption>
+ </preview>
+ <!-- Test that a preview without a <full> element is ignored -->
+ <preview primary=" 0 ">
+ <caption> Caption ignore - 3 </caption>
+ </preview>
+ <!-- Test that a preview with an empty <full> element is ignored -->
+ <preview primary=" 0 ">
+ <full type=" image/png "> </full>
+ <caption> Caption ignore - 3 </caption>
+ <preview primary=" 1 ">
+ <full type=" image/png "> http://localhost:%PORT%/full1-3.png </full>
+ <thumbnail type=" image/png "> http://localhost:%PORT%/thumbnail1-3.png </thumbnail>
+ <caption> Caption 1 - 3 </caption>
+ </preview>
+ <preview primary=" 0 ">
+ <full type=" image/png "> http://localhost:%PORT%/full3-3.png </full>
+ <thumbnail type=" image/png "> http://localhost:%PORT%/thumbnail3-3.png </thumbnail>
+ <caption> Caption 3 - 3 </caption>
+ </preview>
+ </preview>
+ </previews>
+ <!-- Test that a rating between 1 and 5 is correctly parsed -->
+ <rating> 2 </rating>
+ <!-- Test that hompage is used as the add-on's homepageURL
+ even if learnmore is defined -->
+ <learnmore> http://localhost:%PORT%/learnmore3.html </learnmore>
+ <homepage> http://localhost:%PORT%/homepage3.html </homepage>
+ <support> http://localhost:%PORT%/support3.html </support>
+ <contribution_data>
+ <link> http://localhost:%PORT%/contribution3.html </link>
+ <suggested_amount currency="USD"> $11.11 </suggested_amount>
+ <meet_developers> http://localhost:%PORT%/meetDevelopers3.html </meet_developers>
+ </contribution_data>
+ <reviews num=" 1111 "> http://localhost:%PORT%/review3.html </reviews>
+ <total_downloads> 2222 </total_downloads>
+ <weekly_downloads> 3333 </weekly_downloads>
+ <daily_users> 4444 </daily_users>
+ <last_updated epoch=" 1265033045 "> 2010-02-01T14:04:05Z </last_updated>
+ <!-- Test that an incompatible install is ignored -->
+ <install size=" 9999 " os=" UNKNOWN "> http://localhost:%PORT%/fail3.xpi </install>
+ <!-- Test that OS matching is case-insensitive -->
+ <install size=" 5555 " os=" xpCShell " hash=" sha1:c26f0b0d62e5dcddcda95074d3f3fedb9bbc26e3 "> http://localhost:%PORT%/test3.xpi </install>
+ </addon>
+
+ <!-- Fails because name is undefined -->
+ <addon>
+ <type id="1">Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.4</version>
+ <authors><author><name>Test Creator 4</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined name should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test4.xpi</install>
+ </addon>
+
+ <!-- Fails because name is empty-->
+ <addon>
+ <name> </name>
+ <type id="1">Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.5</version>
+ <authors><author><name>Test Creator 5</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty name should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test5.xpi</install>
+ </addon>
+
+ <!-- Fails because type is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.6</version>
+ <authors><author><name>Test Creator 6</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test6.xpi</install>
+ </addon>
+
+ <!-- Fails because type is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="">Empty id attribute</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.7</version>
+ <authors><author><name>Test Creator 7</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test7.xpi</install>
+ </addon>
+
+ <!-- Fails because type is unknown -->
+ <addon>
+ <name>FAIL</name>
+ <type id="9999">Unknown</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.8</version>
+ <authors><author><name>Test Creator 8</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with unknown type should be ignored</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test8.xpi</install>
+ </addon>
+
+ <!-- Fails because guid is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <version>1.9</version>
+ <authors><author><name>Test Creator 9</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined guid should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test9.xpi</install>
+ </addon>
+
+ <!-- Fails because guid is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid> </guid>
+ <version>1.10</version>
+ <authors><author><name>Test Creator 10</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty guid should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test10.xpi</install>
+ </addon>
+
+ <!-- Fails because guid matches previously successful result -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.11</version>
+ <authors><author><name>Test Creator 11</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches a previously successful result should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test11.xpi</install>
+ </addon>
+
+ <!-- Fails because guid matches already installed add-on -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>1.12</version>
+ <authors><author><name>Test Creator 12</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches an installed Addon should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test12.xpi</install>
+ </addon>
+
+ <!-- Fails because version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test13@tests.mozilla.org</guid>
+ <authors><author><name>Test Creator 13</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test13.xpi</install>
+ </addon>
+
+ <!-- Fails because version is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test14@tests.mozilla.org</guid>
+ <version> </version>
+ <authors><author><name>Test Creator 14</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with empty version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test14.xpi</install>
+ </addon>
+
+ <!-- Fails because authors undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test15@tests.mozilla.org</guid>
+ <version>1.15</version>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined authors should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test15.xpi</install>
+ </addon>
+
+ <!-- Fails because it has no defined author elements -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test16@tests.mozilla.org</guid>
+ <version>1.16</version>
+ <authors></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no defined author elements should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test16.xpi</install>
+ </addon>
+
+ <!-- Fails because no non-empty author elements -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test17@tests.mozilla.org</guid>
+ <version>1.17</version>
+ <authors>
+ <author><name></name></author>
+ <author><name></name> </author>
+ </authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no non-empty author elements should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test17.xpi</install>
+ </addon>
+
+ <!-- Fails because status is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test18@tests.mozilla.org</guid>
+ <version>1.18</version>
+ <authors><author><name>Test Creator 18</name></author></authors>
+ <summary>Add-on with undefined status should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test18.xpi</install>
+ </addon>
+
+ <!-- Fails because status is not Public -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test19@tests.mozilla.org</guid>
+ <version>1.19</version>
+ <authors><author><name>Test Creator 19</name></author></authors>
+ <status id="9999">Unknown</status>
+ <summary>Add-on with non-Public status should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test19.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible_applications is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test20@tests.mozilla.org</guid>
+ <version>1.20</version>
+ <authors><author><name>Test Creator 20</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined compatible_applications should be ignored.</summary>
+ <install>http://localhost:%PORT%/test20.xpi</install>
+ </addon>
+
+ <!-- Fails because no compatible applications matched -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test21@tests.mozilla.org</guid>
+ <version>1.21</version>
+ <authors><author><name>Test Creator 21</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no compatible applications should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>unknown@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test21.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's min version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test22@tests.mozilla.org</guid>
+ <version>1.22</version>
+ <authors><author><name>Test Creator 22</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too high of a compatible application min version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <max_version>2.0</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test22.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's min version too high -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test23@tests.mozilla.org</guid>
+ <version>1.23</version>
+ <authors><author><name>Test Creator 23</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too high of a compatible application min version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1.1</min_version>
+ <max_version>2.0</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test23.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's max version is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test24@tests.mozilla.org</guid>
+ <version>1.24</version>
+ <authors><author><name>Test Creator 24</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too low of a compatible application max version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test24.xpi</install>
+ </addon>
+
+ <!-- Fails because compatible application's max version is too low -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test25@tests.mozilla.org</guid>
+ <version>1.25</version>
+ <authors><author><name>Test Creator 25</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with too low of a compatible application max version should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ <max_version>0.9.9</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test25.xpi</install>
+ </addon>
+
+ <!-- Fails because XPI URL is undefined -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test26@tests.mozilla.org</guid>
+ <version>1.26</version>
+ <authors><author><name>Test Creator 26</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with undefined XPI URL should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ </addon>
+
+ <!-- Fails because XPI URL is empty -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test27@tests.mozilla.org</guid>
+ <version>1.27</version>
+ <authors><author><name>Test Creator 27</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with an empty XPI URL should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install> </install>
+ </addon>
+
+ <!-- Fails because install not compatible with OS -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test28@tests.mozilla.org</guid>
+ <version>1.28</version>
+ <authors><author><name>Test Creator 28</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with no installs with compatible OS should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN1">http://localhost:%PORT%/test28.xpi</install>
+ <install os="UNKNOWN2">http://localhost:%PORT%/test28.xpi</install>
+ </addon>
+
+ <!-- Fails because XPI URL matches an installing AddonInstall -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test29@tests.mozilla.org</guid>
+ <version>1.29</version>
+ <authors><author><name>Test Creator 29</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with an XPI URL that matches an installing AddonInstall should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/addons/test_AddonRepository_2.xpi</install>
+ </addon>
+
+ <!-- Passes because the add-on has the right payment info -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>purchase1@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>ALL</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL1</link>
+ <amount amount="5">$5</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Passes because the add-on has the right payment info -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>purchase2@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>XPCShell</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL2</link>
+ <amount amount="10.0">$10</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Fails because the add-on doesn't match the platform -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>purchase3@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <all_compatible_os>
+ <os>FOO</os>
+ </all_compatible_os>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <rating>5</rating>
+ <payment_data>
+ <link>http://localhost:%PORT%/purchaseURL3</link>
+ <amount amount="10">$10</amount>
+ </payment_data>
+ </addon>
+
+ <!-- Passes because the Addon that has a matching XPI URL
+ has a state = STATE_AVAILABLE (non-active install). This is the
+ last passing add-on. -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test-lastPassing@tests.mozilla.org</guid>
+ <version>2.0</version>
+ <authors>
+ <author>
+ <name>Test Creator - Last Passing</name>
+ <link>http://localhost:%PORT%/creatorLastPassing.html</link>
+ </author>
+ </authors>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <!-- Test that a rating > 5 becomes a rating = 5 -->
+ <rating>10</rating>
+ <install>http://localhost:%PORT%/addons/test_AddonRepository_3.xpi</install>
+ </addon>
+
+ <!-- Fails because of MAX_RESULTS limit. The previous <addon> should
+ be the last passing add-on in order to correctly test the limit. -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test-surpassesLimit@tests.mozilla.org</guid>
+ <version>9.9</version>
+ <authors><author><name>Test Creator - Surpasses Limit</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on should not be added because doing so would surpass MAX_RESULTS limit</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test-surpassesLimit.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_cache.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_cache.xml
new file mode 100644
index 000000000..f707f1217
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_cache.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <addon>
+ <name>Repo Add-on 1</name>
+ <type id="1">Extension</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>2.1</version>
+ <authors>
+ <author>
+ <name>Repo Add-on 1 - Creator</name>
+ <link>http://localhost:4444/repo/1/creator.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 1 - First Developer</name>
+ <link>http://localhost:4444/repo/1/firstDeveloper.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 1 - Second Developer</name>
+ <link>http://localhost:4444/repo/1/secondDeveloper.html</link>
+ </author>
+ </authors>
+ <summary>Repo Add-on 1 - Description&lt;br&gt;Second line</summary>
+ <description>&lt;p&gt;Repo Add-on 1 - Full Description &amp;amp; some extra&lt;/p&gt;</description>
+ <eula>Repo Add-on 1 - EULA</eula>
+ <developer_comments>Repo Add-on 1
+ Developer Comments</developer_comments>
+ <icon size="32">http://localhost/repo/1/icon.png</icon>
+ <status id="4">Public</status>
+ <rating>1</rating>
+ <learnmore>http://localhost/repo/1/learnmore.html</learnmore>
+ <homepage>http://localhost/repo/1/homepage.html</homepage>
+ <support>http://localhost/repo/1/support.html</support>
+ <contribution_data>
+ <link>http://localhost/repo/1/contribution.html</link>
+ <suggested_amount currency="USD">$11.11</suggested_amount>
+ <meet_developers>http://localhost/repo/1/meetDevelopers.html</meet_developers>
+ </contribution_data>
+ <reviews num="1111">http://localhost/repo/1/review.html</reviews>
+ <total_downloads>2221</total_downloads>
+ <weekly_downloads>3331</weekly_downloads>
+ <daily_users>4441</daily_users>
+ <last_updated epoch="9">1970-01-01T00:00:09Z</last_updated>
+ <install size="9">http://localhost:4444/repo/1/install.xpi</install>
+ </addon>
+
+ <addon>
+ <name>Repo Add-on 2</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_2@tests.mozilla.org</guid>
+ <version>2.2</version>
+ <authors>
+ <author>
+ <name>Repo Add-on 2 - Creator</name>
+ <link>http://localhost:4444/repo/2/creator.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 2 - First Developer</name>
+ <link>http://localhost:4444/repo/2/firstDeveloper.html</link>
+ </author>
+ <author>
+ <name>Repo Add-on 2 - Second Developer</name>
+ <link>http://localhost:4444/repo/2/secondDeveloper.html</link>
+ </author>
+ </authors>
+ <summary>Repo Add-on 2 - Description</summary>
+ <description>Repo Add-on 2 - Full Description</description>
+ <eula>Repo Add-on 2 - EULA</eula>
+ <developer_comments>Repo Add-on 2 - Developer Comments</developer_comments>
+ <icon size="32">http://localhost/repo/2/icon.png</icon>
+ <status id="9">Unknown</status>
+ <previews>
+ <preview primary="1">
+ <full type="image/png">http://localhost:4444/repo/2/firstFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/2/firstThumbnail.png</thumbnail>
+ <caption>Repo Add-on 2 - First Caption</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:4444/repo/2/secondFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/2/secondThumbnail.png</thumbnail>
+ <caption>Repo Add-on 2 - Second Caption</caption>
+ </preview>
+ </previews>
+ <rating>2</rating>
+ <learnmore>http://localhost/repo/2/learnmore.html</learnmore>
+ <homepage>http://localhost/repo/2/homepage.html</homepage>
+ <support>http://localhost/repo/2/support.html</support>
+ <contribution_data>
+ <link>http://localhost/repo/2/contribution.html</link>
+ <meet_developers>http://localhost/repo/2/meetDevelopers.html</meet_developers>
+ </contribution_data>
+ <reviews num="1112">http://localhost/repo/2/review.html</reviews>
+ <total_downloads>2222</total_downloads>
+ <weekly_downloads>3332</weekly_downloads>
+ <daily_users>4442</daily_users>
+ <last_updated epoch="9">1970-01-01T00:00:09Z</last_updated>
+ <install size="9">http://localhost:4444/repo/2/install.xpi</install>
+ </addon>
+
+ <addon>
+ <name>Repo Add-on 3</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_3@tests.mozilla.org</guid>
+ <version>2.3</version>
+ <icon size="32">http://localhost/repo/3/icon.png</icon>
+ <previews>
+ <preview primary="1">
+ <full type="image/png">http://localhost:4444/repo/3/firstFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/3/firstThumbnail.png</thumbnail>
+ <caption>Repo Add-on 3 - First Caption</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:4444/repo/3/secondFull.png</full>
+ <thumbnail type="image/png">http://localhost:4444/repo/3/secondThumbnail.png</thumbnail>
+ <caption>Repo Add-on 3 - Second Caption</caption>
+ </preview>
+ </previews>
+ </addon>
+
+ <addon_compatibility hosted="true" id="123">
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <name>PASS</name>
+ <version_ranges>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>3.0</min_version>
+ <max_version>4.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>5.0</min_version>
+ <max_version>6.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - invalid type attribute -->
+ <version_range type="unknown">
+ <min_version>9</min_version>
+ <max_version>10</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>10.0</min_version>
+ <max_version>11.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - no matching appID -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <application_id>123</application_id>
+ <min_version>1.0</min_version>
+ <max_version>999.0</max_version>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml
new file mode 100644
index 000000000..003095727
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - ignore</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-ignore@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml
new file mode 100644
index 000000000..fec8b09ca
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - normal</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-normal@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml
new file mode 100644
index 000000000..f99256b87
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="123">
+ <addon>
+ <name>Test Repo Add-on - strict</name>
+ <type id="1">Extension</type>
+ <guid>compatmode-strict@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ </authors>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_empty.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_empty.xml
new file mode 100644
index 000000000..4cd5c1443
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_empty.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9999" />
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_failed.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_failed.xml
new file mode 100644
index 000000000..d02fa0249
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_failed.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9999">
+<!-- Cause XML parse error so that the search fails -->
+<!-- <addon> -->
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors><author><name>Test Creator 1</name></author></authors>
+ <status id="4">Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test1.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
new file mode 100644
index 000000000..8a6167969
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1111">
+ <!-- Passes even though XPI URL matches an installing AddonInstall.
+ Tests optional attributes. -->
+ <addon>
+ <name>PASS</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.1</version>
+ <authors>
+ <author>
+ <name>Test Creator 1</name>
+ <link>http://localhost:%PORT%/creator1.html</link>
+ </author>
+ <author>
+ <name>Test Developer 1</name>
+ <link>http://localhost:%PORT%/developer1.html</link>
+ </author>
+ </authors>
+ <summary>Test Summary 1</summary>
+ <description>Test Description 1</description>
+ <eula>Test EULA 1</eula>
+ <developer_comments>Test Developer Comments 1</developer_comments>
+ <icon size="32">http://localhost:%PORT%/icon1.png</icon>
+ <status id="8">Preliminarily Reviewed</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <previews>
+ <preview primary="1">
+ <full type="image/png" width="400" height="300">
+ http://localhost:%PORT%/full1-1.png
+ </full>
+ <thumbnail type="image/png" width="200" height="150">
+ http://localhost:%PORT%/thumbnail1-1.png
+ </thumbnail>
+ <caption>Caption 1 - 1</caption>
+ </preview>
+ <preview primary="0">
+ <full type="image/png">http://localhost:%PORT%/full2-1.png</full>
+ <thumbnail type="image/png">http://localhost:%PORT%/thumbnail2-1.png</thumbnail>
+ <caption>Caption 2 - 1</caption>
+ </preview>
+ </previews>
+ <rating>4</rating>
+ <learnmore>http://localhost:%PORT%/learnmore1.html</learnmore>
+ <support>http://localhost:%PORT%/support1.html</support>
+ <contribution_data>
+ <link>http://localhost:%PORT%/contribution1.html</link>
+ <suggested_amount currency="USD">$11.11</suggested_amount>
+ <meet_developers>http://localhost:%PORT%/meetDevelopers1.html</meet_developers>
+ </contribution_data>
+ <reviews num="1111">http://localhost:%PORT%/review1.html</reviews>
+ <total_downloads>2222</total_downloads>
+ <weekly_downloads>3333</weekly_downloads>
+ <daily_users>4444</daily_users>
+ <last_updated epoch="1265033045">2010-02-01T14:04:05Z</last_updated>
+ <install size="5555">http://localhost:%PORT%/addons/test_AddonRepository_2.xpi</install>
+ </addon>
+
+ <addon_compatibility hosted="true" id="123">
+ <guid>test1@tests.mozilla.org</guid>
+ <name>PASS</name>
+ <version_ranges>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>3.0</min_version>
+ <max_version>4.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Will be included -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>5.0</min_version>
+ <max_version>6.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - invalid type attribute -->
+ <version_range type="unknown">
+ <min_version>9</min_version>
+ <max_version>10</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <application_id>666</application_id>
+ <min_version>10.0</min_version>
+ <max_version>11.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <!-- Won't be included - no matching appID -->
+ <version_range type="incompatible">
+ <min_version>0.2</min_version>
+ <max_version>0.3</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <application_id>123</application_id>
+ <min_version>1.0</min_version>
+ <max_version>999.0</max_version>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <!-- Fails because guid matches previously successful result -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.2</version>
+ <authors><author><name>Test Creator 2</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that matches a previously successful result should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test2.xpi</install>
+ </addon>
+
+ <!-- Fails because guid was not requested -->
+ <addon>
+ <name>FAIL</name>
+ <type id="1">Extension</type>
+ <guid>notRequested@tests.mozilla.org</guid>
+ <version>1.3</version>
+ <authors><author><name>Test Creator 3</name></author></authors>
+ <status id="4">Public</status>
+ <summary>Add-on with a guid that wasn't requested should be ignored.</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test3.xpi</install>
+ </addon>
+
+ <!-- Passes even though guid matches already installed add-on,
+ type is unknown, no defined author elements, status is not Public,
+ no compatible applications matched, no installs compatible with OS
+ -->
+ <addon>
+ <name>PASS</name>
+ <type id="2">Theme</type>
+ <guid>test_AddonRepository_1@tests.mozilla.org</guid>
+ <version>1.4</version>
+ <status id="9999">Unknown</status>
+ <compatible_applications>
+ <application>
+ <appID>unknown@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN1">http://localhost:%PORT%/test4.xpi</install>
+ <install os="UNKNOWN2">http://localhost:%PORT%/test4.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_backgroundupdate.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_backgroundupdate.rdf
new file mode 100644
index 000000000..ab7cdef34
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_backgroundupdate.rdf
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml
new file mode 100644
index 000000000..368a6ed53
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem name="/^Mozilla Corp\.$/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/block2/" name="/^Moz/" creator="Dangerous"
+ homepageURL="/\.dangerous\.com/" updateURL="/\.dangerous\.com/">
+ <versionRange severity="3">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_prefs_1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_prefs_1.xml
new file mode 100644
index 000000000..41df457b0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_prefs_1.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="block1@tests.mozilla.org">
+ <prefs>
+ <pref>test.blocklist.pref1</pref>
+ <pref>test.blocklist.pref2</pref>
+ </prefs>
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="block2@tests.mozilla.org">
+ <prefs>
+ <pref>test.blocklist.pref3</pref>
+ <pref>test.blocklist.pref4</pref>
+ </prefs>
+ <versionRange severity="3">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_regexp_1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_regexp_1.xml
new file mode 100644
index 000000000..20035c6a2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_regexp_1.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="/block1/">
+ <versionRange severity="1">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="/block1/">
+ <versionRange severity="2">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="2.*"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716.rdf
new file mode 100644
index 000000000..d60d8ca3f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716.rdf
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE RDF:RDF [
+ <!ENTITY bug299716 "urn:mozilla:extension:bug299716">
+ <!ENTITY addons_prefix "http://localhost:4444/addons/test_bug299716">
+ <!ENTITY v0.2 "<em:version>0.2</em:version>">
+
+ <!ENTITY xpcshell.app "
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ ">
+
+ <!ENTITY toolkit.app "
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ ">
+
+ <!ENTITY invalidRange "
+ <em:minVersion>30</em:minVersion>
+ <em:maxVersion>30</em:maxVersion>
+ ">
+
+ <!ENTITY xpcshell.invalid "
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ &invalidRange;
+ ">
+
+ <!ENTITY toolkit.invalid "
+ <em:id>toolkit@mozilla.org</em:id>
+ &invalidRange;
+ ">
+]>
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <!-- XPCShell -->
+ <RDF:Description about="&bug299716;-a@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-a@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-a@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_a_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit -->
+ <RDF:Description about="&bug299716;-b@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-b@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-b@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_b_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- XPCShell + Toolkit -->
+ <RDF:Description about="&bug299716;-c@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-c@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-c@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_c_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_c_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- XPCShell (Toolkit invalid) -->
+ <RDF:Description about="&bug299716;-d@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-d@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-d@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_d_2.xpi">
+ &xpcshell.app;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_d_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit (XPCShell invalid), should not install -->
+ <RDF:Description about="&bug299716;-e@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-e@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-e@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_e_2.xpi">
+ &xpcshell.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_e_2.xpi">
+ &toolkit.app;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- None (XPCShell, Toolkit invalid), should not install -->
+ <RDF:Description about="&bug299716;-f@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-f@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-f@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_f_2.xpi">
+ &xpcshell.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_f_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+
+ <!-- Toolkit (invalid), should not install -->
+ <RDF:Description about="&bug299716;-g@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li RDF:resource="&bug299716;-g@tests.mozilla.org:0.2"/>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="&bug299716;-g@tests.mozilla.org:0.2">
+ &v0.2;
+ <em:targetApplication>
+ <RDF:Description em:updateLink="&addons_prefix;_g_2.xpi">
+ &toolkit.invalid;
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716_2.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716_2.rdf
new file mode 100644
index 000000000..94a4ea450
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716_2.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <RDF:Description about="urn:mozilla:extension:bug299716-2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>0.1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>2.0.*</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug324121.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug324121.rdf
new file mode 100644
index 000000000..2c453f756
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug324121.rdf
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- app id incompatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_6@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- toolkit id compatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug324121_7@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- toolkit id incompatible update available -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug393285.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug393285.xml
new file mode 100644
index 000000000..1767b4332
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug393285.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="test_bug393285_2@tests.mozilla.org"/>
+ <emItem id="test_bug393285_3a@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </emItem>
+ <emItem id="test_bug393285_3b@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </emItem>
+ <emItem id="test_bug393285_4@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0">
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1.0" maxVersion="1.0"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug393285_5@tests.mozilla.org" os="Darwin"/>
+ <emItem id="test_bug393285_6@tests.mozilla.org" os="XPCShell"/>
+ <emItem id="test_bug393285_7@tests.mozilla.org" os="Darwin,XPCShell,WINNT"/>
+ <emItem id="test_bug393285_8@tests.mozilla.org" xpcomabi="x86-msvc"/>
+ <emItem id="test_bug393285_9@tests.mozilla.org" xpcomabi="noarch-spidermonkey"/>
+ <emItem id="test_bug393285_10@tests.mozilla.org" xpcomabi="ppc-gcc3,noarch-spidermonkey,x86-msvc"/>
+ <emItem id="test_bug393285_11@tests.mozilla.org" os="Darwin" xpcomabi="ppc-gcc3,x86-msvc"/>
+ <emItem id="test_bug393285_12@tests.mozilla.org" os="Darwin" xpcomabi="ppc-gcc3,noarch-spidermonkey,x86-msvc"/>
+ <emItem id="test_bug393285_13@tests.mozilla.org" os="XPCShell" xpcomabi="ppc-gcc3,x86-msvc"/>
+ <emItem id="test_bug393285_14@tests.mozilla.org" os="XPCShell,WINNT" xpcomabi="ppc-gcc3,x86-msvc,noarch-spidermonkey"/>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug394300.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug394300.rdf
new file mode 100644
index 000000000..94e12527f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug394300.rdf
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:bug394300_1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>20</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install should be the version detected -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>10</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install. Detecting this would indicate that the order
+ of entries is playing a part in the update detection. -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>6</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - no minVersion or maxVersion specified -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>40</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>30</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <RDF:Description about="urn:mozilla:extension:bug394300_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>20</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install should be the version detected -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>10</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Valid install. Detecting this would indicate that the order
+ of entries is playing a part in the update detection. -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>6</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1.9</em:minVersion>
+ <em:maxVersion>1.9</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - no minVersion or maxVersion specified -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>40</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ <!-- Not a valid install - incompatible app versions -->
+ <RDF:li>
+ <RDF:Description>
+ <em:version>30</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug424262.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug424262.xml
new file mode 100644
index 000000000..d797debbb
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug424262.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>TEST</name>
+ <type id='1'>Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>-5</rating>
+ <type id='1'>Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>0</rating>
+ <type id='1'>Extension</type>
+ <guid>test3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>2</rating>
+ <type id='1'>Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>4</rating>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>5</rating>
+ <type id='1'>Extension</type>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>10</rating>
+ <type id='1'>Extension</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>TEST</name>
+ <rating>100</rating>
+ <type id='1'>Extension</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_app.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_app.xml
new file mode 100644
index 000000000..f12ca1fa6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_app.xml
@@ -0,0 +1,333 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <!-- All extensions are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1 not listed, should never get blocked -->
+ <!-- Always blocked -->
+ <emItem id="test_bug449027_2@tests.mozilla.org"/>
+ <!-- Always blocked -->
+ <emItem id="test_bug449027_3@tests.mozilla.org">
+ <versionRange/>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_4@tests.mozilla.org">
+ <versionRange minVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_5@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_6@tests.mozilla.org">
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </emItem>
+ <emItem id="test_bug449027_7@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ <versionRange minVersion="6" maxVersion="7"/>
+ </emItem>
+ <emItem id="test_bug449027_8@tests.mozilla.org">
+ <versionRange minVersion="2" maxVersion="2"/>
+ <versionRange minVersion="4" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ </emItem>
+ <emItem id="test_bug449027_9@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ </emItem>
+ <emItem id="test_bug449027_10@tests.mozilla.org">
+ <versionRange minVersion="5"/>
+ </emItem>
+ <emItem id="test_bug449027_11@tests.mozilla.org">
+ <versionRange maxVersion="6"/>
+ </emItem>
+ <emItem id="test_bug449027_12@tests.mozilla.org">
+ <versionRange maxVersion="5"/>
+ </emItem>
+
+ <!-- This should block all versions for any application -->
+ <emItem id="test_bug449027_13@tests.mozilla.org">
+ <versionRange>
+ <targetApplication/>
+ </versionRange>
+ </emItem>
+ <!-- Shouldn't block -->
+ <emItem id="test_bug449027_14@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <!-- Should block for any version of the app -->
+ <emItem id="test_bug449027_15@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org"/>
+ </versionRange>
+ </emItem>
+ <!-- Should still block -->
+ <emItem id="test_bug449027_16@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_17@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_18@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_19@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange minVersion="3" maxVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_20@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="2"/>
+ <versionRange minVersion="2" maxVersion="3"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_21@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="1"/>
+ <versionRange minVersion="2" maxVersion="4"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_22@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_23@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_24@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_25@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <!-- All plugins are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1 not listed, should never get blocked -->
+ <!-- Always blocked -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_2$"/>
+ </pluginItem>
+ <!-- Always blocked -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_3$"/>
+ <versionRange/>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_4$"/>
+ <versionRange minVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_5$"/>
+ <versionRange minVersion="6" maxVersion="4"/>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_6$"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange maxVersion="4"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_7$"/>
+ <versionRange maxVersion="4"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ <versionRange minVersion="6" maxVersion="7"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_8$"/>
+ <versionRange minVersion="2" maxVersion="2"/>
+ <versionRange minVersion="4" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_9$"/>
+ <versionRange minVersion="4"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_10$"/>
+ <versionRange minVersion="5"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_11$"/>
+ <versionRange maxVersion="6"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_12$"/>
+ <versionRange maxVersion="5"/>
+ </pluginItem>
+
+ <!-- This should block all versions for any application -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_13$"/>
+ <versionRange>
+ <targetApplication/>
+ </versionRange>
+ </pluginItem>
+ <!-- Shouldn't block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_14$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block for any version of the app -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_15$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should still block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_16$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_17$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_18$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="6" maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_19$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="5" maxVersion="6"/>
+ <versionRange minVersion="3" maxVersion="4"/>
+ <versionRange maxVersion="2"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_20$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="2"/>
+ <versionRange minVersion="2" maxVersion="3"/>
+ <versionRange minVersion="4" maxVersion="5"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_21$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="1" maxVersion="1"/>
+ <versionRange minVersion="2" maxVersion="4"/>
+ <versionRange minVersion="5" maxVersion="6"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_22$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_23$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange minVersion="2"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_24$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="3"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_25$"/>
+ <versionRange>
+ <targetApplication id="xpcshell@tests.mozilla.org">
+ <versionRange maxVersion="4"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_toolkit.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_toolkit.xml
new file mode 100644
index 000000000..ad8ec5ed9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_toolkit.xml
@@ -0,0 +1,208 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <!-- All extensions are version 5 and tests run against toolkitVersion 8 -->
+
+ <!-- Test 1-14 not listed, should never get blocked -->
+
+ <!-- Should block for any version of the app -->
+ <emItem id="test_bug449027_15@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org"/>
+ </versionRange>
+ </emItem>
+ <!-- Should still block -->
+ <emItem id="test_bug449027_16@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Not blocked since neither version range matches -->
+ <emItem id="test_bug449027_17@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Invalid version range, should not block -->
+ <emItem id="test_bug449027_18@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="11" maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <!-- Should block all of these -->
+ <emItem id="test_bug449027_19@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="10" maxVersion="11"/>
+ <versionRange minVersion="8" maxVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_20@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="7"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="9" maxVersion="10"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_21@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="6" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="9"/>
+ <versionRange minVersion="10" maxVersion="11"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_22@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_23@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="7"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_24@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ <emItem id="test_bug449027_25@tests.mozilla.org">
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </emItem>
+ </emItems>
+ <pluginItems>
+ <!-- All plugins are version 5 and tests run against appVersion 3 -->
+
+ <!-- Test 1-14 not listed, should never get blocked -->
+ <!-- Should block for any version of the app -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_15$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org"/>
+ </versionRange>
+ </pluginItem>
+ <!-- Should still block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_16$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Not blocked since neither version range matches -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_17$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Invalid version range, should not block -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_18$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="11" maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <!-- Should block all of these -->
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_19$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="10" maxVersion="11"/>
+ <versionRange minVersion="8" maxVersion="9"/>
+ <versionRange maxVersion="7"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_20$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="7"/>
+ <versionRange minVersion="7" maxVersion="8"/>
+ <versionRange minVersion="9" maxVersion="10"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_21$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="6" maxVersion="6"/>
+ <versionRange minVersion="7" maxVersion="9"/>
+ <versionRange minVersion="10" maxVersion="11"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_22$"/>
+ <versionRange>
+ <targetApplication id="foo@bar.com"/>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_23$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange minVersion="7"/>
+ </targetApplication>
+ <targetApplication id="foo@bar.com"/>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_24$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="8"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug449027_25$"/>
+ <versionRange>
+ <targetApplication id="toolkit@mozilla.org">
+ <versionRange maxVersion="9"/>
+ </targetApplication>
+ </versionRange>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug468528.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug468528.xml
new file mode 100644
index 000000000..85f0da57c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug468528.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_2["/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug468528_3"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_1.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_1.rdf
new file mode 100644
index 000000000..5397e8a87
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_1.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bug470377_1@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_2.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_2.rdf
new file mode 100644
index 000000000..b1dde7f7a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_2.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bug470377_2@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_3.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_3.rdf
new file mode 100644
index 000000000..ae483434a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_3.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bug470377_3@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_4.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_4.rdf
new file mode 100644
index 000000000..97abacc5e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_4.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bug470377_4@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_5.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_5.rdf
new file mode 100644
index 000000000..bff1104a7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_5.rdf
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>bug470377_5@tests.mozilla.org</em:id>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ <em:name>Test for Bug 470377</em:name>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_1.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_1.rdf
new file mode 100644
index 000000000..e4ad91ae9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_1.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>unknown@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_2.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_2.rdf
new file mode 100644
index 000000000..10fcafd39
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_2.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_2@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_3.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_3.rdf
new file mode 100644
index 000000000..684002462
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_3.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_3@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_4.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_4.rdf
new file mode 100644
index 000000000..6e7116239
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_4.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_4@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>toolkit@mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_5.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_5.rdf
new file mode 100644
index 000000000..c926af934
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_5.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:test_bug470377_5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_1.xml
new file mode 100644
index 000000000..c4cc2fe37
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_1.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_2"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_bug514327_3"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_2.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_2.xml
new file mode 100644
index 000000000..cc0a0c69d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="Test Plug-in"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_empty.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_empty.xml
new file mode 100644
index 000000000..0261794f8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_empty.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml
new file mode 100644
index 000000000..d651f8799
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_outdated"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml
new file mode 100644
index 000000000..208444681
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="test_bug514327_outdated"/>
+ <versionRange severity="0"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpi
new file mode 100644
index 000000000..2dbcc0b50
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpi
new file mode 100644
index 000000000..86fc6baaa
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpi
new file mode 100644
index 000000000..adb7be9ad
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug542391.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug542391.rdf
new file mode 100644
index 000000000..db82cf675
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug542391.rdf
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:override1x2-1x3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug554133.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug554133.xml
new file mode 100644
index 000000000..736c10514
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug554133.xml
@@ -0,0 +1,292 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="100">
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test1@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not return an incompatible add-on</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>2</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not return an add-on for a different OS</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install os="UNKNOWN">http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>FAIL</name>
+ <type id='1'>Extension</type>
+ <guid>test5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Should not include the same result twice</summary>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test9@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test10@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test11@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+
+ <addon>
+ <name>PASS</name>
+ <type id='1'>Extension</type>
+ <guid>test12@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://localhost:%PORT%/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://localhost:%PORT%/test.xpi</install>
+ </addon>
+</searchresults>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug619730.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug619730.xml
new file mode 100644
index 000000000..f2511c0de
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug619730.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems testattr="GFX"><gfxItem/><gfxItem/></gfxItems>
+ <testItems testattr="FOO"><testItem/><testItem/><testItem/></testItems>
+ <fooItems/>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug655254.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug655254.rdf
new file mode 100644
index 000000000..9857dcb55
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug655254.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <RDF:Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>1</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+</RDF:RDF>
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_compatoverrides.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_compatoverrides.xml
new file mode 100644
index 000000000..c0d67d033
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_compatoverrides.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="9">
+ <addon>
+ <name>Test addon 2</name>
+ <type id="1">Extension</type>
+ <guid>addon2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+
+ <addon>
+ <name>Test addon 3</name>
+ <type id="1">Extension</type>
+ <guid>addon3@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 3</name>
+ <guid>addon3@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 4</name>
+ <type id="1">Extension</type>
+ <guid>addon4@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 4</name>
+ <guid>addon4@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 5</name>
+ <type id="1">Extension</type>
+ <guid>addon5@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 5</name>
+ <guid>addon5@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.9</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>Unknown App</name>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 6</name>
+ <type id="1">Extension</type>
+ <guid>addon6@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 6</name>
+ <guid>addon6@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>0.9</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 7</name>
+ <type id="1">Extension</type>
+ <guid>addon7@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 7</name>
+ <guid>addon7@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>0.9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon>
+ <name>Test addon 8</name>
+ <type id="1">Extension</type>
+ <guid>addon8@tests.mozilla.org</guid>
+ <version>1.0</version>
+ </addon>
+ <addon_compatibility hosted="true">
+ <name>Test addon 8</name>
+ <guid>addon8@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>6</min_version>
+ <max_version>6.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.9</min_version>
+ <max_version>9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>9</max_version>
+ </application>
+ <application>
+ <name>Unknown app</name>
+ <appID>unknown-app@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ <version_range type="incompatible">
+ <min_version>0.1</min_version>
+ <max_version>0.2</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>0.1</min_version>
+ <max_version>0.9</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon_compatibility hosted="false">
+ <name>Test addon 9</name>
+ <guid>addon9@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+ <addon_compatibility hosted="false">
+ <name>Test addon 10</name>
+ <guid>addon10@tests.mozilla.org</guid>
+ <version_ranges>
+ <version_range type="compatible">
+ <min_version>0.5</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>2</max_version>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_corrupt.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_corrupt.rdf
new file mode 100644
index 000000000..f3341bdcf
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_corrupt.rdf
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_complete/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_complete/bootstrap.js
new file mode 100644
index 000000000..a81c90bf0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_complete/bootstrap.js
@@ -0,0 +1,24 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
+const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
+
+function install(data, reason) {}
+
+// normally we would use BootstrapMonitor here, but we need a reference to
+// the symbol inside `XPIProvider.jsm`.
+function startup(data, reason) {
+ // apply update immediately
+ if (data.hasOwnProperty("instanceID") && data.instanceID) {
+ AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
+ upgrade.install();
+ });
+ } else {
+ throw Error("no instanceID passed to bootstrap startup");
+ }
+}
+
+function shutdown(data, reason) {}
+
+function uninstall(data, reason) {}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_defer/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_defer/bootstrap.js
new file mode 100644
index 000000000..25ffd8565
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_defer/bootstrap.js
@@ -0,0 +1,34 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+const ADDON_ID = "test_delay_update_complete@tests.mozilla.org";
+const INSTALL_COMPLETE_PREF = "bootstraptest.install_complete_done";
+
+// global reference to hold upgrade object
+let gUpgrade;
+
+function install(data, reason) {}
+
+// normally we would use BootstrapMonitor here, but we need a reference to
+// the symbol inside `XPIProvider.jsm`.
+function startup(data, reason) {
+ // do not apply update immediately, hold on to for later
+ if (data.hasOwnProperty("instanceID") && data.instanceID) {
+ AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
+ gUpgrade = upgrade;
+ });
+ } else {
+ throw Error("no instanceID passed to bootstrap startup");
+ }
+
+ // add a listener so the test can pass control back
+ AddonManager.addAddonListener({
+ onFakeEvent: () => {
+ gUpgrade.install();
+ }
+ })
+}
+
+function shutdown(data, reason) {}
+
+function uninstall(data, reason) {}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_ignore/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_ignore/bootstrap.js
new file mode 100644
index 000000000..7693c9c2d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_ignore/bootstrap.js
@@ -0,0 +1,26 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AddonManager.jsm");
+
+const ADDON_ID = "test_delay_update_ignore@tests.mozilla.org";
+const TEST_IGNORE_PREF = "delaytest.ignore";
+
+function install(data, reason) {}
+
+// normally we would use BootstrapMonitor here, but we need a reference to
+// the symbol inside `XPIProvider.jsm`.
+function startup(data, reason) {
+ Services.prefs.setBoolPref(TEST_IGNORE_PREF, false);
+
+ // explicitly ignore update, will be queued for next restart
+ if (data.hasOwnProperty("instanceID") && data.instanceID) {
+ AddonManager.addUpgradeListener(data.instanceID, (upgrade) => {
+ Services.prefs.setBoolPref(TEST_IGNORE_PREF, true);
+ });
+ } else {
+ throw Error("no instanceID passed to bootstrap startup");
+ }
+}
+
+function shutdown(data, reason) {}
+
+function uninstall(data, reason) {}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.json
new file mode 100644
index 000000000..cf3defdc7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.json
@@ -0,0 +1,11 @@
+{
+ "addons": {
+ "test_delay_update_complete_webext@tests.mozilla.org": {
+ "updates": [
+ { "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_delay_update_complete_webextension_v2.xpi"
+ }
+ ]
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.rdf
new file mode 100644
index 000000000..8af39cb0e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:test_delay_update_complete@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <!-- app id compatible update available -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_delay_update_complete_v2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.json
new file mode 100644
index 000000000..2fcab10b5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.json
@@ -0,0 +1,11 @@
+{
+ "addons": {
+ "test_delay_update_defer_webext@tests.mozilla.org": {
+ "updates": [
+ { "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_delay_update_defer_webextension_v2.xpi"
+ }
+ ]
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.rdf
new file mode 100644
index 000000000..d44d4880f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:test_delay_update_defer@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <!-- app id compatible update available -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_delay_update_defer_v2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.json
new file mode 100644
index 000000000..b7f48149d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.json
@@ -0,0 +1,11 @@
+{
+ "addons": {
+ "test_delay_update_ignore_webext@tests.mozilla.org": {
+ "updates": [
+ { "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_delay_update_ignore_webextension_v2.xpi"
+ }
+ ]
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.rdf
new file mode 100644
index 000000000..866884f8d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:test_delay_update_ignore@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <!-- app id compatible update available -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_delay_update_ignore_v2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_dictionary.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_dictionary.rdf
new file mode 100644
index 000000000..364b3bba1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_dictionary.rdf
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:ab-CD@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:ef@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:gh@dictionaries.addons.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_dictionary_5.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/bootstrap.js
new file mode 100644
index 000000000..01682d3b7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/bootstrap.js
@@ -0,0 +1,21 @@
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function install(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 2);
+ Services.prefs.setIntPref("bootstraptest.install_reason", reason);
+}
+
+function startup(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 2);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", reason);
+}
+
+function shutdown(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.active_version", 0);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", reason);
+}
+
+function uninstall(data, reason) {
+ Services.prefs.setIntPref("bootstraptest.installed_version", 0);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/install.rdf
new file mode 100644
index 000000000..ebe547ccc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:install-manifest">
+ <em:id>addon2@tests.mozilla.org</em:id>
+ <em:version>2.0</em:version>
+
+ <!-- Front End MetaData -->
+ <em:name>Distributed add-ons test</em:name>
+ <em:bootstrap>true</em:bootstrap>
+
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>5</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt
new file mode 100644
index 000000000..051aef85a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt
@@ -0,0 +1 @@
+Test of a file in a sub directory \ No newline at end of file
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt
new file mode 100644
index 000000000..9eddc4493
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt
@@ -0,0 +1 @@
+Nested dummy file \ No newline at end of file
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist.xml
new file mode 100644
index 000000000..d535d2c3f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist.xml
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry blockID="g35">
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.0</os>
+ <vendor>0xdcba</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry blockID="g36">
+ <os>WINNT 6.1</os>
+ <vendor>0xabab</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> GREATER_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1111 </driverVersion>
+ <driverVersionComparator> EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>abcd</vendor>
+ <devices>
+ <device>wxyz</device>
+ <device>asdf</device>
+ <device>erty</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 5 </driverVersion>
+ <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>dcdc</vendor>
+ <devices>
+ <device>uiop</device>
+ <device>vbnm</device>
+ <device>hjkl</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 5 </driverVersion>
+ <driverVersionComparator> EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>abab</vendor>
+ <devices>
+ <device>ghjk</device>
+ <device>cvbn</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 7 </driverVersion>
+ <driverVersionComparator> GREATER_THAN_OR_EQUAL </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x6666</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DEVICE </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1112 </driverVersion>
+ <driverVersionMax> 8.52.323.1000 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_EXCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.50.322.1000 </driverVersion>
+ <driverVersionMax> 8.52.322.1112 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_EXCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1000 </driverVersion>
+ <driverVersionMax> 9.52.322.1000 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_EXCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> HARDWARE_VIDEO_DECODING </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 7.82.322.1000 </driverVersion>
+ <driverVersionMax> 9.25.322.1001 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1112 </driverVersion>
+ <driverVersionMax> 9.52.322.1300 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_DECODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1000 </driverVersion>
+ <driverVersionMax> 8.52.322.1112 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_ENCODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1112 </driverVersion>
+ <driverVersionMax> 8.52.322.1200 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE_START </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_MSAA </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1000 </driverVersion>
+ <driverVersionMax> 8.52.322.1200 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE_START </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1000 </driverVersion>
+ <driverVersionMax> 8.52.322.1112 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_INCLUSIVE_START </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xdcdc</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> CANVAS2D_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.1000 </driverVersion>
+ <driverVersionMax> 9.52.322.1000 </driverVersionMax>
+ <driverVersionComparator> BETWEEN_EXCLUSIVE </driverVersionComparator>
+ </gfxBlacklistEntry>
+
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist2.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist2.xml
new file mode 100644
index 000000000..0ad8f6819
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist2.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.1</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.0</os>
+ <vendor>0xdcba</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml
new file mode 100644
index 000000000..f64676355
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml
@@ -0,0 +1,783 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+
+ <gfxBlacklistEntry blockID="g1">
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g2">
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="22.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_LAYERS</feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1" maxVersion="22.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_1_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g11">
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="14.0b2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_OPENGL </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_MSAA </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> STAGEFRIGHT </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_ENCODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_DECODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="17.2a2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="13.2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> HARDWARE_VIDEO_DECODING </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>All</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="10.5" maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g1">
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g2">
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="22.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_LAYERS</feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1" maxVersion="22.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_1_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g11">
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="14.0b2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_OPENGL </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_MSAA </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> STAGEFRIGHT </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_ENCODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_DECODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="17.2a2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="13.2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> HARDWARE_VIDEO_DECODING </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="10.5" maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g1">
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g2">
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="22.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_LAYERS</feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1" maxVersion="22.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_1_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g11">
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="14.0b2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_OPENGL </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_MSAA </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> STAGEFRIGHT </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_ENCODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_DECODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="17.2a2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="13.2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> HARDWARE_VIDEO_DECODING </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Linux</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="10.5" maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g1">
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g2">
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="22.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_9_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_LAYERS</feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="16.0a1" maxVersion="22.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_10_1_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry blockID="g11">
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="14.0b2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_OPENGL </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="12.0" maxVersion="16.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBGL_MSAA </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> STAGEFRIGHT </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_ENCODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> WEBRTC_HW_ACCELERATION_DECODE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="17.2a2" maxVersion="15.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="15.0" maxVersion="13.2"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> HARDWARE_VIDEO_DECODING </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ <gfxBlacklistEntry>
+ <os>Android</os>
+ <vendor>0xabcd</vendor>
+ <versionRange minVersion="10.5" maxVersion="13.0"/>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT3D_11_ANGLE </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ </gfxBlacklistEntry>
+
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml
new file mode 100644
index 000000000..248868a2e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <gfxItems>
+ <gfxBlacklistEntry>
+ <os>WINNT 6.2</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> DIRECT2D </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ <gfxBlacklistEntry>
+ <os>Darwin 13</os>
+ <vendor>0xabcd</vendor>
+ <devices>
+ <device>0x2783</device>
+ <device>0x1234</device>
+ <device>0x2782</device>
+ </devices>
+ <feature> OPENGL_LAYERS </feature>
+ <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ <driverVersion> 8.52.322.2202 </driverVersion>
+ <driverVersionComparator> LESS_THAN </driverVersionComparator>
+ </gfxBlacklistEntry>
+ </gfxItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_1.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_1.rdf
new file mode 100644
index 000000000..016726021
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_1.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:hotfix@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_hotfix_1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_2.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_2.rdf
new file mode 100644
index 000000000..35a2befee
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_2.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:hotfix@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_hotfix_2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_3.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_3.rdf
new file mode 100644
index 000000000..7180da143
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_3.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:hotfix@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_hotfix_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.rdf
new file mode 100644
index 000000000..fe82334fa
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.rdf
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon7@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.xml
new file mode 100644
index 000000000..33f14a2fd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_install.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <addon>
+ <name>Real Test 2</name>
+ <type id='1'>Extension</type>
+ <guid>addon2@tests.mozilla.org</guid>
+ <version>1.0</version>
+ <authors>
+ <author>
+ <name>Test Creator</name>
+ <link>http://example.com/creator.html</link>
+ </author>
+ </authors>
+ <status id='4'>Public</status>
+ <summary>Repository summary</summary>
+ <description>Repository description</description>
+ <compatible_applications>
+ <application>
+ <name>Firefox</name>
+ <appID>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ <application>
+ <name>SeaMonkey</name>
+ <appID>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</appID>
+ <min_version>0</min_version>
+ <max_version>*</max_version>
+ </application>
+ </compatible_applications>
+ <compatible_os>ALL</compatible_os>
+ <install size="2">http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2.xpi</install>
+ </addon>
+
+ <addon_compatibility hosted="false">
+ <guid>addon6@tests.mozilla.org</guid>
+ <name>Addon Test 6</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>1.0</min_version>
+ <max_version>1.0</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <min_version>1.0</min_version>
+ <max_version>1.0</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate.rdf
new file mode 100644
index 000000000..d1dc992d5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate.rdf
@@ -0,0 +1,125 @@
+<?xml version="1.0"?>
+
+<!-- This is a copy of extensions.rdf from Firefox 3.5 including four
+ test extensions. Addon1 was user enabled, addon2 was user disabled, addon3
+ was pending user disable at the next restart and addon4 was pending user
+ enable at the next restart. Additionally addon1 and 2 have had
+ compatibility updates applies to make them compatible with the app and
+ toolkit respectively, addon3 and 4 have not. addon5 is disabled however
+ at the same time as the migration a new version should be detected. addon6
+ is pending install and needs a compatibility update to be compatible.
+ It also contains two themes in the profile -->
+
+<RDF:RDF xmlns:NS1="http://www.mozilla.org/2004/em-rdf#"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="rdf:#$w8dNC3"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="1" />
+ <RDF:Description RDF:about="rdf:#$w8dNC4"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC5"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC6"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC2"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="1" />
+ <RDF:Description RDF:about="rdf:#$w8dNC1"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$w8dNC7"
+ NS1:id="toolkit@mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$oadNC1"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:minVersion="1"
+ NS1:maxVersion="2" />
+ <RDF:Description RDF:about="rdf:#$TpnM4"
+ NS1:id="xpcshell@tests.mozilla.org"
+ NS1:updatedMinVersion="1"
+ NS1:updatedMaxVersion="2" />
+ <RDF:Description RDF:about="urn:mozilla:item:addon1@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Test 1">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$oadNC1"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon2@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 2"
+ NS1:userDisabled="true">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC1"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon3@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 3"
+ NS1:userDisabled="needs-disable">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC3"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon4@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Test 4"
+ NS1:userDisabled="needs-enable">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC2"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon5@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Test 5"
+ NS1:userDisabled="true">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC7"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:addon6@tests.mozilla.org"
+ NS1:name="Test 6"
+ NS1:version="1.0"
+ NS1:newVersion="1.0"
+ NS1:installLocation="app-profile">
+ <NS1:type NC:parseType="Integer">2</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$TpnM4"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:theme1@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="1.0"
+ NS1:name="Theme 2"
+ NS1:internalName="theme1/1.0">
+ <NS1:type NC:parseType="Integer">4</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC5"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="urn:mozilla:item:theme2@tests.mozilla.org"
+ NS1:installLocation="app-profile"
+ NS1:version="2.0"
+ NS1:name="Theme 2"
+ NS1:internalName="theme2/1.0">
+ <NS1:type NC:parseType="Integer">4</NS1:type>
+ <NS1:targetApplication RDF:resource="rdf:#$w8dNC6"/>
+ </RDF:Description>
+ <RDF:Seq RDF:about="urn:mozilla:item:root">
+ <RDF:li RDF:resource="urn:mozilla:item:addon1@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon2@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon3@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon4@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon5@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:addon6@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:theme1@tests.mozilla.org"/>
+ <RDF:li RDF:resource="urn:mozilla:item:theme2@tests.mozilla.org"/>
+ </RDF:Seq>
+</RDF:RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate4.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate4.rdf
new file mode 100644
index 000000000..a3bf4f8ae
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate4.rdf
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:addon5@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon6@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_migrate4_6.xpi</em:updateLink>
+ <em:updateInfoURL>http://example.com/updateInfo.xhtml</em:updateInfoURL>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_no_update.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_no_update.json
new file mode 100644
index 000000000..2773c7f98
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_no_update.json
@@ -0,0 +1,7 @@
+{
+ "addons": {
+ "test_no_update_webext@tests.mozilla.org": {
+ "updates": []
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/ancient.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/ancient.xml
new file mode 100644
index 000000000..699257f87
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/ancient.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem blockID="i454" id="ancient@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/new.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/new.xml
new file mode 100644
index 000000000..8cbfb5d6a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/new.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1396046918000">
+ <emItems>
+ <emItem blockID="i454" id="new@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/old.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/old.xml
new file mode 100644
index 000000000..75bd6e934
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/old.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1296046918000">
+ <emItems>
+ <emItem blockID="i454" id="old@tests.mozilla.org">
+ <versionRange minVersion="0" maxVersion="*" severity="3"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtp.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtp.xml
new file mode 100644
index 000000000..d3564aebd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtp.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_0"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="0"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_1"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="1"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_2"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_3"/>
+ <versionRange minVersion="0" maxVersion="*" vulnerabilitystatus="2"/>
+ </pluginItem>
+ <pluginItem>
+ <match name="name" exp="^test_plugin_4"/>
+ <versionRange minVersion="0" maxVersion="*" severity="1" vulnerabilitystatus="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml
new file mode 100644
index 000000000..7cd8496b3
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <pluginItems>
+ <pluginItem>
+ <match name="name" exp="^Test Plug-in"/>
+ <versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"/>
+ </pluginItem>
+ </pluginItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_proxy/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_proxy/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_proxy/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_softblocked1.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_softblocked1.xml
new file mode 100644
index 000000000..a1d18470c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_softblocked1.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+ <emItems>
+ <emItem id="softblock1@tests.mozilla.org">
+ <versionRange severity="1"/>
+ </emItem>
+ </emItems>
+</blocklist>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_sourceURI.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_sourceURI.xml
new file mode 100644
index 000000000..949288e3f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_sourceURI.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="1">
+ <!-- Passes all requirements -->
+ <addon>
+ <name>Test</name>
+ <type id="1">Extension</type>
+ <guid>addon@tests.mozilla.org</guid>
+ <version>1</version>
+ <compatible_applications>
+ <application>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ </application>
+ </compatible_applications>
+ <install>http://www.example.com/testaddon.xpi</install>
+ </addon>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_temporary/bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/data/test_temporary/bootstrap.js
new file mode 100644
index 000000000..1666f2972
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_temporary/bootstrap.js
@@ -0,0 +1 @@
+Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.json
new file mode 100644
index 000000000..027a9b233
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.json
@@ -0,0 +1,215 @@
+{
+ "addons": {
+ "addon1@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_min_version": "1"
+ }
+ }
+ },
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "2",
+ "strict_min_version": "2"
+ }
+ }
+ },
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update.xpi",
+ "update_info_url": "http://example.com/updateInfo.xhtml",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_min_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon2@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0",
+ "advisory_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon2@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0",
+ "advisory_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon3@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "3",
+ "advisory_max_version": "3"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon4@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "5.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0",
+ "advisory_max_version": "0"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon7@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0",
+ "advisory_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon8@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update8.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "advisory_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon9@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update9_2.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "advisory_max_version": "1"
+ }
+ }
+ },
+ {
+ "_comment_": "Incompatible when strict compatibility is enabled",
+ "version": "3.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update9_3.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.9",
+ "advisory_max_version": "0.9"
+ }
+ }
+ },
+ {
+ "_comment_": "Incompatible due to compatibility override",
+ "version": "4.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update9_4.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.9",
+ "advisory_max_version": "0.9"
+ }
+ }
+ },
+ {
+ "_comment_": "Addon for future version of app",
+ "version": "4.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update9_5.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "5",
+ "advisory_max_version": "6"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon10@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update10.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.1",
+ "advisory_max_version": "0.4"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon11@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update11.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.1",
+ "strict_max_version": "0.2"
+ }
+ }
+ }
+ ]
+ },
+
+ "addon12@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:%PORT%/addons/test_update12.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "advisory_max_version": "1"
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.rdf
new file mode 100644
index 000000000..4d4640f60
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.rdf
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <!-- Shouldn't fire onCompatibilityUpdateAvailable since this
+ information is already in the install.rdf -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+
+ <!-- Should be ignored as it is not for the present version of the
+ application -->
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update.xpi</em:updateLink>
+ <em:updateInfoURL>http://example.com/updateInfo.xhtml</em:updateInfoURL>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon2@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon3@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>3</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon4@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>0</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon7@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon8@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update8.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon9@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Incompatible when strict compatibility is enabled -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.9</em:minVersion>
+ <em:maxVersion>0.9</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Incompatible due to compatibility override -->
+ <li>
+ <Description>
+ <em:version>4.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.9</em:minVersion>
+ <em:maxVersion>0.9</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Addon for future version of app -->
+ <li>
+ <Description>
+ <em:version>5.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>5</em:minVersion>
+ <em:maxVersion>6</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update9_5.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon10@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.4</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update10.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:extension:addon11@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:strictCompatibility>true</em:strictCompatibility>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update11.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <Description about="urn:mozilla:item:addon12@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:%PORT%/addons/test_update12.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.xml b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.xml
new file mode 100644
index 000000000..62928815b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<searchresults total_results="11">
+ <addon>
+ <name>Test Addon 9</name>
+ <type id="1">Extension</type>
+ <guid>addon9@tests.mozilla.org</guid>
+ </addon>
+ <addon_compatibility hosted="true">
+ <guid>addon9@tests.mozilla.org</guid>
+ <name>Test Addon 9</name>
+ <version_ranges>
+ <version_range type="incompatible">
+ <min_version>4</min_version>
+ <max_version>4</max_version>
+ <compatible_applications>
+ <application>
+ <name>XPCShell</name>
+ <min_version>1</min_version>
+ <max_version>1</max_version>
+ <appID>xpcshell@tests.mozilla.org</appID>
+ </application>
+ </compatible_applications>
+ </version_range>
+ </version_ranges>
+ </addon_compatibility>
+</searchresults>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_update_multi.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update_multi.rdf
new file mode 100644
index 000000000..f28a3f26d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_update_multi.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:updatemulti@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_update_multi2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.json b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.json
new file mode 100644
index 000000000..811e50158
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.json
@@ -0,0 +1,327 @@
+{
+ "addons": {
+ "updatecheck1@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "1.0",
+ "update_link": "https://localhost:4444/addons/test1.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ },
+ {
+ "_comment_": "This update is incompatible and so should not be considered a valid update",
+ "version": "2.0",
+ "update_link": "https://localhost:4444/addons/test2.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "2",
+ "strict_max_version": "2"
+ }
+ }
+ },
+ {
+ "version": "3.0",
+ "update_link": "https://localhost:4444/addons/test3.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ },
+ {
+ "version": "2.0",
+ "update_link": "https://localhost:4444/addons/test2.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "2"
+ }
+ }
+ },
+ {
+ "_comment_": "This update is incompatible and so should not be considered a valid update",
+ "version": "4.0",
+ "update_link": "https://localhost:4444/addons/test4.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "2",
+ "strict_max_version": "2"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_5@tests.mozilla.org": {
+ "_comment_": "An update which expects a signature. It will fail since signatures are ",
+ "_comment_": "supported in this format.",
+ "_comment_": "The updateLink will also be ignored since it is not secure and there ",
+ "_comment_": "is no updateHash.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_5@tests.mozilla.org": {
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_7@tests.mozilla.org": {
+ "_comment_": "An update which expects a signature. It will fail since signatures are ",
+ "_comment_": "supported in this format.",
+ "_comment_": "The updateLink will also be ignored since it is not secure ",
+ "_comment_": "and there is no updateHash.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "2"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_8@tests.mozilla.org": {
+ "_comment_": "The updateLink will be ignored since it is not secure and ",
+ "_comment_": "there is no updateHash.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_9@tests.mozilla.org": {
+ "_comment_": "The updateLink will used since there is an updateHash to verify it.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "update_hash": "sha256:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_10@tests.mozilla.org": {
+ "_comment_": "The updateLink will used since it is a secure URL.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "https://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_11@tests.mozilla.org": {
+ "_comment_": "The updateLink will used since it is a secure URL.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "https://localhost:4444/broken.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_12@tests.mozilla.org": {
+ "_comment_": "The updateLink will not be used since the updateHash ",
+ "_comment_": "verifying it is not strong enough.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "http://localhost:4444/broken.xpi",
+ "update_hash": "sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "test_bug378216_13@tests.mozilla.org": {
+ "_comment_": "An update with a weak hash. The updateLink will used since it is ",
+ "_comment_": "a secure URL.",
+
+ "updates": [
+ {
+ "version": "2.0",
+ "update_link": "https://localhost:4444/broken.xpi",
+ "update_hash": "sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "1",
+ "strict_max_version": "1"
+ }
+ }
+ }
+ ]
+ },
+
+ "_comment_": "There should be no information present for test_bug378216_14",
+
+ "test_bug378216_15@tests.mozilla.org": {
+ "_comment_": "Invalid update JSON",
+
+ "updates": "foo"
+ },
+
+ "ignore-compat@tests.mozilla.org": {
+ "_comment_": "Various updates available - one is not compatible, but compatibility checking is disabled",
+
+ "updates": [
+ {
+ "version": "1.0",
+ "update_link": "https://localhost:4444/addons/test1.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.1",
+ "advisory_max_version": "0.2"
+ }
+ }
+ },
+ {
+ "version": "2.0",
+ "update_link": "https://localhost:4444/addons/test2.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.5",
+ "advisory_max_version": "0.6"
+ }
+ }
+ },
+ {
+ "_comment_": "Update for future app versions - should never be compatible",
+ "version": "3.0",
+ "update_link": "https://localhost:4444/addons/test3.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "2",
+ "advisory_max_version": "3"
+ }
+ }
+ }
+ ]
+ },
+
+ "compat-override@tests.mozilla.org": {
+ "_comment_": "Various updates available - one is not compatible, but compatibility checking is disabled",
+
+ "updates": [
+ {
+ "_comment_": "Has compatibility override, but it doesn't match this app version",
+ "version": "1.0",
+ "update_link": "https://localhost:4444/addons/test1.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.1",
+ "advisory_max_version": "0.2"
+ }
+ }
+ },
+ {
+ "_comment_": "Has compatibility override, so is incompaible",
+ "version": "2.0",
+ "update_link": "https://localhost:4444/addons/test2.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.5",
+ "advisory_max_version": "0.6"
+ }
+ }
+ },
+ {
+ "_comment_": "Update for future app versions - should never be compatible",
+ "version": "3.0",
+ "update_link": "https://localhost:4444/addons/test3.xpi",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "2",
+ "advisory_max_version": "3"
+ }
+ }
+ }
+ ]
+ },
+
+ "compat-strict-optin@tests.mozilla.org": {
+ "_comment_": "Opt-in to strict compatibility checking",
+
+ "updates": [
+ {
+ "version": "1.0",
+ "update_link": "https://localhost:4444/addons/test1.xpi",
+ "_comment_": "strictCompatibility: true",
+ "applications": {
+ "gecko": {
+ "strict_min_version": "0.1",
+ "strict_max_version": "0.2"
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.rdf
new file mode 100644
index 000000000..c5d97ada0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.rdf
@@ -0,0 +1,419 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:updatecheck1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- This update is incompatible and so should not be considered a valid
+ update -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- This update is incompatible and so should not be considered a valid
+ update -->
+ <li>
+ <Description>
+ <em:version>4.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test4.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- An update with no signature which will fail if retrieved with an update
+ key. The updateLink will also be ignored since it is not secure and there
+ is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_5@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ </RDF:Description>
+
+ <!-- An update with a broken signature which will fail if retrieved with an
+ update key. The updateLink will also be ignored since it is not secure
+ and there is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_7@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMO1O2gwSCCth1GwYMgscfaNakpN40PJfOWt
+ ub2HVdg8+OXMciF8d/9eVWm8eH/IxuxyZlmRZTs3O5tv9eWAY5uBCtqDf1WgTsGk
+ jrgZow1fITkZI7w0//C8eKdMLAtGueGfNs2IlTd5P/0KH/hf1rPc1wUqEqKCd4+L
+ BcVq13ad</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will be ignored since it
+ is not secure and there is no updateHash. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_8@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMH/33P/bn148mVkAB8i5X8c4LhY52E+MPUT
+ yKHGpowZnRLgL2O0dfpm+rljOBfKi51322PFrsc6VIFml6x4Lrb5foxSyB0Vs9pb
+ SEDFWUKquOQvceQ9iEx5Pe0VzrmUZgcQxd8ksSunWL4wJaBZ/evE5amFC6sw3pv/
+ fjt8p3GN</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since there is
+ an updateHash to verify it. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_9@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJ5Dv3Zd7/j5dLchCw9iO/cxPq8oOhOYD2M+
+ jUKvmHCyTBRIEaJrE4N7yVbRYk++ERIfyVepLivsVi4pBmF7JTdw0NaKUA0LiOoT
+ mRL8I7s5NPjCiiNcdqbncWyiZwIj1w1nkbWGTlH/gEjRW/LbvT4JAuec8yNFDa4S
+ X8mOMf7k</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_10@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAGvf7XqqoTl5WofrNq55E7W+UttOEDXLB3Oi
+ XDiXe0i6njlozilseaUo1hgfQhhzN9gkyetP5tGBVcLRrVyliKpJmD6ABCVGW1lS
+ qS+SEw7gDHyHkvwKMyWKedpRGChqLYnnf+Y+CX3MWLZLkwPXMKdTYgN3Rx0lEnJk
+ 37LSEMKE</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_11@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBACMX/KReOGSJ8CMGRroH1v3Gjv/Qs/pqH+Ow
+ o+hCKWLUKx7hpJgVJkXXdAHW0U88NXlp1S2H0WqA7I/CdmNXJSPzzV/J4z1dZgXh
+ JbW6mqNb0pj6nIe7g8OLzSxDgBmO4DUP5DAmnmqciJLWQzN7OdbcwrWz6xPN5kZF
+ A90eF5zy</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will not be used since the
+ updateHash verifying it is not strong enough. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_12@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJRfcFvHIWxVyycCw8IjNmEhabc2uqA1zQwp
+ 5oKh3Y23hwIsQ6xy68Wtjte1NEYFRt5fWkbMXj9YQj6LpVbzBKiGATcrq6MycZKK
+ o5N22cWbrKKRweJezTyN4eLfQg21pG7r8mdfS0bIA28ZVFtQOmORejoUesEouCGy
+ eKYk9nS2</em:signature>
+ </RDF:Description>
+
+ <!-- An update with a valid signature. The updateLink will used since it is
+ a secure URL. -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_13@tests.mozilla.org">
+ <em:updates>
+ <RDF:Seq>
+ <RDF:li>
+ <RDF:Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <RDF:Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>https://localhost:4444/broken.xpi</em:updateLink>
+ <em:updateHash>md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6</em:updateHash>
+ </RDF:Description>
+ </em:targetApplication>
+ </RDF:Description>
+ </RDF:li>
+ </RDF:Seq>
+ </em:updates>
+ <em:signature>MIGTMA0GCSqGSIb3DQEBBQUAA4GBALQKwzLFr/VOw3gJvv/LCh3/PWDd9FqmFnX+
+ hJjBmCaUDtG7CXn1i0h8ed8IeRHpLLT7FCzVwU3bH9BUjdm8wc3ObtlNbd8go01a
+ CoXz50r3rYPcYz4WS+7/+lvrUqsuWd9Wj+q0NeCPiNaaro6/AolE2Qf5JFRL3lxY
+ lsKWAnVO</em:signature>
+ </RDF:Description>
+
+ <!-- There should be no information present for test_bug378216_14 -->
+
+ <!-- Invalid update RDF -->
+ <RDF:Description about="urn:mozilla:extension:test_bug378216_15@tests.mozilla.org">
+ <em:updates>Foo</em:updates>
+ </RDF:Description>
+
+ <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+ <Description about="urn:mozilla:extension:ignore-compat@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.5</em:minVersion>
+ <em:maxVersion>0.6</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Update for future app versions - should never be compatible -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- Various updates available - one is not compatible, but compatibility checking is disabled -->
+ <Description about="urn:mozilla:extension:compat-override@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <!-- Has compatibility override, but it doesn't match this app version -->
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Has compatibility override, so is incompaible -->
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.5</em:minVersion>
+ <em:maxVersion>0.6</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ <!-- Update for future app versions - should never be compatible -->
+ <li>
+ <Description>
+ <em:version>3.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>2</em:minVersion>
+ <em:maxVersion>3</em:maxVersion>
+ <em:updateLink>https://localhost:4444/addons/test3.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+ <!-- Opt-in to strict compatibility checking -->
+ <Description about="urn:mozilla:extension:compat-strict-optin@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>1.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>0.1</em:minVersion>
+ <em:maxVersion>0.2</em:maxVersion>
+ <em:strictCompatibility>true</em:strictCompatibility>
+ <em:updateLink>https://localhost:4444/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf
new file mode 100644
index 000000000..ec6e88ec4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-ignore@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_normal.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_normal.rdf
new file mode 100644
index 000000000..2ef88860e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_normal.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-normal@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_strict.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_strict.rdf
new file mode 100644
index 000000000..2f72c181d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_strict.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:compatmode-strict@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>2</em:maxVersion>
+ <em:updateLink>https://localhost:%PORT%/addons/test1.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/test_updateid.rdf b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updateid.rdf
new file mode 100644
index 000000000..c13928520
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/test_updateid.rdf
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+ <Description about="urn:mozilla:extension:addon1@tests.mozilla.org">
+ <em:updates>
+ <Seq>
+ <li>
+ <Description>
+ <em:version>2.0</em:version>
+ <em:targetApplication>
+ <Description>
+ <em:id>xpcshell@tests.mozilla.org</em:id>
+ <em:minVersion>1</em:minVersion>
+ <em:maxVersion>1</em:maxVersion>
+ <em:updateLink>http://localhost:4444/addons/test_updateid2.xpi</em:updateLink>
+ </Description>
+ </em:targetApplication>
+ </Description>
+ </li>
+ </Seq>
+ </em:updates>
+ </Description>
+
+</RDF>
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpi
new file mode 100644
index 000000000..51b00475a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpi b/toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpi
new file mode 100644
index 000000000..6b4abaa69
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpi
Binary files differ
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js b/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
new file mode 100644
index 000000000..960caceeb
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/head_addons.js
@@ -0,0 +1,1345 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var AM_Cc = Components.classes;
+var AM_Ci = Components.interfaces;
+var AM_Cu = Components.utils;
+
+AM_Cu.importGlobalProperties(["TextEncoder"]);
+
+const CERTDB_CONTRACTID = "@mozilla.org/security/x509certdb;1";
+const CERTDB_CID = Components.ID("{fb0bbc5c-452e-4783-b32c-80124693d871}");
+
+const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
+const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
+const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
+const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
+const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
+const PREF_GETADDONS_BYIDS_PERFORMANCE = "extensions.getAddons.getWithPerformance.url";
+const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
+
+// Forcibly end the test if it runs longer than 15 minutes
+const TIMEOUT_MS = 900000;
+
+// Maximum error in file modification times. Some file systems don't store
+// modification times exactly. As long as we are closer than this then it
+// still passes.
+const MAX_TIME_DIFFERENCE = 3000;
+
+// Time to reset file modified time relative to Date.now() so we can test that
+// times are modified (10 hours old).
+const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000;
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+Components.utils.import("resource://gre/modules/FileUtils.jsm");
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.import("resource://gre/modules/Promise.jsm");
+Components.utils.import("resource://gre/modules/Task.jsm");
+const { OS } = Components.utils.import("resource://gre/modules/osfile.jsm", {});
+Components.utils.import("resource://gre/modules/AsyncShutdown.jsm");
+
+Components.utils.import("resource://testing-common/AddonTestUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Extension",
+ "resource://gre/modules/Extension.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestUtils",
+ "resource://testing-common/ExtensionXPCShellUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
+ "resource://testing-common/httpd.js");
+XPCOMUtils.defineLazyModuleGetter(this, "MockAsyncShutdown",
+ "resource://testing-common/AddonTestUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "MockRegistrar",
+ "resource://testing-common/MockRegistrar.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "MockRegistry",
+ "resource://testing-common/MockRegistry.jsm");
+
+const {
+ awaitPromise,
+ createAppInfo,
+ createInstallRDF,
+ createTempWebExtensionFile,
+ createUpdateRDF,
+ getFileForAddon,
+ manuallyInstall,
+ manuallyUninstall,
+ promiseAddonByID,
+ promiseAddonEvent,
+ promiseAddonsByIDs,
+ promiseAddonsWithOperationsByTypes,
+ promiseCompleteAllInstalls,
+ promiseConsoleOutput,
+ promiseFindAddonUpdates,
+ promiseInstallAllFiles,
+ promiseInstallFile,
+ promiseRestartManager,
+ promiseSetExtensionModifiedTime,
+ promiseShutdownManager,
+ promiseStartupManager,
+ promiseWriteProxyFileToDir,
+ registerDirectory,
+ setExtensionModifiedTime,
+ writeFilesToZip
+} = AddonTestUtils;
+
+// WebExtension wrapper for ease of testing
+ExtensionTestUtils.init(this);
+
+AddonTestUtils.init(this);
+AddonTestUtils.overrideCertDB();
+
+Object.defineProperty(this, "gAppInfo", {
+ get() {
+ return AddonTestUtils.appInfo;
+ },
+});
+
+Object.defineProperty(this, "gExtensionsINI", {
+ get() {
+ return AddonTestUtils.extensionsINI.clone();
+ },
+});
+
+Object.defineProperty(this, "gInternalManager", {
+ get() {
+ return AddonTestUtils.addonIntegrationService.QueryInterface(AM_Ci.nsITimerCallback);
+ },
+});
+
+Object.defineProperty(this, "gProfD", {
+ get() {
+ return AddonTestUtils.profileDir.clone();
+ },
+});
+
+Object.defineProperty(this, "gTmpD", {
+ get() {
+ return AddonTestUtils.tempDir.clone();
+ },
+});
+
+Object.defineProperty(this, "gUseRealCertChecks", {
+ get() {
+ return AddonTestUtils.useRealCertChecks;
+ },
+ set(val) {
+ return AddonTestUtils.useRealCertChecks = val;
+ },
+});
+
+Object.defineProperty(this, "TEST_UNPACKED", {
+ get() {
+ return AddonTestUtils.testUnpacked;
+ },
+ set(val) {
+ return AddonTestUtils.testUnpacked = val;
+ },
+});
+
+// We need some internal bits of AddonManager
+var AMscope = Components.utils.import("resource://gre/modules/AddonManager.jsm", {});
+var { AddonManager, AddonManagerInternal, AddonManagerPrivate } = AMscope;
+
+var gPort = null;
+var gUrlToFileMap = {};
+
+// Map resource://xpcshell-data/ to the data directory
+var resHandler = Services.io.getProtocolHandler("resource")
+ .QueryInterface(AM_Ci.nsISubstitutingProtocolHandler);
+// Allow non-existent files because of bug 1207735
+var dataURI = NetUtil.newURI(do_get_file("data", true));
+resHandler.setSubstitution("xpcshell-data", dataURI);
+
+function isManifestRegistered(file) {
+ let manifests = Components.manager.getManifestLocations();
+ for (let i = 0; i < manifests.length; i++) {
+ let manifest = manifests.queryElementAt(i, AM_Ci.nsIURI);
+
+ // manifest is the url to the manifest file either in an XPI or a directory.
+ // We want the location of the XPI or directory itself.
+ if (manifest instanceof AM_Ci.nsIJARURI) {
+ manifest = manifest.JARFile.QueryInterface(AM_Ci.nsIFileURL).file;
+ }
+ else if (manifest instanceof AM_Ci.nsIFileURL) {
+ manifest = manifest.file.parent;
+ }
+ else {
+ continue;
+ }
+
+ if (manifest.equals(file))
+ return true;
+ }
+ return false;
+}
+
+// Listens to messages from bootstrap.js telling us what add-ons were started
+// and stopped etc. and performs some sanity checks that only installed add-ons
+// are started etc.
+this.BootstrapMonitor = {
+ inited: false,
+
+ // Contain the current state of add-ons in the system
+ installed: new Map(),
+ started: new Map(),
+
+ // Contain the last state of shutdown and uninstall calls for an add-on
+ stopped: new Map(),
+ uninstalled: new Map(),
+
+ startupPromises: [],
+ installPromises: [],
+
+ init() {
+ this.inited = true;
+ Services.obs.addObserver(this, "bootstrapmonitor-event", false);
+ },
+
+ shutdownCheck() {
+ if (!this.inited)
+ return;
+
+ do_check_eq(this.started.size, 0);
+ },
+
+ clear(id) {
+ this.installed.delete(id);
+ this.started.delete(id);
+ this.stopped.delete(id);
+ this.uninstalled.delete(id);
+ },
+
+ promiseAddonStartup(id) {
+ return new Promise(resolve => {
+ this.startupPromises.push(resolve);
+ });
+ },
+
+ promiseAddonInstall(id) {
+ return new Promise(resolve => {
+ this.installPromises.push(resolve);
+ });
+ },
+
+ checkMatches(cached, current) {
+ do_check_neq(cached, undefined);
+ do_check_eq(current.data.version, cached.data.version);
+ do_check_eq(current.data.installPath, cached.data.installPath);
+ do_check_eq(current.data.resourceURI, cached.data.resourceURI);
+ },
+
+ checkAddonStarted(id, version = undefined) {
+ let started = this.started.get(id);
+ do_check_neq(started, undefined);
+ if (version != undefined)
+ do_check_eq(started.data.version, version);
+
+ // Chrome should be registered by now
+ let installPath = new FileUtils.File(started.data.installPath);
+ let isRegistered = isManifestRegistered(installPath);
+ do_check_true(isRegistered);
+ },
+
+ checkAddonNotStarted(id) {
+ do_check_false(this.started.has(id));
+ },
+
+ checkAddonInstalled(id, version = undefined) {
+ const installed = this.installed.get(id);
+ notEqual(installed, undefined);
+ if (version !== undefined) {
+ equal(installed.data.version, version);
+ }
+ return installed;
+ },
+
+ checkAddonNotInstalled(id) {
+ do_check_false(this.installed.has(id));
+ },
+
+ observe(subject, topic, data) {
+ let info = JSON.parse(data);
+ let id = info.data.id;
+ let installPath = new FileUtils.File(info.data.installPath);
+
+ if (subject && subject.wrappedJSObject) {
+ // NOTE: in some of the new tests, we need to received the real objects instead of
+ // their JSON representations, but most of the current tests expect intallPath
+ // and resourceURI to have been converted to strings.
+ info.data = Object.assign({}, subject.wrappedJSObject.data, {
+ installPath: info.data.installPath,
+ resourceURI: info.data.resourceURI,
+ });
+ }
+
+ // If this is the install event the add-ons shouldn't already be installed
+ if (info.event == "install") {
+ this.checkAddonNotInstalled(id);
+
+ this.installed.set(id, info);
+
+ for (let resolve of this.installPromises)
+ resolve();
+ this.installPromises = [];
+ }
+ else {
+ this.checkMatches(this.installed.get(id), info);
+ }
+
+ // If this is the shutdown event than the add-on should already be started
+ if (info.event == "shutdown") {
+ this.checkMatches(this.started.get(id), info);
+
+ this.started.delete(id);
+ this.stopped.set(id, info);
+
+ // Chrome should still be registered at this point
+ let isRegistered = isManifestRegistered(installPath);
+ do_check_true(isRegistered);
+
+ // XPIProvider doesn't bother unregistering chrome on app shutdown but
+ // since we simulate restarts we must do so manually to keep the registry
+ // consistent.
+ if (info.reason == 2 /* APP_SHUTDOWN */)
+ Components.manager.removeBootstrappedManifestLocation(installPath);
+ }
+ else {
+ this.checkAddonNotStarted(id);
+ }
+
+ if (info.event == "uninstall") {
+ // Chrome should be unregistered at this point
+ let isRegistered = isManifestRegistered(installPath);
+ do_check_false(isRegistered);
+
+ this.installed.delete(id);
+ this.uninstalled.set(id, info)
+ }
+ else if (info.event == "startup") {
+ this.started.set(id, info);
+
+ // Chrome should be registered at this point
+ let isRegistered = isManifestRegistered(installPath);
+ do_check_true(isRegistered);
+
+ for (let resolve of this.startupPromises)
+ resolve();
+ this.startupPromises = [];
+ }
+ }
+}
+
+AddonTestUtils.on("addon-manager-shutdown", () => BootstrapMonitor.shutdownCheck());
+
+function isNightlyChannel() {
+ var channel = "default";
+ try {
+ channel = Services.prefs.getCharPref("app.update.channel");
+ }
+ catch (e) { }
+
+ return channel != "aurora" && channel != "beta" && channel != "release" && channel != "esr";
+}
+
+/**
+ * Tests that an add-on does appear in the crash report annotations, if
+ * crash reporting is enabled. The test will fail if the add-on is not in the
+ * annotation.
+ * @param aId
+ * The ID of the add-on
+ * @param aVersion
+ * The version of the add-on
+ */
+function do_check_in_crash_annotation(aId, aVersion) {
+ if (!("nsICrashReporter" in AM_Ci))
+ return;
+
+ if (!("Add-ons" in gAppInfo.annotations)) {
+ do_check_false(true);
+ return;
+ }
+
+ let addons = gAppInfo.annotations["Add-ons"].split(",");
+ do_check_false(addons.indexOf(encodeURIComponent(aId) + ":" +
+ encodeURIComponent(aVersion)) < 0);
+}
+
+/**
+ * Tests that an add-on does not appear in the crash report annotations, if
+ * crash reporting is enabled. The test will fail if the add-on is in the
+ * annotation.
+ * @param aId
+ * The ID of the add-on
+ * @param aVersion
+ * The version of the add-on
+ */
+function do_check_not_in_crash_annotation(aId, aVersion) {
+ if (!("nsICrashReporter" in AM_Ci))
+ return;
+
+ if (!("Add-ons" in gAppInfo.annotations)) {
+ do_check_true(true);
+ return;
+ }
+
+ let addons = gAppInfo.annotations["Add-ons"].split(",");
+ do_check_true(addons.indexOf(encodeURIComponent(aId) + ":" +
+ encodeURIComponent(aVersion)) < 0);
+}
+
+/**
+ * Returns a testcase xpi
+ *
+ * @param aName
+ * The name of the testcase (without extension)
+ * @return an nsIFile pointing to the testcase xpi
+ */
+function do_get_addon(aName) {
+ return do_get_file("addons/" + aName + ".xpi");
+}
+
+function do_get_addon_hash(aName, aAlgorithm) {
+ let file = do_get_addon(aName);
+ return do_get_file_hash(file);
+}
+
+function do_get_file_hash(aFile, aAlgorithm) {
+ if (!aAlgorithm)
+ aAlgorithm = "sha1";
+
+ let crypto = AM_Cc["@mozilla.org/security/hash;1"].
+ createInstance(AM_Ci.nsICryptoHash);
+ crypto.initWithString(aAlgorithm);
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fis.init(aFile, -1, -1, false);
+ crypto.updateFromStream(fis, aFile.fileSize);
+
+ // return the two-digit hexadecimal code for a byte
+ let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
+
+ let binary = crypto.finish(false);
+ let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
+ return aAlgorithm + ":" + hash.join("");
+}
+
+/**
+ * Returns an extension uri spec
+ *
+ * @param aProfileDir
+ * The extension install directory
+ * @return a uri spec pointing to the root of the extension
+ */
+function do_get_addon_root_uri(aProfileDir, aId) {
+ let path = aProfileDir.clone();
+ path.append(aId);
+ if (!path.exists()) {
+ path.leafName += ".xpi";
+ return "jar:" + Services.io.newFileURI(path).spec + "!/";
+ }
+ return Services.io.newFileURI(path).spec;
+}
+
+function do_get_expected_addon_name(aId) {
+ if (TEST_UNPACKED)
+ return aId;
+ return aId + ".xpi";
+}
+
+/**
+ * Check that an array of actual add-ons is the same as an array of
+ * expected add-ons.
+ *
+ * @param aActualAddons
+ * The array of actual add-ons to check.
+ * @param aExpectedAddons
+ * The array of expected add-ons to check against.
+ * @param aProperties
+ * An array of properties to check.
+ */
+function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
+ do_check_neq(aActualAddons, null);
+ do_check_eq(aActualAddons.length, aExpectedAddons.length);
+ for (let i = 0; i < aActualAddons.length; i++)
+ do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
+}
+
+/**
+ * Check that the actual add-on is the same as the expected add-on.
+ *
+ * @param aActualAddon
+ * The actual add-on to check.
+ * @param aExpectedAddon
+ * The expected add-on to check against.
+ * @param aProperties
+ * An array of properties to check.
+ */
+function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
+ do_check_neq(aActualAddon, null);
+
+ aProperties.forEach(function(aProperty) {
+ let actualValue = aActualAddon[aProperty];
+ let expectedValue = aExpectedAddon[aProperty];
+
+ // Check that all undefined expected properties are null on actual add-on
+ if (!(aProperty in aExpectedAddon)) {
+ if (actualValue !== undefined && actualValue !== null) {
+ do_throw("Unexpected defined/non-null property for add-on " +
+ aExpectedAddon.id + " (addon[" + aProperty + "] = " +
+ actualValue.toSource() + ")");
+ }
+
+ return;
+ }
+ else if (expectedValue && !actualValue) {
+ do_throw("Missing property for add-on " + aExpectedAddon.id +
+ ": expected addon[" + aProperty + "] = " + expectedValue);
+ return;
+ }
+
+ switch (aProperty) {
+ case "creator":
+ do_check_author(actualValue, expectedValue);
+ break;
+
+ case "developers":
+ case "translators":
+ case "contributors":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_author(actualValue[i], expectedValue[i]);
+ break;
+
+ case "screenshots":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_screenshot(actualValue[i], expectedValue[i]);
+ break;
+
+ case "sourceURI":
+ do_check_eq(actualValue.spec, expectedValue);
+ break;
+
+ case "updateDate":
+ do_check_eq(actualValue.getTime(), expectedValue.getTime());
+ break;
+
+ case "compatibilityOverrides":
+ do_check_eq(actualValue.length, expectedValue.length);
+ for (let i = 0; i < actualValue.length; i++)
+ do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
+ break;
+
+ case "icons":
+ do_check_icons(actualValue, expectedValue);
+ break;
+
+ default:
+ if (remove_port(actualValue) !== remove_port(expectedValue))
+ do_throw("Failed for " + aProperty + " for add-on " + aExpectedAddon.id +
+ " (" + actualValue + " === " + expectedValue + ")");
+ }
+ });
+}
+
+/**
+ * Check that the actual author is the same as the expected author.
+ *
+ * @param aActual
+ * The actual author to check.
+ * @param aExpected
+ * The expected author to check against.
+ */
+function do_check_author(aActual, aExpected) {
+ do_check_eq(aActual.toString(), aExpected.name);
+ do_check_eq(aActual.name, aExpected.name);
+ do_check_eq(aActual.url, aExpected.url);
+}
+
+/**
+ * Check that the actual screenshot is the same as the expected screenshot.
+ *
+ * @param aActual
+ * The actual screenshot to check.
+ * @param aExpected
+ * The expected screenshot to check against.
+ */
+function do_check_screenshot(aActual, aExpected) {
+ do_check_eq(aActual.toString(), aExpected.url);
+ do_check_eq(aActual.url, aExpected.url);
+ do_check_eq(aActual.width, aExpected.width);
+ do_check_eq(aActual.height, aExpected.height);
+ do_check_eq(aActual.thumbnailURL, aExpected.thumbnailURL);
+ do_check_eq(aActual.thumbnailWidth, aExpected.thumbnailWidth);
+ do_check_eq(aActual.thumbnailHeight, aExpected.thumbnailHeight);
+ do_check_eq(aActual.caption, aExpected.caption);
+}
+
+/**
+ * Check that the actual compatibility override is the same as the expected
+ * compatibility override.
+ *
+ * @param aAction
+ * The actual compatibility override to check.
+ * @param aExpected
+ * The expected compatibility override to check against.
+ */
+function do_check_compatibilityoverride(aActual, aExpected) {
+ do_check_eq(aActual.type, aExpected.type);
+ do_check_eq(aActual.minVersion, aExpected.minVersion);
+ do_check_eq(aActual.maxVersion, aExpected.maxVersion);
+ do_check_eq(aActual.appID, aExpected.appID);
+ do_check_eq(aActual.appMinVersion, aExpected.appMinVersion);
+ do_check_eq(aActual.appMaxVersion, aExpected.appMaxVersion);
+}
+
+function do_check_icons(aActual, aExpected) {
+ for (var size in aExpected) {
+ do_check_eq(remove_port(aActual[size]), remove_port(aExpected[size]));
+ }
+}
+
+function startupManager(aAppChanged) {
+ promiseStartupManager(aAppChanged);
+}
+
+/**
+ * Restarts the add-on manager as if the host application was restarted.
+ *
+ * @param aNewVersion
+ * An optional new version to use for the application. Passing this
+ * will change nsIXULAppInfo.version and make the startup appear as if
+ * the application version has changed.
+ */
+function restartManager(aNewVersion) {
+ awaitPromise(promiseRestartManager(aNewVersion));
+}
+
+function shutdownManager() {
+ awaitPromise(promiseShutdownManager());
+}
+
+function isItemMarkedMPIncompatible(aId) {
+ return AddonTestUtils.addonsList.isMultiprocessIncompatible(aId);
+}
+
+function isThemeInAddonsList(aDir, aId) {
+ return AddonTestUtils.addonsList.hasTheme(aDir, aId);
+}
+
+function isExtensionInAddonsList(aDir, aId) {
+ return AddonTestUtils.addonsList.hasExtension(aDir, aId);
+}
+
+function check_startup_changes(aType, aIds) {
+ var ids = aIds.slice(0);
+ ids.sort();
+ var changes = AddonManager.getStartupChanges(aType);
+ changes = changes.filter(aEl => /@tests.mozilla.org$/.test(aEl));
+ changes.sort();
+
+ do_check_eq(JSON.stringify(ids), JSON.stringify(changes));
+}
+
+/**
+ * Writes an install.rdf manifest into a directory using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The directory to add the install.rdf to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the directory
+ * @return An nsIFile for the directory in which the add-on is installed.
+ */
+function writeInstallRDFToDir(aData, aDir, aId = aData.id, aExtraFile = null) {
+ let files = {
+ "install.rdf": AddonTestUtils.createInstallRDF(aData),
+ };
+ if (aExtraFile)
+ files[aExtraFile] = "";
+
+ let dir = aDir.clone();
+ dir.append(aId);
+
+ awaitPromise(AddonTestUtils.promiseWriteFilesToDir(dir.path, files));
+ return dir;
+}
+
+/**
+ * Writes an install.rdf manifest into a packed extension using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The install directory to add the extension to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the extension
+ * @return A file pointing to where the extension was installed
+ */
+function writeInstallRDFToXPI(aData, aDir, aId = aData.id, aExtraFile = null) {
+ let files = {
+ "install.rdf": AddonTestUtils.createInstallRDF(aData),
+ };
+ if (aExtraFile)
+ files[aExtraFile] = "";
+
+ if (!aDir.exists())
+ aDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ var file = aDir.clone();
+ file.append(`${aId}.xpi`);
+
+ AddonTestUtils.writeFilesToZip(file.path, files);
+
+ return file;
+}
+
+/**
+ * Writes an install.rdf manifest into an extension using the properties passed
+ * in a JS object. The objects should contain a property for each property to
+ * appear in the RDF. The object may contain an array of objects with id,
+ * minVersion and maxVersion in the targetApplications property to give target
+ * application compatibility.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @param aDir
+ * The install directory to add the extension to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @param aExtraFile
+ * An optional dummy file to create in the extension
+ * @return A file pointing to where the extension was installed
+ */
+function writeInstallRDFForExtension(aData, aDir, aId, aExtraFile) {
+ if (TEST_UNPACKED) {
+ return writeInstallRDFToDir(aData, aDir, aId, aExtraFile);
+ }
+ return writeInstallRDFToXPI(aData, aDir, aId, aExtraFile);
+}
+
+/**
+ * Writes a manifest.json manifest into an extension using the properties passed
+ * in a JS object.
+ *
+ * @param aManifest
+ * The data to write
+ * @param aDir
+ * The install directory to add the extension to
+ * @param aId
+ * An optional string to override the default installation aId
+ * @return A file pointing to where the extension was installed
+ */
+function promiseWriteWebManifestForExtension(aData, aDir, aId = aData.applications.gecko.id) {
+ let files = {
+ "manifest.json": JSON.stringify(aData),
+ }
+ return AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
+}
+
+/**
+ * Creates an XPI file for some manifest data in the temporary directory and
+ * returns the nsIFile for it. The file will be deleted when the test completes.
+ *
+ * @param aData
+ * The object holding data about the add-on
+ * @return A file pointing to the created XPI file
+ */
+function createTempXPIFile(aData, aExtraFile) {
+ let files = {
+ "install.rdf": aData,
+ };
+ if (typeof aExtraFile == "object")
+ Object.assign(files, aExtraFile);
+ else if (aExtraFile)
+ files[aExtraFile] = "";
+
+ return AddonTestUtils.createTempXPIFile(files);
+}
+
+var gExpectedEvents = {};
+var gExpectedInstalls = [];
+var gNext = null;
+
+function getExpectedEvent(aId) {
+ if (!(aId in gExpectedEvents))
+ do_throw("Wasn't expecting events for " + aId);
+ if (gExpectedEvents[aId].length == 0)
+ do_throw("Too many events for " + aId);
+ let event = gExpectedEvents[aId].shift();
+ if (event instanceof Array)
+ return event;
+ return [event, true];
+}
+
+function getExpectedInstall(aAddon) {
+ if (gExpectedInstalls instanceof Array)
+ return gExpectedInstalls.shift();
+ if (!aAddon || !aAddon.id)
+ return gExpectedInstalls["NO_ID"].shift();
+ let id = aAddon.id;
+ if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array))
+ do_throw("Wasn't expecting events for " + id);
+ if (gExpectedInstalls[id].length == 0)
+ do_throw("Too many events for " + id);
+ return gExpectedInstalls[id].shift();
+}
+
+const AddonListener = {
+ onPropertyChanged: function(aAddon, aProperties) {
+ do_print(`Got onPropertyChanged event for ${aAddon.id}`);
+ let [event, properties] = getExpectedEvent(aAddon.id);
+ do_check_eq("onPropertyChanged", event);
+ do_check_eq(aProperties.length, properties.length);
+ properties.forEach(function(aProperty) {
+ // Only test that the expected properties are listed, having additional
+ // properties listed is not necessary a problem
+ if (aProperties.indexOf(aProperty) == -1)
+ do_throw("Did not see property change for " + aProperty);
+ });
+ return check_test_completed(arguments);
+ },
+
+ onEnabling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onEnabling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onEnabling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ return check_test_completed(arguments);
+ },
+
+ onEnabled: function(aAddon) {
+ do_print(`Got onEnabled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onEnabled", event);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ return check_test_completed(arguments);
+ },
+
+ onDisabling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onDisabling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onDisabling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE));
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ return check_test_completed(arguments);
+ },
+
+ onDisabled: function(aAddon) {
+ do_print(`Got onDisabled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onDisabled", event);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ return check_test_completed(arguments);
+ },
+
+ onInstalling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onInstalling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onInstalling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL));
+ return check_test_completed(arguments);
+ },
+
+ onInstalled: function(aAddon) {
+ do_print(`Got onInstalled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onInstalled", event);
+ return check_test_completed(arguments);
+ },
+
+ onUninstalling: function(aAddon, aRequiresRestart) {
+ do_print(`Got onUninstalling event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onUninstalling", event);
+ do_check_eq(aRequiresRestart, expectedRestart);
+ if (expectedRestart)
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ return check_test_completed(arguments);
+ },
+
+ onUninstalled: function(aAddon) {
+ do_print(`Got onUninstalled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onUninstalled", event);
+ return check_test_completed(arguments);
+ },
+
+ onOperationCancelled: function(aAddon) {
+ do_print(`Got onOperationCancelled event for ${aAddon.id}`);
+ let [event, expectedRestart] = getExpectedEvent(aAddon.id);
+ do_check_eq("onOperationCancelled", event);
+ return check_test_completed(arguments);
+ }
+};
+
+const InstallListener = {
+ onNewInstall: function(install) {
+ if (install.state != AddonManager.STATE_DOWNLOADED &&
+ install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
+ install.state != AddonManager.STATE_AVAILABLE)
+ do_throw("Bad install state " + install.state);
+ if (install.state != AddonManager.STATE_DOWNLOAD_FAILED)
+ do_check_eq(install.error, 0);
+ else
+ do_check_neq(install.error, 0);
+ do_check_eq("onNewInstall", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadStarted: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadStarted", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadEnded: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadEnded", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadFailed: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq("onDownloadFailed", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onDownloadCancelled: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_CANCELLED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onDownloadCancelled", getExpectedInstall());
+ return check_test_completed(arguments);
+ },
+
+ onInstallStarted: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALLING);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallStarted", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallEnded: function(install, newAddon) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallEnded", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallFailed: function(install) {
+ do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
+ do_check_eq("onInstallFailed", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onInstallCancelled: function(install) {
+ // If the install was cancelled by a listener returning false from
+ // onInstallStarted, then the state will revert to STATE_DOWNLOADED.
+ let possibleStates = [AddonManager.STATE_CANCELLED,
+ AddonManager.STATE_DOWNLOADED];
+ do_check_true(possibleStates.indexOf(install.state) != -1);
+ do_check_eq(install.error, 0);
+ do_check_eq("onInstallCancelled", getExpectedInstall(install.addon));
+ return check_test_completed(arguments);
+ },
+
+ onExternalInstall: function(aAddon, existingAddon, aRequiresRestart) {
+ do_check_eq("onExternalInstall", getExpectedInstall(aAddon));
+ do_check_false(aRequiresRestart);
+ return check_test_completed(arguments);
+ }
+};
+
+function hasFlag(aBits, aFlag) {
+ return (aBits & aFlag) != 0;
+}
+
+// Just a wrapper around setting the expected events
+function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ gExpectedInstalls = aExpectedInstalls;
+ gExpectedEvents = aExpectedEvents;
+ gNext = aNext;
+}
+
+// Checks if all expected events have been seen and if so calls the callback
+function check_test_completed(aArgs) {
+ if (!gNext)
+ return undefined;
+
+ if (gExpectedInstalls instanceof Array &&
+ gExpectedInstalls.length > 0)
+ return undefined;
+
+ for (let id in gExpectedInstalls) {
+ let installList = gExpectedInstalls[id];
+ if (installList.length > 0)
+ return undefined;
+ }
+
+ for (let id in gExpectedEvents) {
+ if (gExpectedEvents[id].length > 0)
+ return undefined;
+ }
+
+ return gNext.apply(null, aArgs);
+}
+
+// Verifies that all the expected events for all add-ons were seen
+function ensure_test_completed() {
+ for (let i in gExpectedEvents) {
+ if (gExpectedEvents[i].length > 0)
+ do_throw("Didn't see all the expected events for " + i);
+ }
+ gExpectedEvents = {};
+ if (gExpectedInstalls)
+ do_check_eq(gExpectedInstalls.length, 0);
+}
+
+/**
+ * A helper method to install an array of AddonInstall to completion and then
+ * call a provided callback.
+ *
+ * @param aInstalls
+ * The array of AddonInstalls to install
+ * @param aCallback
+ * The callback to call when all installs have finished
+ */
+function completeAllInstalls(aInstalls, aCallback) {
+ promiseCompleteAllInstalls(aInstalls).then(aCallback);
+}
+
+/**
+ * A helper method to install an array of files and call a callback after the
+ * installs are completed.
+ *
+ * @param aFiles
+ * The array of files to install
+ * @param aCallback
+ * The callback to call when all installs have finished
+ * @param aIgnoreIncompatible
+ * Optional parameter to ignore add-ons that are incompatible in
+ * aome way with the application
+ */
+function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
+ promiseInstallAllFiles(aFiles, aIgnoreIncompatible).then(aCallback);
+}
+
+const EXTENSIONS_DB = "extensions.json";
+var gExtensionsJSON = gProfD.clone();
+gExtensionsJSON.append(EXTENSIONS_DB);
+
+
+// By default use strict compatibility
+Services.prefs.setBoolPref("extensions.strictCompatibility", true);
+
+// By default, set min compatible versions to 0
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0");
+Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_PLATFORM_VERSION, "0");
+
+// Ensure signature checks are enabled by default
+Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
+
+
+// Copies blocklistFile (an nsIFile) to gProfD/blocklist.xml.
+function copyBlocklistToProfile(blocklistFile) {
+ var dest = gProfD.clone();
+ dest.append("blocklist.xml");
+ if (dest.exists())
+ dest.remove(false);
+ blocklistFile.copyTo(gProfD, "blocklist.xml");
+ dest.lastModifiedTime = Date.now();
+}
+
+// Throw a failure and attempt to abandon the test if it looks like it is going
+// to timeout
+function timeout() {
+ timer = null;
+ do_throw("Test ran longer than " + TIMEOUT_MS + "ms");
+
+ // Attempt to bail out of the test
+ do_test_finished();
+}
+
+var timer = AM_Cc["@mozilla.org/timer;1"].createInstance(AM_Ci.nsITimer);
+timer.init(timeout, TIMEOUT_MS, AM_Ci.nsITimer.TYPE_ONE_SHOT);
+
+// Make sure that a given path does not exist
+function pathShouldntExist(file) {
+ if (file.exists()) {
+ do_throw(`Test cleanup: path ${file.path} exists when it should not`);
+ }
+}
+
+do_register_cleanup(function addon_cleanup() {
+ if (timer)
+ timer.cancel();
+});
+
+/**
+ * Creates a new HttpServer for testing, and begins listening on the
+ * specified port. Automatically shuts down the server when the test
+ * unit ends.
+ *
+ * @param port
+ * The port to listen on. If omitted, listen on a random
+ * port. The latter is the preferred behavior.
+ *
+ * @return HttpServer
+ */
+function createHttpServer(port = -1) {
+ let server = new HttpServer();
+ server.start(port);
+
+ do_register_cleanup(() => {
+ return new Promise(resolve => {
+ server.stop(resolve);
+ });
+ });
+
+ return server;
+}
+
+/**
+ * Handler function that responds with the interpolated
+ * static file associated to the URL specified by request.path.
+ * This replaces the %PORT% entries in the file with the actual
+ * value of the running server's port (stored in gPort).
+ */
+function interpolateAndServeFile(request, response) {
+ try {
+ let file = gUrlToFileMap[request.path];
+ var data = "";
+ var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ var cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ fstream.init(file, -1, 0, 0);
+ cstream.init(fstream, "UTF-8", 0, 0);
+
+ let str = {};
+ let read = 0;
+ do {
+ // read as much as we can and put it in str.value
+ read = cstream.readString(0xffffffff, str);
+ data += str.value;
+ } while (read != 0);
+ data = data.replace(/%PORT%/g, gPort);
+
+ response.write(data);
+ } catch (e) {
+ do_throw(`Exception while serving interpolated file: ${e}\n${e.stack}`);
+ } finally {
+ cstream.close(); // this closes fstream as well
+ }
+}
+
+/**
+ * Sets up a path handler for the given URL and saves the
+ * corresponding file in the global url -> file map.
+ *
+ * @param url
+ * the actual URL
+ * @param file
+ * nsILocalFile representing a static file
+ */
+function mapUrlToFile(url, file, server) {
+ server.registerPathHandler(url, interpolateAndServeFile);
+ gUrlToFileMap[url] = file;
+}
+
+function mapFile(path, server) {
+ mapUrlToFile(path, do_get_file(path), server);
+}
+
+/**
+ * Take out the port number in an URL
+ *
+ * @param url
+ * String that represents an URL with a port number in it
+ */
+function remove_port(url) {
+ if (typeof url === "string")
+ return url.replace(/:\d+/, "");
+ return url;
+}
+// Wrap a function (typically a callback) to catch and report exceptions
+function do_exception_wrap(func) {
+ return function() {
+ try {
+ func.apply(null, arguments);
+ }
+ catch (e) {
+ do_report_unexpected_exception(e);
+ }
+ };
+}
+
+/**
+ * Change the schema version of the JSON extensions database
+ */
+function changeXPIDBVersion(aNewVersion, aMutator = undefined) {
+ let jData = loadJSON(gExtensionsJSON);
+ jData.schemaVersion = aNewVersion;
+ if (aMutator)
+ aMutator(jData);
+ saveJSON(jData, gExtensionsJSON);
+}
+
+/**
+ * Load a file into a string
+ */
+function loadFile(aFile) {
+ let data = "";
+ let fstream = Components.classes["@mozilla.org/network/file-input-stream;1"].
+ createInstance(Components.interfaces.nsIFileInputStream);
+ let cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"].
+ createInstance(Components.interfaces.nsIConverterInputStream);
+ fstream.init(aFile, -1, 0, 0);
+ cstream.init(fstream, "UTF-8", 0, 0);
+ let str = {};
+ let read = 0;
+ do {
+ read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
+ data += str.value;
+ } while (read != 0);
+ cstream.close();
+ return data;
+}
+
+/**
+ * Raw load of a JSON file
+ */
+function loadJSON(aFile) {
+ let data = loadFile(aFile);
+ do_print("Loaded JSON file " + aFile.path);
+ return (JSON.parse(data));
+}
+
+/**
+ * Raw save of a JSON blob to file
+ */
+function saveJSON(aData, aFile) {
+ do_print("Starting to save JSON file " + aFile.path);
+ let stream = FileUtils.openSafeFileOutputStream(aFile);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ // XXX pretty print the JSON while debugging
+ converter.writeString(JSON.stringify(aData, null, 2));
+ converter.flush();
+ // nsConverterOutputStream doesn't finish() safe output streams on close()
+ FileUtils.closeSafeFileOutputStream(stream);
+ converter.close();
+ do_print("Done saving JSON file " + aFile.path);
+}
+
+/**
+ * Create a callback function that calls do_execute_soon on an actual callback and arguments
+ */
+function callback_soon(aFunction) {
+ return function(...args) {
+ do_execute_soon(function() {
+ aFunction.apply(null, args);
+ }, aFunction.name ? "delayed callback " + aFunction.name : "delayed callback");
+ }
+}
+
+function writeProxyFileToDir(aDir, aAddon, aId) {
+ awaitPromise(promiseWriteProxyFileToDir(aDir, aAddon, aId));
+
+ let file = aDir.clone();
+ file.append(aId);
+ return file
+}
+
+function* serveSystemUpdate(xml, perform_update, testserver) {
+ testserver.registerPathHandler("/data/update.xml", (request, response) => {
+ response.write(xml);
+ });
+
+ try {
+ yield perform_update();
+ }
+ finally {
+ testserver.registerPathHandler("/data/update.xml", null);
+ }
+}
+
+// Runs an update check making it use the passed in xml string. Uses the direct
+// call to the update function so we get rejections on failure.
+function* installSystemAddons(xml, testserver) {
+ do_print("Triggering system add-on update check.");
+
+ yield serveSystemUpdate(xml, function*() {
+ let { XPIProvider } = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", {});
+ yield XPIProvider.updateSystemAddons();
+ }, testserver);
+}
+
+// Runs a full add-on update check which will in some cases do a system add-on
+// update check. Always succeeds.
+function* updateAllSystemAddons(xml, testserver) {
+ do_print("Triggering full add-on update check.");
+
+ yield serveSystemUpdate(xml, function() {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+ resolve();
+ }, "addons-background-update-complete", false);
+
+ // Trigger the background update timer handler
+ gInternalManager.notify(null);
+ });
+ }, testserver);
+}
+
+// Builds an update.xml file for an update check based on the data passed.
+function* buildSystemAddonUpdates(addons, root) {
+ let xml = `<?xml version="1.0" encoding="UTF-8"?>\n\n<updates>\n`;
+ if (addons) {
+ xml += ` <addons>\n`;
+ for (let addon of addons) {
+ xml += ` <addon id="${addon.id}" URL="${root + addon.path}" version="${addon.version}"`;
+ if (addon.size)
+ xml += ` size="${addon.size}"`;
+ if (addon.hashFunction)
+ xml += ` hashFunction="${addon.hashFunction}"`;
+ if (addon.hashValue)
+ xml += ` hashValue="${addon.hashValue}"`;
+ xml += `/>\n`;
+ }
+ xml += ` </addons>\n`;
+ }
+ xml += `</updates>\n`;
+
+ return xml;
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/head_unpack.js b/toolkit/mozapps/webextensions/test/xpcshell/head_unpack.js
new file mode 100644
index 000000000..6310bbc60
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/head_unpack.js
@@ -0,0 +1,3 @@
+/* globals Services, TEST_UNPACKED: true*/
+Services.prefs.setBoolPref("extensions.alwaysUnpack", true);
+TEST_UNPACKED = true;
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository.js b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository.js
new file mode 100644
index 000000000..dd0dc1981
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository.js
@@ -0,0 +1,625 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests AddonRepository.jsm
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+
+const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
+const PREF_GETADDONS_BROWSERECOMMENDED = "extensions.getAddons.recommended.browseURL";
+const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
+const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+const PORT = gServer.identity.primaryPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+
+gPort = PORT;
+
+// Path to source URI of installed add-on
+const INSTALL_URL1 = "/addons/test_AddonRepository_1.xpi";
+// Path to source URI of installing add-on
+const INSTALL_URL2 = "/addons/test_AddonRepository_2.xpi";
+// Path to source URI of non-active add-on (state = STATE_AVAILABLE)
+const INSTALL_URL3 = "/addons/test_AddonRepository_3.xpi";
+
+// Properties of an individual add-on that should be checked
+// Note: name is checked separately
+var ADDON_PROPERTIES = ["id", "type", "version", "creator", "developers",
+ "description", "fullDescription", "developerComments",
+ "eula", "iconURL", "icons", "screenshots", "homepageURL",
+ "supportURL", "contributionURL", "contributionAmount",
+ "averageRating", "reviewCount", "reviewURL",
+ "totalDownloads", "weeklyDownloads", "dailyUsers",
+ "sourceURI", "repositoryStatus", "size", "updateDate",
+ "purchaseURL", "purchaseAmount", "purchaseDisplayAmount",
+ "compatibilityOverrides"];
+
+// Results of getAddonsByIDs
+var GET_RESULTS = [{
+ id: "test1@tests.mozilla.org",
+ type: "extension",
+ version: "1.1",
+ creator: {
+ name: "Test Creator 1",
+ url: BASE_URL + "/creator1.html"
+ },
+ developers: [{
+ name: "Test Developer 1",
+ url: BASE_URL + "/developer1.html"
+ }],
+ description: "Test Summary 1",
+ fullDescription: "Test Description 1",
+ developerComments: "Test Developer Comments 1",
+ eula: "Test EULA 1",
+ iconURL: BASE_URL + "/icon1.png",
+ icons: { "32": BASE_URL + "/icon1.png" },
+ screenshots: [{
+ url: BASE_URL + "/full1-1.png",
+ width: 400,
+ height: 300,
+ thumbnailURL: BASE_URL + "/thumbnail1-1.png",
+ thumbnailWidth: 200,
+ thumbnailHeight: 150,
+ caption: "Caption 1 - 1"
+ }, {
+ url: BASE_URL + "/full2-1.png",
+ thumbnailURL: BASE_URL + "/thumbnail2-1.png",
+ caption: "Caption 2 - 1"
+ }],
+ homepageURL: BASE_URL + "/learnmore1.html",
+ learnmoreURL: BASE_URL + "/learnmore1.html",
+ supportURL: BASE_URL + "/support1.html",
+ contributionURL: BASE_URL + "/meetDevelopers1.html",
+ contributionAmount: "$11.11",
+ averageRating: 4,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/review1.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3333,
+ dailyUsers: 4444,
+ sourceURI: BASE_URL + INSTALL_URL2,
+ repositoryStatus: 8,
+ size: 5555,
+ updateDate: new Date(1265033045000),
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: "test_AddonRepository_1@tests.mozilla.org",
+ type: "theme",
+ version: "1.4",
+ repositoryStatus: 9999,
+ icons: {}
+}];
+
+// Results of retrieveRecommendedAddons and searchAddons
+var SEARCH_RESULTS = [{
+ id: "test1@tests.mozilla.org",
+ type: "extension",
+ version: "1.1",
+ creator: {
+ name: "Test Creator 1",
+ url: BASE_URL + "/creator1.html"
+ },
+ repositoryStatus: 8,
+ sourceURI: BASE_URL + "/test1.xpi",
+ icons: {}
+}, {
+ id: "test2@tests.mozilla.org",
+ type: "extension",
+ version: "1.2",
+ creator: {
+ name: "Test Creator 2",
+ url: BASE_URL + "/creator2.html"
+ },
+ developers: [{
+ name: "Test Developer 2",
+ url: BASE_URL + "/developer2.html"
+ }],
+ description: "Test Summary 2\n\nparagraph",
+ fullDescription: "Test Description 2\nnewline",
+ developerComments: "Test Developer\nComments 2",
+ eula: "Test EULA 2",
+ iconURL: BASE_URL + "/icon2-32.png",
+ icons: {
+ "32": BASE_URL + "/icon2-32.png",
+ "48": BASE_URL + "/icon2-48.png",
+ "64": BASE_URL + "/icon2-64.png"
+ },
+ screenshots: [{
+ url: BASE_URL + "/full1-2.png",
+ thumbnailURL: BASE_URL + "/thumbnail1-2.png"
+ }, {
+ url: BASE_URL + "/full2-2.png",
+ thumbnailURL: BASE_URL + "/thumbnail2-2.png",
+ caption: "Caption 2"
+ }],
+ homepageURL: BASE_URL + "/learnmore2.html",
+ supportURL: BASE_URL + "/support2.html",
+ learnmoreURL: BASE_URL + "/learnmore2.html",
+ contributionURL: BASE_URL + "/meetDevelopers2.html",
+ contributionAmount: null,
+ repositoryStatus: 4,
+ sourceURI: BASE_URL + "/test2.xpi"
+}, {
+ id: "test3@tests.mozilla.org",
+ type: "theme",
+ version: "1.3",
+ creator: {
+ name: "Test Creator 3",
+ url: BASE_URL + "/creator3.html"
+ },
+ developers: [{
+ name: "First Test Developer 3",
+ url: BASE_URL + "/developer1-3.html"
+ }, {
+ name: "Second Test Developer 3",
+ url: BASE_URL + "/developer2-3.html"
+ }],
+ description: "Test Summary 3",
+ fullDescription: "Test Description 3\n\n List item 1\n List item 2",
+ developerComments: "Test Developer Comments 3",
+ eula: "Test EULA 3",
+ iconURL: BASE_URL + "/icon3.png",
+ icons: { "32": BASE_URL + "/icon3.png" },
+ screenshots: [{
+ url: BASE_URL + "/full1-3.png",
+ thumbnailURL: BASE_URL + "/thumbnail1-3.png",
+ caption: "Caption 1 - 3"
+ }, {
+ url: BASE_URL + "/full2-3.png",
+ caption: "Caption 2 - 3"
+ }, {
+ url: BASE_URL + "/full3-3.png",
+ thumbnailURL: BASE_URL + "/thumbnail3-3.png",
+ caption: "Caption 3 - 3"
+ }],
+ homepageURL: BASE_URL + "/homepage3.html",
+ supportURL: BASE_URL + "/support3.html",
+ learnmoreURL: BASE_URL + "/learnmore3.html",
+ contributionURL: BASE_URL + "/meetDevelopers3.html",
+ contributionAmount: "$11.11",
+ averageRating: 2,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/review3.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3333,
+ dailyUsers: 4444,
+ sourceURI: BASE_URL + "/test3.xpi",
+ repositoryStatus: 8,
+ size: 5555,
+ updateDate: new Date(1265033045000),
+
+}, {
+ id: "purchase1@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ purchaseURL: "http://localhost:" + PORT + "/purchaseURL1",
+ purchaseAmount: 5,
+ purchaseDisplayAmount: "$5",
+ icons: {}
+}, {
+ id: "purchase2@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ purchaseURL: "http://localhost:" + PORT + "/purchaseURL2",
+ purchaseAmount: 10,
+ purchaseDisplayAmount: "$10",
+ icons: {}
+}, {
+ id: "test-lastPassing@tests.mozilla.org",
+ type: "extension",
+ version: "2.0",
+ creator: {
+ name: "Test Creator - Last Passing",
+ url: BASE_URL + "/creatorLastPassing.html"
+ },
+ averageRating: 5,
+ repositoryStatus: 4,
+ sourceURI: BASE_URL + "/addons/test_AddonRepository_3.xpi",
+ icons: {}
+}];
+
+const TOTAL_RESULTS = 1111;
+const MAX_RESULTS = SEARCH_RESULTS.length;
+
+// Used to differentiate between testing that a search success
+// or a search failure for retrieveRecommendedAddons and searchAddons
+const FAILED_MAX_RESULTS = 9999;
+
+// Values for testing AddonRepository.getAddonsByIDs()
+var GET_TEST = {
+ preference: PREF_GETADDONS_BYIDS,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%IDS%",
+ failedIDs: ["test1@tests.mozilla.org"],
+ failedURL: "/XPCShell/1/1.5/1.5/test1%40tests.mozilla.org",
+ successfulIDs: ["test1@tests.mozilla.org",
+ "{00000000-1111-2222-3333-444444444444}",
+ "test_AddonRepository_1@tests.mozilla.org"],
+ successfulURL: "/XPCShell/1/1.5/1.5/test1%40tests.mozilla.org," +
+ "%7B00000000-1111-2222-3333-444444444444%7D," +
+ "test_AddonRepository_1%40tests.mozilla.org"
+};
+
+// Values for testing AddonRepository.retrieveRecommendedAddons()
+var RECOMMENDED_TEST = {
+ preference: PREF_GETADDONS_GETRECOMMENDED,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%MAX_RESULTS%",
+ failedURL: "/XPCShell/1/1.5/1.5/" + (2 * FAILED_MAX_RESULTS),
+ successfulURL: "/XPCShell/1/1.5/1.5/" + (2 * MAX_RESULTS)
+};
+
+// Values for testing AddonRepository.searchAddons()
+var SEARCH_TEST = {
+ searchTerms: "odd=search:with&weird\"characters",
+ preference: PREF_GETADDONS_GETSEARCHRESULTS,
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%/%API_VERSION%/" +
+ "%API_VERSION%/%MAX_RESULTS%/%TERMS%",
+ failedURL: "/XPCShell/1/1.5/1.5/" + (2 * FAILED_MAX_RESULTS) +
+ "/odd%3Dsearch%3Awith%26weird%22characters",
+ successfulURL: "/XPCShell/1/1.5/1.5/" + (2 * MAX_RESULTS) +
+ "/odd%3Dsearch%3Awith%26weird%22characters"
+};
+
+// Test that actual results and expected results are equal
+function check_results(aActualAddons, aExpectedAddons, aAddonCount, aInstallNull) {
+ do_check_false(AddonRepository.isSearching);
+
+ do_check_eq(aActualAddons.length, aAddonCount);
+ do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);
+
+ // Additional tests
+ aActualAddons.forEach(function check_each_addon(aActualAddon) {
+ // Separately check name so better messages are output when test fails
+ if (aActualAddon.name == "FAIL")
+ do_throw(aActualAddon.id + " - " + aActualAddon.description);
+ if (aActualAddon.name != "PASS")
+ do_throw(aActualAddon.id + " - " + "invalid add-on name " + aActualAddon.name);
+
+ do_check_eq(aActualAddon.install == null, !!aInstallNull || !aActualAddon.sourceURI);
+
+ // Check that sourceURI property consistent within actual addon
+ if (aActualAddon.install)
+ do_check_eq(aActualAddon.install.sourceURI.spec, aActualAddon.sourceURI.spec);
+ });
+}
+
+// Complete a search, also testing cancelSearch() and isSearching
+function complete_search(aSearch, aSearchCallback) {
+ var failCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_throw("failCallback.searchSucceeded should not be called");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_throw("failCallback.searchFailed should not be called");
+ end_test();
+ }
+ };
+
+ var callbackCalled = false;
+ var testCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_throw("testCallback.searchSucceeded should not be called");
+ end_test();
+ },
+
+ searchFailed: function() {
+ callbackCalled = true;
+ }
+ };
+
+ // Should fail because cancelled it immediately
+ aSearch(failCallback);
+ do_check_true(AddonRepository.isSearching);
+ AddonRepository.cancelSearch();
+ do_check_false(AddonRepository.isSearching);
+
+ aSearch(aSearchCallback);
+ do_check_true(AddonRepository.isSearching);
+
+ // searchFailed should be called immediately because already searching
+ aSearch(testCallback);
+ do_check_true(callbackCalled);
+ do_check_true(AddonRepository.isSearching);
+}
+
+
+function run_test() {
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ // Install an add-on so can check that it isn't returned in the results
+ installAllFiles([do_get_addon("test_AddonRepository_1")], function addon_1_install_callback() {
+ restartManager();
+
+ // Register other add-on XPI files
+ gServer.registerFile(INSTALL_URL2,
+ do_get_addon("test_AddonRepository_2"));
+ gServer.registerFile(INSTALL_URL3,
+ do_get_addon("test_AddonRepository_3"));
+
+ // Register files used to test search failure
+ mapUrlToFile(GET_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+ mapUrlToFile(RECOMMENDED_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+ mapUrlToFile(SEARCH_TEST.failedURL,
+ do_get_file("data/test_AddonRepository_failed.xml"),
+ gServer);
+
+ // Register files used to test search success
+ mapUrlToFile(GET_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository_getAddonsByIDs.xml"),
+ gServer);
+ mapUrlToFile(RECOMMENDED_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository.xml"),
+ gServer);
+ mapUrlToFile(SEARCH_TEST.successfulURL,
+ do_get_file("data/test_AddonRepository.xml"),
+ gServer);
+
+ // Create an active AddonInstall so can check that it isn't returned in the results
+ AddonManager.getInstallForURL(BASE_URL + INSTALL_URL2, function addon_2_get(aInstall) {
+ try {
+ aInstall.install();
+ }
+ catch (e) {
+ do_print("Failed to install add-on " + aInstall.sourceURI.spec);
+ do_report_unexpected_exception(e);
+ }
+
+ // Create a non-active AddonInstall so can check that it is returned in the results
+ AddonManager.getInstallForURL(BASE_URL + INSTALL_URL3,
+ run_test_1, "application/x-xpinstall");
+ }, "application/x-xpinstall");
+ });
+}
+
+function end_test() {
+ let testDir = gProfD.clone();
+ testDir.append("extensions");
+ testDir.append("staged");
+ gServer.stop(function() {
+ function loop() {
+ if (!testDir.exists()) {
+ do_print("Staged directory has been cleaned up");
+ do_test_finished();
+ }
+ do_print("Waiting 1 second until cleanup is complete");
+ do_timeout(1000, loop);
+ }
+ loop();
+ });
+}
+
+// Tests homepageURL, getRecommendedURL() and getSearchURL()
+function run_test_1() {
+ function check_urls(aPreference, aGetURL, aTests) {
+ aTests.forEach(function(aTest) {
+ Services.prefs.setCharPref(aPreference, aTest.preferenceValue);
+ do_check_eq(aGetURL(aTest), aTest.expectedURL);
+ });
+ }
+
+ var urlTests = [{
+ preferenceValue: BASE_URL,
+ expectedURL: BASE_URL
+ }, {
+ preferenceValue: BASE_URL + "/%OS%/%VERSION%",
+ expectedURL: BASE_URL + "/XPCShell/1"
+ }];
+
+ // Extra tests for AddonRepository.getSearchURL();
+ var searchURLTests = [{
+ searchTerms: "test",
+ preferenceValue: BASE_URL + "/search?q=%TERMS%",
+ expectedURL: BASE_URL + "/search?q=test"
+ }, {
+ searchTerms: "test search",
+ preferenceValue: BASE_URL + "/%TERMS%",
+ expectedURL: BASE_URL + "/test%20search"
+ }, {
+ searchTerms: "odd=search:with&weird\"characters",
+ preferenceValue: BASE_URL + "/%TERMS%",
+ expectedURL: BASE_URL + "/odd%3Dsearch%3Awith%26weird%22characters"
+ }];
+
+ // Setup tests for homepageURL, getRecommendedURL() and getSearchURL()
+ var tests = [{
+ initiallyUndefined: true,
+ preference: PREF_GETADDONS_BROWSEADDONS,
+ urlTests: urlTests,
+ getURL: () => AddonRepository.homepageURL
+ }, {
+ initiallyUndefined: true,
+ preference: PREF_GETADDONS_BROWSERECOMMENDED,
+ urlTests: urlTests,
+ getURL: () => AddonRepository.getRecommendedURL()
+ }, {
+ initiallyUndefined: false,
+ preference: PREF_GETADDONS_BROWSESEARCHRESULTS,
+ urlTests: urlTests.concat(searchURLTests),
+ getURL: function getSearchURL(aTest) {
+ var searchTerms = aTest && aTest.searchTerms ? aTest.searchTerms
+ : "unused terms";
+ return AddonRepository.getSearchURL(searchTerms);
+ }
+ }];
+
+ tests.forEach(function url_test(aTest) {
+ if (aTest.initiallyUndefined) {
+ // Preference is not defined by default
+ do_check_eq(Services.prefs.getPrefType(aTest.preference),
+ Services.prefs.PREF_INVALID);
+ do_check_eq(aTest.getURL(), DEFAULT_URL);
+ }
+
+ check_urls(aTest.preference, aTest.getURL, aTest.urlTests);
+ });
+
+ run_test_getAddonsByID_fails();
+}
+
+// Tests failure of AddonRepository.getAddonsByIDs()
+function run_test_getAddonsByID_fails() {
+ Services.prefs.setCharPref(GET_TEST.preference, GET_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("searchAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_getAddonsByID_succeeds();
+ }
+ };
+
+ complete_search(function complete_search_fail_callback(aCallback) {
+ AddonRepository.getAddonsByIDs(GET_TEST.failedIDs, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.getAddonsByIDs()
+function run_test_getAddonsByID_succeeds() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, -1);
+ check_results(aAddonsList, GET_RESULTS, aAddonCount, true);
+ run_test_retrieveRecommended_fails();
+ },
+
+ searchFailed: function() {
+ do_throw("searchAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function complete_search_succeed_callback(aCallback) {
+ AddonRepository.getAddonsByIDs(GET_TEST.successfulIDs, aCallback);
+ }, callback);
+}
+
+// Tests failure of AddonRepository.retrieveRecommendedAddons()
+function run_test_retrieveRecommended_fails() {
+ Services.prefs.setCharPref(RECOMMENDED_TEST.preference,
+ RECOMMENDED_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("retrieveRecommendedAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_retrieveRecommended_succeed();
+ }
+ };
+
+ complete_search(function retrieveRecommended_failing_callback(aCallback) {
+ AddonRepository.retrieveRecommendedAddons(FAILED_MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.retrieveRecommendedAddons()
+function run_test_retrieveRecommended_succeed() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, -1);
+ check_results(aAddonsList, SEARCH_RESULTS, aAddonCount);
+ run_test_searchAddons_fails();
+ },
+
+ searchFailed: function() {
+ do_throw("retrieveRecommendedAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function retrieveRecommended_succeed_callback(aCallback) {
+ AddonRepository.retrieveRecommendedAddons(MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests failure of AddonRepository.searchAddons()
+function run_test_searchAddons_fails() {
+ Services.prefs.setCharPref(SEARCH_TEST.preference, SEARCH_TEST.preferenceValue);
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_throw("searchAddons should not have succeeded");
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_check_false(AddonRepository.isSearching);
+ run_test_searchAddons_succeeds();
+ }
+ };
+
+ complete_search(function(aCallback) {
+ var searchTerms = SEARCH_TEST.searchTerms;
+ AddonRepository.searchAddons(searchTerms, FAILED_MAX_RESULTS, aCallback);
+ }, callback);
+}
+
+// Tests success of AddonRepository.searchAddons()
+function run_test_searchAddons_succeeds() {
+ var callback = {
+ searchSucceeded: function(aAddonsList, aAddonCount, aTotalResults) {
+ do_check_eq(aTotalResults, TOTAL_RESULTS);
+ check_results(aAddonsList, SEARCH_RESULTS, aAddonCount);
+ end_test();
+ },
+
+ searchFailed: function() {
+ do_throw("searchAddons should not have failed");
+ end_test();
+ }
+ };
+
+ complete_search(function(aCallback) {
+ var searchTerms = SEARCH_TEST.searchTerms;
+ AddonRepository.searchAddons(searchTerms, MAX_RESULTS, aCallback);
+ }, callback);
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_cache.js b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_cache.js
new file mode 100644
index 000000000..203235940
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_cache.js
@@ -0,0 +1,704 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests caching in AddonRepository.jsm
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+var gServer;
+
+const PORT = 4444;
+const BASE_URL = "http://localhost:" + PORT;
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
+const GETADDONS_RESULTS = BASE_URL + "/data/test_AddonRepository_cache.xml";
+const GETADDONS_EMPTY = BASE_URL + "/data/test_AddonRepository_empty.xml";
+const GETADDONS_FAILED = BASE_URL + "/data/test_AddonRepository_failed.xml";
+
+const FILE_DATABASE = "addons.json";
+const ADDON_NAMES = ["test_AddonRepository_1",
+ "test_AddonRepository_2",
+ "test_AddonRepository_3"];
+const ADDON_IDS = ADDON_NAMES.map(aName => aName + "@tests.mozilla.org");
+const ADDON_FILES = ADDON_NAMES.map(do_get_addon);
+
+const PREF_ADDON0_CACHE_ENABLED = "extensions." + ADDON_IDS[0] + ".getAddons.cache.enabled";
+const PREF_ADDON1_CACHE_ENABLED = "extensions." + ADDON_IDS[1] + ".getAddons.cache.enabled";
+
+// Properties of an individual add-on that should be checked
+// Note: size and updateDate are checked separately
+const ADDON_PROPERTIES = ["id", "type", "name", "version", "creator",
+ "developers", "translators", "contributors",
+ "description", "fullDescription",
+ "developerComments", "eula", "iconURL", "icons",
+ "screenshots", "homepageURL", "supportURL",
+ "optionsURL", "aboutURL", "contributionURL",
+ "contributionAmount", "averageRating", "reviewCount",
+ "reviewURL", "totalDownloads", "weeklyDownloads",
+ "dailyUsers", "sourceURI", "repositoryStatus",
+ "compatibilityOverrides"];
+
+// The size and updateDate properties are annoying to test for XPI add-ons.
+// However, since we only care about whether the repository value vs. the
+// XPI value is used, we can just test if the property value matches
+// the repository value
+const REPOSITORY_SIZE = 9;
+const REPOSITORY_UPDATEDATE = 9;
+
+// Get the URI of a subfile locating directly in the folder of
+// the add-on corresponding to the specified id
+function get_subfile_uri(aId, aFilename) {
+ let file = gProfD.clone();
+ file.append("extensions");
+ return do_get_addon_root_uri(file, aId) + aFilename;
+}
+
+
+// Expected repository add-ons
+const REPOSITORY_ADDONS = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "Repo Add-on 1",
+ version: "2.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 1 - First Developer",
+ url: BASE_URL + "/repo/1/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 1 - Second Developer",
+ url: BASE_URL + "/repo/1/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 1 - Description\nSecond line",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/repo/1/icon.png",
+ icons: { "32": BASE_URL + "/repo/1/icon.png" },
+ homepageURL: BASE_URL + "/repo/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: BASE_URL + "/repo/1/install.xpi",
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "Repo Add-on 2",
+ version: "2.2",
+ creator: {
+ name: "Repo Add-on 2 - Creator",
+ url: BASE_URL + "/repo/2/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 2 - First Developer",
+ url: BASE_URL + "/repo/2/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 2 - Second Developer",
+ url: BASE_URL + "/repo/2/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 2 - Description",
+ fullDescription: "Repo Add-on 2 - Full Description",
+ developerComments: "Repo Add-on 2 - Developer Comments",
+ eula: "Repo Add-on 2 - EULA",
+ iconURL: BASE_URL + "/repo/2/icon.png",
+ icons: { "32": BASE_URL + "/repo/2/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/2/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/firstThumbnail.png",
+ caption: "Repo Add-on 2 - First Caption"
+ }, {
+ url: BASE_URL + "/repo/2/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/secondThumbnail.png",
+ caption: "Repo Add-on 2 - Second Caption"
+ }],
+ homepageURL: BASE_URL + "/repo/2/homepage.html",
+ supportURL: BASE_URL + "/repo/2/support.html",
+ contributionURL: BASE_URL + "/repo/2/meetDevelopers.html",
+ contributionAmount: null,
+ averageRating: 2,
+ reviewCount: 1112,
+ reviewURL: BASE_URL + "/repo/2/review.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3332,
+ dailyUsers: 4442,
+ sourceURI: BASE_URL + "/repo/2/install.xpi",
+ repositoryStatus: 9
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "Repo Add-on 3",
+ version: "2.3",
+ iconURL: BASE_URL + "/repo/3/icon.png",
+ icons: { "32": BASE_URL + "/repo/3/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/3/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/firstThumbnail.png",
+ caption: "Repo Add-on 3 - First Caption"
+ }, {
+ url: BASE_URL + "/repo/3/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/secondThumbnail.png",
+ caption: "Repo Add-on 3 - Second Caption"
+ }]
+}];
+
+
+// Expected add-ons when not using cache
+const WITHOUT_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: { name: "XPI Add-on 1 - Creator" },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ icons: {}
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+
+// Expected add-ons when using cache
+const WITH_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec,
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ creator: {
+ name: "Repo Add-on 2 - Creator",
+ url: BASE_URL + "/repo/2/creator.html"
+ },
+ developers: [{
+ name: "Repo Add-on 2 - First Developer",
+ url: BASE_URL + "/repo/2/firstDeveloper.html"
+ }, {
+ name: "Repo Add-on 2 - Second Developer",
+ url: BASE_URL + "/repo/2/secondDeveloper.html"
+ }],
+ description: "Repo Add-on 2 - Description",
+ fullDescription: "Repo Add-on 2 - Full Description",
+ developerComments: "Repo Add-on 2 - Developer Comments",
+ eula: "Repo Add-on 2 - EULA",
+ iconURL: BASE_URL + "/repo/2/icon.png",
+ icons: { "32": BASE_URL + "/repo/2/icon.png" },
+ screenshots: [{
+ url: BASE_URL + "/repo/2/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/firstThumbnail.png",
+ caption: "Repo Add-on 2 - First Caption"
+ }, {
+ url: BASE_URL + "/repo/2/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/2/secondThumbnail.png",
+ caption: "Repo Add-on 2 - Second Caption"
+ }],
+ homepageURL: BASE_URL + "/repo/2/homepage.html",
+ supportURL: BASE_URL + "/repo/2/support.html",
+ contributionURL: BASE_URL + "/repo/2/meetDevelopers.html",
+ contributionAmount: null,
+ averageRating: 2,
+ reviewCount: 1112,
+ reviewURL: BASE_URL + "/repo/2/review.html",
+ totalDownloads: 2222,
+ weeklyDownloads: 3332,
+ dailyUsers: 4442,
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ repositoryStatus: 9
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{
+ url: BASE_URL + "/repo/3/firstFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/firstThumbnail.png",
+ caption: "Repo Add-on 3 - First Caption"
+ }, {
+ url: BASE_URL + "/repo/3/secondFull.png",
+ thumbnailURL: BASE_URL + "/repo/3/secondThumbnail.png",
+ caption: "Repo Add-on 3 - Second Caption"
+ }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+// Expected add-ons when using cache
+const WITH_EXTENSION_CACHE = [{
+ id: ADDON_IDS[0],
+ type: "extension",
+ name: "XPI Add-on 1",
+ version: "1.1",
+ creator: {
+ name: "Repo Add-on 1 - Creator",
+ url: BASE_URL + "/repo/1/creator.html"
+ },
+ developers: [{ name: "XPI Add-on 1 - First Developer" },
+ { name: "XPI Add-on 1 - Second Developer" }],
+ translators: [{ name: "XPI Add-on 1 - First Translator" },
+ { name: "XPI Add-on 1 - Second Translator" }],
+ contributors: [{ name: "XPI Add-on 1 - First Contributor" },
+ { name: "XPI Add-on 1 - Second Contributor" }],
+ description: "XPI Add-on 1 - Description",
+ fullDescription: "Repo Add-on 1 - Full Description & some extra",
+ developerComments: "Repo Add-on 1\nDeveloper Comments",
+ eula: "Repo Add-on 1 - EULA",
+ iconURL: BASE_URL + "/xpi/1/icon.png",
+ icons: { "32": BASE_URL + "/xpi/1/icon.png" },
+ homepageURL: BASE_URL + "/xpi/1/homepage.html",
+ supportURL: BASE_URL + "/repo/1/support.html",
+ optionsURL: BASE_URL + "/xpi/1/options.html",
+ aboutURL: BASE_URL + "/xpi/1/about.html",
+ contributionURL: BASE_URL + "/repo/1/meetDevelopers.html",
+ contributionAmount: "$11.11",
+ averageRating: 1,
+ reviewCount: 1111,
+ reviewURL: BASE_URL + "/repo/1/review.html",
+ totalDownloads: 2221,
+ weeklyDownloads: 3331,
+ dailyUsers: 4441,
+ sourceURI: NetUtil.newURI(ADDON_FILES[0]).spec,
+ repositoryStatus: 4,
+ compatibilityOverrides: [{
+ type: "incompatible",
+ minVersion: 0.1,
+ maxVersion: 0.2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 3.0,
+ appMaxVersion: 4.0
+ }, {
+ type: "incompatible",
+ minVersion: 0.2,
+ maxVersion: 0.3,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 5.0,
+ appMaxVersion: 6.0
+ }]
+}, {
+ id: ADDON_IDS[1],
+ type: "theme",
+ name: "XPI Add-on 2",
+ version: "1.2",
+ sourceURI: NetUtil.newURI(ADDON_FILES[1]).spec,
+ icons: {}
+}, {
+ id: ADDON_IDS[2],
+ type: "theme",
+ name: "XPI Add-on 3",
+ version: "1.3",
+ get iconURL () {
+ return get_subfile_uri(ADDON_IDS[2], "icon.png");
+ },
+ get icons () {
+ return { "32": get_subfile_uri(ADDON_IDS[2], "icon.png") };
+ },
+ screenshots: [{ get url () { return get_subfile_uri(ADDON_IDS[2], "preview.png"); } }],
+ sourceURI: NetUtil.newURI(ADDON_FILES[2]).spec
+}];
+
+var gDBFile = gProfD.clone();
+gDBFile.append(FILE_DATABASE);
+
+/*
+ * Check the actual add-on results against the expected add-on results
+ *
+ * @param aActualAddons
+ * The array of actual add-ons to check
+ * @param aExpectedAddons
+ * The array of expected add-ons to check against
+ * @param aFromRepository
+ * An optional boolean representing if the add-ons are from
+ * the repository
+ */
+function check_results(aActualAddons, aExpectedAddons, aFromRepository) {
+ aFromRepository = !!aFromRepository;
+
+ do_check_addons(aActualAddons, aExpectedAddons, ADDON_PROPERTIES);
+
+ // Separately test size and updateDate (they should only be equal to the
+ // REPOSITORY values if they are from the repository)
+ aActualAddons.forEach(function(aActualAddon) {
+ if (aActualAddon.size)
+ do_check_eq(aActualAddon.size === REPOSITORY_SIZE, aFromRepository);
+
+ if (aActualAddon.updateDate) {
+ let time = aActualAddon.updateDate.getTime();
+ do_check_eq(time === 1000 * REPOSITORY_UPDATEDATE, aFromRepository);
+ }
+ });
+}
+
+/*
+ * Check the add-ons in the cache. This function also tests
+ * AddonRepository.getCachedAddonByID()
+ *
+ * @param aExpectedToFind
+ * An array of booleans representing which REPOSITORY_ADDONS are
+ * expected to be found in the cache
+ * @param aExpectedImmediately
+ * A boolean representing if results from the cache are expected
+ * immediately. Results are not immediate if the cache has not been
+ * initialized yet.
+ * @return Promise{null}
+ * Resolves once the checks are complete
+ */
+function check_cache(aExpectedToFind, aExpectedImmediately) {
+ do_check_eq(aExpectedToFind.length, REPOSITORY_ADDONS.length);
+
+ let lookups = [];
+
+ for (let i = 0 ; i < REPOSITORY_ADDONS.length ; i++) {
+ lookups.push(new Promise((resolve, reject) => {
+ let immediatelyFound = true;
+ let expected = aExpectedToFind[i] ? REPOSITORY_ADDONS[i] : null;
+ // can't Promise-wrap this because we're also testing whether the callback is
+ // sync or async
+ AddonRepository.getCachedAddonByID(REPOSITORY_ADDONS[i].id, function(aAddon) {
+ do_check_eq(immediatelyFound, aExpectedImmediately);
+ if (expected == null)
+ do_check_eq(aAddon, null);
+ else
+ check_results([aAddon], [expected], true);
+ resolve();
+ });
+ immediatelyFound = false;
+ }));
+ }
+ return Promise.all(lookups);
+}
+
+/*
+ * Task to check an initialized cache by checking the cache, then restarting the
+ * manager, and checking the cache. This checks that the cache is consistent
+ * across manager restarts.
+ *
+ * @param aExpectedToFind
+ * An array of booleans representing which REPOSITORY_ADDONS are
+ * expected to be found in the cache
+ */
+function* check_initialized_cache(aExpectedToFind) {
+ yield check_cache(aExpectedToFind, true);
+ yield promiseRestartManager();
+
+ // If cache is disabled, then expect results immediately
+ let cacheEnabled = Services.prefs.getBoolPref(PREF_GETADDONS_CACHE_ENABLED);
+ yield check_cache(aExpectedToFind, !cacheEnabled);
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* setup() {
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ // Install XPI add-ons
+ yield promiseInstallAllFiles(ADDON_FILES);
+ yield promiseRestartManager();
+
+ gServer = createHttpServer(PORT);
+ gServer.registerDirectory("/data/", do_get_file("data"));
+});
+
+// Tests AddonRepository.cacheEnabled
+add_task(function* run_test_1() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ do_check_false(AddonRepository.cacheEnabled);
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ do_check_true(AddonRepository.cacheEnabled);
+});
+
+// Tests that the cache and database begin as empty
+add_task(function* run_test_2() {
+ do_check_false(gDBFile.exists());
+ yield check_cache([false, false, false], false);
+ yield AddonRepository.flush();
+});
+
+// Tests repopulateCache when the search fails
+add_task(function* run_test_3() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests repopulateCache when search returns no results
+add_task(function* run_test_4() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests repopulateCache when search returns results
+add_task(function* run_test_5() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ yield AddonRepository.repopulateCache();
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests repopulateCache when caching is disabled for a single add-on
+add_task(function* run_test_5_1() {
+ Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, false);
+
+ yield AddonRepository.repopulateCache();
+
+ // Reset pref for next test
+ Services.prefs.setBoolPref(PREF_ADDON0_CACHE_ENABLED, true);
+
+ yield check_initialized_cache([false, true, true]);
+});
+
+// Tests repopulateCache when caching is disabled
+add_task(function* run_test_6() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+
+ yield AddonRepository.repopulateCache();
+ // Database should have been deleted
+ do_check_false(gDBFile.exists());
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ yield check_cache([false, false, false], false);
+ yield AddonRepository.flush();
+});
+
+// Tests cacheAddons when the search fails
+add_task(function* run_test_7() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_FAILED);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests cacheAddons when the search returns no results
+add_task(function* run_test_8() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_EMPTY);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([false, false, false]);
+});
+
+// Tests cacheAddons for a single add-on when search returns results
+add_task(function* run_test_9() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons([ADDON_IDS[0]], resolve));
+ yield check_initialized_cache([true, false, false]);
+});
+
+// Tests cacheAddons when caching is disabled for a single add-on
+add_task(function* run_test_9_1() {
+ Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, false);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+
+ // Reset pref for next test
+ Services.prefs.setBoolPref(PREF_ADDON1_CACHE_ENABLED, true);
+
+ yield check_initialized_cache([true, false, true]);
+});
+
+// Tests cacheAddons for multiple add-ons, some already in the cache,
+add_task(function* run_test_10() {
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests cacheAddons when caching is disabled
+add_task(function* run_test_11() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+
+ yield new Promise((resolve, reject) =>
+ AddonRepository.cacheAddons(ADDON_IDS, resolve));
+ do_check_true(gDBFile.exists());
+
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+ yield check_initialized_cache([true, true, true]);
+});
+
+// Tests that XPI add-ons do not use any of the repository properties if
+// caching is disabled, even if there are repository properties available
+add_task(function* run_test_12() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, false);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that a background update with caching disabled deletes the add-ons
+// database, and that XPI add-ons still do not use any of repository properties
+add_task(function* run_test_13() {
+ do_check_true(gDBFile.exists());
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_EMPTY);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ // Database should have been deleted
+ do_check_false(gDBFile.exists());
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that the XPI add-ons have the correct properties if caching is
+// enabled but has no information
+add_task(function* run_test_14() {
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ yield AddonRepository.flush();
+ do_check_true(gDBFile.exists());
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITHOUT_CACHE);
+});
+
+// Tests that the XPI add-ons correctly use the repository properties when
+// caching is enabled and the repository information is available
+add_task(function* run_test_15() {
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, GETADDONS_RESULTS);
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_CACHE);
+});
+
+// Tests that restarting the manager does not change the checked properties
+// on the XPI add-ons (repository properties still exist and are still properly
+// used)
+add_task(function* run_test_16() {
+ yield promiseRestartManager();
+
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_CACHE);
+});
+
+// Tests that setting a list of types to cache works
+add_task(function* run_test_17() {
+ Services.prefs.setCharPref(PREF_GETADDONS_CACHE_TYPES, "foo,bar,extension,baz");
+
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ let aAddons = yield promiseAddonsByIDs(ADDON_IDS);
+ check_results(aAddons, WITH_EXTENSION_CACHE);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_compatmode.js b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_compatmode.js
new file mode 100644
index 000000000..6aec96ea1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_compatmode.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that AddonRepository correctly fills in the
+// %COMPATIBILITY_MODE% token in the Search API URL.
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+var COMPATIBILITY_PREF;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_AddonRepository_compatmode_ignore.xml", gServer);
+mapFile("/data/test_AddonRepository_compatmode_normal.xml", gServer);
+mapFile("/data/test_AddonRepository_compatmode_strict.xml", gServer);
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS,
+ "http://localhost:" + gPort + "/data/test_AddonRepository_compatmode_%COMPATIBILITY_MODE%.xml");
+ startupManager();
+ run_test_1();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+// Strict compatibility checking disabled.
+function run_test_1() {
+ do_print("Testing with strict compatibility checking disabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-normal@tests.mozilla.org");
+
+ run_test_2();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
+
+// Strict compatibility checking enabled.
+function run_test_2() {
+ do_print("Testing with strict compatibility checking enabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-strict@tests.mozilla.org");
+
+ run_test_3();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
+
+// Compatibility checking disabled.
+function run_test_3() {
+ do_print("Testing with all compatibility checking disabled");
+ AddonManager.checkCompatibility = false;
+
+ AddonRepository.searchAddons("test", 6, {
+ searchSucceeded: function(aAddons) {
+ do_check_neq(aAddons, null);
+ do_check_eq(aAddons.length, 1);
+ do_check_eq(aAddons[0].id, "compatmode-ignore@tests.mozilla.org");
+
+ end_test();
+ },
+ searchFailed: function() {
+ do_throw("Search should not have failed");
+ }
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_ChromeManifestParser.js b/toolkit/mozapps/webextensions/test/xpcshell/test_ChromeManifestParser.js
new file mode 100644
index 000000000..605c4224b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_ChromeManifestParser.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests ChromeManifestParser.js
+
+Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1"),
+ do_get_addon("test_chromemanifest_2"),
+ do_get_addon("test_chromemanifest_3"),
+ do_get_addon("test_chromemanifest_4")],
+ function() {
+
+ restartManager();
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ // addon1
+ let a1Uri = a1.getResourceURI("/").spec;
+ let expected = [
+ {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}
+ ];
+ let manifestURI = a1.getResourceURI("chrome.manifest");
+ let manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon2
+ let a2Uri = a2.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]}
+ ];
+ manifestURI = a2.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon3
+ let a3Uri = a3.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]},
+ {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]},
+ ];
+ manifestURI = a3.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ // addon4
+ let a4Uri = a4.getResourceURI("/").spec;
+ expected = [
+ {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]},
+ {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+ {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+ {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+ {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]},
+ {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]}
+ ];
+ manifestURI = a4.getResourceURI("chrome.manifest");
+ manifest = ChromeManifestParser.parseSync(manifestURI);
+
+ do_check_true(Array.isArray(manifest));
+ do_check_eq(manifest.length, expected.length);
+ for (let i = 0; i < manifest.length; i++) {
+ do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+ }
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_DeferredSave.js b/toolkit/mozapps/webextensions/test/xpcshell/test_DeferredSave.js
new file mode 100644
index 000000000..2a6ff291e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_DeferredSave.js
@@ -0,0 +1,549 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test behaviour of module to perform deferred save of data
+// files to disk
+
+"use strict";
+
+const testFile = gProfD.clone();
+testFile.append("DeferredSaveTest");
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+var DSContext = Components.utils.import("resource://gre/modules/DeferredSave.jsm", {});
+var DeferredSave = DSContext.DeferredSave;
+
+// Test wrapper to let us do promise/task based testing of DeferredSave
+function DeferredSaveTester(aDataProvider) {
+ let tester = {
+ // Deferred for the promise returned by the mock writeAtomic
+ waDeferred: null,
+
+ // The most recent data "written" by the mock OS.File.writeAtomic
+ writtenData: undefined,
+
+ dataToSave: "Data to save",
+
+ save: (aData, aWriteHandler) => {
+ tester.writeHandler = aWriteHandler || writer;
+ tester.dataToSave = aData;
+ return tester.saver.saveChanges();
+ },
+
+ flush: (aWriteHandler) => {
+ tester.writeHandler = aWriteHandler || writer;
+ return tester.saver.flush();
+ },
+
+ get lastError() {
+ return tester.saver.lastError;
+ }
+ };
+
+ // Default write handler for most cases where the test case doesn't need
+ // to do anything while the write is in progress; just completes the write
+ // on the next event loop
+ function writer(aTester) {
+ do_print("default write callback");
+ let length = aTester.writtenData.length;
+ do_execute_soon(() => aTester.waDeferred.resolve(length));
+ }
+
+ if (!aDataProvider)
+ aDataProvider = () => tester.dataToSave;
+
+ tester.saver = new DeferredSave(testFile.path, aDataProvider);
+
+ // Install a mock for OS.File.writeAtomic to let us control the async
+ // behaviour of the promise
+ DSContext.OS.File.writeAtomic = function mock_writeAtomic(aFile, aData, aOptions) {
+ do_print("writeAtomic: " + aFile + " data: '" + aData + "', " + aOptions.toSource());
+ tester.writtenData = aData;
+ tester.waDeferred = Promise.defer();
+ tester.writeHandler(tester);
+ return tester.waDeferred.promise;
+ };
+
+ return tester;
+}
+
+/**
+ * Install a mock nsITimer factory that triggers on the next spin of
+ * the event loop after it is scheduled
+ */
+function setQuickMockTimer() {
+ let quickTimer = {
+ initWithCallback: function(aFunction, aDelay, aType) {
+ do_print("Starting quick timer, delay = " + aDelay);
+ do_execute_soon(aFunction);
+ },
+ cancel: function() {
+ do_throw("Attempted to cancel a quickMockTimer");
+ }
+ };
+ DSContext.MakeTimer = () => {
+ do_print("Creating quick timer");
+ return quickTimer;
+ };
+}
+
+/**
+ * Install a mock nsITimer factory in DeferredSave.jsm, returning a promise that resolves
+ * when the client code sets the timer. Test cases can use this to wait for client code to
+ * be ready for a timer event, and then signal the event by calling mockTimer.callback().
+ * This could use some enhancement; clients can re-use the returned timer,
+ * but with this implementation it's not possible for the test to wait for
+ * a second call to initWithCallback() on the re-used timer.
+ * @return Promise{mockTimer} that resolves when initWithCallback()
+ * is called
+ */
+function setPromiseMockTimer() {
+ let waiter = Promise.defer();
+ let mockTimer = {
+ callback: null,
+ delay: null,
+ type: null,
+ isCancelled: false,
+
+ initWithCallback: function(aFunction, aDelay, aType) {
+ do_print("Starting timer, delay = " + aDelay);
+ this.callback = aFunction;
+ this.delay = aDelay;
+ this.type = aType;
+ // cancelled timers can be re-used
+ this.isCancelled = false;
+ waiter.resolve(this);
+ },
+ cancel: function() {
+ do_print("Cancelled mock timer");
+ this.callback = null;
+ this.delay = null;
+ this.type = null;
+ this.isCancelled = true;
+ // If initWithCallback was never called, resolve to let tests check for cancel
+ waiter.resolve(this);
+ }
+ };
+ DSContext.MakeTimer = () => {
+ do_print("Creating mock timer");
+ return mockTimer;
+ };
+ return waiter.promise;
+}
+
+/**
+ * Return a Promise<null> that resolves after the specified number of milliseconds
+ */
+function delay(aDelayMS) {
+ let deferred = Promise.defer();
+ do_timeout(aDelayMS, () => deferred.resolve(null));
+ return deferred.promise;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+// Modify set data once, ask for save, make sure it saves cleanly
+add_task(function* test_basic_save_succeeds() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let data = "Test 1 Data";
+
+ yield tester.save(data);
+ do_check_eq(tester.writtenData, data);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Two saves called during the same event loop, both with callbacks
+// Make sure we save only the second version of the data
+add_task(function* test_two_saves() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let firstCallback_happened = false;
+ let firstData = "Test first save";
+ let secondData = "Test second save";
+
+ // first save should not resolve until after the second one is called,
+ // so we can't just yield this promise
+ tester.save(firstData).then(count => {
+ do_check_eq(secondData, tester.writtenData);
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ yield tester.save(secondData);
+ do_check_true(firstCallback_happened);
+ do_check_eq(secondData, tester.writtenData);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Two saves called with a delay in between, both with callbacks
+// Make sure we save the second version of the data
+add_task(function* test_two_saves_delay() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstCallback_happened = false;
+ let delayDone = false;
+
+ let firstData = "First data to save with delay";
+ let secondData = "Modified data to save with delay";
+
+ tester.save(firstData).then(count => {
+ do_check_false(firstCallback_happened);
+ do_check_true(delayDone);
+ do_check_eq(secondData, tester.writtenData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // Wait a short time to let async events possibly spawned by the
+ // first tester.save() to run
+ yield delay(2);
+ delayDone = true;
+ // request to save modified data
+ let saving = tester.save(secondData);
+ // Yield to wait for client code to set the timer
+ let activeTimer = yield timerPromise;
+ // and then trigger it
+ activeTimer.callback();
+ // now wait for the DeferredSave to finish saving
+ yield saving;
+ do_check_true(firstCallback_happened);
+ do_check_eq(secondData, tester.writtenData);
+ do_check_eq(1, tester.saver.totalSaves);
+ do_check_eq(0, tester.saver.overlappedSaves);
+});
+
+// Test case where OS.File immediately reports an error when the write begins
+// Also check that the "error" getter correctly returns the error
+// Then do a write that succeeds, and make sure the error is cleared
+add_task(function* test_error_immediate() {
+ let tester = DeferredSaveTester();
+ let testError = new Error("Forced failure");
+ function writeFail(aTester) {
+ aTester.waDeferred.reject(testError);
+ }
+
+ setQuickMockTimer();
+ yield tester.save("test_error_immediate", writeFail).then(
+ count => do_throw("Did not get expected error"),
+ error => do_check_eq(testError.message, error.message)
+ );
+ do_check_eq(testError, tester.lastError);
+
+ // This write should succeed and clear the error
+ yield tester.save("test_error_immediate succeeds");
+ do_check_eq(null, tester.lastError);
+ // The failed save attempt counts in our total
+ do_check_eq(2, tester.saver.totalSaves);
+});
+
+// Save one set of changes, then while the write is in progress, modify the
+// data two more times. Test that we re-write the dirty data exactly once
+// after the first write succeeds
+add_task(function* dirty_while_writing() {
+ let tester = DeferredSaveTester();
+ let firstData = "First data";
+ let secondData = "Second data";
+ let thirdData = "Third data";
+ let firstCallback_happened = false;
+ let secondCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ setQuickMockTimer();
+ do_print("First save");
+ tester.save(firstData, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ do_print("waiting for writer");
+ let writer = yield writeStarted.promise;
+ do_print("Write started");
+
+ // Delay a bit, modify the data and call saveChanges, delay a bit more,
+ // modify the data and call saveChanges again, another delay,
+ // then complete the in-progress write
+ yield delay(1);
+
+ tester.save(secondData).then(
+ count => {
+ do_check_true(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, thirdData);
+ secondCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // wait and then do the third change
+ yield delay(1);
+ let thirdWrite = tester.save(thirdData);
+
+ // wait a bit more and then finally finish the first write
+ yield delay(1);
+ writer.resolve(firstData.length);
+
+ // Now let everything else finish
+ yield thirdWrite;
+ do_check_true(firstCallback_happened);
+ do_check_true(secondCallback_happened);
+ do_check_eq(tester.writtenData, thirdData);
+ do_check_eq(2, tester.saver.totalSaves);
+ do_check_eq(1, tester.saver.overlappedSaves);
+});
+
+// A write callback for the OS.File.writeAtomic mock that rejects write attempts
+function disabled_write_callback(aTester) {
+ do_throw("Should not have written during clean flush");
+}
+
+// special write callback that disables itself to make sure
+// we don't try to write twice
+function write_then_disable(aTester) {
+ do_print("write_then_disable");
+ let length = aTester.writtenData.length;
+ aTester.writeHandler = disabled_write_callback;
+ do_execute_soon(() => aTester.waDeferred.resolve(length));
+}
+
+// Flush tests. First, do an ordinary clean save and then call flush;
+// there should not be another save
+add_task(function* flush_after_save() {
+ setQuickMockTimer();
+ let tester = DeferredSaveTester();
+ let dataToSave = "Flush after save";
+
+ yield tester.save(dataToSave);
+ yield tester.flush(disabled_write_callback);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Flush while a write is in progress, but the in-memory data is clean
+add_task(function* flush_during_write() {
+ let tester = DeferredSaveTester();
+ let dataToSave = "Flush during write";
+ let firstCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ setQuickMockTimer();
+ tester.save(dataToSave, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ let writer = yield writeStarted.promise;
+
+ // call flush with the write callback disabled, delay a bit more, complete in-progress write
+ let flushing = tester.flush(disabled_write_callback);
+ yield delay(2);
+ writer.resolve(dataToSave.length);
+
+ // now wait for the flush to finish
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// Flush while dirty but write not in progress
+// The data written should be the value at the time
+// flush() is called, even if it is changed later
+add_task(function* flush_while_dirty() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "Flush while dirty, valid data";
+ let firstCallback_happened = false;
+
+ tester.save(firstData, write_then_disable).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ firstCallback_happened = true;
+ do_check_eq(tester.writtenData, firstData);
+ }, do_report_unexpected_exception);
+
+ // Wait for the timer to be set, but don't trigger it so the write won't start
+ let activeTimer = yield timerPromise;
+
+ let flushing = tester.flush();
+
+ // Make sure the timer was cancelled
+ do_check_true(activeTimer.isCancelled);
+
+ // Also make sure that data changed after the flush call
+ // (even without a saveChanges() call) doesn't get written
+ tester.dataToSave = "Flush while dirty, invalid data";
+
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ do_check_eq(1, tester.saver.totalSaves);
+});
+
+// And the grand finale - modify the data, start writing,
+// modify the data again so we're in progress and dirty,
+// then flush, then modify the data again
+// Data for the second write should be taken at the time
+// flush() is called, even if it is modified later
+add_task(function* flush_writing_dirty() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "Flush first pass data";
+ let secondData = "Flush second pass data";
+ let firstCallback_happened = false;
+ let secondCallback_happened = false;
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ tester.save(firstData, writeCallback).then(
+ count => {
+ do_check_false(firstCallback_happened);
+ do_check_eq(tester.writtenData, firstData);
+ firstCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ // Trigger the timer callback as soon as the DeferredSave sets it
+ let activeTimer = yield timerPromise;
+ activeTimer.callback();
+ let writer = yield writeStarted.promise;
+ // the first write has started
+
+ // dirty the data and request another save
+ // after the second save completes, there should not be another write
+ tester.save(secondData, write_then_disable).then(
+ count => {
+ do_check_true(firstCallback_happened);
+ do_check_false(secondCallback_happened);
+ do_check_eq(tester.writtenData, secondData);
+ secondCallback_happened = true;
+ }, do_report_unexpected_exception);
+
+ let flushing = tester.flush(write_then_disable);
+ // Flush should have cancelled our timer
+ do_check_true(activeTimer.isCancelled);
+ tester.dataToSave = "Flush, invalid data: changed late";
+ // complete the first write
+ writer.resolve(firstData.length);
+ // now wait for the second write / flush to complete
+ yield flushing;
+ do_check_true(firstCallback_happened);
+ do_check_true(secondCallback_happened);
+ do_check_eq(tester.writtenData, secondData);
+ do_check_eq(2, tester.saver.totalSaves);
+ do_check_eq(1, tester.saver.overlappedSaves);
+});
+
+// A data provider callback that throws an error the first
+// time it is called, and a different error the second time
+// so that tests can (a) make sure the promise is rejected
+// with the error and (b) make sure the provider is only
+// called once in case of error
+const expectedDataError = "Failed to serialize data";
+var badDataError = null;
+function badDataProvider() {
+ let err = new Error(badDataError);
+ badDataError = "badDataProvider called twice";
+ throw err;
+}
+
+// Handle cases where data provider throws
+// First, throws during a normal save
+add_task(function* data_throw() {
+ setQuickMockTimer();
+ badDataError = expectedDataError;
+ let tester = DeferredSaveTester(badDataProvider);
+ yield tester.save("data_throw").then(
+ count => do_throw("Expected serialization failure"),
+ error => do_check_eq(error.message, expectedDataError));
+});
+
+// Now, throws during flush
+add_task(function* data_throw_during_flush() {
+ badDataError = expectedDataError;
+ let tester = DeferredSaveTester(badDataProvider);
+ let firstCallback_happened = false;
+
+ setPromiseMockTimer();
+ // Write callback should never be called
+ tester.save("data_throw_during_flush", disabled_write_callback).then(
+ count => do_throw("Expected serialization failure"),
+ error => {
+ do_check_false(firstCallback_happened);
+ do_check_eq(error.message, expectedDataError);
+ firstCallback_happened = true;
+ });
+
+ // flush() will cancel the timer
+ yield tester.flush(disabled_write_callback).then(
+ count => do_throw("Expected serialization failure"),
+ error => do_check_eq(error.message, expectedDataError)
+ );
+
+ do_check_true(firstCallback_happened);
+});
+
+// Try to reproduce race condition. The observed sequence of events:
+// saveChanges
+// start writing
+// saveChanges
+// finish writing (need to restart delayed timer)
+// saveChanges
+// flush
+// write starts
+// actually restart timer for delayed write
+// write completes
+// delayed timer goes off, throws error because DeferredSave has been torn down
+add_task(function* delay_flush_race() {
+ let timerPromise = setPromiseMockTimer();
+ let tester = DeferredSaveTester();
+ let firstData = "First save";
+ let secondData = "Second save";
+ let thirdData = "Third save";
+ let writeStarted = Promise.defer();
+
+ function writeCallback(aTester) {
+ writeStarted.resolve(aTester.waDeferred);
+ }
+
+ // This promise won't resolve until after writeStarted
+ let firstSave = tester.save(firstData, writeCallback);
+ (yield timerPromise).callback();
+
+ let writer = yield writeStarted.promise;
+ // the first write has started
+
+ // dirty the data and request another save
+ let secondSave = tester.save(secondData);
+
+ // complete the first write
+ writer.resolve(firstData.length);
+ yield firstSave;
+ do_check_eq(tester.writtenData, firstData);
+
+ tester.save(thirdData);
+ let flushing = tester.flush();
+
+ yield secondSave;
+ do_check_eq(tester.writtenData, thirdData);
+
+ yield flushing;
+ do_check_eq(tester.writtenData, thirdData);
+
+ // Our DeferredSave should not have a _timer here; if it
+ // does, the bug caused a reschedule
+ do_check_eq(null, tester.saver._timer);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_LightweightThemeManager.js b/toolkit/mozapps/webextensions/test/xpcshell/test_LightweightThemeManager.js
new file mode 100644
index 000000000..61a46b251
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_LightweightThemeManager.js
@@ -0,0 +1,598 @@
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+const MANDATORY = ["id", "name", "headerURL"];
+const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
+ "previewURL", "author", "description", "homepageURL",
+ "updateURL", "version"];
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+function dummy(id) {
+ return {
+ id: id || Math.random().toString(),
+ name: Math.random().toString(),
+ headerURL: "http://lwttest.invalid/a.png",
+ footerURL: "http://lwttest.invalid/b.png",
+ textcolor: Math.random().toString(),
+ accentcolor: Math.random().toString()
+ };
+}
+
+function hasPermission(aAddon, aPerm) {
+ var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()];
+ return !!(aAddon.permissions & perm);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 8);
+
+ let {LightweightThemeManager: ltm} = Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", {});
+
+ do_check_eq(typeof ltm, "object");
+ do_check_eq(typeof ltm.usedThemes, "object");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ ltm.previewTheme(dummy("preview0"));
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ ltm.previewTheme(dummy("preview1"));
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+ ltm.resetPreview();
+
+ ltm.currentTheme = dummy("x0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+ do_check_eq(ltm.usedThemes[0].id, "x0");
+ do_check_eq(ltm.getUsedTheme("x0").id, "x0");
+
+ ltm.previewTheme(dummy("preview0"));
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+
+ ltm.resetPreview();
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.id, "x0");
+
+ ltm.currentTheme = dummy("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x0");
+
+ ltm.currentTheme = dummy("x2");
+ do_check_eq(ltm.usedThemes.length, 3);
+ do_check_eq(ltm.currentTheme.id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+ do_check_eq(ltm.usedThemes[2].id, "x0");
+
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_eq(ltm.currentTheme.id, "x7");
+ do_check_eq(ltm.usedThemes[1].id, "x6");
+ do_check_eq(ltm.usedThemes[7].id, "x0");
+
+ ltm.currentTheme = dummy("x8");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_eq(ltm.currentTheme.id, "x8");
+ do_check_eq(ltm.usedThemes[1].id, "x7");
+ do_check_eq(ltm.usedThemes[7].id, "x1");
+ do_check_eq(ltm.getUsedTheme("x0"), null);
+
+ ltm.forgetUsedTheme("nonexistent");
+ do_check_eq(ltm.usedThemes.length, 8);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("x8");
+ do_check_eq(ltm.usedThemes.length, 7);
+ do_check_eq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes[0].id, "x7");
+ do_check_eq(ltm.usedThemes[6].id, "x1");
+
+ ltm.forgetUsedTheme("x7");
+ ltm.forgetUsedTheme("x6");
+ ltm.forgetUsedTheme("x5");
+ ltm.forgetUsedTheme("x4");
+ ltm.forgetUsedTheme("x3");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes[0].id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+
+ ltm.currentTheme = dummy("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x2");
+
+ ltm.currentTheme = dummy("x2");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x2");
+ do_check_eq(ltm.usedThemes[0].id, "x2");
+ do_check_eq(ltm.usedThemes[1].id, "x1");
+
+ ltm.currentTheme = ltm.getUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x2");
+
+ ltm.forgetUsedTheme("x1");
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Use chinese name to test utf-8, for bug #541943
+ var chineseTheme = dummy("chinese0");
+ chineseTheme.name = "笢恅0";
+ chineseTheme.description = "笢恅1";
+ ltm.currentTheme = chineseTheme;
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.name, "笢恅0");
+ do_check_eq(ltm.currentTheme.description, "笢恅1");
+ do_check_eq(ltm.usedThemes[0].name, "笢恅0");
+ do_check_eq(ltm.usedThemes[0].description, "笢恅1");
+ do_check_eq(ltm.getUsedTheme("chinese0").name, "笢恅0");
+ do_check_eq(ltm.getUsedTheme("chinese0").description, "笢恅1");
+
+ // This name used to break the usedTheme JSON causing all LWTs to be lost
+ var chineseTheme1 = dummy("chinese1");
+ chineseTheme1.name = "眵昜湮桵蔗坌~郔乾";
+ chineseTheme1.description = "眵昜湮桵蔗坌~郔乾";
+ ltm.currentTheme = chineseTheme1;
+ do_check_neq(ltm.currentTheme, null);
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.currentTheme.description, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.usedThemes[1].name, "笢恅0");
+ do_check_eq(ltm.usedThemes[1].description, "笢恅1");
+ do_check_eq(ltm.usedThemes[0].name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(ltm.usedThemes[0].description, "眵昜湮桵蔗坌~郔乾");
+
+ ltm.forgetUsedTheme("chinese0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("chinese1");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ do_check_eq(ltm.parseTheme("invalid json"), null);
+ do_check_eq(ltm.parseTheme('"json string"'), null);
+
+ function roundtrip(data, secure) {
+ return ltm.parseTheme(JSON.stringify(data),
+ "http" + (secure ? "s" : "") + "://lwttest.invalid/");
+ }
+
+ var data = dummy();
+ do_check_neq(roundtrip(data), null);
+ data.id = null;
+ do_check_eq(roundtrip(data), null);
+ data.id = 1;
+ do_check_eq(roundtrip(data), null);
+ data.id = 1.5;
+ do_check_eq(roundtrip(data), null);
+ data.id = true;
+ do_check_eq(roundtrip(data), null);
+ data.id = {};
+ do_check_eq(roundtrip(data), null);
+ data.id = [];
+ do_check_eq(roundtrip(data), null);
+
+ // Check whether parseTheme handles international characters right
+ var chineseTheme2 = dummy();
+ chineseTheme2.name = "眵昜湮桵蔗坌~郔乾";
+ chineseTheme2.description = "眵昜湮桵蔗坌~郔乾";
+ do_check_neq(roundtrip(chineseTheme2), null);
+ do_check_eq(roundtrip(chineseTheme2).name, "眵昜湮桵蔗坌~郔乾");
+ do_check_eq(roundtrip(chineseTheme2).description, "眵昜湮桵蔗坌~郔乾");
+
+ data = dummy();
+ data.unknownProperty = "Foo";
+ do_check_eq(typeof roundtrip(data).unknownProperty, "undefined");
+
+ data = dummy();
+ data.unknownURL = "http://lwttest.invalid/";
+ do_check_eq(typeof roundtrip(data).unknownURL, "undefined");
+
+ function roundtripSet(props, modify, test, secure) {
+ props.forEach(function (prop) {
+ var theme = dummy();
+ modify(theme, prop);
+ test(roundtrip(theme, secure), prop, theme);
+ });
+ }
+
+ roundtripSet(MANDATORY, function (theme, prop) {
+ delete theme[prop];
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (theme, prop) {
+ delete theme[prop];
+ }, function (after) {
+ do_check_neq(after, null);
+ });
+
+ roundtripSet(MANDATORY, function (theme, prop) {
+ theme[prop] = "";
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (theme, prop) {
+ theme[prop] = "";
+ }, function (after, prop) {
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ roundtripSet(MANDATORY, function (theme, prop) {
+ theme[prop] = " ";
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(OPTIONAL, function (theme, prop) {
+ theme[prop] = " ";
+ }, function (after, prop) {
+ do_check_neq(after, null);
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ function non_urls(props) {
+ return props.filter(prop => !/URL$/.test(prop));
+ }
+
+ function urls(props) {
+ return props.filter(prop => /URL$/.test(prop));
+ }
+
+ roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function (theme, prop) {
+ theme[prop] = prop;
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop]);
+ });
+
+ roundtripSet(non_urls(MANDATORY.concat(OPTIONAL)), function (theme, prop) {
+ theme[prop] = " " + prop + " ";
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop].trim());
+ });
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (theme, prop) {
+ theme[prop] = Math.random().toString();
+ }, function (after, prop, before) {
+ if (prop == "updateURL")
+ do_check_eq(typeof after[prop], "undefined");
+ else
+ do_check_eq(after[prop], "http://lwttest.invalid/" + before[prop]);
+ });
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (theme, prop) {
+ theme[prop] = Math.random().toString();
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], "https://lwttest.invalid/" + before[prop]);
+ }, true);
+
+ roundtripSet(urls(MANDATORY.concat(OPTIONAL)), function (theme, prop) {
+ theme[prop] = "https://sub.lwttest.invalid/" + Math.random().toString();
+ }, function (after, prop, before) {
+ do_check_eq(after[prop], before[prop]);
+ });
+
+ roundtripSet(urls(MANDATORY), function (theme, prop) {
+ theme[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
+ }, function (after) {
+ do_check_eq(after, null);
+ });
+
+ roundtripSet(urls(OPTIONAL), function (theme, prop) {
+ theme[prop] = "ftp://lwttest.invalid/" + Math.random().toString();
+ }, function (after, prop) {
+ do_check_eq(typeof after[prop], "undefined");
+ });
+
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ data = dummy();
+ delete data.name;
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with no name");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "foo";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a bad headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "ftp://lwtest.invalid/test.png";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a non-http(s) headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.headerURL = "file:///test.png";
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with a non-http(s) headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ data.updateURL = "file:///test.json";
+ ltm.setLocalTheme(data);
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.updateURL, undefined);
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ data = dummy();
+ data.headerURL = "file:///test.png";
+ ltm.setLocalTheme(data);
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.currentTheme.headerURL, "file:///test.png");
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ data = dummy();
+ data.headerURL = "ftp://lwtest.invalid/test.png";
+ try {
+ ltm.setLocalTheme(data);
+ do_throw("Should have rejected a theme with a non-http(s), non-file headerURL");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ data = dummy();
+ delete data.id;
+ try {
+ ltm.currentTheme = data;
+ do_throw("Should have rejected a theme with no ID");
+ }
+ catch (e) {
+ // Expected exception
+ }
+
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Force the theme into the prefs anyway
+ let prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ let themes = [data];
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 1);
+
+ // This should silently drop the bad theme.
+ ltm.currentTheme = dummy();
+ do_check_eq(ltm.usedThemes.length, 1);
+ ltm.forgetUsedTheme(ltm.currentTheme.id);
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Add one broken and some working.
+ themes = [data, dummy("x1"), dummy("x2")];
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 3);
+
+ // Switching to an existing theme should drop the bad theme.
+ ltm.currentTheme = ltm.getUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 2);
+ ltm.forgetUsedTheme("x1");
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+ do_check_eq(ltm.usedThemes.length, 3);
+
+ // Forgetting an existing theme should drop the bad theme.
+ ltm.forgetUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 1);
+ ltm.forgetUsedTheme("x2");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ // Test whether a JSON set with setCharPref can be retrieved with usedThemes
+ ltm.currentTheme = dummy("x0");
+ ltm.currentTheme = dummy("x1");
+ prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(ltm.usedThemes));
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.currentTheme.id, "x1");
+ do_check_eq(ltm.usedThemes[1].id, "x0");
+ do_check_eq(ltm.usedThemes[0].id, "x1");
+
+ ltm.forgetUsedTheme("x0");
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_neq(ltm.currentTheme, null);
+
+ ltm.forgetUsedTheme("x1");
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
+
+ ltm.currentTheme = dummy("x1");
+ ltm.currentTheme = dummy("x2");
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ ltm.currentTheme = dummy("x8");
+ ltm.currentTheme = dummy("x9");
+ ltm.currentTheme = dummy("x10");
+ ltm.currentTheme = dummy("x11");
+ ltm.currentTheme = dummy("x12");
+ ltm.currentTheme = dummy("x13");
+ ltm.currentTheme = dummy("x14");
+ ltm.currentTheme = dummy("x15");
+ ltm.currentTheme = dummy("x16");
+ ltm.currentTheme = dummy("x17");
+ ltm.currentTheme = dummy("x18");
+ ltm.currentTheme = dummy("x19");
+ ltm.currentTheme = dummy("x20");
+ ltm.currentTheme = dummy("x21");
+ ltm.currentTheme = dummy("x22");
+ ltm.currentTheme = dummy("x23");
+ ltm.currentTheme = dummy("x24");
+ ltm.currentTheme = dummy("x25");
+ ltm.currentTheme = dummy("x26");
+ ltm.currentTheme = dummy("x27");
+ ltm.currentTheme = dummy("x28");
+ ltm.currentTheme = dummy("x29");
+ ltm.currentTheme = dummy("x30");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+
+ ltm.currentTheme = dummy("x31");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+ do_check_eq(ltm.getUsedTheme("x1"), null);
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 15);
+
+ do_check_eq(ltm.usedThemes.length, 15);
+
+ Services.prefs.setIntPref("lightweightThemes.maxUsedThemes", 32);
+
+ ltm.currentTheme = dummy("x1");
+ ltm.currentTheme = dummy("x2");
+ ltm.currentTheme = dummy("x3");
+ ltm.currentTheme = dummy("x4");
+ ltm.currentTheme = dummy("x5");
+ ltm.currentTheme = dummy("x6");
+ ltm.currentTheme = dummy("x7");
+ ltm.currentTheme = dummy("x8");
+ ltm.currentTheme = dummy("x9");
+ ltm.currentTheme = dummy("x10");
+ ltm.currentTheme = dummy("x11");
+ ltm.currentTheme = dummy("x12");
+ ltm.currentTheme = dummy("x13");
+ ltm.currentTheme = dummy("x14");
+ ltm.currentTheme = dummy("x15");
+ ltm.currentTheme = dummy("x16");
+
+ ltm.currentTheme = dummy("x32");
+
+ do_check_eq(ltm.usedThemes.length, 32);
+
+ ltm.currentTheme = dummy("x33");
+
+ do_check_eq(ltm.usedThemes.length, 32);
+
+ Services.prefs.clearUserPref("lightweightThemes.maxUsedThemes");
+
+ do_check_eq(ltm.usedThemes.length, 30);
+
+ let usedThemes = ltm.usedThemes;
+ for (let theme of usedThemes) {
+ ltm.forgetUsedTheme(theme.id);
+ }
+
+ // Check builtInTheme functionality for Bug 1094821
+ do_check_eq(ltm._builtInThemes.toString(), "[object Map]");
+ do_check_eq([...ltm._builtInThemes.entries()].length, 0);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ ltm.addBuiltInTheme(dummy("builtInTheme0"));
+ do_check_eq([...ltm._builtInThemes].length, 1);
+ do_check_eq(ltm.usedThemes.length, 1);
+ do_check_eq(ltm.usedThemes[0].id, "builtInTheme0");
+
+ ltm.addBuiltInTheme(dummy("builtInTheme1"));
+ do_check_eq([...ltm._builtInThemes].length, 2);
+ do_check_eq(ltm.usedThemes.length, 2);
+ do_check_eq(ltm.usedThemes[1].id, "builtInTheme1");
+
+ // Clear all and then re-add
+ ltm.clearBuiltInThemes();
+ do_check_eq([...ltm._builtInThemes].length, 0);
+ do_check_eq(ltm.usedThemes.length, 0);
+
+ ltm.addBuiltInTheme(dummy("builtInTheme0"));
+ ltm.addBuiltInTheme(dummy("builtInTheme1"));
+ do_check_eq([...ltm._builtInThemes].length, 2);
+ do_check_eq(ltm.usedThemes.length, 2);
+
+ do_test_pending();
+
+ AddonManager.getAddonByID("builtInTheme0@personas.mozilla.org", builtInThemeAddon => {
+ // App specific theme can't be uninstalled or disabled,
+ // but can be enabled (since it isn't already applied).
+ do_check_eq(hasPermission(builtInThemeAddon, "uninstall"), false);
+ do_check_eq(hasPermission(builtInThemeAddon, "disable"), false);
+ do_check_eq(hasPermission(builtInThemeAddon, "enable"), true);
+
+ ltm.currentTheme = dummy("x0");
+ do_check_eq([...ltm._builtInThemes].length, 2);
+ do_check_eq(ltm.usedThemes.length, 3);
+ do_check_eq(ltm.usedThemes[0].id, "x0");
+ do_check_eq(ltm.currentTheme.id, "x0");
+ do_check_eq(ltm.usedThemes[1].id, "builtInTheme0");
+ do_check_eq(ltm.usedThemes[2].id, "builtInTheme1");
+
+ Assert.throws(() => { ltm.addBuiltInTheme(dummy("builtInTheme0")) },
+ "Exception is thrown adding a duplicate theme");
+ Assert.throws(() => { ltm.addBuiltInTheme("not a theme object") },
+ "Exception is thrown adding an invalid theme");
+
+ AddonManager.getAddonByID("x0@personas.mozilla.org", x0Addon => {
+ // Currently applied (non-app-specific) can be uninstalled or disabled,
+ // but can't be enabled (since it's already applied).
+ do_check_eq(hasPermission(x0Addon, "uninstall"), true);
+ do_check_eq(hasPermission(x0Addon, "disable"), true);
+ do_check_eq(hasPermission(x0Addon, "enable"), false);
+
+ ltm.forgetUsedTheme("x0");
+ do_check_eq(ltm.currentTheme, null);
+
+ // Removing the currently applied app specific theme should unapply it
+ ltm.currentTheme = ltm.getUsedTheme("builtInTheme0");
+ do_check_eq(ltm.currentTheme.id, "builtInTheme0");
+ do_check_true(ltm.forgetBuiltInTheme("builtInTheme0"));
+ do_check_eq(ltm.currentTheme, null);
+
+ do_check_eq([...ltm._builtInThemes].length, 1);
+ do_check_eq(ltm.usedThemes.length, 1);
+
+ do_check_true(ltm.forgetBuiltInTheme("builtInTheme1"));
+ do_check_false(ltm.forgetBuiltInTheme("not-an-existing-theme-id"));
+
+ do_check_eq([...ltm._builtInThemes].length, 0);
+ do_check_eq(ltm.usedThemes.length, 0);
+ do_check_eq(ltm.currentTheme, null);
+
+ do_test_finished();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_ProductAddonChecker.js b/toolkit/mozapps/webextensions/test/xpcshell/test_ProductAddonChecker.js
new file mode 100644
index 000000000..6c562db65
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_ProductAddonChecker.js
@@ -0,0 +1,244 @@
+"use strict";
+
+Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+const LocalFile = new Components.Constructor("@mozilla.org/file/local;1", AM_Ci.nsIFile, "initWithPath");
+
+var testserver = new HttpServer();
+testserver.registerDirectory("/data/", do_get_file("data/productaddons"));
+testserver.start();
+var root = testserver.identity.primaryScheme + "://" +
+ testserver.identity.primaryHost + ":" +
+ testserver.identity.primaryPort + "/data/"
+
+/**
+ * Compares binary data of 2 arrays and returns true if they are the same
+ *
+ * @param arr1 The first array to compare
+ * @param arr2 The second array to compare
+*/
+function compareBinaryData(arr1, arr2) {
+ do_check_eq(arr1.length, arr2.length);
+ for (let i = 0; i < arr1.length; i++) {
+ if (arr1[i] != arr2[i]) {
+ do_print("Data differs at index " + i +
+ ", arr1: " + arr1[i] + ", arr2: " + arr2[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+/**
+ * Reads a file's data and returns it
+ *
+ * @param file The file to read the data from
+ * @return array of bytes for the data in the file.
+*/
+function getBinaryFileData(file) {
+ let fileStream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ // Open as RD_ONLY with default permissions.
+ fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
+
+ let stream = AM_Cc["@mozilla.org/binaryinputstream;1"].
+ createInstance(AM_Ci.nsIBinaryInputStream);
+ stream.setInputStream(fileStream);
+ let bytes = stream.readByteArray(stream.available());
+ fileStream.close();
+ return bytes;
+}
+
+/**
+ * Compares binary data of 2 files and returns true if they are the same
+ *
+ * @param file1 The first file to compare
+ * @param file2 The second file to compare
+*/
+function compareFiles(file1, file2) {
+ return compareBinaryData(getBinaryFileData(file1), getBinaryFileData(file2));
+}
+
+add_task(function* test_404() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "404.xml");
+ do_check_true(res.usedFallback);
+});
+
+add_task(function* test_not_xml() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "bad.txt");
+ do_check_true(res.usedFallback);
+});
+
+add_task(function* test_invalid_xml() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "bad.xml");
+ do_check_true(res.usedFallback);
+});
+
+add_task(function* test_wrong_xml() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "bad2.xml");
+ do_check_true(res.usedFallback);
+});
+
+add_task(function* test_missing() {
+ let addons = yield ProductAddonChecker.getProductAddonList(root + "missing.xml");
+ do_check_eq(addons, null);
+});
+
+add_task(function* test_empty() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "empty.xml");
+ do_check_true(Array.isArray(res.gmpAddons));
+ do_check_eq(res.gmpAddons.length, 0);
+});
+
+add_task(function* test_good_xml() {
+ let res = yield ProductAddonChecker.getProductAddonList(root + "good.xml");
+ do_check_true(Array.isArray(res.gmpAddons));
+
+ // There are three valid entries in the XML
+ do_check_eq(res.gmpAddons.length, 5);
+
+ let addon = res.gmpAddons[0];
+ do_check_eq(addon.id, "test1");
+ do_check_eq(addon.URL, "http://example.com/test1.xpi");
+ do_check_eq(addon.hashFunction, undefined);
+ do_check_eq(addon.hashValue, undefined);
+ do_check_eq(addon.version, undefined);
+ do_check_eq(addon.size, undefined);
+
+ addon = res.gmpAddons[1];
+ do_check_eq(addon.id, "test2");
+ do_check_eq(addon.URL, "http://example.com/test2.xpi");
+ do_check_eq(addon.hashFunction, "md5");
+ do_check_eq(addon.hashValue, "djhfgsjdhf");
+ do_check_eq(addon.version, undefined);
+ do_check_eq(addon.size, undefined);
+
+ addon = res.gmpAddons[2];
+ do_check_eq(addon.id, "test3");
+ do_check_eq(addon.URL, "http://example.com/test3.xpi");
+ do_check_eq(addon.hashFunction, undefined);
+ do_check_eq(addon.hashValue, undefined);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.size, 45);
+
+ addon = res.gmpAddons[3];
+ do_check_eq(addon.id, "test4");
+ do_check_eq(addon.URL, undefined);
+ do_check_eq(addon.hashFunction, undefined);
+ do_check_eq(addon.hashValue, undefined);
+ do_check_eq(addon.version, undefined);
+ do_check_eq(addon.size, undefined);
+
+ addon = res.gmpAddons[4];
+ do_check_eq(addon.id, undefined);
+ do_check_eq(addon.URL, "http://example.com/test5.xpi");
+ do_check_eq(addon.hashFunction, undefined);
+ do_check_eq(addon.hashValue, undefined);
+ do_check_eq(addon.version, undefined);
+ do_check_eq(addon.size, undefined);
+});
+
+add_task(function* test_download_nourl() {
+ try {
+ let path = yield ProductAddonChecker.downloadAddon({});
+
+ yield OS.File.remove(path);
+ do_throw("Should not have downloaded a file with a missing url");
+ }
+ catch (e) {
+ do_check_true(true, "Should have thrown when downloading a file with a missing url.");
+ }
+});
+
+add_task(function* test_download_missing() {
+ try {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "nofile.xpi",
+ });
+
+ yield OS.File.remove(path);
+ do_throw("Should not have downloaded a missing file");
+ }
+ catch (e) {
+ do_check_true(true, "Should have thrown when downloading a missing file.");
+ }
+});
+
+add_task(function* test_download_noverify() {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "unsigned.xpi",
+ });
+
+ let stat = yield OS.File.stat(path);
+ do_check_false(stat.isDir);
+ do_check_eq(stat.size, 452)
+
+ do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
+
+ yield OS.File.remove(path);
+});
+
+add_task(function* test_download_badsize() {
+ try {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "unsigned.xpi",
+ size: 400,
+ });
+
+ yield OS.File.remove(path);
+ do_throw("Should not have downloaded a file with a bad size");
+ }
+ catch (e) {
+ do_check_true(true, "Should have thrown when downloading a file with a bad size.");
+ }
+});
+
+add_task(function* test_download_badhashfn() {
+ try {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "unsigned.xpi",
+ hashFunction: "sha2567",
+ hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
+ });
+
+ yield OS.File.remove(path);
+ do_throw("Should not have downloaded a file with a bad hash function");
+ }
+ catch (e) {
+ do_check_true(true, "Should have thrown when downloading a file with a bad hash function.");
+ }
+});
+
+add_task(function* test_download_badhash() {
+ try {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "unsigned.xpi",
+ hashFunction: "sha256",
+ hashValue: "8b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
+ });
+
+ yield OS.File.remove(path);
+ do_throw("Should not have downloaded a file with a bad hash");
+ }
+ catch (e) {
+ do_check_true(true, "Should have thrown when downloading a file with a bad hash.");
+ }
+});
+
+add_task(function* test_download_works() {
+ let path = yield ProductAddonChecker.downloadAddon({
+ URL: root + "unsigned.xpi",
+ size: 452,
+ hashFunction: "sha256",
+ hashValue: "9b9abf7ddfc1a6d7ffc7e0247481dcc202363e4445ad3494fb22036f1698c7f3",
+ });
+
+ let stat = yield OS.File.stat(path);
+ do_check_false(stat.isDir);
+
+ do_check_true(compareFiles(do_get_file("data/productaddons/unsigned.xpi"), new LocalFile(path)));
+
+ yield OS.File.remove(path);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_XPIStates.js b/toolkit/mozapps/webextensions/test/xpcshell/test_XPIStates.js
new file mode 100644
index 000000000..99ab8ab13
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_XPIStates.js
@@ -0,0 +1,299 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test that we only check manifest age for disabled extensions
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+/* We want one add-on installed packed, and one installed unpacked
+ */
+
+function run_test() {
+ // Shut down the add-on manager after all tests run.
+ do_register_cleanup(promiseShutdownManager);
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Use bootstrap extensions so the changes will be immediate.
+// A packed extension, to be enabled
+writeInstallRDFToXPI({
+ id: "packed-enabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Packed, Enabled",
+}, profileDir);
+
+// Packed, will be disabled
+writeInstallRDFToXPI({
+ id: "packed-disabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Packed, Disabled",
+}, profileDir);
+
+// Unpacked, enabled
+writeInstallRDFToDir({
+ id: "unpacked-enabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Unpacked, Enabled",
+}, profileDir, undefined, "extraFile.js");
+
+
+// Unpacked, disabled
+writeInstallRDFToDir({
+ id: "unpacked-disabled@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Unpacked, disabled",
+}, profileDir, undefined, "extraFile.js");
+
+// Keep track of the last time stamp we've used, so that we can keep moving
+// it forward (if we touch two different files in the same add-on with the same
+// timestamp we may not consider the change significant)
+var lastTimestamp = Date.now();
+
+/*
+ * Helper function to touch a file and then test whether we detect the change.
+ * @param XS The XPIState object.
+ * @param aPath File path to touch.
+ * @param aChange True if we should notice the change, False if we shouldn't.
+ */
+function checkChange(XS, aPath, aChange) {
+ do_check_true(aPath.exists());
+ lastTimestamp += 10000;
+ do_print("Touching file " + aPath.path + " with " + lastTimestamp);
+ aPath.lastModifiedTime = lastTimestamp;
+ do_check_eq(XS.getInstallState(), aChange);
+ // Save the pref so we don't detect this change again
+ XS.save();
+}
+
+// Get a reference to the XPIState (loaded by startupManager) so we can unit test it.
+function getXS() {
+ let XPI = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+ return XPI.XPIStates;
+}
+
+add_task(function* detect_touches() {
+ startupManager();
+ let [pe, pd, ue, ud] = yield promiseAddonsByIDs([
+ "packed-enabled@tests.mozilla.org",
+ "packed-disabled@tests.mozilla.org",
+ "unpacked-enabled@tests.mozilla.org",
+ "unpacked-disabled@tests.mozilla.org"
+ ]);
+
+ do_print("Disable test add-ons");
+ pd.userDisabled = true;
+ ud.userDisabled = true;
+
+ let XS = getXS();
+
+ // Should be no changes detected here, because everything should start out up-to-date.
+ do_check_false(XS.getInstallState());
+
+ let states = XS.getLocation("app-profile");
+
+ // State should correctly reflect enabled/disabled
+ do_check_true(states.get("packed-enabled@tests.mozilla.org").enabled);
+ do_check_false(states.get("packed-disabled@tests.mozilla.org").enabled);
+ do_check_true(states.get("unpacked-enabled@tests.mozilla.org").enabled);
+ do_check_false(states.get("unpacked-disabled@tests.mozilla.org").enabled);
+
+ // Touch various files and make sure the change is detected.
+
+ // We notice that a packed XPI is touched for an enabled add-on.
+ let peFile = profileDir.clone();
+ peFile.append("packed-enabled@tests.mozilla.org.xpi");
+ checkChange(XS, peFile, true);
+
+ // We should notice the packed XPI change for a disabled add-on too.
+ let pdFile = profileDir.clone();
+ pdFile.append("packed-disabled@tests.mozilla.org.xpi");
+ checkChange(XS, pdFile, true);
+
+ // We notice changing install.rdf for an enabled unpacked add-on.
+ let ueDir = profileDir.clone();
+ ueDir.append("unpacked-enabled@tests.mozilla.org");
+ let manifest = ueDir.clone();
+ manifest.append("install.rdf");
+ checkChange(XS, manifest, true);
+ // We also notice changing another file for enabled unpacked add-on.
+ let otherFile = ueDir.clone();
+ otherFile.append("extraFile.js");
+ checkChange(XS, otherFile, true);
+
+ // We notice changing install.rdf for a *disabled* unpacked add-on.
+ let udDir = profileDir.clone();
+ udDir.append("unpacked-disabled@tests.mozilla.org");
+ manifest = udDir.clone();
+ manifest.append("install.rdf");
+ checkChange(XS, manifest, true);
+ // Finally, the case we actually care about...
+ // We *don't* notice changing another file for disabled unpacked add-on.
+ otherFile = udDir.clone();
+ otherFile.append("extraFile.js");
+ checkChange(XS, otherFile, false);
+
+ /*
+ * When we enable an unpacked add-on that was modified while it was
+ * disabled, we reflect the new timestamp in the add-on DB (otherwise, we'll
+ * think it changed on next restart).
+ */
+ ud.userDisabled = false;
+ let xState = XS.getAddon("app-profile", ud.id);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, ud.updateDate.getTime());
+});
+
+/*
+ * Uninstalling bootstrap add-ons should immediately remove them from the
+ * extensions.xpiState preference.
+ */
+add_task(function* uninstall_bootstrap() {
+ let [pe, pd, ue, ud] = yield promiseAddonsByIDs([
+ "packed-enabled@tests.mozilla.org",
+ "packed-disabled@tests.mozilla.org",
+ "unpacked-enabled@tests.mozilla.org",
+ "unpacked-disabled@tests.mozilla.org"
+ ]);
+ pe.uninstall();
+ let xpiState = Services.prefs.getCharPref("extensions.xpiState");
+ do_check_false(xpiState.includes("\"packed-enabled@tests.mozilla.org\""));
+});
+
+/*
+ * Installing a restartless add-on should immediately add it to XPIState
+ */
+add_task(function* install_bootstrap() {
+ let XS = getXS();
+
+ let installer = yield new Promise((resolve, reject) =>
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+
+ let promiseInstalled = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onInstallFailed: reject,
+ onInstallEnded: (install, newAddon) => resolve(newAddon)
+ });
+ });
+
+ installer.install();
+
+ let newAddon = yield promiseInstalled;
+ let xState = XS.getAddon("app-profile", newAddon.id);
+ do_check_true(!!xState);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, newAddon.updateDate.getTime());
+ newAddon.uninstall();
+});
+
+/*
+ * Installing an add-on that requires restart doesn't add to XPIState
+ * until after the restart; disable and enable happen immediately so that
+ * the next restart won't / will scan as necessary on the next restart,
+ * uninstalling it marks XPIState as disabled immediately
+ * and removes XPIState after restart.
+ */
+add_task(function* install_restart() {
+ let XS = getXS();
+
+ let installer = yield new Promise((resolve, reject) =>
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_4"), resolve));
+
+ let promiseInstalled = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onInstallFailed: reject,
+ onInstallEnded: (install, newAddon) => resolve(newAddon)
+ });
+ });
+
+ installer.install();
+
+ let newAddon = yield promiseInstalled;
+ let newID = newAddon.id;
+ let xState = XS.getAddon("app-profile", newID);
+ do_check_false(xState);
+
+ // Now we restart the add-on manager, and we need to get the XPIState again
+ // because the add-on manager reloads it.
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+
+ newAddon = yield promiseAddonByID(newID);
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_true(xState.enabled);
+ do_check_eq(xState.scanTime, newAddon.updateDate.getTime());
+
+ // Check that XPIState enabled flag is updated immediately,
+ // and doesn't change over restart.
+ newAddon.userDisabled = true;
+ do_check_false(xState.enabled);
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_false(xState.enabled);
+
+ newAddon = yield promiseAddonByID(newID);
+ newAddon.userDisabled = false;
+ do_check_true(xState.enabled);
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_true(xState.enabled);
+
+ // Uninstalling immediately marks XPIState disabled,
+ // removes state after restart.
+ newAddon = yield promiseAddonByID(newID);
+ newAddon.uninstall();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_true(xState);
+ do_check_false(xState.enabled);
+
+ // Restart to finish uninstall.
+ XS = null;
+ newAddon = null;
+ yield promiseRestartManager();
+ XS = getXS();
+ xState = XS.getAddon("app-profile", newID);
+ do_check_false(xState);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_XPIcancel.js b/toolkit/mozapps/webextensions/test/xpcshell/test_XPIcancel.js
new file mode 100644
index 000000000..d733778a5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_XPIcancel.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test the cancellable doing/done/cancelAll API in XPIProvider
+
+var scope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+var XPIProvider = scope.XPIProvider;
+
+function run_test() {
+ // Check that cancelling with nothing in progress doesn't blow up
+ XPIProvider.cancelAll();
+
+ // Check that a basic object gets cancelled
+ let getsCancelled = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ }
+ };
+ XPIProvider.doing(getsCancelled);
+ XPIProvider.cancelAll();
+ do_check_true(getsCancelled.isCancelled);
+
+ // Check that if we complete a cancellable, it doesn't get cancelled
+ let doesntGetCancelled = {
+ cancel: () => do_throw("This should not have been cancelled")
+ };
+ XPIProvider.doing(doesntGetCancelled);
+ do_check_true(XPIProvider.done(doesntGetCancelled));
+ XPIProvider.cancelAll();
+
+ // A cancellable that adds a cancellable
+ getsCancelled.isCancelled = false;
+ let addsAnother = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ XPIProvider.doing(getsCancelled);
+ }
+ }
+ XPIProvider.doing(addsAnother);
+ XPIProvider.cancelAll();
+ do_check_true(addsAnother.isCancelled);
+ do_check_true(getsCancelled.isCancelled);
+
+ // A cancellable that removes another. This assumes that Set() iterates in the
+ // order that members were added
+ let removesAnother = {
+ isCancelled: false,
+ cancel: function () {
+ if (this.isCancelled)
+ do_throw("Already cancelled");
+ this.isCancelled = true;
+ XPIProvider.done(doesntGetCancelled);
+ }
+ }
+ XPIProvider.doing(removesAnother);
+ XPIProvider.doing(doesntGetCancelled);
+ XPIProvider.cancelAll();
+ do_check_true(removesAnother.isCancelled);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_addon_path_service.js b/toolkit/mozapps/webextensions/test/xpcshell/test_addon_path_service.js
new file mode 100644
index 000000000..56ce3c614
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_addon_path_service.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var service = Components.classes["@mozilla.org/addon-path-service;1"].getService(Components.interfaces.amIAddonPathService);
+
+function insert(path, value)
+{
+ service.insertPath("/test/" + path, value);
+}
+
+function find(path)
+{
+ return service.findAddonId("/test/" + path);
+}
+
+function run_test()
+{
+ insert("abc", "10");
+ insert("def", "11");
+ insert("axy", "12");
+ insert("defghij", "13");
+ insert("defghi", "14");
+
+ do_check_eq(find("abc"), "10");
+ do_check_eq(find("abc123"), "10");
+ do_check_eq(find("def"), "11");
+ do_check_eq(find("axy"), "12");
+ do_check_eq(find("axy1"), "12");
+ do_check_eq(find("defghij"), "13");
+ do_check_eq(find("abd"), "");
+ do_check_eq(find("x"), "");
+
+ insert("file:///home/billm/mozilla/in4/objdir-ff-dbg/dist/bin/browser/extensions/%7B972ce4c6-7e08-4474-a285-3208198ce6fd%7D/", "{972ce4c6-7e08-4474-a285-3208198ce6fd}");
+ insert("file:///home/billm/mozilla/addons/dl-helper-workspace/addon/", "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}");
+
+ do_check_eq(find("file:///home/billm/mozilla/addons/dl-helper-workspace/addon/local/modules/medialist-manager.jsm"), "{b9db16a4-6edc-47ec-a1f4-b86292ed211d}");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_asyncBlocklistLoad.js b/toolkit/mozapps/webextensions/test/xpcshell/test_asyncBlocklistLoad.js
new file mode 100644
index 000000000..38e563979
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_asyncBlocklistLoad.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* () {
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService().wrappedJSObject;
+ let scope = Components.utils.import("resource://gre/modules/osfile.jsm");
+
+ // sync -> async
+ blocklist._loadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ yield blocklist._preloadBlocklist();
+ do_check_false(blocklist._isBlocklistPreloaded());
+ blocklist._clear();
+
+ // async -> sync
+ yield blocklist._preloadBlocklist();
+ do_check_false(blocklist._isBlocklistLoaded());
+ do_check_true(blocklist._isBlocklistPreloaded());
+ blocklist._loadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ do_check_false(blocklist._isBlocklistPreloaded());
+ blocklist._clear();
+
+ // async -> sync -> async
+ let read = scope.OS.File.read;
+ scope.OS.File.read = function(...args) {
+ return new Promise((resolve, reject) => {
+ do_execute_soon(() => {
+ blocklist._loadBlocklist();
+ resolve(read(...args));
+ });
+ });
+ }
+
+ yield blocklist._preloadBlocklist();
+ do_check_true(blocklist._isBlocklistLoaded());
+ do_check_false(blocklist._isBlocklistPreloaded());
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_backgroundupdate.js b/toolkit/mozapps/webextensions/test/xpcshell/test_backgroundupdate.js
new file mode 100644
index 000000000..3890b76e1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_backgroundupdate.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that background updates & notifications work as expected
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_backgroundupdate.rdf", testserver);
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Verify that with no add-ons installed the background update notifications get
+// called
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
+ do_check_eq(aAddons.length, 0);
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+ do_execute_soon(run_test_2);
+ }, "addons-background-update-complete", false);
+
+ // Trigger the background update timer handler
+ gInternalManager.notify(null);
+ });
+}
+
+// Verify that with two add-ons installed both of which claim to have updates
+// available we get the notification after both updates attempted to start
+function run_test_2() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_backgroundupdate.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Background update uses a different pref, if set
+ Services.prefs.setCharPref("extensions.update.background.url",
+ "http://localhost:" + gPort +"/data/test_backgroundupdate.rdf");
+ restartManager();
+
+ // Do hotfix checks
+ Services.prefs.setCharPref("extensions.hotfix.id", "hotfix@tests.mozilla.org");
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" + gPort + "/missing.rdf");
+
+ let installCount = 0;
+ let completeCount = 0;
+ let sawCompleteNotification = false;
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+
+ do_check_eq(installCount, 3);
+ sawCompleteNotification = true;
+ }, "addons-background-update-complete", false);
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ installCount++;
+ },
+
+ onDownloadFailed: function(aInstall) {
+ completeCount++;
+ if (completeCount == 3) {
+ do_check_true(sawCompleteNotification);
+ end_test();
+ }
+ }
+ });
+
+ // Trigger the background update timer handler
+ gInternalManager.notify(null);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bad_json.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bad_json.js
new file mode 100644
index 000000000..d3ccf68f3
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bad_json.js
@@ -0,0 +1,54 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that we rebuild the database correctly if it contains
+// JSON data that parses correctly but doesn't contain required fields
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending("Bad JSON");
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // This addon will be auto-installed at startup
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ shutdownManager();
+
+ // First startup/shutdown finished
+ // Replace the JSON store with something bogus
+ saveJSON({not: "what we expect to find"}, gExtensionsJSON);
+
+ startupManager(false);
+ // Retrieve an addon to force the database to rebuild
+ AddonManager.getAddonsByIDs([addon1.id], callback_soon(after_db_rebuild));
+}
+
+function after_db_rebuild([a1]) {
+ do_check_eq(a1.id, addon1.id);
+
+ shutdownManager();
+
+ // Make sure our JSON database has schemaVersion and our installed extension
+ let data = loadJSON(gExtensionsJSON);
+ do_check_true("schemaVersion" in data);
+ do_check_eq(data.addons[0].id, addon1.id);
+
+ do_test_finished("Bad JSON");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_badschema.js b/toolkit/mozapps/webextensions/test/xpcshell/test_badschema.js
new file mode 100644
index 000000000..6ebf088d6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_badschema.js
@@ -0,0 +1,404 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a database with a bad schema
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_corrupt.rdf", testserver);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Create and configure the HTTP server.
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_1_modified_db);
+ });
+}
+
+
+function run_test_1_modified_db() {
+ // After restarting the database won't be open so we can alter
+ // the schema
+ shutdownManager();
+ changeXPIDBVersion(100);
+ startupManager();
+
+ // Accessing the add-ons should open and recover the database. Since
+ // migration occurs everything should be recovered correctly
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_1_after_rebuild);
+ });
+}
+
+function run_test_1_after_rebuild() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_gfx.js b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_gfx.js
new file mode 100644
index 000000000..cbcd5cb7e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_gfx.js
@@ -0,0 +1,157 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const TEST_APP_ID = "xpcshell@tests.mozilla.org";
+
+
+const EVENT_NAME = "blocklist-data-gfxItems";
+
+const SAMPLE_GFX_RECORD = {
+ "driverVersionComparator": "LESS_THAN_OR_EQUAL",
+ "driverVersion": "8.17.12.5896",
+ "vendor": "0x10de",
+ "blockID": "g36",
+ "feature": "DIRECT3D_9_LAYERS",
+ "devices": ["0x0a6c", "geforce"],
+ "featureStatus": "BLOCKED_DRIVER_VERSION",
+ "last_modified": 1458035931837,
+ "os": "WINNT 6.1",
+ "id": "3f947f16-37c2-4e96-d356-78b26363729b",
+ "versionRange": {"minVersion": 0, "maxVersion": "*"}
+};
+
+
+function Blocklist() {
+ let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService().wrappedJSObject;
+ blocklist._clear();
+ return blocklist;
+}
+
+
+function run_test() {
+ run_next_test();
+}
+
+
+add_task(function* test_sends_serialized_data() {
+ const blocklist = Blocklist();
+ blocklist._gfxEntries = [SAMPLE_GFX_RECORD];
+
+ const expected = "blockID:g36\tdevices:0x0a6c,geforce\tdriverVersion:8.17.12.5896\t" +
+ "driverVersionComparator:LESS_THAN_OR_EQUAL\tfeature:DIRECT3D_9_LAYERS\t" +
+ "featureStatus:BLOCKED_DRIVER_VERSION\tos:WINNT 6.1\tvendor:0x10de\t" +
+ "versionRange:0,*";
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._notifyObserversBlocklistGFX();
+ equal(received, expected);
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+
+add_task(function* test_parsing_fails_if_devices_contains_comma() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <devices>" +
+ " <device>0x2,582</device>" +
+ " <device>0x2782</device>" +
+ " </devices>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].devices.length, 1);
+ equal(blocklist._gfxEntries[0].devices[0], "0x2782");
+});
+
+
+add_task(function* test_empty_values_are_ignored() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <os></os>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._loadBlocklistFromString(input);
+ ok(received.indexOf("os" < 0));
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+add_task(function* test_empty_devices_are_ignored() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <devices></devices>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._loadBlocklistFromString(input);
+ ok(received.indexOf("devices" < 0));
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+add_task(function* test_version_range_default_values() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\"13.0b2\" maxVersion=\"42.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange maxVersion=\"2.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\"1.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\" \"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange/>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].versionRange.minVersion, "13.0b2");
+ equal(blocklist._gfxEntries[0].versionRange.maxVersion, "42.0");
+ equal(blocklist._gfxEntries[1].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[1].versionRange.maxVersion, "2.0");
+ equal(blocklist._gfxEntries[2].versionRange.minVersion, "1.0");
+ equal(blocklist._gfxEntries[2].versionRange.maxVersion, "*");
+ equal(blocklist._gfxEntries[3].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[3].versionRange.maxVersion, "*");
+ equal(blocklist._gfxEntries[4].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[4].versionRange.maxVersion, "*");
+});
+
+add_task(function* test_blockid_attribute() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry blockID=\"g60\">" +
+ " <vendor> 0x10de </vendor>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <feature> DIRECT3D_9_LAYERS </feature>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].blockID, "g60");
+ ok(!blocklist._gfxEntries[1].hasOwnProperty("blockID"));
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_metadata_filters.js b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_metadata_filters.js
new file mode 100644
index 000000000..5befa6fa0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_metadata_filters.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests blocking of extensions by ID, name, creator, homepageURL, updateURL
+// and RegExps for each. See bug 897735.
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_metadata_filters_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = args.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Should get blocked by name
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "Mozilla Corp.",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Should get blocked by all the attributes.
+ writeInstallRDFForExtension({
+ id: "block2@tests.mozilla.org",
+ version: "1.0",
+ name: "Moz-addon",
+ creator: "Dangerous",
+ homepageURL: "www.extension.dangerous.com",
+ updateURL: "www.extension.dangerous.com/update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Fails to get blocked because of a different ID even though other
+ // attributes match against a blocklist entry.
+ writeInstallRDFForExtension({
+ id: "block3@tests.mozilla.org",
+ version: "1.0",
+ name: "Moz-addon",
+ creator: "Dangerous",
+ homepageURL: "www.extensions.dangerous.com",
+ updateURL: "www.extension.dangerous.com/update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org",
+ "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_metadata_filters_1.xml", function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org",
+ "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_prefs.js b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_prefs.js
new file mode 100644
index 000000000..41ef62f98
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_prefs.js
@@ -0,0 +1,148 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests resetting of preferences in blocklist entry when an add-on is blocked.
+// See bug 802434.
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
+ return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+});
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_prefs_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// A window watcher to handle the blocklist UI.
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = args.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Add 2 extensions
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "Blocked add-on-1 with to-be-reset prefs",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "block2@tests.mozilla.org",
+ version: "1.0",
+ name: "Blocked add-on-2 with to-be-reset prefs",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ // Pre-set the preferences that we expect to get reset.
+ gPref.setIntPref("test.blocklist.pref1", 15);
+ gPref.setIntPref("test.blocklist.pref2", 15);
+ gPref.setBoolPref("test.blocklist.pref3", true);
+ gPref.setBoolPref("test.blocklist.pref4", true);
+
+ startupManager();
+
+ // Before blocklist is loaded.
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ do_check_eq(gPref.getIntPref("test.blocklist.pref1"), 15);
+ do_check_eq(gPref.getIntPref("test.blocklist.pref2"), 15);
+ do_check_eq(gPref.getBoolPref("test.blocklist.pref3"), true);
+ do_check_eq(gPref.getBoolPref("test.blocklist.pref4"), true);
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_prefs_1.xml", function() {
+ restartManager();
+
+ // Blocklist changes should have applied and the prefs must be reset.
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+ "block2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ do_check_neq(a2, null);
+ do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ // All these prefs must be reset to defaults.
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref1"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref2"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref3"), false);
+ do_check_eq(gPref.prefHasUserValue("test.blocklist.pref4"), false);
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_regexp.js b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_regexp.js
new file mode 100644
index 000000000..c89ccdef8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_regexp.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that blocklist entries using RegExp work as expected. This only covers
+// behavior specific to RegExp entries - general behavior is already tested
+// in test_blocklistchange.js.
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_regexp_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = args.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ writeInstallRDFForExtension({
+ id: "block1@tests.mozilla.org",
+ version: "1.0",
+ name: "RegExp blocked add-on",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org"], function([a1]) {
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_blocklist_regexp_1.xml", function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["block1@tests.mozilla.org"], function([a1]) {
+ // Blocklist contains two entries that will match this addon - ensure
+ // that the first one is applied.
+ do_check_neq(a1, null);
+ do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_blocklistchange.js b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklistchange.js
new file mode 100644
index 000000000..d065f700d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_blocklistchange.js
@@ -0,0 +1,1305 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that changes that cause an add-on to become unblocked or blocked have
+// the right effect
+
+// The tests follow a mostly common pattern. First they start with the add-ons
+// unblocked, then they make a change that causes the add-ons to become blocked
+// then they make a similar change that keeps the add-ons blocked then they make
+// a change that unblocks the add-ons. Some tests skip the initial part and
+// start with add-ons detected as blocked.
+
+// softblock1 is enabled/disabled by the blocklist changes so its softDisabled
+// property should always match its userDisabled property
+
+// softblock2 gets manually enabled then disabled after it becomes blocked so
+// its softDisabled property should never become true after that
+
+// softblock3 does the same as softblock2 however it remains disabled
+
+// softblock4 is disabled while unblocked and so should never have softDisabled
+// set to true and stay userDisabled. This add-on is not used in tests that
+// start with add-ons blocked as it would be identical to softblock3
+
+// softblock5 is a theme. Currently themes just get disabled when they become
+// softblocked and have to be manually re-enabled if they become completely
+// unblocked (bug 657520)
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+// Allow insecure updates
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false)
+
+var testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/blocklistchange/addon_update1.rdf", testserver);
+mapFile("/data/blocklistchange/addon_update2.rdf", testserver);
+mapFile("/data/blocklistchange/addon_update3.rdf", testserver);
+mapFile("/data/blocklistchange/addon_change.xml", testserver);
+mapFile("/data/blocklistchange/app_update.xml", testserver);
+mapFile("/data/blocklistchange/blocklist_update1.xml", testserver);
+mapFile("/data/blocklistchange/blocklist_update2.xml", testserver);
+mapFile("/data/blocklistchange/manual_update.xml", testserver);
+
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+
+var default_theme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_1 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_2 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock1_3 = {
+ id: "softblock1@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_1 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_2 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock2_3 = {
+ id: "softblock2@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_1 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_2 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock3_3 = {
+ id: "softblock3@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_1 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_2 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock4_3 = {
+ id: "softblock4@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_1 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_2 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "2.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var softblock5_3 = {
+ id: "softblock5@tests.mozilla.org",
+ version: "3.0",
+ name: "Softblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_1 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "1.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_2 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "2.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var hardblock_3 = {
+ id: "hardblock@tests.mozilla.org",
+ version: "3.0",
+ name: "Hardblocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_1 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "1.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update1.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_2 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "2.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update2.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+var regexpblock_3 = {
+ id: "regexpblock@tests.mozilla.org",
+ version: "3.0",
+ name: "RegExp-blocked add-on",
+ updateURL: "http://localhost:" + gPort + "/data/blocklistchange/addon_update3.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+};
+
+const ADDON_IDS = ["softblock1@tests.mozilla.org",
+ "softblock2@tests.mozilla.org",
+ "softblock3@tests.mozilla.org",
+ "softblock4@tests.mozilla.org",
+ "softblock5@tests.mozilla.org",
+ "hardblock@tests.mozilla.org",
+ "regexpblock@tests.mozilla.org"];
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, openArgs) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = openArgs.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+var InstallConfirm = {
+ confirm: function(aWindow, aUrl, aInstalls, aInstallCount) {
+ aInstalls.forEach(function(aInstall) {
+ aInstall.install();
+ });
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.amIWebInstallPrompt)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+var InstallConfirmFactory = {
+ createInstance: function createInstance(outer, iid) {
+ if (outer != null)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return InstallConfirm.QueryInterface(iid);
+ }
+};
+
+var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{f0863905-4dde-42e2-991c-2dc8209bc9ca}"),
+ "Fake Install Prompt",
+ "@mozilla.org/addons/web-install-prompt;1", InstallConfirmFactory);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function Pload_blocklist(aFile) {
+ let blocklist_updated = new Promise((resolve, reject) => {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ resolve();
+ }, "blocklist-updated", false);
+ });
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/blocklistchange/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+ return blocklist_updated;
+}
+
+// Does a background update check for add-ons and returns a promise that
+// resolves when any started installs complete
+function Pbackground_update() {
+ var installCount = 0;
+ var backgroundCheckCompleted = false;
+
+ let updated = new Promise((resolve, reject) => {
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ installCount++;
+ },
+
+ onInstallEnded: function(aInstall) {
+ installCount--;
+ // Wait until all started installs have completed
+ if (installCount)
+ return;
+
+ AddonManager.removeInstallListener(this);
+
+ // If the background check hasn't yet completed then let that call the
+ // callback when it is done
+ if (!backgroundCheckCompleted)
+ return;
+
+ resolve();
+ }
+ })
+
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+ backgroundCheckCompleted = true;
+
+ // If any new installs have started then we'll call the callback once they
+ // are completed
+ if (installCount)
+ return;
+
+ resolve();
+ }, "addons-background-update-complete", false);
+ });
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+ return updated;
+}
+
+// Manually updates the test add-ons to the given version
+function Pmanual_update(aVersion) {
+ let Pinstalls = [];
+ for (let name of ["soft1", "soft2", "soft3", "soft4", "soft5", "hard1", "regexp1"]) {
+ Pinstalls.push(new Promise((resolve, reject) => {
+ AddonManager.getInstallForURL("http://localhost:" + gPort + "/addons/blocklist_"
+ + name + "_" + aVersion + ".xpi",
+ resolve, "application/x-xpinstall");
+ }));
+ }
+
+ return Promise.all(Pinstalls).then(installs => {
+ let completePromises = [];
+ for (let install of installs) {
+ completePromises.push(new Promise(resolve => {
+ install.addListener({
+ onDownloadCancelled: resolve,
+ onInstallEnded: resolve
+ })
+ }));
+ }
+
+ // Use the default web installer to cancel/allow installs based on whether
+ // the add-on is valid or not.
+ let webInstaller = Cc["@mozilla.org/addons/web-install-listener;1"]
+ .getService(Ci.amIWebInstallListener);
+ webInstaller.onWebInstallRequested(null, null, installs);
+
+ return Promise.all(completePromises);
+ });
+}
+
+// Checks that an add-ons properties match expected values
+function check_addon(aAddon, aExpectedVersion, aExpectedUserDisabled,
+ aExpectedSoftDisabled, aExpectedState) {
+ do_check_neq(aAddon, null);
+ do_print("Testing " + aAddon.id + " version " + aAddon.version + " user "
+ + aAddon.userDisabled + " soft " + aAddon.softDisabled
+ + " perms " + aAddon.permissions);
+
+ do_check_eq(aAddon.version, aExpectedVersion);
+ do_check_eq(aAddon.blocklistState, aExpectedState);
+ do_check_eq(aAddon.userDisabled, aExpectedUserDisabled);
+ do_check_eq(aAddon.softDisabled, aExpectedSoftDisabled);
+ if (aAddon.softDisabled)
+ do_check_true(aAddon.userDisabled);
+
+ if (aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ do_print("blocked, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_print("blocked, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ else if (aAddon.userDisabled) {
+ do_print("userDisabled, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_true(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_print("userDisabled, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ else {
+ do_print("other, PERM_CAN_ENABLE " + aAddon.id);
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ if (aAddon.type != "theme") {
+ do_print("other, PERM_CAN_DISABLE " + aAddon.id);
+ do_check_true(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
+ }
+ }
+ do_check_eq(aAddon.appDisabled, aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ let willBeActive = aAddon.isActive;
+ if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE))
+ willBeActive = false;
+ else if (hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE))
+ willBeActive = true;
+
+ if (aExpectedUserDisabled || aExpectedState == Ci.nsIBlocklistService.STATE_BLOCKED) {
+ do_check_false(willBeActive);
+ }
+ else {
+ do_check_true(willBeActive);
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ run_next_test();
+}
+
+add_task(function* init() {
+ writeInstallRDFForExtension(default_theme, profileDir);
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ startupManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+ s4.userDisabled = true;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then switches application versions to
+// change add-ons to blocked and back
+add_task(function* run_app_update_test() {
+ do_print("Test: " + arguments.callee.name);
+ yield promiseRestartManager();
+ yield Pload_blocklist("app_update.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* app_update_step_2() {
+ yield promiseRestartManager("2");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* app_update_step_3() {
+ yield promiseRestartManager();
+
+ yield promiseRestartManager("2.5");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* app_update_step_4() {
+ yield promiseRestartManager("1");
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then switches application versions to
+// change add-ons to blocked and back. A DB schema change is faked to force a
+// rebuild when the application version changes
+add_task(function* run_app_update_schema_test() {
+ do_print("Test: " + arguments.callee.name);
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* update_schema_2() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ gAppInfo.version = "2";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* update_schema_3() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+ changeXPIDBVersion(100);
+ gAppInfo.version = "2.5";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* update_schema_4() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* update_schema_5() {
+ yield promiseShutdownManager();
+
+ changeXPIDBVersion(100);
+ gAppInfo.version = "1";
+ startupManager(true);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then loads new blocklists to change add-ons
+// to blocked and back again.
+add_task(function* run_blocklist_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield Pload_blocklist("blocklist_update1.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+
+ yield Pload_blocklist("blocklist_update2.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pload_blocklist("blocklist_update2.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ yield Pload_blocklist("blocklist_update1.xml");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons unblocked and then new versions are installed outside of
+// the app to change them to blocked and back again.
+add_task(function* run_addon_change_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield Pload_blocklist("addon_change.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "test/1.0");
+});
+
+add_task(function* run_addon_change_2() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock2_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock3_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock4_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock5_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(hardblock_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_2.id), Date.now() + 10000);
+ writeInstallRDFForExtension(regexpblock_2, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_2.id), Date.now() + 10000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* run_addon_change_3() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_3.id), Date.now() + 20000);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_3.id), Date.now() + 20000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+});
+
+add_task(function* run_addon_change_4() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_1.id), Date.now() + 30000);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_1.id), Date.now() + 30000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ do_check_eq(Services.prefs.getCharPref("general.skins.selectedSkin"), "classic/1.0");
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s5.userDisabled = false;
+});
+
+// Starts with add-ons blocked and then new versions are installed outside of
+// the app to change them to unblocked.
+add_task(function* run_addon_change_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_2, profileDir);
+ writeInstallRDFForExtension(softblock2_2, profileDir);
+ writeInstallRDFForExtension(softblock3_2, profileDir);
+ writeInstallRDFForExtension(softblock4_2, profileDir);
+ writeInstallRDFForExtension(softblock5_2, profileDir);
+ writeInstallRDFForExtension(hardblock_2, profileDir);
+ writeInstallRDFForExtension(regexpblock_2, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "2.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+});
+
+add_task(function* addon_change_2_test_2() {
+ yield promiseRestartManager();
+
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_3.id), Date.now() + 10000);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_3.id), Date.now() + 10000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+});
+
+add_task(function* addon_change_2_test_3() {
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock1_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock2_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock3_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock4_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, softblock5_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, hardblock_1.id), Date.now() + 20000);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+ setExtensionModifiedTime(getFileForAddon(profileDir, regexpblock_1.id), Date.now() + 20000);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+ s5.userDisabled = false;
+});
+
+// Add-ons are initially unblocked then attempts to upgrade to blocked versions
+// in the background which should fail
+add_task(function* run_background_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ yield Pbackground_update();
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+});
+
+// Starts with add-ons blocked and then new versions are detected and installed
+// automatically for unblocked versions.
+add_task(function* run_background_update_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_3, profileDir);
+ writeInstallRDFForExtension(softblock2_3, profileDir);
+ writeInstallRDFForExtension(softblock3_3, profileDir);
+ writeInstallRDFForExtension(softblock4_3, profileDir);
+ writeInstallRDFForExtension(softblock5_3, profileDir);
+ writeInstallRDFForExtension(hardblock_3, profileDir);
+ writeInstallRDFForExtension(regexpblock_3, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "3.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pbackground_update();
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+ s5.userDisabled = true;
+});
+
+// Starts with add-ons blocked and then simulates the user upgrading them to
+// unblocked versions.
+add_task(function* run_manual_update_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseRestartManager();
+ yield Pload_blocklist("manual_update.xml");
+ yield promiseRestartManager();
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pmanual_update("2");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s4, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s5, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ // Can't manually update to a hardblocked add-on
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ yield Pmanual_update("3");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s4, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s5, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+});
+
+// Starts with add-ons blocked and then new versions are installed outside of
+// the app to change them to unblocked.
+add_task(function* run_manual_update_2_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ writeInstallRDFForExtension(softblock1_1, profileDir);
+ writeInstallRDFForExtension(softblock2_1, profileDir);
+ writeInstallRDFForExtension(softblock3_1, profileDir);
+ writeInstallRDFForExtension(softblock4_1, profileDir);
+ writeInstallRDFForExtension(softblock5_1, profileDir);
+ writeInstallRDFForExtension(hardblock_1, profileDir);
+ writeInstallRDFForExtension(regexpblock_1, profileDir);
+
+ startupManager(false);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ s2.userDisabled = false;
+ s2.userDisabled = true;
+ check_addon(s2, "1.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ s3.userDisabled = false;
+ check_addon(s3, "1.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ yield promiseRestartManager();
+
+ yield Pmanual_update("2");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "2.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "2.0", true, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "2.0", false, false, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ // Can't manually update to a hardblocked add-on
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+
+ yield promiseRestartManager();
+
+ yield Pmanual_update("3");
+ yield promiseRestartManager();
+
+ [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s2, "3.0", true, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(s3, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(h, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ check_addon(r, "3.0", false, false, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ s1.userDisabled = false;
+ s2.userDisabled = false;
+ s4.userDisabled = true;
+});
+
+// Uses the API to install blocked add-ons from the local filesystem
+add_task(function* run_local_install_test() {
+ do_print("Test: " + arguments.callee.name + "\n");
+ yield promiseShutdownManager();
+
+ getFileForAddon(profileDir, softblock1_1.id).remove(true);
+ getFileForAddon(profileDir, softblock2_1.id).remove(true);
+ getFileForAddon(profileDir, softblock3_1.id).remove(true);
+ getFileForAddon(profileDir, softblock4_1.id).remove(true);
+ getFileForAddon(profileDir, softblock5_1.id).remove(true);
+ getFileForAddon(profileDir, hardblock_1.id).remove(true);
+ getFileForAddon(profileDir, regexpblock_1.id).remove(true);
+
+ startupManager(false);
+
+ yield promiseInstallAllFiles([
+ do_get_file("addons/blocklist_soft1_1.xpi"),
+ do_get_file("addons/blocklist_soft2_1.xpi"),
+ do_get_file("addons/blocklist_soft3_1.xpi"),
+ do_get_file("addons/blocklist_soft4_1.xpi"),
+ do_get_file("addons/blocklist_soft5_1.xpi"),
+ do_get_file("addons/blocklist_hard1_1.xpi"),
+ do_get_file("addons/blocklist_regexp1_1.xpi")
+ ]);
+
+ let aInstalls = yield new Promise((resolve, reject) => {
+ AddonManager.getAllInstalls(resolve)
+ });
+ // Should have finished all installs without needing to restart
+ do_check_eq(aInstalls.length, 0);
+
+ let [s1, s2, s3, s4, s5, h, r] = yield promiseAddonsByIDs(ADDON_IDS);
+
+ check_addon(s1, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s2, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(s3, "1.0", true, true, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+ check_addon(h, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+ check_addon(r, "1.0", false, false, Ci.nsIBlocklistService.STATE_BLOCKED);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap.js
new file mode 100644
index 000000000..ff58599bc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap.js
@@ -0,0 +1,1403 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const APP_STARTUP = 1;
+const APP_SHUTDOWN = 2;
+const ADDON_ENABLE = 3;
+const ADDON_DISABLE = 4;
+const ADDON_INSTALL = 5;
+const ADDON_UNINSTALL = 6;
+const ADDON_UPGRADE = 7;
+const ADDON_DOWNGRADE = 8;
+
+const ID1 = "bootstrap1@tests.mozilla.org";
+const ID2 = "bootstrap2@tests.mozilla.org";
+
+// This verifies that bootstrappable add-ons can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+BootstrapMonitor.init();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(undefined);
+gPort = testserver.identity.primaryPort;
+
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+function getStartupReason() {
+ let info = BootstrapMonitor.started.get(ID1);
+ return info ? info.reason : undefined;
+}
+
+function getShutdownReason() {
+ let info = BootstrapMonitor.stopped.get(ID1);
+ return info ? info.reason : undefined;
+}
+
+function getInstallReason() {
+ let info = BootstrapMonitor.installed.get(ID1);
+ return info ? info.reason : undefined;
+}
+
+function getUninstallReason() {
+ let info = BootstrapMonitor.uninstalled.get(ID1);
+ return info ? info.reason : undefined;
+}
+
+function getStartupOldVersion() {
+ let info = BootstrapMonitor.started.get(ID1);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getShutdownNewVersion() {
+ let info = BootstrapMonitor.stopped.get(ID1);
+ return info ? info.data.newVersion : undefined;
+}
+
+function getInstallOldVersion() {
+ let info = BootstrapMonitor.installed.get(ID1);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getUninstallNewVersion() {
+ let info = BootstrapMonitor.uninstalled.get(ID1);
+ return info ? info.data.newVersion : undefined;
+}
+
+function do_check_bootstrappedPref(aCallback) {
+ let data = Services.prefs.getCharPref("extensions.bootstrappedAddons");
+ data = JSON.parse(data);
+
+ AddonManager.getAddonsByTypes(["extension"], function(aAddons) {
+ for (let addon of aAddons) {
+ if (!addon.id.endsWith("@tests.mozilla.org"))
+ continue;
+ if (!addon.isActive)
+ continue;
+ if (addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE)
+ continue;
+
+ do_check_true(addon.id in data);
+ let addonData = data[addon.id];
+ delete data[addon.id];
+
+ do_check_eq(addonData.version, addon.version);
+ do_check_eq(addonData.type, addon.type);
+ let file = addon.getResourceURI().QueryInterface(Components.interfaces.nsIFileURL).file;
+ do_check_eq(addonData.descriptor, file.persistentDescriptor);
+ }
+ do_check_eq(Object.keys(data).length, 0);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+
+function run_test() {
+ do_test_pending();
+
+ startupManager();
+
+ do_check_false(gExtensionsJSON.exists());
+
+ do_check_false(gExtensionsINI.exists());
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.addon.syncGUID, null);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ let addon = install.addon;
+
+ BootstrapMonitor.promiseAddonStartup(ID1).then(function() {
+ do_check_bootstrappedPref(function() {
+ check_test_1(addon.syncGUID);
+ });
+ });
+
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+
+ // startup should not have been called yet.
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ });
+ install.install();
+ });
+}
+
+function check_test_1(installSyncGUID) {
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_neq(b1.syncGUID, null);
+ do_check_eq(b1.syncGUID, installSyncGUID);
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, ID1);
+ do_check_eq(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ prepare_test({
+ [ID1]: [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_check_bootstrappedPref(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ startupManager(false);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ do_check_bootstrappedPref(run_test_4);
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ prepare_test({
+ [ID1]: [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_ENABLE);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_check_bootstrappedPref(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ // By the time we've shut down, the database must have been written
+ do_check_true(gExtensionsJSON.exists());
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+ startupManager(false);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ do_check_bootstrappedPref(run_test_6);
+ });
+}
+
+// Tests that installing an upgrade doesn't require a restart
+function run_test_6() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_6);
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ });
+ install.install();
+ });
+}
+
+function check_test_6() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+ do_check_eq(getStartupReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), 1);
+ do_check_eq(getShutdownReason(), ADDON_UPGRADE);
+ do_check_eq(getShutdownNewVersion(), 2);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+ do_check_in_crash_annotation(ID1, "2.0");
+
+ do_check_bootstrappedPref(run_test_7);
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ prepare_test({
+ [ID1]: [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ do_check_bootstrappedPref(check_test_7);
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_not_in_crash_annotation(ID1, "2.0");
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID(ID1, function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_check_bootstrappedPref(run_test_8);
+ });
+ }));
+}
+
+// Test that a bootstrapped extension dropped into the profile loads properly
+// on startup and doesn't cause an EM restart
+function run_test_8() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ ID1);
+
+ startupManager(false);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ do_check_bootstrappedPref(run_test_9);
+ });
+}
+
+// Test that items detected as removed during startup get removed properly
+function run_test_9() {
+ shutdownManager();
+
+ manuallyUninstall(profileDir, ID1);
+ BootstrapMonitor.clear(ID1);
+
+ startupManager(false);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_eq(b1, null);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ do_check_bootstrappedPref(run_test_10);
+ });
+}
+
+
+// Tests that installing a downgrade sends the right reason
+function run_test_10() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_not_in_crash_annotation(ID1, "2.0");
+
+ BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_10_pt1);
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_print("Waiting for startup of bootstrap1_2");
+ });
+ install.install();
+ });
+}
+
+function check_test_10_pt1() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation(ID1, "2.0");
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ BootstrapMonitor.promiseAddonStartup(ID1).then(check_test_10_pt2);
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() { });
+ install.install();
+ });
+ });
+}
+
+function check_test_10_pt2() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 2);
+ do_check_eq(getStartupOldVersion(), 2);
+ do_check_eq(getShutdownReason(), ADDON_DOWNGRADE);
+ do_check_eq(getShutdownNewVersion(), 1);
+ do_check_eq(getUninstallNewVersion(), 1);
+ do_check_in_crash_annotation(ID1, "1.0");
+ do_check_not_in_crash_annotation(ID1, "2.0");
+
+ do_check_bootstrappedPref(run_test_11);
+ });
+}
+
+// Tests that uninstalling a disabled add-on still calls the uninstall method
+function run_test_11() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ prepare_test({
+ [ID1]: [
+ ["onDisabling", false],
+ "onDisabled",
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ b1.userDisabled = true;
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_eq(getShutdownReason(), ADDON_DISABLE);
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ b1.uninstall();
+
+ check_test_11();
+ });
+}
+
+function check_test_11() {
+ ensure_test_completed();
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ do_check_bootstrappedPref(run_test_12);
+}
+
+// Tests that bootstrapped extensions are correctly loaded even if the app is
+// upgraded at the same time
+function run_test_12() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ ID1);
+
+ startupManager(true);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ b1.uninstall();
+ do_execute_soon(test_12_restart);
+ });
+}
+
+function test_12_restart() {
+ restartManager();
+ do_check_bootstrappedPref(run_test_13);
+}
+
+
+// Tests that installing a bootstrapped extension with an invalid application
+// entry doesn't call it's startup method
+function run_test_13() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_3"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_not_in_crash_annotation(ID1, "3.0");
+
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+}
+
+function check_test_13() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+ BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+ do_check_not_in_crash_annotation(ID1, "3.0");
+
+ do_execute_soon(test_13_restart);
+ });
+ });
+}
+
+function test_13_restart() {
+ restartManager();
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+ BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+ do_check_not_in_crash_annotation(ID1, "3.0");
+
+ do_check_bootstrappedPref(function() {
+ b1.uninstall();
+ do_execute_soon(run_test_14);
+ });
+ });
+}
+
+// Tests that a bootstrapped extension with an invalid target application entry
+// does not get loaded when detected during startup
+function run_test_14() {
+ restartManager();
+
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_3"), profileDir,
+ ID1);
+
+ startupManager(false);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "3.0");
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "3.0"); // We call install even for disabled add-ons
+ BootstrapMonitor.checkAddonNotStarted(ID1); // Should not have called startup though
+ do_check_not_in_crash_annotation(ID1, "3.0");
+
+ do_check_bootstrappedPref(function() {
+ b1.uninstall();
+
+ run_test_15();
+ });
+ });
+}
+
+// Tests that upgrading a disabled bootstrapped extension still calls uninstall
+// and install but doesn't startup the new version
+function run_test_15() {
+ BootstrapMonitor.promiseAddonStartup(ID1).then(function test_15_after_startup() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ b1.userDisabled = true;
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_true(install.addon.userDisabled);
+
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_15));
+ install.install();
+ });
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function test_15_addon_installed() { });
+}
+
+function check_test_15() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ do_check_bootstrappedPref(function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "2.0");
+ do_check_false(b1_2.appDisabled);
+ do_check_true(b1_2.userDisabled);
+ do_check_false(b1_2.isActive);
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ b1_2.uninstall();
+
+ run_test_16();
+ }));
+ });
+ });
+}
+
+// Tests that bootstrapped extensions don't get loaded when in safe mode
+function run_test_16() {
+ BootstrapMonitor.promiseAddonStartup(ID1).then(function test_16_after_startup() {
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ do_check_eq(b1.iconURL, "chrome://foo/skin/icon.png");
+ do_check_eq(b1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(b1.optionsURL, "chrome://foo/content/options.xul");
+
+ shutdownManager();
+
+ // Should have stopped
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ gAppInfo.inSafeMode = true;
+ startupManager(false);
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
+ // Should still be stopped
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ do_check_false(b1_2.isActive);
+ do_check_eq(b1_2.iconURL, null);
+ do_check_eq(b1_2.aboutURL, null);
+ do_check_eq(b1_2.optionsURL, null);
+
+ shutdownManager();
+ gAppInfo.inSafeMode = false;
+ startupManager(false);
+
+ // Should have started
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, function(b1_3) {
+ b1_3.uninstall();
+
+ do_execute_soon(run_test_17);
+ });
+ }));
+ }));
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() { });
+}
+
+// Check that a bootstrapped extension in a non-profile location is loaded
+function run_test_17() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), userExtDir,
+ ID1);
+
+ startupManager();
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ do_check_bootstrappedPref(run_test_18);
+ });
+}
+
+// Check that installing a new bootstrapped extension in the profile replaces
+// the existing one
+function run_test_18() {
+ BootstrapMonitor.promiseAddonStartup(ID1).then(function test_18_after_startup() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ do_check_eq(getShutdownReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getStartupReason(), ADDON_UPGRADE);
+
+ do_check_eq(getShutdownNewVersion(), 2);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), 1);
+
+ do_check_bootstrappedPref(run_test_19);
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_2")], function() { });
+}
+
+// Check that uninstalling the profile version reveals the non-profile one
+function run_test_19() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // The revealed add-on gets activated asynchronously
+ prepare_test({
+ [ID1]: [
+ ["onUninstalling", false],
+ "onUninstalled",
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [], check_test_19);
+
+ b1.uninstall();
+ });
+}
+
+function check_test_19() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // Should have reverted to the older version
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ // TODO these reasons really should be ADDON_DOWNGRADE (bug 607818)
+ do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
+ do_check_eq(getUninstallReason(), ADDON_UNINSTALL);
+ do_check_eq(getInstallReason(), ADDON_INSTALL);
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_eq(getUninstallNewVersion(), undefined);
+ do_check_eq(getInstallOldVersion(), undefined);
+ do_check_eq(getStartupOldVersion(), undefined);
+
+ do_check_bootstrappedPref(run_test_20);
+ });
+}
+
+// Check that a new profile extension detected at startup replaces the non-profile
+// one
+function run_test_20() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
+ ID1);
+
+ startupManager();
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getStartupReason(), APP_STARTUP);
+
+ do_check_eq(getShutdownNewVersion(), undefined);
+ do_check_eq(getUninstallNewVersion(), 2);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupOldVersion(), undefined);
+
+ do_execute_soon(run_test_21);
+ });
+}
+
+// Check that a detected removal reveals the non-profile one
+function run_test_21() {
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), undefined);
+
+ manuallyUninstall(profileDir, ID1);
+ BootstrapMonitor.clear(ID1);
+
+ startupManager();
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ // This won't be set as the bootstrap script was gone so we couldn't
+ // uninstall it properly
+ do_check_eq(getUninstallReason(), undefined);
+ do_check_eq(getUninstallNewVersion(), undefined);
+
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 2);
+
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), undefined);
+
+ do_check_bootstrappedPref(function() {
+ shutdownManager();
+
+ manuallyUninstall(userExtDir, ID1);
+ BootstrapMonitor.clear(ID1);
+
+ startupManager(false);
+ run_test_22();
+ });
+ });
+}
+
+// Check that an upgrade from the filesystem is detected and applied correctly
+function run_test_22() {
+ shutdownManager();
+
+ let file = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir,
+ ID1);
+
+ // Make it look old so changes are detected
+ setExtensionModifiedTime(file, file.lastModifiedTime - 5000);
+
+ startupManager();
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(), undefined);
+
+ manuallyUninstall(profileDir, ID1);
+ BootstrapMonitor.clear(ID1);
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir,
+ ID1);
+
+ startupManager();
+
+ AddonManager.getAddonByID(ID1, function(b1_2) {
+ // Should have installed and started
+ BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "2.0");
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "2.0");
+ do_check_true(b1_2.isActive);
+ do_check_false(b1_2.isSystem);
+
+ // This won't be set as the bootstrap script was gone so we couldn't
+ // uninstall it properly
+ do_check_eq(getUninstallReason(), undefined);
+ do_check_eq(getUninstallNewVersion(), undefined);
+
+ do_check_eq(getInstallReason(), ADDON_UPGRADE);
+ do_check_eq(getInstallOldVersion(), 1);
+ do_check_eq(getStartupReason(), APP_STARTUP);
+ do_check_eq(getStartupOldVersion(), undefined);
+
+ do_check_bootstrappedPref(function() {
+ b1_2.uninstall();
+
+ run_test_23();
+ });
+ });
+ }));
+}
+
+
+// Tests that installing from a URL doesn't require a restart
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_bootstrap1_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], function() {
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Bootstrap 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_true(install.addon.hasResource("bootstrap.js"));
+ do_check_false(install.addon.hasResource("foo.bar"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation(ID1, "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ [ID1]: [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ do_check_bootstrappedPref(check_test_23);
+ });
+ });
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_23() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_execute_soon(function test_23_after_startup() {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ do_check_eq(getStartupReason(), ADDON_INSTALL);
+ do_check_eq(getStartupOldVersion(), undefined);
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_true(b1.hasResource("bootstrap.js"));
+ do_check_false(b1.hasResource("foo.bar"));
+ do_check_in_crash_annotation(ID1, "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, ID1);
+ do_check_eq(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 0);
+
+ restartManager();
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
+ b1_2.uninstall();
+ restartManager();
+
+ testserver.stop(run_test_24);
+ }));
+ }));
+ });
+ });
+ });
+}
+
+// Tests that we recover from a broken preference
+function run_test_24() {
+ do_print("starting 24");
+
+ Promise.all([BootstrapMonitor.promiseAddonStartup(ID2),
+ promiseInstallAllFiles([do_get_addon("test_bootstrap1_1"), do_get_addon("test_bootstrap2_1")])])
+ .then(function test_24_pref() {
+ do_print("test 24 got prefs");
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ BootstrapMonitor.checkAddonInstalled(ID2, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID2, "1.0");
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ BootstrapMonitor.checkAddonInstalled(ID2, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID2, "1.0");
+
+ shutdownManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+ BootstrapMonitor.checkAddonInstalled(ID2, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID2);
+
+ // Break the preference
+ let bootstrappedAddons = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
+ bootstrappedAddons[ID1].descriptor += "foo";
+ Services.prefs.setCharPref("extensions.bootstrappedAddons", JSON.stringify(bootstrappedAddons));
+
+ startupManager(false);
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+ BootstrapMonitor.checkAddonInstalled(ID2, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID2, "1.0");
+
+ run_test_25();
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on calls
+// the uninstall method
+function run_test_25() {
+ BootstrapMonitor.promiseAddonStartup(ID1).then(function test_25_after_pref() {
+ do_print("test 25 pref change detected");
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ installAllFiles([do_get_addon("test_bootstrap1_4")], function() {
+ // Needs a restart to complete this so the old version stays running
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallNewVersion(), 4);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "4.0");
+ do_check_true(b1_2.isActive);
+ do_check_false(b1_2.isSystem);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_26);
+ });
+ }));
+ });
+ });
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function test_25_installed() {
+ do_print("test 25 install done");
+ });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on calls
+// the install method
+function run_test_26() {
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() {
+ // Needs a restart to complete this
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "4.0");
+ do_check_true(b1.isActive);
+ do_check_false(b1.isSystem);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 4);
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ AddonManager.getAddonByID(ID1, function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "1.0");
+ do_check_true(b1_2.isActive);
+ do_check_false(b1_2.isSystem);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_27);
+ });
+ }));
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on while
+// disabled calls the uninstall method
+function run_test_27() {
+ AddonManager.getAddonByID(ID1, function(b1) {
+ do_check_neq(b1, null);
+ b1.userDisabled = true;
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.isActive);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ installAllFiles([do_get_addon("test_bootstrap1_4")], function() {
+ // Updating disabled things happens immediately
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ do_check_eq(getUninstallReason(), ADDON_UPGRADE);
+ do_check_eq(getUninstallNewVersion(), 4);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "4.0");
+ do_check_false(b1_2.isActive);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID1);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, function(b1_3) {
+ do_check_neq(b1_3, null);
+ do_check_eq(b1_3.version, "4.0");
+ do_check_false(b1_3.isActive);
+ do_check_eq(b1_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_bootstrappedPref(run_test_28);
+ });
+ }));
+ });
+ });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on when
+// disabled calls the install method but not the startup method
+function run_test_28() {
+ installAllFiles([do_get_addon("test_bootstrap1_1")], function() {
+ do_execute_soon(function bootstrap_disabled_downgrade_check() {
+ // Doesn't need a restart to complete this
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ do_check_eq(getInstallReason(), ADDON_DOWNGRADE);
+ do_check_eq(getInstallOldVersion(), 4);
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.isActive);
+ do_check_true(b1.userDisabled);
+ do_check_eq(b1.pendingOperations, AddonManager.PENDING_NONE);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID1);
+
+ AddonManager.getAddonByID(ID1, function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_true(b1_2.userDisabled);
+ b1_2.userDisabled = false;
+ do_check_eq(b1_2.version, "1.0");
+ do_check_true(b1_2.isActive);
+ do_check_false(b1_2.isSystem);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+ BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID1, "1.0");
+
+ do_check_bootstrappedPref(do_test_finished);
+ });
+ }));
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_const.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_const.js
new file mode 100644
index 000000000..101d49510
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_const.js
@@ -0,0 +1,17 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+startupManager();
+
+add_task(function*() {
+ let sawInstall = false;
+ Services.obs.addObserver(function() {
+ sawInstall = true;
+ }, "addon-install", false);
+
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap_const")]);
+
+ ok(sawInstall);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_globals.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_globals.js
new file mode 100644
index 000000000..29b538d21
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_globals.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that bootstrap.js has the expected globals defined
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+const EXPECTED_GLOBALS = [
+ ["Worker", "function"],
+ ["ChromeWorker", "function"],
+ ["console", "object"]
+];
+
+function run_test() {
+ do_test_pending();
+ startupManager();
+ let sawGlobals = false;
+
+ Services.obs.addObserver(function(subject) {
+ subject.wrappedJSObject.expectedGlobals = EXPECTED_GLOBALS;
+ }, "bootstrap-request-globals", false);
+
+ Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
+ for (let [name, ] of EXPECTED_GLOBALS)
+ do_check_true(seenGlobals.has(name));
+
+ sawGlobals = true;
+ }, "bootstrap-seen-globals", false);
+
+ installAllFiles([do_get_addon("bootstrap_globals")], function() {
+ do_check_true(sawGlobals);
+ shutdownManager();
+ do_test_finished();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_resource.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_resource.js
new file mode 100644
index 000000000..7b7883225
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_resource.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that resource protocol substitutions are set and unset for bootstrapped add-ons.
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ let resourceProtocol = Services.io.getProtocolHandler("resource")
+ .QueryInterface(Components.interfaces.nsIResProtocolHandler);
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_6")],
+ function() {
+
+ AddonManager.getAddonByID("addon6@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+ do_check_true(resourceProtocol.hasSubstitution("test-addon-1"));
+
+ prepare_test({
+ "addon6@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ addon.userDisabled = true;
+ ensure_test_completed();
+ do_check_false(resourceProtocol.hasSubstitution("test-addon-1"))
+
+ prepare_test({
+ "addon6@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ addon.userDisabled = false;
+ ensure_test_completed();
+ do_check_true(resourceProtocol.hasSubstitution("test-addon-1"));
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901.js
new file mode 100644
index 000000000..c13531dff
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+ run_next_test();
+}
+
+add_task(function* () {
+ let profileDir = OS.Constants.Path.profileDir;
+ let trashDir = OS.Path.join(profileDir, "extensions", "trash");
+ let testFile = OS.Path.join(trashDir, "test.txt");
+
+ yield OS.File.makeDir(trashDir, {
+ from: profileDir,
+ ignoreExisting: true
+ });
+
+ let trashDirExists = yield OS.File.exists(trashDir);
+ ok(trashDirExists, "trash directory should have been created");
+
+ let file = yield OS.File.open(testFile, {create: true}, {winShare: 0});
+ let fileExists = yield OS.File.exists(testFile);
+ ok(fileExists, "test.txt should have been created in " + trashDir);
+
+ yield promiseInstallAllFiles([do_get_addon("test_install1")]);
+ yield promiseRestartManager();
+ fileExists = yield OS.File.exists(testFile);
+ ok(fileExists, "test.txt still exists");
+ yield file.close();
+ yield OS.File.removeDir(OS.Path.join(OS.Constants.Path.profileDir, "extensions"));
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901_2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901_2.js
new file mode 100644
index 000000000..8e9f30ef2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901_2.js
@@ -0,0 +1,60 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+ run_next_test();
+}
+
+add_task(function* () {
+ let profileDir = OS.Constants.Path.profileDir;
+ let trashDir = OS.Path.join(profileDir, "extensions", "trash");
+ let testFile = OS.Path.join(trashDir, "test.txt");
+
+ yield OS.File.makeDir(trashDir, {
+ from: profileDir,
+ ignoreExisting: true
+ });
+
+ let trashDirExists = yield OS.File.exists(trashDir);
+ ok(trashDirExists, "trash directory should have been created");
+
+ let file = yield OS.File.open(testFile, {create: true}, {winShare: 0});
+ let fileExists = yield OS.File.exists(testFile);
+ ok(fileExists, "test.txt should have been created in " + trashDir);
+
+ let promiseInstallStatus = new Promise((resolve, reject) => {
+ let listener = {
+ onInstallFailed: function() {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: function() {
+ AddonManager.removeInstallListener(listener);
+ ok(true, "extension installation should not have failed");
+ resolve();
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")]);
+
+ // The testFile should still exist at this point because we have not
+ // yet closed the file handle and as a result, Windows cannot remove it.
+ fileExists = yield OS.File.exists(testFile);
+ ok(fileExists, "test.txt should still exist");
+
+ // Wait for the AddonManager to tell us if the installation of the extension
+ // succeeded or not.
+ yield promiseInstallStatus;
+
+ // Cleanup
+ yield promiseShutdownManager();
+ yield file.close();
+ yield OS.File.remove(testFile);
+ yield OS.File.removeDir(trashDir);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716.js
new file mode 100644
index 000000000..66656abe6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716.js
@@ -0,0 +1,208 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Update check listener.
+const checkListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function onUpdateAvailable(aAddon, aInstall) {
+ for (let currentAddon of ADDONS) {
+ if (currentAddon.id == aAddon.id) {
+ currentAddon.newInstall = aInstall;
+ return;
+ }
+ }
+ },
+
+ onUpdateFinished: function onUpdateFinished() {
+ if (--this.pendingCount == 0)
+ next_test();
+ }
+}
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var ADDONS = [
+ // XPCShell
+ {
+ id: "bug299716-a@tests.mozilla.org",
+ addon: "test_bug299716_a_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // Toolkit
+ {
+ id: "bug299716-b@tests.mozilla.org",
+ addon: "test_bug299716_b_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // XPCShell + Toolkit
+ {
+ id: "bug299716-c@tests.mozilla.org",
+ addon: "test_bug299716_c_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // XPCShell (Toolkit invalid)
+ {
+ id: "bug299716-d@tests.mozilla.org",
+ addon: "test_bug299716_d_1",
+ installed: true,
+ item: null,
+ newInstall: null
+ },
+
+ // Toolkit (XPCShell invalid)
+ {
+ id: "bug299716-e@tests.mozilla.org",
+ addon: "test_bug299716_e_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "XPCShell"
+ },
+
+ // None (XPCShell, Toolkit invalid)
+ {
+ id: "bug299716-f@tests.mozilla.org",
+ addon: "test_bug299716_f_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "XPCShell"
+ },
+
+ // None (Toolkit invalid)
+ {
+ id: "bug299716-g@tests.mozilla.org",
+ addon: "test_bug299716_g_1",
+ installed: false,
+ item: null,
+ newInstall: null,
+ failedAppName: "Toolkit"
+ },
+];
+
+var next_test = function() {};
+
+function do_check_item(aItem, aVersion, aAddonsEntry) {
+ if (aAddonsEntry.installed) {
+ if (aItem == null)
+ do_throw("Addon " + aAddonsEntry.id + " wasn't detected");
+ if (aItem.version != aVersion)
+ do_throw("Addon " + aAddonsEntry.id + " was version " + aItem.version + " instead of " + aVersion);
+ } else if (aItem != null) {
+ do_throw("Addon " + aAddonsEntry.id + " was detected");
+ }
+}
+
+/**
+ * Start the test by installing extensions.
+ */
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "5", "1.9");
+
+ const dataDir = do_get_file("data");
+ const addonsDir = do_get_addon(ADDONS[0].addon).parent;
+
+ // Make sure we can actually get our data files.
+ const xpiFile = addonsDir.clone();
+ xpiFile.append("test_bug299716_a_2.xpi");
+ do_check_true(xpiFile.exists());
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", addonsDir);
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ // Make sure we can fetch the files over HTTP.
+ const Ci = Components.interfaces;
+ const xhr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
+ .createInstance(Ci.nsIXMLHttpRequest)
+ xhr.open("GET", "http://localhost:4444/addons/test_bug299716_a_2.xpi", false);
+ xhr.send(null);
+ do_check_true(xhr.status == 200);
+
+ xhr.open("GET", "http://localhost:4444/data/test_bug299716.rdf", false);
+ xhr.send(null);
+ do_check_true(xhr.status == 200);
+
+ // Start the real test.
+ startupManager();
+ dump("\n\n*** INSTALLING NEW ITEMS\n\n");
+
+ installAllFiles(ADDONS.map(a => do_get_addon(a.addon)), run_test_pt2,
+ true);
+}
+
+/**
+ * Check the versions of all items, and ask the extension manager to find updates.
+ */
+function run_test_pt2() {
+ dump("\n\n*** DONE INSTALLING NEW ITEMS\n\n");
+ dump("\n\n*** RESTARTING EXTENSION MANAGER\n\n");
+ restartManager();
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(items) {
+ dump("\n\n*** REQUESTING UPDATE\n\n");
+ // checkListener will call run_test_pt3().
+ next_test = run_test_pt3;
+
+ // Try to update the items.
+ for (var i = 0; i < ADDONS.length; i++) {
+ var item = items[i];
+ do_check_item(item, "0.1", ADDONS[i]);
+
+ if (item) {
+ checkListener.pendingCount++;
+ ADDONS[i].item = item;
+ item.findUpdates(checkListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ }
+ }
+ });
+}
+
+/**
+ * Install new items for each enabled extension.
+ */
+function run_test_pt3() {
+ // Install the new items.
+ dump("\n\n*** UPDATING ITEMS\n\n");
+ completeAllInstalls(ADDONS.filter(a => a.newInstall).map(a => a.newInstall),
+ run_test_pt4);
+}
+
+/**
+ * Check the final version of each extension.
+ */
+function run_test_pt4() {
+ dump("\n\n*** RESTARTING EXTENSION MANAGER\n\n");
+ restartManager();
+
+ dump("\n\n*** FINAL CHECKS\n\n");
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(items) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ var item = items[i];
+ do_check_item(item, "0.2", ADDONS[i]);
+ }
+
+ testserver.stop(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716_2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716_2.js
new file mode 100644
index 000000000..c183edad4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug299716_2.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var ADDON = {
+ id: "bug299716-2@tests.mozilla.org",
+ addon: "test_bug299716_2"
+};
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9");
+
+ const dataDir = do_get_file("data");
+ const addonsDir = do_get_addon(ADDON.addon).parent;
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", addonsDir);
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ startupManager();
+
+ installAllFiles([do_get_addon(ADDON.addon)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ADDON.id, function(item) {
+ do_check_eq(item.version, 0.1);
+ do_check_false(item.isCompatible);
+
+ item.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_false(item.isCompatible);
+
+ testserver.stop(do_test_finished);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug324121.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug324121.js
new file mode 100644
index 000000000..84b6c6189
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug324121.js
@@ -0,0 +1,178 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+var next_test = null;
+var gItemsNotChecked =[];
+
+var ADDONS = [ {id: "bug324121_1@tests.mozilla.org",
+ addon: "test_bug324121_1",
+ shouldCheck: false },
+ {id: "bug324121_2@tests.mozilla.org",
+ addon: "test_bug324121_2",
+ shouldCheck: true },
+ {id: "bug324121_3@tests.mozilla.org",
+ addon: "test_bug324121_3",
+ shouldCheck: true },
+ {id: "bug324121_4@tests.mozilla.org",
+ addon: "test_bug324121_4",
+ shouldCheck: true },
+ {id: "bug324121_5@tests.mozilla.org",
+ addon: "test_bug324121_5",
+ shouldCheck: false },
+ {id: "bug324121_6@tests.mozilla.org",
+ addon: "test_bug324121_6",
+ shouldCheck: true },
+ {id: "bug324121_7@tests.mozilla.org",
+ addon: "test_bug324121_7",
+ shouldCheck: true },
+ {id: "bug324121_8@tests.mozilla.org",
+ addon: "test_bug324121_8",
+ shouldCheck: true },
+ {id: "bug324121_9@tests.mozilla.org",
+ addon: "test_bug324121_9",
+ shouldCheck: false } ];
+
+// nsIAddonUpdateCheckListener
+var updateListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function onAddonUpdateEnded(aAddon) {
+ switch (aAddon.id) {
+ // add-on disabled - should not happen
+ case "bug324121_1@tests.mozilla.org":
+ // app id already compatible - should not happen
+ case "bug324121_5@tests.mozilla.org":
+ // toolkit id already compatible - should not happen
+ case "bug324121_9@tests.mozilla.org":
+ do_throw("Should not have seen an update check for " + aAddon.id);
+ break;
+
+ // app id incompatible update available
+ case "bug324121_3@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_4@tests.mozilla.org":
+ // toolkit id incompatible update available
+ case "bug324121_7@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_8@tests.mozilla.org":
+ do_throw("Should be no update available for " + aAddon.id);
+ break;
+
+ // Updates available
+ case "bug324121_2@tests.mozilla.org":
+ case "bug324121_6@tests.mozilla.org":
+ break;
+
+ default:
+ do_throw("Update check for unknown " + aAddon.id);
+ }
+
+ // pos should always be >= 0 so just let this throw if this fails
+ var pos = gItemsNotChecked.indexOf(aAddon.id);
+ gItemsNotChecked.splice(pos, 1);
+ },
+
+ onNoUpdateAvailable: function onNoUpdateAvailable(aAddon) {
+ switch (aAddon.id) {
+ // add-on disabled - should not happen
+ case "bug324121_1@tests.mozilla.org":
+ // app id already compatible - should not happen
+ case "bug324121_5@tests.mozilla.org":
+ // toolkit id already compatible - should not happen
+ case "bug324121_9@tests.mozilla.org":
+ do_throw("Should not have seen an update check for " + aAddon.id);
+ break;
+
+ // app id incompatible update available
+ case "bug324121_3@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_4@tests.mozilla.org":
+ // toolkit id incompatible update available
+ case "bug324121_7@tests.mozilla.org":
+ // update rdf not found
+ case "bug324121_8@tests.mozilla.org":
+ break;
+
+ // Updates available
+ case "bug324121_2@tests.mozilla.org":
+ case "bug324121_6@tests.mozilla.org":
+ do_throw("Should be an update available for " + aAddon.id);
+ break;
+
+ default:
+ do_throw("Update check for unknown " + aAddon.id);
+ }
+
+ // pos should always be >= 0 so just let this throw if this fails
+ var pos = gItemsNotChecked.indexOf(aAddon.id);
+ gItemsNotChecked.splice(pos, 1);
+ },
+
+ onUpdateFinished: function onUpdateFinished(aAddon) {
+ if (--this.pendingCount == 0)
+ test_complete();
+ }
+};
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ const dataDir = do_get_file("data");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", dataDir);
+ testserver.start(4444);
+
+ startupManager();
+
+ installAllFiles(ADDONS.map(a => do_get_addon(a.addon)), function() {
+ restartManager();
+ AddonManager.getAddonByID(ADDONS[0].id, callback_soon(function(firstAddon) {
+ do_check_true(firstAddon);
+ firstAddon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonsByTypes(["extension"], function(installedItems) {
+ var items = [];
+
+ for (let addon of ADDONS) {
+ for (let installedItem of installedItems) {
+ if (addon.id != installedItem.id)
+ continue;
+ if (installedItem.userDisabled)
+ continue;
+
+ if (addon.shouldCheck == installedItem.isCompatibleWith("3", "3")) {
+ do_throw(installedItem.id + " had the wrong compatibility: " +
+ installedItem.isCompatibleWith("3", "3"));
+ }
+
+ if (addon.shouldCheck) {
+ gItemsNotChecked.push(addon.id);
+ updateListener.pendingCount++;
+ installedItem.findUpdates(updateListener,
+ AddonManager.UPDATE_WHEN_USER_REQUESTED,
+ "3", "3");
+ }
+ }
+ }
+ });
+ }));
+ });
+}
+
+function test_complete() {
+ do_check_eq(gItemsNotChecked.length, 0);
+ testserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug335238.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug335238.js
new file mode 100644
index 000000000..251bdca70
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug335238.js
@@ -0,0 +1,173 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+// This is the data we expect to see sent as part of the update url.
+var EXPECTED = [
+ {
+ id: "bug335238_1@tests.mozilla.org",
+ version: "1.3.4",
+ maxAppVersion: "5",
+ status: "userEnabled",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_2@tests.mozilla.org",
+ version: "28at",
+ maxAppVersion: "7",
+ status: "userDisabled",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_3@tests.mozilla.org",
+ version: "58",
+ maxAppVersion: "*",
+ status: "userDisabled,softblocked",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ },
+ {
+ id: "bug335238_4@tests.mozilla.org",
+ version: "4",
+ maxAppVersion: "2+",
+ status: "userEnabled,blocklisted",
+ appId: "xpcshell@tests.mozilla.org",
+ appVersion: "1",
+ appOs: "XPCShell",
+ appAbi: "noarch-spidermonkey",
+ locale: "en-US",
+ reqVersion: "2"
+ }
+];
+
+var ADDONS = [
+ {id: "bug335238_1@tests.mozilla.org",
+ addon: "test_bug335238_1"},
+ {id: "bug335238_2@tests.mozilla.org",
+ addon: "test_bug335238_2"},
+ {id: "bug335238_3@tests.mozilla.org",
+ addon: "test_bug335238_3"},
+ {id: "bug335238_4@tests.mozilla.org",
+ addon: "test_bug335238_4"}
+];
+
+// This is a replacement for the blocklist service
+var BlocklistService = {
+ getAddonBlocklistState: function(aAddon, aAppVersion, aToolkitVersion) {
+ if (aAddon.id == "bug335238_3@tests.mozilla.org")
+ return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
+ if (aAddon.id == "bug335238_4@tests.mozilla.org")
+ return Ci.nsIBlocklistService.STATE_BLOCKED;
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ getPluginBlocklistState: function(aPlugin, aVersion, aAppVersion, aToolkitVersion) {
+ return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
+ },
+
+ isAddonBlocklisted: function(aAddon, aAppVersion, aToolkitVersion) {
+ return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
+ Ci.nsIBlocklistService.STATE_BLOCKED;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIBlocklistService)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/extensions/blocklist;1", BlocklistService);
+
+var server;
+
+var updateListener = {
+ pendingCount: 0,
+
+ onUpdateAvailable: function(aAddon) {
+ do_throw("Should not have seen an update for " + aAddon.id);
+ },
+
+ onUpdateFinished: function() {
+ if (--this.pendingCount == 0)
+ server.stop(do_test_finished);
+ }
+}
+
+var requestHandler = {
+ handle: function(metadata, response)
+ {
+ var expected = EXPECTED[metadata.path.substring(1)];
+ var params = metadata.queryString.split("&");
+ do_check_eq(params.length, 10);
+ for (var k in params) {
+ var pair = params[k].split("=");
+ var name = decodeURIComponent(pair[0]);
+ var value = decodeURIComponent(pair[1]);
+ do_check_eq(expected[name], value);
+ }
+ response.setStatusLine(metadata.httpVersion, 404, "Not Found");
+ }
+}
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ server = new HttpServer();
+ server.registerPathHandler("/0", requestHandler);
+ server.registerPathHandler("/1", requestHandler);
+ server.registerPathHandler("/2", requestHandler);
+ server.registerPathHandler("/3", requestHandler);
+ server.start(4444);
+
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "en-US");
+
+ startupManager();
+ installAllFiles(ADDONS.map(a => do_get_addon(a.addon)), function() {
+
+ restartManager();
+ AddonManager.getAddonByID(ADDONS[1].id, callback_soon(function(addon) {
+ do_check_true(!(!addon));
+ addon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(installedItems) {
+ installedItems.forEach(function(item) {
+ updateListener.pendingCount++;
+ item.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+ }));
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug371495.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug371495.js
new file mode 100644
index 000000000..43656f126
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug371495.js
@@ -0,0 +1,35 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+const ADDON = "test_bug371495";
+const ID = "bug371495@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ AddonManager.getAddonByID(ID, callback_soon(function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "Test theme");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, callback_soon(function(addon2) {
+ do_check_neq(addon2, null);
+ do_check_eq(addon2.optionsURL, null);
+ do_check_eq(addon2.aboutURL, null);
+
+ do_execute_soon(do_test_finished);
+ }));
+ }));
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug384052.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug384052.js
new file mode 100644
index 000000000..aeaaf3d8f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug384052.js
@@ -0,0 +1,103 @@
+const CLASS_ID = Components.ID("{12345678-1234-1234-1234-123456789abc}");
+const CONTRACT_ID = "@mozilla.org/test-parameter-source;1";
+
+// Get and create the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+var gTestURL = "http://127.0.0.1:" + gPort + "/update.rdf?itemID=%ITEM_ID%&custom1=%CUSTOM1%&custom2=%CUSTOM2%";
+var gExpectedQuery = "itemID=test@mozilla.org&custom1=custom_parameter_1&custom2=custom_parameter_2";
+var gSeenExpectedURL = false;
+
+var gComponentRegistrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+var gCategoryManager = AM_Cc["@mozilla.org/categorymanager;1"].getService(AM_Ci.nsICategoryManager);
+
+// Factory for our parameter handler
+var paramHandlerFactory = {
+ QueryInterface: function(iid) {
+ if (iid.equals(AM_Ci.nsIFactory) || iid.equals(AM_Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ createInstance: function(outer, iid) {
+ var bag = AM_Cc["@mozilla.org/hash-property-bag;1"].
+ createInstance(AM_Ci.nsIWritablePropertyBag);
+ bag.setProperty("CUSTOM1", "custom_parameter_1");
+ bag.setProperty("CUSTOM2", "custom_parameter_2");
+ return bag.QueryInterface(iid);
+ }
+};
+
+function initTest()
+{
+ do_test_pending();
+ // Setup extension manager
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // Configure the HTTP server.
+ testserver.registerPathHandler("/update.rdf", function(aRequest, aResponse) {
+ gSeenExpectedURL = aRequest.queryString == gExpectedQuery;
+ aResponse.setStatusLine(null, 404, "Not Found");
+ });
+
+ // Register our parameter handlers
+ gComponentRegistrar.registerFactory(CLASS_ID, "Test component", CONTRACT_ID, paramHandlerFactory);
+ gCategoryManager.addCategoryEntry("extension-update-params", "CUSTOM1", CONTRACT_ID, false, false);
+ gCategoryManager.addCategoryEntry("extension-update-params", "CUSTOM2", CONTRACT_ID, false, false);
+
+ // Install a test extension into the profile
+ let dir = gProfD.clone();
+ dir.append("extensions");
+ writeInstallRDFForExtension({
+ id: "test@mozilla.org",
+ version: "1.0",
+ name: "Test extension",
+ updateURL: gTestURL,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ }, dir);
+
+ startupManager();
+}
+
+function shutdownTest()
+{
+ shutdownManager();
+
+ gComponentRegistrar.unregisterFactory(CLASS_ID, paramHandlerFactory);
+ gCategoryManager.deleteCategoryEntry("extension-update-params", "CUSTOM1", false);
+ gCategoryManager.deleteCategoryEntry("extension-update-params", "CUSTOM2", false);
+
+ do_test_finished();
+}
+
+function run_test()
+{
+ initTest();
+
+ AddonManager.getAddonByID("test@mozilla.org", function(item) {
+ // Initiate update
+ item.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_throw("Should not have seen a compatibility update");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon, error) {
+ do_check_eq(error, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
+ do_check_true(gSeenExpectedURL);
+ do_execute_soon(shutdownTest);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug393285.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug393285.js
new file mode 100644
index 000000000..ebc330cdd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug393285.js
@@ -0,0 +1,316 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var addonIDs = ["test_bug393285_1@tests.mozilla.org",
+ "test_bug393285_2@tests.mozilla.org",
+ "test_bug393285_3a@tests.mozilla.org",
+ "test_bug393285_3b@tests.mozilla.org",
+ "test_bug393285_4@tests.mozilla.org",
+ "test_bug393285_5@tests.mozilla.org",
+ "test_bug393285_6@tests.mozilla.org",
+ "test_bug393285_7@tests.mozilla.org",
+ "test_bug393285_8@tests.mozilla.org",
+ "test_bug393285_9@tests.mozilla.org",
+ "test_bug393285_10@tests.mozilla.org",
+ "test_bug393285_11@tests.mozilla.org",
+ "test_bug393285_12@tests.mozilla.org",
+ "test_bug393285_13@tests.mozilla.org",
+ "test_bug393285_14@tests.mozilla.org"];
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = args.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_1@tests.mozilla.org",
+ name: "extension 1",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_2@tests.mozilla.org",
+ name: "extension 2",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3a@tests.mozilla.org",
+ name: "extension 3a",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3b@tests.mozilla.org",
+ name: "extension 3b",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_4@tests.mozilla.org",
+ name: "extension 4",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_5@tests.mozilla.org",
+ name: "extension 5",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_6@tests.mozilla.org",
+ name: "extension 6",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_7@tests.mozilla.org",
+ name: "extension 7",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_8@tests.mozilla.org",
+ name: "extension 8",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_9@tests.mozilla.org",
+ name: "extension 9",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_10@tests.mozilla.org",
+ name: "extension 10",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_11@tests.mozilla.org",
+ name: "extension 11",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_12@tests.mozilla.org",
+ name: "extension 12",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_13@tests.mozilla.org",
+ name: "extension 13",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_14@tests.mozilla.org",
+ name: "extension 14",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+ for (let addon of addons) {
+ do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ }
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_bug393285.xml", function() {
+ restartManager();
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs(addonIDs,
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15]) {
+ // No info in blocklist, shouldn't be blocked
+ do_check_false(blocklist.isAddonBlocklisted(a1, "1", "1.9"));
+
+ // Should always be blocked
+ do_check_true(blocklist.isAddonBlocklisted(a2, "1", "1.9"));
+
+ // Only version 1 should be blocked
+ do_check_true(blocklist.isAddonBlocklisted(a3, "1", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a4, "1", "1.9"));
+
+ // Should be blocked for app version 1
+ do_check_true(blocklist.isAddonBlocklisted(a5, "1", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a5, "2", "1.9"));
+
+ // Not blocklisted because we are a different OS
+ do_check_false(blocklist.isAddonBlocklisted(a6, "2", "1.9"));
+
+ // Blocklisted based on OS
+ do_check_true(blocklist.isAddonBlocklisted(a7, "2", "1.9"));
+ do_check_true(blocklist.isAddonBlocklisted(a8, "2", "1.9"));
+
+ // Not blocklisted because we are a different ABI
+ do_check_false(blocklist.isAddonBlocklisted(a9, "2", "1.9"));
+
+ // Blocklisted based on ABI
+ do_check_true(blocklist.isAddonBlocklisted(a10, "2", "1.9"));
+ do_check_true(blocklist.isAddonBlocklisted(a11, "2", "1.9"));
+
+ // Doesnt match both os and abi so not blocked
+ do_check_false(blocklist.isAddonBlocklisted(a12, "2", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a13, "2", "1.9"));
+ do_check_false(blocklist.isAddonBlocklisted(a14, "2", "1.9"));
+
+ // Matches both os and abi so blocked
+ do_check_true(blocklist.isAddonBlocklisted(a15, "2", "1.9"));
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug394300.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug394300.js
new file mode 100644
index 000000000..bd393b91c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug394300.js
@@ -0,0 +1,56 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+// nsIAddonUpdateCheckListener implementation
+var updateListener = {
+ _count: 0,
+
+ onUpdateAvailable: function onAddonUpdateEnded(aAddon, aInstall) {
+ do_check_eq(aInstall.version, 10);
+ },
+
+ onNoUpdateAvailable: function onNoUpdateAvailable(aAddon) {
+ do_throw("Expected an available update for " + aAddon.id);
+ },
+
+ onUpdateFinished: function onUpdateFinished() {
+ if (++this._count == 2)
+ server.stop(do_test_finished);
+ },
+}
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ installAllFiles([do_get_addon("test_bug394300_1"),
+ do_get_addon("test_bug394300_2")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug394300_1@tests.mozilla.org",
+ "bug394300_2@tests.mozilla.org"], function(updates) {
+
+ do_check_neq(updates[0], null);
+ do_check_neq(updates[1], null);
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data"));
+ server.start(4444);
+
+ updates[0].findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ updates[1].findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug397778.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug397778.js
new file mode 100644
index 000000000..aa18a6946
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug397778.js
@@ -0,0 +1,117 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+const ADDON = "test_bug397778";
+const ID = "bug397778@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ AddonManager.getAddonByID(ID, callback_soon(function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr Name");
+ do_check_eq(addon.description, "fr Description");
+
+ // Disable item
+ addon.userDisabled = true;
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(newAddon) {
+ do_check_neq(newAddon, null);
+ do_check_eq(newAddon.name, "fr Name");
+
+ do_execute_soon(run_test_2);
+ });
+ }));
+}
+
+function run_test_2() {
+ // Change locale. The more specific de-DE is the best match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+function run_test_3() {
+ // Change locale. Locale case should have no effect
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "DE-de");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+function run_test_4() {
+ // Change locale. es-ES should closely match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "es-AR");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "es-ES Name");
+ do_check_eq(addon.description, "es-ES Description");
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+function run_test_5() {
+ // Change locale. Either zh-CN or zh-TW could match
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "zh");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ if (addon.name != "zh-TW Name" && addon.name != "zh-CN Name")
+ do_throw("zh matched to " + addon.name);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+function run_test_6() {
+ // Unknown locale should try to match against en-US as well. Of en,en-GB
+ // en should match as being less specific
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "nl-NL");
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "en Name");
+ do_check_eq(addon.description, "en Description");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug406118.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug406118.js
new file mode 100644
index 000000000..e22ab87c9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug406118.js
@@ -0,0 +1,155 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var addonIDs = ["test_bug393285_1@tests.mozilla.org",
+ "test_bug393285_2@tests.mozilla.org",
+ "test_bug393285_3a@tests.mozilla.org",
+ "test_bug393285_4@tests.mozilla.org"];
+
+var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = args.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function load_blocklist(aFile, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + aFile);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_1@tests.mozilla.org",
+ name: "extension 1",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_2@tests.mozilla.org",
+ name: "extension 2",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_3a@tests.mozilla.org",
+ name: "extension 3a",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "test_bug393285_4@tests.mozilla.org",
+ name: "extension 4",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+ for (let addon of addons) {
+ do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ }
+ run_test_1();
+ });
+}
+
+function run_test_1() {
+ load_blocklist("test_bug393285.xml", function() {
+ restartManager();
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs(addonIDs,
+ function([a1, a2, a3, a4]) {
+ // No info in blocklist, shouldn't be blocked
+ do_check_false(blocklist.isAddonBlocklisted(a1, null, null));
+
+ // All these should be blocklisted for the current app.
+ do_check_true(blocklist.isAddonBlocklisted(a2, null, null));
+ do_check_true(blocklist.isAddonBlocklisted(a3, null, null));
+ do_check_true(blocklist.isAddonBlocklisted(a4, null, null));
+
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug424262.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug424262.js
new file mode 100644
index 000000000..8b29e15a5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug424262.js
@@ -0,0 +1,62 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+const PREF_GETADDONS_GETRECOMMENDED = "extensions.getAddons.recommended.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+var RESULTS = [
+ null,
+ null,
+ 0,
+ 2,
+ 4,
+ 5,
+ 5,
+ 5
+];
+
+var RecommendedCallback = {
+ searchSucceeded: function(addons, length, total) {
+ dump("loaded");
+ // Search is complete
+ do_check_eq(length, RESULTS.length);
+
+ for (var i = 0; i < length; i++) {
+ if (addons[i].averageRating != RESULTS[i])
+ do_throw("Rating for " + addons[i].id + " was " + addons[i].averageRating + ", should have been " + RESULTS[i]);
+ }
+ server.stop(do_test_finished);
+ },
+
+ searchFailed: function() {
+ server.stop(do_test_finished);
+ do_throw("Recommended results failed");
+ }
+};
+
+function run_test()
+{
+ // EM needs to be running.
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ server = new HttpServer();
+ server.start(-1);
+ gPort = server.identity.primaryPort;
+ mapFile("/data/test_bug424262.xml", server);
+
+ // Point the addons repository to the test server
+ Services.prefs.setCharPref(PREF_GETADDONS_GETRECOMMENDED, "http://localhost:" +
+ gPort + "/data/test_bug424262.xml");
+
+ do_check_neq(AddonRepository, null);
+
+ do_test_pending();
+ // Pull some results.
+ AddonRepository.retrieveRecommendedAddons(RESULTS.length, RecommendedCallback);
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug425657.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug425657.js
new file mode 100644
index 000000000..f11a942fb
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug425657.js
@@ -0,0 +1,27 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const ADDON = "test_bug425657";
+const ID = "bug425657@tests.mozilla.org";
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ // Install test add-on
+ startupManager();
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "Deutsches W\u00f6rterbuch");
+ do_check_eq(addon.name.length, 20);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug430120.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug430120.js
new file mode 100644
index 000000000..e13f36a7c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug430120.js
@@ -0,0 +1,135 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const BLOCKLIST_TIMER = "blocklist-background-update-timer";
+const PREF_BLOCKLIST_URL = "extensions.blocklist.url";
+const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
+const PREF_APP_DISTRIBUTION = "distribution.id";
+const PREF_APP_DISTRIBUTION_VERSION = "distribution.version";
+const PREF_APP_UPDATE_CHANNEL = "app.update.channel";
+const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale";
+const CATEGORY_UPDATE_TIMER = "update-timer";
+
+// Get the HTTP server.
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+var testserver;
+var gOSVersion;
+var gBlocklist;
+
+// This is a replacement for the timer service so we can trigger timers
+var timerService = {
+
+ hasTimer: function(id) {
+ var catMan = Components.classes["@mozilla.org/categorymanager;1"]
+ .getService(Components.interfaces.nsICategoryManager);
+ var entries = catMan.enumerateCategory(CATEGORY_UPDATE_TIMER);
+ while (entries.hasMoreElements()) {
+ var entry = entries.getNext().QueryInterface(Components.interfaces.nsISupportsCString).data;
+ var value = catMan.getCategoryEntry(CATEGORY_UPDATE_TIMER, entry);
+ var timerID = value.split(",")[2];
+ if (id == timerID) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ fireTimer: function(id) {
+ gBlocklist.QueryInterface(Components.interfaces.nsITimerCallback).notify(null);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIUpdateTimerManager)
+ || iid.equals(Components.interfaces.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/updates/timer-manager;1", timerService);
+
+function failHandler(metadata, response) {
+ do_throw("Should not have attempted to retrieve the blocklist when it is disabled");
+}
+
+function pathHandler(metadata, response) {
+ var ABI = "noarch-spidermonkey";
+ // the blacklist service special-cases ABI for Universal binaries,
+ // so do the same here.
+ if ("@mozilla.org/xpcom/mac-utils;1" in Components.classes) {
+ var macutils = Components.classes["@mozilla.org/xpcom/mac-utils;1"]
+ .getService(Components.interfaces.nsIMacUtils);
+ if (macutils.isUniversalBinary)
+ ABI += "-u-" + macutils.architecturesInBinary;
+ }
+ do_check_eq(metadata.queryString,
+ "xpcshell@tests.mozilla.org&1&XPCShell&1&" +
+ gAppInfo.appBuildID + "&" +
+ "XPCShell_" + ABI + "&locale&updatechannel&" +
+ gOSVersion + "&1.9&distribution&distribution-version");
+ gBlocklist.observe(null, "quit-application", "");
+ gBlocklist.observe(null, "xpcom-shutdown", "");
+ testserver.stop(do_test_finished);
+}
+
+function run_test() {
+ var osVersion;
+ var sysInfo = Components.classes["@mozilla.org/system-info;1"]
+ .getService(Components.interfaces.nsIPropertyBag2);
+ try {
+ osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
+ if (osVersion) {
+ try {
+ osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
+ }
+ catch (e) {
+ }
+ gOSVersion = encodeURIComponent(osVersion);
+ }
+ }
+ catch (e) {
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ testserver = new HttpServer();
+ testserver.registerPathHandler("/1", failHandler);
+ testserver.registerPathHandler("/2", pathHandler);
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ // Initialise the blocklist service
+ gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(Components.interfaces.nsIBlocklistService)
+ .QueryInterface(Components.interfaces.nsIObserver);
+ gBlocklist.observe(null, "profile-after-change", "");
+
+ do_check_true(timerService.hasTimer(BLOCKLIST_TIMER));
+
+ do_test_pending();
+
+ // This should have no effect as the blocklist is disabled
+ Services.prefs.setCharPref(PREF_BLOCKLIST_URL, "http://localhost:" + gPort + "/1");
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, false);
+ timerService.fireTimer(BLOCKLIST_TIMER);
+
+ // Some values have to be on the default branch to work
+ var defaults = Services.prefs.QueryInterface(Components.interfaces.nsIPrefService)
+ .getDefaultBranch(null);
+ defaults.setCharPref(PREF_APP_UPDATE_CHANNEL, "updatechannel");
+ defaults.setCharPref(PREF_APP_DISTRIBUTION, "distribution");
+ defaults.setCharPref(PREF_APP_DISTRIBUTION_VERSION, "distribution-version");
+ defaults.setCharPref(PREF_GENERAL_USERAGENT_LOCALE, "locale");
+
+ // This should correctly escape everything
+ Services.prefs.setCharPref(PREF_BLOCKLIST_URL, "http://localhost:" + gPort + "/2?" +
+ "%APP_ID%&%APP_VERSION%&%PRODUCT%&%VERSION%&%BUILD_ID%&" +
+ "%BUILD_TARGET%&%LOCALE%&%CHANNEL%&" +
+ "%OS_VERSION%&%PLATFORM_VERSION%&%DISTRIBUTION%&%DISTRIBUTION_VERSION%");
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, true);
+ timerService.fireTimer(BLOCKLIST_TIMER);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug449027.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug449027.js
new file mode 100644
index 000000000..1512a7f92
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug449027.js
@@ -0,0 +1,429 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+var ADDONS = [{
+ id: "test_bug449027_1@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 1",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_2@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 2",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_3@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 3",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_4@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 4",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_5@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 5",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_6@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 6",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_7@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 7",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_8@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 8",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_9@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 9",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_10@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 10",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_11@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 11",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_12@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 12",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_13@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 13",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_14@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 14",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_15@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 15",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_16@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 16",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_17@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 17",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_18@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 18",
+ version: "5",
+ start: false,
+ appBlocks: false,
+ toolkitBlocks: false
+}, {
+ id: "test_bug449027_19@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 19",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_20@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 20",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_21@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 21",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_22@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 22",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_23@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 23",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_24@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 24",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}, {
+ id: "test_bug449027_25@tests.mozilla.org",
+ name: "Bug 449027 Addon Test 25",
+ version: "5",
+ start: false,
+ appBlocks: true,
+ toolkitBlocks: true
+}];
+
+function MockPluginTag(name, version, start, appBlocks, toolkitBlocks)
+{
+ this.name = name;
+ this.version = version;
+ this.start = start;
+ this.appBlocks = appBlocks;
+ this.toolkitBlocks = toolkitBlocks;
+}
+Object.defineProperty(MockPluginTag.prototype, "blocklisted", {
+ get: function MockPluginTag_getBlocklisted() {
+ let bls = AM_Cc["@mozilla.org/extensions/blocklist;1"].getService(Ci.nsIBlocklistService);
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ }
+});
+
+var PLUGINS = [
+ new MockPluginTag("test_bug449027_1", "5", false, false, false),
+ new MockPluginTag("test_bug449027_2", "5", false, true, false),
+ new MockPluginTag("test_bug449027_3", "5", false, true, false),
+ new MockPluginTag("test_bug449027_4", "5", false, false, false),
+ new MockPluginTag("test_bug449027_5", "5", false, false, false),
+ new MockPluginTag("test_bug449027_6", "5", false, true, false),
+ new MockPluginTag("test_bug449027_7", "5", false, true, false),
+ new MockPluginTag("test_bug449027_8", "5", false, true, false),
+ new MockPluginTag("test_bug449027_9", "5", false, true, false),
+ new MockPluginTag("test_bug449027_10", "5", false, true, false),
+ new MockPluginTag("test_bug449027_11", "5", false, true, false),
+ new MockPluginTag("test_bug449027_12", "5", false, true, false),
+ new MockPluginTag("test_bug449027_13", "5", false, true, false),
+ new MockPluginTag("test_bug449027_14", "5", false, false, false),
+ new MockPluginTag("test_bug449027_15", "5", false, true, true),
+ new MockPluginTag("test_bug449027_16", "5", false, true, true),
+ new MockPluginTag("test_bug449027_17", "5", false, false, false),
+ new MockPluginTag("test_bug449027_18", "5", false, false, false),
+ new MockPluginTag("test_bug449027_19", "5", false, true, true),
+ new MockPluginTag("test_bug449027_20", "5", false, true, true),
+ new MockPluginTag("test_bug449027_21", "5", false, true, true),
+ new MockPluginTag("test_bug449027_22", "5", false, true, true),
+ new MockPluginTag("test_bug449027_23", "5", false, true, true),
+ new MockPluginTag("test_bug449027_24", "5", false, true, true),
+ new MockPluginTag("test_bug449027_25", "5", false, true, true)
+];
+
+var gCallback = null;
+var gTestserver = null;
+var gNewBlocks = [];
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+ do_check_neq(gCallback, null);
+
+ args = args.wrappedJSObject;
+
+ gNewBlocks = [];
+ var list = args.list;
+ for (let listItem of list)
+ gNewBlocks.push(listItem.name + " " + listItem.version);
+
+ // Call the callback after the blocklist has finished up
+ do_timeout(0, gCallback);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function create_addon(addon) {
+ var installrdf = "<?xml version=\"1.0\"?>\n" +
+ "\n" +
+ "<RDF xmlns=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n" +
+ " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" +
+ " <Description about=\"urn:mozilla:install-manifest\">\n" +
+ " <em:id>" + addon.id + "</em:id>\n" +
+ " <em:version>" + addon.version + "</em:version>\n" +
+ " <em:targetApplication>\n" +
+ " <Description>\n" +
+ " <em:id>xpcshell@tests.mozilla.org</em:id>\n" +
+ " <em:minVersion>3</em:minVersion>\n" +
+ " <em:maxVersion>3</em:maxVersion>\n" +
+ " </Description>\n" +
+ " </em:targetApplication>\n" +
+ " <em:name>" + addon.name + "</em:name>\n" +
+ " </Description>\n" +
+ "</RDF>\n";
+ var target = gProfD.clone();
+ target.append("extensions");
+ target.append(addon.id);
+ target.append("install.rdf");
+ target.create(target.NORMAL_FILE_TYPE, 0o644);
+ var stream = Components.classes["@mozilla.org/network/file-output-stream;1"]
+ .createInstance(Ci.nsIFileOutputStream);
+ stream.init(target, 0x04 | 0x08 | 0x20, 0o664, 0); // write, create, truncate
+ stream.write(installrdf, installrdf.length);
+ stream.close();
+}
+
+/**
+ * Checks that items are blocklisted correctly according to the current test.
+ * If a lastTest is provided checks that the notification dialog got passed
+ * the newly blocked items compared to the previous test.
+ */
+function check_state(test, lastTest, callback) {
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ var blocked = addons[i].blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED;
+ if (blocked != ADDONS[i][test])
+ do_throw("Blocklist state did not match expected for extension " + (i + 1) + ", test " + test);
+ }
+
+ for (i = 0; i < PLUGINS.length; i++) {
+ if (PLUGINS[i].blocklisted != PLUGINS[i][test])
+ do_throw("Blocklist state did not match expected for plugin " + (i + 1) + ", test " + test);
+ }
+
+ if (lastTest) {
+ var expected = 0;
+ for (i = 0; i < ADDONS.length; i++) {
+ if (ADDONS[i][test] && !ADDONS[i][lastTest]) {
+ if (gNewBlocks.indexOf(ADDONS[i].name + " " + ADDONS[i].version) < 0)
+ do_throw("Addon " + (i + 1) + " should have been listed in the blocklist notification for test " + test);
+ expected++;
+ }
+ }
+
+ for (i = 0; i < PLUGINS.length; i++) {
+ if (PLUGINS[i][test] && !PLUGINS[i][lastTest]) {
+ if (gNewBlocks.indexOf(PLUGINS[i].name + " " + PLUGINS[i].version) < 0)
+ do_throw("Plugin " + (i + 1) + " should have been listed in the blocklist notification for test " + test);
+ expected++;
+ }
+ }
+
+ do_check_eq(expected, gNewBlocks.length);
+ }
+ do_execute_soon(callback);
+ });
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + file);
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function run_test() {
+ // Setup for test
+ dump("Setting up tests\n");
+ // Rather than keeping lots of identical add-ons in version control, just
+ // write them into the profile.
+ for (let addon of ADDONS)
+ create_addon(addon);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ gTestserver = new HttpServer();
+ gTestserver.registerDirectory("/data/", do_get_file("data"));
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ do_test_pending();
+ check_test_pt1();
+}
+
+/**
+ * Checks the initial state is correct
+ */
+function check_test_pt1() {
+ dump("Checking pt 1\n");
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ if (!addons[i])
+ do_throw("Addon " + (i + 1) + " did not get installed correctly");
+ }
+
+ do_execute_soon(function checkstate1() { check_state("start", null, run_test_pt2); });
+ });
+}
+
+/**
+ * Load the toolkit based blocks
+ */
+function run_test_pt2() {
+ dump("Running test pt 2\n");
+ gCallback = check_test_pt2;
+ load_blocklist("test_bug449027_toolkit.xml");
+}
+
+function check_test_pt2() {
+ dump("Checking pt 2\n");
+ check_state("toolkitBlocks", "start", run_test_pt3);
+}
+
+/**
+ * Load the application based blocks
+ */
+function run_test_pt3() {
+ dump("Running test pt 3\n");
+ gCallback = check_test_pt3;
+ load_blocklist("test_bug449027_app.xml");
+}
+
+function check_test_pt3() {
+ dump("Checking pt 3\n");
+ check_state("appBlocks", "toolkitBlocks", end_test);
+}
+
+function end_test() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug455906.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug455906.js
new file mode 100644
index 000000000..06e29b376
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug455906.js
@@ -0,0 +1,517 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/bug455906_warn.xml", gTestserver);
+mapFile("/data/bug455906_start.xml", gTestserver);
+mapFile("/data/bug455906_block.xml", gTestserver);
+mapFile("/data/bug455906_empty.xml", gTestserver);
+
+// Workaround for Bug 658720 - URL formatter can leak during xpcshell tests
+const PREF_BLOCKLIST_ITEM_URL = "extensions.blocklist.itemURL";
+Services.prefs.setCharPref(PREF_BLOCKLIST_ITEM_URL, "http://localhost:" + gPort + "/blocklist/%blockID%");
+
+var ADDONS = [{
+ // Tests how the blocklist affects a disabled add-on
+ id: "test_bug455906_1@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 1",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on
+ id: "test_bug455906_2@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 2",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on, to be disabled by the notification
+ id: "test_bug455906_3@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 3",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects a disabled add-on that was already warned about
+ id: "test_bug455906_4@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 4",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an enabled add-on that was already warned about
+ id: "test_bug455906_5@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 5",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an already blocked add-on
+ id: "test_bug455906_6@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 6",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Tests how the blocklist affects an incompatible add-on
+ id: "test_bug455906_7@tests.mozilla.org",
+ name: "Bug 455906 Addon Test 7",
+ version: "5",
+ appVersion: "2"
+}, {
+ // Spare add-on used to ensure we get a notification when switching lists
+ id: "dummy_bug455906_1@tests.mozilla.org",
+ name: "Dummy Addon 1",
+ version: "5",
+ appVersion: "3"
+}, {
+ // Spare add-on used to ensure we get a notification when switching lists
+ id: "dummy_bug455906_2@tests.mozilla.org",
+ name: "Dummy Addon 2",
+ version: "5",
+ appVersion: "3"
+}];
+
+function MockPlugin(name, version, enabledState) {
+ this.name = name;
+ this.version = version;
+ this.enabledState = enabledState;
+}
+Object.defineProperty(MockPlugin.prototype, "blocklisted", {
+ get: function MockPlugin_getBlocklisted() {
+ let bls = Cc["@mozilla.org/extensions/blocklist;1"].getService(Ci.nsIBlocklistService);
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ }
+});
+Object.defineProperty(MockPlugin.prototype, "disabled", {
+ get: function MockPlugin_getDisabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ }
+});
+
+var PLUGINS = [
+ // Tests how the blocklist affects a disabled plugin
+ new MockPlugin("test_bug455906_1", "5", Ci.nsIPluginTag.STATE_DISABLED),
+ // Tests how the blocklist affects an enabled plugin
+ new MockPlugin("test_bug455906_2", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects an enabled plugin, to be disabled by the notification
+ new MockPlugin("test_bug455906_3", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects a disabled plugin that was already warned about
+ new MockPlugin("test_bug455906_4", "5", Ci.nsIPluginTag.STATE_DISABLED),
+ // Tests how the blocklist affects an enabled plugin that was already warned about
+ new MockPlugin("test_bug455906_5", "5", Ci.nsIPluginTag.STATE_ENABLED),
+ // Tests how the blocklist affects an already blocked plugin
+ new MockPlugin("test_bug455906_6", "5", Ci.nsIPluginTag.STATE_ENABLED)
+];
+
+var gNotificationCheck = null;
+var gTestCheck = null;
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, windowArguments) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ if (gNotificationCheck) {
+ var args = windowArguments.wrappedJSObject;
+ gNotificationCheck(args);
+ }
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+ // Call the next test after the blocklist has finished up
+ do_timeout(0, gTestCheck);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function create_addon(addon) {
+ var installrdf = "<?xml version=\"1.0\"?>\n" +
+ "\n" +
+ "<RDF xmlns=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n" +
+ " xmlns:em=\"http://www.mozilla.org/2004/em-rdf#\">\n" +
+ " <Description about=\"urn:mozilla:install-manifest\">\n" +
+ " <em:id>" + addon.id + "</em:id>\n" +
+ " <em:version>" + addon.version + "</em:version>\n" +
+ " <em:targetApplication>\n" +
+ " <Description>\n" +
+ " <em:id>xpcshell@tests.mozilla.org</em:id>\n" +
+ " <em:minVersion>" + addon.appVersion + "</em:minVersion>\n" +
+ " <em:maxVersion>" + addon.appVersion + "</em:maxVersion>\n" +
+ " </Description>\n" +
+ " </em:targetApplication>\n" +
+ " <em:name>" + addon.name + "</em:name>\n" +
+ " </Description>\n" +
+ "</RDF>\n";
+ var target = gProfD.clone();
+ target.append("extensions");
+ target.append(addon.id);
+ target.append("install.rdf");
+ target.create(target.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ var stream = Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(Ci.nsIFileOutputStream);
+ stream.init(target,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ stream.write(installrdf, installrdf.length);
+ stream.close();
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function check_addon_state(addon) {
+ return addon.userDisabled + "," + addon.softDisabled + "," + addon.appDisabled;
+}
+
+function check_plugin_state(plugin) {
+ return plugin.disabled + "," + plugin.blocklisted;
+}
+
+function create_blocklistURL(blockID) {
+ let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
+ url = url.replace(/%blockID%/g, blockID);
+ return url;
+}
+
+// Performs the initial setup
+function run_test() {
+ // Setup for test
+ dump("Setting up tests\n");
+ // Rather than keeping lots of identical add-ons in version control, just
+ // write them into the profile.
+ for (let addon of ADDONS)
+ create_addon(addon);
+
+ // Copy the initial blocklist into the profile to check add-ons start in the
+ // right state.
+ copyBlocklistToProfile(do_get_file("data/bug455906_start.xml"));
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+ check_test_pt1();
+}
+
+// Before every main test this is the state the add-ons are meant to be in
+function check_initial_state(callback) {
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(addons) {
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,true");
+
+ callback();
+ });
+}
+
+// Tests the add-ons were installed and the initial blocklist applied as expected
+function check_test_pt1() {
+ dump("Checking pt 1\n");
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), callback_soon(function(addons) {
+ for (var i = 0; i < ADDONS.length; i++) {
+ if (!addons[i])
+ do_throw("Addon " + (i + 1) + " did not get installed correctly");
+ }
+
+ do_check_eq(check_addon_state(addons[0]), "false,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+
+ // Warn add-ons should be soft disabled automatically
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "true,true,false");
+
+ // Blocked and incompatible should be app disabled only
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+
+ // We've overridden the plugin host so we cannot tell what that would have
+ // initialised the plugins as
+
+ // Put the add-ons into the base state
+ addons[0].userDisabled = true;
+ addons[4].userDisabled = false;
+
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt2;
+ gTestCheck = check_test_pt2;
+ load_blocklist("bug455906_warn.xml");
+ });
+ }));
+}
+
+function check_notification_pt2(args) {
+ dump("Checking notification pt 2\n");
+ do_check_eq(args.list.length, 4);
+
+ for (let addon of args.list) {
+ if (addon.item instanceof Ci.nsIPluginTag) {
+ switch (addon.item.name) {
+ case "test_bug455906_2":
+ do_check_false(addon.blocked);
+ break;
+ case "test_bug455906_3":
+ do_check_false(addon.blocked);
+ addon.disable = true;
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.name);
+ }
+ }
+ else {
+ switch (addon.item.id) {
+ case "test_bug455906_2@tests.mozilla.org":
+ do_check_false(addon.blocked);
+ break;
+ case "test_bug455906_3@tests.mozilla.org":
+ do_check_false(addon.blocked);
+ addon.disable = true;
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.id);
+ }
+ }
+ }
+}
+
+function check_test_pt2() {
+ restartManager();
+ dump("Checking results pt 2\n");
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), callback_soon(function(addons) {
+ // Should have disabled this add-on as requested
+ do_check_eq(check_addon_state(addons[2]), "true,true,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "true,false");
+
+ // The blocked add-on should have changed to soft disabled
+ do_check_eq(check_addon_state(addons[5]), "true,true,false");
+ do_check_eq(check_addon_state(addons[6]), "true,true,true");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "true,false");
+
+ // These should have been unchanged
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[3]), "true,true,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+
+ // Back to starting state
+ addons[2].userDisabled = false;
+ addons[5].userDisabled = false;
+ PLUGINS[2].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ PLUGINS[5].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ restartManager();
+ gNotificationCheck = null;
+ gTestCheck = run_test_pt3;
+ load_blocklist("bug455906_start.xml");
+ }));
+}
+
+function run_test_pt3() {
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt3;
+ gTestCheck = check_test_pt3;
+ load_blocklist("bug455906_block.xml");
+ });
+}
+
+function check_notification_pt3(args) {
+ dump("Checking notification pt 3\n");
+ do_check_eq(args.list.length, 6);
+
+ for (let addon of args.list) {
+ if (addon.item instanceof Ci.nsIPluginTag) {
+ switch (addon.item.name) {
+ case "test_bug455906_2":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_3":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_5":
+ do_check_true(addon.blocked);
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.name);
+ }
+ }
+ else {
+ switch (addon.item.id) {
+ case "test_bug455906_2@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_3@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ case "test_bug455906_5@tests.mozilla.org":
+ do_check_true(addon.blocked);
+ break;
+ default:
+ do_throw("Unknown addon: " + addon.item.id);
+ }
+ }
+ }
+}
+
+function check_test_pt3() {
+ restartManager();
+ dump("Checking results pt 3\n");
+
+ let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsIBlocklistService);
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(addons) {
+ // All should have gained the blocklist state, user disabled as previously
+ do_check_eq(check_addon_state(addons[0]), "true,false,true");
+ do_check_eq(check_addon_state(addons[1]), "false,false,true");
+ do_check_eq(check_addon_state(addons[2]), "false,false,true");
+ do_check_eq(check_addon_state(addons[4]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,true");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,true");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,true");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,true");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,true");
+
+ // Should have gained the blocklist state but no longer be soft disabled
+ do_check_eq(check_addon_state(addons[3]), "false,false,true");
+
+ // Check blockIDs are correct
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[0]), create_blocklistURL(addons[0].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[1]), create_blocklistURL(addons[1].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[2]), create_blocklistURL(addons[2].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[3]), create_blocklistURL(addons[3].id));
+ do_check_eq(blocklist.getAddonBlocklistURL(addons[4]), create_blocklistURL(addons[4].id));
+
+ // All plugins have the same blockID on the test
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[0]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[1]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[2]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[3]), create_blocklistURL('test_bug455906_plugin'));
+ do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[4]), create_blocklistURL('test_bug455906_plugin'));
+
+ // Shouldn't be changed
+ do_check_eq(check_addon_state(addons[5]), "false,false,true");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,true");
+
+ // Back to starting state
+ gNotificationCheck = null;
+ gTestCheck = run_test_pt4;
+ load_blocklist("bug455906_start.xml");
+ });
+}
+
+function run_test_pt4() {
+ AddonManager.getAddonByID(ADDONS[4].id, callback_soon(function(addon) {
+ addon.userDisabled = false;
+ PLUGINS[4].enabledState = Ci.nsIPluginTag.STATE_ENABLED;
+ restartManager();
+ check_initial_state(function() {
+ gNotificationCheck = check_notification_pt4;
+ gTestCheck = check_test_pt4;
+ load_blocklist("bug455906_empty.xml");
+ });
+ }));
+}
+
+function check_notification_pt4(args) {
+ dump("Checking notification pt 4\n");
+
+ // Should be just the dummy add-on to force this notification
+ do_check_eq(args.list.length, 1);
+ do_check_false(args.list[0].item instanceof Ci.nsIPluginTag);
+ do_check_eq(args.list[0].item.id, "dummy_bug455906_2@tests.mozilla.org");
+}
+
+function check_test_pt4() {
+ restartManager();
+ dump("Checking results pt 4\n");
+
+ AddonManager.getAddonsByIDs(ADDONS.map(a => a.id), function(addons) {
+ // This should have become unblocked
+ do_check_eq(check_addon_state(addons[5]), "false,false,false");
+ do_check_eq(check_plugin_state(PLUGINS[5]), "false,false");
+
+ // Should get re-enabled
+ do_check_eq(check_addon_state(addons[3]), "false,false,false");
+
+ // No change for anything else
+ do_check_eq(check_addon_state(addons[0]), "true,false,false");
+ do_check_eq(check_addon_state(addons[1]), "false,false,false");
+ do_check_eq(check_addon_state(addons[2]), "false,false,false");
+ do_check_eq(check_addon_state(addons[4]), "false,false,false");
+ do_check_eq(check_addon_state(addons[6]), "false,false,true");
+ do_check_eq(check_plugin_state(PLUGINS[0]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[1]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[2]), "false,false");
+ do_check_eq(check_plugin_state(PLUGINS[3]), "true,false");
+ do_check_eq(check_plugin_state(PLUGINS[4]), "false,false");
+
+ finish();
+ });
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug465190.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug465190.js
new file mode 100644
index 000000000..e8e2353e2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug465190.js
@@ -0,0 +1,39 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var installLocation = gProfD.clone();
+installLocation.append("baddir");
+installLocation.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o664);
+
+var dirProvider2 = {
+ getFile: function(prop, persistent) {
+ persistent.value = true;
+ if (prop == "XREUSysExt")
+ return installLocation.clone();
+ return null;
+ },
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIDirectoryServiceProvider) ||
+ iid.equals(Components.interfaces.nsISupports)) {
+ return this;
+ }
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+};
+Services.dirsvc.QueryInterface(Components.interfaces.nsIDirectoryService)
+ .registerProvider(dirProvider2);
+
+function run_test()
+{
+ var log = gProfD.clone();
+ log.append("extensions.log");
+ do_check_false(log.exists());
+
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1");
+
+ startupManager();
+ do_check_false(log.exists());
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug468528.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug468528.js
new file mode 100644
index 000000000..01c976a17
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug468528.js
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const nsIBLS = Components.interfaces.nsIBlocklistService;
+
+var PLUGINS = [{
+ // Normal blacklisted plugin, before an invalid regexp
+ name: "test_bug468528_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Normal blacklisted plugin, with an invalid regexp
+ name: "test_bug468528_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Normal blacklisted plugin, after an invalid regexp
+ name: "test_bug468528_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // Non-blocklisted plugin
+ name: "test_bug468528_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}];
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // We cannot force the blocklist to update so just copy our test list to the profile
+ copyBlocklistToProfile(do_get_file("data/test_bug468528.xml"));
+
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+ .getService(nsIBLS);
+
+ // blocked (sanity check)
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // not blocked - won't match due to invalid regexp
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ // blocked - the invalid regexp for the previous item shouldn't affect this one
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // not blocked - the previous invalid regexp shouldn't act as a wildcard
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1.js
new file mode 100644
index 000000000..920c3731a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1.js
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+
+ installAllFiles(ADDONS.map(a => do_get_addon(a)), function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+ do_check_neq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1_strictcompat.js
new file mode 100644
index 000000000..6fa11eb39
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1_strictcompat.js
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+
+ installAllFiles(ADDONS.map(a => do_get_addon(a)), function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+ do_check_eq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_2.js
new file mode 100644
index 000000000..0c912ceff
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_2.js
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var ADDONS = [
+ "test_bug470377_1",
+ "test_bug470377_2",
+ "test_bug470377_3",
+ "test_bug470377_4",
+ "test_bug470377_5",
+];
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data/test_bug470377"));
+ server.start(-1);
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles(ADDONS.map(a => do_get_addon(a)), function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+ do_check_neq(a3, null);
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+
+ server.stop(do_test_finished);
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3.js
new file mode 100644
index 000000000..0d1d30f3b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3.js
@@ -0,0 +1,95 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+
+ // inject the add-ons into the profile
+ var dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3_strictcompat.js
new file mode 100644
index 000000000..100ea99d7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3_strictcompat.js
@@ -0,0 +1,94 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ // inject the add-ons into the profile
+ var dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = gProfD.clone();
+ dest.append("extensions");
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_4.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_4.js
new file mode 100644
index 000000000..c51c38b0c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_4.js
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.1a4", "2");
+
+ // inject the add-ons into the profile
+ var profileDir = gProfD.clone();
+ profileDir.append("extensions");
+ var dest = profileDir.clone();
+ dest.append("bug470377_1@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ var source = do_get_file("data/test_bug470377/install_1.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_2@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_2.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_3@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_3.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_4@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_4.rdf");
+ source.copyTo(dest, "install.rdf");
+ dest = profileDir.clone();
+ dest.append("bug470377_5@tests.mozilla.org");
+ dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o755);
+ source = do_get_file("data/test_bug470377/install_5.rdf");
+ source.copyTo(dest, "install.rdf");
+
+ run_test_1();
+}
+
+function run_test_1() {
+ startupManager();
+ AddonManager.checkCompatibility = false;
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ AddonManager.checkCompatibility = true;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug470377_1@tests.mozilla.org",
+ "bug470377_2@tests.mozilla.org",
+ "bug470377_3@tests.mozilla.org",
+ "bug470377_4@tests.mozilla.org",
+ "bug470377_5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_1.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_1.js
new file mode 100644
index 000000000..46b65ffff
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_1.js
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+const nsIBLS = Ci.nsIBlocklistService;
+
+var PLUGINS = [{
+ // blocklisted - default severity
+ name: "test_bug514327_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // outdated - severity of "0"
+ name: "test_bug514327_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // outdated - severity of "0"
+ name: "test_bug514327_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // not blocklisted, not outdated
+ name: "test_bug514327_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false,
+ outdated: false
+}];
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_1.xml"));
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ // blocked (sanity check)
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ // outdated
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // outdated
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // not blocked
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_2.js
new file mode 100644
index 000000000..261739da4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_2.js
@@ -0,0 +1,41 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+const nsIBLS = Ci.nsIBlocklistService;
+
+// Finds the test nsIPluginTag
+function get_test_plugintag() {
+ var host = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
+ var tags = host.getPluginTags();
+ for (let tag of tags) {
+ if (tag.name == "Test Plug-in")
+ return tag;
+ }
+ return null;
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_2.xml"));
+
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+ var prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+
+ prefs.setBoolPref("plugin.load_flash_only", false);
+
+ var plugin = get_test_plugintag();
+ if (!plugin)
+ do_throw("Plugin tag not found");
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+ do_execute_soon(function() {
+ // should be marked as outdated by the blocklist
+ do_check_true(blocklist.getPluginBlocklistState(plugin, "1", "1.9") == nsIBLS.STATE_OUTDATED);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_3.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_3.js
new file mode 100644
index 000000000..634361991
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_3.js
@@ -0,0 +1,139 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://testing-common/httpd.js");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+const nsIBLS = Ci.nsIBlocklistService;
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+var gBlocklist = null;
+var gPrefs = null;
+var gTestserver = null;
+
+var gNextTestPart = null;
+
+
+var PLUGINS = [{
+ // Tests a plugin whose state goes from not-blocked, to outdated
+ name: "test_bug514327_outdated",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}, {
+ // Used to trigger the blocklist dialog, which indicates the blocklist has updated
+ name: "test_bug514327_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}, {
+ // Used to trigger the blocklist dialog, which indicates the blocklist has updated
+ name: "test_bug514327_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+} ];
+
+
+// A fake plugin host for the blocklist service to use
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIPluginHost)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+ // Should only include one item
+ do_check_eq(args.wrappedJSObject.list.length, 1);
+ // And that item should be the blocked plugin, not the outdated one
+ var item = args.wrappedJSObject.list[0];
+ do_check_true(item.item instanceof Ci.nsIPluginTag);
+ do_check_neq(item.name, "test_bug514327_outdated");
+
+ // Call the next test after the blocklist has finished up
+ do_timeout(0, gNextTestPart);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+
+function do_update_blocklist(aDatafile, aNextPart) {
+ gNextTestPart = aNextPart;
+
+ gPrefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/" + aDatafile);
+ gBlocklist.QueryInterface(Ci.nsITimerCallback).notify(null);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ gTestserver = new HttpServer();
+ gTestserver.registerDirectory("/data/", do_get_file("data"));
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ startupManager();
+
+ // initialize the blocklist with no entries
+ copyBlocklistToProfile(do_get_file("data/test_bug514327_3_empty.xml"));
+
+ gPrefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+ gBlocklist = Cc["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ // should NOT be marked as outdated by the blocklist
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ do_test_pending();
+
+ // update blocklist with data that marks the plugin as outdated
+ do_update_blocklist("test_bug514327_3_outdated_1.xml", test_part_1);
+}
+
+function test_part_1() {
+ // plugin should now be marked as outdated
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ // update blocklist with data that marks the plugin as outdated
+ do_update_blocklist("test_bug514327_3_outdated_2.xml", test_part_2);
+}
+
+function test_part_2() {
+ // plugin should still be marked as outdated
+ do_check_true(gBlocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ finish();
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug521905.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug521905.js
new file mode 100644
index 000000000..b507fc100
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug521905.js
@@ -0,0 +1,59 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const ADDON = "test_bug521905";
+const ID = "bug521905@tests.mozilla.org";
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+function run_test() {
+ // This test is only relevant on builds where the version is included in the
+ // checkCompatibility preference name
+ if (isNightlyChannel()) {
+ return;
+ }
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.0pre", "2");
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles([do_get_addon(ADDON)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ do_execute_soon(run_test_1);
+ });
+ });
+}
+
+function run_test_1() {
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.0pre", true);
+
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_false(addon.isActive);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.0p", false);
+
+ restartManager();
+ AddonManager.getAddonByID(ID, function(addon) {
+ do_check_neq(addon, null);
+ do_check_false(addon.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug526598.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug526598.js
new file mode 100644
index 000000000..debf59172
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug526598.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ installAllFiles([do_get_file("data/test_bug526598_1.xpi"),
+ do_get_file("data/test_bug526598_2.xpi")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug526598_1@tests.mozilla.org",
+ "bug526598_2@tests.mozilla.org"],
+ callback_soon(function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_true(a1.hasResource("install.rdf"));
+ let uri = a1.getResourceURI("install.rdf");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ let file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ do_check_neq(a2, null);
+ do_check_true(a2.hasResource("install.rdf"));
+ uri = a2.getResourceURI("install.rdf");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ a1.uninstall();
+ a2.uninstall();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["bug526598_1@tests.mozilla.org",
+ "bug526598_2@tests.mozilla.org"],
+ function([newa1, newa2]) {
+ do_check_eq(newa1, null);
+ do_check_eq(newa2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug541420.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug541420.js
new file mode 100644
index 000000000..b7af5453f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug541420.js
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ installAllFiles([do_get_file("data/test_bug541420.xpi")], function() {
+
+ restartManager();
+
+ AddonManager.getAddonByID("bug541420@tests.mozilla.org", function(addon) {
+
+ do_check_neq(addon, null);
+ do_check_true(addon.hasResource("binary"));
+ let uri = addon.getResourceURI("binary");
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ let file = uri.file;
+ do_check_true(file.exists());
+ do_check_true(file.isReadable());
+ do_check_true(file.isWritable());
+
+ // We don't understand executable permissions on Windows since we don't
+ // support NTFS permissions so we don't need to test there. OSX's isExecutable
+ // only tests if the file is an application so it is better to just check the
+ // raw permission bits
+ if (!("nsIWindowsRegKey" in Components.interfaces))
+ do_check_true((file.permissions & 0o100) == 0o100);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug542391.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug542391.js
new file mode 100644
index 000000000..aa1bbd53f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug542391.js
@@ -0,0 +1,464 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gInstallUpdate = false;
+var gCheckUpdates = false;
+
+// This will be called to show the compatibility update dialog.
+var WindowWatcher = {
+ expected: false,
+ args: null,
+
+ openWindow: function(parent, url, name, features, args) {
+ do_check_true(Services.startup.interrupted);
+ do_check_eq(url, URI_EXTENSION_UPDATE_DIALOG);
+ do_check_true(this.expected);
+ this.expected = false;
+ this.args = args.QueryInterface(AM_Ci.nsIVariant);
+
+ var updated = !gCheckUpdates;
+ if (gCheckUpdates) {
+ AddonManager.getAddonByID("override1x2-1x3@tests.mozilla.org", function(a6) {
+ a6.findUpdates({
+ onUpdateFinished: function() {
+ AddonManagerPrivate.removeStartupChange("disabled", "override1x2-1x3@tests.mozilla.org");
+ updated = true;
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }
+
+ var installed = !gInstallUpdate;
+ if (gInstallUpdate) {
+ // Simulate installing an update while in the dialog
+ installAllFiles([do_get_addon("upgradeable1x2-3_2")], function() {
+ AddonManagerPrivate.removeStartupChange("disabled", "upgradeable1x2-3@tests.mozilla.org");
+ AddonManagerPrivate.addStartupChange("updated", "upgradeable1x2-3@tests.mozilla.org");
+ installed = true;
+ });
+ }
+
+ // The dialog is meant to be opened modally and the install operation can be
+ // asynchronous, so we must spin an event loop (like the modal window does)
+ // until the install is complete
+ let thr = AM_Cc["@mozilla.org/thread-manager;1"].
+ getService(AM_Ci.nsIThreadManager).
+ mainThread;
+
+ while (!installed || !updated)
+ thr.processNextEvent(false);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function check_state_v1([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v1_2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "2.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v3([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "1.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+function check_state_v3_2([a1, a2, a3, a4, a5, a6]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_eq(a3.version, "2.0");
+
+ do_check_neq(a4, null);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.appDisabled);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.userDisabled);
+ do_check_true(a6.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+}
+
+// Install all the test add-ons, disable two of them and "upgrade" the app to
+// version 2 which will appDisable one.
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+
+ // Add an extension to the profile to make sure the dialog doesn't show up
+ // on new profiles
+ var dest = writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ // Create and configure the HTTP server.
+ testserver = createHttpServer(4444);
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+ startupManager();
+
+ // Remove the add-on we installed directly in the profile directory;
+ // this should show as uninstalled on next restart
+ dest.remove(true);
+
+ // Load up an initial set of add-ons
+ yield promiseInstallAllFiles([do_get_addon("min1max1"),
+ do_get_addon("min1max2"),
+ do_get_addon("upgradeable1x2-3_1"),
+ do_get_addon("min1max3"),
+ do_get_addon("min1max3b"),
+ do_get_addon("override1x2-1x3")]);
+ yield promiseRestartManager();
+
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", ["addon1@tests.mozilla.org"]);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ // user-disable two add-ons
+ let [a2, a4] = yield promiseAddonsByIDs(["min1max2@tests.mozilla.org",
+ "min1max3@tests.mozilla.org"]);
+ do_check_true(a2 != null && a4 != null);
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ yield promiseRestartManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v1(addons);
+
+ // Restart as version 2, add-on _1 should become app-disabled
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["min1max1@tests.mozilla.org"]);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+
+ addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v2(addons);
+});
+
+// Upgrade to version 3 which will appDisable addons
+// upgradeable1x2-3 and override1x2-1x3
+// Only the newly disabled add-ons should be passed to the
+// upgrade window
+add_task(function* run_test_1() {
+ gCheckUpdates = true;
+ WindowWatcher.expected = true;
+
+ yield promiseRestartManager("3");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+ gCheckUpdates = false;
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v3(addons);
+
+ do_check_eq(WindowWatcher.args.length, 2);
+ do_check_true(WindowWatcher.args.indexOf("upgradeable1x2-3@tests.mozilla.org") >= 0);
+ do_check_true(WindowWatcher.args.indexOf("override1x2-1x3@tests.mozilla.org") >= 0);
+});
+
+// Downgrade to version 2 which will remove appDisable from two add-ons
+// Still displays the compat window, because metadata is not recently updated
+add_task(function* run_test_2() {
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ do_check_false(WindowWatcher.expected);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v2(addons);
+});
+
+// Upgrade back to version 3 which should only appDisable
+// upgradeable1x2-3, because we already have the override
+// stored in our DB for override1x2-1x3. Ensure that when
+// the upgrade dialog updates an add-on no restart is necessary
+add_task(function* run_test_5() {
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+ // tell the mock compatibility window to install the available upgrade
+ gInstallUpdate = true;
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("3");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+ do_check_false(WindowWatcher.expected);
+ gInstallUpdate = false;
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v3_2(addons);
+
+ do_check_eq(WindowWatcher.args.length, 1);
+ do_check_true(WindowWatcher.args.indexOf("upgradeable1x2-3@tests.mozilla.org") >= 0);
+});
+
+// Downgrade to version 1 which will appEnable all the add-ons
+// except upgradeable1x2-3; the update we installed isn't compatible with 1
+add_task(function* run_test_6() {
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("1");
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", ["upgradeable1x2-3@tests.mozilla.org"]);
+ check_startup_changes("enabled", ["min1max1@tests.mozilla.org"]);
+ do_check_false(WindowWatcher.expected);
+
+ let addons = yield promiseAddonsByIDs(["min1max1@tests.mozilla.org",
+ "min1max2@tests.mozilla.org",
+ "upgradeable1x2-3@tests.mozilla.org",
+ "min1max3@tests.mozilla.org",
+ "min1max3b@tests.mozilla.org",
+ "override1x2-1x3@tests.mozilla.org"]);
+ check_state_v1_2(addons);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug554133.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug554133.js
new file mode 100644
index 000000000..c252e1ced
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug554133.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that if the AMO response provides total_results,
+// searchSucceeded is called with the correct number of total results
+
+Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+const PREF_GETADDONS_GETSEARCHRESULTS = "extensions.getAddons.search.url";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var server;
+
+var TESTS = [
+{
+ query: "bug554133",
+ maxResults: 2,
+ length: 2,
+ total: 100
+},
+{
+ query: "bug554133",
+ maxResults: 10,
+ length: 10,
+ total: 100
+},
+{
+ query: "bug554133",
+ maxResults: 100,
+ length: 10,
+ total: 100
+}
+];
+
+var gCurrentTest = 0;
+var SearchCallback = {
+ searchSucceeded: function(addons, length, total) {
+ do_check_false(AddonRepository.isSearching);
+ do_check_eq(addons.length, length);
+ do_check_eq(length, TESTS[gCurrentTest].length);
+ do_check_eq(total, TESTS[gCurrentTest].total);
+
+ gCurrentTest++;
+ run_current_test();
+ },
+
+ searchFailed: function() {
+ server.stop(do_test_finished);
+ do_throw("Search results failed");
+ }
+};
+
+function run_current_test() {
+ if (gCurrentTest < TESTS.length) {
+ var query = TESTS[gCurrentTest].query;
+ var maxResults = TESTS[gCurrentTest].maxResults;
+ AddonRepository.searchAddons(query, maxResults, SearchCallback);
+ }
+ else
+ server.stop(do_test_finished);
+}
+
+function run_test()
+{
+ // Setup for test
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ startupManager();
+
+ server = new HttpServer();
+ server.registerDirectory("/", do_get_file("data"));
+ mapFile("/data/test_bug554133.xml", server);
+ server.start(-1);
+ gPort = server.identity.primaryPort;
+
+ // Point search to the test server
+ Services.prefs.setCharPref(PREF_GETADDONS_GETSEARCHRESULTS,
+ "http://localhost:" + gPort + "/data/test_%TERMS%.xml");
+
+ do_check_neq(AddonRepository, null);
+ gCurrentTest = 0;
+ run_current_test();
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug559800.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug559800.js
new file mode 100644
index 000000000..866ad3ad7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug559800.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that deleting the database from the profile doesn't break
+// anything
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// getting an unused port
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ startupManager();
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ shutdownManager();
+
+ gExtensionsJSON.remove(true);
+
+ do_execute_soon(check_test_1);
+ }));
+}
+
+function check_test_1() {
+ startupManager(false);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ // due to delayed write, the file may not exist until
+ // after shutdown
+ shutdownManager();
+ do_check_true(gExtensionsJSON.exists());
+ do_check_true(gExtensionsJSON.fileSize > 0);
+
+ end_test();
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug563256.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug563256.js
new file mode 100644
index 000000000..497e66526
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug563256.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the themes switch as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "alternate@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ internalName: "alternate/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d, a]) {
+ do_check_neq(d, null);
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a, null);
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ run_test_1(d, a);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks switching to a different theme and back again leaves everything the
+// same
+function run_test_1(d, a) {
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_execute_soon(run_test_2);
+}
+
+// Tests that after the restart themes can be changed as expected
+function run_test_2() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d, a]) {
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_check_neq(d, null);
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a, null);
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ a.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ do_execute_soon(check_test_2);
+ });
+}
+
+function check_test_2() {
+ restartManager();
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], callback_soon(function([d, a]) {
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "alternate/1.0");
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_false(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_true(a.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ d.userDisabled = false;
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, d.id));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(a.userDisabled);
+ do_check_false(a.appDisabled);
+ do_check_true(a.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, a.id));
+ do_check_false(hasFlag(a.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "alternate/1.0");
+
+ restartManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "alternate@tests.mozilla.org"], function([d2, a2]) {
+ do_check_neq(d2, null);
+ do_check_false(d2.userDisabled);
+ do_check_false(d2.appDisabled);
+ do_check_true(d2.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, d2.id));
+ do_check_false(hasFlag(d2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(d2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug564030.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug564030.js
new file mode 100644
index 000000000..92ba3d68f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug564030.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that upgrading an incompatible add-on to a compatible one forces an
+// EM restart
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9.2");
+
+ var dest = writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }, profileDir);
+ // Attempt to make this look like it was added some time in the past so
+ // the update makes the last modified time change.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a) {
+ do_check_neq(a, null);
+ do_check_eq(a.version, "1.0");
+ do_check_false(a.userDisabled);
+ do_check_true(a.appDisabled);
+ do_check_false(a.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a.id));
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug566626.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug566626.js
new file mode 100644
index 000000000..641ff87c9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug566626.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that multiple calls to the async API return fully formed
+// add-ons
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gAddon;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Verifies that multiple calls to get an add-on at various stages of execution
+// return an add-on with a valid name.
+function run_test_1() {
+ var count = 0;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+
+ do_execute_soon(function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+ });
+
+ do_execute_soon(function() {
+ do_execute_soon(function() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ if (count == 0)
+ gAddon = a1;
+ else
+ do_check_eq(a1, gAddon);
+ count++;
+ if (count == 4)
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Verifies that a subsequent call gets the same add-on from the cache
+function run_test_2() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.name, "Test 1");
+
+ do_check_eq(a1, gAddon);
+
+ do_execute_soon(do_test_finished);
+ });
+
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug567184.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug567184.js
new file mode 100644
index 000000000..0e7863068
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug567184.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug567184"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug567184@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_1);
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("bug567184@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_true(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug569138.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug569138.js
new file mode 100644
index 000000000..4869fc117
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug569138.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons with invalid target application entries show
+// up in the API but are correctly appDisabled
+
+// A working add-on
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Missing id
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+// Missing minVersion
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ maxVersion: "1"
+ }]
+};
+
+// Missing maxVersion
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1"
+ }]
+};
+
+// Blank id
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+// Blank minVersion
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "",
+ maxVersion: "1"
+ }]
+};
+
+// Blank maxVersion
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: ""
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.appDisabled);
+ do_check_false(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+
+ do_execute_soon(do_test_finished);
+
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug570173.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug570173.js
new file mode 100644
index 000000000..2c87d8c79
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug570173.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update check failures are propogated correctly
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var testserver;
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Create and configure the HTTP server.
+ testserver = createHttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ gPort = testserver.identity.primaryPort;
+
+ run_next_test();
+}
+
+// Verify that an update check returns the correct errors.
+add_task(function* () {
+ for (let manifestType of ["rdf", "json"]) {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: `http://localhost:${gPort}/data/test_missing.${manifestType}`,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ bootstrap: "true",
+ }, profileDir);
+
+ yield promiseRestartManager();
+
+ let addon = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ ok(addon);
+ ok(addon.updateURL.endsWith(manifestType));
+ equal(addon.version, "1.0");
+
+ // We're expecting an error, so resolve when the promise is rejected.
+ let update = yield promiseFindAddonUpdates(addon, AddonManager.UPDATE_WHEN_USER_REQUESTED)
+ .catch(Promise.resolve);
+
+ ok(!update.compatibilityUpdate, "not expecting a compatibility update");
+ ok(!update.updateAvailable, "not expecting a compatibility update");
+
+ equal(update.error, AddonManager.UPDATE_STATUS_DOWNLOAD_ERROR);
+
+ addon.uninstall();
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug576735.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug576735.js
new file mode 100644
index 000000000..df64e159d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug576735.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that we recover gracefully from an extension directory disappearing
+// when we were expecting to uninstall it.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.uninstall();
+
+ shutdownManager();
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+
+ writeInstallRDFForExtension(addon2, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1_2, a2_2]) {
+ // Addon1 should no longer be installed
+ do_check_eq(a1_2, null);
+
+ // Addon2 should have been detected
+ do_check_neq(a2_2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug587088.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug587088.js
new file mode 100644
index 000000000..8d9857e7f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug587088.js
@@ -0,0 +1,174 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that trying to upgrade or uninstall an extension that has a file locked
+// will roll back the upgrade or uninstall and retry at the next restart
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ // This is only an issue on windows.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ run_test_1();
+}
+
+function check_addon(aAddon, aVersion) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, aVersion);
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+
+ do_check_true(aAddon.hasResource("testfile"));
+ if (aVersion == "1.0") {
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+ }
+ else {
+ do_check_false(aAddon.hasResource("testfile1"));
+ do_check_true(aAddon.hasResource("testfile2"));
+ }
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_NONE);
+}
+
+function check_addon_upgrading(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+
+ do_check_true(aAddon.hasResource("testfile"));
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_UPGRADE);
+
+ do_check_eq(aAddon.pendingUpgrade.version, "2.0");
+}
+
+function check_addon_uninstalling(aAddon, aAfterRestart) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+
+ if (aAfterRestart) {
+ do_check_false(aAddon.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, aAddon.id));
+ }
+ else {
+ do_check_true(aAddon.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, aAddon.id));
+ }
+
+ do_check_true(aAddon.hasResource("testfile"));
+ do_check_true(aAddon.hasResource("testfile1"));
+ do_check_false(aAddon.hasResource("testfile2"));
+
+ do_check_eq(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL);
+}
+
+function run_test_1() {
+ installAllFiles([do_get_addon("test_bug587088_1")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ check_addon(a1, "1.0");
+
+ // Lock either install.rdf for unpacked add-ons or the xpi for packed add-ons.
+ let uri = a1.getResourceURI("install.rdf");
+ if (uri.schemeIs("jar"))
+ uri = a1.getResourceURI();
+
+ let fstream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fstream.init(uri.QueryInterface(AM_Ci.nsIFileURL).file, -1, 0, 0);
+
+ installAllFiles([do_get_addon("test_bug587088_2")], function() {
+
+ check_addon_upgrading(a1);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1_2) {
+ check_addon_upgrading(a1_2);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1_3) {
+ check_addon_upgrading(a1_3);
+
+ fstream.close();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_4) {
+ check_addon(a1_4, "2.0");
+
+ a1_4.uninstall();
+ do_execute_soon(run_test_2);
+ });
+ }));
+ }));
+ });
+ });
+ });
+}
+
+// Test that a failed uninstall gets rolled back
+function run_test_2() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_bug587088_1")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ check_addon(a1, "1.0");
+
+ // Lock either install.rdf for unpacked add-ons or the xpi for packed add-ons.
+ let uri = a1.getResourceURI("install.rdf");
+ if (uri.schemeIs("jar"))
+ uri = a1.getResourceURI();
+
+ let fstream = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ fstream.init(uri.QueryInterface(AM_Ci.nsIFileURL).file, -1, 0, 0);
+
+ a1.uninstall();
+
+ check_addon_uninstalling(a1);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1_2) {
+ check_addon_uninstalling(a1_2, true);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1_3) {
+ check_addon_uninstalling(a1_3, true);
+
+ fstream.close();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_4) {
+ do_check_eq(a1_4, null);
+ var dir = profileDir.clone();
+ dir.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dir.exists());
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ }));
+ }));
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug594058.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug594058.js
new file mode 100644
index 000000000..858579815
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug594058.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests is modifying a file in an unpacked extension
+// causes cache invalidation.
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Allow the mismatch UI to show
+Services.prefs.setBoolPref("extensions.showMismatchUI", true);
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+var Ci = Components.interfaces;
+const extDir = gProfD.clone();
+extDir.append("extensions");
+
+var gCachePurged = false;
+
+// Override the window watcher
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, args) {
+ do_check_false(gCachePurged);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+/**
+ * Start the test by installing extensions.
+ */
+function run_test() {
+ do_test_pending();
+ gCachePurged = false;
+
+ let obs = AM_Cc["@mozilla.org/observer-service;1"].
+ getService(AM_Ci.nsIObserverService);
+ obs.addObserver({
+ observe: function(aSubject, aTopic, aData) {
+ gCachePurged = true;
+ }
+ }, "startupcache-invalidate", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+ // nsAppRunner takes care of clearing this when a new app is installed
+ do_check_false(gCachePurged);
+
+ installAllFiles([do_get_addon("test_bug594058")], function() {
+ restartManager();
+ do_check_true(gCachePurged);
+ gCachePurged = false;
+
+ // Now, make it look like we've updated the file. First, start the EM
+ // so it records the bogus old time, then update the file and restart.
+ let extFile = extDir.clone();
+ let pastTime = extFile.lastModifiedTime - 5000;
+ extFile.append("bug594058@tests.mozilla.org");
+ setExtensionModifiedTime(extFile, pastTime);
+ let otherFile = extFile.clone();
+ otherFile.append("directory");
+ otherFile.lastModifiedTime = pastTime;
+ otherFile.append("file1");
+ otherFile.lastModifiedTime = pastTime;
+
+ restartManager();
+ gCachePurged = false;
+
+ otherFile.lastModifiedTime = pastTime + 5000;
+ restartManager();
+ do_check_true(gCachePurged);
+ gCachePurged = false;
+
+ restartManager();
+ do_check_false(gCachePurged);
+
+ do_test_finished();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug595081.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug595081.js
new file mode 100644
index 000000000..db53dc747
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug595081.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that the AddonManager objects cannot be tampered with
+
+function run_test() {
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ // Verify that properties cannot be changed
+ let old = AddonManager.STATE_AVAILABLE;
+ AddonManager.STATE_AVAILABLE = 28;
+ do_check_eq(AddonManager.STATE_AVAILABLE, old);
+
+ // Verify that functions cannot be replaced
+ AddonManager.isInstallEnabled = function() {
+ do_throw("Should not be able to replace a function");
+ }
+ AddonManager.isInstallEnabled("application/x-xpinstall");
+
+ // Verify that properties cannot be added
+ AddonManager.foo = "bar";
+ do_check_false("foo" in AddonManager);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug595573.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug595573.js
new file mode 100644
index 000000000..7e2bf7d77
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug595573.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests if addons with UUID based ids install and stay installed
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ run_test_1();
+}
+
+function run_test_1() {
+ installAllFiles([do_get_addon("test_bug595573")], function() {
+ restartManager();
+
+ AddonManager.getAddonByID("{2f69dacd-03df-4150-a9f1-e8a7b2748829}", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+function run_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("{2f69dacd-03df-4150-a9f1-e8a7b2748829}", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug596607.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug596607.js
new file mode 100644
index 000000000..bdcf93a1f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug596607.js
@@ -0,0 +1,147 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that a reference to a non-existent extension in the registry doesn't
+// break things
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const addon1Dir = writeInstallRDFForExtension(addon1, gProfD, "addon1");
+const addon2Dir = writeInstallRDFForExtension(addon2, gProfD, "addon2");
+const addon3Dir = gProfD.clone();
+addon3Dir.append("addon3@tests.mozilla.org");
+
+let registry;
+
+function run_test() {
+ // This test only works where there is a registry.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ registry = new MockRegistry();
+ do_register_cleanup(() => {
+ registry.shutdown();
+ });
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests whether starting a fresh profile with a bad entry works
+function run_test_1() {
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon2Dir.path);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", addon3Dir.path);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests whether removing the bad entry has any effect
+function run_test_2() {
+ shutdownManager();
+
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", addon3Dir.path);
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests adding the bad entry to an existing profile has any effect
+function run_test_3() {
+ shutdownManager();
+
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon3@tests.mozilla.org", null);
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"], function([a1, a2, a3]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug616841.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug616841.js
new file mode 100644
index 000000000..d0c973960
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug616841.js
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that string comparisons work correctly in callbacks
+
+function test_string_compare() {
+ do_check_true("C".localeCompare("D") < 0);
+ do_check_true("D".localeCompare("C") > 0);
+ do_check_true("\u010C".localeCompare("D") < 0);
+ do_check_true("D".localeCompare("\u010C") > 0);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ do_test_pending();
+
+ test_string_compare();
+
+ AddonManager.getAddonByID("foo", function(aAddon) {
+ test_string_compare();
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug619730.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug619730.js
new file mode 100644
index 000000000..1c21385e0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug619730.js
@@ -0,0 +1,64 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests whether
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_bug619730.xml", gTestserver);
+
+function load_blocklist(file, aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ do_execute_soon(aCallback);
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+var gSawGFX = false;
+var gSawTest = false;
+
+// Performs the initial setup
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(aSubject instanceof AM_Ci.nsIDOMElement);
+ do_check_eq(aSubject.getAttribute("testattr"), "GFX");
+ do_check_eq(aSubject.childNodes.length, 2);
+ gSawGFX = true;
+ }, "blocklist-data-gfxItems", false);
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(aSubject instanceof AM_Ci.nsIDOMElement);
+ do_check_eq(aSubject.getAttribute("testattr"), "FOO");
+ do_check_eq(aSubject.childNodes.length, 3);
+ gSawTest = true;
+ }, "blocklist-data-testItems", false);
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ do_check_true(gSawGFX);
+ do_check_true(gSawTest);
+ }, "blocklist-data-fooItems", false);
+
+ // Need to wait for the blocklist to load; Bad Things happen if the test harness
+ // shuts down AddonManager before the blocklist service is done telling it about
+ // changes
+ load_blocklist("test_bug619730.xml", () => gTestserver.stop(do_test_finished));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug620837.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug620837.js
new file mode 100644
index 000000000..6bfbfcaf2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug620837.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+const PREF_BLOCKLIST_LASTUPDATETIME = "app.update.lastUpdateTime.blocklist-background-update-timer";
+const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal";
+const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
+
+const SECONDS_IN_DAY = 60 * 60 * 24;
+
+var gExpectedQueryString = null;
+var gNextTest = null;
+var gTestserver = null;
+
+function notify_blocklist() {
+ var blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+function pathHandler(metadata, response) {
+ do_check_eq(metadata.queryString, gExpectedQueryString);
+ gNextTest();
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ gTestserver = new HttpServer();
+ gTestserver.registerPathHandler("/", pathHandler);
+ gTestserver.start(-1);
+ gPort = gTestserver.identity.primaryPort;
+
+ Services.prefs.setCharPref("extensions.blocklist.url",
+ "http://localhost:" + gPort +
+ "/?%PING_COUNT%&%TOTAL_PING_COUNT%&%DAYS_SINCE_LAST_PING%");
+
+ do_test_pending();
+ test1();
+}
+
+function getNowInSeconds() {
+ return Math.round(Date.now() / 1000);
+}
+
+function test1() {
+ gNextTest = test2;
+ gExpectedQueryString = "1&1&new";
+ notify_blocklist();
+}
+
+function test2() {
+ gNextTest = test3;
+ gExpectedQueryString = "invalid&invalid&invalid";
+ notify_blocklist();
+}
+
+function test3() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - SECONDS_IN_DAY));
+ gNextTest = test4;
+ gExpectedQueryString = "2&2&1";
+ notify_blocklist();
+}
+
+function test4() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, -1);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 2)));
+ gNextTest = test5;
+ gExpectedQueryString = "1&3&2";
+ notify_blocklist();
+}
+
+function test5() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME, getNowInSeconds());
+ gNextTest = test6;
+ gExpectedQueryString = "invalid&invalid&0";
+ notify_blocklist();
+}
+
+function test6() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 3)));
+ gNextTest = test7;
+ gExpectedQueryString = "2&4&3";
+ notify_blocklist();
+}
+
+function test7() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, 2147483647);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 4)));
+ gNextTest = test8;
+ gExpectedQueryString = "2147483647&5&4";
+ notify_blocklist();
+}
+
+function test8() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 5)));
+ gNextTest = test9;
+ gExpectedQueryString = "1&6&5";
+ notify_blocklist();
+}
+
+function test9() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, 2147483647);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 6)));
+ gNextTest = test10;
+ gExpectedQueryString = "2&2147483647&6";
+ notify_blocklist();
+}
+
+function test10() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 7)));
+ gNextTest = test11;
+ gExpectedQueryString = "3&1&7";
+ notify_blocklist();
+}
+
+function test11() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, -1);
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 8)));
+ gNextTest = test12;
+ gExpectedQueryString = "1&2&8";
+ notify_blocklist();
+}
+
+function test12() {
+ Services.prefs.setIntPref(PREF_BLOCKLIST_LASTUPDATETIME,
+ (getNowInSeconds() - (SECONDS_IN_DAY * 9)));
+ gNextTest = finish;
+ gExpectedQueryString = "2&3&9";
+ notify_blocklist();
+}
+
+function finish() {
+ gTestserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug655254.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug655254.js
new file mode 100644
index 000000000..449c59065
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug655254.js
@@ -0,0 +1,164 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that moving an extension in the filesystem without any other
+// change still keeps updated compatibility information
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9.2");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_bug655254.rdf", testserver);
+
+var userDir = gProfD.clone();
+userDir.append("extensions2");
+userDir.append(gAppInfo.ID);
+
+var dirProvider = {
+ getFile: function(aProp, aPersistent) {
+ aPersistent.value = false;
+ if (aProp == "XREUSysExt")
+ return userDir.parent;
+ return null;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIDirectoryServiceProvider,
+ AM_Ci.nsISupports])
+};
+Services.dirsvc.registerProvider(dirProvider);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ updateURL: "http://localhost:" + gPort + "/data/test_bug655254.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ var time = Date.now();
+ var dir = writeInstallRDFForExtension(addon1, userDir);
+ setExtensionModifiedTime(dir, time);
+
+ manuallyInstall(do_get_addon("test_bug655254_2"), userDir, "addon2@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.appDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ a1.findUpdates({
+ onUpdateFinished: function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1_2) {
+ do_check_neq(a1_2, null);
+ do_check_false(a1_2.appDisabled);
+ do_check_true(a1_2.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1_2.id));
+
+ shutdownManager();
+
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ userDir.parent.moveTo(gProfD, "extensions3");
+ userDir = gProfD.clone();
+ userDir.append("extensions3");
+ userDir.append(gAppInfo.ID);
+ do_check_true(userDir.exists());
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1_3, a2_3]) {
+ do_check_neq(a1_3, null);
+ do_check_false(a1_3.appDisabled);
+ do_check_true(a1_3.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1_3.id));
+
+ do_check_neq(a2_3, null);
+ do_check_false(a2_3.appDisabled);
+ do_check_true(a2_3.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2_3.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ do_execute_soon(run_test_2);
+ });
+ }));
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Set up the profile
+function run_test_2() {
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 1);
+
+ a2.userDisabled = true;
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ shutdownManager();
+
+ userDir.parent.moveTo(gProfD, "extensions4");
+ userDir = gProfD.clone();
+ userDir.append("extensions4");
+ userDir.append(gAppInfo.ID);
+ do_check_true(userDir.exists());
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1_2, a2_2]) {
+ do_check_neq(a1_2, null);
+ do_check_false(a1_2.appDisabled);
+ do_check_true(a1_2.isActive);
+ do_check_true(isExtensionInAddonsList(userDir, a1_2.id));
+
+ do_check_neq(a2_2, null);
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.isActive);
+ do_check_false(isExtensionInAddonsList(userDir, a2_2.id));
+ do_check_eq(Services.prefs.getIntPref("bootstraptest.active_version"), 0);
+
+ end_test();
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug659772.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug659772.js
new file mode 100644
index 000000000..6e98a69a4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug659772.js
@@ -0,0 +1,340 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that a pending upgrade during a schema update doesn't break things
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ run_test_1();
+}
+
+// Tests whether a schema migration without app version change works
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Prepare the add-on update, and a bootstrapped addon (bug 693714)
+ installAllFiles([
+ do_get_addon("test_bug659772"),
+ do_get_addon("test_bootstrap1_1")
+ ], function() {
+ shutdownManager();
+
+ // Make it look like the next time the app is started it has a new DB schema
+ changeXPIDBVersion(1);
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ let jsonfile = gProfD.clone();
+ jsonfile.append("extensions");
+ jsonfile.append("staged");
+ jsonfile.append("addon3@tests.mozilla.org.json");
+ do_check_true(jsonfile.exists());
+
+ // Remove an unnecessary property from the cached manifest
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ let json = AM_Cc["@mozilla.org/dom/json;1"].
+ createInstance(AM_Ci.nsIJSON);
+ fis.init(jsonfile, -1, 0, 0);
+ let addonObj = json.decodeFromStream(fis, jsonfile.fileSize);
+ fis.close();
+ delete addonObj.optionsType;
+
+ let stream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ stream.init(jsonfile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
+ 0);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ converter.writeString(JSON.stringify(addonObj));
+ converter.close();
+ stream.close();
+
+ Services.prefs.clearUserPref("bootstraptest.install_reason");
+ Services.prefs.clearUserPref("bootstraptest.uninstall_reason");
+
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1_2, a2_2, a3_2, a4_2]) {
+ do_check_neq(a1_2, null);
+ do_check_eq(a1_2.version, "2.0");
+ do_check_false(a1_2.appDisabled);
+ do_check_false(a1_2.userDisabled);
+ do_check_true(a1_2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2_2, null);
+ do_check_eq(a2_2.version, "2.0");
+ do_check_false(a2_2.appDisabled);
+ do_check_false(a2_2.userDisabled);
+ do_check_true(a2_2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ // Should stay enabled because we migrate the compat info from
+ // the previous version of the DB
+ do_check_neq(a3_2, null);
+ do_check_eq(a3_2.version, "2.0");
+ todo_check_false(a3_2.appDisabled); // XXX unresolved issue
+ do_check_false(a3_2.userDisabled);
+ todo_check_true(a3_2.isActive); // XXX same
+ todo_check_true(isExtensionInAddonsList(profileDir, addon3.id)); // XXX same
+
+ do_check_neq(a4_2, null);
+ do_check_eq(a4_2.version, "2.0");
+ do_check_true(a4_2.appDisabled);
+ do_check_false(a4_2.userDisabled);
+ do_check_false(a4_2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Check that install and uninstall haven't been called on the bootstrapped addon
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason"));
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason"));
+
+ a1_2.uninstall();
+ a2_2.uninstall();
+ a3_2.uninstall();
+ a4_2.uninstall();
+ do_execute_soon(run_test_2);
+ });
+ });
+ });
+}
+
+// Tests whether a schema migration with app version change works
+function run_test_2() {
+ restartManager();
+
+ shutdownManager();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.version, "2.0");
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.version, "2.0");
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.version, "2.0");
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Prepare the add-on update, and a bootstrapped addon (bug 693714)
+ installAllFiles([
+ do_get_addon("test_bug659772"),
+ do_get_addon("test_bootstrap1_1")
+ ], function() { do_execute_soon(prepare_schema_migrate); });
+
+ function prepare_schema_migrate() {
+ shutdownManager();
+
+ // Make it look like the next time the app is started it has a new DB schema
+ changeXPIDBVersion(1);
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ let jsonfile = gProfD.clone();
+ jsonfile.append("extensions");
+ jsonfile.append("staged");
+ jsonfile.append("addon3@tests.mozilla.org.json");
+ do_check_true(jsonfile.exists());
+
+ // Remove an unnecessary property from the cached manifest
+ let fis = AM_Cc["@mozilla.org/network/file-input-stream;1"].
+ createInstance(AM_Ci.nsIFileInputStream);
+ let json = AM_Cc["@mozilla.org/dom/json;1"].
+ createInstance(AM_Ci.nsIJSON);
+ fis.init(jsonfile, -1, 0, 0);
+ let addonObj = json.decodeFromStream(fis, jsonfile.fileSize);
+ fis.close();
+ delete addonObj.optionsType;
+
+ let stream = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ let converter = AM_Cc["@mozilla.org/intl/converter-output-stream;1"].
+ createInstance(AM_Ci.nsIConverterOutputStream);
+ stream.init(jsonfile, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
+ FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE,
+ 0);
+ converter.init(stream, "UTF-8", 0, 0x0000);
+ converter.writeString(JSON.stringify(addonObj));
+ converter.close();
+ stream.close();
+
+ Services.prefs.clearUserPref("bootstraptest.install_reason");
+ Services.prefs.clearUserPref("bootstraptest.uninstall_reason");
+
+ gAppInfo.version = "2";
+ startupManager(true);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ callback_soon(function([a1_2, a2_2, a3_2, a4_2]) {
+ do_check_neq(a1_2, null);
+ do_check_eq(a1_2.version, "2.0");
+ do_check_true(a1_2.appDisabled);
+ do_check_false(a1_2.userDisabled);
+ do_check_false(a1_2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, addon1.id));
+
+ do_check_neq(a2_2, null);
+ do_check_eq(a2_2.version, "2.0");
+ do_check_false(a2_2.appDisabled);
+ do_check_false(a2_2.userDisabled);
+ do_check_true(a2_2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon2.id));
+
+ // Should become appDisabled because we migrate the compat info from
+ // the previous version of the DB
+ do_check_neq(a3_2, null);
+ do_check_eq(a3_2.version, "2.0");
+ todo_check_true(a3_2.appDisabled);
+ do_check_false(a3_2.userDisabled);
+ todo_check_false(a3_2.isActive);
+ todo_check_false(isExtensionInAddonsList(profileDir, addon3.id));
+
+ do_check_neq(a4_2, null);
+ do_check_eq(a4_2.version, "2.0");
+ do_check_false(a4_2.appDisabled);
+ do_check_false(a4_2.userDisabled);
+ do_check_true(a4_2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, addon4.id));
+
+ // Check that install and uninstall haven't been called on the bootstrapped addon
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.install_reason"));
+ do_check_false(Services.prefs.prefHasUserValue("bootstraptest.uninstall_reason"));
+
+ a1_2.uninstall();
+ a2_2.uninstall();
+ a3_2.uninstall();
+ a4_2.uninstall();
+ restartManager();
+
+ shutdownManager();
+
+ do_test_finished();
+ }));
+ }
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug675371.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug675371.js
new file mode 100644
index 000000000..6f2a5e7cd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug675371.js
@@ -0,0 +1,91 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug675371"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test));
+ install.install();
+ });
+}
+
+function check_test() {
+ AddonManager.getAddonByID("bug675371@tests.mozilla.org", do_exception_wrap(function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ // Tests that chrome.manifest is registered when the addon is installed.
+ var target = { };
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ // Tests that chrome.manifest is unregistered when the addon is disabled.
+ addon.userDisabled = true;
+ target.active = false;
+ try {
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_throw("Chrome file should not have been found");
+ } catch (e) {
+ do_check_false(target.active);
+ }
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ // Tests that chrome.manifest is registered when the addon is enabled.
+ addon.userDisabled = false;
+ target.active = false;
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ // Tests that chrome.manifest is unregistered when the addon is uninstalled.
+ addon.uninstall();
+ target.active = false;
+ try {
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_throw("Chrome file should not have been found");
+ } catch (e) {
+ do_check_false(target.active);
+ }
+
+ do_execute_soon(do_test_finished);
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug740612.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug740612.js
new file mode 100644
index 000000000..d17e7acde
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug740612.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that attempts to override the global values fails but doesn't
+// destroy the world with it
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function run_test() {
+ do_test_pending();
+
+ manuallyInstall(do_get_addon("test_bug740612_1"), profileDir,
+ "bug740612_1@tests.mozilla.org");
+ manuallyInstall(do_get_addon("test_bug740612_2"), profileDir,
+ "bug740612_2@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["bug740612_1@tests.mozilla.org",
+ "bug740612_2@tests.mozilla.org"],
+ function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_neq(a2, null);
+ do_check_eq(getInstalledVersion(), "1.0");
+ do_check_eq(getActiveVersion(), "1.0");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug753900.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug753900.js
new file mode 100644
index 000000000..206862339
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug753900.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that strange characters in an add-on version don't break the
+// crash annotation.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1,0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1:0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1,0",
+ name: "Test 3",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1:0",
+ name: "Test 4",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+ do_check_neq(a2, null);
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+ do_check_neq(a3, null);
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+ do_check_neq(a4, null);
+ do_check_in_crash_annotation(addon4.id, addon4.version);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug757663.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug757663.js
new file mode 100644
index 000000000..54cee0839
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug757663.js
@@ -0,0 +1,112 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This test verifies that removing a listener during a callback for that type
+// of listener still results in all listeners being called.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var listener1 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ AddonManager.removeAddonListener(this);
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ AddonManager.removeInstallListener(this);
+ }
+};
+var listener2 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ }
+};
+var listener3 = {
+ sawEvent: false,
+ onDisabling: function() {
+ this.sawEvent = true;
+ },
+ onNewInstall: function() {
+ this.sawEvent = true;
+ }
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.addAddonListener(listener1);
+ AddonManager.addAddonListener(listener2);
+ AddonManager.addAddonListener(listener3);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org"], function([a1]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.isActive);
+
+ a1.userDisabled = true;
+
+ do_check_true(listener1.sawEvent);
+ listener1.sawEvent = false;
+ do_check_true(listener2.sawEvent);
+ listener2.sawEvent = false;
+ do_check_true(listener3.sawEvent);
+ listener3.sawEvent = false;
+
+ AddonManager.removeAddonListener(listener1);
+ AddonManager.removeAddonListener(listener2);
+ AddonManager.removeAddonListener(listener3);
+
+ a1.uninstall();
+ run_test_2();
+ });
+}
+
+function run_test_2() {
+ AddonManager.addInstallListener(listener1);
+ AddonManager.addInstallListener(listener2);
+ AddonManager.addInstallListener(listener3);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug757663"), function(aInstall) {
+
+ do_check_true(listener1.sawEvent);
+ listener1.sawEvent = false;
+ do_check_true(listener2.sawEvent);
+ listener2.sawEvent = false;
+ do_check_true(listener3.sawEvent);
+ listener3.sawEvent = false;
+
+ AddonManager.removeInstallListener(listener1);
+ AddonManager.removeInstallListener(listener2);
+ AddonManager.removeInstallListener(listener3);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_bug953156.js b/toolkit/mozapps/webextensions/test/xpcshell/test_bug953156.js
new file mode 100644
index 000000000..a7acb9ad2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_bug953156.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bug675371"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({
+ "bug675371@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test));
+ install.install();
+ });
+}
+
+function check_test() {
+ AddonManager.getAddonByID("bug675371@tests.mozilla.org", do_exception_wrap(function(addon) {
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+
+ // Tests that chrome.manifest is registered when the addon is installed.
+ var target = { active: false };
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ shutdownManager();
+
+ // Tests that chrome.manifest remains registered at app shutdown.
+ target.active = false;
+ Services.scriptloader.loadSubScript("chrome://bug675371/content/test.js", target);
+ do_check_true(target.active);
+
+ do_execute_soon(do_test_finished);
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_cache_certdb.js b/toolkit/mozapps/webextensions/test/xpcshell/test_cache_certdb.js
new file mode 100644
index 000000000..edb442aad
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_cache_certdb.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// We require signature checks for this test
+Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
+gUseRealCertChecks = true;
+
+const CERT = `MIIDITCCAgmgAwIBAgIJALAv8fydd6nBMA0GCSqGSIb3DQEBBQUAMCcxJTAjBgNV
+BAMMHGJvb3RzdHJhcDFAdGVzdHMubW96aWxsYS5vcmcwHhcNMTYwMjAyMjMxNjUy
+WhcNMjYwMTMwMjMxNjUyWjAnMSUwIwYDVQQDDBxib290c3RyYXAxQHRlc3RzLm1v
+emlsbGEub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5caNuLTu
+H8dEqNntLlhKi4y09hrgcF3cb6n5Xx9DIHA8CKiZxt9qGXKeeiDwEiiQ8ibJYzdc
+jLkbzJUyPVUaH9ygrWynSpSTOvv/Ys3+ERrCo9W7Zuzwdmzt6TTEjFMS4lVx06us
+3uUqkdp3JMgCqCEbOFZiztICiSKrp8QFJkAfApZzBqmJOPOWH0yZ2CRRzvbQZ6af
+hqQDUalJQjWfsenyUWphhbREqExetxHJFR3OrmJt/shXVyz6dD7TBuE3PPUh1RpE
+3ejVufcTzjV3XmK79PxsKLM9V2+ww9e9V3OET57kyvn+bpSWdUYm3X4DA8dxNW6+
+kTFWRnQNZ+zQVQIDAQABo1AwTjAdBgNVHQ4EFgQUac36ccv+99N5HxYa8dCDYRaF
+HNQwHwYDVR0jBBgwFoAUac36ccv+99N5HxYa8dCDYRaFHNQwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQUFAAOCAQEAFfu3MN8EtY5wcxOFdGShOmGQPm2MJJVE6MG+
+p4RqHrukHZSgKOyWjkRk7t6NXzNcnHco9HFv7FQRAXSJ5zObmyu+TMZlu4jHHCav
+GMcV3C/4SUGtlipZbgNe00UAIm6tM3Wh8dr38W7VYg4KGAwXou5XhQ9gCAnSn90o
+H/42NqHTjJsR4v18izX2aO25ARQdMby7Lsr5j9RqweHywiSlPusFcKRseqOnIP0d
+JT3+qh78LeMbNBO2mYD3SP/zu0TAmkAVNcj2KPw0+a0kVZ15rvslPC/K3xn9msMk
+fQthv3rDAcsWvi9YO7T+vylgZBgJfn1ZqpQqy58xN96uh6nPOw==`;
+
+function overrideCertDB() {
+ // Unregister the real database.
+ let registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
+ let factory = registrar.getClassObject(CERTDB_CID, AM_Ci.nsIFactory);
+ registrar.unregisterFactory(CERTDB_CID, factory);
+
+ // Get the real DB
+ let realCertDB = factory.createInstance(null, AM_Ci.nsIX509CertDB);
+
+ let fakeCert = realCertDB.constructX509FromBase64(CERT.replace(/\n/g, ""));
+
+ let fakeCertDB = {
+ openSignedAppFileAsync(root, file, callback) {
+ callback.openSignedAppFileFinished(Components.results.NS_OK, null, fakeCert);
+ },
+
+ verifySignedDirectoryAsync(root, dir, callback) {
+ callback.verifySignedDirectoryFinished(Components.results.NS_OK, fakeCert);
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIX509CertDB])
+ };
+
+ for (let property of Object.keys(realCertDB)) {
+ if (property in fakeCertDB) {
+ continue;
+ }
+
+ if (typeof realCertDB[property] == "function") {
+ fakeCertDB[property] = realCertDB[property].bind(realCertDB);
+ }
+ }
+
+ let certDBFactory = {
+ createInstance: function(outer, iid) {
+ if (outer != null) {
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ }
+ return fakeCertDB.QueryInterface(iid);
+ }
+ };
+ registrar.registerFactory(CERTDB_CID, "CertDB",
+ CERTDB_CONTRACTID, certDBFactory);
+}
+
+add_task(function*() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ // Once the application is started we shouldn't be able to replace the
+ // certificate database
+ overrideCertDB();
+
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_cacheflush.js b/toolkit/mozapps/webextensions/test/xpcshell/test_cacheflush.js
new file mode 100644
index 000000000..f3448abd2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_cacheflush.js
@@ -0,0 +1,127 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that flushing the zipreader cache happens when appropriate
+
+var gExpectedFile = null;
+var gCacheFlushCount = 0;
+
+var CacheFlushObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "flush-cache-entry")
+ return;
+ // Ignore flushes triggered by the fake cert DB
+ if (aData == "cert-override")
+ return;
+
+ do_check_true(gExpectedFile != null);
+ do_check_true(aSubject instanceof AM_Ci.nsIFile);
+ do_check_eq(aSubject.path, gExpectedFile.path);
+ gCacheFlushCount++;
+ }
+};
+
+function run_test() {
+ do_test_pending();
+ Services.obs.addObserver(CacheFlushObserver, "flush-cache-entry", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that the cache is flushed when cancelling a pending install
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_addon("test_cacheflush1"), function(aInstall) {
+ completeAllInstalls([aInstall], function() {
+ // We should flush the staged XPI when cancelling the install
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ aInstall.cancel();
+
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ run_test_2();
+ });
+ });
+}
+
+// Tests that the cache is flushed when uninstalling an add-on
+function run_test_2() {
+ installAllFiles([do_get_addon("test_cacheflush1")], function() {
+ // Installing will flush the staged XPI during startup
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ restartManager();
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ // We should flush the installed XPI when uninstalling
+ do_check_true(a1 != null);
+ a1.uninstall();
+ do_check_eq(gCacheFlushCount, 0);
+
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("addon1@tests.mozilla.org.xpi");
+ restartManager();
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Tests that the cache is flushed when installing a restartless add-on
+function run_test_3() {
+ AddonManager.getInstallForFile(do_get_addon("test_cacheflush2"), function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ // We should flush the staged XPI when completing the install
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("staged");
+ gExpectedFile.append("addon2@tests.mozilla.org.xpi");
+ },
+
+ onInstallEnded: function() {
+ do_check_eq(gCacheFlushCount, 1);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(run_test_4);
+ }
+ });
+
+ aInstall.install();
+ });
+}
+
+// Tests that the cache is flushed when uninstalling a restartless add-on
+function run_test_4() {
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ // We should flush the installed XPI when uninstalling
+ gExpectedFile = gProfD.clone();
+ gExpectedFile.append("extensions");
+ gExpectedFile.append("addon2@tests.mozilla.org.xpi");
+
+ a2.uninstall();
+ do_check_eq(gCacheFlushCount, 2);
+ gExpectedFile = null;
+ gCacheFlushCount = 0;
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_checkCompatibility_themeOverride.js b/toolkit/mozapps/webextensions/test/xpcshell/test_checkCompatibility_themeOverride.js
new file mode 100644
index 000000000..b6cb13e08
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_checkCompatibility_themeOverride.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the (temporary)
+// extensions.checkCompatibility.temporaryThemeOverride_minAppVersion
+// preference works.
+
+var ADDONS = [{
+ id: "addon1@tests.mozilla.org",
+ type: 4,
+ internalName: "theme1/1.0",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1.0",
+ maxVersion: "1.0"
+ }]
+}, {
+ id: "addon2@tests.mozilla.org",
+ type: 4,
+ internalName: "theme2/1.0",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2.0",
+ maxVersion: "2.0"
+ }]
+}];
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3.0", "1");
+
+ for (let a of ADDONS) {
+ writeInstallRDFForExtension(a, profileDir);
+ }
+
+ startupManager();
+
+ run_test_1();
+}
+
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+function run_test_2() {
+ Services.prefs.setCharPref("extensions.checkCompatibility.temporaryThemeOverride_minAppVersion", "2.0");
+ if (isNightlyChannel())
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.3.0", false);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"],
+ function([a1, a2]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+ do_check_true(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_checkcompatibility.js b/toolkit/mozapps/webextensions/test/xpcshell/test_checkcompatibility.js
new file mode 100644
index 000000000..b9fc0b3ab
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_checkcompatibility.js
@@ -0,0 +1,196 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the extensions.checkCompatibility.* preferences work.
+
+var ADDONS = [{
+ // Cannot be enabled as it has no target app info for the applciation
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "unknown@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, {
+ // Always appears incompatible but can be enabled if compatibility checking is
+ // disabled
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, {
+ // Always appears incompatible but can be enabled if compatibility checking is
+ // disabled
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+}, { // Always compatible and enabled
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+}, { // Always compatible and enabled
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+}];
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIsNightly = false;
+
+function run_test() {
+ do_test_pending("checkcompatibility.js");
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2.2.3", "2");
+
+ ADDONS.forEach(function(a) {
+ writeInstallRDFForExtension(a, profileDir);
+ });
+
+ gIsNightly = isNightlyChannel();
+
+ startupManager();
+
+ run_test_1();
+}
+
+/**
+ * Checks that the add-ons are enabled as expected.
+ * @param overridden
+ * A boolean indicating that compatibility checking is overridden
+ * @param a1
+ * The Addon for addon1@tests.mozilla.org
+ * @param a2
+ * The Addon for addon2@tests.mozilla.org
+ * @param a3
+ * The Addon for addon3@tests.mozilla.org
+ * @param a4
+ * The Addon for addon4@tests.mozilla.org
+ * @param a5
+ * The Addon for addon5@tests.mozilla.org
+ */
+function check_state(overridden, a1, a2, a3, a4, a5) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_false(a1.isCompatible);
+
+ do_check_neq(a2, null);
+ if (overridden)
+ do_check_true(a2.isActive);
+ else
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+
+ do_check_neq(a3, null);
+ if (overridden)
+ do_check_true(a3.isActive);
+ else
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_true(a5.isCompatible);
+}
+
+// Tests that with compatibility checking enabled we see the incompatible
+// add-ons disabled
+function run_test_1() {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(false, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests that with compatibility checking disabled we see the incompatible
+// add-ons enabled
+function run_test_2() {
+ if (gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.2", false);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(true, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests that with compatibility checking disabled we see the incompatible
+// add-ons enabled.
+function run_test_3() {
+ if (!gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.1a", false);
+ restartManager("2.1a4");
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(true, a1, a2, a3, a4, a5);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Tests that with compatibility checking enabled we see the incompatible
+// add-ons disabled.
+function run_test_4() {
+ if (gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", true);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.2.1a", true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ check_state(false, a1, a2, a3, a4, a5);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_childprocess.js b/toolkit/mozapps/webextensions/test/xpcshell/test_childprocess.js
new file mode 100644
index 000000000..a6c635eac
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_childprocess.js
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the AddonManager refuses to load in child processes.
+
+function run_test() {
+ // Already loaded the module by head_addons.js. Need to unload this again, so
+ // that overriding the app-info and re-importing the module works.
+ Components.utils.unload("resource://gre/modules/AddonManager.jsm");
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ gAppInfo.processType = AM_Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+ try {
+ Components.utils.import("resource://gre/modules/AddonManager.jsm");
+ do_throw("AddonManager should have refused to load");
+ }
+ catch (ex) {
+ do_print(ex.message);
+ do_check_true(!!ex.message);
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_compatoverrides.js b/toolkit/mozapps/webextensions/test/xpcshell/test_compatoverrides.js
new file mode 100644
index 000000000..c079534c3
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_compatoverrides.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests compatibility overrides, for when strict compatibility checking is
+// disabled. See bug 693906.
+
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+const PORT = gPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+const REQ_URL = "/data.xml";
+
+// register static file and mark it for interpolation
+mapUrlToFile(REQ_URL, do_get_file("data/test_compatoverrides.xml"), gServer);
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ BASE_URL + REQ_URL);
+
+
+// Not hosted, no overrides
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, no overrides
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, matching override
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, matching override, wouldn't be compatible if strict checking is enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+};
+
+// Hosted, app ID doesn't match in override
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, addon version range doesn't match in override
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, app version range doesn't match in override
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Hosted, multiple overrides
+var addon8 = {
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 8",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Not hosted, matching override
+var addon9 = {
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 9",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Not hosted, override is of unsupported type (compatible)
+var addon10 = {
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon 10",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(addon8, profileDir);
+ writeInstallRDFForExtension(addon9, profileDir);
+ writeInstallRDFForExtension(addon10, profileDir);
+
+ startupManager();
+
+ AddonManagerInternal.backgroundUpdateCheck().then(run_test_1);
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
+
+function check_compat_status(aCallback) {
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.compatibilityOverrides, null);
+ do_check_true(a1.isCompatible);
+ do_check_false(a1.appDisabled);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.compatibilityOverrides, null);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+
+ do_check_neq(a3, null);
+ do_check_neq(a3.compatibilityOverrides, null);
+ do_check_eq(a3.compatibilityOverrides.length, 1);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ do_check_neq(a4, null);
+ do_check_neq(a4.compatibilityOverrides, null);
+ do_check_eq(a4.compatibilityOverrides.length, 1);
+ do_check_false(a4.isCompatible);
+ do_check_true(a4.appDisabled);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.compatibilityOverrides, null);
+ do_check_true(a5.isCompatible);
+ do_check_false(a5.appDisabled);
+
+ do_check_neq(a6, null);
+ do_check_neq(a6.compatibilityOverrides, null);
+ do_check_eq(a6.compatibilityOverrides.length, 1);
+ do_check_true(a6.isCompatible);
+ do_check_false(a6.appDisabled);
+
+ do_check_neq(a7, null);
+ do_check_neq(a7.compatibilityOverrides, null);
+ do_check_eq(a7.compatibilityOverrides.length, 1);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ do_check_neq(a8, null);
+ do_check_neq(a8.compatibilityOverrides, null);
+ do_check_eq(a8.compatibilityOverrides.length, 3);
+ do_check_false(a8.isCompatible);
+ do_check_true(a8.appDisabled);
+
+ do_check_neq(a9, null);
+ do_check_neq(a9.compatibilityOverrides, null);
+ do_check_eq(a9.compatibilityOverrides.length, 1);
+ do_check_false(a9.isCompatible);
+ do_check_true(a9.appDisabled);
+
+ do_check_neq(a10, null);
+ do_check_eq(a10.compatibilityOverrides, null);
+ do_check_true(a10.isCompatible);
+ do_check_false(a10.appDisabled);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+function run_test_1() {
+ do_print("Run test 1");
+ check_compat_status(run_test_2);
+}
+
+function run_test_2() {
+ do_print("Run test 2");
+ restartManager();
+ check_compat_status(end_test);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt.js b/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt.js
new file mode 100644
index 000000000..210c6a936
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt.js
@@ -0,0 +1,406 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_corrupt.rdf", testserver);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and stay enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Would stay incompatible with strict compat
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Shutdown and replace the database with a corrupt file (a directory
+ // serves this purpose). On startup the add-ons manager won't rebuild
+ // because there is a file there still.
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ gExtensionsJSON.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ startupManager(false);
+
+ // Accessing the add-ons should open and recover the database
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1_2, a2_2, a3_2, a4_2, a5_2, a6_2, a7_2, t1_2, t2_2]) {
+ // Should be correctly recovered
+ do_check_neq(a1_2, null);
+ do_check_true(a1_2.isActive);
+ do_check_false(a1_2.userDisabled);
+ do_check_false(a1_2.appDisabled);
+ do_check_eq(a1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(a2_2, null);
+ do_check_false(a2_2.isActive);
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.appDisabled);
+ do_check_eq(a2_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3_2, null);
+ do_check_true(a3_2.isActive);
+ do_check_false(a3_2.userDisabled);
+ do_check_false(a3_2.appDisabled);
+ do_check_eq(a3_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered and with strict
+ // compatibility it would not have been able to tell that it was
+ // previously userDisabled. However, without strict compat, it wasn't
+ // appDisabled, so it knows it must have been userDisabled.
+ do_check_neq(a4_2, null);
+ do_check_false(a4_2.isActive);
+ do_check_true(a4_2.userDisabled);
+ do_check_false(a4_2.appDisabled);
+ do_check_eq(a4_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5_2, null);
+ do_check_true(a5_2.isActive);
+ do_check_false(a5_2.userDisabled);
+ do_check_false(a5_2.appDisabled);
+ do_check_eq(a5_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6_2, null);
+ do_check_true(a6_2.isActive);
+ do_check_false(a6_2.userDisabled);
+ do_check_false(a6_2.appDisabled);
+ do_check_eq(a6_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7_2, null);
+ do_check_false(a7_2.isActive);
+ do_check_true(a7_2.userDisabled);
+ do_check_false(a7_2.appDisabled);
+ do_check_eq(a7_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1_2, null);
+ do_check_false(t1_2.isActive);
+ do_check_true(t1_2.userDisabled);
+ do_check_false(t1_2.appDisabled);
+ do_check_eq(t1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t2_2, null);
+ do_check_true(t2_2.isActive);
+ do_check_false(t2_2.userDisabled);
+ do_check_false(t2_2.appDisabled);
+ do_check_eq(t2_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.throws(shutdownManager);
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1_3, a2_3, a3_3, a4_3, a5_3, a6_3, a7_3, t1_3, t2_3]) {
+ do_check_neq(a1_3, null);
+ do_check_true(a1_3.isActive);
+ do_check_false(a1_3.userDisabled);
+ do_check_false(a1_3.appDisabled);
+ do_check_eq(a1_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2_3, null);
+ do_check_false(a2_3.isActive);
+ do_check_true(a2_3.userDisabled);
+ do_check_false(a2_3.appDisabled);
+ do_check_eq(a2_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3_3, null);
+ do_check_true(a3_3.isActive);
+ do_check_false(a3_3.userDisabled);
+ do_check_false(a3_3.appDisabled);
+ do_check_eq(a3_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4_3, null);
+ do_check_false(a4_3.isActive);
+ do_check_true(a4_3.userDisabled);
+ do_check_false(a4_3.appDisabled);
+ do_check_eq(a4_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5_3, null);
+ do_check_true(a5_3.isActive);
+ do_check_false(a5_3.userDisabled);
+ do_check_false(a5_3.appDisabled);
+ do_check_eq(a5_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6_3, null);
+ do_check_true(a6_3.isActive);
+ do_check_false(a6_3.userDisabled);
+ do_check_false(a6_3.appDisabled);
+ do_check_eq(a6_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7_3, null);
+ do_check_false(a7_3.isActive);
+ do_check_true(a7_3.userDisabled);
+ do_check_false(a7_3.appDisabled);
+ do_check_eq(a7_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1_3, null);
+ do_check_false(t1_3.isActive);
+ do_check_true(t1_3.userDisabled);
+ do_check_false(t1_3.appDisabled);
+ do_check_eq(t1_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2_3, null);
+ do_check_true(t2_3.isActive);
+ do_check_false(t2_3.userDisabled);
+ do_check_false(t2_3.appDisabled);
+ do_check_eq(t2_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.throws(shutdownManager);
+
+ end_test();
+ }));
+ }));
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt_strictcompat.js
new file mode 100644
index 000000000..622973472
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_corrupt_strictcompat.js
@@ -0,0 +1,405 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_corrupt.rdf", testserver);
+
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a2, a3, a4,
+ a7, t2]) {
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(run_test_1);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Shutdown and replace the database with a corrupt file (a directory
+ // serves this purpose). On startup the add-ons manager won't rebuild
+ // because there is a file there still.
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ gExtensionsJSON.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ startupManager(false);
+
+ // Accessing the add-ons should open and recover the database
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1_2, a2_2, a3_2, a4_2, a5_2, a6_2, a7_2, t1_2, t2_2]) {
+ // Should be correctly recovered
+ do_check_neq(a1_2, null);
+ do_check_true(a1_2.isActive);
+ do_check_false(a1_2.userDisabled);
+ do_check_false(a1_2.appDisabled);
+ do_check_eq(a1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(a2_2, null);
+ do_check_false(a2_2.isActive);
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.appDisabled);
+ do_check_eq(a2_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3_2, null);
+ do_check_true(a3_2.isActive);
+ do_check_false(a3_2.userDisabled);
+ do_check_true(a3_2.appDisabled);
+ do_check_eq(a3_2.pendingOperations, AddonManager.PENDING_DISABLE);
+
+ // The compatibility update won't be recovered and it will not have been
+ // able to tell that it was previously userDisabled
+ do_check_neq(a4_2, null);
+ do_check_false(a4_2.isActive);
+ do_check_false(a4_2.userDisabled);
+ do_check_true(a4_2.appDisabled);
+ do_check_eq(a4_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5_2, null);
+ do_check_false(a5_2.isActive);
+ do_check_false(a5_2.userDisabled);
+ do_check_true(a5_2.appDisabled);
+ do_check_eq(a5_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6_2, null);
+ do_check_true(a6_2.isActive);
+ do_check_false(a6_2.userDisabled);
+ do_check_false(a6_2.appDisabled);
+ do_check_eq(a6_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7_2, null);
+ do_check_false(a7_2.isActive);
+ do_check_true(a7_2.userDisabled);
+ do_check_false(a7_2.appDisabled);
+ do_check_eq(a7_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1_2, null);
+ do_check_false(t1_2.isActive);
+ do_check_true(t1_2.userDisabled);
+ do_check_false(t1_2.appDisabled);
+ do_check_eq(t1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t2_2, null);
+ do_check_true(t2_2.isActive);
+ do_check_false(t2_2.userDisabled);
+ do_check_false(t2_2.appDisabled);
+ do_check_eq(t2_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.throws(shutdownManager);
+ startupManager(false);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([a1_3, a2_3, a3_3, a4_3, a5_3, a6_3, a7_3, t1_3, t2_3]) {
+ do_check_neq(a1_3, null);
+ do_check_true(a1_3.isActive);
+ do_check_false(a1_3.userDisabled);
+ do_check_false(a1_3.appDisabled);
+ do_check_eq(a1_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a2_3, null);
+ do_check_false(a2_3.isActive);
+ do_check_true(a2_3.userDisabled);
+ do_check_false(a2_3.appDisabled);
+ do_check_eq(a2_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a3_3, null);
+ do_check_false(a3_3.isActive);
+ do_check_false(a3_3.userDisabled);
+ do_check_true(a3_3.appDisabled);
+ do_check_eq(a3_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4_3, null);
+ do_check_false(a4_3.isActive);
+ do_check_false(a4_3.userDisabled);
+ do_check_true(a4_3.appDisabled);
+ do_check_eq(a4_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5_3, null);
+ do_check_false(a5_3.isActive);
+ do_check_false(a5_3.userDisabled);
+ do_check_true(a5_3.appDisabled);
+ do_check_eq(a5_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a6_3, null);
+ do_check_true(a6_3.isActive);
+ do_check_false(a6_3.userDisabled);
+ do_check_false(a6_3.appDisabled);
+ do_check_eq(a6_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7_3, null);
+ do_check_false(a7_3.isActive);
+ do_check_true(a7_3.userDisabled);
+ do_check_false(a7_3.appDisabled);
+ do_check_eq(a7_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1_3, null);
+ do_check_false(t1_3.isActive);
+ do_check_true(t1_3.userDisabled);
+ do_check_false(t1_3.appDisabled);
+ do_check_eq(t1_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t2_3, null);
+ do_check_true(t2_3.isActive);
+ do_check_false(t2_3.userDisabled);
+ do_check_false(t2_3.appDisabled);
+ do_check_eq(t2_3.pendingOperations, AddonManager.PENDING_NONE);
+
+ Assert.throws(shutdownManager);
+
+ end_test();
+ }));
+ }));
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_corruptfile.js b/toolkit/mozapps/webextensions/test/xpcshell/test_corruptfile.js
new file mode 100644
index 000000000..92b375850
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_corruptfile.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that attempting to install a corrupt XPI file doesn't break the universe
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ if (TEST_UNPACKED)
+ run_test_unpacked();
+ else
+ run_test_packed();
+}
+
+// When installing packed we won't detect corruption in the XPI until we attempt
+// to load bootstrap.js so everything will look normal from the outside.
+function run_test_packed() {
+ do_test_pending();
+
+ prepare_test({
+ "corrupt@tests.mozilla.org": [
+ ["onInstalling", false],
+ ["onInstalled", false]
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ installAllFiles([do_get_file("data/corruptfile.xpi")], function() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("corrupt@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_test_finished();
+ });
+ });
+}
+
+// When extracting the corruption will be detected and the add-on fails to
+// install
+function run_test_unpacked() {
+ do_test_pending();
+
+ prepare_test({
+ "corrupt@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onOperationCancelled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallFailed"
+ ]);
+
+ installAllFiles([do_get_file("data/corruptfile.xpi")], function() {
+ ensure_test_completed();
+
+ // Check the add-on directory isn't left over
+ var addonDir = profileDir.clone();
+ addonDir.append("corrupt@tests.mozilla.org");
+ pathShouldntExist(addonDir);
+
+ // Check the staging directory isn't left over
+ var stageDir = profileDir.clone();
+ stageDir.append("staged");
+ pathShouldntExist(stageDir);
+
+ AddonManager.getAddonByID("corrupt@tests.mozilla.org", function(addon) {
+ do_check_eq(addon, null);
+
+ do_test_finished();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_dataDirectory.js b/toolkit/mozapps/webextensions/test/xpcshell/test_dataDirectory.js
new file mode 100644
index 000000000..bf75818e9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_dataDirectory.js
@@ -0,0 +1,50 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// Disables security checking our updates which haven't been signed
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+var ADDON = {
+ id: "datadirectory1@tests.mozilla.org",
+ addon: "test_data_directory"
+};
+
+function run_test() {
+ var expectedDir = gProfD.clone();
+ expectedDir.append("extension-data");
+ expectedDir.append(ADDON.id);
+
+ do_test_pending();
+ do_check_false(expectedDir.exists());
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9");
+ startupManager();
+
+ installAllFiles([do_get_addon(ADDON.addon)], function() {
+ restartManager();
+
+ AddonManager.getAddonByID(ADDON.id, function(item) {
+ item.getDataDirectory(promise_callback);
+ });
+ });
+}
+
+function promise_callback() {
+ do_check_eq(arguments.length, 2);
+ var expectedDir = gProfD.clone();
+ expectedDir.append("extension-data");
+ expectedDir.append(ADDON.id);
+
+ do_check_eq(arguments[0], expectedDir.path);
+ do_check_true(expectedDir.exists());
+ do_check_true(expectedDir.isDirectory());
+
+ do_check_eq(arguments[1], null);
+
+ // Cleanup.
+ expectedDir.parent.remove(true);
+
+ do_test_finished();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_default_providers_pref.js b/toolkit/mozapps/webextensions/test/xpcshell/test_default_providers_pref.js
new file mode 100644
index 000000000..1b61e033a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_default_providers_pref.js
@@ -0,0 +1,13 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the extensions.defaultProviders.enabled pref which turns
+// off the default XPIProvider and LightweightThemeManager.
+
+function run_test() {
+ Services.prefs.setBoolPref("extensions.defaultProviders.enabled", false);
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+ do_check_false(AddonManager.isInstallEnabled("application/x-xpinstall"));
+ Services.prefs.clearUserPref("extensions.defaultProviders.enabled");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update.js b/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update.js
new file mode 100644
index 000000000..3d7eef051
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update.js
@@ -0,0 +1,260 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that delaying an update works
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const tempdir = gTmpD.clone();
+
+const IGNORE_ID = "test_delay_update_ignore@tests.mozilla.org";
+const COMPLETE_ID = "test_delay_update_complete@tests.mozilla.org";
+const DEFER_ID = "test_delay_update_defer@tests.mozilla.org";
+
+const TEST_IGNORE_PREF = "delaytest.ignore";
+
+// Note that we would normally use BootstrapMonitor but it currently requires
+// the objects in `data` to be serializable, and we need a real reference to the
+// `instanceID` symbol to test.
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+// Create and configure the HTTP server.
+let testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_delay_updates_complete.rdf", testserver);
+mapFile("/data/test_delay_updates_ignore.rdf", testserver);
+mapFile("/data/test_delay_updates_defer.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+function* createIgnoreAddon() {
+ writeInstallRDFToDir({
+ id: IGNORE_ID,
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ updateURL: `http://localhost:${gPort}/data/test_delay_updates_ignore.rdf`,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Delay Update Ignore",
+ }, profileDir, IGNORE_ID, "bootstrap.js");
+
+ let unpacked_addon = profileDir.clone();
+ unpacked_addon.append(IGNORE_ID);
+ do_get_file("data/test_delay_update_ignore/bootstrap.js")
+ .copyTo(unpacked_addon, "bootstrap.js");
+}
+
+function* createCompleteAddon() {
+ writeInstallRDFToDir({
+ id: COMPLETE_ID,
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ updateURL: `http://localhost:${gPort}/data/test_delay_updates_complete.rdf`,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Delay Update Complete",
+ }, profileDir, COMPLETE_ID, "bootstrap.js");
+
+ let unpacked_addon = profileDir.clone();
+ unpacked_addon.append(COMPLETE_ID);
+ do_get_file("data/test_delay_update_complete/bootstrap.js")
+ .copyTo(unpacked_addon, "bootstrap.js");
+}
+
+function* createDeferAddon() {
+ writeInstallRDFToDir({
+ id: DEFER_ID,
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ updateURL: `http://localhost:${gPort}/data/test_delay_updates_defer.rdf`,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Delay Update Defer",
+ }, profileDir, DEFER_ID, "bootstrap.js");
+
+ let unpacked_addon = profileDir.clone();
+ unpacked_addon.append(DEFER_ID);
+ do_get_file("data/test_delay_update_defer/bootstrap.js")
+ .copyTo(unpacked_addon, "bootstrap.js");
+}
+
+// add-on registers upgrade listener, and ignores update.
+add_task(function*() {
+
+ yield createIgnoreAddon();
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Delay Update Ignore");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_POSTPONED);
+
+ // addon upgrade has been delayed
+ let addon_postponed = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "Test Delay Update Ignore");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+ do_check_true(Services.prefs.getBoolPref(TEST_IGNORE_PREF));
+
+ // restarting allows upgrade to proceed
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "Test Delay Update Ignore");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
+
+// add-on registers upgrade listener, and allows update.
+add_task(function*() {
+
+ yield createCompleteAddon();
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Delay Update Complete");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ yield promiseCompleteAllInstalls([install]);
+
+ // upgrade is initially postponed
+ let addon_postponed = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "Test Delay Update Complete");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // addon upgrade has been allowed
+ let [addon_allowed] = yield promiseAddonEvent("onInstalled");
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "Test Delay Update Complete");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // restarting changes nothing
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "Test Delay Update Complete");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
+
+// add-on registers upgrade listener, initially defers update then allows upgrade
+add_task(function*() {
+
+ yield createDeferAddon();
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Delay Update Defer");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ yield promiseCompleteAllInstalls([install]);
+
+ // upgrade is initially postponed
+ let addon_postponed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "Test Delay Update Defer");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // add-on will not allow upgrade until fake event fires
+ AddonManagerPrivate.callAddonListeners("onFakeEvent");
+
+ // addon upgrade has been allowed
+ let [addon_allowed] = yield promiseAddonEvent("onInstalled");
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "Test Delay Update Defer");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // restarting changes nothing
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "Test Delay Update Defer");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update_webextension.js b/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update_webextension.js
new file mode 100644
index 000000000..cdfac8f8c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_delay_update_webextension.js
@@ -0,0 +1,344 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that delaying an update works for WebExtensions.
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+/* globals browser*/
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const tempdir = gTmpD.clone();
+const stageDir = profileDir.clone();
+stageDir.append("staged");
+
+const IGNORE_ID = "test_delay_update_ignore_webext@tests.mozilla.org";
+const COMPLETE_ID = "test_delay_update_complete_webext@tests.mozilla.org";
+const DEFER_ID = "test_delay_update_defer_webext@tests.mozilla.org";
+const NOUPDATE_ID = "test_no_update_webext@tests.mozilla.org";
+
+// Create and configure the HTTP server.
+let testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_delay_updates_complete.json", testserver);
+mapFile("/data/test_delay_updates_ignore.json", testserver);
+mapFile("/data/test_delay_updates_defer.json", testserver);
+mapFile("/data/test_no_update.json", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+const { Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseWebExtensionStartup() {
+ return new Promise(resolve => {
+ let listener = (event, extension) => {
+ Management.off("startup", listener);
+ resolve(extension);
+ };
+
+ Management.on("startup", listener);
+ });
+}
+
+// add-on registers upgrade listener, and ignores update.
+add_task(function* delay_updates_ignore() {
+ startupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": IGNORE_ID,
+ "update_url": `http://localhost:${gPort}/data/test_delay_updates_ignore.json`,
+ },
+ },
+ },
+ background() {
+ browser.runtime.onUpdateAvailable.addListener(details => {
+ if (details) {
+ if (details.version) {
+ // This should be the version of the pending update.
+ browser.test.assertEq("2.0", details.version, "correct version");
+ browser.test.notifyPass("delay");
+ }
+ } else {
+ browser.test.fail("no details object passed");
+ }
+ });
+ browser.test.sendMessage("ready");
+ },
+ }, IGNORE_ID);
+
+ yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+
+ let addon = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Generated extension");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_POSTPONED);
+
+ // addon upgrade has been delayed
+ let addon_postponed = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "Generated extension");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ yield extension.awaitFinish("delay");
+
+ // restarting allows upgrade to proceed
+ yield extension.markUnloaded();
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(IGNORE_ID);
+ yield promiseWebExtensionStartup();
+
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "Delay Upgrade");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield addon_upgraded.uninstall();
+ yield promiseShutdownManager();
+});
+
+// add-on registers upgrade listener, and allows update.
+add_task(function* delay_updates_complete() {
+ startupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": COMPLETE_ID,
+ "update_url": `http://localhost:${gPort}/data/test_delay_updates_complete.json`,
+ },
+ },
+ },
+ background() {
+ browser.runtime.onUpdateAvailable.addListener(details => {
+ browser.test.notifyPass("reload");
+ browser.runtime.reload();
+ });
+ browser.test.sendMessage("ready");
+ },
+ }, COMPLETE_ID);
+
+ yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+
+ let addon = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Generated extension");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ let promiseInstalled = promiseAddonEvent("onInstalled");
+ yield promiseCompleteAllInstalls([install]);
+
+ yield extension.awaitFinish("reload");
+
+ // addon upgrade has been allowed
+ let [addon_allowed] = yield promiseInstalled;
+ yield promiseWebExtensionStartup();
+
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "Delay Upgrade");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ if (stageDir.exists()) {
+ do_throw("Staging directory should not exist for formerly-postponed extension");
+ }
+
+ yield extension.markUnloaded();
+ yield addon_allowed.uninstall();
+ yield promiseShutdownManager();
+});
+
+// add-on registers upgrade listener, initially defers update then allows upgrade
+add_task(function* delay_updates_defer() {
+ startupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": DEFER_ID,
+ "update_url": `http://localhost:${gPort}/data/test_delay_updates_defer.json`,
+ },
+ },
+ },
+ background() {
+ browser.runtime.onUpdateAvailable.addListener(details => {
+ // Upgrade will only proceed when "allow" message received.
+ browser.test.onMessage.addListener(msg => {
+ if (msg == "allow") {
+ browser.test.notifyPass("allowed");
+ browser.runtime.reload();
+ } else {
+ browser.test.fail(`wrong message: ${msg}`);
+ }
+ });
+ browser.test.sendMessage("truly ready");
+ });
+ browser.test.sendMessage("ready");
+ },
+ }, DEFER_ID);
+
+ yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+
+ let addon = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Generated extension");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+
+ let promiseInstalled = promiseAddonEvent("onInstalled");
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_POSTPONED);
+
+ // upgrade is initially postponed
+ let addon_postponed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "Generated extension");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // add-on will not allow upgrade until message is received
+ yield extension.awaitMessage("truly ready");
+ extension.sendMessage("allow");
+ yield extension.awaitFinish("allowed");
+
+ // addon upgrade has been allowed
+ let [addon_allowed] = yield promiseInstalled;
+ yield promiseWebExtensionStartup();
+
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "Delay Upgrade");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ yield extension.markUnloaded();
+ yield promiseRestartManager();
+
+ // restart changes nothing
+ addon_allowed = yield promiseAddonByID(DEFER_ID);
+ yield promiseWebExtensionStartup();
+
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "Delay Upgrade");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ yield addon_allowed.uninstall();
+ yield promiseShutdownManager();
+});
+
+// browser.runtime.reload() without a pending upgrade should just reload.
+add_task(function* runtime_reload() {
+ startupManager();
+
+ let extension = ExtensionTestUtils.loadExtension({
+ useAddonManager: "permanent",
+ manifest: {
+ "version": "1.0",
+ "applications": {
+ "gecko": {
+ "id": NOUPDATE_ID,
+ "update_url": `http://localhost:${gPort}/data/test_no_update.json`,
+ },
+ },
+ },
+ background() {
+ browser.test.onMessage.addListener(msg => {
+ if (msg == "reload") {
+ browser.runtime.reload();
+ } else {
+ browser.test.fail(`wrong message: ${msg}`);
+ }
+ });
+ browser.test.sendMessage("ready");
+ },
+ }, NOUPDATE_ID);
+
+ yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
+
+ let addon = yield promiseAddonByID(NOUPDATE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Generated extension");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ yield promiseFindAddonUpdates(addon);
+
+ extension.sendMessage("reload");
+ // Wait for extension to restart, to make sure reload works.
+ yield promiseWebExtensionStartup();
+
+ addon = yield promiseAddonByID(NOUPDATE_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Generated extension");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ yield extension.markUnloaded();
+ yield addon.uninstall();
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_dependencies.js b/toolkit/mozapps/webextensions/test/xpcshell/test_dependencies.js
new file mode 100644
index 000000000..3afc03f84
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_dependencies.js
@@ -0,0 +1,144 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+startupManager();
+
+const BOOTSTRAP = String.raw`
+ Components.utils.import("resource://gre/modules/Services.jsm");
+
+ function startup(data) {
+ Services.obs.notifyObservers(null, "test-addon-bootstrap-startup", data.id);
+ }
+ function shutdown(data) {
+ Services.obs.notifyObservers(null, "test-addon-bootstrap-shutdown", data.id);
+ }
+ function install() {}
+ function uninstall() {}
+`;
+
+const ADDONS = [
+ {
+ id: "addon1@dependency-test.mozilla.org",
+ dependencies: ["addon2@dependency-test.mozilla.org"],
+ },
+ {
+ id: "addon2@dependency-test.mozilla.org",
+ dependencies: ["addon3@dependency-test.mozilla.org"],
+ },
+ {
+ id: "addon3@dependency-test.mozilla.org",
+ },
+ {
+ id: "addon4@dependency-test.mozilla.org",
+ },
+ {
+ id: "addon5@dependency-test.mozilla.org",
+ dependencies: ["addon2@dependency-test.mozilla.org"],
+ },
+];
+
+let addonFiles = [];
+
+let events = [];
+add_task(function* setup() {
+ let startupObserver = (subject, topic, data) => {
+ events.push(["startup", data]);
+ };
+ let shutdownObserver = (subject, topic, data) => {
+ events.push(["shutdown", data]);
+ };
+
+ Services.obs.addObserver(startupObserver, "test-addon-bootstrap-startup", false);
+ Services.obs.addObserver(shutdownObserver, "test-addon-bootstrap-shutdown", false);
+ do_register_cleanup(() => {
+ Services.obs.removeObserver(startupObserver, "test-addon-bootstrap-startup");
+ Services.obs.removeObserver(shutdownObserver, "test-addon-bootstrap-shutdown");
+ });
+
+ for (let addon of ADDONS) {
+ Object.assign(addon, {
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1",
+ }],
+ version: "1.0",
+ name: addon.id,
+ bootstrap: true,
+ });
+
+ addonFiles.push(createTempXPIFile(addon, {"bootstrap.js": BOOTSTRAP}));
+ }
+});
+
+add_task(function*() {
+ deepEqual(events, [], "Should have no events");
+
+ yield promiseInstallAllFiles([addonFiles[3]]);
+
+ deepEqual(events, [
+ ["startup", ADDONS[3].id],
+ ]);
+
+ events.length = 0;
+
+ yield promiseInstallAllFiles([addonFiles[0]]);
+ deepEqual(events, [], "Should have no events");
+
+ yield promiseInstallAllFiles([addonFiles[1]]);
+ deepEqual(events, [], "Should have no events");
+
+ yield promiseInstallAllFiles([addonFiles[2]]);
+
+ deepEqual(events, [
+ ["startup", ADDONS[2].id],
+ ["startup", ADDONS[1].id],
+ ["startup", ADDONS[0].id],
+ ]);
+
+ events.length = 0;
+
+ yield promiseInstallAllFiles([addonFiles[2]]);
+
+ deepEqual(events, [
+ ["shutdown", ADDONS[0].id],
+ ["shutdown", ADDONS[1].id],
+ ["shutdown", ADDONS[2].id],
+
+ ["startup", ADDONS[2].id],
+ ["startup", ADDONS[1].id],
+ ["startup", ADDONS[0].id],
+ ]);
+
+ events.length = 0;
+
+ yield promiseInstallAllFiles([addonFiles[4]]);
+
+ deepEqual(events, [
+ ["startup", ADDONS[4].id],
+ ]);
+
+ events.length = 0;
+
+ yield promiseRestartManager();
+
+ deepEqual(events, [
+ ["shutdown", ADDONS[4].id],
+ ["shutdown", ADDONS[3].id],
+ ["shutdown", ADDONS[0].id],
+ ["shutdown", ADDONS[1].id],
+ ["shutdown", ADDONS[2].id],
+
+ ["startup", ADDONS[2].id],
+ ["startup", ADDONS[1].id],
+ ["startup", ADDONS[0].id],
+ ["startup", ADDONS[3].id],
+ ["startup", ADDONS[4].id],
+ ]);
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_dictionary.js b/toolkit/mozapps/webextensions/test/xpcshell/test_dictionary.js
new file mode 100644
index 000000000..f4b6a0535
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_dictionary.js
@@ -0,0 +1,811 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that bootstrappable add-ons can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+Components.utils.import("resource://testing-common/httpd.js");
+// Create and configure the HTTP server.
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register files with server
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_dictionary.rdf", testserver);
+
+/**
+ * This object is both a factory and an mozISpellCheckingEngine implementation (so, it
+ * is de-facto a service). It's also an interface requestor that gives out
+ * itself when asked for mozISpellCheckingEngine.
+ */
+var HunspellEngine = {
+ dictionaryDirs: [],
+ listener: null,
+
+ QueryInterface: function hunspell_qi(iid) {
+ if (iid.equals(Components.interfaces.nsISupports) ||
+ iid.equals(Components.interfaces.nsIFactory) ||
+ iid.equals(Components.interfaces.mozISpellCheckingEngine))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+ createInstance: function hunspell_ci(outer, iid) {
+ if (outer)
+ throw Components.results.NS_ERROR_NO_AGGREGATION;
+ return this.QueryInterface(iid);
+ },
+ lockFactory: function hunspell_lockf(lock) {
+ throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+ },
+
+ addDirectory: function hunspell_addDirectory(dir) {
+ this.dictionaryDirs.push(dir);
+ if (this.listener)
+ this.listener("addDirectory");
+ },
+
+ removeDirectory: function hunspell_addDirectory(dir) {
+ this.dictionaryDirs.splice(this.dictionaryDirs.indexOf(dir), 1);
+ if (this.listener)
+ this.listener("removeDirectory");
+ },
+
+ getInterface: function hunspell_gi(iid) {
+ if (iid.equals(Components.interfaces.mozISpellCheckingEngine))
+ return this;
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ },
+
+ contractID: "@mozilla.org/spellchecker/engine;1",
+ classID: Components.ID("{6f3c63bc-a4fd-449b-9a58-a2d9bd972cce}"),
+
+ activate: function hunspell_activate() {
+ this.origClassID = Components.manager.nsIComponentRegistrar
+ .contractIDToCID(this.contractID);
+ this.origFactory = Components.manager
+ .getClassObject(Components.classes[this.contractID],
+ Components.interfaces.nsIFactory);
+
+ Components.manager.nsIComponentRegistrar
+ .unregisterFactory(this.origClassID, this.origFactory);
+ Components.manager.nsIComponentRegistrar.registerFactory(this.classID,
+ "Test hunspell", this.contractID, this);
+ },
+
+ deactivate: function hunspell_deactivate() {
+ Components.manager.nsIComponentRegistrar.unregisterFactory(this.classID, this);
+ Components.manager.nsIComponentRegistrar.registerFactory(this.origClassID,
+ "Hunspell", this.contractID, this.origFactory);
+ },
+
+ isDictionaryEnabled: function hunspell_isDictionaryEnabled(name) {
+ return this.dictionaryDirs.some(function(dir) {
+ var dic = dir.clone();
+ dic.append(name);
+ return dic.exists();
+ });
+ }
+};
+
+function run_test() {
+ do_test_pending();
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ HunspellEngine.activate();
+
+ AddonManager.getInstallForFile(do_get_addon("test_dictionary"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "dictionary");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Dictionary");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ HunspellEngine.listener = function(aEvent) {
+ HunspellEngine.listener = null;
+ do_check_eq(aEvent, "addDirectory");
+ do_execute_soon(check_test_1);
+ };
+ });
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "ab-CD@dictionaries.addons.mozilla.org");
+
+ let chromeReg = AM_Cc["@mozilla.org/chrome/chrome-registry;1"].
+ getService(AM_Ci.nsIChromeRegistry);
+ try {
+ chromeReg.convertChromeURL(NetUtil.newURI("chrome://dict/content/dict.xul"));
+ do_throw("Chrome manifest should not have been registered");
+ }
+ catch (e) {
+ // Expected the chrome url to not be registered
+ }
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ startupManager(false);
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ run_test_4();
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+ startupManager(false);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ run_test_7();
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ check_test_7();
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Test that a bootstrapped extension dropped into the profile loads properly
+// on startup and doesn't cause an EM restart
+function run_test_8() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that items detected as removed during startup get removed properly
+function run_test_9() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.remove(true);
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_eq(b1, null);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+
+// Tests that bootstrapped extensions are correctly loaded even if the app is
+// upgraded at the same time
+function run_test_12() {
+ shutdownManager();
+
+ let dir = profileDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager(true);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ b1.uninstall();
+ do_execute_soon(run_test_16);
+ });
+}
+
+
+// Tests that bootstrapped extensions don't get loaded when in safe mode
+function run_test_16() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_dictionary")], function() {
+ // spin the event loop to let the addon finish starting
+ do_execute_soon(function check_installed_dictionary() {
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ shutdownManager();
+
+ // Should have stopped
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ gAppInfo.inSafeMode = true;
+ startupManager(false);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1_2) {
+ // Should still be stopped
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_false(b1_2.isActive);
+
+ shutdownManager();
+ gAppInfo.inSafeMode = false;
+ startupManager(false);
+
+ // Should have started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1_3) {
+ b1_3.uninstall();
+
+ do_execute_soon(run_test_17);
+ });
+ }));
+ }));
+ });
+ });
+}
+
+// Check that a bootstrapped extension in a non-profile location is loaded
+function run_test_17() {
+ shutdownManager();
+
+ let dir = userExtDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(AM_Ci.nsIZipReader);
+ zip.open(do_get_addon("test_dictionary"));
+ dir.append("install.rdf");
+ zip.extract("install.rdf", dir);
+ dir = dir.parent;
+ dir.append("dictionaries");
+ dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+ dir.append("ab-CD.dic");
+ zip.extract("dictionaries/ab-CD.dic", dir);
+ zip.close();
+
+ startupManager();
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ // Should have installed and started
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+
+ // From run_test_21
+ dir = userExtDir.clone();
+ dir.append("ab-CD@dictionaries.addons.mozilla.org");
+ dir.remove(true);
+
+ restartManager();
+
+ run_test_23();
+ }));
+}
+
+// Tests that installing from a URL doesn't require a restart
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_dictionary.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], function() {
+ do_check_eq(install.type, "dictionary");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Dictionary");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+ do_check_not_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let addon = install.addon;
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ // spin to let the addon startup finish
+ do_execute_soon(check_test_23);
+ });
+ });
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_23() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+ do_check_in_crash_annotation("ab-CD@dictionaries.addons.mozilla.org", "1.0");
+
+ let dir = do_get_addon_root_uri(profileDir, "ab-CD@dictionaries.addons.mozilla.org");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 0);
+
+ restartManager();
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1_2) {
+ b1_2.uninstall();
+ do_execute_soon(run_test_25);
+ });
+ }));
+ });
+ });
+}
+
+// Tests that updating from a bootstrappable add-on to a normal add-on calls
+// the uninstall method
+function run_test_25() {
+ restartManager();
+
+ HunspellEngine.listener = function(aEvent) {
+ HunspellEngine.listener = null;
+ do_check_eq(aEvent, "addDirectory");
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ installAllFiles([do_get_addon("test_dictionary_2")], function test_25_installed2() {
+ // Needs a restart to complete this so the old version stays running
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "2.0");
+ do_check_true(b1_2.isActive);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_execute_soon(run_test_26);
+ });
+ }));
+ });
+ };
+
+ installAllFiles([do_get_addon("test_dictionary")], function test_25_installed() { });
+}
+
+// Tests that updating from a normal add-on to a bootstrappable add-on calls
+// the install method
+function run_test_26() {
+ installAllFiles([do_get_addon("test_dictionary")], function test_26_install() {
+ // Needs a restart to complete this
+ do_check_false(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_true(b1.isActive);
+ do_check_true(hasFlag(b1.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ restartManager();
+
+ do_check_true(HunspellEngine.isDictionaryEnabled("ab-CD.dic"));
+
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1_2) {
+ do_check_neq(b1_2, null);
+ do_check_eq(b1_2.version, "1.0");
+ do_check_true(b1_2.isActive);
+ do_check_eq(b1_2.pendingOperations, AddonManager.PENDING_NONE);
+
+ HunspellEngine.deactivate();
+ b1_2.uninstall();
+ do_execute_soon(run_test_27);
+ });
+ }));
+ });
+}
+
+// Tests that an update check from a normal add-on to a bootstrappable add-on works
+function run_test_27() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "ab-CD@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "ab-CD@dictionaries.addons.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_27));
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_27(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("ab-CD@dictionaries.addons.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "2.0");
+ do_check_eq(b1.type, "dictionary");
+ b1.uninstall();
+ do_execute_soon(run_test_28);
+ });
+}
+
+// Tests that an update check from a bootstrappable add-on to a normal add-on works
+function run_test_28() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "ef@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ type: "64",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary ef",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "ef@dictionaries.addons.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_28));
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_28(install) {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("ef@dictionaries.addons.mozilla.org", function(b2) {
+ do_check_neq(b2, null);
+ do_check_eq(b2.version, "2.0");
+ do_check_eq(b2.type, "extension");
+ b2.uninstall();
+ do_execute_soon(run_test_29);
+ });
+}
+
+// Tests that an update check from a bootstrappable add-on to a bootstrappable add-on works
+function run_test_29() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "gh@dictionaries.addons.mozilla.org",
+ version: "1.0",
+ type: "64",
+ updateURL: "http://localhost:" + gPort + "/data/test_dictionary.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Dictionary gh",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "gh@dictionaries.addons.mozilla.org": [
+ ["onInstalling", false /* = no restart */],
+ ["onInstalled", false]
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_29);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function check_test_29(install) {
+ AddonManager.getAddonByID("gh@dictionaries.addons.mozilla.org", function(b2) {
+ do_check_neq(b2, null);
+ do_check_eq(b2.version, "2.0");
+ do_check_eq(b2.type, "dictionary");
+
+ prepare_test({
+ "gh@dictionaries.addons.mozilla.org": [
+ ["onUninstalling", false],
+ ["onUninstalled", false],
+ ]
+ }, [
+ ], callback_soon(finish_test_29));
+
+ b2.uninstall();
+ });
+}
+
+function finish_test_29() {
+ testserver.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_disable.js b/toolkit/mozapps/webextensions/test/xpcshell/test_disable.js
new file mode 100644
index 000000000..867715863
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_disable.js
@@ -0,0 +1,194 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// This verifies that add-ons can be disabled and enabled.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ optionsURL: "chrome://foo/content/options.xul",
+ aboutURL: "chrome://foo/content/about.xul",
+ iconURL: "chrome://foo/content/icon.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIconURL = null;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ writeInstallRDFForExtension(addon1, profileDir, addon1.id, "icon.png");
+ gIconURL = do_get_addon_root_uri(profileDir.clone(), addon1.id) + "icon.png";
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_1();
+ });
+ }));
+}
+
+// Disabling an add-on should work
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ a1.userDisabled = true;
+ do_check_eq(a1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(a1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(a1.iconURL, "chrome://foo/content/icon.png");
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_false(newa1.isActive);
+ do_check_true(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, null);
+ do_check_eq(newa1.optionsURL, null);
+ do_check_eq(newa1.iconURL, gIconURL);
+ do_check_false(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_2();
+ });
+ }));
+ });
+}
+
+// Enabling an add-on should work.
+function run_test_2() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ a1.userDisabled = false;
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE |
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_3();
+ });
+ }));
+ });
+}
+
+// Disabling then enabling without restart should fire onOperationCancelled.
+function run_test_3() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.userDisabled = true;
+ ensure_test_completed();
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.userDisabled = false;
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_true(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, "chrome://foo/content/about.xul");
+ do_check_eq(newa1.optionsURL, "chrome://foo/content/options.xul");
+ do_check_eq(newa1.iconURL, "chrome://foo/content/icon.png");
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_distribution.js b/toolkit/mozapps/webextensions/test/xpcshell/test_distribution.js
new file mode 100644
index 000000000..720b454cc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_distribution.js
@@ -0,0 +1,273 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons distributed with the application get installed
+// correctly
+
+// Allow distributed add-ons to install
+Services.prefs.setBoolPref("extensions.installDistroAddons", true);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const distroDir = gProfD.clone();
+distroDir.append("distribution");
+distroDir.append("extensions");
+registerDirectory("XREAppDist", distroDir.parent);
+
+var addon1_1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test version 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+var addon1_2 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test version 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+var addon1_3 = {
+ id: "addon1@tests.mozilla.org",
+ version: "3.0",
+ name: "Test version 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "5"
+ }]
+};
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function getInstalledVersion() {
+ return Services.prefs.getIntPref("bootstraptest.installed_version");
+}
+
+function setOldModificationTime() {
+ // Make sure the installed extension has an old modification time so any
+ // changes will be detected
+ shutdownManager()
+ let extension = gProfD.clone();
+ extension.append("extensions");
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack"))
+ extension.append("addon1@tests.mozilla.org");
+ else
+ extension.append("addon1@tests.mozilla.org.xpi");
+ setExtensionModifiedTime(extension, Date.now() - MAKE_FILE_OLD_DIFFERENCE);
+ startupManager(false);
+}
+
+function run_test() {
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests that on the first startup the add-on gets installed, with now as the
+// profile modifiedTime.
+function run_test_1() {
+ let extension = writeInstallRDFForExtension(addon1_1, distroDir);
+ setExtensionModifiedTime(extension, Date.now() - MAKE_FILE_OLD_DIFFERENCE);
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_false(a1.foreignInstall);
+
+ // Modification time should be updated when the addon is copied to the
+ // profile.
+ let testURI = a1.getResourceURI(TEST_UNPACKED ? "install.rdf" : "");
+ let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file;
+
+ do_check_true(testFile.exists());
+ let difference = testFile.lastModifiedTime - Date.now();
+ do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests that starting with a newer version in the distribution dir doesn't
+// install it yet
+function run_test_2() {
+ setOldModificationTime();
+
+ writeInstallRDFForExtension(addon1_2, distroDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that an app upgrade installs the newer version
+function run_test_3() {
+ restartManager("2");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_false(a1.foreignInstall);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that an app upgrade doesn't downgrade the extension
+function run_test_4() {
+ setOldModificationTime();
+
+ writeInstallRDFForExtension(addon1_1, distroDir);
+
+ restartManager("3");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Tests that after uninstalling a restart doesn't re-install the extension
+function run_test_5() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ a1.uninstall();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_2) {
+ do_check_eq(a1_2, null);
+
+ do_execute_soon(run_test_6);
+ });
+ }));
+}
+
+// Tests that upgrading the application still doesn't re-install the uninstalled
+// extension
+function run_test_6() {
+ restartManager("4");
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Tests that a pending install of a newer version of a distributed add-on
+// at app change still gets applied
+function run_test_7() {
+ Services.prefs.clearUserPref("extensions.installedDistroAddon.addon1@tests.mozilla.org");
+
+ installAllFiles([do_get_addon("test_distribution1_2")], function() {
+ restartManager(2);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ a1.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+}
+
+// Tests that a pending install of a older version of a distributed add-on
+// at app change gets replaced by the distributed version
+function run_test_8() {
+ restartManager();
+
+ writeInstallRDFForExtension(addon1_3, distroDir);
+
+ installAllFiles([do_get_addon("test_distribution1_2")], function() {
+ restartManager(3);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "3.0");
+ do_check_true(a1.isActive);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ a1.uninstall();
+ do_execute_soon(run_test_9);
+ });
+ });
+}
+
+// Tests that bootstrapped add-ons distributed start up correctly, also that
+// add-ons with multiple directories get copied fully
+function run_test_9() {
+ restartManager();
+
+ // Copy the test add-on to the distro dir
+ let addon = do_get_file("data/test_distribution2_2");
+ addon.copyTo(distroDir, "addon2@tests.mozilla.org");
+
+ restartManager("5");
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+
+ do_check_eq(getInstalledVersion(), 2);
+ do_check_eq(getActiveVersion(), 2);
+
+ do_check_true(a2.hasResource("bootstrap.js"));
+ do_check_true(a2.hasResource("subdir/dummy.txt"));
+ do_check_true(a2.hasResource("subdir/subdir2/dummy2.txt"));
+
+ // Currently installs are unpacked if the source is a directory regardless
+ // of the install.rdf property or the global preference
+
+ let addonDir = profileDir.clone();
+ addonDir.append("addon2@tests.mozilla.org");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("subdir");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("subdir2");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isDirectory());
+ addonDir.append("dummy2.txt");
+ do_check_true(addonDir.exists());
+ do_check_true(addonDir.isFile());
+
+ a2.uninstall();
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_dss.js b/toolkit/mozapps/webextensions/test/xpcshell/test_dss.js
new file mode 100644
index 000000000..b408cc9c7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_dss.js
@@ -0,0 +1,824 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// using a dynamic port in the addon metadata
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+// This verifies that themes behave as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+const PREF_EXTENSIONS_DSS_ENABLED = "extensions.dss.enabled";
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Observer to ensure a "lightweight-theme-styling-update" notification is sent
+// when expected
+var gLWThemeChanged = false;
+var LightweightThemeObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "lightweight-theme-styling-update")
+ return;
+
+ gLWThemeChanged = true;
+ }
+};
+
+AM_Cc["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .addObserver(LightweightThemeObserver, "lightweight-theme-styling-update", false);
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+ Services.prefs.setBoolPref(PREF_EXTENSIONS_DSS_ENABLED, true);
+ writeInstallRDFForExtension({
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ // We need a default theme for some of these things to work but we have hidden
+ // the one in the application directory.
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+ do_check_eq(t1.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_eq(t2.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks enabling one theme disables the others
+function run_test_1() {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(t1.userDisabled);
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(check_test_1);
+ });
+}
+
+function check_test_1() {
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme2/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_neq(t2, null);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_true(t2.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Removing the active theme should fall back to the default (not ideal in this
+// case since we don't have the default theme installed)
+function run_test_2() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("theme2@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(t2, null);
+ do_check_false(isThemeInAddonsList(profileDir, "theme2@tests.mozilla.org"));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Installing a lightweight theme should happen instantly and disable the default theme
+function run_test_3() {
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(null, p1);
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.type, "theme");
+ do_check_eq(p1.description, "A test theme");
+ do_check_eq(p1.creator, "Mozilla");
+ do_check_eq(p1.homepageURL, "http://localhost:" + gPort + "/data/index.html");
+ do_check_eq(p1.iconURL, "http://localhost:" + gPort + "/data/icon.png");
+ do_check_eq(p1.screenshots.length, 1);
+ do_check_eq(p1.screenshots[0], "http://localhost:" + gPort + "/data/preview.png");
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.userDisabled);
+ do_check_true(p1.isCompatible);
+ do_check_true(p1.providesUpdatesSecurely);
+ do_check_eq(p1.blocklistState, 0);
+ do_check_true(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true("isCompatibleWith" in p1);
+ do_check_true("findUpdates" in p1);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "1@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_4);
+ });
+ });
+}
+
+// Installing a second lightweight theme should disable the first with no restart
+function run_test_4() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ],
+ "2@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "2",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A second test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByIDs(["1@personas.mozilla.org",
+ "2@personas.mozilla.org"], function([p1, p2]) {
+ do_check_neq(null, p2);
+ do_check_false(p2.appDisabled);
+ do_check_false(p2.userDisabled);
+ do_check_true(p2.isActive);
+ do_check_eq(p2.pendingOperations, 0);
+ do_check_eq(p2.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+
+ do_check_neq(null, p1);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_ENABLE);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "2@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Switching to a custom theme should disable the lightweight theme and require
+// a restart. Cancelling that should also be possible.
+function run_test_5() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onOperationCancelled", true]
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+ do_check_true(hasFlag(AddonManager.PERM_CAN_ENABLE, p2.permissions));
+ do_check_true(gLWThemeChanged);
+
+ do_execute_soon(check_test_5);
+ });
+}
+
+function check_test_5() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Switching from a custom theme to a lightweight theme should require a restart
+function run_test_6() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_6);
+ });
+}
+
+function check_test_6() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Uninstalling a lightweight theme should not require a restart
+function run_test_7() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ p1.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 1);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Uninstalling a lightweight theme in use should not require a restart and it
+// should reactivate the default theme
+// Also, uninstalling a lightweight theme in use should send a
+// "lightweight-theme-styling-update" notification through the observer service
+function run_test_8() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("2@personas.mozilla.org", function(p2) {
+ p2.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 0);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Uninstalling a theme not in use should not require a restart
+function run_test_9() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ t1.uninstall();
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(newt1) {
+ do_check_eq(newt1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_10);
+ });
+ });
+}
+
+// Uninstalling a custom theme in use should require a restart
+function run_test_10() {
+ AddonManager.getAddonByID("theme2@tests.mozilla.org", callback_soon(function(oldt2) {
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ oldt2.userDisabled = false;
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ callback_soon(function([d, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ t2.uninstall();
+
+ ensure_test_completed();
+ do_check_false(gLWThemeChanged);
+
+ restartManager();
+
+ do_execute_soon(run_test_11);
+ }));
+ }));
+}
+
+// Installing a custom theme not in use should not require a restart
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_11));
+ install.install();
+ });
+}
+
+function check_test_11() {
+ restartManager();
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ var previewSpec = do_get_addon_root_uri(profileDir, "theme1@tests.mozilla.org") + "preview.png";
+ do_check_eq(t1.screenshots.length, 1);
+ do_check_eq(t1.screenshots[0], previewSpec);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Updating a custom theme not in use should not require a restart
+function run_test_12() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_12);
+ install.install();
+ });
+}
+
+function check_test_12() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Updating a custom theme in use should require a restart
+function run_test_13() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onInstalling",
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+ }));
+}
+
+function check_test_13() {
+ restartManager();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(gLWThemeChanged);
+ t1.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+}
+
+// Switching from a lightweight theme to the default theme should not require
+// a restart
+function run_test_14() {
+ restartManager();
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png"
+ };
+
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ d.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_duplicateplugins.js b/toolkit/mozapps/webextensions/test/xpcshell/test_duplicateplugins.js
new file mode 100644
index 000000000..4d1848ea4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_duplicateplugins.js
@@ -0,0 +1,187 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+var Ci = Components.interfaces;
+
+// This verifies that duplicate plugins are coalesced and maintain their ID
+// across restarts.
+
+var PLUGINS = [{
+ name: "Duplicate Plugin 1",
+ description: "A duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/home/mozilla/.plugins/dupplugin1.so"
+}, {
+ name: "Duplicate Plugin 1",
+ description: "A duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/usr/lib/plugins/dupplugin1.so"
+}, {
+ name: "Duplicate Plugin 2",
+ description: "Another duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/home/mozilla/.plugins/dupplugin2.so"
+}, {
+ name: "Duplicate Plugin 2",
+ description: "Another duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/usr/lib/plugins/dupplugin2.so"
+}, {
+ name: "Non-duplicate Plugin", // 3
+ description: "Not a duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/home/mozilla/.plugins/dupplugin3.so"
+}, {
+ name: "Non-duplicate Plugin", // 4
+ description: "Not a duplicate because the descriptions are different",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/usr/lib/plugins/dupplugin4.so"
+}, {
+ name: "Another Non-duplicate Plugin", // 5
+ description: "Not a duplicate plugin",
+ version: "1",
+ blocklisted: false,
+ enabledState: Ci.nsIPluginTag.STATE_ENABLED,
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ },
+ filename: "/home/mozilla/.plugins/dupplugin5.so"
+}];
+
+// A fake plugin host to return the plugins defined above
+var PluginHost = {
+ getPluginTags: function(countRef) {
+ countRef.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Components.interfaces.nsIPluginHost)
+ || iid.equals(Components.interfaces.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
+
+var gPluginIDs = [null, null, null, null, null];
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref("media.gmp-provider.enabled", false);
+
+ startupManager();
+
+ run_test_1();
+}
+
+function found_plugin(aNum, aId) {
+ if (gPluginIDs[aNum])
+ do_throw("Found duplicate of plugin " + aNum);
+ gPluginIDs[aNum] = aId;
+}
+
+// Test that the plugins were coalesced and all appear in the returned list
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["plugin"], function(aAddons) {
+ do_check_eq(aAddons.length, 5);
+ aAddons.forEach(function(aAddon) {
+ if (aAddon.name == "Duplicate Plugin 1") {
+ found_plugin(0, aAddon.id);
+ do_check_eq(aAddon.description, "A duplicate plugin");
+ }
+ else if (aAddon.name == "Duplicate Plugin 2") {
+ found_plugin(1, aAddon.id);
+ do_check_eq(aAddon.description, "Another duplicate plugin");
+ }
+ else if (aAddon.name == "Another Non-duplicate Plugin") {
+ found_plugin(5, aAddon.id);
+ do_check_eq(aAddon.description, "Not a duplicate plugin");
+ }
+ else if (aAddon.name == "Non-duplicate Plugin") {
+ if (aAddon.description == "Not a duplicate plugin")
+ found_plugin(3, aAddon.id);
+ else if (aAddon.description == "Not a duplicate because the descriptions are different")
+ found_plugin(4, aAddon.id);
+ else
+ do_throw("Found unexpected plugin with description " + aAddon.description);
+ }
+ else {
+ do_throw("Found unexpected plugin " + aAddon.name);
+ }
+ });
+
+ run_test_2();
+ });
+}
+
+// Test that disabling a coalesced plugin disables all its tags
+function run_test_2() {
+ AddonManager.getAddonByID(gPluginIDs[0], function(p) {
+ do_check_false(p.userDisabled);
+ p.userDisabled = true;
+ do_check_true(PLUGINS[0].disabled);
+ do_check_true(PLUGINS[1].disabled);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that IDs persist across restart
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID(gPluginIDs[0], callback_soon(function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Duplicate Plugin 1");
+ do_check_eq(p.description, "A duplicate plugin");
+
+ // Reorder the plugins and restart again
+ [PLUGINS[0], PLUGINS[1]] = [PLUGINS[1], PLUGINS[0]];
+ restartManager();
+
+ AddonManager.getAddonByID(gPluginIDs[0], function(p_2) {
+ do_check_neq(p_2, null);
+ do_check_eq(p_2.name, "Duplicate Plugin 1");
+ do_check_eq(p_2.description, "A duplicate plugin");
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_e10s_restartless.js b/toolkit/mozapps/webextensions/test/xpcshell/test_e10s_restartless.js
new file mode 100644
index 000000000..1a3a0e747
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_e10s_restartless.js
@@ -0,0 +1,429 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+const ID2 = "bootstrap2@tests.mozilla.org";
+
+const APP_STARTUP = 1;
+const ADDON_INSTALL = 5;
+
+function getStartupReason(id) {
+ let info = BootstrapMonitor.started.get(id);
+ return info ? info.reason : undefined;
+}
+
+BootstrapMonitor.init();
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+startupManager();
+
+function* check_normal() {
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, install.addon);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+ addon.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+ addon.userDisabled = false;
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_true(addon.isActive);
+ do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ yield promiseRestartManager();
+}
+
+// Installing the add-on normally doesn't require a restart
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = false;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", false);
+
+ yield check_normal();
+});
+
+// Enabling the pref doesn't change anything
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = false;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+ yield check_normal();
+});
+
+// Default e10s doesn't change anything
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", false);
+
+ yield check_normal();
+});
+
+// Pref and e10s blocks install
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_true(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ yield promiseRestartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+ addon.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+
+ do_check_true(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+ addon.userDisabled = false;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ do_check_true(addon.isActive);
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ yield promiseRestartManager();
+});
+
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_true(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ yield promiseRestartManager();
+
+ // After install and restart we should block.
+ let blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+ addon.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+ do_check_true(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+
+ yield promiseRestartManager();
+
+ // After disable and restart we should not block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+ addon = yield promiseAddonByID(ID);
+ addon.userDisabled = false;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ yield promiseRestartManager();
+
+ // After re-enable and restart we should block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ do_check_true(addon.isActive);
+ BootstrapMonitor.checkAddonStarted(ID);
+ // This should probably be ADDON_ENABLE, but its not easy to make
+ // that happen. See bug 1304392 for discussion.
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ yield promiseRestartManager();
+
+ // After uninstall and restart we should not block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+ restartManager();
+});
+
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+
+ let install1 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ let install2 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap2_1"), resolve));
+ yield promiseCompleteAllInstalls([install1, install2]);
+ do_check_eq(install1.state, AddonManager.STATE_INSTALLED);
+ do_check_eq(install2.state, AddonManager.STATE_INSTALLED);
+ do_check_true(hasFlag(install1.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+ do_check_true(hasFlag(install2.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ let addon = yield promiseAddonByID(ID);
+ let addon2 = yield promiseAddonByID(ID2);
+
+ do_check_eq(addon, null);
+ do_check_eq(addon2, null);
+
+ yield promiseRestartManager();
+
+ // After install and restart we should block.
+ let blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+
+ BootstrapMonitor.checkAddonInstalled(ID2);
+ BootstrapMonitor.checkAddonStarted(ID2);
+ do_check_eq(getStartupReason(ID2), ADDON_INSTALL);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ addon2 = yield promiseAddonByID(ID2);
+ do_check_neq(addon2, null);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+ addon.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_false(hasFlag(addon.pendingOperations, AddonManager.PENDING_DISABLE));
+ do_check_true(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+
+ yield promiseRestartManager();
+
+ // After disable one addon and restart we should block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ addon2 = yield promiseAddonByID(ID2);
+
+ do_check_false(hasFlag(addon2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE));
+ addon2.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID2);
+ do_check_false(addon2.isActive);
+ do_check_false(hasFlag(addon2.pendingOperations, AddonManager.PENDING_DISABLE));
+ do_check_true(hasFlag(addon2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE));
+
+ yield promiseRestartManager();
+
+ // After disable both addons and restart we should not block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+ addon = yield promiseAddonByID(ID);
+ addon.userDisabled = false;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_false(addon.isActive);
+ do_check_true(hasFlag(addon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ yield promiseRestartManager();
+
+ // After re-enable one addon and restart we should block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ do_check_true(addon.isActive);
+ BootstrapMonitor.checkAddonStarted(ID);
+ // Bug 1304392 again (see comment above)
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+
+ do_check_false(hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ yield promiseRestartManager();
+
+ // After uninstall the only enabled addon and restart we should not block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+ addon2 = yield promiseAddonByID(ID2);
+ addon2.uninstall();
+
+ restartManager();
+});
+
+// Check that the rollout policy sets work as expected
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+ Services.prefs.setCharPref("extensions.e10s.rollout.policy", "xpcshell-test");
+
+ // Both 'bootstrap1' and 'bootstrap2' addons are listed in the allowed policy
+ // set, so they should install and start normally.
+ yield check_normal();
+
+ // Check that the two add-ons can be installed together correctly as
+ // check_normal() only perform checks on bootstrap1.
+ let install1 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ let install2 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap2_1"), resolve));
+ yield promiseCompleteAllInstalls([install1, install2]);
+
+ do_check_eq(install1.state, AddonManager.STATE_INSTALLED);
+ do_check_eq(install2.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install1.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+ do_check_false(hasFlag(install2.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ let addon = yield promiseAddonByID(ID);
+ let addon2 = yield promiseAddonByID(ID2);
+
+ do_check_neq(addon, null);
+ do_check_neq(addon2, null);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID2);
+ BootstrapMonitor.checkAddonStarted(ID2);
+
+ yield promiseRestartManager();
+
+ // After install and restart e10s should not be blocked.
+ let blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+ // Check that adding bootstrap2 to the blocklist will trigger a disable of e10s.
+ Services.prefs.setCharPref("extensions.e10s.rollout.blocklist", ID2);
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ yield promiseRestartManager();
+
+ // Check that after restarting, e10s continues to be blocked.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ // Check that uninstalling bootstrap2 (which is in the blocklist) will
+ // cause e10s to be re-enabled.
+ addon2 = yield promiseAddonByID(ID2);
+ do_check_false(hasFlag(addon2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL));
+ addon2.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID2);
+ BootstrapMonitor.checkAddonNotInstalled(ID2);
+
+ yield promiseRestartManager();
+
+ // After uninstall the blocklisted addon and restart we should not block.
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_false(blocked);
+
+
+ // Let's perform similar checks again, now that bootstrap2 is in the blocklist.
+ // The bootstrap1 add-on should install and start correctly, but bootstrap2 should not.
+ addon = yield promiseAddonByID(ID);
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ yield promiseRestartManager();
+
+ install1 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ install2 = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap2_1"), resolve));
+ yield promiseCompleteAllInstalls([install1, install2]);
+
+ do_check_eq(install1.state, AddonManager.STATE_INSTALLED);
+ do_check_eq(install2.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install1.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+ do_check_true(hasFlag(install2.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ addon = yield promiseAddonByID(ID);
+ addon2 = yield promiseAddonByID(ID2);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon2, null);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ BootstrapMonitor.checkAddonNotInstalled(ID2);
+ BootstrapMonitor.checkAddonNotStarted(ID2);
+
+ yield promiseRestartManager();
+
+ blocked = Services.prefs.getBoolPref("extensions.e10sBlockedByAddons");
+ do_check_true(blocked);
+
+ // Clean-up
+ addon = yield promiseAddonByID(ID);
+ addon2 = yield promiseAddonByID(ID2);
+
+ addon.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+
+ addon2.uninstall();
+ BootstrapMonitor.checkAddonNotStarted(ID2);
+ BootstrapMonitor.checkAddonNotInstalled(ID2);
+
+ Services.prefs.clearUserPref("extensions.e10s.rollout.policy");
+ Services.prefs.clearUserPref("extensions.e10s.rollout.blocklist");
+
+ yield promiseRestartManager();
+});
+
+// The hotfix is unaffected
+add_task(function*() {
+ gAppInfo.browserTabsRemoteAutostart = true;
+ Services.prefs.setBoolPref("extensions.e10sBlocksEnabling", true);
+ Services.prefs.setCharPref("extensions.hotfix.id", ID);
+ Services.prefs.setBoolPref("extensions.hotfix.cert.checkAttributes", false);
+
+ yield check_normal();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_error.js b/toolkit/mozapps/webextensions/test/xpcshell/test_error.js
new file mode 100644
index 000000000..11a465b55
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_error.js
@@ -0,0 +1,90 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that various error conditions are handled correctly
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+// Checks that a local file validates ok
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_file("data/unsigned.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.error, 0);
+
+ install.cancel();
+
+ run_test_2();
+ });
+}
+
+// Checks that a corrupt file shows an error
+function run_test_2() {
+ AddonManager.getInstallForFile(do_get_file("data/corrupt.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ run_test_3();
+ });
+}
+
+// Checks that an empty file shows an error
+function run_test_3() {
+ AddonManager.getInstallForFile(do_get_file("data/empty.xpi"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ run_test_4();
+ });
+}
+
+// Checks that a file that doesn't match its hash shows an error
+function run_test_4() {
+ let url = Services.io.newFileURI(do_get_file("data/unsigned.xpi")).spec;
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_INCORRECT_HASH);
+
+ run_test_5();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+// Checks that a file that doesn't exist shows an error
+function run_test_5() {
+ let file = do_get_file("data");
+ file.append("missing.xpi");
+ AddonManager.getInstallForFile(file, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_NETWORK_FAILURE);
+
+ run_test_6();
+ });
+}
+
+// Checks that an add-on with an illegal ID shows an error
+function run_test_6() {
+ AddonManager.getInstallForFile(do_get_addon("test_bug567173"), function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_experiment.js b/toolkit/mozapps/webextensions/test/xpcshell/test_experiment.js
new file mode 100644
index 000000000..3dcd83da8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_experiment.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var scope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+const XPIProvider = scope.XPIProvider;
+const ID = "experiment1@tests.mozilla.org";
+
+var gIsNightly = false;
+
+function run_test() {
+ BootstrapMonitor.init();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ gIsNightly = isNightlyChannel();
+
+ run_next_test();
+}
+
+add_task(function* test_experiment() {
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseInstallAllFiles([do_get_addon("test_experiment1")]);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ let addon = yield promiseAddonByID(ID);
+ Assert.ok(addon, "Addon is found.");
+
+ Assert.ok(addon.userDisabled, "Experiments are userDisabled by default.");
+ Assert.ok(!addon.appDisabled, "Experiments are not appDisabled by compatibility.");
+ Assert.equal(addon.isActive, false, "Add-on is not active.");
+ Assert.equal(addon.updateURL, null, "No updateURL for experiments.");
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
+ "Background updates are disabled.");
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UNINSTALL + AddonManager.PERM_CAN_ENABLE,
+ "Permissions are minimal.");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+ "Should not be pending enable");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+ "Should not be pending disable");
+
+ // Setting applyBackgroundUpdates should not work.
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
+ "Setting applyBackgroundUpdates shouldn't do anything.");
+
+ let noCompatibleCalled = false;
+ let noUpdateCalled = false;
+ let finishedCalled = false;
+
+ let listener = {
+ onNoCompatibilityUpdateAvailable: () => { noCompatibleCalled = true; },
+ onNoUpdateAvailable: () => { noUpdateCalled = true; },
+ onUpdateFinished: () => { finishedCalled = true; },
+ };
+
+ addon.findUpdates(listener, "testing", null, null);
+ Assert.ok(noCompatibleCalled, "Listener called.");
+ Assert.ok(noUpdateCalled, "Listener called.");
+ Assert.ok(finishedCalled, "Listener called.");
+});
+
+// Changes to userDisabled should not be persisted to the database.
+add_task(function* test_userDisabledNotPersisted() {
+ let addon = yield promiseAddonByID(ID);
+ Assert.ok(addon, "Add-on is found.");
+ Assert.ok(addon.userDisabled, "Add-on is user disabled.");
+
+ let promise = promiseAddonEvent("onEnabled");
+ addon.userDisabled = false;
+ let [addon2] = yield promise;
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ Assert.equal(addon2.id, addon.id, "Changed add-on matches expected.");
+ Assert.equal(addon2.userDisabled, false, "Add-on is no longer user disabled.");
+ Assert.ok(addon2.isActive, "Add-on is active.");
+
+ Assert.ok(ID in XPIProvider.bootstrappedAddons,
+ "Experiment add-on listed in XPIProvider bootstrapped list.");
+
+ addon = yield promiseAddonByID(ID);
+ Assert.ok(addon, "Add-on retrieved.");
+ Assert.equal(addon.userDisabled, false, "Add-on is still enabled after API retrieve.");
+ Assert.ok(addon.isActive, "Add-on is still active.");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_ENABLE),
+ "Should not be pending enable");
+ Assert.ok(!(addon.pendingOperations & AddonManager.PENDING_DISABLE),
+ "Should not be pending disable");
+
+ // Now when we restart the manager the add-on should revert state.
+ yield promiseRestartManager();
+ let persisted = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
+ Assert.ok(!(ID in persisted),
+ "Experiment add-on not persisted to bootstrappedAddons.");
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ addon = yield promiseAddonByID(ID);
+ Assert.ok(addon, "Add-on retrieved.");
+ Assert.ok(addon.userDisabled, "Add-on is disabled after restart.");
+ Assert.equal(addon.isActive, false, "Add-on is not active after restart.");
+ addon.uninstall();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+});
+
+add_task(function* test_checkCompatibility() {
+ if (gIsNightly)
+ Services.prefs.setBoolPref("extensions.checkCompatibility.nightly", false);
+ else
+ Services.prefs.setBoolPref("extensions.checkCompatibility.1", false);
+
+ yield promiseRestartManager();
+
+ yield promiseInstallAllFiles([do_get_addon("test_experiment1")]);
+ let addon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ Assert.ok(addon, "Add-on is found.");
+ Assert.ok(addon.userDisabled, "Add-on is user disabled.");
+ Assert.ok(!addon.appDisabled, "Add-on is not app disabled.");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_ext_management.js b/toolkit/mozapps/webextensions/test/xpcshell/test_ext_management.js
new file mode 100644
index 000000000..a3b3f477c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_ext_management.js
@@ -0,0 +1,137 @@
+"use strict";
+
+add_task(function* setup() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "48", "48");
+ startupManager();
+});
+
+/* eslint-disable no-undef */
+// Shared background function for getSelf tests
+function backgroundGetSelf() {
+ browser.management.getSelf().then(extInfo => {
+ browser.test.sendMessage("management-getSelf", extInfo);
+ }, error => {
+ browser.test.notifyFail(`getSelf rejected with error: ${error}`);
+ });
+}
+/* eslint-enable no-undef */
+
+add_task(function* test_management_get_self_complete() {
+ const id = "get_self_test_complete@tests.mozilla.com";
+ const permissions = ["management", "cookies"];
+ const hostPermissions = ["*://example.org/", "https://foo.example.org/"];
+
+ let manifest = {
+ applications: {
+ gecko: {
+ id,
+ update_url: "https://updates.mozilla.com/",
+ },
+ },
+ name: "test extension name",
+ short_name: "test extension short name",
+ description: "test extension description",
+ version: "1.0",
+ homepage_url: "http://www.example.com/",
+ options_ui: {
+ "page": "get_self_options.html",
+ },
+ icons: {
+ "16": "icons/icon-16.png",
+ "48": "icons/icon-48.png",
+ },
+ permissions: [...permissions, ...hostPermissions],
+ };
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest,
+ background: backgroundGetSelf,
+ useAddonManager: "temporary",
+ });
+ yield extension.startup();
+ let extInfo = yield extension.awaitMessage("management-getSelf");
+
+ equal(extInfo.id, id, "getSelf returned the expected id");
+ equal(extInfo.installType, "development", "getSelf returned the expected installType");
+ for (let prop of ["name", "description", "version"]) {
+ equal(extInfo[prop], manifest[prop], `getSelf returned the expected ${prop}`);
+ }
+ equal(extInfo.shortName, manifest.short_name, "getSelf returned the expected shortName");
+ equal(extInfo.mayDisable, true, "getSelf returned the expected value for mayDisable");
+ equal(extInfo.enabled, true, "getSelf returned the expected value for enabled");
+ equal(extInfo.homepageUrl, manifest.homepage_url, "getSelf returned the expected homepageUrl");
+ equal(extInfo.updateUrl, manifest.applications.gecko.update_url, "getSelf returned the expected updateUrl");
+ ok(extInfo.optionsUrl.endsWith(manifest.options_ui.page), "getSelf returned the expected optionsUrl");
+ for (let [index, size] of Object.keys(manifest.icons).sort().entries()) {
+ equal(extInfo.icons[index].size, +size, "getSelf returned the expected icon size");
+ equal(extInfo.icons[index].url, manifest.icons[size], "getSelf returned the expected icon url");
+ }
+ deepEqual(extInfo.permissions.sort(), permissions.sort(), "getSelf returned the expected permissions");
+ deepEqual(extInfo.hostPermissions.sort(), hostPermissions.sort(), "getSelf returned the expected hostPermissions");
+ equal(extInfo.installType, "development", "getSelf returned the expected installType");
+ yield extension.unload();
+});
+
+add_task(function* test_management_get_self_minimal() {
+ const id = "get_self_test_minimal@tests.mozilla.com";
+
+ let manifest = {
+ applications: {
+ gecko: {
+ id,
+ },
+ },
+ name: "test extension name",
+ version: "1.0",
+ };
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest,
+ background: backgroundGetSelf,
+ useAddonManager: "temporary",
+ });
+ yield extension.startup();
+ let extInfo = yield extension.awaitMessage("management-getSelf");
+
+ equal(extInfo.id, id, "getSelf returned the expected id");
+ equal(extInfo.installType, "development", "getSelf returned the expected installType");
+ for (let prop of ["name", "version"]) {
+ equal(extInfo[prop], manifest[prop], `getSelf returned the expected ${prop}`);
+ }
+ for (let prop of ["shortName", "description", "optionsUrl"]) {
+ equal(extInfo[prop], "", `getSelf returned the expected ${prop}`);
+ }
+ for (let prop of ["homepageUrl", " updateUrl", "icons"]) {
+ equal(Reflect.getOwnPropertyDescriptor(extInfo, prop), undefined, `getSelf did not return a ${prop} property`);
+ }
+ for (let prop of ["permissions", "hostPermissions"]) {
+ deepEqual(extInfo[prop], [], `getSelf returned the expected ${prop}`);
+ }
+ yield extension.unload();
+});
+
+add_task(function* test_management_get_self_permanent() {
+ const id = "get_self_test_permanent@tests.mozilla.com";
+
+ let manifest = {
+ applications: {
+ gecko: {
+ id,
+ },
+ },
+ name: "test extension name",
+ version: "1.0",
+ };
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest,
+ background: backgroundGetSelf,
+ useAddonManager: "permanent",
+ });
+ yield extension.startup();
+ let extInfo = yield extension.awaitMessage("management-getSelf");
+
+ equal(extInfo.id, id, "getSelf returned the expected id");
+ equal(extInfo.installType, "normal", "getSelf returned the expected installType");
+ yield extension.unload();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_filepointer.js b/toolkit/mozapps/webextensions/test/xpcshell/test_filepointer.js
new file mode 100644
index 000000000..406489e40
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_filepointer.js
@@ -0,0 +1,403 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that various operations with file pointers work and do not affect the
+// source files
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon1_2 = {
+ id: "addon1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+profileDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+const sourceDir = gProfD.clone();
+sourceDir.append("source");
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver;
+
+function writePointer(aId, aName) {
+ let file = profileDir.clone();
+ file.append(aName ? aName : aId);
+
+ let target = sourceDir.clone();
+ target.append(do_get_expected_addon_name(aId));
+
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(file,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(target.path, target.path.length);
+ fos.close();
+}
+
+function writeRelativePointer(aId, aName) {
+ let file = profileDir.clone();
+ file.append(aName ? aName : aId);
+
+ let absTarget = sourceDir.clone();
+ absTarget.append(do_get_expected_addon_name(aId));
+
+ var relTarget = absTarget.getRelativeDescriptor(profileDir);
+
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(file,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(relTarget, relTarget.length);
+ fos.close();
+}
+
+function run_test() {
+ // pointer files only work with unpacked directories
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack") == false)
+ return;
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Tests that installing a new add-on by pointer works
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, sourceDir);
+ writePointer(addon1.id);
+
+ startupManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, sourceDir.path);
+
+ let rootUri = do_get_addon_root_uri(sourceDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ // Check that upgrade is disabled for addons installed by file-pointers.
+ do_check_eq(a1.permissions & AddonManager.PERM_CAN_UPGRADE, 0);
+ run_test_2();
+ });
+}
+
+// Tests that installing the addon from some other source doesn't clobber
+// the original sources
+function run_test_2() {
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_filepointer.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], callback_soon(check_test_2));
+
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, profileDir.path);
+
+ let rootUri = do_get_addon_root_uri(profileDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ a1.uninstall();
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Tests that uninstalling doesn't clobber the original sources
+function run_test_3() {
+ restartManager();
+
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ a1.uninstall();
+
+ restartManager();
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ do_execute_soon(run_test_4);
+ }));
+}
+
+// Tests that misnaming a pointer doesn't clobber the sources
+function run_test_4() {
+ writePointer("addon2@tests.mozilla.org", addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ let pointer = profileDir.clone();
+ pointer.append("addon2@tests.mozilla.org");
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Tests that changing the ID of an existing add-on doesn't clobber the sources
+function run_test_5() {
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ // Make sure the modification time changes enough to be detected.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ writeInstallRDFForExtension(addon2, sourceDir, addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1_2, a2_2]) {
+ do_check_eq(a1_2, null);
+ do_check_eq(a2_2, null);
+
+ let source = sourceDir.clone();
+ source.append(addon1.id);
+ do_check_true(source.exists());
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_6);
+ });
+ }));
+}
+
+// Removing the pointer file should uninstall the add-on
+function run_test_6() {
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ // Make sure the modification time changes enough to be detected in run_test_8.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ pointer.remove(false);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_2) {
+ do_check_eq(a1_2, null);
+
+ do_execute_soon(run_test_7);
+ });
+ }));
+}
+
+// Removing the pointer file and replacing it with a directory should work
+function run_test_7() {
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ pointer.remove(false);
+
+ writeInstallRDFForExtension(addon1_2, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_2) {
+ do_check_neq(a1_2, null);
+ do_check_eq(a1_2.version, "2.0");
+
+ a1_2.uninstall();
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Changes to the source files should be detected
+function run_test_8() {
+ restartManager();
+
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ writeInstallRDFForExtension(addon1_2, sourceDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1_2) {
+ do_check_neq(a1_2, null);
+ do_check_eq(a1_2.version, "2.0");
+
+ a1_2.uninstall();
+
+ do_execute_soon(run_test_9);
+ });
+ }));
+}
+
+// Removing the add-on the pointer file points at should uninstall the add-on
+function run_test_9() {
+ restartManager();
+
+ var dest = writeInstallRDFForExtension(addon1, sourceDir);
+ writePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ dest.remove(true);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1_2) {
+ do_check_eq(a1_2, null);
+
+ let pointer = profileDir.clone();
+ pointer.append(addon1.id);
+ do_check_false(pointer.exists());
+
+ do_execute_soon(run_test_10);
+ });
+ }));
+}
+
+// Tests that installing a new add-on by pointer with a relative path works
+function run_test_10() {
+ writeInstallRDFForExtension(addon1, sourceDir);
+ writeRelativePointer(addon1.id);
+
+ restartManager();
+
+ AddonManager.getAddonByID(addon1.id, function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+
+ let file = a1.getResourceURI().QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_eq(file.parent.path, sourceDir.path);
+
+ let rootUri = do_get_addon_root_uri(sourceDir, addon1.id);
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+ uri = a1.getResourceURI("install.rdf");
+ do_check_eq(uri.spec, rootUri + "install.rdf");
+
+ // Check that upgrade is disabled for addons installed by file-pointers.
+ do_check_eq(a1.permissions & AddonManager.PERM_CAN_UPGRADE, 0);
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_fuel.js b/toolkit/mozapps/webextensions/test/xpcshell/test_fuel.js
new file mode 100644
index 000000000..0c3035f76
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_fuel.js
@@ -0,0 +1,165 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+// This just verifies that FUEL integrates to the add-ons manager
+
+var testdata = {
+ dummyid: "fuel-dummy-extension@mozilla.org",
+ dummyname: "Dummy Extension",
+ inspectorid: "addon1@tests.mozilla.org",
+ inspectorname: "Test Addon",
+ missing: "fuel.fuel-test-missing",
+ dummy: "fuel.fuel-test"
+};
+
+var Application = null
+
+function run_test() {
+ var cm = AM_Cc["@mozilla.org/categorymanager;1"].
+ getService(AM_Ci.nsICategoryManager);
+
+ try {
+ var contract = cm.getCategoryEntry("JavaScript-global-privileged-property",
+ "Application");
+ Application = AM_Cc[contract].getService(AM_Ci.extIApplication);
+ }
+ catch (e) {
+ // This application does not include a FUEL variant.
+ return;
+ }
+
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ const profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test Addon",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ }, profileDir);
+
+ startupManager();
+
+ Application.getExtensions(function(extensions) {
+ // test to see if the extensions object is available
+ do_check_neq(extensions, null);
+
+ // test to see if a non-existant extension exists
+ do_check_true(!extensions.has(testdata.dummyid));
+
+ // test to see if an extension exists
+ do_check_true(extensions.has(testdata.inspectorid));
+
+ var inspector = extensions.get(testdata.inspectorid);
+ do_check_eq(inspector.id, testdata.inspectorid);
+ do_check_eq(inspector.name, testdata.inspectorname);
+ do_check_eq(inspector.version, "1.0");
+ do_check_true(inspector.firstRun, true);
+ do_check_true(inspector.enabled);
+
+ // test to see if extension find works
+ do_check_eq(extensions.all.length, 1);
+ // STORAGE TESTING
+ // Make sure the we are given the same extension (cached) so things like .storage work right
+ inspector.storage.set("test", "simple check");
+ do_check_true(inspector.storage.has("test"));
+
+ var inspector2 = extensions.get(testdata.inspectorid);
+ do_check_eq(inspector2.id, testdata.inspectorid);
+ do_check_true(inspector.storage.has("test"));
+ do_check_eq(inspector2.storage.get("test", "cache"), inspector.storage.get("test", "original"));
+
+ inspector.events.addListener("disable", onGenericEvent);
+ inspector.events.addListener("enable", onGenericEvent);
+ inspector.events.addListener("uninstall", onGenericEvent);
+ inspector.events.addListener("cancel", onGenericEvent);
+
+ AddonManager.getAddonByID(testdata.inspectorid, function(a) {
+ a.userDisabled = true;
+
+ do_check_eq(gLastEvent, "disable");
+
+ // enabling after a disable will only fire a 'cancel' event
+ // see - http://mxr.mozilla.org/seamonkey/source/toolkit/mozapps/extensions/src/nsExtensionManager.js.in#5216
+ a.userDisabled = false;
+ do_check_eq(gLastEvent, "cancel");
+
+ a.uninstall();
+ do_check_eq(gLastEvent, "uninstall");
+
+ a.cancelUninstall();
+ do_check_eq(gLastEvent, "cancel");
+
+ // PREF TESTING
+ // Reset the install event preference, so that we can test it again later
+ // inspector.prefs.get("install-event-fired").reset();
+
+ // test the value of the preference root
+ do_check_eq(extensions.all[0].prefs.root, "extensions.addon1@tests.mozilla.org.");
+
+ // test getting nonexistent values
+ var itemValue = inspector.prefs.getValue(testdata.missing, "default");
+ do_check_eq(itemValue, "default");
+
+ do_check_eq(inspector.prefs.get(testdata.missing), null);
+
+ // test setting and getting a value
+ inspector.prefs.setValue(testdata.dummy, "dummy");
+ itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "dummy");
+
+ // test for overwriting an existing value
+ inspector.prefs.setValue(testdata.dummy, "smarty");
+ itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "smarty");
+
+ // test setting and getting a value
+ inspector.prefs.get(testdata.dummy).value = "dummy2";
+ itemValue = inspector.prefs.get(testdata.dummy).value;
+ do_check_eq(itemValue, "dummy2");
+
+ // test resetting a pref [since there is no default value, the pref should disappear]
+ inspector.prefs.get(testdata.dummy).reset();
+ itemValue = inspector.prefs.getValue(testdata.dummy, "default");
+ do_check_eq(itemValue, "default");
+
+ // test to see if a non-existant property exists
+ do_check_true(!inspector.prefs.has(testdata.dummy));
+
+ inspector.prefs.events.addListener("change", onPrefChange);
+ inspector.prefs.setValue("fuel.fuel-test", "change event");
+ });
+ });
+}
+
+var gLastEvent;
+function onGenericEvent(event) {
+ gLastEvent = event.type;
+}
+
+function onPrefChange(evt) {
+ Application.getExtensions(function(extensions) {
+ var inspector3 = extensions.get(testdata.inspectorid);
+
+ do_check_eq(evt.data, testdata.dummy);
+ inspector3.prefs.events.removeListener("change", onPrefChange);
+
+ inspector3.prefs.get("fuel.fuel-test").events.addListener("change", onPrefChange2);
+ inspector3.prefs.setValue("fuel.fuel-test", "change event2");
+ });
+}
+
+function onPrefChange2(evt) {
+ do_check_eq(evt.data, testdata.dummy);
+
+ do_execute_soon(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_general.js b/toolkit/mozapps/webextensions/test/xpcshell/test_general.js
new file mode 100644
index 000000000..e69b13314
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_general.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This just verifies that the EM can actually startup and shutdown a few times
+// without any errors
+
+// We have to look up how many add-ons are present since there will be plugins
+// etc. detected
+var gCount;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ var count = 0;
+ startupManager();
+ AddonManager.getAddonsByTypes(null, function(list) {
+ gCount = list.length;
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function run_test_1() {
+ restartManager();
+
+ AddonManager.getAddonsByTypes(null, function(addons) {
+ do_check_eq(gCount, addons.length);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(pendingAddons) {
+ do_check_eq(0, pendingAddons.length);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+function run_test_2() {
+ shutdownManager();
+
+ startupManager(false);
+
+ AddonManager.getAddonsByTypes(null, function(addons) {
+ do_check_eq(gCount, addons.length);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonsByTypes(null, callback_soon(function(addons) {
+ do_check_eq(gCount, addons.length);
+ do_test_finished();
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_getresource.js b/toolkit/mozapps/webextensions/test/xpcshell/test_getresource.js
new file mode 100644
index 000000000..c83638d54
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_getresource.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// install.rdf size, icon.png size, subfile.txt size
+const ADDON_SIZE = 672 + 15 + 26;
+
+// This verifies the functionality of getResourceURI
+// There are two cases - with a filename it returns an nsIFileURL to the filename
+// and with no parameters, it returns an nsIFileURL to the root of the addon
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ startupManager();
+
+ AddonManager.getInstallForFile(do_get_addon("test_getresource"), function(aInstall) {
+ do_check_true(aInstall.addon.hasResource("install.rdf"));
+ do_check_eq(aInstall.addon.getResourceURI().spec, aInstall.sourceURI.spec);
+
+ do_check_true(aInstall.addon.hasResource("icon.png"));
+ do_check_eq(aInstall.addon.getResourceURI("icon.png").spec,
+ "jar:" + aInstall.sourceURI.spec + "!/icon.png");
+
+ do_check_false(aInstall.addon.hasResource("missing.txt"));
+
+ do_check_true(aInstall.addon.hasResource("subdir/subfile.txt"));
+ do_check_eq(aInstall.addon.getResourceURI("subdir/subfile.txt").spec,
+ "jar:" + aInstall.sourceURI.spec + "!/subdir/subfile.txt");
+
+ do_check_false(aInstall.addon.hasResource("subdir/missing.txt"));
+
+ do_check_eq(aInstall.addon.size, ADDON_SIZE);
+
+ completeAllInstalls([aInstall], function() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+
+ let addonDir = gProfD.clone();
+ addonDir.append("extensions");
+ let rootUri = do_get_addon_root_uri(addonDir, "addon1@tests.mozilla.org");
+
+ let uri = a1.getResourceURI("/");
+ do_check_eq(uri.spec, rootUri);
+
+ let file = rootUri + "install.rdf";
+ do_check_true(a1.hasResource("install.rdf"));
+ uri = a1.getResourceURI("install.rdf")
+ do_check_eq(uri.spec, file);
+
+ file = rootUri + "icon.png";
+ do_check_true(a1.hasResource("icon.png"));
+ uri = a1.getResourceURI("icon.png")
+ do_check_eq(uri.spec, file);
+
+ do_check_false(a1.hasResource("missing.txt"));
+
+ file = rootUri + "subdir/subfile.txt";
+ do_check_true(a1.hasResource("subdir/subfile.txt"));
+ uri = a1.getResourceURI("subdir/subfile.txt")
+ do_check_eq(uri.spec, file);
+
+ do_check_false(a1.hasResource("subdir/missing.txt"));
+
+ do_check_eq(a1.size, ADDON_SIZE);
+
+ a1.uninstall();
+
+ try {
+ // hasResource should never throw an exception.
+ do_check_false(a1.hasResource("icon.png"));
+ } catch (e) {
+ do_check_true(false);
+ }
+
+ AddonManager.getInstallForFile(do_get_addon("test_getresource"),
+ callback_soon(function(aInstall_2) {
+ do_check_false(a1.hasResource("icon.png"));
+ do_check_true(aInstall_2.addon.hasResource("icon.png"));
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_eq(newa1, null);
+
+ do_execute_soon(do_test_finished);
+ });
+ }));
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Device.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Device.js
new file mode 100644
index 000000000..9b0eb54a7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Device.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which differs only on device ID, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x9876");
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("aabb");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_CANVAS2D_ACCELERATION);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_DriverNew.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_DriverNew.js
new file mode 100644
index 000000000..f8b783ff2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_DriverNew.js
@@ -0,0 +1,92 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a new-enough driver bypasses the blacklist, even if the rest of
+// the attributes match the blacklist entry.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("wxyz");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js
new file mode 100644
index 000000000..1b3410e87
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which is newer than the equal
+// blacklist entry is allowed.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1112");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on OS X.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "15.0", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_11_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_11_ANGLE);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_HARDWARE_VIDEO_DECODING);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_DECODE);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_ENCODE);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_MSAA);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_ANGLE);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_CANVAS2D_ACCELERATION);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js
new file mode 100644
index 000000000..248c9e7f6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which is older than the equal
+// blacklist entry is correctly allowed.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1110");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("4");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js
new file mode 100644
index 000000000..8b8928bb8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which exactly matches the equal
+// blacklist entry is successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcdc");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.1111");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("dcdc");
+ gfxInfo.spoofDeviceID("uiop");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js
new file mode 100644
index 000000000..bd5858023
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which is lower than the greater-than-or-equal
+// blacklist entry is allowed.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabab");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abab");
+ gfxInfo.spoofDeviceID("ghjk");
+ gfxInfo.spoofDriverVersion("6");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js
new file mode 100644
index 000000000..b5c5ed2a6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which exactly matches the greater-than-or-equal
+// blacklist entry is successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabab");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't support driver versions on Linux.
+ do_test_finished();
+ return;
+ case "Darwin":
+ // We don't support driver versions on Darwin.
+ do_test_finished();
+ return;
+ case "Android":
+ gfxInfo.spoofVendorID("abab");
+ gfxInfo.spoofDeviceID("ghjk");
+ gfxInfo.spoofDriverVersion("7");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js
new file mode 100644
index 000000000..ff37e6676
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js
@@ -0,0 +1,89 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which exactly matches the blacklist entry is
+// successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x6666");
+
+ // Spoof the OS version so it matches the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ break;
+ case "Darwin":
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var driverVersion = gfxInfo.adapterDriverVersion;
+ if (driverVersion) {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DEVICE);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ }
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OK.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OK.js
new file mode 100644
index 000000000..72b2a2bdc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OK.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which exactly matches the blacklist entry is
+// successfully blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OS.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OS.js
new file mode 100644
index 000000000..fa0deb19a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OS.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which differs only on OS version, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows Vista
+ gfxInfo.spoofOSVersion(0x60000);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1080);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js
new file mode 100644
index 000000000..f01329b45
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether new OS versions are matched properly.
+// Uses test_gfxBlacklist_OS.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist_OSVersion.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 8
+ gfxInfo.spoofOSVersion(0x60002);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ // Mountain Lion
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OSVersion.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js
new file mode 100644
index 000000000..4b1069dc6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js
@@ -0,0 +1,95 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether blocklists specifying new OSeswcorrectly don't block if driver
+// versions are appropriately up-to-date.
+// Uses test_gfxBlacklist_OS.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist_OSVersion.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2202");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 8
+ gfxInfo.spoofOSVersion(0x60002);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ gfxInfo.spoofOSVersion(0x1080);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OSVersion.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js
new file mode 100644
index 000000000..0c5a0dcb7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js
@@ -0,0 +1,96 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether old OS versions are not matched when the blacklist contains
+// only new OS versions.
+// Uses test_gfxBlacklist_OS.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist_OSVersion.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+
+ // Spoof the version of the OS appropriately to test the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ // We don't have any OS versions on Linux, just "Linux".
+ do_test_finished();
+ return;
+ case "Darwin":
+ // Lion
+ gfxInfo.spoofOSVersion(0x1080);
+ break;
+ case "Android":
+ // On Android, the driver version is used as the OS version (because
+ // there's so many of them).
+ do_test_finished();
+ return;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ if (get_platform() == "WINNT") {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ } else if (get_platform() == "Darwin") {
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ }
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_OSVersion.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Vendor.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Vendor.js
new file mode 100644
index 000000000..868c48149
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Vendor.js
@@ -0,0 +1,93 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This should eventually be moved to head_addons.js
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which differs only on vendor, but otherwise
+// exactly matches the blacklist entry, is not blocked.
+// Uses test_gfxBlacklist.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xdcba");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("dcba");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Version.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Version.js
new file mode 100644
index 000000000..48174b772
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Version.js
@@ -0,0 +1,145 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+// Test whether a machine which exactly matches the blacklist entry is
+// successfully blocked.
+// Uses test_gfxBlacklist_AllOS.xml
+
+Cu.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist_AllOS.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Cc["@mozilla.org/xre/app-info;1"]
+ .getService(Ci.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "15.0", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function checkBlacklist()
+ {
+ var failureId = {};
+ var status;
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ do_check_eq(failureId.value, "FEATURE_FAILURE_DL_BLACKLIST_g1");
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ do_check_eq(failureId.value, "FEATURE_FAILURE_DL_BLACKLIST_g2");
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_10_LAYERS, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ do_check_eq(failureId.value, "");
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_10_1_LAYERS, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+ do_check_eq(failureId.value, "");
+
+ // These four pass on Linux independent of the blocklist XML file as the
+ // try machines don't have support.
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_OPENGL_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_OPENGL, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ do_check_eq(failureId.value, "FEATURE_FAILURE_DL_BLACKLIST_g11");
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_ANGLE, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+ do_check_eq(failureId.value, "FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBGL_MSAA, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_STAGEFRIGHT, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_ENCODE, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_WEBRTC_HW_ACCELERATION_DECODE, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_11_LAYERS, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_HARDWARE_VIDEO_DECODING, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_11_ANGLE, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DX_INTEROP2, failureId);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(function(aSubject, aTopic, aData) {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(checkBlacklist);
+ }, "blocklist-data-gfxItems", false);
+
+ load_blocklist("test_gfxBlacklist_AllOS.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_prefs.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_prefs.js
new file mode 100644
index 000000000..fbb992879
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_prefs.js
@@ -0,0 +1,135 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var { classes: Cc, interfaces: Ci } = Components;
+
+// Test whether the blacklist succesfully adds and removes the prefs that store
+// its decisions when the remote blacklist is changed.
+// Uses test_gfxBlacklist.xml and test_gfxBlacklist2.xml
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gTestserver = new HttpServer();
+gTestserver.start(-1);
+gPort = gTestserver.identity.primaryPort;
+mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+mapFile("/data/test_gfxBlacklist2.xml", gTestserver);
+
+function get_platform() {
+ var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
+ .getService(Components.interfaces.nsIXULRuntime);
+ return xulRuntime.OS;
+}
+
+function load_blocklist(file) {
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+ gPort + "/data/" + file);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+}
+
+// Performs the initial setup
+function run_test() {
+ try {
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ } catch (e) {
+ do_test_finished();
+ return;
+ }
+
+ // We can't do anything if we can't spoof the stuff we need.
+ if (!(gfxInfo instanceof Ci.nsIGfxInfoDebug)) {
+ do_test_finished();
+ return;
+ }
+
+ gfxInfo.QueryInterface(Ci.nsIGfxInfoDebug);
+
+ // Set the vendor/device ID, etc, to match the test file.
+ switch (get_platform()) {
+ case "WINNT":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofDriverVersion("8.52.322.2201");
+ // Windows 7
+ gfxInfo.spoofOSVersion(0x60001);
+ break;
+ case "Linux":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ break;
+ case "Darwin":
+ gfxInfo.spoofVendorID("0xabcd");
+ gfxInfo.spoofDeviceID("0x1234");
+ gfxInfo.spoofOSVersion(0x1090);
+ break;
+ case "Android":
+ gfxInfo.spoofVendorID("abcd");
+ gfxInfo.spoofDeviceID("asdf");
+ gfxInfo.spoofDriverVersion("5");
+ break;
+ }
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "3", "8");
+ startupManager();
+
+ do_test_pending();
+
+ function blacklistAdded(aSubject, aTopic, aData)
+ {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(ensureBlacklistSet);
+ }
+ function ensureBlacklistSet()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ do_check_eq(prefs.getIntPref("gfx.blacklist.direct2d"),
+ Ci.nsIGfxInfo.FEATURE_BLOCKED_DRIVER_VERSION);
+
+ Services.obs.removeObserver(blacklistAdded, "blocklist-data-gfxItems");
+ Services.obs.addObserver(blacklistRemoved, "blocklist-data-gfxItems", false);
+ load_blocklist("test_gfxBlacklist2.xml");
+ }
+
+ function blacklistRemoved(aSubject, aTopic, aData)
+ {
+ // If we wait until after we go through the event loop, gfxInfo is sure to
+ // have processed the gfxItems event.
+ do_execute_soon(ensureBlacklistUnset);
+ }
+ function ensureBlacklistUnset()
+ {
+ var status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT2D);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ // Make sure unrelated features aren't affected
+ status = gfxInfo.getFeatureStatus(Ci.nsIGfxInfo.FEATURE_DIRECT3D_9_LAYERS);
+ do_check_eq(status, Ci.nsIGfxInfo.FEATURE_STATUS_OK);
+
+ var prefs = Cc["@mozilla.org/preferences-service;1"].
+ getService(Ci.nsIPrefBranch);
+ var exists = false;
+ try {
+ prefs.getIntPref("gfx.blacklist.direct2d");
+ exists = true;
+ } catch (e) {}
+
+ do_check_false(exists);
+
+ gTestserver.stop(do_test_finished);
+ }
+
+ Services.obs.addObserver(blacklistAdded, "blocklist-data-gfxItems", false);
+ load_blocklist("test_gfxBlacklist.xml");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_gmpProvider.js b/toolkit/mozapps/webextensions/test/xpcshell/test_gmpProvider.js
new file mode 100644
index 000000000..545d7d666
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_gmpProvider.js
@@ -0,0 +1,416 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+var {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+var GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
+Cu.import("resource://gre/modules/UpdateUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "pluginsBundle",
+ () => Services.strings.createBundle("chrome://global/locale/plugins.properties"));
+
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+ "resource://gre/modules/FileUtils.jsm");
+
+var gMockAddons = new Map();
+var gMockEmeAddons = new Map();
+
+for (let plugin of GMPScope.GMP_PLUGINS) {
+ let mockAddon = Object.freeze({
+ id: plugin.id,
+ isValid: true,
+ isInstalled: false,
+ nameId: plugin.name,
+ descriptionId: plugin.description,
+ missingKey: plugin.missingKey,
+ missingFilesKey: plugin.missingFilesKey,
+ });
+ gMockAddons.set(mockAddon.id, mockAddon);
+ if (mockAddon.id == "gmp-widevinecdm" ||
+ mockAddon.id.indexOf("gmp-eme-") == 0) {
+ gMockEmeAddons.set(mockAddon.id, mockAddon);
+ }
+}
+
+var gInstalledAddonId = "";
+var gPrefs = Services.prefs;
+var gGetKey = GMPScope.GMPPrefs.getPrefKey;
+
+function MockGMPInstallManager() {
+}
+
+MockGMPInstallManager.prototype = {
+ checkForAddons: () => Promise.resolve({
+ usedFallback: true,
+ gmpAddons: [...gMockAddons.values()]
+ }),
+
+ installAddon: addon => {
+ gInstalledAddonId = addon.id;
+ return Promise.resolve();
+ },
+};
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_LOGGING_DUMP, true);
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_LOGGING_LEVEL, 0);
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+ for (let addon of gMockAddons.values()) {
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VISIBLE, addon.id),
+ true);
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_FORCE_SUPPORTED, addon.id),
+ true);
+ }
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+
+ run_next_test();
+}
+
+add_task(function* test_notInstalled() {
+ for (let addon of gMockAddons.values()) {
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id), "");
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
+ }
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ Assert.ok(!addon.isInstalled);
+ Assert.equal(addon.type, "plugin");
+ Assert.equal(addon.version, "");
+
+ let mockAddon = gMockAddons.get(addon.id);
+
+ Assert.notEqual(mockAddon, null);
+ let name = pluginsBundle.GetStringFromName(mockAddon.nameId);
+ Assert.equal(addon.name, name);
+ let description = pluginsBundle.GetStringFromName(mockAddon.descriptionId);
+ Assert.equal(addon.description, description);
+
+ Assert.ok(!addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.userDisabled);
+
+ Assert.equal(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+ Assert.equal(addon.size, 0);
+ Assert.equal(addon.scope, AddonManager.SCOPE_APPLICATION);
+ Assert.equal(addon.pendingOperations, AddonManager.PENDING_NONE);
+ Assert.equal(addon.operationsRequiringRestart, AddonManager.PENDING_NONE);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_ENABLE);
+
+ Assert.equal(addon.updateDate, null);
+
+ Assert.ok(addon.isCompatible);
+ Assert.ok(addon.isPlatformCompatible);
+ Assert.ok(addon.providesUpdatesSecurely);
+ Assert.ok(!addon.foreignInstall);
+
+ let mimetypes = addon.pluginMimeTypes;
+ Assert.ok(mimetypes);
+ Assert.equal(mimetypes.length, 0);
+ let libraries = addon.pluginLibraries;
+ Assert.ok(libraries);
+ Assert.equal(libraries.length, 0);
+ Assert.equal(addon.pluginFullpath, "");
+ }
+});
+
+add_task(function* test_installed() {
+ const TEST_DATE = new Date(2013, 0, 1, 12);
+ const TEST_VERSION = "1.2.3.4";
+ const TEST_TIME_SEC = Math.round(TEST_DATE.getTime() / 1000);
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ let mockAddon = gMockAddons.get(addon.id);
+ Assert.notEqual(mockAddon, null);
+
+ let file = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file.append(addon.id);
+ file.append(TEST_VERSION);
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, mockAddon.id), false);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_LAST_UPDATE, mockAddon.id),
+ "" + TEST_TIME_SEC);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, mockAddon.id),
+ TEST_VERSION);
+
+ Assert.ok(addon.isInstalled);
+ Assert.equal(addon.type, "plugin");
+ Assert.ok(!addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(addon.userDisabled);
+
+ let name = pluginsBundle.GetStringFromName(mockAddon.nameId);
+ Assert.equal(addon.name, name);
+ Assert.equal(addon.version, TEST_VERSION);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_ENABLE);
+
+ Assert.equal(addon.updateDate.getTime(), TEST_TIME_SEC * 1000);
+
+ let mimetypes = addon.pluginMimeTypes;
+ Assert.ok(mimetypes);
+ Assert.equal(mimetypes.length, 0);
+ let libraries = addon.pluginLibraries;
+ Assert.ok(libraries);
+ Assert.equal(libraries.length, 1);
+ Assert.equal(libraries[0], TEST_VERSION);
+ let fullpath = addon.pluginFullpath;
+ Assert.equal(fullpath.length, 1);
+ Assert.equal(fullpath[0], file.path);
+ }
+});
+
+add_task(function* test_enable() {
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+
+ Assert.ok(addon.isActive);
+ Assert.ok(!addon.appDisabled);
+ Assert.ok(!addon.userDisabled);
+
+ Assert.equal(addon.permissions, AddonManager.PERM_CAN_UPGRADE |
+ AddonManager.PERM_CAN_DISABLE);
+ }
+});
+
+add_task(function* test_globalEmeDisabled() {
+ let addons = yield promiseAddonsByIDs([...gMockEmeAddons.keys()]);
+ Assert.equal(addons.length, gMockEmeAddons.size);
+
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, false);
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+ for (let addon of addons) {
+ Assert.ok(!addon.isActive);
+ Assert.ok(addon.appDisabled);
+ Assert.ok(!addon.userDisabled);
+
+ Assert.equal(addon.permissions, 0);
+ }
+ gPrefs.setBoolPref(GMPScope.GMPPrefs.KEY_EME_ENABLED, true);
+ GMPScope.GMPProvider.shutdown();
+ GMPScope.GMPProvider.startup();
+});
+
+add_task(function* test_autoUpdatePrefPersistance() {
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ let autoupdateKey = gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id);
+ gPrefs.clearUserPref(autoupdateKey);
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ Assert.ok(!gPrefs.getBoolPref(autoupdateKey));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
+ Assert.ok(gPrefs.getBoolPref(autoupdateKey));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+ Assert.ok(!gPrefs.prefHasUserValue(autoupdateKey));
+ }
+});
+
+function createMockPluginFilesIfNeeded(aFile, aPluginId) {
+ function createFile(aFileName) {
+ let f = aFile.clone();
+ f.append(aFileName);
+ if (!f.exists()) {
+ f.create(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+ }
+ }
+
+ let id = aPluginId.substring(4);
+ let libName = AppConstants.DLL_PREFIX + id + AppConstants.DLL_SUFFIX;
+
+ createFile(libName);
+ if (aPluginId == "gmp-widevinecdm") {
+ createFile("manifest.json");
+ } else {
+ createFile(id + ".info");
+ }
+ if (aPluginId == "gmp-eme-adobe")
+ createFile(id + ".voucher");
+}
+
+// Array.includes() is only in Nightly channel, so polyfill so we don't fail
+// on other branches.
+if (![].includes) {
+ Array.prototype.includes = function(element) {
+ return Object(this).indexOf(element) != -1;
+ }
+}
+
+add_task(function* test_pluginRegistration() {
+ const TEST_VERSION = "1.2.3.4";
+
+ let profD = do_get_profile();
+ for (let addon of gMockAddons.values()) {
+ let file = profD.clone();
+ file.append(addon.id);
+ file.append(TEST_VERSION);
+
+ let addedPaths = [];
+ let removedPaths = [];
+ let clearPaths = () => { addedPaths = []; removedPaths = []; }
+
+ let MockGMPService = {
+ addPluginDirectory: path => {
+ if (!addedPaths.includes(path)) {
+ addedPaths.push(path);
+ }
+ },
+ removePluginDirectory: path => {
+ if (!removedPaths.includes(path)) {
+ removedPaths.push(path);
+ }
+ },
+ removeAndDeletePluginDirectory: path => {
+ if (!removedPaths.includes(path)) {
+ removedPaths.push(path);
+ }
+ },
+ };
+ GMPScope.gmpService = MockGMPService;
+
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+
+ // Test that plugin registration fails if the plugin dynamic library and
+ // info files are not present.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Create dummy GMP library/info files, and test that plugin registration
+ // succeeds during startup, now that we've added GMP info/lib files.
+ createMockPluginFilesIfNeeded(file, addon.id);
+
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.notEqual(addedPaths.indexOf(file.path), -1);
+ Assert.deepEqual(removedPaths, []);
+
+ // Setting the ABI to something invalid should cause plugin to be removed at startup.
+ clearPaths();
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), "invalid-ABI");
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Setting the ABI to expected ABI should cause registration at startup.
+ clearPaths();
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ABI, addon.id), UpdateUtils.ABI);
+ yield promiseRestartManager();
+ Assert.notEqual(addedPaths.indexOf(file.path), -1);
+ Assert.deepEqual(removedPaths, []);
+
+ // Check that clearing the version doesn't trigger registration.
+ clearPaths();
+ gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id));
+ Assert.deepEqual(addedPaths, []);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Restarting with no version set should not trigger registration.
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.equal(removedPaths.indexOf(file.path), -1);
+
+ // Changing the pref mid-session should cause unregistration and registration.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ const TEST_VERSION_2 = "5.6.7.8";
+ let file2 = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ file2.append(addon.id);
+ file2.append(TEST_VERSION_2);
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION_2);
+ Assert.deepEqual(addedPaths, [file2.path]);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Disabling the plugin should cause unregistration.
+ gPrefs.setCharPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_VERSION, addon.id),
+ TEST_VERSION);
+ clearPaths();
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), false);
+ Assert.deepEqual(addedPaths, []);
+ Assert.deepEqual(removedPaths, [file.path]);
+
+ // Restarting with the plugin disabled should not cause registration.
+ clearPaths();
+ yield promiseRestartManager();
+ Assert.equal(addedPaths.indexOf(file.path), -1);
+ Assert.equal(removedPaths.indexOf(file.path), -1);
+
+ // Re-enabling the plugin should cause registration.
+ clearPaths();
+ gPrefs.setBoolPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_ENABLED, addon.id), true);
+ Assert.deepEqual(addedPaths, [file.path]);
+ Assert.deepEqual(removedPaths, []);
+ GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+ }
+});
+
+add_task(function* test_periodicUpdate() {
+ Object.defineProperty(GMPScope, "GMPInstallManager", {
+ value: MockGMPInstallManager,
+ writable: true,
+ enumerable: true,
+ configurable: true
+ });
+
+ let addons = yield promiseAddonsByIDs([...gMockAddons.keys()]);
+ Assert.equal(addons.length, gMockAddons.size);
+
+ for (let addon of addons) {
+ gPrefs.clearUserPref(gGetKey(GMPScope.GMPPrefs.KEY_PLUGIN_AUTOUPDATE, addon.id));
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK, 0);
+ let result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, false);
+
+ addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK, Date.now() / 1000 - 60);
+ result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, false);
+
+ gPrefs.setIntPref(GMPScope.GMPPrefs.KEY_UPDATE_LAST_CHECK,
+ Date.now() / 1000 - 2 * GMPScope.SEC_IN_A_DAY);
+ gInstalledAddonId = "";
+ result =
+ yield addon.findUpdates({}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ Assert.strictEqual(result, true);
+ Assert.equal(gInstalledAddonId, addon.id);
+ }
+
+ GMPScope = Cu.import("resource://gre/modules/addons/GMPProvider.jsm");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_hasbinarycomponents.js b/toolkit/mozapps/webextensions/test/xpcshell/test_hasbinarycomponents.js
new file mode 100644
index 000000000..925e63626
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_hasbinarycomponents.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests detection of binary components via parsing of chrome manifests.
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1"),
+ do_get_addon("test_chromemanifest_2"),
+ do_get_addon("test_chromemanifest_3"),
+ do_get_addon("test_chromemanifest_4"),
+ do_get_addon("test_chromemanifest_5")],
+ function() {
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+ // addon1 has no binary components
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.hasBinaryComponents);
+ do_check_true(a1.isCompatible);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // addon2 has a binary component, is compatible
+ do_check_neq(a2, null);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ // addon3 has a binary component, is incompatible
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ // addon4 has a binary component listed in a sub-manifest, is incompatible
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a2.hasBinaryComponents);
+ do_check_false(a4.isCompatible);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ // addon5 has a binary component, but is set to not unpack
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ if (TEST_UNPACKED)
+ do_check_true(a5.hasBinaryComponents);
+ else
+ do_check_false(a5.hasBinaryComponents);
+ do_check_true(a5.isCompatible);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_execute_soon(do_test_finished);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix.js b/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix.js
new file mode 100644
index 000000000..c9303897f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix.js
@@ -0,0 +1,309 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that hotfix installation works
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Ignore any certificate requirements the app has set
+Services.prefs.setBoolPref("extensions.hotfix.cert.checkAttributes", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+mapFile("/data/test_hotfix_1.rdf", testserver);
+mapFile("/data/test_hotfix_2.rdf", testserver);
+mapFile("/data/test_hotfix_3.rdf", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Test that background updates find and install any available hotfix
+function run_test_1() {
+ Services.prefs.setCharPref("extensions.hotfix.id", "hotfix@tests.mozilla.org");
+ Services.prefs.setCharPref("extensions.update.background.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_1.rdf");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_1));
+
+ // We don't need to wait on the promise, just waiting for the install to finish is enough.
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function check_test_1() {
+ restartManager();
+
+ AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+
+ aAddon.uninstall();
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Don't install an already used hotfix
+function run_test_2() {
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function() {
+ do_throw("Should not have seen a new install created");
+ }
+ });
+
+ // Run the background update
+ AddonManagerInternal.backgroundUpdateCheck().then(run_test_3);
+}
+
+// Install a newer hotfix
+function run_test_3() {
+ restartManager();
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_2.rdf");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_3));
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function check_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "2.0");
+
+ aAddon.uninstall();
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Don't install an incompatible hotfix
+function run_test_4() {
+ restartManager();
+
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_3.rdf");
+
+ AddonManager.addInstallListener({
+ onNewInstall: function() {
+ do_throw("Should not have seen a new install created");
+ }
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck().then(run_test_5);
+}
+
+// Don't install an older hotfix
+function run_test_5() {
+ restartManager();
+
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_1.rdf");
+
+ AddonManager.addInstallListener({
+ onNewInstall: function() {
+ do_throw("Should not have seen a new install created");
+ }
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck().then(run_test_6);
+}
+
+// Don't re-download an already pending install
+function run_test_6() {
+ restartManager();
+
+ Services.prefs.setCharPref("extensions.hotfix.lastVersion", "0");
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_1.rdf");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_6));
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function check_test_6() {
+ AddonManager.addInstallListener({
+ onNewInstall: function() {
+ do_throw("Should not have seen a new install created");
+ }
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck()
+ .then(promiseRestartManager)
+ .then(() => promiseAddonByID("hotfix@tests.mozilla.org"))
+ .then(aAddon => {
+ aAddon.uninstall();
+ run_test_7();
+ });
+}
+
+// Start downloading again if something cancels the install
+function run_test_7() {
+ restartManager();
+
+ Services.prefs.setCharPref("extensions.hotfix.lastVersion", "0");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_7);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function check_test_7(aInstall) {
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ aInstall.cancel();
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(finish_test_7));
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function finish_test_7() {
+ restartManager();
+
+ AddonManager.getAddonByID("hotfix@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "1.0");
+
+ aAddon.uninstall();
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Cancel a pending install when a newer version is already available
+function run_test_8() {
+ restartManager();
+
+ Services.prefs.setCharPref("extensions.hotfix.lastVersion", "0");
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_1.rdf");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_8);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function check_test_8() {
+ Services.prefs.setCharPref("extensions.hotfix.url", "http://localhost:" +
+ gPort + "/data/test_hotfix_2.rdf");
+
+ prepare_test({
+ "hotfix@tests.mozilla.org": [
+ "onOperationCancelled",
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallCancelled",
+ "onInstallEnded",
+ ], finish_test_8);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+}
+
+function finish_test_8() {
+ AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0].version, "2.0");
+
+ restartManager();
+
+ AddonManager.getAddonByID("hotfix@tests.mozilla.org", callback_soon(function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.version, "2.0");
+
+ aAddon.uninstall();
+ restartManager();
+
+ end_test();
+ }));
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix_cert.js b/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix_cert.js
new file mode 100644
index 000000000..42ee59740
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_hotfix_cert.js
@@ -0,0 +1,167 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that hotfix installation works
+const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
+const PREF_EM_HOTFIX_LASTVERSION = "extensions.hotfix.lastVersion";
+const PREF_EM_HOTFIX_URL = "extensions.hotfix.url";
+const PREF_EM_CERT_CHECKATTRIBUTES = "extensions.hotfix.cert.checkAttributes";
+const PREF_EM_HOTFIX_CERTS = "extensions.hotfix.certs.";
+
+// Derived from "openssl x509 -in firefox-hotfix.crt -fingerprint -sha1"
+const GOOD_FINGERPRINT = "39:E7:2B:7A:5B:CF:37:78:F9:5D:4A:E0:53:2D:2F:3D:68:53:C5:60";
+const BAD_FINGERPRINT = "40:E7:2B:7A:5B:CF:37:78:F9:5D:4A:E0:53:2D:2F:3D:68:53:C5:60";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+testserver.registerDirectory("/data/", do_get_file("data"));
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// Ignore any certificate requirements the app has set
+Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
+Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, "http://localhost:" + gPort + "/hotfix.rdf");
+// Clear out all hotfix cert prefs to make sure only the test prefs apply.
+var defaults = Services.prefs.getDefaultBranch("");
+defaults.deleteBranch(PREF_EM_HOTFIX_CERTS);
+
+/*
+ * Register an addon install listener and return a promise that:
+ * resolves with the AddonInstall object if the install succeeds
+ * rejects with the AddonInstall if the install fails
+ */
+function promiseInstallListener() {
+ return new Promise((resolve, reject) => {
+ let listener = {
+ onDownloadFailed: ai => {
+ AddonManager.removeInstallListener(listener);
+ reject(ai);
+ },
+ onInstallEnded: ai => {
+ AddonManager.removeInstallListener(listener);
+ resolve(ai);
+ },
+ onDownloadCancelled: ai => {
+ AddonManager.removeInstallListener(listener);
+ reject(ai);
+ }
+ };
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+function promiseSuccessfulInstall() {
+ return promiseInstallListener().then(
+ aInstall => {
+ do_check_true(true);
+ do_check_eq(aInstall.addon.id, Services.prefs.getCharPref(PREF_EM_HOTFIX_ID));
+ aInstall.addon.uninstall();
+ Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
+ },
+ aInstall => {
+ do_throw("Install should not have failed");
+ });
+}
+
+function promiseFailedInstall() {
+ return promiseInstallListener().then(
+ aInstall => {
+ do_throw("Install should not have succeeded");
+ aInstall.addon.uninstall();
+ Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
+ },
+ aInstall => {
+ do_check_true(true);
+ });
+}
+
+var tryInstallHotfix = Task.async(function*(id, file, installListener) {
+ Services.prefs.setCharPref(PREF_EM_HOTFIX_ID, id);
+
+ testserver.registerPathHandler("/hotfix.rdf", function(request, response) {
+ response.write(createUpdateRDF({
+ [id]: [{
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "*",
+ updateLink: "http://localhost:" + gPort + "/data/signing_checks/" + file,
+ }]
+ }]
+ }));
+ });
+
+ yield Promise.all([
+ installListener,
+ AddonManagerPrivate.backgroundUpdateCheck()
+ ]);
+
+ testserver.registerPathHandler("/hotfix.rdf", null);
+ Services.prefs.clearUserPref(PREF_EM_HOTFIX_ID);
+});
+
+// Test valid AMO hotfix signed add-ons doesn't work if the fingerprint pref is wrong
+add_task(function* amo_signed_hotfix() {
+ Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", BAD_FINGERPRINT);
+
+ yield tryInstallHotfix("firefox-hotfix@mozilla.org",
+ "hotfix_good.xpi",
+ promiseFailedInstall());
+});
+
+// Test valid AMO hotfix signed add-ons works
+add_task(function* amo_signed_hotfix() {
+ Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", GOOD_FINGERPRINT);
+
+ yield tryInstallHotfix("firefox-hotfix@mozilla.org",
+ "hotfix_good.xpi",
+ promiseSuccessfulInstall());
+});
+
+// A hotfix altered after signing should fail
+add_task(function* amo_broken_hotfix() {
+ yield tryInstallHotfix("firefox-hotfix@mozilla.org",
+ "hotfix_broken.xpi",
+ promiseFailedInstall());
+});
+
+// Test an add-on with the wrong ID but signed by the right cert fails
+add_task(function* amo_wrongID_rightcert() {
+ yield tryInstallHotfix("test@tests.mozilla.org",
+ "hotfix_badid.xpi",
+ promiseFailedInstall());
+});
+
+// It shouldn't matter that it requested the ID matching the cert to begin with
+// if the embedded cert's ID doesn't match the add-on's ID
+add_task(function* amo_wrongID_rightcert2() {
+ yield tryInstallHotfix("firefox-hotfix@mozilla.org",
+ "hotfix_badid.xpi",
+ promiseFailedInstall());
+});
+
+// Test something signed by a regular AMO cert doesn't work
+add_task(function* amo_signed_addon() {
+ yield tryInstallHotfix("test@tests.mozilla.org",
+ "signed_bootstrap_1.xpi",
+ promiseFailedInstall());
+});
+
+// Test totally unsigned add-on fails
+add_task(function* unsigned() {
+ yield tryInstallHotfix("test@tests.mozilla.org",
+ "unsigned_bootstrap_2.xpi",
+ promiseFailedInstall());
+});
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+
+ run_next_test();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_install.js b/toolkit/mozapps/webextensions/test/xpcshell/test_install.js
new file mode 100644
index 000000000..60af3a9fd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_install.js
@@ -0,0 +1,1843 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be installed from XPI files
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+// install.rdf size, icon.png, icon64.png size
+const ADDON1_SIZE = 705 + 16 + 16;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+var testserver;
+var gInstallDate;
+var gInstall = null;
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerPathHandler("/redirect", function(aRequest, aResponse) {
+ aResponse.setStatusLine(null, 301, "Moved Permanently");
+ let url = aRequest.host + ":" + aRequest.port + aRequest.queryString;
+ aResponse.setHeader("Location", "http://" + url);
+ });
+ testserver.start(-1);
+ gPort = testserver.identity.primaryPort;
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Checks that an install from a local file proceeds as expected
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_neq(install.addon.syncGUID, null);
+ do_check_eq(install.addon.install, install);
+ do_check_eq(install.addon.size, ADDON1_SIZE);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ let file = do_get_addon("test_install1");
+ let uri = Services.io.newFileURI(file).spec;
+ do_check_eq(install.addon.getResourceURI("install.rdf").spec, "jar:" + uri + "!/install.rdf");
+ do_check_eq(install.addon.iconURL, "jar:" + uri + "!/icon.png");
+ do_check_eq(install.addon.icon64URL, "jar:" + uri + "!/icon64.png");
+ do_check_eq(install.iconURL, null);
+
+ do_check_eq(install.sourceURI.spec, uri);
+ do_check_eq(install.addon.sourceURI.spec, uri);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ AddonManager.getInstallsByTypes(["foo"], function(fooInstalls) {
+ do_check_eq(fooInstalls.length, 0);
+
+ AddonManager.getInstallsByTypes(["extension"], function(extensionInstalls) {
+ do_check_eq(extensionInstalls.length, 1);
+ do_check_eq(extensionInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ check_test_1(install.addon.syncGUID);
+ });
+ install.install();
+ });
+ });
+ });
+ });
+}
+
+function check_test_1(installSyncGUID) {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(olda1) {
+ do_check_eq(olda1, null);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(pendingAddons) {
+ do_check_eq(pendingAddons.length, 1);
+ do_check_eq(pendingAddons[0].id, "addon1@tests.mozilla.org");
+ let uri = NetUtil.newURI(pendingAddons[0].iconURL);
+ if (uri instanceof AM_Ci.nsIJARURI) {
+ let jarURI = uri.QueryInterface(AM_Ci.nsIJARURI);
+ let archiveURI = jarURI.JARFile;
+ let archiveFile = archiveURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(archiveFile);
+ do_check_true(zipReader.hasEntry(jarURI.JAREntry));
+ }
+ finally {
+ zipReader.close();
+ }
+ }
+ else {
+ let iconFile = uri.QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_true(iconFile.exists());
+ // Make the iconFile predictably old.
+ iconFile.lastModifiedTime = Date.now() - MAKE_FILE_OLD_DIFFERENCE;
+ }
+
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = pendingAddons[0].getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ // The pending add-on cannot be disabled or enabled.
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_DISABLE));
+
+ restartManager();
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls, 0);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_neq(a1, null);
+ do_check_neq(a1.syncGUID, null);
+ do_check_true(a1.syncGUID.length >= 9);
+ do_check_eq(a1.syncGUID, installSyncGUID);
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(do_get_addon("test_install1").exists());
+ do_check_in_crash_annotation(a1.id, a1.version);
+ do_check_eq(a1.size, ADDON1_SIZE);
+ do_check_false(a1.foreignInstall);
+
+ do_check_eq(a1.sourceURI.spec,
+ Services.io.newFileURI(do_get_addon("test_install1")).spec);
+ let difference = a1.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a1.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ do_check_true(a1.hasResource("install.rdf"));
+ do_check_false(a1.hasResource("foo.bar"));
+
+ let uri2 = do_get_addon_root_uri(profileDir, "addon1@tests.mozilla.org");
+ do_check_eq(a1.getResourceURI("install.rdf").spec, uri2 + "install.rdf");
+ do_check_eq(a1.iconURL, uri2 + "icon.png");
+ do_check_eq(a1.icon64URL, uri2 + "icon64.png");
+
+ // Ensure that extension bundle (or icon if unpacked) has updated
+ // lastModifiedDate.
+ let testURI = a1.getResourceURI(TEST_UNPACKED ? "icon.png" : "");
+ let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file;
+ do_check_true(testFile.exists());
+ difference = testFile.lastModifiedTime - Date.now();
+ do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE);
+
+ a1.uninstall();
+ let { id, version } = a1;
+ restartManager();
+ do_check_not_in_crash_annotation(id, version);
+
+ do_execute_soon(run_test_2);
+ }));
+ });
+ }));
+ });
+}
+
+// Tests that an install from a url downloads.
+function run_test_2() {
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 2");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.iconURL, null);
+ do_check_eq(install.sourceURI.spec, url);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_2);
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_execute_soon(function() {
+ Components.utils.forceGC();
+ });
+ }
+ });
+
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 2", null, "1.0");
+}
+
+function check_test_2(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Real Test 2");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ do_check_eq(install.iconURL, null);
+
+ // Pause the install here and start it again in run_test_3
+ do_execute_soon(function() { run_test_3(install); });
+ return false;
+}
+
+// Tests that the downloaded XPI installs ok
+function run_test_3(install) {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_3);
+ install.install();
+}
+
+function check_test_3(aInstall) {
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = aInstall.addon.getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_eq(olda2, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_neq(a2.syncGUID, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Real Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_1").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:" + gPort + "/addons/test_install2_1.xpi");
+
+ let difference = a2.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a2.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ gInstallDate = a2.installDate.getTime();
+
+ run_test_4();
+ });
+ });
+ }));
+}
+
+// Tests that installing a new version of an existing add-on works
+function run_test_4() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_4);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+}
+
+function check_test_4(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.existingAddon);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ run_test_5();
+ // Installation will continue when there is nothing returned.
+}
+
+// Continue installing the new version
+function run_test_5() {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_5);
+}
+
+function check_test_5(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ AddonManager.getInstallsByTypes(null, callback_soon(function(installs) {
+ do_check_eq(installs.length, 1);
+ do_check_eq(installs[0].addon, olda2.pendingUpgrade);
+ restartManager();
+
+ AddonManager.getInstallsByTypes(null, function(installs2) {
+ do_check_eq(installs2.length, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "3.0");
+ do_check_eq(a2.name, "Real Test 3");
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_2").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:" + gPort + "/addons/test_install2_2.xpi");
+ do_check_false(a2.foreignInstall);
+
+ do_check_eq(a2.installDate.getTime(), gInstallDate);
+ // Update date should be later (or the same if this test is too fast)
+ do_check_true(a2.installDate <= a2.updateDate);
+
+ a2.uninstall();
+ do_execute_soon(run_test_6);
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install that requires a compatibility update works
+function run_test_6() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_6);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_6(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.appDisabled);
+ run_test_7();
+ return true;
+}
+
+// Continue the install
+function run_test_7() {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_7);
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", callback_soon(function(olda3) {
+ do_check_eq(olda3, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_neq(a3.syncGUID, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+ }));
+}
+
+function run_test_8() {
+ restartManager();
+
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install3"), function(install) {
+ do_check_true(install.addon.isCompatible);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_8));
+ install.install();
+ });
+}
+
+function check_test_8() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_neq(a3.syncGUID, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that after cancelling a download it is removed from the active installs
+function run_test_9() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_9);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_9(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ // Allow the file removal to complete
+ do_execute_soon(function() {
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+ do_check_false(file.exists());
+
+ run_test_10();
+ });
+ });
+ });
+
+ install.cancel();
+}
+
+// Tests that after cancelling a pending install it is removed from the active
+// installs
+function run_test_10() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_10);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_10(install) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(callback_soon(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+
+ restartManager();
+
+ // Check that the install did not complete
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_eq(a3, null);
+
+ do_execute_soon(run_test_11);
+ });
+ }));
+}
+
+// Tests that a multi-package install shows up as multiple installs with the
+// correct sourceURI.
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install4"), function(install) {
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_neq(install.linkedInstalls, null);
+ do_check_eq(install.linkedInstalls.length, 5);
+
+ // Might be in any order so sort them based on ID
+ let installs = [install].concat(install.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.state != b.state) {
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 1;
+ else if (b.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return -1;
+ }
+
+ // Don't care what order the failed installs show up in
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 0;
+
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, install.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[0].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, install.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[1].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon6.xpi and would be incompatible with strict compat enabled
+ do_check_eq(installs[2].sourceURI, install.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_false(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[2].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, install.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[3].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ do_check_eq(installs[4].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[4].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_check_eq(installs[5].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[5].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 4);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_11));
+
+ installs[0].install();
+ installs[1].install();
+ installs[3].install();
+
+ // Note that we install addon6 last. Since it doesn't need a restart to
+ // install it completes asynchronously which would otherwise make the
+ // onInstallStarted/onInstallEnded events go out of sequence unless this
+ // is the last install operation
+ installs[2].install();
+ });
+ });
+}
+
+function check_test_11() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Same as test 11 but for a remote XPI
+function run_test_12() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install4.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ gInstall = install;
+
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "NO_ID": [
+ "onDownloadStarted",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onDownloadEnded"
+ ],
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_12));
+ install.install();
+ }, "application/x-xpinstall", null, "Multi Test 4");
+}
+
+function check_test_12() {
+ do_check_eq(gInstall.linkedInstalls.length, 5);
+
+ // Might be in any order so sort them based on ID
+ let installs = [gInstall].concat(gInstall.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.state != b.state) {
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 1;
+ else if (b.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return -1;
+ }
+
+ // Don't care what order the failed installs show up in
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 0;
+
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon6.xpi and would be incompatible with strict compat enabled
+ do_check_eq(installs[2].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_false(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_INSTALLED);
+
+ do_check_eq(installs[4].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[4].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_check_eq(installs[5].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[5].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+
+// Tests that cancelling an upgrade leaves the original add-on's pendingOperations
+// correct
+function run_test_13() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_13);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+ });
+}
+
+function check_test_13(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, install.addon);
+
+ do_check_true(hasFlag(install.addon.pendingOperations,
+ AddonManager.PENDING_INSTALL));
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ install.cancel();
+
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ do_check_false(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, null);
+
+ restartManager();
+
+ // Check that the upgrade did not complete
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.version, "2.0");
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+ }));
+}
+
+// Check that cancelling the install from onDownloadStarted actually cancels it
+function run_test_14() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted"
+ ], check_test_14);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_14(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_throw("Download should not have continued");
+ },
+ onDownloadEnded: function() {
+ do_throw("Download should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it continues downloading. The
+ // The listener only really tests if we give it time to see progress, the
+ // file check isn't ideal either
+ do_execute_soon(function() {
+ do_check_false(file.exists());
+
+ run_test_15();
+ });
+ });
+
+ // Wait for the channel to be ready to cancel
+ do_execute_soon(function() {
+ install.cancel();
+ });
+}
+
+// Checks that cancelling the install from onDownloadEnded actually cancels it
+function run_test_15() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_15);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_15(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ install.addListener({
+ onInstallStarted: function() {
+ do_throw("Install should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it starts installing
+ do_execute_soon(run_test_16);
+}
+
+// Verify that the userDisabled value carries over to the upgrade by default
+function run_test_16() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install2_1_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url_2 = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install2_2_ended() {
+ do_check_true(aInstall_2.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_17);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_17() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install2_1_ended2() {
+ do_check_false(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ let url_2 = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall_2.addon.userDisabled);
+ aInstall_2.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install2_2_ended2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_18);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_18() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_1_ended3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url_2 = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallStarted: function() {
+ do_check_true(aInstall_2.addon.userDisabled);
+ aInstall_2.addon.userDisabled = false;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_2_ended3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_false(a2_2.userDisabled);
+ do_check_true(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_18_1);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Checks that metadata is not stored if the pref is set to false
+function run_test_18_1() {
+ restartManager();
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:" + gPort + "/data/test_install.xml");
+
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", false);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test18_1_install_ended() {
+ do_check_neq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_19);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Checks that metadata is downloaded for new installs and is visible before and
+// after restart
+function run_test_19() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", true);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test19_install_ended() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_20);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Do the same again to make sure it works when the data is already in the cache
+function run_test_20() {
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test20_install_ended() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_21);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that installing an add-on that is already pending install cancels the
+// first install
+function run_test_21() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled",
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallCancelled",
+ "onInstallEnded",
+ ], check_test_21);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+}
+
+function check_test_21(aInstall) {
+ AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], aInstall);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2, null);
+
+ run_test_22();
+ });
+ }));
+}
+
+// Tests that an install can be restarted after being cancelled
+function run_test_22() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_22);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_22(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_22);
+
+ aInstall.install();
+}
+
+function finish_test_22(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_23();
+}
+
+// Tests that an install can be restarted after being cancelled when a hash
+// was provided
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_23);
+ aInstall.install();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function check_test_23(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_23);
+
+ aInstall.install();
+}
+
+function finish_test_23(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_24();
+}
+
+// Tests that an install with a bad hash can be restarted after it fails, though
+// it will only fail again
+function run_test_24() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadFailed",
+ ], check_test_24);
+ aInstall.install();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+function check_test_24(aInstall) {
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadFailed"
+ ], run_test_25);
+
+ aInstall.install();
+}
+
+// Tests that installs with a hash for a local file work
+function run_test_25() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = Services.io.newFileURI(do_get_addon("test_install3")).spec;
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(aInstall.error, 0);
+
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_26();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function run_test_26() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadCancelled"
+ ]);
+
+ let observerService = AM_Cc["@mozilla.org/network/http-activity-distributor;1"].
+ getService(AM_Ci.nsIHttpActivityDistributor);
+ observerService.addObserver({
+ observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
+ aStringData) {
+ aChannel.QueryInterface(AM_Ci.nsIChannel);
+ // Wait for the final event for the redirected URL
+ if (aChannel.URI.spec != "http://localhost:" + gPort + "/addons/test_install1.xpi" ||
+ aType != AM_Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION ||
+ aSubtype != AM_Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
+ return;
+
+ // Request should have been cancelled
+ do_check_eq(aChannel.status, Components.results.NS_BINDING_ABORTED);
+
+ observerService.removeObserver(this);
+
+ run_test_27();
+ }
+ });
+
+ let url = "http://localhost:" + gPort + "/redirect?/addons/test_install1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onDownloadProgress: function(aDownloadProgressInstall) {
+ aDownloadProgressInstall.cancel();
+ }
+ });
+
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Tests that an install can be restarted during onDownloadCancelled after being
+// cancelled in mid-download
+function run_test_27() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ aInstall.addListener({
+ onDownloadProgress: function() {
+ aInstall.removeListener(this);
+ aInstall.cancel();
+ }
+ });
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadCancelled",
+ ], check_test_27);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_27);
+
+ let file = aInstall.file;
+ aInstall.install();
+ do_check_neq(file.path, aInstall.file.path);
+ do_check_false(file.exists());
+}
+
+function finish_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_28();
+}
+
+// Tests that an install that isn't strictly compatible and has
+// binary components correctly has appDisabled set (see bug 702868).
+function run_test_28() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install5.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 5");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted"
+ ], check_test_28);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 5", null, "1.0");
+}
+
+function check_test_28(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 5");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLING);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.isCompatible);
+ do_check_true(install.addon.appDisabled);
+
+ prepare_test({}, [
+ "onInstallCancelled"
+ ], finish_test_28);
+ return false;
+}
+
+function finish_test_28(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], run_test_29);
+
+ install.cancel();
+}
+
+// Tests that an install with a matching compatibility override has appDisabled
+// set correctly.
+function run_test_29() {
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:" + gPort + "/addons/test_install6.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Addon Test 6");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_29);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Addon Test 6", null, "1.0");
+}
+
+function check_test_29(install) {
+ // ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.addon, null);
+ do_check_false(install.addon.isCompatible);
+ do_check_true(install.addon.appDisabled);
+
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], run_test_30);
+ install.cancel();
+ return false;
+}
+
+// Tests that a multi-package XPI with no add-ons inside shows up as a
+// corrupt file
+function run_test_30() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install7"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+ do_check_eq(install.linkedInstalls, null);
+
+ run_test_31();
+ });
+}
+
+// Tests that a multi-package XPI with no valid add-ons inside shows up as a
+// corrupt file
+function run_test_31() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install8"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+ do_check_eq(install.linkedInstalls, null);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_install_from_sources.js b/toolkit/mozapps/webextensions/test/xpcshell/test_install_from_sources.js
new file mode 100644
index 000000000..18bb7d74e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_install_from_sources.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+BootstrapMonitor.init();
+
+// Partial list of bootstrap reasons from XPIProvider.jsm
+const BOOTSTRAP_REASONS = {
+ ADDON_INSTALL: 5,
+ ADDON_UPGRADE: 7,
+ ADDON_DOWNGRADE: 8,
+};
+
+// Install an unsigned add-on with no existing add-on present.
+// Restart and make sure it is still around.
+add_task(function*() {
+ let extInstallCalled = false;
+ AddonManager.addInstallListener({
+ onExternalInstall: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ extInstallCalled = true;
+ },
+ });
+
+ let installingCalled = false;
+ let installedCalled = false;
+ AddonManager.addAddonListener({
+ onInstalling: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ installingCalled = true;
+ },
+ onInstalled: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ installedCalled = true;
+ },
+ onInstallStarted: (aInstall) => {
+ do_throw("onInstallStarted called unexpectedly");
+ }
+ });
+
+ yield AddonManager.installAddonFromSources(do_get_file("data/from_sources/"));
+
+ do_check_true(extInstallCalled);
+ do_check_true(installingCalled);
+ do_check_true(installedCalled);
+
+ let install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ yield promiseRestartManager();
+
+ install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ yield promiseRestartManager();
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_install_icons.js b/toolkit/mozapps/webextensions/test/xpcshell/test_install_icons.js
new file mode 100644
index 000000000..70f91c560
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_install_icons.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// use httpserver to find an available port
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+
+var addon_url = "http://localhost:" + gPort + "/test.xpi";
+var icon32_url = "http://localhost:" + gPort + "/icon.png";
+var icon64_url = "http://localhost:" + gPort + "/icon64.png";
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+
+ test_1();
+}
+
+function test_1() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, null);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], undefined);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_2();
+ }, "application/x-xpinstall", null, null, null, null, null);
+}
+
+function test_2() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_3();
+ }, "application/x-xpinstall", null, null, icon32_url, null, null);
+}
+
+function test_3() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], undefined);
+ test_4();
+ }, "application/x-xpinstall", null, null, { "32": icon32_url }, null, null);
+}
+
+function test_4() {
+ AddonManager.getInstallForURL(addon_url, function(aInstall) {
+ do_check_eq(aInstall.iconURL, icon32_url);
+ do_check_neq(aInstall.icons, null);
+ do_check_eq(aInstall.icons[32], icon32_url);
+ do_check_eq(aInstall.icons[64], icon64_url);
+ do_execute_soon(do_test_finished);
+ }, "application/x-xpinstall", null, null, { "32": icon32_url, "64": icon64_url }, null, null);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_install_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_install_strictcompat.js
new file mode 100644
index 000000000..77f806ba2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_install_strictcompat.js
@@ -0,0 +1,1726 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be installed from XPI files
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+
+// install.rdf size, icon.png, icon64.png size
+const ADDON1_SIZE = 705 + 16 + 16;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/httpd.js");
+
+var testserver;
+var gInstallDate;
+var gInstall = null;
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ // Create and configure the HTTP server.
+ testserver = new HttpServer();
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerPathHandler("/redirect", function(aRequest, aResponse) {
+ aResponse.setStatusLine(null, 301, "Moved Permanently");
+ let url = aRequest.host + ":" + aRequest.port + aRequest.queryString;
+ aResponse.setHeader("Location", "http://" + url);
+ });
+ testserver.start(4444);
+
+ do_test_pending();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+// Checks that an install from a local file proceeds as expected
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.type, "extension");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_eq(install.addon.install, install);
+ do_check_eq(install.addon.size, ADDON1_SIZE);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ let file = do_get_addon("test_install1");
+ let uri = Services.io.newFileURI(file).spec;
+ do_check_eq(install.addon.getResourceURI("install.rdf").spec, "jar:" + uri + "!/install.rdf");
+ do_check_eq(install.addon.iconURL, "jar:" + uri + "!/icon.png");
+ do_check_eq(install.addon.icon64URL, "jar:" + uri + "!/icon64.png");
+ do_check_eq(install.iconURL, null);
+
+ do_check_eq(install.sourceURI.spec, uri);
+ do_check_eq(install.addon.sourceURI.spec, uri);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ AddonManager.getInstallsByTypes(["foo"], function(fooInstalls) {
+ do_check_eq(fooInstalls.length, 0);
+
+ AddonManager.getInstallsByTypes(["extension"], function(extensionInstalls) {
+ do_check_eq(extensionInstalls.length, 1);
+ do_check_eq(extensionInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_1);
+ install.install();
+ });
+ });
+ });
+ });
+}
+
+function check_test_1() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(olda1) {
+ do_check_eq(olda1, null);
+
+ AddonManager.getAddonsWithOperationsByTypes(null, callback_soon(function(pendingAddons) {
+ do_check_eq(pendingAddons.length, 1);
+ do_check_eq(pendingAddons[0].id, "addon1@tests.mozilla.org");
+ let uri = NetUtil.newURI(pendingAddons[0].iconURL);
+ if (uri instanceof AM_Ci.nsIJARURI) {
+ let jarURI = uri.QueryInterface(AM_Ci.nsIJARURI);
+ let archiveURI = jarURI.JARFile;
+ let archiveFile = archiveURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+ createInstance(Ci.nsIZipReader);
+ try {
+ zipReader.open(archiveFile);
+ do_check_true(zipReader.hasEntry(jarURI.JAREntry));
+ }
+ finally {
+ zipReader.close();
+ }
+ }
+ else {
+ let iconFile = uri.QueryInterface(AM_Ci.nsIFileURL).file;
+ do_check_true(iconFile.exists());
+ }
+
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = pendingAddons[0].getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ // The pending add-on cannot be disabled or enabled.
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_false(hasFlag(pendingAddons[0].permissions, AddonManager.PERM_CAN_DISABLE));
+
+ restartManager();
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls, 0);
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(do_get_addon("test_install1").exists());
+ do_check_in_crash_annotation(a1.id, a1.version);
+ do_check_eq(a1.size, ADDON1_SIZE);
+
+ do_check_eq(a1.sourceURI.spec,
+ Services.io.newFileURI(do_get_addon("test_install1")).spec);
+ let difference = a1.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a1.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ do_check_true(a1.hasResource("install.rdf"));
+ do_check_false(a1.hasResource("foo.bar"));
+
+ let root_uri = do_get_addon_root_uri(profileDir, "addon1@tests.mozilla.org");
+ do_check_eq(a1.getResourceURI("install.rdf").spec, root_uri + "install.rdf");
+ do_check_eq(a1.iconURL, root_uri + "icon.png");
+ do_check_eq(a1.icon64URL, root_uri + "icon64.png");
+
+ a1.uninstall();
+ do_execute_soon(function() { run_test_2(a1) });
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install from a url downloads.
+function run_test_2(aAddon) {
+ let { id, version } = aAddon;
+ restartManager();
+ do_check_not_in_crash_annotation(id, version);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test 2");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.iconURL, null);
+ do_check_eq(install.sourceURI.spec, url);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_2);
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_execute_soon(function() {
+ Components.utils.forceGC();
+ });
+ }
+ });
+
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 2", null, "1.0");
+}
+
+function check_test_2(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.name, "Real Test 2");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+ do_check_eq(install.iconURL, null);
+
+ // Pause the install here and start it again in run_test_3
+ do_execute_soon(function() { run_test_3(install); });
+ return false;
+}
+
+// Tests that the downloaded XPI installs ok
+function run_test_3(install) {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_3);
+ install.install();
+}
+
+function check_test_3(aInstall) {
+ // Make the pending install have a sensible date
+ let updateDate = Date.now();
+ let extURI = aInstall.addon.getResourceURI("");
+ let ext = extURI.QueryInterface(AM_Ci.nsIFileURL).file;
+ setExtensionModifiedTime(ext, updateDate);
+
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_eq(olda2, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Real Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_1").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:4444/addons/test_install2_1.xpi");
+
+ let difference = a2.installDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on install time was out by " + difference + "ms");
+
+ difference = a2.updateDate.getTime() - updateDate;
+ if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
+ do_throw("Add-on update time was out by " + difference + "ms");
+
+ gInstallDate = a2.installDate.getTime();
+
+ run_test_4();
+ });
+ });
+ }));
+}
+
+// Tests that installing a new version of an existing add-on works
+function run_test_4() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_4);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+}
+
+function check_test_4(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_neq(install.existingAddon);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ run_test_5();
+ // Installation will continue when there is nothing returned.
+}
+
+// Continue installing the new version
+function run_test_5() {
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_5);
+}
+
+function check_test_5(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+
+ AddonManager.getInstallsByTypes(null, callback_soon(function(installs) {
+ do_check_eq(installs.length, 1);
+ do_check_eq(installs[0].addon, olda2.pendingUpgrade);
+ restartManager();
+
+ AddonManager.getInstallsByTypes(null, function(installs2) {
+ do_check_eq(installs2.length, 0);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_eq(a2.type, "extension");
+ do_check_eq(a2.version, "3.0");
+ do_check_eq(a2.name, "Real Test 3");
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(do_get_addon("test_install2_2").exists());
+ do_check_in_crash_annotation(a2.id, a2.version);
+ do_check_eq(a2.sourceURI.spec,
+ "http://localhost:4444/addons/test_install2_2.xpi");
+
+ do_check_eq(a2.installDate.getTime(), gInstallDate);
+ // Update date should be later (or the same if this test is too fast)
+ do_check_true(a2.installDate <= a2.updateDate);
+
+ a2.uninstall();
+ do_execute_soon(run_test_6);
+ });
+ });
+ }));
+ });
+}
+
+// Tests that an install that requires a compatibility update works
+function run_test_6() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_6);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_6(install) {
+ ensure_test_completed();
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.existingAddon, null);
+ do_check_false(install.addon.appDisabled);
+ run_test_7();
+ return true;
+}
+
+// Continue the install
+function run_test_7() {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_7);
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", callback_soon(function(olda3) {
+ do_check_eq(olda3, null);
+ restartManager();
+
+ AddonManager.getAllInstalls(function(installs) {
+ do_check_eq(installs, 0);
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_8);
+ });
+ });
+ }));
+}
+
+function run_test_8() {
+ restartManager();
+
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install3"), function(install) {
+ do_check_true(install.addon.isCompatible);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_8));
+ install.install();
+ });
+}
+
+function check_test_8() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_eq(a3.type, "extension");
+ do_check_eq(a3.version, "1.0");
+ do_check_eq(a3.name, "Real Test 4");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(do_get_addon("test_install3").exists());
+ a3.uninstall();
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Test that after cancelling a download it is removed from the active installs
+function run_test_9() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_9);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_9(install) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ // Allow the file removal to complete
+ do_execute_soon(function() {
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+ do_check_false(file.exists());
+
+ run_test_10();
+ });
+ });
+ });
+
+ install.cancel();
+}
+
+// Tests that after cancelling a pending install it is removed from the active
+// installs
+function run_test_10() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Real Test 4");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getInstallsByTypes(null, function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], check_test_10);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Real Test 4", null, "1.0");
+}
+
+function check_test_10(install) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(callback_soon(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 0);
+
+ restartManager();
+
+ // Check that the install did not complete
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_eq(a3, null);
+
+ run_test_11();
+ });
+ }));
+}
+
+// Tests that a multi-package install shows up as multiple installs with the
+// correct sourceURI.
+function run_test_11() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install4"), function(install) {
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_neq(install.linkedInstalls, null);
+ do_check_eq(install.linkedInstalls.length, 5);
+
+ // Might be in any order so sort them based on ID
+ let installs = [install].concat(install.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.state != b.state) {
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 1;
+ else if (b.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return -1;
+ }
+
+ // Don't care what order the failed installs show up in
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 0;
+
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, install.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[0].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, install.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[1].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon6.xpi and is incompatible
+ do_check_eq(installs[2].sourceURI, install.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_true(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_DOWNLOADED);
+ do_check_false(hasFlag(installs[2].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, install.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(installs[3].addon.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ do_check_eq(installs[4].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[4].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_check_eq(installs[5].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[5].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 4);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_11));
+
+ installs[0].install();
+ installs[1].install();
+ installs[3].install();
+
+ // Note that we install addon6 last. Since it doesn't need a restart to
+ // install it completes asynchronously which would otherwise make the
+ // onInstallStarted/onInstallEnded events go out of sequence unless this
+ // is the last install operation
+ installs[2].install();
+ });
+ });
+}
+
+function check_test_11() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Same as test 11 but for a remote XPI
+function run_test_12() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall",
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install4.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ gInstall = install;
+
+ ensure_test_completed();
+ do_check_neq(install, null);
+ do_check_eq(install.linkedInstalls, null);
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({
+ "addon4@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstalling"
+ ],
+ "addon6@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, {
+ "NO_ID": [
+ "onDownloadStarted",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onNewInstall",
+ "onDownloadEnded"
+ ],
+ "addon4@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon5@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon6@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ],
+ "addon7@tests.mozilla.org": [
+ "onInstallStarted",
+ "onInstallEnded"
+ ]
+ }, callback_soon(check_test_12));
+ install.install();
+ }, "application/x-xpinstall", null, "Multi Test 4");
+}
+
+function check_test_12() {
+ do_check_eq(gInstall.linkedInstalls.length, 5);
+
+ // Might be in any order so sort them based on ID
+ let installs = [gInstall].concat(gInstall.linkedInstalls);
+ installs.sort(function(a, b) {
+ if (a.state != b.state) {
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 1;
+ else if (b.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return -1;
+ }
+
+ // Don't care what order the failed installs show up in
+ if (a.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ return 0;
+
+ if (a.addon.id < b.addon.id)
+ return -1;
+ if (a.addon.id > b.addon.id)
+ return 1;
+ return 0;
+ });
+
+ // Comes from addon4.xpi and is made compatible by an update check
+ do_check_eq(installs[0].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[0].addon.id, "addon4@tests.mozilla.org");
+ do_check_false(installs[0].addon.appDisabled);
+ do_check_eq(installs[0].version, "1.0");
+ do_check_eq(installs[0].name, "Multi Test 1");
+ do_check_eq(installs[0].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon5.jar and is compatible by default
+ do_check_eq(installs[1].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[1].addon.id, "addon5@tests.mozilla.org");
+ do_check_false(installs[1].addon.appDisabled);
+ do_check_eq(installs[1].version, "3.0");
+ do_check_eq(installs[1].name, "Multi Test 2");
+ do_check_eq(installs[1].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon6.xpi and is incompatible
+ do_check_eq(installs[2].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[2].addon.id, "addon6@tests.mozilla.org");
+ do_check_true(installs[2].addon.appDisabled);
+ do_check_eq(installs[2].version, "2.0");
+ do_check_eq(installs[2].name, "Multi Test 3");
+ do_check_eq(installs[2].state, AddonManager.STATE_INSTALLED);
+
+ // Comes from addon7.jar and is made compatible by an update check
+ do_check_eq(installs[3].sourceURI, gInstall.sourceURI);
+ do_check_eq(installs[3].addon.id, "addon7@tests.mozilla.org");
+ do_check_false(installs[3].addon.appDisabled);
+ do_check_eq(installs[3].version, "5.0");
+ do_check_eq(installs[3].name, "Multi Test 4");
+ do_check_eq(installs[3].state, AddonManager.STATE_INSTALLED);
+
+ do_check_eq(installs[4].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[4].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ do_check_eq(installs[5].state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(installs[5].error, AddonManager.ERROR_CORRUPT_FILE);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a4, a5, a6, a7]) {
+ do_check_neq(a4, null);
+ do_check_neq(a5, null);
+ do_check_neq(a6, null);
+ do_check_neq(a7, null);
+
+ a4.uninstall();
+ a5.uninstall();
+ a6.uninstall();
+ a7.uninstall();
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+
+// Tests that cancelling an upgrade leaves the original add-on's pendingOperations
+// correct
+function run_test_13() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Test 3");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+
+ AddonManager.getAllInstalls(function(activeInstalls) {
+ do_check_eq(activeInstalls.length, 1);
+ do_check_eq(activeInstalls[0], install);
+ do_check_eq(install.existingAddon, null);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_13);
+ install.install();
+ });
+ }, "application/x-xpinstall", null, "Test 3", null, "3.0");
+ });
+}
+
+function check_test_13(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.version, "3.0");
+ do_check_eq(install.name, "Real Test 3");
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon2@tests.mozilla.org");
+ do_check_eq(install.addon.install, install);
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(olda2) {
+ do_check_neq(olda2, null);
+ do_check_true(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, install.addon);
+
+ do_check_true(hasFlag(install.addon.pendingOperations,
+ AddonManager.PENDING_INSTALL));
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ install.cancel();
+
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ do_check_false(hasFlag(olda2.pendingOperations, AddonManager.PENDING_UPGRADE));
+ do_check_eq(olda2.pendingUpgrade, null);
+
+ restartManager();
+
+ // Check that the upgrade did not complete
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.version, "2.0");
+
+ a2.uninstall();
+
+ do_execute_soon(run_test_14);
+ });
+ }));
+}
+
+// Check that cancelling the install from onDownloadStarted actually cancels it
+function run_test_14() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted"
+ ], check_test_14);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_14(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ], function() {
+ let file = install.file;
+
+ install.addListener({
+ onDownloadProgress: function() {
+ do_throw("Download should not have continued");
+ },
+ onDownloadEnded: function() {
+ do_throw("Download should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it continues downloading. The
+ // The listener only really tests if we give it time to see progress, the
+ // file check isn't ideal either
+ do_execute_soon(function() {
+ do_check_false(file.exists());
+
+ run_test_15();
+ });
+ });
+
+ // Wait for the channel to be ready to cancel
+ do_execute_soon(function() {
+ install.cancel();
+ });
+}
+
+// Checks that cancelling the install from onDownloadEnded actually cancels it
+function run_test_15() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(install) {
+ ensure_test_completed();
+
+ do_check_eq(install.file, null);
+
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], check_test_15);
+ install.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_15(install) {
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ install.cancel();
+
+ ensure_test_completed();
+
+ install.addListener({
+ onInstallStarted: function() {
+ do_throw("Install should not have continued");
+ }
+ });
+
+ // Allow the listener to return to see if it starts installing
+ do_execute_soon(run_test_16);
+}
+
+// Verify that the userDisabled value carries over to the upgrade by default
+function run_test_16() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test16_install1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url_2 = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function test16_install2() {
+ do_check_true(aInstall_2.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_17);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_17() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function() {
+ do_check_false(aInstall.addon.userDisabled);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.isActive);
+
+ let url_2 = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall_2.addon.userDisabled);
+ aInstall_2.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_true(a2_2.userDisabled);
+ do_check_false(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_18);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that changing the userDisabled value before onInstallEnded works
+function run_test_18() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallStarted: function() {
+ do_check_false(aInstall.addon.userDisabled);
+ aInstall.addon.userDisabled = true;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test18_install1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.isActive);
+
+ let url_2 = "http://localhost:4444/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallStarted: function() {
+ do_check_true(aInstall_2.addon.userDisabled);
+ aInstall_2.addon.userDisabled = false;
+ },
+
+ onInstallEnded: function() {
+ do_execute_soon(function test18_install2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2_2) {
+ do_check_false(a2_2.userDisabled);
+ do_check_true(a2_2.isActive);
+
+ a2_2.uninstall();
+ do_execute_soon(run_test_18_1);
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Checks that metadata is not stored if the pref is set to false
+function run_test_18_1() {
+ restartManager();
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:4444/data/test_install.xml");
+
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", false);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test18_install() {
+ do_check_neq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_19);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Checks that metadata is downloaded for new installs and is visible before and
+// after restart
+function run_test_19() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.addon2@tests.mozilla.org.getAddons.cache.enabled", true);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test19_install() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_20);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Do the same again to make sure it works when the data is already in the cache
+function run_test_20() {
+ restartManager();
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function(unused, aAddon) {
+ do_execute_soon(function test20_install() {
+ do_check_eq(aAddon.fullDescription, "Repository description");
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2.fullDescription, "Repository description");
+
+ a2.uninstall();
+ do_execute_soon(run_test_21);
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+// Verify that installing an add-on that is already pending install cancels the
+// first install
+function run_test_21() {
+ restartManager();
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", false);
+
+ installAllFiles([do_get_addon("test_install2_1")], function() {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled",
+ "onInstalling"
+ ]
+ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallCancelled",
+ "onInstallEnded",
+ ], check_test_21);
+
+ let url = "http://localhost:4444/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+ });
+}
+
+function check_test_21(aInstall) {
+ AddonManager.getAllInstalls(callback_soon(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], aInstall);
+
+ prepare_test({
+ "addon2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled",
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_eq(a2, null);
+
+ run_test_22();
+ });
+ }));
+}
+
+// Tests that an install can be restarted after being cancelled
+function run_test_22() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_22);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_22(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_22);
+
+ aInstall.install();
+}
+
+function finish_test_22(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_23();
+}
+
+// Tests that an install can be restarted after being cancelled when a hash
+// was provided
+function run_test_23() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_23);
+ aInstall.install();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function check_test_23(aInstall) {
+ prepare_test({}, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_23);
+
+ aInstall.install();
+}
+
+function finish_test_23(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_24();
+}
+
+// Tests that an install with a bad hash can be restarted after it fails, though
+// it will only fail again
+function run_test_24() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadFailed",
+ ], check_test_24);
+ aInstall.install();
+ }, "application/x-xpinstall", "sha1:foo");
+}
+
+function check_test_24(aInstall) {
+ prepare_test({ }, [
+ "onDownloadStarted",
+ "onDownloadFailed"
+ ], run_test_25);
+
+ aInstall.install();
+}
+
+// Tests that installs with a hash for a local file work
+function run_test_25() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = Services.io.newFileURI(do_get_addon("test_install3")).spec;
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(aInstall.error, 0);
+
+ prepare_test({ }, [
+ "onDownloadCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_26();
+ }, "application/x-xpinstall", do_get_addon_hash("test_install3"));
+}
+
+function run_test_26() {
+ prepare_test({ }, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadCancelled"
+ ]);
+
+ let observerService = AM_Cc["@mozilla.org/network/http-activity-distributor;1"].
+ getService(AM_Ci.nsIHttpActivityDistributor);
+ observerService.addObserver({
+ observeActivity: function(aChannel, aType, aSubtype, aTimestamp, aSizeData,
+ aStringData) {
+ aChannel.QueryInterface(AM_Ci.nsIChannel);
+ // Wait for the final event for the redirected URL
+ if (aChannel.URI.spec != "http://localhost:4444/addons/test_install1.xpi" ||
+ aType != AM_Ci.nsIHttpActivityObserver.ACTIVITY_TYPE_HTTP_TRANSACTION ||
+ aSubtype != AM_Ci.nsIHttpActivityObserver.ACTIVITY_SUBTYPE_TRANSACTION_CLOSE)
+ return;
+
+ // Request should have been cancelled
+ do_check_eq(aChannel.status, Components.results.NS_BINDING_ABORTED);
+
+ observerService.removeObserver(this);
+
+ run_test_27();
+ }
+ });
+
+ let url = "http://localhost:4444/redirect?/addons/test_install1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onDownloadProgress: function(aDownloadProgressInstall) {
+ aDownloadProgressInstall.cancel();
+ }
+ });
+
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+
+// Tests that an install can be restarted during onDownloadCancelled after being
+// cancelled in mid-download
+function run_test_27() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ let url = "http://localhost:4444/addons/test_install3.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ ensure_test_completed();
+
+ do_check_neq(aInstall, null);
+ do_check_eq(aInstall.state, AddonManager.STATE_AVAILABLE);
+
+ aInstall.addListener({
+ onDownloadProgress: function() {
+ aInstall.removeListener(this);
+ aInstall.cancel();
+ }
+ });
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadCancelled",
+ ], check_test_27);
+ aInstall.install();
+ }, "application/x-xpinstall");
+}
+
+function check_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ "onInstallStarted",
+ "onInstallEnded"
+ ], finish_test_27);
+
+ let file = aInstall.file;
+ aInstall.install();
+ do_check_neq(file.path, aInstall.file.path);
+ do_check_false(file.exists());
+}
+
+function finish_test_27(aInstall) {
+ prepare_test({
+ "addon3@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ }, [
+ "onInstallCancelled"
+ ]);
+
+ aInstall.cancel();
+
+ ensure_test_completed();
+
+ run_test_30();
+}
+
+// Tests that a multi-package XPI with no add-ons inside shows up as a
+// corrupt file
+function run_test_30() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install7"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+ do_check_eq(install.linkedInstalls, null);
+
+ run_test_31();
+ });
+}
+
+// Tests that a multi-package XPI with no valid add-ons inside shows up as a
+// corrupt file
+function run_test_31() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install8"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+ do_check_eq(install.linkedInstalls, null);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_isDebuggable.js b/toolkit/mozapps/webextensions/test/xpcshell/test_isDebuggable.js
new file mode 100644
index 000000000..87f2856b0
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_isDebuggable.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var ADDONS = [
+ "test_bootstrap2_1", // restartless addon
+ "test_bootstrap1_4", // old-school addon
+ "test_jetpack" // sdk addon
+];
+
+var IDS = [
+ "bootstrap1@tests.mozilla.org",
+ "bootstrap2@tests.mozilla.org",
+ "jetpack@tests.mozilla.org"
+];
+
+function run_test() {
+ do_test_pending();
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ startupManager();
+ AddonManager.checkCompatibility = false;
+
+ installAllFiles(ADDONS.map(do_get_addon), function () {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(IDS, function([a1, a2, a3]) {
+ do_check_eq(a1.isDebuggable, false);
+ do_check_eq(a2.isDebuggable, true);
+ do_check_eq(a3.isDebuggable, true);
+ do_test_finished();
+ });
+ }, true);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_isReady.js b/toolkit/mozapps/webextensions/test/xpcshell/test_isReady.js
new file mode 100644
index 000000000..6222398a7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_isReady.js
@@ -0,0 +1,49 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* () {
+ equal(AddonManager.isReady, false, "isReady should be false before startup");
+
+ let gotStartupEvent = false;
+ let gotShutdownEvent = false;
+ let listener = {
+ onStartup() {
+ gotStartupEvent = true;
+ },
+ onShutdown() {
+ gotShutdownEvent = true;
+ },
+ };
+ AddonManager.addManagerListener(listener);
+
+ do_print("Starting manager...");
+ startupManager();
+ equal(AddonManager.isReady, true, "isReady should be true after startup");
+ equal(gotStartupEvent, true, "Should have seen onStartup event after startup");
+ equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown");
+
+ gotStartupEvent = false;
+ gotShutdownEvent = false;
+
+ do_print("Shutting down manager...");
+ let shutdownPromise = promiseShutdownManager();
+ equal(AddonManager.isReady, false, "isReady should be false when shutdown commences");
+ yield shutdownPromise;
+
+ equal(AddonManager.isReady, false, "isReady should be false after shutdown");
+ equal(gotStartupEvent, false, "Should not have seen onStartup event after shutdown");
+ equal(gotShutdownEvent, true, "Should have seen onShutdown event after shutdown");
+
+ AddonManager.addManagerListener(listener);
+ gotStartupEvent = false;
+ gotShutdownEvent = false;
+
+ do_print("Starting manager again...");
+ startupManager();
+ equal(AddonManager.isReady, true, "isReady should be true after repeat startup");
+ equal(gotStartupEvent, true, "Should have seen onStartup event after repeat startup");
+ equal(gotShutdownEvent, false, "Should not have seen onShutdown event before shutdown, following repeat startup");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_json_updatecheck.js b/toolkit/mozapps/webextensions/test/xpcshell/test_json_updatecheck.js
new file mode 100644
index 000000000..adf789afb
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_json_updatecheck.js
@@ -0,0 +1,372 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+"use strict";
+
+// This verifies that AddonUpdateChecker works correctly for JSON
+// update manifests, particularly for behavior which does not
+// cleanly overlap with RDF manifests.
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+const TOOLKIT_MINVERSION = "42.0a1";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42.0a2", "42.0a2");
+
+Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+
+let testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+
+let gUpdateManifests = {};
+
+function mapManifest(aPath, aManifestData) {
+ gUpdateManifests[aPath] = aManifestData;
+ testserver.registerPathHandler(aPath, serveManifest);
+}
+
+function serveManifest(request, response) {
+ let manifest = gUpdateManifests[request.path];
+
+ response.setHeader("Content-Type", manifest.contentType, false);
+ response.write(manifest.data);
+}
+
+const extensionsDir = gProfD.clone();
+extensionsDir.append("extensions");
+
+
+function checkUpdates(aData) {
+ // Registers JSON update manifest for it with the testing server,
+ // checks for updates, and yields the list of updates on
+ // success.
+
+ let extension = aData.manifestExtension || "json";
+
+ let path = `/updates/${aData.id}.${extension}`;
+ let updateUrl = `http://localhost:${gPort}${path}`
+
+ let addonData = {};
+ if ("updates" in aData)
+ addonData.updates = aData.updates;
+
+ let manifestJSON = {
+ "addons": {
+ [aData.id]: addonData
+ }
+ };
+
+ mapManifest(path.replace(/\?.*/, ""),
+ { data: JSON.stringify(manifestJSON),
+ contentType: aData.contentType || "application/json" });
+
+
+ return new Promise((resolve, reject) => {
+ AddonUpdateChecker.checkForUpdates(aData.id, aData.updateKey, updateUrl, {
+ onUpdateCheckComplete: resolve,
+
+ onUpdateCheckError: function(status) {
+ reject(new Error("Update check failed with status " + status));
+ }
+ });
+ });
+}
+
+
+add_task(function* test_default_values() {
+ // Checks that the appropriate defaults are used for omitted values.
+
+ startupManager();
+
+ let updates = yield checkUpdates({
+ id: "updatecheck-defaults@tests.mozilla.org",
+ version: "0.1",
+ updates: [{
+ version: "0.2"
+ }]
+ });
+
+ equal(updates.length, 1);
+ let update = updates[0];
+
+ equal(update.targetApplications.length, 1);
+ let targetApp = update.targetApplications[0];
+
+ equal(targetApp.id, TOOLKIT_ID);
+ equal(targetApp.minVersion, TOOLKIT_MINVERSION);
+ equal(targetApp.maxVersion, "*");
+
+ equal(update.version, "0.2");
+ equal(update.multiprocessCompatible, true, "multiprocess_compatible flag");
+ equal(update.strictCompatibility, false, "inferred strictConpatibility flag");
+ equal(update.updateURL, null, "updateURL");
+ equal(update.updateHash, null, "updateHash");
+ equal(update.updateInfoURL, null, "updateInfoURL");
+
+ // If there's no applications property, we default to using one
+ // containing "gecko". If there is an applications property, but
+ // it doesn't contain "gecko", the update is skipped.
+ updates = yield checkUpdates({
+ id: "updatecheck-defaults@tests.mozilla.org",
+ version: "0.1",
+ updates: [{
+ version: "0.2",
+ applications: { "foo": {} }
+ }]
+ });
+
+ equal(updates.length, 0);
+
+ // Updates property is also optional. No updates, but also no error.
+ updates = yield checkUpdates({
+ id: "updatecheck-defaults@tests.mozilla.org",
+ version: "0.1",
+ });
+
+ equal(updates.length, 0);
+});
+
+
+add_task(function* test_explicit_values() {
+ // Checks that the appropriate explicit values are used when
+ // provided.
+
+ let updates = yield checkUpdates({
+ id: "updatecheck-explicit@tests.mozilla.org",
+ version: "0.1",
+ updates: [{
+ version: "0.2",
+ update_link: "https://example.com/foo.xpi",
+ update_hash: "sha256:0",
+ update_info_url: "https://example.com/update_info.html",
+ multiprocess_compatible: false,
+ applications: {
+ gecko: {
+ strict_min_version: "42.0a2.xpcshell",
+ strict_max_version: "43.xpcshell"
+ }
+ }
+ }]
+ });
+
+ equal(updates.length, 1);
+ let update = updates[0];
+
+ equal(update.targetApplications.length, 1);
+ let targetApp = update.targetApplications[0];
+
+ equal(targetApp.id, TOOLKIT_ID);
+ equal(targetApp.minVersion, "42.0a2.xpcshell");
+ equal(targetApp.maxVersion, "43.xpcshell");
+
+ equal(update.version, "0.2");
+ equal(update.multiprocessCompatible, false, "multiprocess_compatible flag");
+ equal(update.strictCompatibility, true, "inferred strictCompatibility flag");
+ equal(update.updateURL, "https://example.com/foo.xpi", "updateURL");
+ equal(update.updateHash, "sha256:0", "updateHash");
+ equal(update.updateInfoURL, "https://example.com/update_info.html", "updateInfoURL");
+});
+
+
+add_task(function* test_secure_hashes() {
+ // Checks that only secure hash functions are accepted for
+ // non-secure update URLs.
+
+ let hashFunctions = ["sha512",
+ "sha256",
+ "sha1",
+ "md5",
+ "md4",
+ "xxx"];
+
+ let updateItems = hashFunctions.map((hash, idx) => ({
+ version: `0.${idx}`,
+ update_link: `http://localhost:${gPort}/updates/${idx}-${hash}.xpi`,
+ update_hash: `${hash}:08ac852190ecd81f40a514ea9299fe9143d9ab5e296b97e73fb2a314de49648a`,
+ }));
+
+ let { messages, result: updates } = yield promiseConsoleOutput(() => {
+ return checkUpdates({
+ id: "updatecheck-hashes@tests.mozilla.org",
+ version: "0.1",
+ updates: updateItems
+ });
+ });
+
+ equal(updates.length, hashFunctions.length);
+
+ updates = updates.filter(update => update.updateHash || update.updateURL);
+ equal(updates.length, 2, "expected number of update hashes were accepted");
+
+ ok(updates[0].updateHash.startsWith("sha512:"), "sha512 hash is present");
+ ok(updates[0].updateURL);
+
+ ok(updates[1].updateHash.startsWith("sha256:"), "sha256 hash is present");
+ ok(updates[1].updateURL);
+
+ messages = messages.filter(msg => /Update link.*not secure.*strong enough hash \(needs to be sha256 or sha512\)/.test(msg.message));
+ equal(messages.length, hashFunctions.length - 2, "insecure hashes generated the expected warning");
+});
+
+
+add_task(function* test_strict_compat() {
+ // Checks that strict compatibility is enabled for strict max
+ // versions other than "*", but not for advisory max versions.
+ // Also, ensure that strict max versions take precedence over
+ // advisory versions.
+
+ let { messages, result: updates } = yield promiseConsoleOutput(() => {
+ return checkUpdates({
+ id: "updatecheck-strict@tests.mozilla.org",
+ version: "0.1",
+ updates: [
+ { version: "0.2",
+ applications: { gecko: { strict_max_version: "*" } } },
+ { version: "0.3",
+ applications: { gecko: { strict_max_version: "43" } } },
+ { version: "0.4",
+ applications: { gecko: { advisory_max_version: "43" } } },
+ { version: "0.5",
+ applications: { gecko: { advisory_max_version: "43",
+ strict_max_version: "44" } } },
+ ]
+ });
+ });
+
+ equal(updates.length, 4, "all update items accepted");
+
+ equal(updates[0].targetApplications[0].maxVersion, "*");
+ equal(updates[0].strictCompatibility, false);
+
+ equal(updates[1].targetApplications[0].maxVersion, "43");
+ equal(updates[1].strictCompatibility, true);
+
+ equal(updates[2].targetApplications[0].maxVersion, "43");
+ equal(updates[2].strictCompatibility, false);
+
+ equal(updates[3].targetApplications[0].maxVersion, "44");
+ equal(updates[3].strictCompatibility, true);
+
+ messages = messages.filter(msg => /Ignoring 'advisory_max_version'.*'strict_max_version' also present/.test(msg.message));
+ equal(messages.length, 1, "mix of advisory_max_version and strict_max_version generated the expected warning");
+});
+
+
+add_task(function* test_update_url_security() {
+ // Checks that update links to privileged URLs are not accepted.
+
+ let { messages, result: updates } = yield promiseConsoleOutput(() => {
+ return checkUpdates({
+ id: "updatecheck-security@tests.mozilla.org",
+ version: "0.1",
+ updates: [
+ { version: "0.2",
+ update_link: "chrome://browser/content/browser.xul",
+ update_hash: "sha256:08ac852190ecd81f40a514ea9299fe9143d9ab5e296b97e73fb2a314de49648a" },
+ { version: "0.3",
+ update_link: "http://example.com/update.xpi",
+ update_hash: "sha256:18ac852190ecd81f40a514ea9299fe9143d9ab5e296b97e73fb2a314de49648a" },
+ ]
+ });
+ });
+
+ equal(updates.length, 2, "both updates were processed");
+ equal(updates[0].updateURL, null, "privileged update URL was removed");
+ equal(updates[1].updateURL, "http://example.com/update.xpi", "safe update URL was accepted");
+
+ messages = messages.filter(msg => /http:\/\/localhost.*\/updates\/.*may not load or link to chrome:/.test(msg.message));
+ equal(messages.length, 1, "privileged upate URL generated the expected console message");
+});
+
+
+add_task(function* test_no_update_key() {
+ // Checks that updates fail when an update key has been specified.
+
+ let { messages } = yield promiseConsoleOutput(function* () {
+ yield Assert.rejects(
+ checkUpdates({
+ id: "updatecheck-updatekey@tests.mozilla.org",
+ version: "0.1",
+ updateKey: "ayzzx=",
+ updates: [
+ { version: "0.2" },
+ { version: "0.3" },
+ ]
+ }),
+ null, "updated expected to fail");
+ });
+
+ messages = messages.filter(msg => /Update keys are not supported for JSON update manifests/.test(msg.message));
+ equal(messages.length, 1, "got expected update-key-unsupported error");
+});
+
+
+add_task(function* test_type_detection() {
+ // Checks that JSON update manifests are detected correctly
+ // regardless of extension or MIME type.
+
+ let tests = [
+ { contentType: "application/json",
+ extension: "json",
+ valid: true },
+ { contentType: "application/json",
+ extension: "php",
+ valid: true },
+ { contentType: "text/plain",
+ extension: "json",
+ valid: true },
+ { contentType: "application/octet-stream",
+ extension: "json",
+ valid: true },
+ { contentType: "text/plain",
+ extension: "json?foo=bar",
+ valid: true },
+ { contentType: "text/plain",
+ extension: "php",
+ valid: true },
+ { contentType: "text/plain",
+ extension: "rdf",
+ valid: true },
+ { contentType: "application/json",
+ extension: "rdf",
+ valid: true },
+ { contentType: "text/xml",
+ extension: "json",
+ valid: true },
+ { contentType: "application/rdf+xml",
+ extension: "json",
+ valid: true },
+ ];
+
+ for (let [i, test] of tests.entries()) {
+ let { messages } = yield promiseConsoleOutput(function *() {
+ let id = `updatecheck-typedetection-${i}@tests.mozilla.org`;
+ let updates;
+ try {
+ updates = yield checkUpdates({
+ id: id,
+ version: "0.1",
+ contentType: test.contentType,
+ manifestExtension: test.extension,
+ updates: [{ version: "0.2" }]
+ });
+ } catch (e) {
+ ok(!test.valid, "update manifest correctly detected as RDF");
+ return;
+ }
+
+ ok(test.valid, "update manifest correctly detected as JSON");
+ equal(updates.length, 1, "correct number of updates");
+ equal(updates[0].id, id, "update is for correct extension");
+ });
+
+ if (test.valid) {
+ // Make sure we don't get any XML parsing errors from the
+ // XMLHttpRequest machinery.
+ ok(!messages.some(msg => /not well-formed/.test(msg.message)),
+ "expect XMLHttpRequest not to attempt XML parsing");
+ }
+
+ messages = messages.filter(msg => /Update manifest was not valid XML/.test(msg.message));
+ equal(messages.length, !test.valid, "expected number of XML parsing errors");
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_langpack.js b/toolkit/mozapps/webextensions/test/xpcshell/test_langpack.js
new file mode 100644
index 000000000..a97a14d4d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_langpack.js
@@ -0,0 +1,339 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that language packs can be used without restarts.
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+// Enable installing distribution add-ons
+Services.prefs.setBoolPref("extensions.installDistroAddons", true);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+const distroDir = gProfD.clone();
+distroDir.append("distribution");
+distroDir.append("extensions");
+registerDirectory("XREAppDist", distroDir.parent);
+
+var chrome = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
+ .getService(Components.interfaces.nsIXULChromeRegistry);
+
+function do_unregister_manifest() {
+ let path = getFileForAddon(profileDir, "langpack-x-testing@tests.mozilla.org");
+ Components.manager.removeBootstrappedManifestLocation(path);
+}
+
+function do_check_locale_not_registered(provider) {
+ let didThrow = false;
+ try {
+ chrome.getSelectedLocale(provider);
+ } catch (e) {
+ didThrow = true;
+ }
+ do_check_true(didThrow);
+}
+
+function run_test() {
+ do_test_pending();
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that installing doesn't require a restart
+function run_test_1() {
+ do_check_locale_not_registered("test-langpack");
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_langpack"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "locale");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Language Pack x-testing");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.hasResource("install.rdf"));
+ do_check_false(install.addon.hasResource("bootstrap.js"));
+ do_check_eq(install.addon.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_INSTALL, 0);
+
+ let addon = install.addon;
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ do_check_true(addon.hasResource("install.rdf"));
+ // spin to let the startup complete
+ do_execute_soon(check_test_1);
+ });
+ install.install();
+ });
+}
+
+function check_test_1() {
+ AddonManager.getAllInstalls(function(installs) {
+ // There should be no active installs now since the install completed and
+ // doesn't require a restart.
+ do_check_eq(installs.length, 0);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ let dir = do_get_addon_root_uri(profileDir, "langpack-x-testing@tests.mozilla.org");
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 0);
+
+ run_test_2();
+ });
+ });
+ });
+}
+
+// Tests that disabling doesn't require a restart
+function run_test_2() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_DISABLE, 0);
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_true(newb1.userDisabled);
+ do_check_false(newb1.isActive);
+
+ do_execute_soon(run_test_3);
+ });
+ });
+}
+
+// Test that restarting doesn't accidentally re-enable
+function run_test_3() {
+ shutdownManager();
+ startupManager(false);
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_true(b1.userDisabled);
+ do_check_false(b1.isActive);
+
+ run_test_4();
+ });
+}
+
+// Tests that enabling doesn't require a restart
+function run_test_4() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_ENABLE, 0);
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_neq(newb1, null);
+ do_check_eq(newb1.version, "1.0");
+ do_check_false(newb1.appDisabled);
+ do_check_false(newb1.userDisabled);
+ do_check_true(newb1.isActive);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that a restart shuts down and restarts the add-on
+function run_test_5() {
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, b1.id));
+
+ run_test_7();
+ });
+}
+
+// Tests that uninstalling doesn't require a restart
+function run_test_7() {
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ do_check_eq(b1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ b1.uninstall();
+
+ check_test_7();
+ });
+}
+
+function check_test_7() {
+ ensure_test_completed();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_eq(b1, null);
+
+ restartManager();
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(newb1) {
+ do_check_eq(newb1, null);
+
+ do_execute_soon(run_test_8);
+ });
+ }));
+}
+
+// Tests that a locale detected in the profile starts working immediately
+function run_test_8() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_langpack"), profileDir, "langpack-x-testing@tests.mozilla.org");
+
+ startupManager(false);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org",
+ callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", function(b2) {
+ prepare_test({
+ "langpack-x-testing@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ b2.uninstall();
+ ensure_test_completed();
+ do_execute_soon(run_test_9);
+ });
+ }));
+}
+
+// Tests that a locale from distribution/extensions gets installed and starts
+// working immediately
+function run_test_9() {
+ shutdownManager();
+ manuallyInstall(do_get_addon("test_langpack"), distroDir, "langpack-x-testing@tests.mozilla.org");
+ gAppInfo.version = "2.0";
+ startupManager(true);
+
+ AddonManager.getAddonByID("langpack-x-testing@tests.mozilla.org", callback_soon(function(b1) {
+ do_check_neq(b1, null);
+ do_check_eq(b1.version, "1.0");
+ do_check_false(b1.appDisabled);
+ do_check_false(b1.userDisabled);
+ do_check_true(b1.isActive);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+ do_check_true(b1.hasResource("install.rdf"));
+ do_check_false(b1.hasResource("bootstrap.js"));
+
+ shutdownManager();
+ do_unregister_manifest();
+ // check chrome reg that language pack is not registered
+ do_check_locale_not_registered("test-langpack");
+ startupManager(false);
+ // check chrome reg that language pack is registered
+ do_check_eq(chrome.getSelectedLocale("test-langpack"), "x-testing");
+
+ do_test_finished();
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_locale.js b/toolkit/mozapps/webextensions/test/xpcshell/test_locale.js
new file mode 100644
index 000000000..b4c7311e5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_locale.js
@@ -0,0 +1,149 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that localized properties work as expected
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+
+function run_test() {
+ do_test_pending();
+
+ // Setup for test
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ startupManager();
+
+ run_test_1();
+}
+
+// Tests that the localized properties are visible before installation
+function run_test_1() {
+ AddonManager.getInstallForFile(do_get_addon("test_locale"), function(install) {
+ do_check_eq(install.addon.name, "fr-FR Name");
+ do_check_eq(install.addon.description, "fr-FR Description");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(run_test_2));
+ install.install();
+ });
+}
+
+// Tests that the localized properties are visible after installation
+function run_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "fr-FR Name");
+ do_check_eq(addon.description, "fr-FR Description");
+
+ addon.userDisabled = true;
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that the localized properties are still there when disabled.
+function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr-FR Name");
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Localised preference values should be ignored when the add-on is disabled
+function run_test_4() {
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.name", "Name from prefs");
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.contributor.1", "Contributor 1");
+ Services.prefs.setCharPref("extensions.addon1@tests.mozilla.org.contributor.2", "Contributor 2");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ do_check_eq(addon.name, "fr-FR Name");
+ let contributors = addon.contributors;
+ do_check_eq(contributors.length, 3);
+ do_check_eq(contributors[0], "Fr Contributor 1");
+ do_check_eq(contributors[1], "Fr Contributor 2");
+ do_check_eq(contributors[2], "Fr Contributor 3");
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Test that changing locale works
+function run_test_5() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de-DE");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "de-DE Name");
+ do_check_eq(addon.description, null);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Test that missing locales use the fallbacks
+function run_test_6() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "nl-NL");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Fallback Name");
+ do_check_eq(addon.description, "Fallback Description");
+
+ addon.userDisabled = false;
+ do_execute_soon(run_test_7);
+ }));
+}
+
+// Test that the prefs will override the fallbacks
+function run_test_7() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Name from prefs");
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Test that the prefs will override localized values from the manifest
+function run_test_8() {
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+
+ do_check_eq(addon.name, "Name from prefs");
+ let contributors = addon.contributors;
+ do_check_eq(contributors.length, 2);
+ do_check_eq(contributors[0], "Contributor 1");
+ do_check_eq(contributors[1], "Contributor 2");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_locked.js b/toolkit/mozapps/webextensions/test/xpcshell/test_locked.js
new file mode 100644
index 000000000..86457eab1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_locked.js
@@ -0,0 +1,544 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_corrupt.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and stay enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Would stay incompatible with strict compat
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ // New profile so new add-ons are ignored
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ let [a2, a3, a4, a7, t2] =
+ yield promiseAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+ let deferredUpdateFinished = Promise.defer();
+ // Set up the initial state
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ // Let the updates finish before restarting the manager
+ deferredUpdateFinished.resolve();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+
+ yield deferredUpdateFinished.promise;
+});
+
+
+add_task(function* run_test_1() {
+ restartManager();
+ let [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ // Accessing the add-ons should open and recover the database
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ // The compatibility update won't be recovered and with strict
+ // compatibility it would not have been able to tell that it was
+ // previously userDisabled. However, without strict compat, it wasn't
+ // appDisabled, so it knows it must have been userDisabled.
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Restarting will actually apply changes to extensions.ini which will
+ // then be put into the in-memory database when we next fail to load the
+ // real thing
+ try {
+ shutdownManager();
+ } catch (e) {
+ // We're expecting an error here.
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // After allowing access to the original DB things should go back to as
+ // they were previously
+ try {
+ shutdownManager();
+ } catch (e) {
+ // We're expecting an error here.
+ }
+ do_print("Unlocking " + gExtensionsJSON.path);
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager();
+
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ try {
+ shutdownManager();
+ } catch (e) {
+ // We're expecting an error here.
+ }
+});
+
+
+function run_test() {
+ run_next_test();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_locked2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_locked2.js
new file mode 100644
index 000000000..4a62c585f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_locked2.js
@@ -0,0 +1,297 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we handle a locked database when there are extension changes
+// in progress
+
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+// Will be left alone
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be enabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be uninstalled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+
+// Will be updated
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function*() {
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+
+ // Make it look like add-on 5 was installed some time in the past so the update is
+ // detected
+ let path = getFileForAddon(profileDir, addon5.id).path;
+ yield promiseSetExtensionModifiedTime(path, Date.now() - (60000));
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+
+ let a1, a2, a3, a4, a5, a6;
+
+ [a2] = yield promiseAddonsByIDs(["addon2@tests.mozilla.org"]);
+ a2.userDisabled = true;
+
+ restartManager();
+
+ [a1, a2, a3, a4, a5] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"]);
+
+ a2.userDisabled = false;
+ a3.userDisabled = true;
+ a4.uninstall();
+
+ yield promiseInstallAllFiles([do_get_addon("test_locked2_5"),
+ do_get_addon("test_locked2_6")]);
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_ENABLE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_UNINSTALL);
+ do_check_true(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "1.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_UPGRADE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_eq(a4, null);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "2.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+
+ // After allowing access to the original DB things should still be
+ // back how they were before the lock
+ let shutdownError;
+ try {
+ shutdownManager();
+ } catch (e) {
+ shutdownError = e;
+ }
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager();
+
+ // On Unix, we can save the DB even when the original file wasn't
+ // readable, so our changes were saved. On Windows,
+ // these things happened when we had no access to the database so
+ // they are seen as external changes when we get the database back
+ if (shutdownError) {
+ do_print("Previous XPI save failed");
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED,
+ ["addon6@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED,
+ ["addon4@tests.mozilla.org"]);
+ }
+ else {
+ do_print("Previous XPI save succeeded");
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ }
+
+ [a1, a2, a3, a4, a5, a6] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_eq(a4, null);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.version, "2.0");
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a6.id));
+});
+
+function run_test() {
+ run_next_test();
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_locked_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_locked_strictcompat.js
new file mode 100644
index 000000000..9e17b4c8b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_locked_strictcompat.js
@@ -0,0 +1,567 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we rebuild something sensible from a corrupt database
+
+Components.utils.import("resource://testing-common/httpd.js");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_corrupt.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+// Will be enabled
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will be disabled
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Will get a compatibility update and be enabled
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Will get a compatibility update and be disabled
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ updateURL: "http://localhost:" + gPort + "/data/test_corrupt.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Stays incompatible
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Enabled bootstrapped
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// Disabled bootstrapped
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ bootstrap: "true",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The default theme
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+// The selected theme
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ internalName: "test/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+add_task(function* init() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Startup the profile and setup the initial state
+ startupManager();
+
+ // New profile so new add-ons are ignored
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ let a1, a2, a3, a4, a5, a6, a7, t1, t2;
+
+ [a2, a3, a4, a7, t2] =
+ yield promiseAddonsByIDs(["addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Set up the initial state
+ let deferredUpdateFinished = Promise.defer();
+
+ a2.userDisabled = true;
+ a4.userDisabled = true;
+ a7.userDisabled = true;
+ t2.userDisabled = false;
+ a3.findUpdates({
+ onUpdateFinished: function() {
+ a4.findUpdates({
+ onUpdateFinished: function() {
+ deferredUpdateFinished.resolve();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ yield deferredUpdateFinished.promise;
+});
+
+add_task(function* run_test_1() {
+ let a1, a2, a3, a4, a5, a6, a7, t1, t2;
+
+ restartManager();
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Open another handle on the JSON DB with as much Unix and Windows locking
+ // as we can to simulate some other process interfering with it
+ shutdownManager();
+ do_print("Locking " + gExtensionsJSON.path);
+ let options = {
+ winShare: 0
+ };
+ if (OS.Constants.libc.O_EXLOCK)
+ options.unixFlags = OS.Constants.libc.O_EXLOCK;
+
+ let file = yield OS.File.open(gExtensionsJSON.path, {read:true, write:true, existing:true}, options);
+
+ let filePermissions = gExtensionsJSON.permissions;
+ if (!OS.Constants.Win) {
+ gExtensionsJSON.permissions = 0;
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ // Accessing the add-ons should open and recover the database
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ // Should be correctly recovered
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ // Should be correctly recovered
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ // The compatibility update won't be recovered but it should still be
+ // active for this session
+ do_check_neq(a3, null);
+ do_check_true(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ // The compatibility update won't be recovered and it will not have been
+ // able to tell that it was previously userDisabled
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ // Should be correctly recovered
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ // Should be correctly recovered
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // Restarting will actually apply changes to extensions.ini which will
+ // then be put into the in-memory database when we next fail to load the
+ // real thing
+ try {
+ shutdownManager();
+ } catch (e) {
+ // We're expecting an error here.
+ }
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ // After allowing access to the original DB things should go back to as
+ // back how they were before the lock
+ let shutdownError;
+ try {
+ shutdownManager();
+ } catch (e) {
+ shutdownError = e;
+ }
+ do_print("Unlocking " + gExtensionsJSON.path);
+ yield file.close();
+ gExtensionsJSON.permissions = filePermissions;
+ startupManager(false);
+
+ // Shouldn't have seen any startup changes
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+
+ [a1, a2, a3, a4, a5, a6, a7, t1, t2] =
+ yield promiseAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"]);
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ // On Unix, we may be able to save our changes over the locked DB so we
+ // remember that this extension was changed to disabled. On Windows we
+ // couldn't replace the old DB so we read the older version of the DB
+ // where the extension is enabled
+ if (shutdownError) {
+ do_print("XPI save failed");
+ do_check_true(a3.isActive);
+ do_check_false(a3.appDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ }
+ else {
+ do_print("XPI save succeeded");
+ do_check_false(a3.isActive);
+ do_check_true(a3.appDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ }
+ do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a4, null);
+ do_check_false(a4.isActive);
+ // The reverse of the platform difference for a3 - Unix should
+ // stay the same as the last iteration because the save succeeded,
+ // Windows should still say userDisabled
+ if (OS.Constants.Win) {
+ do_check_true(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ }
+ else {
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ }
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a5, null);
+ do_check_false(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_true(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+
+ do_check_neq(t2, null);
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+
+ try {
+ shutdownManager();
+ } catch (e) {
+ // An error is expected here.
+ }
+});
+
+function run_test() {
+ run_next_test();
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_manifest.js b/toolkit/mozapps/webextensions/test/xpcshell/test_manifest.js
new file mode 100644
index 000000000..85279ba5d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_manifest.js
@@ -0,0 +1,562 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This tests that all properties are read from the install manifests and that
+// items are correctly enabled/disabled based on them (blocklist tests are
+// elsewhere)
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ const profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ optionsURL: "chrome://test/content/options.xul",
+ aboutURL: "chrome://test/content/about.xul",
+ iconURL: "chrome://test/skin/icon.png",
+ icon64URL: "chrome://test/skin/icon64.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ description: "Test Description",
+ creator: "Test Creator",
+ homepageURL: "http://www.example.com",
+ developer: [
+ "Test Developer 1",
+ "Test Developer 2"
+ ],
+ translator: [
+ "Test Translator 1",
+ "Test Translator 2"
+ ],
+ contributor: [
+ "Test Contributor 1",
+ "Test Contributor 2"
+ ]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "https://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 2"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 3"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://www.foo.com",
+ updateKey: "foo",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "*"
+ }],
+ name: "Test Addon 5"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1.1",
+ maxVersion: "*"
+ }],
+ name: "Test Addon 8"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.*"
+ }],
+ name: "Test Addon 9"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2.1",
+ maxVersion: "1.9.*"
+ }],
+ name: "Test Addon 10"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.2"
+ }],
+ name: "Test Addon 11"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon12@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.1.*"
+ }],
+ name: "Test Addon 12"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon13@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.*"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0.5"
+ }],
+ name: "Test Addon 13"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon14@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9",
+ maxVersion: "1.9.1"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 14"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon15@tests.mozilla.org",
+ version: "1.0",
+ updateKey: "foo",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 15"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon16@tests.mozilla.org",
+ version: "1.0",
+ updateKey: "foo",
+ updateURL: "https://www.foo.com",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 16"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon17@tests.mozilla.org",
+ version: "1.0",
+ optionsURL: "chrome://test/content/options.xul",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 17"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon18@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 18"
+ }, profileDir, undefined, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon19@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "99",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 19"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon20@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "1",
+ optionsURL: "chrome://test/content/options.xul",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 20"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon21@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "3",
+ optionsURL: "chrome://test/content/options.xul",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 21"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon22@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 22"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon23@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 23"
+ }, profileDir, undefined, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon24@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 24"
+ }, profileDir, undefined, "options.xul");
+
+ writeInstallRDFForExtension({
+ id: "addon25@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 25"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon26@tests.mozilla.org",
+ version: "1.0",
+ optionsType: "4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 26"
+ }, profileDir, undefined, "options.xul");
+
+ do_test_pending();
+ startupManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org",
+ "addon10@tests.mozilla.org",
+ "addon11@tests.mozilla.org",
+ "addon12@tests.mozilla.org",
+ "addon13@tests.mozilla.org",
+ "addon14@tests.mozilla.org",
+ "addon15@tests.mozilla.org",
+ "addon16@tests.mozilla.org",
+ "addon17@tests.mozilla.org",
+ "addon18@tests.mozilla.org",
+ "addon19@tests.mozilla.org",
+ "addon20@tests.mozilla.org",
+ "addon21@tests.mozilla.org",
+ "addon22@tests.mozilla.org",
+ "addon23@tests.mozilla.org",
+ "addon24@tests.mozilla.org",
+ "addon25@tests.mozilla.org",
+ "addon26@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+ a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,
+ a21, a22, a23, a24, a25, a26]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.type, "extension");
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a1.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
+ do_check_eq(a1.aboutURL, "chrome://test/content/about.xul");
+ do_check_eq(a1.iconURL, "chrome://test/skin/icon.png");
+ do_check_eq(a1.icon64URL, "chrome://test/skin/icon64.png");
+ do_check_eq(a1.icons[32], "chrome://test/skin/icon.png");
+ do_check_eq(a1.icons[64], "chrome://test/skin/icon64.png");
+ do_check_eq(a1.name, "Test Addon 1");
+ do_check_eq(a1.description, "Test Description");
+ do_check_eq(a1.creator, "Test Creator");
+ do_check_eq(a1.homepageURL, "http://www.example.com");
+ do_check_eq(a1.developers[0], "Test Developer 1");
+ do_check_eq(a1.developers[1], "Test Developer 2");
+ do_check_eq(a1.translators[0], "Test Translator 1");
+ do_check_eq(a1.translators[1], "Test Translator 2");
+ do_check_eq(a1.contributors[0], "Test Contributor 1");
+ do_check_eq(a1.contributors[1], "Test Contributor 2");
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isCompatible);
+ do_check_true(a1.providesUpdatesSecurely);
+ do_check_eq(a1.blocklistState, AM_Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_true(a2.isActive);
+ do_check_false(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.providesUpdatesSecurely);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.id, "addon3@tests.mozilla.org");
+ do_check_false(a3.isActive);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.providesUpdatesSecurely);
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.id, "addon4@tests.mozilla.org");
+ do_check_true(a4.isActive);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.providesUpdatesSecurely);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.isActive);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isCompatible);
+
+ do_check_neq(a6, null);
+ do_check_true(a6.isActive);
+ do_check_false(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_true(a6.isCompatible);
+
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_false(a7.userDisabled);
+ do_check_true(a7.appDisabled);
+ do_check_false(a7.isCompatible);
+
+ do_check_neq(a8, null);
+ do_check_false(a8.isActive);
+ do_check_false(a8.userDisabled);
+ do_check_true(a8.appDisabled);
+ do_check_false(a8.isCompatible);
+
+ do_check_neq(a9, null);
+ do_check_true(a9.isActive);
+ do_check_false(a9.userDisabled);
+ do_check_false(a9.appDisabled);
+ do_check_true(a9.isCompatible);
+
+ do_check_neq(a10, null);
+ do_check_false(a10.isActive);
+ do_check_false(a10.userDisabled);
+ do_check_true(a10.appDisabled);
+ do_check_false(a10.isCompatible);
+
+ do_check_neq(a11, null);
+ do_check_true(a11.isActive);
+ do_check_false(a11.userDisabled);
+ do_check_false(a11.appDisabled);
+ do_check_true(a11.isCompatible);
+
+ do_check_neq(a12, null);
+ do_check_false(a12.isActive);
+ do_check_false(a12.userDisabled);
+ do_check_true(a12.appDisabled);
+ do_check_false(a12.isCompatible);
+
+ do_check_neq(a13, null);
+ do_check_false(a13.isActive);
+ do_check_false(a13.userDisabled);
+ do_check_true(a13.appDisabled);
+ do_check_false(a13.isCompatible);
+
+ do_check_neq(a14, null);
+ do_check_true(a14.isActive);
+ do_check_false(a14.userDisabled);
+ do_check_false(a14.appDisabled);
+ do_check_true(a14.isCompatible);
+
+ do_check_neq(a15, null);
+ do_check_true(a15.isActive);
+ do_check_false(a15.userDisabled);
+ do_check_false(a15.appDisabled);
+ do_check_true(a15.isCompatible);
+ do_check_true(a15.providesUpdatesSecurely);
+
+ do_check_neq(a16, null);
+ do_check_true(a16.isActive);
+ do_check_false(a16.userDisabled);
+ do_check_false(a16.appDisabled);
+ do_check_true(a16.isCompatible);
+ do_check_true(a16.providesUpdatesSecurely);
+
+ do_check_neq(a17, null);
+ do_check_true(a17.isActive);
+ do_check_false(a17.userDisabled);
+ do_check_false(a17.appDisabled);
+ do_check_true(a17.isCompatible);
+ do_check_eq(a17.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a17.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+ do_check_neq(a18, null);
+ do_check_true(a18.isActive);
+ do_check_false(a18.userDisabled);
+ do_check_false(a18.appDisabled);
+ do_check_true(a18.isCompatible);
+ if (Services.prefs.getBoolPref("extensions.alwaysUnpack")) {
+ do_check_eq(a18.optionsURL, Services.io.newFileURI(profileDir).spec +
+ "addon18@tests.mozilla.org/options.xul");
+ } else {
+ do_check_eq(a18.optionsURL, "jar:" + Services.io.newFileURI(profileDir).spec +
+ "addon18@tests.mozilla.org.xpi!/options.xul");
+ }
+ do_check_eq(a18.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+
+ do_check_eq(a19, null);
+
+ do_check_neq(a20, null);
+ do_check_true(a20.isActive);
+ do_check_false(a20.userDisabled);
+ do_check_false(a20.appDisabled);
+ do_check_true(a20.isCompatible);
+ do_check_eq(a20.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a20.optionsType, AddonManager.OPTIONS_TYPE_DIALOG);
+
+ do_check_neq(a21, null);
+ do_check_true(a21.isActive);
+ do_check_false(a21.userDisabled);
+ do_check_false(a21.appDisabled);
+ do_check_true(a21.isCompatible);
+ do_check_eq(a21.optionsURL, "chrome://test/content/options.xul");
+ do_check_eq(a21.optionsType, AddonManager.OPTIONS_TYPE_TAB);
+
+ do_check_neq(a22, null);
+ do_check_eq(a22.optionsType, null);
+ do_check_eq(a22.optionsURL, null);
+
+ do_check_neq(a23, null);
+ do_check_eq(a23.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+ do_check_neq(a23.optionsURL, null);
+
+ do_check_neq(a24, null);
+ do_check_eq(a24.optionsType, AddonManager.OPTIONS_TYPE_INLINE);
+ do_check_neq(a24.optionsURL, null);
+
+ do_check_neq(a25, null);
+ do_check_eq(a25.optionsType, null);
+ do_check_eq(a25.optionsURL, null);
+
+ do_check_neq(a26, null);
+ do_check_eq(a26.optionsType, AddonManager.OPTIONS_TYPE_INLINE_INFO);
+ do_check_neq(a26.optionsURL, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_mapURIToAddonID.js b/toolkit/mozapps/webextensions/test/xpcshell/test_mapURIToAddonID.js
new file mode 100644
index 000000000..1dd05064e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_mapURIToAddonID.js
@@ -0,0 +1,347 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons URIs can be mapped to add-on IDs
+//
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// Enable loading extensions from the user scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const userExtDir = gProfD.clone();
+userExtDir.append("extensions2");
+userExtDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userExtDir.parent);
+
+BootstrapMonitor.init();
+
+function TestProvider(result) {
+ this.result = result;
+}
+TestProvider.prototype = {
+ uri: Services.io.newURI("hellow://world", null, null),
+ id: "valid@id",
+ startup: function() {},
+ shutdown: function() {},
+ mapURIToAddonID: function(aURI) {
+ if (aURI.spec === this.uri.spec) {
+ return this.id;
+ }
+ throw Components.Exception("Not mapped", this.result);
+ }
+};
+
+function TestProviderNoMap() {}
+TestProviderNoMap.prototype = {
+ startup: function() {},
+ shutdown: function() {}
+};
+
+function check_mapping(uri, id) {
+ do_check_eq(AddonManager.mapURIToAddonID(uri), id);
+ let svc = Components.classes["@mozilla.org/addons/integration;1"].
+ getService(Components.interfaces.amIAddonManager);
+ let val = {};
+ do_check_true(svc.mapURIToAddonID(uri, val));
+ do_check_eq(val.value, id);
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function run_test() {
+ do_test_pending();
+
+ run_test_early();
+}
+
+function run_test_early() {
+ startupManager();
+
+ installAllFiles([do_get_addon("test_chromemanifest_1")], function() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
+ let uri = addon.getResourceURI(".");
+ let id = addon.id;
+ check_mapping(uri, id);
+
+ shutdownManager();
+
+ // Force an early call, to check that mappings will get correctly
+ // initialized when the manager actually starts up.
+ // See bug 957089
+
+ // First force-initialize the XPIProvider.
+ let s = Components.utils.import(
+ "resource://gre/modules/addons/XPIProvider.jsm", {});
+
+ // Make the early API call.
+ // AddonManager still misses its provider and so doesn't work yet.
+ do_check_null(AddonManager.mapURIToAddonID(uri));
+ // But calling XPIProvider directly works immediately
+ do_check_eq(s.XPIProvider.mapURIToAddonID(uri), id);
+
+ // Actually start up the manager.
+ startupManager(false);
+
+ // Check that the mapping is there now.
+ check_mapping(uri, id);
+ do_check_eq(s.XPIProvider.mapURIToAddonID(uri), id);
+
+ run_test_nomapping();
+ });
+ });
+}
+
+function run_test_nomapping() {
+ do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
+ try {
+ let svc = Components.classes["@mozilla.org/addons/integration;1"].
+ getService(Components.interfaces.amIAddonManager);
+ let val = {};
+ do_check_false(svc.mapURIToAddonID(TestProvider.prototype.uri, val));
+ }
+ catch (ex) {
+ do_throw(ex);
+ }
+
+ run_test_1();
+}
+
+
+// Tests that add-on URIs are mappable after an install
+function run_test_1() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), function(install) {
+ ensure_test_completed();
+
+ let addon = install.addon;
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], function() {
+ let uri = addon.getResourceURI(".");
+ check_mapping(uri, addon.id);
+
+ BootstrapMonitor.promiseAddonStartup("bootstrap1@tests.mozilla.org").then(function() {
+ run_test_2(uri);
+ });
+ });
+ install.install();
+ });
+}
+
+// Tests that add-on URIs are still mappable, even after the add-on gets
+// disabled in-session.
+function run_test_2(uri) {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ b1.userDisabled = true;
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ do_check_true(newb1.userDisabled);
+ check_mapping(uri, newb1.id);
+
+ do_execute_soon(() => run_test_3(uri));
+ });
+ });
+}
+
+// Tests that add-on URIs are mappable if the add-on was never started in a
+// session
+function run_test_3(uri) {
+ restartManager();
+
+ check_mapping(uri, "bootstrap1@tests.mozilla.org");
+
+ run_test_4();
+}
+
+// Tests that add-on URIs are mappable after a restart + reenable
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ b1.userDisabled = false;
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(newb1) {
+ let uri = newb1.getResourceURI(".");
+ check_mapping(uri, newb1.id);
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Tests that add-on URIs are mappable after a restart
+function run_test_5() {
+ restartManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ let uri = b1.getResourceURI(".");
+ check_mapping(uri, b1.id);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Tests that add-on URIs are mappable after being uninstalled
+function run_test_6() {
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ prepare_test({
+ "bootstrap1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ let uri = b1.getResourceURI(".");
+ b1.uninstall();
+ ensure_test_completed();
+
+ check_mapping(uri, b1.id);
+
+ restartManager();
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Tests that add-on URIs are mappable for add-ons detected at startup
+function run_test_7() {
+ shutdownManager();
+
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, "bootstrap1@tests.mozilla.org");
+
+ startupManager();
+
+ AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
+ let uri = b1.getResourceURI(".");
+ check_mapping(uri, b1.id);
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Tests that temporary addon-on URIs are mappable after install and uninstall
+function run_test_8() {
+ prepare_test({
+ "bootstrap2@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall",
+ ], function(b2) {
+ let uri = b2.getResourceURI(".");
+ check_mapping(uri, b2.id);
+
+ prepare_test({
+ "bootstrap2@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ b2.uninstall();
+ ensure_test_completed();
+
+ check_mapping(uri, b2.id);
+
+ do_execute_soon(run_test_invalidarg);
+ });
+ AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap2_1"));
+}
+
+// Tests that the AddonManager will bail when mapURIToAddonID is called with an
+// invalid argument
+function run_test_invalidarg() {
+ restartManager();
+
+ let tests = [undefined,
+ null,
+ 1,
+ "string",
+ "chrome://global/content/",
+ function() {}
+ ];
+ for (var test of tests) {
+ try {
+ AddonManager.mapURIToAddonID(test);
+ throw new Error("Shouldn't be able to map the URI in question");
+ }
+ catch (ex) {
+ if (ex.result) {
+ do_check_eq(ex.result, Components.results.NS_ERROR_INVALID_ARG);
+ } else {
+ do_throw(ex);
+ }
+ }
+ }
+
+ run_test_provider();
+}
+
+// Tests that custom providers are correctly handled
+function run_test_provider() {
+ restartManager();
+
+ const provider = new TestProvider(Components.results.NS_ERROR_NOT_AVAILABLE);
+ AddonManagerPrivate.registerProvider(provider);
+
+ check_mapping(provider.uri, provider.id);
+
+ let u2 = provider.uri.clone();
+ u2.path = "notmapped";
+ do_check_eq(AddonManager.mapURIToAddonID(u2), null);
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ run_test_provider_nomap();
+}
+
+// Tests that custom providers are correctly handled, even not implementing
+// mapURIToAddonID
+function run_test_provider_nomap() {
+ restartManager();
+
+ const provider = new TestProviderNoMap();
+ AddonManagerPrivate.registerProvider(provider);
+
+ do_check_eq(AddonManager.mapURIToAddonID(TestProvider.prototype.uri), null);
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ do_test_finished();
+}
+
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_metadata_update.js b/toolkit/mozapps/webextensions/test/xpcshell/test_metadata_update.js
new file mode 100644
index 000000000..dbf5db485
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_metadata_update.js
@@ -0,0 +1,159 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+/*
+ * Test whether we need to block start-up to check add-on compatibility,
+ * based on how long since the last succesful metadata ping.
+ * All tests depend on having one add-on installed in the profile
+ */
+
+
+const URI_EXTENSION_UPDATE_DIALOG = "chrome://mozapps/content/extensions/update.xul";
+const PREF_EM_SHOW_MISMATCH_UI = "extensions.showMismatchUI";
+
+// Constants copied from AddonRepository.jsm
+const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
+const PREF_METADATA_UPDATETHRESHOLD_SEC = "extensions.getAddons.cache.updateThreshold";
+const DEFAULT_METADATA_UPDATETHRESHOLD_SEC = 172800; // two days
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+// None of this works without the add-on repository cache
+Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var Cu = Components.utils;
+var Cr = Components.results;
+
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+var testserver;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// This will be called to show the compatibility update dialog.
+var WindowWatcher = {
+ expected: false,
+
+ openWindow: function(parent, url, name, features, args) {
+ do_check_true(Services.startup.interrupted);
+ do_check_eq(url, URI_EXTENSION_UPDATE_DIALOG);
+ do_check_true(this.expected);
+ this.expected = false;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+// Return Date.now() in seconds, rounded
+function now() {
+ return Math.round(Date.now() / 1000);
+}
+
+// First time with a new profile, so we don't have a cache.lastUpdate pref
+add_task(function* checkFirstMetadata() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_EM_SHOW_MISMATCH_UI, true);
+
+ // Create and configure the HTTP server.
+ testserver = createHttpServer();
+ testserver.registerDirectory("/data/", do_get_file("data"));
+ testserver.registerDirectory("/addons/", do_get_file("addons"));
+ gPort = testserver.identity.primaryPort;
+ const BASE_URL = "http://localhost:" + gPort;
+ const GETADDONS_RESULTS = BASE_URL + "/data/test_AddonRepository_cache.xml";
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, GETADDONS_RESULTS);
+
+ // Put an add-on in our profile so the metadata check will have something to do
+ var min1max2 = {
+ id: "min1max2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test addon compatible with v1->v2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ };
+ writeInstallRDFForExtension(min1max2, profileDir);
+
+ startupManager();
+
+ // Make sure that updating metadata for the first time sets the lastUpdate preference
+ yield AddonRepository.repopulateCache();
+ do_print("Update done, getting last update");
+ let lastUpdate = Services.prefs.getIntPref(PREF_METADATA_LASTUPDATE);
+ do_check_true(lastUpdate > 0);
+
+ // Make sure updating metadata again updates the preference
+ let oldUpdate = lastUpdate - 2 * DEFAULT_METADATA_UPDATETHRESHOLD_SEC;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldUpdate);
+ yield AddonRepository.repopulateCache();
+ do_check_neq(oldUpdate, Services.prefs.getIntPref(PREF_METADATA_LASTUPDATE));
+});
+
+// First upgrade with no lastUpdate pref and no add-ons changing shows UI
+add_task(function* upgrade_no_lastupdate() {
+ Services.prefs.clearUserPref(PREF_METADATA_LASTUPDATE);
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate more than default threshold and no add-ons changing shows UI
+add_task(function* upgrade_old_lastupdate() {
+ let oldEnough = now() - DEFAULT_METADATA_UPDATETHRESHOLD_SEC - 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldEnough);
+
+ WindowWatcher.expected = true;
+ // upgrade, downgrade, it has the same effect on the code path under test
+ yield promiseRestartManager("1");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate less than default threshold and no add-ons changing doesn't show
+add_task(function* upgrade_young_lastupdate() {
+ let notOldEnough = now() - DEFAULT_METADATA_UPDATETHRESHOLD_SEC + 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, notOldEnough);
+
+ WindowWatcher.expected = false;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Repeat more-than and less-than but with updateThreshold preference set
+// Upgrade with lastUpdate more than pref threshold and no add-ons changing shows UI
+const TEST_UPDATETHRESHOLD_SEC = 50000;
+add_task(function* upgrade_old_pref_lastupdate() {
+ Services.prefs.setIntPref(PREF_METADATA_UPDATETHRESHOLD_SEC, TEST_UPDATETHRESHOLD_SEC);
+
+ let oldEnough = now() - TEST_UPDATETHRESHOLD_SEC - 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, oldEnough);
+
+ WindowWatcher.expected = true;
+ yield promiseRestartManager("1");
+ do_check_false(WindowWatcher.expected);
+});
+
+// Upgrade with lastUpdate less than pref threshold and no add-ons changing doesn't show
+add_task(function* upgrade_young_pref_lastupdate() {
+ let notOldEnough = now() - TEST_UPDATETHRESHOLD_SEC + 1000;
+ Services.prefs.setIntPref(PREF_METADATA_LASTUPDATE, notOldEnough);
+
+ WindowWatcher.expected = false;
+ yield promiseRestartManager("2");
+ do_check_false(WindowWatcher.expected);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate1.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate1.js
new file mode 100644
index 000000000..8c13593b9
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate1.js
@@ -0,0 +1,231 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from the old rdf style database
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ type: 4,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ let stagedXPIs = profileDir.clone();
+ stagedXPIs.append("staged-xpis");
+ stagedXPIs.append("addon6@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ let addon6 = do_get_addon("test_migrate6");
+ addon6.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon7@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ let addon7 = do_get_addon("test_migrate7");
+ addon7.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon8@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+ let addon8 = do_get_addon("test_migrate8");
+ addon8.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ let old = do_get_file("data/test_migrate.rdf");
+ old.copyTo(gProfD, "extensions.rdf");
+
+ let oldCache = gProfD.clone();
+ oldCache.append("extensions.cache");
+ oldCache.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
+
+ // Theme state is determined by the selected theme pref
+ Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
+
+ Services.prefs.setCharPref("extensions.lastAppVersion", "1");
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ do_check_false(oldCache.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, a8, t1,
+ t2]) {
+ // addon1 was user and app enabled in the old extensions.rdf
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(a1.hasBinaryComponents);
+ do_check_true(a1.seen);
+
+ // addon2 was user disabled and app enabled in the old extensions.rdf
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(a2.hasBinaryComponents);
+ do_check_true(a2.seen);
+
+ // addon3 was pending user disable and app disabled in the old extensions.rdf
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_false(a3.hasBinaryComponents);
+ do_check_true(a3.seen);
+
+ // addon4 was pending user enable and app disabled in the old extensions.rdf
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_false(a4.hasBinaryComponents);
+ do_check_true(a4.seen);
+
+ // addon5 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a5, null);
+ do_check_true(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+ do_check_false(a5.hasBinaryComponents);
+ do_check_true(a5.seen);
+
+ // addon6, addon7 and addon8 will have been lost as they were staged in the
+ // pre-Firefox 4.0 directory
+ do_check_eq(a6, null);
+ do_check_eq(a7, null);
+ do_check_eq(a8, null);
+
+ // Theme 1 was previously enabled
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(t1.seen);
+
+ // Theme 2 was previously disabled
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(t2.seen);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate2.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate2.js
new file mode 100644
index 000000000..cc7336713
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate2.js
@@ -0,0 +1,267 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from SQLITE databases
+// Note that since the database doesn't contain the foreignInstall field we
+// should just assume that no add-ons in the user profile were foreignInstalls
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 7",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon8 = {
+ id: "addon8@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 8",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const globalDir = gProfD.clone();
+globalDir.append("extensions2");
+globalDir.append(gAppInfo.ID);
+registerDirectory("XRESysSExtPD", globalDir.parent);
+const userDir = gProfD.clone();
+userDir.append("extensions3");
+userDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userDir.parent);
+
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, globalDir);
+ writeInstallRDFForExtension(addon8, userDir);
+
+ // Write out a minimal database
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "userDisabled INTEGER, installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :userDisabled, :installDate)");
+
+ let internal_ids = {};
+
+ [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0", "0"],
+ ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "1", "0"],
+ ["addon3@tests.mozilla.org", "app-profile", "2.0", "1", "1", "0"],
+ ["addon4@tests.mozilla.org", "app-profile", "2.0", "0", "0", "0"],
+ ["addon5@tests.mozilla.org", "app-profile", "2.0", "1", "0", "0"],
+ ["addon6@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"],
+ ["addon7@tests.mozilla.org", "app-system-share", "1.0", "1", "0", "0"],
+ ["addon8@tests.mozilla.org", "app-system-user", "1.0", "1", "0", "0"]].forEach(function(a) {
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.userDisabled = a[4];
+ stmt.params.installDate = a[5];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ });
+ stmt.finalize();
+
+ // Add updated target application into for addon5
+ stmt = db.createStatement("INSERT INTO targetApplication VALUES " +
+ "(:internal_id, :id, :minVersion, :maxVersion)");
+ stmt.params.internal_id = internal_ids["addon5@tests.mozilla.org"];
+ stmt.params.id = "xpcshell@tests.mozilla.org";
+ stmt.params.minVersion = "0";
+ stmt.params.maxVersion = "1";
+ stmt.execute();
+
+ // Add updated target application into for addon6
+ stmt.params.internal_id = internal_ids["addon6@tests.mozilla.org"];
+ stmt.params.id = "xpcshell@tests.mozilla.org";
+ stmt.params.minVersion = "0";
+ stmt.params.maxVersion = "1";
+ stmt.execute();
+ stmt.finalize();
+
+ db.schemaVersion = 10000;
+ Services.prefs.setIntPref("extensions.databaseSchema", 14);
+ db.close();
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8]) {
+ // addon1 was enabled in the database
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_false(a1.foreignInstall);
+ do_check_true(a1.seen);
+ // addon2 was disabled in the database
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(a2.strictCompatibility);
+ do_check_false(a2.foreignInstall);
+ do_check_true(a2.seen);
+ // addon3 was pending-disable in the database
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(a3.strictCompatibility);
+ do_check_false(a3.foreignInstall);
+ do_check_true(a3.seen);
+ // addon4 was pending-enable in the database
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.isActive);
+ do_check_true(a4.strictCompatibility);
+ do_check_false(a4.foreignInstall);
+ do_check_true(a4.seen);
+ // addon5 was enabled in the database but needed a compatibility update
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_false(a5.strictCompatibility);
+ do_check_false(a5.foreignInstall);
+ do_check_true(a5.seen);
+ // addon6 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a6, null);
+ do_check_true(a6.userDisabled);
+ do_check_true(a6.appDisabled);
+ do_check_false(a6.isActive);
+ do_check_false(a6.strictCompatibility);
+ do_check_false(a6.foreignInstall);
+ do_check_true(a6.seen);
+ // addon7 is in the global install location so should be a foreignInstall
+ do_check_neq(a7, null);
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_false(a7.strictCompatibility);
+ do_check_true(a7.foreignInstall);
+ do_check_true(a7.seen);
+ // addon8 is in the user install location so should be a foreignInstall
+ do_check_neq(a8, null);
+ do_check_false(a8.userDisabled);
+ do_check_false(a8.appDisabled);
+ do_check_true(a8.isActive);
+ do_check_false(a8.strictCompatibility);
+ do_check_true(a8.foreignInstall);
+ do_check_true(a8.seen);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate3.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate3.js
new file mode 100644
index 000000000..71bd66603
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate3.js
@@ -0,0 +1,229 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from the old extensions.rdf database. This
+// matches test_migrate1.js however it runs with a lightweight theme selected
+// so the themes should appear disabled.
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 1",
+ type: 4,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var theme2 = {
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Theme 2",
+ type: 4,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+ writeInstallRDFForExtension(theme2, profileDir);
+
+ // Cannot use the LightweightThemeManager before AddonManager has been started
+ // so inject the correct prefs
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify([{
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ }]));
+ Services.prefs.setCharPref("lightweightThemes.selectedThemeID", "1");
+
+ let stagedXPIs = profileDir.clone();
+ stagedXPIs.append("staged-xpis");
+ stagedXPIs.append("addon6@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ let addon6 = do_get_addon("test_migrate6");
+ addon6.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ stagedXPIs.append("addon7@tests.mozilla.org");
+ stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ let addon7 = do_get_addon("test_migrate7");
+ addon7.copyTo(stagedXPIs, "tmp.xpi");
+ stagedXPIs = stagedXPIs.parent;
+
+ let old = do_get_file("data/test_migrate.rdf");
+ old.copyTo(gProfD, "extensions.rdf");
+
+ // Theme state is determined by the selected theme pref
+ Services.prefs.setCharPref("general.skins.selectedSkin", "theme1/1.0");
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([a1, a2, a3,
+ a4, a5, a6,
+ a7, t1, t2]) {
+ // addon1 was user and app enabled in the old extensions.rdf
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(a1.seen);
+
+ // addon2 was user disabled and app enabled in the old extensions.rdf
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(a2.seen);
+
+ // addon3 was pending user disable and app disabled in the old extensions.rdf
+ do_check_neq(a3, null);
+ do_check_true(a3.userDisabled);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(a3.seen);
+
+ // addon4 was pending user enable and app disabled in the old extensions.rdf
+ do_check_neq(a4, null);
+ do_check_false(a4.userDisabled);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_true(a4.seen);
+
+ // addon5 was disabled and compatible but a new version has been installed
+ // since, it should still be disabled but should be incompatible
+ do_check_neq(a5, null);
+ do_check_true(a5.userDisabled);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+ do_check_true(a5.seen);
+
+ // addon6 and addon7 will have been lost as they were staged in the
+ // pre-Firefox 4.0 directory
+ do_check_eq(a6, null);
+ do_check_eq(a7, null);
+
+ // Theme 1 was previously disabled
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(t1.seen);
+
+ // Theme 2 was previously disabled
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(t2.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(t2.seen);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate4.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate4.js
new file mode 100644
index 000000000..fad015886
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate4.js
@@ -0,0 +1,321 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we migrate data from a previous version of the JSON database
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_migrate4.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ updateURL: "http://localhost:" + gPort + "/data/test_migrate4.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }]
+};
+
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ updateURL: "http://localhost:" + gPort + "/data/test_migrate4.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "1"
+ }]
+};
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var oldSyncGUIDs = {};
+
+function prepare_profile() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+
+ startupManager();
+ installAllFiles([do_get_addon("test_migrate8"), do_get_addon("test_migrate9")],
+ function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon9@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a9]) {
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+ a2.userDisabled = true;
+ a2.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ a3.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
+ a4.userDisabled = true;
+ a6.userDisabled = true;
+ a9.userDisabled = false;
+
+ for (let addon of [a1, a2, a3, a4, a5, a6]) {
+ oldSyncGUIDs[addon.id] = addon.syncGUID;
+ }
+
+ a6.findUpdates({
+ onUpdateAvailable: function(aAddon, aInstall6) {
+ AddonManager.getInstallForURL("http://localhost:" + gPort + "/addons/test_migrate4_7.xpi", function(aInstall7) {
+ completeAllInstalls([aInstall6, aInstall7], function() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1_2, a2_2, a3_2, a4_2, a5_2, a6_2]) {
+ a3_2.userDisabled = true;
+ a4_2.userDisabled = false;
+
+ a5_2.findUpdates({
+ onUpdateFinished: function() {
+ do_execute_soon(perform_migration);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+ }, "application/x-xpinstall");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
+
+function perform_migration() {
+ shutdownManager();
+
+ // Turn on disabling for all scopes
+ Services.prefs.setIntPref("extensions.autoDisableScopes", 15);
+
+ changeXPIDBVersion(1, data => {
+ // Delete the seen property from all add-ons to make sure it defaults to true
+ for (let addon of data.addons) {
+ delete addon.seen;
+ }
+ });
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ gAppInfo.version = "2"
+ startupManager(true);
+ test_results();
+}
+
+function test_results() {
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org",
+ "addon8@tests.mozilla.org",
+ "addon9@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7, a8, a9]) {
+ // addon1 was enabled
+ do_check_neq(a1, null);
+ do_check_eq(a1.syncGUID, oldSyncGUIDs[a1.id]);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a1.foreignInstall);
+ do_check_true(a1.seen);
+ do_check_false(a1.hasBinaryComponents);
+ do_check_false(a1.strictCompatibility);
+
+ // addon2 was disabled
+ do_check_neq(a2, null);
+ do_check_eq(a2.syncGUID, oldSyncGUIDs[a2.id]);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ do_check_eq(a2.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_true(a2.foreignInstall);
+ do_check_true(a2.seen);
+ do_check_false(a2.hasBinaryComponents);
+ do_check_false(a2.strictCompatibility);
+
+ // addon3 was pending-disable in the database
+ do_check_neq(a3, null);
+ do_check_eq(a3.syncGUID, oldSyncGUIDs[a3.id]);
+ do_check_true(a3.userDisabled);
+ do_check_false(a3.appDisabled);
+ do_check_false(a3.isActive);
+ do_check_eq(a3.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
+ do_check_true(a3.foreignInstall);
+ do_check_true(a3.seen);
+ do_check_false(a3.hasBinaryComponents);
+ do_check_false(a3.strictCompatibility);
+
+ // addon4 was pending-enable in the database
+ do_check_neq(a4, null);
+ do_check_eq(a4.syncGUID, oldSyncGUIDs[a4.id]);
+ do_check_false(a4.userDisabled);
+ do_check_false(a4.appDisabled);
+ do_check_true(a4.isActive);
+ do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a4.foreignInstall);
+ do_check_true(a4.seen);
+ do_check_false(a4.hasBinaryComponents);
+ do_check_true(a4.strictCompatibility);
+
+ // addon5 was enabled in the database but needed a compatibility update
+ do_check_neq(a5, null);
+ do_check_false(a5.userDisabled);
+ do_check_false(a5.appDisabled);
+ do_check_true(a5.isActive);
+ do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a5.foreignInstall);
+ do_check_true(a5.seen);
+ do_check_false(a5.hasBinaryComponents);
+ do_check_false(a5.strictCompatibility);
+
+ // addon6 was disabled and compatible but a new version has been installed
+ do_check_neq(a6, null);
+ do_check_eq(a6.syncGUID, oldSyncGUIDs[a6.id]);
+ do_check_eq(a6.version, "2.0");
+ do_check_true(a6.userDisabled);
+ do_check_false(a6.appDisabled);
+ do_check_false(a6.isActive);
+ do_check_eq(a6.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_true(a6.foreignInstall);
+ do_check_true(a6.seen);
+ do_check_eq(a6.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_6.xpi");
+ do_check_eq(a6.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ do_check_false(a6.hasBinaryComponents);
+ do_check_false(a6.strictCompatibility);
+
+ // addon7 was installed manually
+ do_check_neq(a7, null);
+ do_check_eq(a7.version, "1.0");
+ do_check_false(a7.userDisabled);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isActive);
+ do_check_eq(a7.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_false(a7.foreignInstall);
+ do_check_true(a7.seen);
+ do_check_eq(a7.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_7.xpi");
+ do_check_eq(a7.releaseNotesURI, null);
+ do_check_false(a7.hasBinaryComponents);
+ do_check_false(a7.strictCompatibility);
+
+ // addon8 was enabled and has binary components
+ do_check_neq(a8, null);
+ do_check_false(a8.userDisabled);
+ do_check_false(a8.appDisabled);
+ do_check_true(a8.isActive);
+ do_check_false(a8.foreignInstall);
+ do_check_true(a8.seen);
+ do_check_true(a8.hasBinaryComponents);
+ do_check_false(a8.strictCompatibility);
+
+ // addon9 is the active theme
+ do_check_neq(a9, null);
+ do_check_false(a9.userDisabled);
+ do_check_false(a9.appDisabled);
+ do_check_true(a9.isActive);
+ do_check_false(a9.foreignInstall);
+ do_check_true(a9.seen);
+ do_check_false(a9.hasBinaryComponents);
+ do_check_true(a9.strictCompatibility);
+
+ testserver.stop(do_test_finished);
+ });
+}
+
+function run_test() {
+ do_test_pending();
+
+ prepare_profile();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate5.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate5.js
new file mode 100644
index 000000000..885cd5a7c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate5.js
@@ -0,0 +1,139 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we fail to migrate but still start up ok when there is a SQLITE database
+// with no useful data in it.
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0",
+ maxVersion: "0"
+ }]
+};
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "2.0",
+ name: "Default theme",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "2.0",
+ name: "Test theme",
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+
+ // Write out a broken database (no userDisabled field)
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :installDate)");
+
+ let internal_ids = {};
+
+ [["addon1@tests.mozilla.org", "app-profile", "1.0", "1", "0"],
+ ["addon2@tests.mozilla.org", "app-profile", "2.0", "0", "0"],
+ ["default@tests.mozilla.org", "app-profile", "2.0", "1", "0"],
+ ["theme1@tests.mozilla.org", "app-profile", "2.0", "0", "0"]].forEach(function(a) {
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.installDate = a[4];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ });
+ stmt.finalize();
+
+ db.schemaVersion = 100;
+ Services.prefs.setIntPref("extensions.databaseSchema", 100);
+ db.close();
+
+ startupManager();
+ check_startup_changes("installed", []);
+ check_startup_changes("updated", []);
+ check_startup_changes("uninstalled", []);
+ check_startup_changes("disabled", []);
+ check_startup_changes("enabled", []);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ function([a1, a2, d, t1]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.userDisabled);
+ do_check_true(a2.appDisabled);
+ do_check_false(a2.isActive);
+
+ // Should have enabled the selected theme
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_true(t1.isActive);
+
+ do_check_neq(d, null);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrateAddonRepository.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrateAddonRepository.js
new file mode 100644
index 000000000..d9cfc8790
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrateAddonRepository.js
@@ -0,0 +1,127 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const EXPECTED_SCHEMA_VERSION = 4;
+var dbfile;
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Write out a minimal database.
+ dbfile = gProfD.clone();
+ dbfile.append("addons.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+
+ db.createTable("addon",
+ "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT UNIQUE, " +
+ "type TEXT, " +
+ "name TEXT, " +
+ "version TEXT, " +
+ "creator TEXT, " +
+ "creatorURL TEXT, " +
+ "description TEXT, " +
+ "fullDescription TEXT, " +
+ "developerComments TEXT, " +
+ "eula TEXT, " +
+ "iconURL TEXT, " +
+ "homepageURL TEXT, " +
+ "supportURL TEXT, " +
+ "contributionURL TEXT, " +
+ "contributionAmount TEXT, " +
+ "averageRating INTEGER, " +
+ "reviewCount INTEGER, " +
+ "reviewURL TEXT, " +
+ "totalDownloads INTEGER, " +
+ "weeklyDownloads INTEGER, " +
+ "dailyUsers INTEGER, " +
+ "sourceURI TEXT, " +
+ "repositoryStatus INTEGER, " +
+ "size INTEGER, " +
+ "updateDate INTEGER");
+
+ db.createTable("developer",
+ "addon_internal_id INTEGER, " +
+ "num INTEGER, " +
+ "name TEXT, " +
+ "url TEXT, " +
+ "PRIMARY KEY (addon_internal_id, num)");
+
+ db.createTable("screenshot",
+ "addon_internal_id INTEGER, " +
+ "num INTEGER, " +
+ "url TEXT, " +
+ "thumbnailURL TEXT, " +
+ "caption TEXT, " +
+ "PRIMARY KEY (addon_internal_id, num)");
+
+ let insertStmt = db.createStatement("INSERT INTO addon (id) VALUES (:id)");
+ insertStmt.params.id = "test1@tests.mozilla.org";
+ insertStmt.execute();
+ insertStmt.finalize();
+
+ insertStmt = db.createStatement("INSERT INTO screenshot VALUES " +
+ "(:addon_internal_id, :num, :url, :thumbnailURL, :caption)");
+
+ insertStmt.params.addon_internal_id = 1;
+ insertStmt.params.num = 0;
+ insertStmt.params.url = "http://localhost/full1-1.png";
+ insertStmt.params.thumbnailURL = "http://localhost/thumbnail1-1.png";
+ insertStmt.params.caption = "Caption 1 - 1";
+ insertStmt.execute();
+ insertStmt.finalize();
+
+ db.schemaVersion = 1;
+ db.close();
+
+
+ Services.prefs.setBoolPref("extensions.getAddons.cache.enabled", true);
+ AddonRepository.getCachedAddonByID("test1@tests.mozilla.org", function (aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_eq(aAddon.screenshots.length, 1);
+ do_check_true(aAddon.screenshots[0].width === null);
+ do_check_true(aAddon.screenshots[0].height === null);
+ do_check_true(aAddon.screenshots[0].thumbnailWidth === null);
+ do_check_true(aAddon.screenshots[0].thumbnailHeight === null);
+ do_check_eq(aAddon.iconURL, undefined);
+ do_check_eq(JSON.stringify(aAddon.icons), "{}");
+ AddonRepository.shutdown().then(
+ function checkAfterRepoShutdown() {
+ // Check the DB schema has changed once AddonRepository has freed it.
+ db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ do_check_eq(db.schemaVersion, EXPECTED_SCHEMA_VERSION);
+ do_check_true(db.indexExists("developer_idx"));
+ do_check_true(db.indexExists("screenshot_idx"));
+ do_check_true(db.indexExists("compatibility_override_idx"));
+ do_check_true(db.tableExists("compatibility_override"));
+ do_check_true(db.indexExists("icon_idx"));
+ do_check_true(db.tableExists("icon"));
+
+ // Check the trigger is working
+ db.executeSimpleSQL("INSERT INTO addon (id, type, name) VALUES('test_addon', 'extension', 'Test Addon')");
+ let internalID = db.lastInsertRowID;
+ db.executeSimpleSQL("INSERT INTO compatibility_override (addon_internal_id, num, type) VALUES('" + internalID + "', '1', 'incompatible')");
+
+ let selectStmt = db.createStatement("SELECT COUNT(*) AS count FROM compatibility_override");
+ selectStmt.executeStep();
+ do_check_eq(selectStmt.row.count, 1);
+ selectStmt.reset();
+
+ db.executeSimpleSQL("DELETE FROM addon");
+ selectStmt.executeStep();
+ do_check_eq(selectStmt.row.count, 0);
+ selectStmt.finalize();
+
+ db.close();
+ do_test_finished();
+ },
+ do_report_unexpected_exception
+ );
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_migrate_max_version.js b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate_max_version.js
new file mode 100644
index 000000000..171ac411e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_migrate_max_version.js
@@ -0,0 +1,103 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Checks that we don't migrate data from SQLITE if
+// the "extensions.databaseSchema" preference shows we've
+// already upgraded to JSON
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ // Write out a minimal database
+ let dbfile = gProfD.clone();
+ dbfile.append("extensions.sqlite");
+ let db = AM_Cc["@mozilla.org/storage/service;1"].
+ getService(AM_Ci.mozIStorageService).
+ openDatabase(dbfile);
+ db.createTable("addon", "internal_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
+ "id TEXT, location TEXT, version TEXT, active INTEGER, " +
+ "userDisabled INTEGER, installDate INTEGER");
+ db.createTable("targetApplication", "addon_internal_id INTEGER, " +
+ "id TEXT, minVersion TEXT, maxVersion TEXT");
+ let stmt = db.createStatement("INSERT INTO addon VALUES (NULL, :id, :location, " +
+ ":version, :active, :userDisabled, :installDate)");
+
+ let internal_ids = {};
+
+ let a = ["addon1@tests.mozilla.org", "app-profile", "1.0", "0", "1", "0"];
+ stmt.params.id = a[0];
+ stmt.params.location = a[1];
+ stmt.params.version = a[2];
+ stmt.params.active = a[3];
+ stmt.params.userDisabled = a[4];
+ stmt.params.installDate = a[5];
+ stmt.execute();
+ internal_ids[a[0]] = db.lastInsertRowID;
+ stmt.finalize();
+
+ db.schemaVersion = 14;
+ Services.prefs.setIntPref("extensions.databaseSchema", 14);
+ db.close();
+
+ startupManager();
+ run_next_test();
+}
+
+add_test(function before_rebuild() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org",
+ function check_before_rebuild (a1) {
+ // First check that it migrated OK once
+ // addon1 was disabled in the database
+ do_check_neq(a1, null);
+ do_check_true(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_false(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_false(a1.foreignInstall);
+
+ run_next_test();
+ });
+});
+
+// now shut down, remove the JSON database,
+// start up again, and make sure the data didn't migrate this time
+add_test(function rebuild_again() {
+ shutdownManager();
+ gExtensionsJSON.remove(true);
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org",
+ function check_after_rebuild(a1) {
+ // addon1 was rebuilt from extensions directory,
+ // so it appears enabled as a foreign install
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isActive);
+ do_check_false(a1.strictCompatibility);
+ do_check_true(a1.foreignInstall);
+
+ run_next_test();
+ });
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_multiprocessCompatible.js b/toolkit/mozapps/webextensions/test/xpcshell/test_multiprocessCompatible.js
new file mode 100644
index 000000000..4cd103fa6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_multiprocessCompatible.js
@@ -0,0 +1,120 @@
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer;
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+function build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible) {
+ return function* () {
+ dump("Running test" +
+ " multiprocessCompatible: " + multiprocessCompatible +
+ " bootstrap: " + bootstrap +
+ " updateMultiprocessCompatible: " + updateMultiprocessCompatible +
+ "\n");
+
+ let addonData = {
+ id: "addon@tests.mozilla.org",
+ name: "Test Add-on",
+ version: "1.0",
+ multiprocessCompatible,
+ bootstrap,
+ updateURL: "http://localhost:" + gPort + "/updaterdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }
+
+ gServer.registerPathHandler("/updaterdf", function(request, response) {
+ let updateData = {};
+ updateData[addonData.id] = [{
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+ }];
+
+ if (updateMultiprocessCompatible !== undefined) {
+ updateData[addonData.id][0].multiprocessCompatible = updateMultiprocessCompatible;
+ }
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.write(createUpdateRDF(updateData));
+ });
+
+ let expectedMPC = updateMultiprocessCompatible === undefined ?
+ multiprocessCompatible :
+ updateMultiprocessCompatible;
+
+ let xpifile = createTempXPIFile(addonData);
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(xpifile, resolve));
+ do_check_eq(install.addon.multiprocessCompatible, !!multiprocessCompatible);
+ do_check_eq(install.addon.mpcOptedOut, multiprocessCompatible === false)
+ yield promiseCompleteAllInstalls([install]);
+
+ if (!bootstrap) {
+ yield promiseRestartManager();
+ do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
+ do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
+ }
+
+ let addon = yield promiseAddonByID(addonData.id);
+ do_check_neq(addon, null);
+ do_check_eq(addon.multiprocessCompatible, !!multiprocessCompatible);
+ do_check_eq(addon.mpcOptedOut, multiprocessCompatible === false);
+
+ yield promiseFindAddonUpdates(addon);
+
+ // Should have applied the compatibility change
+ do_check_eq(addon.multiprocessCompatible, !!expectedMPC);
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(addonData.id);
+ // Should have persisted the compatibility change
+ do_check_eq(addon.multiprocessCompatible, !!expectedMPC);
+ if (!bootstrap) {
+ do_check_true(isExtensionInAddonsList(profileDir, addonData.id));
+ do_check_eq(isItemMarkedMPIncompatible(addonData.id), !multiprocessCompatible);
+ }
+
+ addon.uninstall();
+ yield promiseRestartManager();
+
+ gServer.registerPathHandler("/updaterdf", null);
+ }
+}
+
+/* Builds a set of tests to run the same steps for every combination of:
+ * The add-on being restartless
+ * The initial add-on supporting multiprocess
+ * The update saying the add-on should or should not support multiprocess (or not say anything at all)
+ */
+for (let bootstrap of [false, true]) {
+ for (let multiprocessCompatible of [undefined, false, true]) {
+ for (let updateMultiprocessCompatible of [undefined, false, true]) {
+ add_task(build_test(multiprocessCompatible, bootstrap, updateMultiprocessCompatible));
+ }
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ startupManager();
+
+ // Create and configure the HTTP server.
+ gServer = new HttpServer();
+ gServer.registerDirectory("/data/", gTmpD);
+ gServer.start(-1);
+ gPort = gServer.identity.primaryPort;
+
+ run_next_test();
+}
+
+function end_test() {
+ gServer.stop(do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_no_addons.js b/toolkit/mozapps/webextensions/test/xpcshell/test_no_addons.js
new file mode 100644
index 000000000..a2ea5301e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_no_addons.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test startup and restart when no add-ons are installed
+// bug 944006
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// Load XPI Provider to get schema version ID
+var XPIScope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+const DB_SCHEMA = XPIScope.DB_SCHEMA;
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Test for a preference to either exist with a specified value, or not exist at all
+function checkPending() {
+ try {
+ do_check_false(Services.prefs.getBoolPref("extensions.pendingOperations"));
+ }
+ catch (e) {
+ // OK
+ }
+}
+
+function checkString(aPref, aValue) {
+ try {
+ do_check_eq(Services.prefs.getCharPref(aPref), aValue)
+ }
+ catch (e) {
+ // OK
+ }
+}
+
+// Make sure all our extension state is empty/nonexistent
+function check_empty_state() {
+ do_check_false(gExtensionsJSON.exists());
+ do_check_false(gExtensionsINI.exists());
+
+ do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+
+ checkString("extensions.bootstrappedAddons", "{}");
+ checkString("extensions.installCache", "[]");
+ checkPending();
+}
+
+// After first run with no add-ons, we expect:
+// no extensions.json is created
+// no extensions.ini
+// database schema version preference is set
+// bootstrap add-ons preference is not found
+// add-on directory state preference is an empty array
+// no pending operations
+add_task(function* first_run() {
+ startupManager();
+ check_empty_state();
+ yield true;
+});
+
+// Now do something that causes a DB load, and re-check
+function* trigger_db_load() {
+ let addonDefer = Promise.defer();
+ AddonManager.getAddonsByTypes(['extension'], addonDefer.resolve);
+ let addonList = yield addonDefer.promise;
+
+ do_check_eq(addonList.length, 0);
+ check_empty_state();
+
+ yield true;
+}
+add_task(trigger_db_load);
+
+// Now restart the manager and check again
+add_task(function* restart_and_recheck() {
+ restartManager();
+ check_empty_state();
+ yield true;
+});
+
+// and reload the DB again
+add_task(trigger_db_load);
+
+// When we start up with no DB and an old database schema, we should update the
+// schema number but not create a database
+add_task(function upgrade_schema_version() {
+ shutdownManager();
+ Services.prefs.setIntPref("extensions.databaseSchema", 1);
+
+ startupManager();
+ do_check_eq(Services.prefs.getIntPref("extensions.databaseSchema"), DB_SCHEMA);
+ check_empty_state();
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_nodisable_hidden.js b/toolkit/mozapps/webextensions/test/xpcshell/test_nodisable_hidden.js
new file mode 100644
index 000000000..2d11e9c5b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_nodisable_hidden.js
@@ -0,0 +1,107 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This test verifies that hidden add-ons cannot be user disabled.
+
+// for normal add-ons
+const profileDir = FileUtils.getDir("ProfD", ["extensions"]);
+// for system add-ons
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+const NORMAL_ID = "normal@tests.mozilla.org";
+const SYSTEM_ID = "system@tests.mozilla.org";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+// normal add-ons can be user disabled.
+add_task(function*() {
+
+ writeInstallRDFToDir({
+ id: NORMAL_ID,
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test disabling hidden add-ons, non-hidden add-on case.",
+ }, profileDir, NORMAL_ID);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test disabling hidden add-ons, non-hidden add-on case.");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_false(addon.userDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ // normal add-ons can be disabled by the user.
+ addon.userDisabled = true;
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test disabling hidden add-ons, non-hidden add-on case.");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.userDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ addon.uninstall();
+
+ shutdownManager();
+});
+
+// system add-ons can never be user disabled.
+add_task(function*() {
+
+ writeInstallRDFToDir({
+ id: SYSTEM_ID,
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test disabling hidden add-ons, hidden system add-on case.",
+ }, distroDir, SYSTEM_ID);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(SYSTEM_ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test disabling hidden add-ons, hidden system add-on case.");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_false(addon.userDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ // system add-ons cannot be disabled by the user.
+ try {
+ addon.userDisabled = true;
+ do_throw("Expected addon.userDisabled on a hidden add-on to throw!");
+ } catch (e) {
+ do_check_eq(e.message, `Cannot disable hidden add-on ${SYSTEM_ID}`);
+ }
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test disabling hidden add-ons, hidden system add-on case.");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_false(addon.userDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ shutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_onPropertyChanged_appDisabled.js b/toolkit/mozapps/webextensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
new file mode 100644
index 000000000..f9b7da073
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_onPropertyChanged_appDisabled.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ AddonManager.strictCompatibility = false;
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ aAddon.userDisabled = true;
+ do_execute_soon(run_test_1);
+ });
+}
+
+function run_test_1() {
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_true(aAddon.userDisabled);
+ do_check_false(aAddon.isActive);
+ do_check_false(aAddon.appDisabled);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["appDisabled"]]
+ ]
+ }, [], run_test_2);
+
+ AddonManager.strictCompatibility = true;
+ });
+}
+
+function run_test_2() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
+ do_check_neq(aAddon, null);
+ do_check_true(aAddon.userDisabled);
+ do_check_false(aAddon.isActive);
+ do_check_true(aAddon.appDisabled);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["appDisabled"]]
+ ]
+ }, [], callback_soon(do_test_finished));
+
+ AddonManager.strictCompatibility = false;
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_overrideblocklist.js b/toolkit/mozapps/webextensions/test/xpcshell/test_overrideblocklist.js
new file mode 100644
index 000000000..c39e432bd
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_overrideblocklist.js
@@ -0,0 +1,200 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const KEY_PROFILEDIR = "ProfD";
+const KEY_APPDIR = "XCurProcD";
+const FILE_BLOCKLIST = "blocklist.xml";
+
+const PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
+
+const OLD = do_get_file("data/test_overrideblocklist/old.xml");
+const NEW = do_get_file("data/test_overrideblocklist/new.xml");
+const ANCIENT = do_get_file("data/test_overrideblocklist/ancient.xml");
+const OLD_TSTAMP = 1296046918000;
+const NEW_TSTAMP = 1396046918000;
+
+const gAppDir = FileUtils.getFile(KEY_APPDIR, []);
+
+var oldAddon = {
+ id: "old@tests.mozilla.org",
+ version: 1
+}
+var newAddon = {
+ id: "new@tests.mozilla.org",
+ version: 1
+}
+var ancientAddon = {
+ id: "ancient@tests.mozilla.org",
+ version: 1
+}
+var invalidAddon = {
+ id: "invalid@tests.mozilla.org",
+ version: 1
+}
+
+function incrementAppVersion() {
+ gAppInfo.version = "" + (parseInt(gAppInfo.version) + 1);
+}
+
+function clearBlocklists() {
+ let blocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ if (blocklist.exists())
+ blocklist.remove(true);
+
+ blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+ if (blocklist.exists())
+ blocklist.remove(true);
+}
+
+function reloadBlocklist() {
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, false);
+ Services.prefs.setBoolPref(PREF_BLOCKLIST_ENABLED, true);
+}
+
+function copyToApp(file) {
+ file.clone().copyTo(gAppDir, FILE_BLOCKLIST);
+}
+
+function copyToProfile(file, tstamp) {
+ file = file.clone();
+ file.copyTo(gProfD, FILE_BLOCKLIST);
+ file = gProfD.clone();
+ file.append(FILE_BLOCKLIST);
+ file.lastModifiedTime = tstamp;
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ let appBlocklist = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
+ if (appBlocklist.exists()) {
+ try {
+ appBlocklist.moveTo(gAppDir, "blocklist.old");
+ }
+ catch (e) {
+ todo(false, "Aborting test due to unmovable blocklist file: " + e);
+ return;
+ }
+ do_register_cleanup(function() {
+ clearBlocklists();
+ appBlocklist.moveTo(gAppDir, FILE_BLOCKLIST);
+ });
+ }
+
+ run_next_test();
+}
+
+// On first run whataver is in the app dir should get copied to the profile
+add_test(function test_copy() {
+ clearBlocklists();
+ copyToApp(OLD);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// An ancient blocklist should be ignored
+add_test(function test_ancient() {
+ clearBlocklists();
+ copyToApp(ANCIENT);
+ copyToProfile(OLD, OLD_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// A new blocklist should override an old blocklist
+add_test(function test_override() {
+ clearBlocklists();
+ copyToApp(NEW);
+ copyToProfile(OLD, OLD_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_false(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_true(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// An old blocklist shouldn't override a new blocklist
+add_test(function test_retain() {
+ clearBlocklists();
+ copyToApp(OLD);
+ copyToProfile(NEW, NEW_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+
+ reloadBlocklist();
+ let blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_false(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_true(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
+
+// A missing blocklist in the profile should still load an app-shipped blocklist
+add_test(function test_missing() {
+ clearBlocklists();
+ copyToApp(OLD);
+ copyToProfile(NEW, NEW_TSTAMP);
+
+ incrementAppVersion();
+ startupManager();
+ shutdownManager();
+
+ let blocklist = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+ blocklist.remove(true);
+ startupManager(false);
+
+ reloadBlocklist();
+ blocklist = AM_Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(AM_Ci.nsIBlocklistService);
+ do_check_false(blocklist.isAddonBlocklisted(invalidAddon));
+ do_check_false(blocklist.isAddonBlocklisted(ancientAddon));
+ do_check_true(blocklist.isAddonBlocklisted(oldAddon));
+ do_check_false(blocklist.isAddonBlocklisted(newAddon));
+
+ shutdownManager();
+
+ run_next_test();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_pass_symbol.js b/toolkit/mozapps/webextensions/test/xpcshell/test_pass_symbol.js
new file mode 100644
index 000000000..657601e45
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_pass_symbol.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const PASS_PREF = "symboltest.instanceid.pass";
+const FAIL_BOGUS_PREF = "symboltest.instanceid.fail_bogus";
+const FAIL_ID_PREF = "symboltest.instanceid.fail_bogus";
+const ADDON_ID = "test_symbol@tests.mozilla.org";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+BootstrapMonitor.init();
+
+// symbol is passed when add-on is installed
+add_task(function*() {
+ for (let pref of [PASS_PREF, FAIL_BOGUS_PREF, FAIL_ID_PREF])
+ Services.prefs.clearUserPref(pref);
+
+ yield promiseInstallAllFiles([do_get_addon("test_symbol")], true);
+
+ let addon = yield promiseAddonByID(ADDON_ID);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Symbol");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+
+ // most of the test is in bootstrap.js in the addon because BootstrapMonitor
+ // currently requires the objects in `data` to be serializable, and we
+ // need a real reference to the symbol to test this.
+ do_execute_soon(function() {
+ // give the startup time to run
+ do_check_true(Services.prefs.getBoolPref(PASS_PREF));
+ do_check_true(Services.prefs.getBoolPref(FAIL_BOGUS_PREF));
+ do_check_true(Services.prefs.getBoolPref(FAIL_ID_PREF));
+ });
+
+ yield promiseRestartManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_permissions.js b/toolkit/mozapps/webextensions/test/xpcshell/test_permissions.js
new file mode 100644
index 000000000..48fef406f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_permissions.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// Checks that permissions set in preferences are correctly imported but can
+// be removed by the user.
+
+const XPI_MIMETYPE = "application/x-xpinstall";
+
+function newPrincipal(uri) {
+ return Services.scriptSecurityManager.createCodebasePrincipal(NetUtil.newURI(uri), {});
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ Services.prefs.setCharPref("xpinstall.whitelist.add", "https://test1.com,https://test2.com");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.36", "https://test3.com,https://www.test4.com");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.test5", "https://test5.com");
+
+ Services.perms.add(NetUtil.newURI("https://www.test9.com"), "install",
+ AM_Ci.nsIPermissionManager.ALLOW_ACTION);
+
+ startupManager();
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://test1.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test4.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test5.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test6.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test6.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test7.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test8.com")));
+
+ // This should remain unaffected
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("http://www.test9.com")));
+ do_check_true(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test9.com")));
+
+ Services.perms.removeAll();
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+
+ // Upgrade the application and verify that the permissions are still not there
+ restartManager("2");
+
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test1.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test2.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://test3.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test4.com")));
+ do_check_false(AddonManager.isInstallAllowed(XPI_MIMETYPE,
+ newPrincipal("https://www.test5.com")));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_permissions_prefs.js b/toolkit/mozapps/webextensions/test/xpcshell/test_permissions_prefs.js
new file mode 100644
index 000000000..576f04a65
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_permissions_prefs.js
@@ -0,0 +1,74 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that xpinstall.[whitelist|blacklist].add preferences are emptied when
+// converted into permissions.
+
+const PREF_XPI_WHITELIST_PERMISSIONS = "xpinstall.whitelist.add";
+const PREF_XPI_BLACKLIST_PERMISSIONS = "xpinstall.blacklist.add";
+
+function newPrincipal(uri) {
+ return Services.scriptSecurityManager.createCodebasePrincipal(NetUtil.newURI(uri), {});
+}
+
+function do_check_permission_prefs(preferences) {
+ // Check preferences were emptied
+ for (let pref of preferences) {
+ try {
+ do_check_eq(Services.prefs.getCharPref(pref), "");
+ }
+ catch (e) {
+ // Successfully emptied
+ }
+ }
+}
+
+function clear_imported_preferences_cache() {
+ let scope = Components.utils.import("resource://gre/modules/PermissionsUtils.jsm", {});
+ scope.gImportedPrefBranches.clear();
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ // Create own preferences to test
+ Services.prefs.setCharPref("xpinstall.whitelist.add.EMPTY", "");
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST", "http://whitelist.example.com");
+ Services.prefs.setCharPref("xpinstall.blacklist.add.EMPTY", "");
+ Services.prefs.setCharPref("xpinstall.blacklist.add.TEST", "http://blacklist.example.com");
+
+ // Get list of preferences to check
+ var whitelistPreferences = Services.prefs.getChildList(PREF_XPI_WHITELIST_PERMISSIONS, {});
+ var blacklistPreferences = Services.prefs.getChildList(PREF_XPI_BLACKLIST_PERMISSIONS, {});
+ var preferences = whitelistPreferences.concat(blacklistPreferences);
+
+ startupManager();
+
+ // Permissions are imported lazily - act as thought we're checking an install,
+ // to trigger on-deman importing of the permissions.
+ AddonManager.isInstallAllowed("application/x-xpinstall", newPrincipal("http://example.com/file.xpi"));
+ do_check_permission_prefs(preferences);
+
+
+ // Import can also be triggerred by an observer notification by any other area
+ // of code, such as a permissions management UI.
+
+ // First, request to flush all permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST2", "https://whitelist2.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "install");
+ do_check_permission_prefs(preferences);
+
+ // Then, request to flush just install permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST3", "https://whitelist3.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "");
+ do_check_permission_prefs(preferences);
+
+ // And a request to flush some other permissions sholdn't flush install permissions
+ clear_imported_preferences_cache();
+ Services.prefs.setCharPref("xpinstall.whitelist.add.TEST4", "https://whitelist4.example.com");
+ Services.obs.notifyObservers(null, "flush-pending-permissions", "lolcats");
+ do_check_eq(Services.prefs.getCharPref("xpinstall.whitelist.add.TEST4"), "https://whitelist4.example.com");
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_pluginBlocklistCtp.js b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginBlocklistCtp.js
new file mode 100644
index 000000000..563b7434c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginBlocklistCtp.js
@@ -0,0 +1,182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const nsIBLS = Components.interfaces.nsIBlocklistService;
+Components.utils.import("resource://testing-common/httpd.js");
+
+var gBlocklistService = null;
+var gNotifier = null;
+var gNextTest = null;
+var gPluginHost = null;
+
+var gServer = new HttpServer();
+gServer.start(-1);
+gPort = gServer.identity.primaryPort;
+mapFile("/data/test_pluginBlocklistCtp.xml", gServer);
+mapFile("/data/test_pluginBlocklistCtpUndo.xml", gServer);
+
+var PLUGINS = [{
+ // severity=0, vulnerabilitystatus=0 -> outdated
+ name: "test_plugin_0",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=0, vulnerabilitystatus=1 -> update available
+ name: "test_plugin_1",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=0, vulnerabilitystatus=2 -> no update
+ name: "test_plugin_2",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // no severity field -> severity=3 by default -> hardblock
+ name: "test_plugin_3",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // severity=1, vulnerabilitystatus=2 -> softblock
+ name: "test_plugin_4",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+},
+{
+ // not in the blocklist -> not blocked
+ name: "test_plugin_5",
+ version: "5",
+ disabled: false,
+ blocklisted: false
+}];
+
+function test_basic() {
+ var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(nsIBLS);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_VULNERABLE_UPDATE_AVAILABLE);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_VULNERABLE_NO_UPDATE);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_BLOCKED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[4], "1", "1.9") == nsIBLS.STATE_SOFTBLOCKED);
+
+ do_check_true(blocklist.getPluginBlocklistState(PLUGINS[5], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
+
+ gNextTest = test_is_not_clicktoplay;
+ do_execute_soon(gNextTest);
+}
+
+function get_test_plugin() {
+ var pluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+ for (var plugin of pluginHost.getPluginTags()) {
+ if (plugin.name == "Test Plug-in")
+ return plugin;
+ }
+ do_check_true(false);
+ return null;
+}
+
+// At this time, the blocklist does not have an entry for the test plugin,
+// so it shouldn't be click-to-play.
+function test_is_not_clicktoplay() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtpUndo.xml");
+ gNextTest = test_is_clicktoplay;
+ gNotifier.notify(null);
+}
+
+// Here, we've updated the blocklist to have a block for the test plugin,
+// so it should be click-to-play.
+function test_is_clicktoplay() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_eq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtp.xml");
+ gNextTest = test_is_not_clicktoplay2;
+ gNotifier.notify(null);
+}
+
+// But now we've removed that entry from the blocklist (really we've gone back
+// to the old one), so the plugin shouldn't be click-to-play any more.
+function test_is_not_clicktoplay2() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtpUndo.xml");
+ gNextTest = test_disable_blocklist;
+ gNotifier.notify(null);
+}
+
+// Test that disabling the blocklist when a plugin is ctp-blocklisted will
+// result in the plugin not being click-to-play.
+function test_disable_blocklist() {
+ var plugin = get_test_plugin();
+ var blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_eq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+
+ gNextTest = null;
+ Services.prefs.setBoolPref("extensions.blocklist.enabled", false);
+ blocklistState = gBlocklistService.getPluginBlocklistState(plugin, "1", "1.9");
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+ do_check_neq(blocklistState, Components.interfaces.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+
+ // it should still be possible to make a plugin click-to-play via the pref
+ // and setting that plugin's enabled state to click-to-play
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ let previousEnabledState = plugin.enabledState;
+ plugin.enabledState = Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY;
+ do_check_eq(gPluginHost.getStateForType("application/x-test"), Components.interfaces.nsIPluginTag.STATE_CLICKTOPLAY);
+ // clean up plugin state
+ plugin.enabledState = previousEnabledState;
+
+ gServer.stop(do_test_finished);
+}
+
+// Observe "blocklist-updated" so we know when to advance to the next test
+function observer() {
+ if (gNextTest)
+ do_execute_soon(gNextTest);
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+
+ Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" + gPort + "/data/test_pluginBlocklistCtp.xml");
+ Services.prefs.setBoolPref("plugin.load_flash_only", false);
+ startupManager();
+
+ gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
+ gBlocklistService = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(Components.interfaces.nsIBlocklistService);
+ gNotifier = Components.classes["@mozilla.org/extensions/blocklist;1"].getService(Components.interfaces.nsITimerCallback);
+ Services.obs.addObserver(observer, "blocklist-updated", false);
+
+ do_register_cleanup(function() {
+ Services.prefs.clearUserPref("extensions.blocklist.url");
+ Services.prefs.clearUserPref("extensions.blocklist.enabled");
+ Services.prefs.clearUserPref("plugins.click_to_play");
+ Services.obs.removeObserver(observer, "blocklist-updated");
+ });
+
+ gNextTest = test_basic;
+ do_test_pending();
+ gNotifier.notify(null);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_pluginInfoURL.js b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginInfoURL.js
new file mode 100644
index 000000000..e5acb28a4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginInfoURL.js
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+var Ci = Components.interfaces;
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+/**
+ * MockPlugin mimics the behaviour of a plugin.
+ */
+function MockPlugin(name, version, enabledState) {
+ this.name = name;
+ this.version = version;
+ this.enabledState = enabledState;
+}
+
+MockPlugin.prototype = {
+ get blocklisted() {
+ let bls = Services.blocklist;
+ return bls.getPluginBlocklistState(this) == bls.STATE_BLOCKED;
+ },
+
+ get disabled() {
+ return this.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
+ }
+};
+
+// The mocked blocked plugin used to test the blocklist.
+const PLUGINS = [
+ new MockPlugin('test_with_infoURL', '5', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_with_altInfoURL', '5', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_no_infoURL', '5', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_newVersion', '1', Ci.nsIPluginTag.STATE_ENABLED),
+ new MockPlugin('test_newVersion', '3', Ci.nsIPluginTag.STATE_ENABLED)
+];
+
+/**
+ * The entry point of the unit tests, which is also responsible of
+ * copying the blocklist file to the profile folder.
+ */
+function run_test() {
+ copyBlocklistToProfile(do_get_file('data/pluginInfoURL_block.xml'));
+
+ createAppInfo('xpcshell@tests.mozilla.org', 'XPCShell', '3', '8');
+ startupManager();
+
+ run_next_test();
+}
+
+/**
+ * Test that the blocklist service correctly loads and returns the infoURL for
+ * a plugin that matches the first entry in the blocklist.
+ */
+add_task(function* test_infoURL() {
+ // The testInfoURL must match the value within the
+ // <infoURL> tag in pluginInfoURL_block.xml.
+ let testInfoURL = 'http://test.url.com/';
+
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[0]),
+ testInfoURL, 'Should be the provided url when an infoURL tag is available');
+});
+
+/**
+ * Test that the blocklist service correctly loads and returns the infoURL for
+ * a plugin that partially matches an earlier entry in the blocklist.
+ */
+add_task(function* test_altInfoURL() {
+ let altTestInfoURL = 'http://alt.test.url.com/';
+
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[1]),
+ altTestInfoURL, 'Should be the alternative infoURL');
+});
+
+/**
+ * Test that the blocklist service correctly returns null
+ * if the infoURL tag is missing in the blocklist.xml file.
+ */
+add_task(function* test_infoURL_missing() {
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[2]), null,
+ 'Should be null when no infoURL tag is available.');
+});
+
+add_task(function* test_intoURL_newVersion() {
+ let testInfoURL = 'http://test.url2.com/';
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[3]),
+ testInfoURL, 'Old plugin should match');
+ Assert.strictEqual(Services.blocklist.getPluginInfoURL(PLUGINS[4]),
+ null, 'New plugin should not match');
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_pluginchange.js b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginchange.js
new file mode 100644
index 000000000..05e17b35e
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_pluginchange.js
@@ -0,0 +1,283 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const LIST_UPDATED_TOPIC = "plugins-list-updated";
+
+// We need to use the same algorithm for generating IDs for plugins
+var { getIDHashForString } = Components.utils.import("resource://gre/modules/addons/PluginProvider.jsm");
+var { MockRegistrar } = Components.utils.import("resource://testing-common/MockRegistrar.jsm");
+
+function PluginTag(name, description) {
+ this.name = name;
+ this.description = description;
+}
+
+PluginTag.prototype = {
+ name: null,
+ description: null,
+ version: "1.0",
+ filename: null,
+ fullpath: null,
+ disabled: false,
+ blocklisted: false,
+ clicktoplay: false,
+
+ mimeTypes: [],
+
+ getMimeTypes: function(count) {
+ count.value = this.mimeTypes.length;
+ return this.mimeTypes;
+ }
+};
+
+const PLUGINS = [
+ // A standalone plugin
+ new PluginTag("Java", "A mock Java plugin"),
+
+ // A plugin made up of two plugin files
+ new PluginTag("Flash", "A mock Flash plugin"),
+ new PluginTag("Flash", "A mock Flash plugin")
+];
+
+const gPluginHost = {
+ // nsIPluginHost
+ getPluginTags: function(count) {
+ count.value = PLUGINS.length;
+ return PLUGINS;
+ },
+
+ QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIPluginHost])
+};
+
+MockRegistrar.register("@mozilla.org/plugin/host;1", gPluginHost);
+
+// This verifies that when the list of plugins changes the add-ons manager
+// correctly updates
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setBoolPref("media.gmp-provider.enabled", false);
+
+ startupManager();
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ run_test_1();
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+function sortAddons(addons) {
+ addons.sort(function(a, b) {
+ return a.name.localeCompare(b.name);
+ });
+}
+
+// Basic check that the mock object works
+function run_test_1() {
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_2();
+ });
+}
+
+// No change to the list should not trigger any events or changes in the API
+function run_test_2() {
+ // Reorder the list a bit
+ let tag = PLUGINS[0];
+ PLUGINS[0] = PLUGINS[2];
+ PLUGINS[2] = PLUGINS[1];
+ PLUGINS[1] = tag;
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_3();
+ });
+}
+
+// Tests that a newly detected plugin shows up in the API and sends out events
+function run_test_3() {
+ let tag = new PluginTag("Quicktime", "A mock Quicktime plugin");
+ PLUGINS.push(tag);
+ let id = getIDHashForString(tag.name + tag.description);
+
+ let test_params = {};
+ test_params[id] = [
+ ["onInstalling", false],
+ "onInstalled"
+ ];
+
+ prepare_test(test_params, [
+ "onExternalInstall"
+ ]);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 3);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Java");
+ do_check_false(addons[1].userDisabled);
+ do_check_eq(addons[2].name, "Quicktime");
+ do_check_false(addons[2].userDisabled);
+
+ run_test_4();
+ });
+}
+
+// Tests that a removed plugin disappears from in the API and sends out events
+function run_test_4() {
+ let tag = PLUGINS.splice(1, 1)[0];
+ let id = getIDHashForString(tag.name + tag.description);
+
+ let test_params = {};
+ test_params[id] = [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ];
+
+ prepare_test(test_params);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_5();
+ });
+}
+
+// Removing part of the flash plugin should have no effect
+function run_test_5() {
+ PLUGINS.splice(0, 1);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_6();
+ });
+}
+
+// Replacing flash should be detected
+function run_test_6() {
+ let oldTag = PLUGINS.splice(0, 1)[0];
+ let newTag = new PluginTag("Flash 2", "A new crash-free Flash!");
+ newTag.disabled = true;
+ PLUGINS.push(newTag);
+
+ let test_params = {};
+ test_params[getIDHashForString(oldTag.name + oldTag.description)] = [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ];
+ test_params[getIDHashForString(newTag.name + newTag.description)] = [
+ ["onInstalling", false],
+ "onInstalled"
+ ];
+
+ prepare_test(test_params, [
+ "onExternalInstall"
+ ]);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash 2");
+ do_check_true(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_false(addons[1].userDisabled);
+
+ run_test_7();
+ });
+}
+
+// If new tags are detected and the disabled state changes then we should send
+// out appropriate notifications
+function run_test_7() {
+ PLUGINS[0] = new PluginTag("Quicktime", "A mock Quicktime plugin");
+ PLUGINS[0].disabled = true;
+ PLUGINS[1] = new PluginTag("Flash 2", "A new crash-free Flash!");
+
+ let test_params = {};
+ test_params[getIDHashForString(PLUGINS[0].name + PLUGINS[0].description)] = [
+ ["onDisabling", false],
+ "onDisabled"
+ ];
+ test_params[getIDHashForString(PLUGINS[1].name + PLUGINS[1].description)] = [
+ ["onEnabling", false],
+ "onEnabled"
+ ];
+
+ prepare_test(test_params);
+
+ Services.obs.notifyObservers(null, LIST_UPDATED_TOPIC, null);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ sortAddons(addons);
+
+ do_check_eq(addons.length, 2);
+
+ do_check_eq(addons[0].name, "Flash 2");
+ do_check_false(addons[0].userDisabled);
+ do_check_eq(addons[1].name, "Quicktime");
+ do_check_true(addons[1].userDisabled);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_plugins.js b/toolkit/mozapps/webextensions/test/xpcshell/test_plugins.js
new file mode 100644
index 000000000..3f0ac7ebe
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_plugins.js
@@ -0,0 +1,210 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var TEST_PLUGIN_DESCRIPTION = "Flash plug-in for testing purposes.";
+
+// This verifies that plugins exist and can be enabled and disabled.
+var gID = null;
+
+function setTestPluginState(state) {
+ let tags = AM_Cc["@mozilla.org/plugin/host;1"].getService(AM_Ci.nsIPluginHost)
+ .getPluginTags();
+ for (let tag of tags) {
+ do_print("Checking tag: " + tag.description);
+ if (tag.description == TEST_PLUGIN_DESCRIPTION) {
+ tag.enabledState = state;
+ return;
+ }
+ }
+ throw Error("No plugin tag found for the test plugin");
+}
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ Services.prefs.setBoolPref("plugins.click_to_play", true);
+ Services.prefs.setBoolPref("plugin.load_flash_only", false);
+
+ setTestPluginState(AM_Ci.nsIPluginTag.STATE_CLICKTOPLAY);
+
+ startupManager();
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ run_test_1();
+}
+
+// Finds the test plugin library
+function get_test_plugin() {
+ var pluginEnum = Services.dirsvc.get("APluginsDL", AM_Ci.nsISimpleEnumerator);
+ while (pluginEnum.hasMoreElements()) {
+ let dir = pluginEnum.getNext().QueryInterface(AM_Ci.nsILocalFile);
+ let plugin = dir.clone();
+ // OSX plugin
+ plugin.append("npswftest.plugin");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ plugin = dir.clone();
+ // *nix plugin
+ plugin.append("libnpswftest.so");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ // Windows plugin
+ plugin = dir.clone();
+ plugin.append("npswftest.dll");
+ if (plugin.exists()) {
+ plugin.normalize();
+ return plugin;
+ }
+ }
+ return null;
+}
+
+function getFileSize(aFile) {
+ if (!aFile.isDirectory())
+ return aFile.fileSize;
+
+ let size = 0;
+ let entries = aFile.directoryEntries.QueryInterface(AM_Ci.nsIDirectoryEnumerator);
+ let entry;
+ while (entry = entries.nextFile)
+ size += getFileSize(entry);
+ entries.close();
+ return size;
+}
+
+function getPluginLastModifiedTime(aPluginFile) {
+ // On OS X we use the bundle contents last modified time as using
+ // the package directories modified date may be outdated.
+ // See bug 313700.
+ try {
+ let localFileMac = aPluginFile.QueryInterface(AM_Ci.nsILocalFileMac);
+ if (localFileMac) {
+ return localFileMac.bundleContentsLastModifiedTime;
+ }
+ } catch (e) {
+ }
+
+ return aPluginFile.lastModifiedTime;
+}
+
+// Tests that the test plugin exists
+function run_test_1() {
+ var testPlugin = get_test_plugin();
+ do_check_neq(testPlugin, null);
+
+ AddonManager.getAddonsByTypes(["plugin"], function(addons) {
+ do_check_true(addons.length > 0);
+
+ addons.forEach(function(p) {
+ if (p.description == TEST_PLUGIN_DESCRIPTION)
+ gID = p.id;
+ });
+
+ do_check_neq(gID, null);
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Shockwave Flash");
+ do_check_eq(p.description, TEST_PLUGIN_DESCRIPTION);
+ do_check_eq(p.creator, null);
+ do_check_eq(p.version, "1.0.0.0");
+ do_check_eq(p.type, "plugin");
+ do_check_eq(p.userDisabled, "askToActivate");
+ do_check_false(p.appDisabled);
+ do_check_true(p.isActive);
+ do_check_true(p.isCompatible);
+ do_check_true(p.providesUpdatesSecurely);
+ do_check_eq(p.blocklistState, 0);
+ do_check_eq(p.permissions, AddonManager.PERM_CAN_DISABLE | AddonManager.PERM_CAN_ENABLE);
+ do_check_eq(p.pendingOperations, 0);
+ do_check_true(p.size > 0);
+ do_check_eq(p.size, getFileSize(testPlugin));
+ do_check_true(p.updateDate > 0);
+ do_check_true("isCompatibleWith" in p);
+ do_check_true("findUpdates" in p);
+
+ let lastModifiedTime = getPluginLastModifiedTime(testPlugin);
+ do_check_eq(p.updateDate.getTime(), lastModifiedTime);
+ do_check_eq(p.installDate.getTime(), lastModifiedTime);
+
+ run_test_2(p);
+ });
+ });
+}
+
+// Tests that disabling a plugin works
+function run_test_2(p) {
+ let test = {};
+ test[gID] = [
+ ["onDisabling", false],
+ "onDisabled",
+ ["onPropertyChanged", ["userDisabled"]]
+ ];
+ prepare_test(test);
+
+ p.userDisabled = true;
+
+ ensure_test_completed();
+
+ do_check_true(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_false(p.isActive);
+
+ AddonManager.getAddonByID(gID, function(p2) {
+ do_check_neq(p2, null);
+ do_check_true(p2.userDisabled);
+ do_check_false(p2.appDisabled);
+ do_check_false(p2.isActive);
+ do_check_eq(p2.name, "Shockwave Flash");
+
+ run_test_3(p2);
+ });
+}
+
+// Tests that enabling a plugin works
+function run_test_3(p) {
+ let test = {};
+ test[gID] = [
+ ["onEnabling", false],
+ "onEnabled"
+ ];
+ prepare_test(test);
+
+ p.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p.userDisabled);
+ do_check_false(p.appDisabled);
+ do_check_true(p.isActive);
+
+ AddonManager.getAddonByID(gID, function(p2) {
+ do_check_neq(p2, null);
+ do_check_false(p2.userDisabled);
+ do_check_false(p2.appDisabled);
+ do_check_true(p2.isActive);
+ do_check_eq(p2.name, "Shockwave Flash");
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Verify that after a restart the test plugin has the same ID
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID(gID, function(p) {
+ do_check_neq(p, null);
+ do_check_eq(p.name, "Shockwave Flash");
+
+ Services.prefs.clearUserPref("plugins.click_to_play");
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_pref_properties.js b/toolkit/mozapps/webextensions/test/xpcshell/test_pref_properties.js
new file mode 100644
index 000000000..c6a10e7c1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_pref_properties.js
@@ -0,0 +1,221 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests the preference-related properties of AddonManager
+// eg: AddonManager.checkCompatibility, AddonManager.updateEnabled, etc
+
+var gManagerEventsListener = {
+ seenEvents: [],
+ init: function() {
+ let events = ["onCompatibilityModeChanged", "onCheckUpdateSecurityChanged",
+ "onUpdateModeChanged"];
+ events.forEach(function(aEvent) {
+ this[aEvent] = function() {
+ do_print("Saw event " + aEvent);
+ this.seenEvents.push(aEvent);
+ }
+ }, this);
+ AddonManager.addManagerListener(this);
+ // Try to add twice, to test that the second time silently fails.
+ AddonManager.addManagerListener(this);
+ },
+ shutdown: function() {
+ AddonManager.removeManagerListener(this);
+ },
+ expect: function(aEvents) {
+ this.expectedEvents = aEvents;
+ },
+ checkExpected: function() {
+ do_print("Checking expected events...");
+ while (this.expectedEvents.length > 0) {
+ let event = this.expectedEvents.pop();
+ do_print("Looking for expected event " + event);
+ let matchingEvents = this.seenEvents.filter(function(aSeenEvent) {
+ return aSeenEvent == event;
+ });
+ do_check_eq(matchingEvents.length, 1);
+ }
+ this.seenEvents = [];
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setBoolPref("extensions.update.enabled", true);
+ Services.prefs.setBoolPref("extensions.update.autoUpdateDefault", true);
+ Services.prefs.setBoolPref("extensions.strictCompatibility", true);
+ Services.prefs.setBoolPref("extensions.checkUpdatesecurity", true);
+
+ startupManager();
+ gManagerEventsListener.init();
+
+
+ // AddonManager.updateEnabled
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.updateEnabled);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.updateEnabled);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.updateEnabled = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.updateEnabled);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.updateEnabled);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.enabled"));
+
+ // AddonManager.autoUpdateDefault
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.autoUpdateDefault = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.autoUpdateDefault);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.autoUpdateDefault = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.autoUpdateDefault);
+ do_check_false(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect(["onUpdateModeChanged"]);
+ AddonManager.autoUpdateDefault = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.autoUpdateDefault);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.autoUpdateDefault = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.autoUpdateDefault);
+ do_check_true(Services.prefs.getBoolPref("extensions.update.autoUpdateDefault"));
+
+ // AddonManager.strictCompatibility
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.strictCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.strictCompatibility);
+ do_check_false(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.strictCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.strictCompatibility);
+ do_check_false(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.strictCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.strictCompatibility);
+ do_check_true(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.strictCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.strictCompatibility);
+ do_check_true(Services.prefs.getBoolPref("extensions.strictCompatibility"));
+
+
+ // AddonManager.checkCompatibility
+ if (isNightlyChannel()) {
+ var version = "nightly";
+ } else {
+ version = Services.appinfo.version.replace(/^([^\.]+\.[0-9]+[a-z]*).*/gi, "$1");
+ }
+ const COMPATIBILITY_PREF = "extensions.checkCompatibility." + version;
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.checkCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.getBoolPref(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkCompatibility = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.getBoolPref(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect(["onCompatibilityModeChanged"]);
+ AddonManager.checkCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.prefHasUserValue(COMPATIBILITY_PREF));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkCompatibility = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkCompatibility);
+ do_check_false(Services.prefs.prefHasUserValue(COMPATIBILITY_PREF));
+
+
+ // AddonManager.checkUpdateSecurity
+ gManagerEventsListener.expect(["onCheckUpdateSecurityChanged"]);
+ AddonManager.checkUpdateSecurity = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkUpdateSecurity);
+ if (AddonManager.checkUpdateSecurityDefault)
+ do_check_false(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkUpdateSecurity = false;
+ gManagerEventsListener.checkExpected();
+ do_check_false(AddonManager.checkUpdateSecurity);
+ if (AddonManager.checkUpdateSecurityDefault)
+ do_check_false(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect(["onCheckUpdateSecurityChanged"]);
+ AddonManager.checkUpdateSecurity = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkUpdateSecurity);
+ if (!AddonManager.checkUpdateSecurityDefault)
+ do_check_true(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.expect([]);
+ AddonManager.checkUpdateSecurity = true;
+ gManagerEventsListener.checkExpected();
+ do_check_true(AddonManager.checkUpdateSecurity);
+ if (!AddonManager.checkUpdateSecurityDefault)
+ do_check_true(Services.prefs.getBoolPref("extensions.checkUpdateSecurity"));
+ else
+ do_check_false(Services.prefs.prefHasUserValue("extensions.checkUpdateSecurity"));
+
+ gManagerEventsListener.shutdown();
+
+ // AddonManager.hotfixID
+ let hotfixID = "hotfix@tests.mozilla.org";
+ Services.prefs.setCharPref("extensions.hotfix.id", hotfixID);
+ do_check_eq(AddonManager.hotfixID, hotfixID);
+ // Change the pref and make sure the property is updated
+ hotfixID = "hotfix2@tests.mozilla.org";
+ Services.prefs.setCharPref("extensions.hotfix.id", hotfixID);
+ do_check_eq(AddonManager.hotfixID, hotfixID);
+ // Test an invalid pref value
+ hotfixID = 99;
+ Services.prefs.deleteBranch("extensions.hotfix.id");
+ Services.prefs.setIntPref("extensions.hotfix.id", hotfixID);
+ do_check_eq(AddonManager.hotfixID, null);
+ Services.prefs.clearUserPref("extensions.hotfix.id");
+
+ // After removing the listener, ensure we get no further events.
+ gManagerEventsListener.expect([]);
+ AddonManager.updateEnabled = false;
+ gManagerEventsListener.checkExpected();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_provider_markSafe.js b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_markSafe.js
new file mode 100644
index 000000000..228eb7d34
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_markSafe.js
@@ -0,0 +1,49 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+var startupOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ markSafe: false,
+ apiAccessed: false,
+
+ startup() {
+ if (this.markSafe)
+ AddonManagerPrivate.markProviderSafe(this);
+
+ let uri = Services.io.newURI("beard://long", null, null);
+ AddonManager.isInstallEnabled("made-up-mimetype");
+ },
+ supportsMimetype(mimetype) {
+ this.apiAccessed = true;
+ return false;
+ },
+
+ get name() {
+ return name;
+ },
+ };
+
+ return mockProvider;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* testMarkSafe() {
+ do_print("Starting with provider normally");
+ let provider = mockAddonProvider("Mock1");
+ AddonManagerPrivate.registerProvider(provider);
+ startupManager();
+ ok(!provider.apiAccessed, "Provider API should not have been accessed");
+ AddonManagerPrivate.unregisterProvider(provider);
+ yield promiseShutdownManager();
+
+ do_print("Starting with provider that marks itself safe");
+ provider.apiAccessed = false;
+ provider.markSafe = true;
+ AddonManagerPrivate.registerProvider(provider);
+ startupManager();
+ ok(provider.apiAccessed, "Provider API should have been accessed");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_provider_shutdown.js b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_shutdown.js
new file mode 100644
index 000000000..d210eb81d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_shutdown.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Verify that we report shutdown status for Addon Manager providers
+// and AddonRepository correctly.
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Make a mock AddonRepository that just lets us hang shutdown.
+// Needs two promises - one to let us know that AM has called shutdown,
+// and one for us to let AM know that shutdown is done.
+function mockAddonProvider(aName) {
+ let mockProvider = {
+ donePromise: null,
+ doneResolve: null,
+ doneReject: null,
+ shutdownPromise: null,
+ shutdownResolve: null,
+
+ get name() {
+ return aName;
+ },
+
+ shutdown() {
+ this.shutdownResolve();
+ return this.donePromise;
+ },
+ };
+ mockProvider.donePromise = new Promise((resolve, reject) => {
+ mockProvider.doneResolve = resolve;
+ mockProvider.doneReject = reject;
+ });
+ mockProvider.shutdownPromise = new Promise((resolve, reject) => {
+ mockProvider.shutdownResolve = resolve;
+ });
+ return mockProvider;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+// Helper to find a particular shutdown blocker's status in the JSON blob
+function findInStatus(aStatus, aName) {
+ for (let {name, state} of aStatus.state) {
+ if (name == aName) {
+ return state;
+ }
+ }
+ return null;
+}
+
+/*
+ * Make sure we report correctly when an add-on provider or AddonRepository block shutdown
+ */
+add_task(function* blockRepoShutdown() {
+ // Reach into the AddonManager scope and inject our mock AddonRepository
+ let realAddonRepo = AMscope.AddonRepository;
+ // the mock provider behaves enough like AddonRepository for the purpose of this test
+ let mockRepo = mockAddonProvider("Mock repo");
+ AMscope.AddonRepository = mockRepo;
+
+ let mockProvider = mockAddonProvider("Mock provider");
+
+ startupManager();
+ AddonManagerPrivate.registerProvider(mockProvider);
+
+ // Start shutting the manager down
+ let managerDown = promiseShutdownManager();
+
+ // Wait for manager to call provider shutdown.
+ yield mockProvider.shutdownPromise;
+ // check AsyncShutdown state
+ let status = MockAsyncShutdown.status();
+ equal(findInStatus(status[0], "Mock provider"), "(none)");
+ equal(status[1].name, "AddonRepository: async shutdown");
+ equal(status[1].state, "pending");
+ // let the provider finish
+ mockProvider.doneResolve();
+
+ // Wait for manager to call repo shutdown and start waiting for it
+ yield mockRepo.shutdownPromise;
+ // Check the shutdown state
+ status = MockAsyncShutdown.status();
+ equal(status[0].name, "AddonManager: Waiting for providers to shut down.");
+ equal(status[0].state, "Complete");
+ equal(status[1].name, "AddonRepository: async shutdown");
+ equal(status[1].state, "in progress");
+
+ // Now finish our shutdown, and wait for the manager to wrap up
+ mockRepo.doneResolve();
+ yield managerDown;
+
+ // Check the shutdown state again
+ status = MockAsyncShutdown.status();
+ equal(status[0].name, "AddonRepository: async shutdown");
+ equal(status[0].state, "done");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_shutdown.js b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_shutdown.js
new file mode 100644
index 000000000..f90e38292
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_shutdown.js
@@ -0,0 +1,64 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+var shutdownOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ hasShutdown: false,
+ unsafeAccess: false,
+
+ shutdownCallback: null,
+
+ startup() { },
+ shutdown() {
+ this.hasShutdown = true;
+ shutdownOrder.push(this.name);
+ if (this.shutdownCallback)
+ return this.shutdownCallback();
+ return undefined;
+ },
+ getAddonByID(id, callback) {
+ if (this.hasShutdown) {
+ this.unsafeAccess = true;
+ }
+ callback(null);
+ },
+
+ get name() {
+ return name;
+ },
+ };
+
+ return mockProvider;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* unsafeProviderShutdown() {
+ let firstProvider = mockAddonProvider("Mock1");
+ AddonManagerPrivate.registerProvider(firstProvider);
+ let secondProvider = mockAddonProvider("Mock2");
+ AddonManagerPrivate.registerProvider(secondProvider);
+
+ startupManager();
+
+ let shutdownPromise = null;
+ yield new Promise(resolve => {
+ secondProvider.shutdownCallback = function() {
+ return new Promise(shutdownResolve => {
+ AddonManager.getAddonByID("does-not-exist", () => {
+ shutdownResolve();
+ resolve();
+ });
+ });
+ };
+
+ shutdownPromise = promiseShutdownManager();
+ });
+ yield shutdownPromise;
+
+ equal(shutdownOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have shutdown in expected order");
+ ok(!firstProvider.unsafeAccess, "First registered mock provider should not have been accessed unsafely");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_startup.js b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_startup.js
new file mode 100644
index 000000000..1193ddfe4
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_startup.js
@@ -0,0 +1,55 @@
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+var startupOrder = [];
+
+function mockAddonProvider(name) {
+ let mockProvider = {
+ hasStarted: false,
+ unsafeAccess: false,
+
+ startupCallback: null,
+
+ startup() {
+ this.hasStarted = true;
+ startupOrder.push(this.name);
+ if (this.startupCallback)
+ this.startupCallback();
+ },
+ getAddonByID(id, callback) {
+ if (!this.hasStarted) {
+ this.unsafeAccess = true;
+ }
+ callback(null);
+ },
+
+ get name() {
+ return name;
+ },
+ };
+
+ return mockProvider;
+}
+
+function run_test() {
+ run_next_test();
+}
+
+add_task(function* unsafeProviderStartup() {
+ let secondProvider = null;
+
+ yield new Promise(resolve => {
+ let firstProvider = mockAddonProvider("Mock1");
+ firstProvider.startupCallback = function() {
+ AddonManager.getAddonByID("does-not-exist", resolve);
+ };
+ AddonManagerPrivate.registerProvider(firstProvider);
+
+ secondProvider = mockAddonProvider("Mock2");
+ AddonManagerPrivate.registerProvider(secondProvider);
+
+ startupManager();
+ });
+
+ equal(startupOrder.join(","), ["Mock1", "Mock2"].join(","), "Mock providers should have hasStarted in expected order");
+ ok(!secondProvider.unsafeAccess, "Second registered mock provider should not have been accessed unsafely");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_proxies.js b/toolkit/mozapps/webextensions/test/xpcshell/test_proxies.js
new file mode 100644
index 000000000..7b28c78f2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_proxies.js
@@ -0,0 +1,240 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests the semantics of extension proxy files and symlinks
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+Components.utils.import("resource://gre/modules/osfile.jsm");
+
+var ADDONS = [
+ {
+ id: "proxy1@tests.mozilla.org",
+ dirId: "proxy1@tests.mozilla.com",
+ type: "proxy"
+ },
+ {
+ id: "proxy2@tests.mozilla.org",
+ type: "proxy"
+ },
+ {
+ id: "symlink1@tests.mozilla.org",
+ dirId: "symlink1@tests.mozilla.com",
+ type: "symlink"
+ },
+ {
+ id: "symlink2@tests.mozilla.org",
+ type: "symlink"
+ }
+];
+
+var METADATA = {
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+}
+
+const ios = AM_Cc["@mozilla.org/network/io-service;1"].getService(AM_Ci.nsIIOService);
+
+const LocalFile = Components.Constructor("@mozilla.org/file/local;1",
+ "nsILocalFile", "initWithPath");
+const Process = Components.Constructor("@mozilla.org/process/util;1",
+ "nsIProcess", "init");
+
+const gHaveSymlinks = AppConstants.platform != "win";
+
+
+function createSymlink(aSource, aDest) {
+ if (aSource instanceof AM_Ci.nsIFile)
+ aSource = aSource.path;
+ if (aDest instanceof AM_Ci.nsIFile)
+ aDest = aDest.path;
+
+ return OS.File.unixSymLink(aSource, aDest);
+}
+
+function writeFile(aData, aFile) {
+ if (!aFile.parent.exists())
+ aFile.parent.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+ var fos = AM_Cc["@mozilla.org/network/file-output-stream;1"].
+ createInstance(AM_Ci.nsIFileOutputStream);
+ fos.init(aFile,
+ FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE | FileUtils.MODE_TRUNCATE,
+ FileUtils.PERMS_FILE, 0);
+ fos.write(aData, aData.length);
+ fos.close();
+}
+
+function checkAddonsExist() {
+ for (let addon of ADDONS) {
+ let file = addon.directory.clone();
+ file.append("install.rdf");
+ do_check_true(file.exists(), Components.stack.caller);
+ }
+}
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+ add_task(run_proxy_tests);
+
+ if (gHaveSymlinks)
+ add_task(run_symlink_tests);
+
+ run_next_test();
+}
+
+function* run_proxy_tests() {
+ if (!gHaveSymlinks) {
+ ADDONS = ADDONS.filter(a => a.type != "symlink");
+ }
+
+ for (let addon of ADDONS) {
+ addon.directory = gTmpD.clone();
+ addon.directory.append(addon.id);
+
+ addon.proxyFile = profileDir.clone();
+ addon.proxyFile.append(addon.dirId || addon.id);
+
+ METADATA.id = addon.id;
+ METADATA.name = addon.id;
+ writeInstallRDFToDir(METADATA, gTmpD);
+
+ if (addon.type == "proxy") {
+ writeFile(addon.directory.path, addon.proxyFile)
+ }
+ else if (addon.type == "symlink") {
+ yield createSymlink(addon.directory, addon.proxyFile)
+ }
+ }
+
+ startupManager();
+
+ // Check that all add-ons original sources still exist after invalid
+ // add-ons have been removed at startup.
+ checkAddonsExist();
+
+ return new Promise(resolve => {
+ AddonManager.getAddonsByIDs(ADDONS.map(addon => addon.id), resolve);
+ }).then(addons => {
+ try {
+ for (let [i, addon] of addons.entries()) {
+ // Ensure that valid proxied add-ons were installed properly on
+ // platforms that support the installation method.
+ print(ADDONS[i].id,
+ ADDONS[i].dirId,
+ ADDONS[i].dirId != null,
+ ADDONS[i].type == "symlink");
+ do_check_eq(addon == null,
+ ADDONS[i].dirId != null);
+
+ if (addon != null) {
+ let fixURL = url => {
+ if (AppConstants.platform == "macosx")
+ return url.replace(RegExp(`^file:///private/`), "file:///");
+ return url;
+ };
+
+ // Check that proxied add-ons do not have upgrade permissions.
+ do_check_eq(addon.permissions & AddonManager.PERM_CAN_UPGRADE, 0);
+
+ // Check that getResourceURI points to the right place.
+ do_check_eq(ios.newFileURI(ADDONS[i].directory).spec,
+ fixURL(addon.getResourceURI().spec),
+ `Base resource URL resolves as expected`);
+
+ let file = ADDONS[i].directory.clone();
+ file.append("install.rdf");
+
+ do_check_eq(ios.newFileURI(file).spec,
+ fixURL(addon.getResourceURI("install.rdf").spec),
+ `Resource URLs resolve as expected`);
+
+ addon.uninstall();
+ }
+ }
+
+ // Check that original sources still exist after explicit uninstall.
+ restartManager();
+ checkAddonsExist();
+
+ shutdownManager();
+
+ // Check that all of the proxy files have been removed and remove
+ // the original targets.
+ for (let addon of ADDONS) {
+ equal(addon.proxyFile.exists(), addon.dirId != null,
+ `Proxy file ${addon.proxyFile.path} should exist?`);
+ addon.directory.remove(true);
+ try {
+ addon.proxyFile.remove(false);
+ } catch (e) {}
+ }
+ }
+ catch (e) {
+ do_throw(e);
+ }
+ });
+}
+
+function* run_symlink_tests() {
+ // Check that symlinks are not followed out of a directory tree
+ // when deleting an add-on.
+
+ METADATA.id = "unpacked@test.mozilla.org";
+ METADATA.name = METADATA.id;
+ METADATA.unpack = "true";
+
+ let tempDirectory = gTmpD.clone();
+ tempDirectory.append(METADATA.id);
+
+ let tempFile = tempDirectory.clone();
+ tempFile.append("test.txt");
+ tempFile.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+
+ let addonDirectory = profileDir.clone();
+ addonDirectory.append(METADATA.id);
+
+ writeInstallRDFToDir(METADATA, profileDir);
+
+ let symlink = addonDirectory.clone();
+ symlink.append(tempDirectory.leafName);
+ yield createSymlink(tempDirectory, symlink);
+
+ // Make sure that the symlink was created properly.
+ let file = symlink.clone();
+ file.append(tempFile.leafName);
+ file.normalize();
+ do_check_eq(file.path.replace(/^\/private\//, "/"), tempFile.path);
+
+ startupManager();
+
+ return new Promise(resolve => {
+ AddonManager.getAddonByID(METADATA.id, resolve);
+ }).then(addon => {
+ do_check_neq(addon, null);
+
+ addon.uninstall();
+
+ restartManager();
+ shutdownManager();
+
+ // Check that the install directory is gone.
+ do_check_false(addonDirectory.exists());
+
+ // Check that the temp file is not gone.
+ do_check_true(tempFile.exists());
+
+ tempDirectory.remove(true);
+ });
+}
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_proxy.js b/toolkit/mozapps/webextensions/test/xpcshell/test_proxy.js
new file mode 100644
index 000000000..c35870c9b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_proxy.js
@@ -0,0 +1,106 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "proxy1@tests.mozilla.org";
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+BootstrapMonitor.init();
+
+// Ensure that a proxy file to an add-on with a valid manifest works.
+add_task(function*() {
+ let tempdir = gTmpD.clone();
+ writeInstallRDFToDir({
+ id: ID,
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (proxy)",
+ }, tempdir, ID, "bootstrap.js");
+
+ let unpackedAddon = tempdir.clone();
+ unpackedAddon.append(ID);
+ do_get_file("data/test_proxy/bootstrap.js")
+ .copyTo(unpackedAddon, "bootstrap.js");
+
+ // create proxy file in profile/extensions dir
+ let extensionsDir = gProfD.clone();
+ extensionsDir.append("extensions");
+ let proxyFile = writeProxyFileToDir(extensionsDir, unpackedAddon, ID);
+
+ yield promiseRestartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1 (proxy)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ do_check_true(proxyFile.exists());
+
+ addon.uninstall();
+ unpackedAddon.remove(true);
+
+ yield promiseRestartManager();
+});
+
+
+// Ensure that a proxy file to an add-on is not removed even
+// if the manifest file is invalid. See bug 1195353.
+add_task(function*() {
+ let tempdir = gTmpD.clone();
+
+ // use a mismatched ID to make this install.rdf invalid
+ writeInstallRDFToDir({
+ id: "bad-proxy1@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (proxy)",
+ }, tempdir, ID, "bootstrap.js");
+
+ let unpackedAddon = tempdir.clone();
+ unpackedAddon.append(ID);
+ do_get_file("data/test_proxy/bootstrap.js")
+ .copyTo(unpackedAddon, "bootstrap.js");
+
+ // create proxy file in profile/extensions dir
+ let extensionsDir = gProfD.clone();
+ extensionsDir.append("extensions");
+ let proxyFile = writeProxyFileToDir(extensionsDir, unpackedAddon, ID);
+
+ yield promiseRestartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ do_check_true(proxyFile.exists());
+
+ unpackedAddon.remove(true);
+ proxyFile.remove(true);
+
+ yield promiseRestartManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_registry.js b/toolkit/mozapps/webextensions/test/xpcshell/test_registry.js
new file mode 100644
index 000000000..eab7af6b6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_registry.js
@@ -0,0 +1,158 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests that extensions installed through the registry work as expected
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+const addon1Dir = writeInstallRDFForExtension(addon1, gProfD, "addon1");
+const addon2Dir = writeInstallRDFForExtension(addon2, gProfD, "addon2");
+
+let registry;
+
+function run_test() {
+ // This test only works where there is a registry.
+ if (!("nsIWindowsRegKey" in AM_Ci))
+ return;
+
+ registry = new MockRegistry();
+ do_register_cleanup(() => {
+ registry.shutdown();
+ });
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+// Tests whether basic registry install works
+function run_test_1() {
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon2Dir.path);
+
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a1.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Tests whether uninstalling from the registry works
+function run_test_2() {
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", null);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Checks that the ID in the registry must match that in the install manifest
+function run_test_3() {
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon2Dir.path);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon1Dir.path);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Tests whether an extension's ID can change without its directory changing
+function run_test_4() {
+ // Restarting with bad items in the registry should not force an EM restart
+ restartManager();
+
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", null);
+
+ restartManager();
+
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", addon1Dir.path);
+ restartManager();
+
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon1@tests.mozilla.org", null);
+ registry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
+ "SOFTWARE\\Mozilla\\XPCShell\\Extensions",
+ "addon2@tests.mozilla.org", addon1Dir.path);
+ writeInstallRDFForExtension(addon2, gProfD, "addon1");
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"], function([a1, a2]) {
+ do_check_eq(a1, null);
+ do_check_neq(a2, null);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_reload.js b/toolkit/mozapps/webextensions/test/xpcshell/test_reload.js
new file mode 100644
index 000000000..5873d1980
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_reload.js
@@ -0,0 +1,235 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+const sampleAddon = {
+ id: "webextension1@tests.mozilla.org",
+ name: "webextension_1",
+}
+
+const manifestSample = {
+ id: "bootstrap1@tests.mozilla.org",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+};
+
+const { Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseAddonStartup() {
+ return new Promise(resolve => {
+ let listener = (extension) => {
+ Management.off("startup", listener);
+ resolve(extension);
+ };
+
+ Management.on("startup", listener);
+ });
+}
+
+function* installAddon(fixtureName, addonID) {
+ yield promiseInstallAllFiles([do_get_addon(fixtureName)]);
+ return promiseAddonByID(addonID);
+}
+
+function* tearDownAddon(addon) {
+ addon.uninstall();
+ yield promiseShutdownManager();
+}
+
+add_task(function* test_reloading_a_temp_addon() {
+ yield promiseRestartManager();
+ yield AddonManager.installTemporaryAddon(do_get_addon(sampleAddon.name));
+ const addon = yield promiseAddonByID(sampleAddon.id)
+
+ var receivedOnUninstalled = false;
+ var receivedOnUninstalling = false;
+ var receivedOnInstalled = false;
+ var receivedOnInstalling = false;
+
+ const onReload = new Promise(resolve => {
+ const listener = {
+ onUninstalling: (addonObj) => {
+ if (addonObj.id === sampleAddon.id) {
+ receivedOnUninstalling = true;
+ }
+ },
+ onUninstalled: (addonObj) => {
+ if (addonObj.id === sampleAddon.id) {
+ receivedOnUninstalled = true;
+ }
+ },
+ onInstalling: (addonObj) => {
+ receivedOnInstalling = true;
+ equal(addonObj.id, sampleAddon.id);
+ },
+ onInstalled: (addonObj) => {
+ receivedOnInstalled = true;
+ equal(addonObj.id, sampleAddon.id);
+ // This should be the last event called.
+ AddonManager.removeAddonListener(listener);
+ resolve();
+ },
+ }
+ AddonManager.addAddonListener(listener);
+ });
+
+ yield addon.reload();
+ yield onReload;
+
+ // Make sure reload() doesn't trigger uninstall events.
+ equal(receivedOnUninstalled, false, "reload should not trigger onUninstalled");
+ equal(receivedOnUninstalling, false, "reload should not trigger onUninstalling");
+
+ // Make sure reload() triggers install events, like an upgrade.
+ equal(receivedOnInstalling, true, "reload should trigger onInstalling");
+ equal(receivedOnInstalled, true, "reload should trigger onInstalled");
+
+ yield tearDownAddon(addon);
+});
+
+add_task(function* test_can_reload_permanent_addon() {
+ yield promiseRestartManager();
+ const addon = yield installAddon(sampleAddon.name, sampleAddon.id);
+
+ let disabledCalled = false;
+ let enabledCalled = false;
+ AddonManager.addAddonListener({
+ onDisabled: (aAddon) => {
+ do_check_false(enabledCalled);
+ disabledCalled = true
+ },
+ onEnabled: (aAddon) => {
+ do_check_true(disabledCalled);
+ enabledCalled = true
+ }
+ })
+
+ yield addon.reload();
+
+ do_check_true(disabledCalled);
+ do_check_true(enabledCalled);
+
+ notEqual(addon, null);
+ equal(addon.appDisabled, false);
+ equal(addon.userDisabled, false);
+
+ yield tearDownAddon(addon);
+});
+
+add_task(function* test_reload_to_invalid_version_fails() {
+ yield promiseRestartManager();
+ let tempdir = gTmpD.clone();
+
+ // The initial version of the add-on will be compatible, and will therefore load
+ const addonId = "invalid_version_cannot_be_reloaded@tests.mozilla.org";
+ let manifest = {
+ name: "invalid_version_cannot_be_reloaded",
+ description: "test invalid_version_cannot_be_reloaded",
+ manifest_version: 2,
+ version: "1.0",
+ applications: {
+ gecko: {
+ id: addonId,
+ }
+ },
+ };
+
+ let addonDir = yield promiseWriteWebManifestForExtension(manifest, tempdir, "invalid_version");
+ yield AddonManager.installTemporaryAddon(addonDir);
+ yield promiseAddonStartup();
+
+ let addon = yield promiseAddonByID(addonId);
+ notEqual(addon, null);
+ equal(addon.id, addonId);
+ equal(addon.version, "1.0");
+ equal(addon.appDisabled, false);
+ equal(addon.userDisabled, false);
+ addonDir.remove(true);
+
+ // update the manifest to make the add-on version incompatible, so the reload will reject
+ manifest.applications.gecko.strict_min_version = "1";
+ manifest.applications.gecko.strict_max_version = "1";
+ manifest.version = "2.0";
+
+ addonDir = yield promiseWriteWebManifestForExtension(manifest, tempdir, "invalid_version", false);
+ let expectedMsg = new RegExp("Add-on invalid_version_cannot_be_reloaded@tests.mozilla.org is not compatible with application version. " +
+ "add-on minVersion: 1. add-on maxVersion: 1.");
+
+ yield Assert.rejects(addon.reload(),
+ expectedMsg,
+ "Reload rejects when application version does not fall between minVersion and maxVersion");
+
+ let reloadedAddon = yield promiseAddonByID(addonId);
+ notEqual(reloadedAddon, null);
+ equal(reloadedAddon.id, addonId);
+ equal(reloadedAddon.version, "1.0");
+ equal(reloadedAddon.appDisabled, false);
+ equal(reloadedAddon.userDisabled, false);
+
+ yield tearDownAddon(reloadedAddon);
+ addonDir.remove(true);
+});
+
+add_task(function* test_manifest_changes_are_refreshed() {
+ yield promiseRestartManager();
+ let tempdir = gTmpD.clone();
+
+ const unpackedAddon = writeInstallRDFToDir(
+ Object.assign({}, manifestSample, {
+ name: "Test Bootstrap 1",
+ }), tempdir, manifestSample.id, "bootstrap.js");
+
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+ const addon = yield promiseAddonByID(manifestSample.id);
+ notEqual(addon, null);
+ equal(addon.name, "Test Bootstrap 1");
+
+ writeInstallRDFToDir(Object.assign({}, manifestSample, {
+ name: "Test Bootstrap 1 (reloaded)",
+ }), tempdir, manifestSample.id);
+
+ yield addon.reload();
+
+ const reloadedAddon = yield promiseAddonByID(manifestSample.id);
+ notEqual(reloadedAddon, null);
+ equal(reloadedAddon.name, "Test Bootstrap 1 (reloaded)");
+
+ yield tearDownAddon(reloadedAddon);
+ unpackedAddon.remove(true);
+});
+
+add_task(function* test_reload_fails_on_installation_errors() {
+ yield promiseRestartManager();
+ let tempdir = gTmpD.clone();
+
+ const unpackedAddon = writeInstallRDFToDir(
+ Object.assign({}, manifestSample, {
+ name: "Test Bootstrap 1",
+ }), tempdir, manifestSample.id, "bootstrap.js");
+
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+ const addon = yield promiseAddonByID(manifestSample.id);
+ notEqual(addon, null);
+
+ // Trigger an installation error with an empty manifest.
+ writeInstallRDFToDir({}, tempdir, manifestSample.id);
+
+ yield Assert.rejects(addon.reload(), /No ID in install manifest/);
+
+ // The old add-on should be active. I.E. the broken reload will not
+ // disturb it.
+ const oldAddon = yield promiseAddonByID(manifestSample.id);
+ notEqual(oldAddon, null);
+ equal(oldAddon.isActive, true);
+ equal(oldAddon.name, "Test Bootstrap 1");
+
+ yield tearDownAddon(addon);
+ unpackedAddon.remove(true);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_safemode.js b/toolkit/mozapps/webextensions/test/xpcshell/test_safemode.js
new file mode 100644
index 000000000..05647f807
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_safemode.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// Tests that extensions behave correctly in safe mode
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ optionsURL: "chrome://foo/content/options.xul",
+ aboutURL: "chrome://foo/content/about.xul",
+ iconURL: "chrome://foo/content/icon.png",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gIconURL = null;
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ gAppInfo.inSafeMode = true;
+
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(a1) {
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ writeInstallRDFForExtension(addon1, profileDir, addon1.id, "icon.png");
+ gIconURL = do_get_addon_root_uri(profileDir.clone(), addon1.id) + "icon.png";
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(newa1) {
+ do_check_neq(newa1, null);
+ do_check_false(newa1.isActive);
+ do_check_false(newa1.userDisabled);
+ do_check_eq(newa1.aboutURL, null);
+ do_check_eq(newa1.optionsURL, null);
+ do_check_eq(newa1.iconURL, gIconURL);
+ do_check_true(isExtensionInAddonsList(profileDir, newa1.id));
+ do_check_true(hasFlag(newa1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(newa1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(newa1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ run_test_1();
+ });
+ }));
+}
+
+// Disabling an add-on should work
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_false(hasFlag(a1.operationsRequiringRestart,
+ AddonManager.OP_NEEDS_RESTART_DISABLE));
+ a1.userDisabled = true;
+ do_check_false(a1.isActive);
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ run_test_2();
+ });
+}
+
+// Enabling an add-on should happen without restart but not become active.
+function run_test_2() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ a1.userDisabled = false;
+ do_check_false(a1.isActive);
+ do_check_eq(a1.aboutURL, null);
+ do_check_eq(a1.optionsURL, null);
+ do_check_eq(a1.iconURL, gIconURL);
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(a1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_NONE);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_schema_change.js b/toolkit/mozapps/webextensions/test/xpcshell/test_schema_change.js
new file mode 100644
index 000000000..3d386f663
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_schema_change.js
@@ -0,0 +1,317 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+BootstrapMonitor.init();
+
+const PREF_DB_SCHEMA = "extensions.databaseSchema";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "49");
+startupManager();
+
+/**
+ * Schema change with no application update reloads metadata.
+ */
+add_task(function* schema_change() {
+ const ID = "schema-change@tests.mozilla.org";
+
+ let xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ yield promiseInstallAllFiles([xpiFile]);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ yield shutdownManager();
+
+ xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on 2",
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ Services.prefs.setIntPref(PREF_DB_SCHEMA, 0);
+
+ let file = profileDir.clone();
+ file.append(`${ID}.xpi`);
+
+ // Make sure the timestamp is unchanged, so it is not re-scanned for that reason.
+ let timestamp = file.lastModifiedTime;
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ file.lastModifiedTime = timestamp;
+
+ yield startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "2.0", "Got the expected version");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
+
+/**
+ * Application update with no schema change does not reload metadata.
+ */
+add_task(function* schema_change() {
+ const ID = "schema-change@tests.mozilla.org";
+
+ let xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ });
+
+ yield promiseInstallAllFiles([xpiFile]);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ yield shutdownManager();
+
+ xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on 2",
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ });
+
+ gAppInfo.version = "2";
+ let file = profileDir.clone();
+ file.append(`${ID}.xpi`);
+
+ // Make sure the timestamp is unchanged, so it is not re-scanned for that reason.
+ let timestamp = file.lastModifiedTime;
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ file.lastModifiedTime = timestamp;
+
+ yield startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
+
+/**
+ * App update and a schema change causes a reload of the manifest.
+ */
+add_task(function* schema_change_app_update() {
+ const ID = "schema-change@tests.mozilla.org";
+
+ let xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ });
+
+ yield promiseInstallAllFiles([xpiFile]);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ yield shutdownManager();
+
+ xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on 2",
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "3"
+ }]
+ });
+
+ gAppInfo.version = "3";
+ Services.prefs.setIntPref(PREF_DB_SCHEMA, 0);
+
+ let file = profileDir.clone();
+ file.append(`${ID}.xpi`);
+
+ // Make sure the timestamp is unchanged, so it is not re-scanned for that reason.
+ let timestamp = file.lastModifiedTime;
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ file.lastModifiedTime = timestamp;
+
+ yield startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.appDisabled, false);
+ equal(addon.version, "2.0", "Got the expected version");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
+
+/**
+ * No schema change, no manifest reload.
+ */
+add_task(function* schema_change() {
+ const ID = "schema-change@tests.mozilla.org";
+
+ let xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ yield promiseInstallAllFiles([xpiFile]);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ yield shutdownManager();
+
+ xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on 2",
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ let file = profileDir.clone();
+ file.append(`${ID}.xpi`);
+
+ // Make sure the timestamp is unchanged, so it is not re-scanned for that reason.
+ let timestamp = file.lastModifiedTime;
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ file.lastModifiedTime = timestamp;
+
+ yield startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
+
+/**
+ * Modified timestamp on the XPI causes a reload of the manifest.
+ */
+add_task(function* schema_change() {
+ const ID = "schema-change@tests.mozilla.org";
+
+ let xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ yield promiseInstallAllFiles([xpiFile]);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+
+ yield shutdownManager();
+
+ xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on 2",
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ });
+
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ let file = profileDir.clone();
+ file.append(`${ID}.xpi`);
+
+ // Set timestamp in the future so manifest is re-scanned.
+ let timestamp = new Date(Date.now() + 60000);
+ xpiFile.moveTo(profileDir, `${ID}.xpi`);
+
+ file.lastModifiedTime = timestamp;
+
+ yield startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "2.0", "Got the expected version");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_seen.js b/toolkit/mozapps/webextensions/test/xpcshell/test_seen.js
new file mode 100644
index 000000000..e499e7339
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_seen.js
@@ -0,0 +1,211 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+
+let profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+startupManager();
+
+// By default disable add-ons from the profile
+Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_PROFILE);
+
+// Installing an add-on through the API should mark it as seen
+add_task(function*() {
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ let addon = install.addon;
+ do_check_eq(addon.version, "1.0");
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ // Installing an update should retain that
+ install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ addon = install.addon;
+ do_check_eq(addon.version, "2.0");
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+});
+
+// Sideloading an add-on should mark it as unseen
+add_task(function*() {
+ let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+ // Make sure the startup code will detect sideloaded updates
+ setExtensionModifiedTime(path, Date.now() - 10000);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "1.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ yield promiseShutdownManager();
+
+ // Sideloading an update shouldn't change the state
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
+ setExtensionModifiedTime(path, Date.now());
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "2.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+});
+
+// Sideloading an add-on should mark it as unseen
+add_task(function*() {
+ let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+ // Make sure the startup code will detect sideloaded updates
+ setExtensionModifiedTime(path, Date.now() - 10000);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "1.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ // Updating through the API shouldn't change the state
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ addon = install.addon;
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "2.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+});
+
+// Sideloading an add-on should mark it as unseen
+add_task(function*() {
+ let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+ // Make sure the startup code will detect sideloaded updates
+ setExtensionModifiedTime(path, Date.now() - 10000);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "1.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+ addon.markAsSeen();
+ do_check_true(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ yield promiseShutdownManager();
+
+ // Sideloading an update shouldn't change the state
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
+ setExtensionModifiedTime(path, Date.now());
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "2.0");
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+});
+
+// Sideloading an add-on should mark it as unseen
+add_task(function*() {
+ let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
+ // Make sure the startup code will detect sideloaded updates
+ setExtensionModifiedTime(path, Date.now() - 10000);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "1.0");
+ do_check_true(addon.foreignInstall);
+ do_check_false(addon.seen);
+ addon.markAsSeen();
+ do_check_true(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ // Updating through the API shouldn't change the state
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
+
+ addon = install.addon;
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_eq(addon.version, "2.0");
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_seen_newprofile.js b/toolkit/mozapps/webextensions/test/xpcshell/test_seen_newprofile.js
new file mode 100644
index 000000000..43ee18594
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_seen_newprofile.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_SYSTEM);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+BootstrapMonitor.init();
+
+const globalDir = gProfD.clone();
+globalDir.append("extensions2");
+globalDir.append(gAppInfo.ID);
+registerDirectory("XRESysSExtPD", globalDir.parent);
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// By default disable add-ons from the system
+Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_SYSTEM);
+
+// When new add-ons already exist in a system location when starting with a new
+// profile they should be marked as already seen.
+add_task(function*() {
+ manuallyInstall(do_get_addon("test_bootstrap1_1"), globalDir, ID);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_true(addon.foreignInstall);
+ do_check_true(addon.seen);
+ do_check_true(addon.userDisabled);
+ do_check_false(addon.isActive);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_shutdown.js b/toolkit/mozapps/webextensions/test/xpcshell/test_shutdown.js
new file mode 100644
index 000000000..725887bc1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_shutdown.js
@@ -0,0 +1,85 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Verify that API functions fail if the Add-ons Manager isn't initialised.
+
+const IGNORE = ["getPreferredIconURL", "escapeAddonURI",
+ "shouldAutoUpdate", "getStartupChanges",
+ "addTypeListener", "removeTypeListener",
+ "addAddonListener", "removeAddonListener",
+ "addInstallListener", "removeInstallListener",
+ "addManagerListener", "removeManagerListener",
+ "mapURIToAddonID", "shutdown", "init",
+ "stateToString", "errorToString", "getUpgradeListener",
+ "addUpgradeListener", "removeUpgradeListener"];
+
+const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
+ "AddonScreenshot", "AddonType", "startup", "shutdown",
+ "registerProvider", "unregisterProvider",
+ "addStartupChange", "removeStartupChange",
+ "recordTimestamp", "recordSimpleMeasure",
+ "recordException", "getSimpleMeasures", "simpleTimer",
+ "setTelemetryDetails", "getTelemetryDetails",
+ "callNoUpdateListeners", "backgroundUpdateTimerHandler",
+ "hasUpgradeListener", "getUpgradeListener"];
+
+function test_functions() {
+ for (let prop in AddonManager) {
+ if (IGNORE.indexOf(prop) != -1)
+ continue;
+ if (typeof AddonManager[prop] != "function")
+ continue;
+
+ let args = [];
+
+ // Getter functions need a callback and in some cases not having one will
+ // throw before checking if the add-ons manager is initialized so pass in
+ // an empty one.
+ if (prop.startsWith("get")) {
+ // For now all getter functions with more than one argument take the
+ // callback in the second argument.
+ if (AddonManager[prop].length > 1) {
+ args.push(undefined, () => {});
+ }
+ else {
+ args.push(() => {});
+ }
+ }
+
+ try {
+ do_print("AddonManager." + prop);
+ AddonManager[prop](...args);
+ do_throw(prop + " did not throw an exception");
+ }
+ catch (e) {
+ if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
+ do_throw(prop + " threw an unexpected exception: " + e);
+ }
+ }
+
+ for (let prop in AddonManagerPrivate) {
+ if (typeof AddonManagerPrivate[prop] != "function")
+ continue;
+ if (IGNORE_PRIVATE.indexOf(prop) != -1)
+ continue;
+
+ try {
+ do_print("AddonManagerPrivate." + prop);
+ AddonManagerPrivate[prop]();
+ do_throw(prop + " did not throw an exception");
+ }
+ catch (e) {
+ if (e.result != Components.results.NS_ERROR_NOT_INITIALIZED)
+ do_throw(prop + " threw an unexpected exception: " + e);
+ }
+ }
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ test_functions();
+ startupManager();
+ shutdownManager();
+ test_functions();
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_inject.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_inject.js
new file mode 100644
index 000000000..3b96f40ba
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_inject.js
@@ -0,0 +1,382 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+const DATA = "data/signing_checks/";
+const ADDONS = {
+ bootstrap: {
+ unsigned: "unsigned_bootstrap_2.xpi",
+ badid: "signed_bootstrap_badid_2.xpi",
+ signed: "signed_bootstrap_2.xpi",
+ preliminary: "preliminary_bootstrap_2.xpi",
+ },
+ nonbootstrap: {
+ unsigned: "unsigned_nonbootstrap_2.xpi",
+ badid: "signed_nonbootstrap_badid_2.xpi",
+ signed: "signed_nonbootstrap_2.xpi",
+ }
+};
+const ID = "test@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Deletes a file from the test add-on in the profile
+function breakAddon(file) {
+ if (TEST_UNPACKED) {
+ file.append("test.txt");
+ file.remove(true);
+ }
+ else {
+ var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+ createInstance(AM_Ci.nsIZipWriter);
+ zipW.open(file, FileUtils.MODE_RDWR | FileUtils.MODE_APPEND);
+ zipW.removeEntry("test.txt", false);
+ zipW.close();
+ }
+}
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.install_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", -1);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", -1);
+}
+
+function clearCache(file) {
+ if (TEST_UNPACKED)
+ return;
+
+ Services.obs.notifyObservers(file, "flush-cache-entry", null);
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+
+ // Start and stop the manager to initialise everything in the profile before
+ // actual testing
+ startupManager();
+ shutdownManager();
+ resetPrefs();
+
+ run_next_test();
+}
+
+// Injecting into profile (bootstrap)
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.unsigned), profileDir, ID);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+ do_check_eq(getActiveVersion(), -1);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.signed), profileDir, ID);
+ breakAddon(file);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_eq(getActiveVersion(), -1);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.badid), profileDir, ID);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_eq(getActiveVersion(), -1);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+// Installs a signed add-on then modifies it in place breaking its signing
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.signed), profileDir, ID);
+
+ // Make it appear to come from the past so when we modify it later it is
+ // detected during startup. Obviously malware can bypass this method of
+ // detection but the periodic scan will catch that
+ yield promiseSetExtensionModifiedTime(file.path, Date.now() - 600000);
+
+ startupManager();
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
+ do_check_eq(getActiveVersion(), 2);
+
+ yield promiseShutdownManager();
+ do_check_eq(getActiveVersion(), 0);
+
+ clearCache(file);
+ breakAddon(file);
+ resetPrefs();
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_eq(getActiveVersion(), -1);
+
+ let ids = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
+ do_check_eq(ids.length, 1);
+ do_check_eq(ids[0], ID);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+// Injecting into profile (non-bootstrap)
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.nonbootstrap.unsigned), profileDir, ID);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+ do_check_false(isExtensionInAddonsList(profileDir, ID));
+
+ addon.uninstall();
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.nonbootstrap.signed), profileDir, ID);
+ breakAddon(file);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_false(isExtensionInAddonsList(profileDir, ID));
+
+ addon.uninstall();
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.nonbootstrap.badid), profileDir, ID);
+
+ startupManager();
+
+ // Currently we leave the sideloaded add-on there but just don't run it
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_false(isExtensionInAddonsList(profileDir, ID));
+
+ addon.uninstall();
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+// Installs a signed add-on then modifies it in place breaking its signing
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.nonbootstrap.signed), profileDir, ID);
+
+ // Make it appear to come from the past so when we modify it later it is
+ // detected during startup. Obviously malware can bypass this method of
+ // detection but the periodic scan will catch that
+ yield promiseSetExtensionModifiedTime(file.path, Date.now() - 60000);
+
+ startupManager();
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SIGNED);
+ do_check_true(isExtensionInAddonsList(profileDir, ID));
+
+ yield promiseShutdownManager();
+
+ clearCache(file);
+ breakAddon(file);
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_BROKEN);
+ do_check_false(isExtensionInAddonsList(profileDir, ID));
+
+ let ids = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
+ do_check_eq(ids.length, 1);
+ do_check_eq(ids[0], ID);
+
+ addon.uninstall();
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+// Stage install then modify before startup (non-bootstrap)
+add_task(function*() {
+ startupManager();
+ yield promiseInstallAllFiles([do_get_file(DATA + ADDONS.nonbootstrap.signed)]);
+ yield promiseShutdownManager();
+
+ let staged = profileDir.clone();
+ staged.append("staged");
+ staged.append(do_get_expected_addon_name(ID));
+ do_check_true(staged.exists());
+
+ breakAddon(staged);
+ startupManager();
+
+ // Should have refused to install the broken staged version
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ clearCache(staged);
+
+ yield promiseShutdownManager();
+});
+
+// Manufacture staged install (bootstrap)
+add_task(function*() {
+ let stage = profileDir.clone();
+ stage.append("staged");
+
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.signed), stage, ID);
+ breakAddon(file);
+
+ startupManager();
+
+ // Should have refused to install the broken staged version
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+ do_check_eq(getActiveVersion(), -1);
+
+ do_check_false(file.exists());
+ clearCache(file);
+
+ yield promiseShutdownManager();
+ resetPrefs();
+});
+
+// Preliminarily-signed sideloaded add-ons should work
+add_task(function*() {
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.preliminary), profileDir, ID);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
+ do_check_eq(getActiveVersion(), 2);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
+
+// Preliminarily-signed sideloaded add-ons should work via staged install
+add_task(function*() {
+ let stage = profileDir.clone();
+ stage.append("staged");
+
+ let file = manuallyInstall(do_get_file(DATA + ADDONS.bootstrap.preliminary), stage, ID);
+
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_PRELIMINARY);
+ do_check_eq(getActiveVersion(), 2);
+
+ addon.uninstall();
+ yield promiseShutdownManager();
+ resetPrefs();
+
+ do_check_false(file.exists());
+ clearCache(file);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_install.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_install.js
new file mode 100644
index 000000000..19b07ac16
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_install.js
@@ -0,0 +1,265 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+const DATA = "data/signing_checks/";
+const ADDONS = {
+ bootstrap: {
+ unsigned: "unsigned_bootstrap_2.xpi",
+ badid: "signed_bootstrap_badid_2.xpi",
+ preliminary: "preliminary_bootstrap_2.xpi",
+ signed: "signed_bootstrap_2.xpi",
+ },
+};
+const WORKING = "signed_bootstrap_1.xpi";
+const ID = "test@tests.mozilla.org";
+
+var gServer = createHttpServer(4444);
+
+// Creates an add-on with a broken signature by changing an existing file
+function createBrokenAddonModify(file) {
+ let brokenFile = gTmpD.clone();
+ brokenFile.append("broken.xpi");
+ file.copyTo(brokenFile.parent, brokenFile.leafName);
+
+ var stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
+ createInstance(AM_Ci.nsIStringInputStream);
+ stream.setData("FOOBAR", -1);
+ var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+ createInstance(AM_Ci.nsIZipWriter);
+ zipW.open(brokenFile, FileUtils.MODE_RDWR | FileUtils.MODE_APPEND);
+ zipW.removeEntry("test.txt", false);
+ zipW.addEntryStream("test.txt", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
+ stream, false);
+ zipW.close();
+
+ return brokenFile;
+}
+
+// Creates an add-on with a broken signature by adding a new file
+function createBrokenAddonAdd(file) {
+ let brokenFile = gTmpD.clone();
+ brokenFile.append("broken.xpi");
+ file.copyTo(brokenFile.parent, brokenFile.leafName);
+
+ var stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
+ createInstance(AM_Ci.nsIStringInputStream);
+ stream.setData("FOOBAR", -1);
+ var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+ createInstance(AM_Ci.nsIZipWriter);
+ zipW.open(brokenFile, FileUtils.MODE_RDWR | FileUtils.MODE_APPEND);
+ zipW.addEntryStream("test2.txt", 0, AM_Ci.nsIZipWriter.COMPRESSION_NONE,
+ stream, false);
+ zipW.close();
+
+ return brokenFile;
+}
+
+// Creates an add-on with a broken signature by removing an existing file
+function createBrokenAddonRemove(file) {
+ let brokenFile = gTmpD.clone();
+ brokenFile.append("broken.xpi");
+ file.copyTo(brokenFile.parent, brokenFile.leafName);
+
+ var stream = AM_Cc["@mozilla.org/io/string-input-stream;1"].
+ createInstance(AM_Ci.nsIStringInputStream);
+ stream.setData("FOOBAR", -1);
+ var zipW = AM_Cc["@mozilla.org/zipwriter;1"].
+ createInstance(AM_Ci.nsIZipWriter);
+ zipW.open(brokenFile, FileUtils.MODE_RDWR | FileUtils.MODE_APPEND);
+ zipW.removeEntry("test.txt", false);
+ zipW.close();
+
+ return brokenFile;
+}
+
+function createInstall(url) {
+ return new Promise(resolve => {
+ AddonManager.getInstallForURL(url, resolve, "application/x-xpinstall");
+ });
+}
+
+function serveUpdateRDF(leafName) {
+ gServer.registerPathHandler("/update.rdf", function(request, response) {
+ let updateData = {};
+ updateData[ID] = [{
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "4",
+ maxVersion: "6",
+ updateLink: "http://localhost:4444/" + leafName
+ }]
+ }];
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.write(createUpdateRDF(updateData));
+ });
+}
+
+
+function* test_install_broken(file, expectedError) {
+ gServer.registerFile("/" + file.leafName, file);
+
+ let install = yield createInstall("http://localhost:4444/" + file.leafName);
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, expectedError);
+ do_check_eq(install.addon, null);
+
+ gServer.registerFile("/" + file.leafName, null);
+}
+
+function* test_install_working(file, expectedSignedState) {
+ gServer.registerFile("/" + file.leafName, file);
+
+ let install = yield createInstall("http://localhost:4444/" + file.leafName);
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.addon, null);
+ do_check_eq(install.addon.signedState, expectedSignedState);
+
+ gServer.registerFile("/" + file.leafName, null);
+
+ install.addon.uninstall();
+}
+
+function* test_update_broken(file, expectedError) {
+ // First install the older version
+ yield promiseInstallAllFiles([do_get_file(DATA + WORKING)]);
+
+ gServer.registerFile("/" + file.leafName, file);
+ serveUpdateRDF(file.leafName);
+
+ let addon = yield promiseAddonByID(ID);
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, expectedError);
+ do_check_eq(install.addon, null);
+
+ gServer.registerFile("/" + file.leafName, null);
+ gServer.registerPathHandler("/update.rdf", null);
+
+ addon.uninstall();
+}
+
+function* test_update_working(file, expectedSignedState) {
+ // First install the older version
+ yield promiseInstallAllFiles([do_get_file(DATA + WORKING)]);
+
+ gServer.registerFile("/" + file.leafName, file);
+ serveUpdateRDF(file.leafName);
+
+ let addon = yield promiseAddonByID(ID);
+ let update = yield promiseFindAddonUpdates(addon);
+ let install = update.updateAvailable;
+ yield promiseCompleteAllInstalls([install]);
+
+ do_check_eq(install.state, AddonManager.STATE_INSTALLED);
+ do_check_neq(install.addon, null);
+ do_check_eq(install.addon.signedState, expectedSignedState);
+
+ gServer.registerFile("/" + file.leafName, null);
+ gServer.registerPathHandler("/update.rdf", null);
+
+ install.addon.uninstall();
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+ startupManager();
+
+ run_next_test();
+}
+
+// Try to install a broken add-on
+add_task(function*() {
+ let file = createBrokenAddonModify(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_install_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+add_task(function*() {
+ let file = createBrokenAddonAdd(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_install_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+add_task(function*() {
+ let file = createBrokenAddonRemove(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_install_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+// Try to install an add-on with an incorrect ID
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.badid);
+ yield test_install_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+});
+
+// Try to install an unsigned add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.unsigned);
+ yield test_install_broken(file, AddonManager.ERROR_SIGNEDSTATE_REQUIRED);
+});
+
+// Try to install a preliminarily reviewed add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.preliminary);
+ yield test_install_working(file, AddonManager.SIGNEDSTATE_PRELIMINARY);
+});
+
+// Try to install a signed add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.signed);
+ yield test_install_working(file, AddonManager.SIGNEDSTATE_SIGNED);
+});
+
+// Try to update to a broken add-on
+add_task(function*() {
+ let file = createBrokenAddonModify(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_update_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+add_task(function*() {
+ let file = createBrokenAddonAdd(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_update_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+add_task(function*() {
+ let file = createBrokenAddonRemove(do_get_file(DATA + ADDONS.bootstrap.signed));
+ yield test_update_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+ file.remove(true);
+});
+
+// Try to update to an add-on with an incorrect ID
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.badid);
+ yield test_update_broken(file, AddonManager.ERROR_CORRUPT_FILE);
+});
+
+// Try to update to an unsigned add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.unsigned);
+ yield test_update_broken(file, AddonManager.ERROR_SIGNEDSTATE_REQUIRED);
+});
+
+// Try to update to a preliminarily reviewed add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.preliminary);
+ yield test_update_working(file, AddonManager.SIGNEDSTATE_PRELIMINARY);
+});
+
+// Try to update to a signed add-on
+add_task(function*() {
+ let file = do_get_file(DATA + ADDONS.bootstrap.signed);
+ yield test_update_working(file, AddonManager.SIGNEDSTATE_SIGNED);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_long.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_long.js
new file mode 100644
index 000000000..b74d7804a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_long.js
@@ -0,0 +1,49 @@
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+gUseRealCertChecks = true;
+
+const DATA = "data/signing_checks/";
+
+const ID_63 = "123456789012345678901234567890123456789012345@tests.mozilla.org"
+const ID_64 = "1234567890123456789012345678901234567890123456@tests.mozilla.org"
+const ID_65 = "12345678901234567890123456789012345678901234568@tests.mozilla.org"
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ startupManager();
+
+ run_next_test();
+}
+
+// Installs the cases that should be working
+add_task(function* test_working() {
+ yield promiseInstallAllFiles([do_get_file(DATA + "long_63_plain.xpi"),
+ do_get_file(DATA + "long_64_plain.xpi"),
+ do_get_file(DATA + "long_65_hash.xpi")]);
+
+ let addons = yield promiseAddonsByIDs([ID_63, ID_64, ID_65]);
+
+ for (let addon of addons) {
+ do_check_neq(addon, null);
+ do_check_true(addon.signedState > AddonManager.SIGNEDSTATE_MISSING);
+
+ addon.uninstall();
+ }
+});
+
+// Checks the cases that should be broken
+add_task(function* test_broken() {
+ function promiseInstallForFile(file) {
+ return new Promise(resolve => AddonManager.getInstallForFile(file, resolve));
+ }
+
+ let promises = [promiseInstallForFile(do_get_file(DATA + "long_63_hash.xpi")),
+ promiseInstallForFile(do_get_file(DATA + "long_64_hash.xpi"))];
+ let installs = yield Promise.all(promises);
+
+ for (let install of installs) {
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_migrate.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_migrate.js
new file mode 100644
index 000000000..97e2ff79f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_migrate.js
@@ -0,0 +1,194 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+// Allow attempting to show the compatibility UI which should not happen
+Services.prefs.setBoolPref("extensions.showMismatchUI", true);
+
+const DATA = "data/signing_checks/";
+const ADDONS = {
+ bootstrap: {
+ unsigned: "unsigned_bootstrap_2.xpi",
+ badid: "signed_bootstrap_badid_2.xpi",
+ signed: "signed_bootstrap_2.xpi",
+ },
+ nonbootstrap: {
+ unsigned: "unsigned_nonbootstrap_2.xpi",
+ badid: "signed_nonbootstrap_badid_2.xpi",
+ signed: "signed_nonbootstrap_2.xpi",
+ }
+};
+const ID = "test@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Override the window watcher
+var WindowWatcher = {
+ sawAddon: false,
+
+ openWindow: function(parent, url, name, features, args) {
+ let ids = args.QueryInterface(AM_Ci.nsIVariant);
+ this.sawAddon = ids.indexOf(ID) >= 0;
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(AM_Ci.nsIWindowWatcher)
+ || iid.equals(AM_Ci.nsISupports))
+ return this;
+
+ throw Components.results.NS_ERROR_NO_INTERFACE;
+ }
+}
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+function resetPrefs() {
+ Services.prefs.setIntPref("bootstraptest.active_version", -1);
+ Services.prefs.setIntPref("bootstraptest.installed_version", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.install_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
+ Services.prefs.setIntPref("bootstraptest.startup_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.shutdown_newversion", -1);
+ Services.prefs.setIntPref("bootstraptest.install_oldversion", -1);
+ Services.prefs.setIntPref("bootstraptest.uninstall_newversion", -1);
+}
+
+function getActiveVersion() {
+ return Services.prefs.getIntPref("bootstraptest.active_version");
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+
+ // Start and stop the manager to initialise everything in the profile before
+ // actual testing
+ startupManager();
+ shutdownManager();
+ resetPrefs();
+
+ run_next_test();
+}
+
+// Removes the signedState field from add-ons in the json database to make it
+// look like the database was written with an older version of the application
+function stripDB() {
+ let jData = loadJSON(gExtensionsJSON);
+ jData.schemaVersion--;
+
+ for (let addon of jData.addons)
+ delete addon.signedState;
+
+ saveJSON(jData, gExtensionsJSON);
+}
+
+function* test_breaking_migrate(addons, test, expectedSignedState) {
+ // Startup as the old version
+ gAppInfo.version = "4";
+ startupManager(true);
+
+ // Install the signed add-on
+ yield promiseInstallAllFiles([do_get_file(DATA + addons.signed)]);
+ // Restart to let non-restartless add-ons install fully
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+ resetPrefs();
+ stripDB();
+
+ // Now replace it with the version to test. Doing this so quickly shouldn't
+ // trigger the file modification code to detect the change by itself.
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_file(DATA + addons[test]), profileDir, ID);
+
+ // Update the application
+ gAppInfo.version = "5";
+ startupManager(true);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, expectedSignedState);
+
+ // Add-on shouldn't be active
+ if (addons == ADDONS.bootstrap)
+ do_check_eq(getActiveVersion(), -1);
+ else
+ do_check_false(isExtensionInAddonsList(profileDir, ID));
+
+ // Should have flagged the change during startup
+ let changes = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_DISABLED);
+ do_check_eq(changes.length, 1);
+ do_check_eq(changes[0], ID);
+
+ // Shouldn't have checked for updates for the add-on
+ do_check_false(WindowWatcher.sawAddon);
+ WindowWatcher.sawAddon = false;
+
+ addon.uninstall();
+ // Restart to let non-restartless add-ons uninstall fully
+ yield promiseRestartManager();
+ yield shutdownManager();
+ resetPrefs();
+}
+
+function* test_working_migrate(addons, test, expectedSignedState) {
+ // Startup as the old version
+ gAppInfo.version = "4";
+ startupManager(true);
+
+ // Install the signed add-on
+ yield promiseInstallAllFiles([do_get_file(DATA + addons.signed)]);
+ // Restart to let non-restartless add-ons install fully
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+ resetPrefs();
+ stripDB();
+
+ // Now replace it with the version to test. Doing this so quickly shouldn't
+ // trigger the file modification code to detect the change by itself.
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_file(DATA + addons[test]), profileDir, ID);
+
+ // Update the application
+ gAppInfo.version = "5";
+ startupManager(true);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, expectedSignedState);
+
+ if (addons == ADDONS.bootstrap)
+ do_check_eq(getActiveVersion(), 2);
+ else
+ do_check_true(isExtensionInAddonsList(profileDir, ID));
+
+ // Shouldn't have checked for updates for the add-on
+ do_check_false(WindowWatcher.sawAddon);
+ WindowWatcher.sawAddon = false;
+
+ addon.uninstall();
+ // Restart to let non-restartless add-ons uninstall fully
+ yield promiseRestartManager();
+ yield shutdownManager();
+ resetPrefs();
+}
+
+add_task(function*() {
+ yield test_breaking_migrate(ADDONS.bootstrap, "unsigned", AddonManager.SIGNEDSTATE_MISSING);
+ yield test_breaking_migrate(ADDONS.nonbootstrap, "unsigned", AddonManager.SIGNEDSTATE_MISSING);
+});
+
+add_task(function*() {
+ yield test_breaking_migrate(ADDONS.bootstrap, "badid", AddonManager.SIGNEDSTATE_BROKEN);
+ yield test_breaking_migrate(ADDONS.nonbootstrap, "badid", AddonManager.SIGNEDSTATE_BROKEN);
+});
+
+add_task(function*() {
+ yield test_working_migrate(ADDONS.bootstrap, "signed", AddonManager.SIGNEDSTATE_SIGNED);
+ yield test_working_migrate(ADDONS.nonbootstrap, "signed", AddonManager.SIGNEDSTATE_SIGNED);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_multi.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_multi.js
new file mode 100644
index 000000000..01de29088
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_multi.js
@@ -0,0 +1,55 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+const DATA = "data/signing_checks/";
+
+// Each multi-package XPI contains one valid theme and one other add-on that
+// has the following error state:
+const ADDONS = {
+ "multi_signed.xpi": 0,
+ "multi_badid.xpi": AddonManager.ERROR_CORRUPT_FILE,
+ "multi_broken.xpi": AddonManager.ERROR_CORRUPT_FILE,
+ "multi_unsigned.xpi": AddonManager.ERROR_SIGNEDSTATE_REQUIRED,
+};
+
+function createInstall(filename) {
+ return new Promise(resolve => {
+ AddonManager.getInstallForFile(do_get_file(DATA + filename), resolve, "application/x-xpinstall");
+ });
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+ startupManager();
+
+ run_next_test();
+}
+
+function* test_addon(filename) {
+ do_print("Testing " + filename);
+
+ let install = yield createInstall(filename);
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_eq(install.error, 0);
+
+ do_check_neq(install.linkedInstalls, null);
+ do_check_eq(install.linkedInstalls.length, 1);
+
+ let linked = install.linkedInstalls[0];
+ do_print(linked.state);
+ do_check_eq(linked.error, ADDONS[filename]);
+ if (linked.error == 0) {
+ do_check_eq(linked.state, AddonManager.STATE_DOWNLOADED);
+ linked.cancel();
+ }
+ else {
+ do_check_eq(linked.state, AddonManager.STATE_DOWNLOAD_FAILED);
+ }
+
+ install.cancel();
+}
+
+for (let filename of Object.keys(ADDONS))
+ add_task(test_addon.bind(null, filename));
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_updatepref.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_updatepref.js
new file mode 100644
index 000000000..eb1bb78b3
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_updatepref.js
@@ -0,0 +1,136 @@
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+gUseRealCertChecks = true;
+
+const DATA = "data/signing_checks/";
+const ID = "test@tests.mozilla.org";
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start();
+
+gServer.registerPathHandler("/update.rdf", function(request, response) {
+ let updateData = {};
+ updateData[ID] = [{
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "4",
+ maxVersion: "6"
+ }]
+ }];
+
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.write(createUpdateRDF(updateData));
+});
+
+const SERVER = "127.0.0.1:" + gServer.identity.primaryPort;
+Services.prefs.setCharPref("extensions.update.background.url", "http://" + SERVER + "/update.rdf");
+
+function verifySignatures() {
+ return new Promise(resolve => {
+ let observer = (subject, topic, data) => {
+ Services.obs.removeObserver(observer, "xpi-signature-changed");
+ resolve(JSON.parse(data));
+ }
+ Services.obs.addObserver(observer, "xpi-signature-changed", false);
+
+ do_print("Verifying signatures");
+ let XPIscope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+ XPIscope.XPIProvider.verifySignatures();
+ });
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+
+ // Start and stop the manager to initialise everything in the profile before
+ // actual testing
+ startupManager();
+ shutdownManager();
+
+ run_next_test();
+}
+
+// Updating the pref without changing the app version won't disable add-ons
+// immediately but will after a signing check
+add_task(function*() {
+ Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
+ startupManager();
+
+ // Install the signed add-on
+ yield promiseInstallAllFiles([do_get_file(DATA + "unsigned_bootstrap_2.xpi")]);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ yield promiseShutdownManager();
+
+ Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ // Update checks shouldn't affect the add-on
+ yield AddonManagerInternal.backgroundUpdateCheck();
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ let changes = yield verifySignatures();
+
+ do_check_eq(changes.disabled.length, 1);
+ do_check_eq(changes.disabled[0], ID);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ addon.uninstall();
+
+ yield promiseShutdownManager();
+});
+
+// Updating the pref with changing the app version will disable add-ons
+// immediately
+add_task(function*() {
+ Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
+ startupManager();
+
+ // Install the signed add-on
+ yield promiseInstallAllFiles([do_get_file(DATA + "unsigned_bootstrap_2.xpi")]);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ yield promiseShutdownManager();
+
+ Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, true);
+ gAppInfo.version = 5.0
+ startupManager(true);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.appDisabled);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_MISSING);
+
+ addon.uninstall();
+
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_signed_verify.js b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_verify.js
new file mode 100644
index 000000000..0b5b30d89
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_signed_verify.js
@@ -0,0 +1,234 @@
+// Enable signature checks for these tests
+gUseRealCertChecks = true;
+// Disable update security
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+const DATA = "data/signing_checks/";
+const GOOD = [
+ ["signed_bootstrap_2.xpi", AddonManager.SIGNEDSTATE_SIGNED],
+ ["signed_nonbootstrap_2.xpi", AddonManager.SIGNEDSTATE_SIGNED]
+];
+const BAD = [
+ ["unsigned_bootstrap_2.xpi", AddonManager.SIGNEDSTATE_MISSING],
+ ["signed_bootstrap_badid_2.xpi", AddonManager.SIGNEDSTATE_BROKEN],
+ ["unsigned_nonbootstrap_2.xpi", AddonManager.SIGNEDSTATE_MISSING],
+ ["signed_nonbootstrap_badid_2.xpi", AddonManager.SIGNEDSTATE_BROKEN],
+];
+const ID = "test@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function verifySignatures() {
+ return new Promise(resolve => {
+ let observer = (subject, topic, data) => {
+ Services.obs.removeObserver(observer, "xpi-signature-changed");
+ resolve(JSON.parse(data));
+ }
+ Services.obs.addObserver(observer, "xpi-signature-changed", false);
+
+ do_print("Verifying signatures");
+ let XPIscope = Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm");
+ XPIscope.XPIProvider.verifySignatures();
+ });
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "4", "4");
+
+ run_next_test();
+}
+
+function verify_no_change([startFile, startState], [endFile, endState]) {
+ add_task(function*() {
+ do_print("A switch from " + startFile + " to " + endFile + " should cause no change.");
+
+ // Install the first add-on
+ manuallyInstall(do_get_file(DATA + startFile), profileDir, ID);
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ let wasAppDisabled = addon.appDisabled;
+ do_check_neq(addon.appDisabled, addon.isActive);
+ do_check_eq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(addon.signedState, startState);
+
+ // Swap in the files from the next add-on
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_file(DATA + endFile), profileDir, ID);
+
+ let events = {
+ [ID]: []
+ };
+
+ if (startState != endState)
+ events[ID].unshift(["onPropertyChanged", ["signedState"]]);
+
+ prepare_test(events);
+
+ // Trigger the check
+ let changes = yield verifySignatures();
+ do_check_eq(changes.enabled.length, 0);
+ do_check_eq(changes.disabled.length, 0);
+
+ do_check_eq(addon.appDisabled, wasAppDisabled);
+ do_check_neq(addon.appDisabled, addon.isActive);
+ do_check_eq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(addon.signedState, endState);
+
+ // Remove the add-on and restart to let it go away
+ manuallyUninstall(profileDir, ID);
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+ });
+}
+
+function verify_enables([startFile, startState], [endFile, endState]) {
+ add_task(function*() {
+ do_print("A switch from " + startFile + " to " + endFile + " should enable the add-on.");
+
+ // Install the first add-on
+ manuallyInstall(do_get_file(DATA + startFile), profileDir, ID);
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_false(addon.isActive);
+ do_check_eq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(addon.signedState, startState);
+
+ // Swap in the files from the next add-on
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_file(DATA + endFile), profileDir, ID);
+
+ let needsRestart = hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+ do_print(needsRestart);
+
+ let events = {};
+ if (!needsRestart) {
+ events[ID] = [
+ ["onPropertyChanged", ["appDisabled"]],
+ ["onEnabling", false],
+ "onEnabled"
+ ];
+ }
+ else {
+ events[ID] = [
+ ["onPropertyChanged", ["appDisabled"]],
+ "onEnabling"
+ ];
+ }
+
+ if (startState != endState)
+ events[ID].unshift(["onPropertyChanged", ["signedState"]]);
+
+ prepare_test(events);
+
+ // Trigger the check
+ let changes = yield verifySignatures();
+ do_check_eq(changes.enabled.length, 1);
+ do_check_eq(changes.enabled[0], ID);
+ do_check_eq(changes.disabled.length, 0);
+
+ do_check_false(addon.appDisabled);
+ if (needsRestart)
+ do_check_neq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ else
+ do_check_true(addon.isActive);
+ do_check_eq(addon.signedState, endState);
+
+ ensure_test_completed();
+
+ // Remove the add-on and restart to let it go away
+ manuallyUninstall(profileDir, ID);
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+ });
+}
+
+function verify_disables([startFile, startState], [endFile, endState]) {
+ add_task(function*() {
+ do_print("A switch from " + startFile + " to " + endFile + " should disable the add-on.");
+
+ // Install the first add-on
+ manuallyInstall(do_get_file(DATA + startFile), profileDir, ID);
+ startupManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_eq(addon.signedState, startState);
+
+ let needsRestart = hasFlag(addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_DISABLE);
+
+ // Swap in the files from the next add-on
+ manuallyUninstall(profileDir, ID);
+ manuallyInstall(do_get_file(DATA + endFile), profileDir, ID);
+
+ let events = {};
+ if (!needsRestart) {
+ events[ID] = [
+ ["onPropertyChanged", ["appDisabled"]],
+ ["onDisabling", false],
+ "onDisabled"
+ ];
+ }
+ else {
+ events[ID] = [
+ ["onPropertyChanged", ["appDisabled"]],
+ "onDisabling"
+ ];
+ }
+
+ if (startState != endState)
+ events[ID].unshift(["onPropertyChanged", ["signedState"]]);
+
+ prepare_test(events);
+
+ // Trigger the check
+ let changes = yield verifySignatures();
+ do_check_eq(changes.enabled.length, 0);
+ do_check_eq(changes.disabled.length, 1);
+ do_check_eq(changes.disabled[0], ID);
+
+ do_check_true(addon.appDisabled);
+ if (needsRestart)
+ do_check_neq(addon.pendingOperations, AddonManager.PENDING_NONE);
+ else
+ do_check_false(addon.isActive);
+ do_check_eq(addon.signedState, endState);
+
+ ensure_test_completed();
+
+ // Remove the add-on and restart to let it go away
+ manuallyUninstall(profileDir, ID);
+ yield promiseRestartManager();
+ yield promiseShutdownManager();
+ });
+}
+
+for (let start of GOOD) {
+ for (let end of BAD) {
+ verify_disables(start, end);
+ }
+}
+
+for (let start of BAD) {
+ for (let end of GOOD) {
+ verify_enables(start, end);
+ }
+}
+
+for (let start of GOOD) {
+ for (let end of GOOD.filter(f => f != start)) {
+ verify_no_change(start, end);
+ }
+}
+
+for (let start of BAD) {
+ for (let end of BAD.filter(f => f != start)) {
+ verify_no_change(start, end);
+ }
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_softblocked.js b/toolkit/mozapps/webextensions/test/xpcshell/test_softblocked.js
new file mode 100644
index 000000000..260b536e1
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_softblocked.js
@@ -0,0 +1,109 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const { utils: Cu, interfaces: Ci, classes: Cc, results: Cr } = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://testing-common/MockRegistrar.jsm");
+
+// Allow insecure updates
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false)
+
+const testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+testserver.registerDirectory("/data/", do_get_file("data"));
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+ openWindow: function(parent, url, name, features, openArgs) {
+ // Should be called to list the newly blocklisted items
+ do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+ // Simulate auto-disabling any softblocks
+ var list = openArgs.wrappedJSObject.list;
+ list.forEach(function(aItem) {
+ if (!aItem.blocked)
+ aItem.disable = true;
+ });
+
+ // run the code after the blocklist is closed
+ Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+ },
+
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWindowWatcher)
+ || iid.equals(Ci.nsISupports))
+ return this;
+
+ throw Cr.NS_ERROR_NO_INTERFACE;
+ }
+};
+
+MockRegistrar.register("@mozilla.org/embedcomp/window-watcher;1", WindowWatcher);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function load_blocklist(aFile) {
+ return new Promise((resolve, reject) => {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+ resolve();
+ }, "blocklist-updated", false);
+
+ Services.prefs.setCharPref("extensions.blocklist.url", `http://localhost:${gPort}/data/${aFile}`);
+ var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService(Ci.nsITimerCallback);
+ blocklist.notify(null);
+ });
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ run_next_test();
+}
+
+// Tests that an appDisabled add-on that becomes softBlocked remains disabled
+// when becoming appEnabled
+add_task(function* () {
+ writeInstallRDFForExtension({
+ id: "softblock1@tests.mozilla.org",
+ version: "1.0",
+ name: "Softblocked add-on",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "3"
+ }]
+ }, profileDir);
+
+ startupManager();
+
+ let s1 = yield promiseAddonByID("softblock1@tests.mozilla.org");
+
+ // Make sure to mark it as previously enabled.
+ s1.userDisabled = false;
+
+ do_check_false(s1.softDisabled);
+ do_check_true(s1.appDisabled);
+ do_check_false(s1.isActive);
+
+ yield load_blocklist("test_softblocked1.xml");
+
+ do_check_true(s1.softDisabled);
+ do_check_true(s1.appDisabled);
+ do_check_false(s1.isActive);
+
+ yield promiseRestartManager("2");
+
+ s1 = yield promiseAddonByID("softblock1@tests.mozilla.org");
+
+ do_check_true(s1.softDisabled);
+ do_check_false(s1.appDisabled);
+ do_check_false(s1.isActive);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_sourceURI.js b/toolkit/mozapps/webextensions/test/xpcshell/test_sourceURI.js
new file mode 100644
index 000000000..db5e4f7cc
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_sourceURI.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://testing-common/httpd.js");
+var gServer = new HttpServer();
+gServer.start(-1);
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+const PORT = gServer.identity.primaryPort;
+const BASE_URL = "http://localhost:" + PORT;
+const DEFAULT_URL = "about:blank";
+
+var addon = {
+ id: "addon@tests.mozilla.org",
+ version: "1.0",
+ name: "Test",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function backgroundUpdate(aCallback) {
+ Services.obs.addObserver(function() {
+ Services.obs.removeObserver(arguments.callee, "addons-background-update-complete");
+ aCallback();
+ }, "addons-background-update-complete", false);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+}
+
+function run_test() {
+ do_test_pending();
+
+ mapUrlToFile("/cache.xml", do_get_file("data/test_sourceURI.xml"), gServer);
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS, BASE_URL + "/cache.xml");
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE, BASE_URL + "/cache.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+ writeInstallRDFForExtension(addon, profileDir);
+ startupManager();
+
+ AddonManager.getAddonByID("addon@tests.mozilla.org", function(a) {
+ do_check_neq(a, null);
+ do_check_eq(a.sourceURI, null);
+
+ backgroundUpdate(function() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_neq(a2.sourceURI, null);
+ do_check_eq(a2.sourceURI.spec, "http://www.example.com/testaddon.xpi");
+
+ do_test_finished();
+ });
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_startup.js b/toolkit/mozapps/webextensions/test/xpcshell/test_startup.js
new file mode 100644
index 000000000..fdd00c1ad
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_startup.js
@@ -0,0 +1,932 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies startup detection of added/removed/changed items and install
+// location priorities
+
+// Enable loading extensions from the user and system scopes
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER +
+ AddonManager.SCOPE_SYSTEM);
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }, { // Repeated target application entries should be ignored
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }]
+};
+
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "2.0",
+ name: "Test 2",
+ targetApplications: [{ // Bad target application entries should be ignored
+ minVersion: "3",
+ maxVersion: "4"
+ }, {
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+};
+
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 3",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+// Should be ignored because it has no ID
+var addon4 = {
+ version: "4.0",
+ name: "Test 4",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Should be ignored because it has no version
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Should be ignored because it has an invalid type
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 6",
+ type: 5,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+// Should be ignored because it has an invalid type
+var addon7 = {
+ id: "addon7@tests.mozilla.org",
+ version: "3.0",
+ name: "Test 3",
+ type: "extension",
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "1.9.2",
+ maxVersion: "1.9.2.*"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const globalDir = gProfD.clone();
+globalDir.append("extensions2");
+globalDir.append(gAppInfo.ID);
+registerDirectory("XRESysSExtPD", globalDir.parent);
+const userDir = gProfD.clone();
+userDir.append("extensions3");
+userDir.append(gAppInfo.ID);
+registerDirectory("XREUSysExt", userDir.parent);
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var gCachePurged = false;
+
+// Set up the profile
+function run_test() {
+ do_test_pending("test_startup main");
+
+ let obs = AM_Cc["@mozilla.org/observer-service;1"].
+ getService(AM_Ci.nsIObserverService);
+ obs.addObserver({
+ observe: function(aSubject, aTopic, aData) {
+ gCachePurged = true;
+ }
+ }, "startupcache-invalidate", false);
+
+ startupManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+
+ do_check_false(gExtensionsJSON.exists());
+
+ do_check_false(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+
+ do_check_eq(a1, null);
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+ do_check_eq(a2, null);
+ do_check_not_in_crash_annotation(addon2.id, addon2.version);
+ do_check_eq(a3, null);
+ do_check_not_in_crash_annotation(addon3.id, addon3.version);
+ do_check_eq(a4, null);
+ do_check_eq(a5, null);
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_test_finished("test_startup main");
+}
+
+// Try to install all the items into the profile
+function run_test_1() {
+ writeInstallRDFForExtension(addon1, profileDir);
+ var dest = writeInstallRDFForExtension(addon2, profileDir);
+ // Attempt to make this look like it was added some time in the past so
+ // the change in run_test_2 makes the last modified time change.
+ setExtensionModifiedTime(dest, dest.lastModifiedTime - 5000);
+
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir, "addon4@tests.mozilla.org");
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ do_print("Checking for " + gExtensionsINI.path);
+ do_check_true(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_neq(a1.syncGUID, null);
+ do_check_true(a1.syncGUID.length >= 9);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.name, "Test 1");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a1.sourceURI, null);
+ do_check_true(a1.foreignInstall);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.seen);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_neq(a2.syncGUID, null);
+ do_check_true(a2.syncGUID.length >= 9);
+ do_check_eq(a2.version, "2.0");
+ do_check_eq(a2.name, "Test 2");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a2.sourceURI, null);
+ do_check_true(a2.foreignInstall);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.seen);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.id, "addon3@tests.mozilla.org");
+ do_check_neq(a3.syncGUID, null);
+ do_check_true(a3.syncGUID.length >= 9);
+ do_check_eq(a3.version, "3.0");
+ do_check_eq(a3.name, "Test 3");
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+ do_check_eq(a3.scope, AddonManager.SCOPE_PROFILE);
+ do_check_eq(a3.sourceURI, null);
+ do_check_true(a3.foreignInstall);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.seen);
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon5@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a6, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon6@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon6@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_check_eq(a7, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon7@tests.mozilla.org"));
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon7@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ AddonManager.getAddonsByTypes(["extension"], function(extensionAddons) {
+ do_check_eq(extensionAddons.length, 3);
+
+ do_execute_soon(run_test_2);
+ });
+ });
+}
+
+// Test that modified items are detected and items in other install locations
+// are ignored
+function run_test_2() {
+ addon1.version = "1.1";
+ writeInstallRDFForExtension(addon1, userDir);
+ addon2.version="2.1";
+ writeInstallRDFForExtension(addon2, profileDir);
+ addon2.version="2.2";
+ writeInstallRDFForExtension(addon2, globalDir);
+ addon2.version="2.3";
+ writeInstallRDFForExtension(addon2, userDir);
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon3@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ do_check_true(gExtensionsINI.exists());
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true(a1.foreignInstall);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.1");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true(a2.foreignInstall);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon3.id, addon3.version);
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Check that removing items from the profile reveals their hidden versions.
+function run_test_3() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ writeInstallRDFForExtension(addon3, profileDir, "addon4@tests.mozilla.org");
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ do_check_false(dest.exists());
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that disabling an install location works
+function run_test_4() {
+ Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_SYSTEM);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon1@tests.mozilla.org"));
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.2");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_true(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_execute_soon(run_test_5);
+ });
+}
+
+// Switching disabled locations works
+function run_test_5() {
+ Services.prefs.setIntPref("extensions.enabledScopes", AddonManager.SCOPE_USER);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Resetting the pref makes everything visible again
+function run_test_6() {
+ Services.prefs.clearUserPref("extensions.enabledScopes");
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.1");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_true(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_USER);
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Check that items in the profile hide the others again.
+function run_test_7() {
+ addon1.version = "1.2";
+ writeInstallRDFForExtension(addon1, profileDir);
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.2");
+ do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_true(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon2.id, a2.version);
+ do_check_eq(a2.scope, AddonManager.SCOPE_SYSTEM);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Disabling all locations still leaves the profile working
+function run_test_8() {
+ Services.prefs.setIntPref("extensions.enabledScopes", 0);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_in_crash_annotation(addon1.id, a1.version);
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a2, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon2@tests.mozilla.org"));
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// More hiding and revealing
+function run_test_9() {
+ Services.prefs.clearUserPref("extensions.enabledScopes", 0);
+
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = globalDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ addon2.version = "2.4";
+ writeInstallRDFForExtension(addon2, profileDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.2");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_false(isExtensionInAddonsList(userDir, a1.id));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.4");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_10);
+ });
+}
+
+// Checks that a removal from one location and an addition in another location
+// for the same item is handled
+function run_test_10() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ addon1.version = "1.3";
+ writeInstallRDFForExtension(addon1, userDir);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, ["addon1@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_eq(a1.id, "addon1@tests.mozilla.org");
+ do_check_eq(a1.version, "1.3");
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_true(isExtensionInAddonsList(userDir, a1.id));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a1.scope, AddonManager.SCOPE_USER);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.id, "addon2@tests.mozilla.org");
+ do_check_eq(a2.version, "2.4");
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_false(isExtensionInAddonsList(userDir, a2.id));
+ do_check_false(isExtensionInAddonsList(globalDir, a2.id));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
+ do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+
+ do_check_eq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+
+ do_check_eq(a4, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+
+ do_execute_soon(run_test_11);
+ });
+}
+
+// This should remove any remaining items
+function run_test_11() {
+ var dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+
+ gCachePurged = false;
+ restartManager();
+ check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_CHANGED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org"]);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_DISABLED, []);
+ check_startup_changes(AddonManager.STARTUP_CHANGE_ENABLED, []);
+ do_check_true(gCachePurged);
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_eq(a1, null);
+ do_check_eq(a2, null);
+ do_check_eq(a3, null);
+ do_check_eq(a4, null);
+ do_check_eq(a5, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(profileDir, "addon5@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(userDir, "addon5@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon1@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon2@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon3@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon4@tests.mozilla.org"));
+ do_check_false(isExtensionInAddonsList(globalDir, "addon5@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+ do_check_not_in_crash_annotation(addon2.id, addon2.version);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Test that auto-disabling for specific scopes works
+function run_test_12() {
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_USER);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ callback_soon(function([a1, a2, a3, a4, a5]) {
+ do_check_neq(a1, null);
+ do_check_false(a1.userDisabled);
+ do_check_true(a1.seen);
+ do_check_true(a1.isActive);
+
+ do_check_neq(a2, null);
+ do_check_true(a2.userDisabled);
+ do_check_false(a2.seen);
+ do_check_false(a2.isActive);
+
+ do_check_neq(a3, null);
+ do_check_false(a3.userDisabled);
+ do_check_true(a3.seen);
+ do_check_true(a3.isActive);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest.remove(true);
+ dest = userDir.clone();
+ dest.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest.remove(true);
+ dest = globalDir.clone();
+ dest.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_SYSTEM);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1_2, a2_2, a3_2, a4_2, a5_2]) {
+ do_check_neq(a1_2, null);
+ do_check_false(a1_2.userDisabled);
+ do_check_true(a1_2.seen);
+ do_check_true(a1_2.isActive);
+
+ do_check_neq(a2_2, null);
+ do_check_false(a2_2.userDisabled);
+ do_check_true(a2_2.seen);
+ do_check_true(a2_2.isActive);
+
+ do_check_neq(a3_2, null);
+ do_check_true(a3_2.userDisabled);
+ do_check_false(a3_2.seen);
+ do_check_false(a3_2.isActive);
+
+ var dest2 = profileDir.clone();
+ dest2.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ dest2.remove(true);
+ dest2 = userDir.clone();
+ dest2.append(do_get_expected_addon_name("addon2@tests.mozilla.org"));
+ dest2.remove(true);
+ dest2 = globalDir.clone();
+ dest2.append(do_get_expected_addon_name("addon3@tests.mozilla.org"));
+ dest2.remove(true);
+
+ restartManager();
+
+ Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_USER + AddonManager.SCOPE_SYSTEM);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, userDir);
+ writeInstallRDFForExtension(addon3, globalDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1_3, a2_3, a3_3, a4_3, a5_3]) {
+ do_check_neq(a1_3, null);
+ do_check_false(a1_3.userDisabled);
+ do_check_true(a1_3.seen);
+ do_check_true(a1_3.isActive);
+
+ do_check_neq(a2_3, null);
+ do_check_true(a2_3.userDisabled);
+ do_check_false(a2_3.seen);
+ do_check_false(a2_3.isActive);
+
+ do_check_neq(a3_3, null);
+ do_check_true(a3_3.userDisabled);
+ do_check_false(a3_3.seen);
+ do_check_false(a3_3.isActive);
+
+ do_execute_soon(end_test);
+ });
+ });
+ }));
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_strictcompatibility.js b/toolkit/mozapps/webextensions/test/xpcshell/test_strictcompatibility.js
new file mode 100644
index 000000000..cb6704936
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_strictcompatibility.js
@@ -0,0 +1,203 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests AddonManager.strictCompatibility and it's related preference,
+// extensions.strictCompatibility, and the strictCompatibility option in
+// install.rdf
+
+
+// Always compatible
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Incompatible in strict compatibility mode
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.7",
+ maxVersion: "0.8"
+ }]
+};
+
+// Theme - always uses strict compatibility, so is always incompatible
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ internalName: "test-theme-3",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+// Opt-in to strict compatibility - always incompatible
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ strictCompatibility: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+// Addon from the future - would be marked as compatibile-by-default,
+// but minVersion is higher than the app version
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "5"
+ }]
+};
+
+// Extremely old addon - maxVersion is less than the mimimum compat version
+// set in extensions.minCompatibleVersion
+var addon6 = {
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 6",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }]
+};
+
+// Dictionary - incompatible in strict compatibility mode
+var addon7= {
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 7",
+ type: "64",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.8",
+ maxVersion: "0.9"
+ }]
+};
+
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function do_check_compat_status(aStrict, aAddonCompat, aCallback) {
+ do_check_eq(AddonManager.strictCompatibility, aStrict);
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org",
+ "addon7@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5, a6, a7]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.isCompatible, aAddonCompat[0]);
+ do_check_eq(a1.appDisabled, !aAddonCompat[0]);
+ do_check_false(a1.strictCompatibility);
+
+ do_check_neq(a2, null);
+ do_check_eq(a2.isCompatible, aAddonCompat[1]);
+ do_check_eq(a2.appDisabled, !aAddonCompat[1]);
+ do_check_false(a2.strictCompatibility);
+
+ do_check_neq(a3, null);
+ do_check_eq(a3.isCompatible, aAddonCompat[2]);
+ do_check_eq(a3.appDisabled, !aAddonCompat[2]);
+ do_check_true(a3.strictCompatibility);
+
+ do_check_neq(a4, null);
+ do_check_eq(a4.isCompatible, aAddonCompat[3]);
+ do_check_eq(a4.appDisabled, !aAddonCompat[3]);
+ do_check_true(a4.strictCompatibility);
+
+ do_check_neq(a5, null);
+ do_check_eq(a5.isCompatible, aAddonCompat[4]);
+ do_check_eq(a5.appDisabled, !aAddonCompat[4]);
+ do_check_false(a5.strictCompatibility);
+
+ do_check_neq(a6, null);
+ do_check_eq(a6.isCompatible, aAddonCompat[5]);
+ do_check_eq(a6.appDisabled, !aAddonCompat[5]);
+ do_check_false(a6.strictCompatibility);
+
+ do_check_neq(a7, null);
+ do_check_eq(a7.isCompatible, aAddonCompat[6]);
+ do_check_eq(a7.appDisabled, !aAddonCompat[6]);
+ do_check_false(a7.strictCompatibility);
+
+ do_execute_soon(aCallback);
+ });
+}
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+ writeInstallRDFForExtension(addon6, profileDir);
+ writeInstallRDFForExtension(addon7, profileDir);
+
+ Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.1");
+
+ startupManager();
+
+ // Should default to enabling strict compat.
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_1);
+}
+
+function run_test_1() {
+ do_print("Test 1");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_2);
+}
+
+function run_test_2() {
+ do_print("Test 2");
+ restartManager();
+ do_check_compat_status(false, [true, true, false, false, false, true, true], run_test_3);
+}
+
+function run_test_3() {
+ do_print("Test 3");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_4);
+}
+
+function run_test_4() {
+ do_print("Test 4");
+ restartManager();
+ do_check_compat_status(true, [true, false, false, false, false, false, false], run_test_5);
+}
+
+function run_test_5() {
+ do_print("Test 5");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ Services.prefs.setCharPref(PREF_EM_MIN_COMPAT_APP_VERSION, "0.4");
+ do_check_compat_status(false, [true, true, false, false, false, false, true], do_test_finished);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_switch_os.js b/toolkit/mozapps/webextensions/test/xpcshell/test_switch_os.js
new file mode 100644
index 000000000..552d7cfae
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_switch_os.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+
+const ID = "bootstrap1@tests.mozilla.org";
+
+BootstrapMonitor.init();
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+add_task(function*() {
+ startupManager();
+
+ let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
+ yield promiseCompleteAllInstalls([install]);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_false(addon.userDisabled);
+ do_check_true(addon.isActive);
+
+ yield promiseShutdownManager();
+
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ let jData = loadJSON(gExtensionsJSON);
+
+ for (let addonInstance of jData.addons) {
+ if (addonInstance.id == ID) {
+ // Set to something that would be an invalid descriptor for this platform
+ addonInstance.descriptor = AppConstants.platform == "win" ? "/foo/bar" : "C:\\foo\\bar";
+ }
+ }
+
+ saveJSON(jData, gExtensionsJSON);
+
+ startupManager();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ BootstrapMonitor.checkAddonStarted(ID);
+ do_check_false(addon.userDisabled);
+ do_check_true(addon.isActive);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_syncGUID.js b/toolkit/mozapps/webextensions/test/xpcshell/test_syncGUID.js
new file mode 100644
index 000000000..385f58405
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_syncGUID.js
@@ -0,0 +1,156 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/Services.jsm");
+
+// restartManager() mucks with XPIProvider.jsm importing, so we hack around.
+this.__defineGetter__("XPIProvider", function () {
+ let scope = {};
+ return Components.utils.import("resource://gre/modules/addons/XPIProvider.jsm", scope)
+ .XPIProvider;
+});
+
+const addonId = "addon1@tests.mozilla.org";
+
+function run_test() {
+ Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+ startupManager();
+
+ run_next_test();
+}
+
+const UUID_PATTERN = /^\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}$/i;
+
+add_test(function test_getter_and_setter() {
+ // Our test add-on requires a restart.
+ let listener = {
+ onInstallEnded: function onInstallEnded() {
+ AddonManager.removeInstallListener(listener);
+ // never restart directly inside an onInstallEnded handler!
+ do_execute_soon(function getter_setter_install_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID(addonId, function(addon) {
+
+ do_check_neq(addon, null);
+ do_check_neq(addon.syncGUID, null);
+ do_check_true(UUID_PATTERN.test(addon.syncGUID));
+
+ let oldGUID = addon.SyncGUID;
+ let newGUID = "foo";
+
+ addon.syncGUID = newGUID;
+ do_check_eq(newGUID, addon.syncGUID);
+
+ // Verify change made it to DB.
+ AddonManager.getAddonByID(addonId, function(newAddon) {
+ do_check_neq(newAddon, null);
+ do_check_eq(newGUID, newAddon.syncGUID);
+ });
+
+ run_next_test();
+ });
+ });
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+
+ AddonManager.getInstallForFile(do_get_addon("test_install1"),
+ function(install) {
+ install.install();
+ });
+});
+
+add_test(function test_fetch_by_guid_unknown_guid() {
+ XPIProvider.getAddonBySyncGUID("XXXX", function(addon) {
+ do_check_eq(null, addon);
+ run_next_test();
+ });
+});
+
+// Ensure setting an extension to an existing syncGUID results in error.
+add_test(function test_error_on_duplicate_syncguid_insert() {
+ const installNames = ["test_install1", "test_install2_1"];
+ const installIDs = ["addon1@tests.mozilla.org", "addon2@tests.mozilla.org"];
+
+ let installCount = 0;
+
+ let listener = {
+ onInstallEnded: function onInstallEnded() {
+ installCount++;
+
+ if (installCount == installNames.length) {
+ AddonManager.removeInstallListener(listener);
+ do_execute_soon(function duplicate_syncguid_install_ended() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(installIDs, callback_soon(function(addons) {
+ let initialGUID = addons[1].syncGUID;
+
+ try {
+ addons[1].syncGUID = addons[0].syncGUID;
+ do_throw("Should not get here.");
+ }
+ catch (e) {
+ do_check_true(e.message.startsWith("Addon sync GUID conflict"));
+ restartManager();
+
+ AddonManager.getAddonByID(installIDs[1], function(addon) {
+ do_check_eq(initialGUID, addon.syncGUID);
+ run_next_test();
+ });
+ }
+ }));
+ });
+ }
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ let getInstallCB = function(install) { install.install(); };
+
+ for (let name of installNames) {
+ AddonManager.getInstallForFile(do_get_addon(name), getInstallCB);
+ }
+});
+
+add_test(function test_fetch_by_guid_known_guid() {
+ AddonManager.getAddonByID(addonId, function(addon) {
+ do_check_neq(null, addon);
+ do_check_neq(null, addon.syncGUID);
+
+ let syncGUID = addon.syncGUID;
+
+ XPIProvider.getAddonBySyncGUID(syncGUID, function(newAddon) {
+ do_check_neq(null, newAddon);
+ do_check_eq(syncGUID, newAddon.syncGUID);
+
+ run_next_test();
+ });
+ });
+});
+
+add_test(function test_addon_manager_get_by_sync_guid() {
+ AddonManager.getAddonByID(addonId, function(addon) {
+ do_check_neq(null, addon.syncGUID);
+
+ let syncGUID = addon.syncGUID;
+
+ AddonManager.getAddonBySyncGUID(syncGUID, function(newAddon) {
+ do_check_neq(null, newAddon);
+ do_check_eq(addon.id, newAddon.id);
+ do_check_eq(syncGUID, newAddon.syncGUID);
+
+ AddonManager.getAddonBySyncGUID("DOES_NOT_EXIST", function(missing) {
+ do_check_eq(undefined, missing);
+
+ run_next_test();
+ });
+ });
+ });
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_system_delay_update.js b/toolkit/mozapps/webextensions/test/xpcshell/test_system_delay_update.js
new file mode 100644
index 000000000..35964c663
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_system_delay_update.js
@@ -0,0 +1,461 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that delaying a system add-on update works.
+
+Components.utils.import("resource://testing-common/httpd.js");
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+const tempdir = gTmpD.clone();
+
+const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
+const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
+
+const IGNORE_ID = "system_delay_ignore@tests.mozilla.org";
+const COMPLETE_ID = "system_delay_complete@tests.mozilla.org";
+const DEFER_ID = "system_delay_defer@tests.mozilla.org";
+const DEFER_ALSO_ID = "system_delay_defer_also@tests.mozilla.org";
+const NORMAL_ID = "system1@tests.mozilla.org";
+
+
+const TEST_IGNORE_PREF = "delaytest.ignore";
+
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+let testserver = new HttpServer();
+testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
+testserver.start();
+let root = `${testserver.identity.primaryScheme}://${testserver.identity.primaryHost}:${testserver.identity.primaryPort}/data/`;
+Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
+
+
+// Note that we would normally use BootstrapMonitor but it currently requires
+// the objects in `data` to be serializable, and we need a real reference to the
+// `instanceID` symbol to test.
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+
+function promiseInstallPostponed(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seen = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: (install) => {
+ AddonManager.removeInstallListener(listener);
+ reject(`extension installation should not have ended for ${install.addon.id}`);
+ },
+ onInstallPostponed: (install) => {
+ seen.push(install.addon.id);
+ if (seen.includes(addonID1) && seen.includes(addonID2)) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+function promiseInstallResumed(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seenPostponed = [];
+ let seenEnded = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: (install) => {
+ seenEnded.push(install.addon.id);
+ if ((seenEnded.includes(addonID1) && seenEnded.includes(addonID2)) &&
+ (seenPostponed.includes(addonID1) && seenPostponed.includes(addonID2))) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ },
+ onInstallPostponed: (install) => {
+ seenPostponed.push(install.addon.id);
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+function promiseInstallDeferred(addonID1, addonID2) {
+ return new Promise((resolve, reject) => {
+ let seenEnded = [];
+ let listener = {
+ onInstallFailed: () => {
+ AddonManager.removeInstallListener(listener);
+ reject("extension installation should not have failed");
+ },
+ onInstallEnded: (install) => {
+ seenEnded.push(install.addon.id);
+ if (seenEnded.includes(addonID1) && seenEnded.includes(addonID2)) {
+ AddonManager.removeInstallListener(listener);
+ resolve();
+ }
+ },
+ onInstallPostponed: (install) => {
+ AddonManager.removeInstallListener(listener);
+ reject(`extension installation should not have been postponed for ${install.addon.id}`);
+ }
+ };
+
+ AddonManager.addInstallListener(listener);
+ });
+}
+
+
+// add-on registers upgrade listener, and ignores update.
+add_task(function*() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ do_get_file("data/system_addons/system_delay_ignore.xpi").copyTo(distroDir, "system_delay_ignore@tests.mozilla.org.xpi");
+ do_get_file("data/system_addons/system1_1.xpi").copyTo(distroDir, "system1@tests.mozilla.org.xpi");
+
+ startupManager();
+ let updateList = [
+ { id: IGNORE_ID, version: "2.0", path: "system_delay_ignore_2.xpi" },
+ { id: NORMAL_ID, version: "2.0", path: "system1_2.xpi" },
+ ];
+
+ let postponed = promiseInstallPostponed(IGNORE_ID, NORMAL_ID);
+ yield installSystemAddons(yield buildSystemAddonUpdates(updateList, root), testserver);
+ yield postponed;
+
+ // addon upgrade has been delayed.
+ let addon_postponed = yield promiseAddonByID(IGNORE_ID, NORMAL_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Ignore");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+ do_check_true(Services.prefs.getBoolPref(TEST_IGNORE_PREF));
+
+ // other addons in the set are delayed as well.
+ addon_postponed = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Add-on 1");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // restarting allows upgrades to proceed
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(IGNORE_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Test Delay Update Ignore");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ addon_upgraded = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Add-on 1");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
+
+// add-on registers upgrade listener, and allows update.
+add_task(function*() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ do_get_file("data/system_addons/system_delay_complete.xpi").copyTo(distroDir, "system_delay_complete@tests.mozilla.org.xpi");
+ do_get_file("data/system_addons/system1_1.xpi").copyTo(distroDir, "system1@tests.mozilla.org.xpi");
+
+ startupManager();
+
+ let updateList = [
+ { id: COMPLETE_ID, version: "2.0", path: "system_delay_complete_2.xpi" },
+ { id: NORMAL_ID, version: "2.0", path: "system1_2.xpi" },
+ ];
+
+ // initial state
+ let addon_allowed = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "1.0");
+ do_check_eq(addon_allowed.name, "System Test Delay Update Complete");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ addon_allowed = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "1.0");
+ do_check_eq(addon_allowed.name, "System Add-on 1");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ let resumed = promiseInstallResumed(COMPLETE_ID, NORMAL_ID);
+ yield installSystemAddons(yield buildSystemAddonUpdates(updateList, root), testserver);
+
+ // update is initially postponed, then resumed
+ yield resumed;
+
+ // addon upgrade has been allowed
+ addon_allowed = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Test Delay Update Complete");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // other upgrades in the set are allowed as well
+ addon_allowed = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Add-on 1");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // restarting changes nothing
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(COMPLETE_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Test Delay Update Complete");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ addon_upgraded = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Add-on 1");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
+
+// add-on registers upgrade listener, initially defers update then allows upgrade
+add_task(function*() {
+ // discard system addon updates
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ do_get_file("data/system_addons/system_delay_defer.xpi").copyTo(distroDir, "system_delay_defer@tests.mozilla.org.xpi");
+ do_get_file("data/system_addons/system1_1.xpi").copyTo(distroDir, "system1@tests.mozilla.org.xpi");
+
+ startupManager();
+
+ let updateList = [
+ { id: DEFER_ID, version: "2.0", path: "system_delay_defer_2.xpi" },
+ { id: NORMAL_ID, version: "2.0", path: "system1_2.xpi" },
+ ];
+
+ let postponed = promiseInstallPostponed(DEFER_ID, NORMAL_ID);
+ yield installSystemAddons(yield buildSystemAddonUpdates(updateList, root), testserver);
+ yield postponed;
+
+ // upgrade is initially postponed
+ let addon_postponed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Defer");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // other addons in the set are postponed as well.
+ addon_postponed = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Add-on 1");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ let deferred = promiseInstallDeferred(DEFER_ID, NORMAL_ID);
+ // add-on will not allow upgrade until fake event fires
+ AddonManagerPrivate.callAddonListeners("onFakeEvent");
+
+ yield deferred;
+
+ // addon upgrade has been allowed
+ let addon_allowed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Test Delay Update Defer");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // other addons in the set are allowed as well.
+ addon_allowed = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Add-on 1");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // restarting changes nothing
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Test Delay Update Defer");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ addon_upgraded = yield promiseAddonByID(NORMAL_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Add-on 1");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
+
+// multiple add-ons registers upgrade listeners, initially defers then each unblock in turn.
+add_task(function*() {
+ // discard system addon updates.
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "");
+
+ do_get_file("data/system_addons/system_delay_defer.xpi").copyTo(distroDir, "system_delay_defer@tests.mozilla.org.xpi");
+ do_get_file("data/system_addons/system_delay_defer_also.xpi").copyTo(distroDir, "system_delay_defer_also@tests.mozilla.org.xpi");
+
+ startupManager();
+
+ let updateList = [
+ { id: DEFER_ID, version: "2.0", path: "system_delay_defer_2.xpi" },
+ { id: DEFER_ALSO_ID, version: "2.0", path: "system_delay_defer_also_2.xpi" },
+ ];
+
+ let postponed = promiseInstallPostponed(DEFER_ID, DEFER_ALSO_ID);
+ yield installSystemAddons(yield buildSystemAddonUpdates(updateList, root), testserver);
+ yield postponed;
+
+ // upgrade is initially postponed
+ let addon_postponed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Defer");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ // other addons in the set are postponed as well.
+ addon_postponed = yield promiseAddonByID(DEFER_ALSO_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Defer Also");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ let deferred = promiseInstallDeferred(DEFER_ID, DEFER_ALSO_ID);
+ // add-on will not allow upgrade until fake event fires
+ AddonManagerPrivate.callAddonListeners("onFakeEvent");
+
+ // Upgrade blockers still present.
+ addon_postponed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Defer");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ addon_postponed = yield promiseAddonByID(DEFER_ALSO_ID);
+ do_check_neq(addon_postponed, null);
+ do_check_eq(addon_postponed.version, "1.0");
+ do_check_eq(addon_postponed.name, "System Test Delay Update Defer Also");
+ do_check_true(addon_postponed.isCompatible);
+ do_check_false(addon_postponed.appDisabled);
+ do_check_true(addon_postponed.isActive);
+ do_check_eq(addon_postponed.type, "extension");
+
+ AddonManagerPrivate.callAddonListeners("onOtherFakeEvent");
+
+ yield deferred;
+
+ // addon upgrade has been allowed
+ let addon_allowed = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_allowed, null);
+ do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Test Delay Update Defer");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // other addons in the set are allowed as well.
+ addon_allowed = yield promiseAddonByID(DEFER_ALSO_ID);
+ do_check_neq(addon_allowed, null);
+ // do_check_eq(addon_allowed.version, "2.0");
+ do_check_eq(addon_allowed.name, "System Test Delay Update Defer Also");
+ do_check_true(addon_allowed.isCompatible);
+ do_check_false(addon_allowed.appDisabled);
+ do_check_true(addon_allowed.isActive);
+ do_check_eq(addon_allowed.type, "extension");
+
+ // restarting changes nothing
+ yield promiseRestartManager();
+
+ let addon_upgraded = yield promiseAddonByID(DEFER_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Test Delay Update Defer");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ addon_upgraded = yield promiseAddonByID(DEFER_ALSO_ID);
+ do_check_neq(addon_upgraded, null);
+ do_check_eq(addon_upgraded.version, "2.0");
+ do_check_eq(addon_upgraded.name, "System Test Delay Update Defer Also");
+ do_check_true(addon_upgraded.isCompatible);
+ do_check_false(addon_upgraded.appDisabled);
+ do_check_true(addon_upgraded.isActive);
+ do_check_eq(addon_upgraded.type, "extension");
+
+ yield shutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_system_reset.js b/toolkit/mozapps/webextensions/test/xpcshell/test_system_reset.js
new file mode 100644
index 000000000..31b7e5783
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_system_reset.js
@@ -0,0 +1,418 @@
+// Tests that we reset to the default system add-ons correctly when switching
+// application versions
+const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
+
+BootstrapMonitor.init();
+
+const updatesDir = FileUtils.getDir("ProfD", ["features"]);
+
+// Build the test sets
+var dir = FileUtils.getDir("ProfD", ["sysfeatures", "app1"], true);
+do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+
+dir = FileUtils.getDir("ProfD", ["sysfeatures", "app2"], true);
+do_get_file("data/system_addons/system1_2.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
+
+dir = FileUtils.getDir("ProfD", ["sysfeatures", "app3"], true);
+do_get_file("data/system_addons/system1_1_badcert.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system3_1.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
+
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "app0"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
+
+function makeUUID() {
+ let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
+ getService(AM_Ci.nsIUUIDGenerator);
+ return uuidGen.generateUUID().toString();
+}
+
+function* check_installed(conditions) {
+ for (let i = 0; i < conditions.length; i++) {
+ let condition = conditions[i];
+ let id = "system" + (i + 1) + "@tests.mozilla.org";
+ let addon = yield promiseAddonByID(id);
+
+ if (!("isUpgrade" in condition) || !("version" in condition)) {
+ throw Error("condition must contain isUpgrade and version");
+ }
+ let isUpgrade = conditions[i].isUpgrade;
+ let version = conditions[i].version;
+
+ let expectedDir = isUpgrade ? updatesDir : distroDir;
+
+ if (version) {
+ // Add-on should be installed
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, version);
+ do_check_true(addon.isActive);
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.hidden);
+ do_check_true(addon.isSystem);
+ do_check_false(hasFlag(addon.permissions, AddonManager.PERM_CAN_UPGRADE));
+ if (isUpgrade) {
+ do_check_true(hasFlag(addon.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ } else {
+ do_check_false(hasFlag(addon.permissions, AddonManager.PERM_CAN_UNINSTALL));
+ }
+
+ // Verify the add-ons file is in the right place
+ let file = expectedDir.clone();
+ file.append(id + ".xpi");
+ do_check_true(file.exists());
+ do_check_true(file.isFile());
+
+ let uri = addon.getResourceURI(null);
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ do_check_eq(uri.file.path, file.path);
+
+ if (isUpgrade) {
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
+ }
+
+ // Verify the add-on actually started
+ BootstrapMonitor.checkAddonStarted(id, version);
+ }
+ else {
+ if (isUpgrade) {
+ // Add-on should not be installed
+ do_check_eq(addon, null);
+ }
+ else {
+ // Either add-on should not be installed or it shouldn't be active
+ do_check_true(!addon || !addon.isActive);
+ }
+
+ BootstrapMonitor.checkAddonNotStarted(id);
+
+ if (addon)
+ BootstrapMonitor.checkAddonInstalled(id);
+ else
+ BootstrapMonitor.checkAddonNotInstalled(id);
+ }
+ }
+}
+
+// Test with a missing features directory
+add_task(function* test_missing_app_dir() {
+ startupManager();
+
+ let conditions = [
+ { isUpgrade: false, version: null },
+ { isUpgrade: false, version: null },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ do_check_false(updatesDir.exists());
+
+ yield promiseShutdownManager();
+});
+
+// Add some features in a new version
+add_task(function* test_new_version() {
+ gAppInfo.version = "1";
+ distroDir.leafName = "app1";
+ startupManager();
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ do_check_false(updatesDir.exists());
+
+ yield promiseShutdownManager();
+});
+
+// Another new version swaps one feature and upgrades another
+add_task(function* test_upgrade() {
+ gAppInfo.version = "2";
+ distroDir.leafName = "app2";
+ startupManager();
+
+ let conditions = [
+ { isUpgrade: false, version: "2.0" },
+ { isUpgrade: false, version: null },
+ { isUpgrade: false, version: "1.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ do_check_false(updatesDir.exists());
+
+ yield promiseShutdownManager();
+});
+
+// Downgrade
+add_task(function* test_downgrade() {
+ gAppInfo.version = "1";
+ distroDir.leafName = "app1";
+ startupManager();
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ do_check_false(updatesDir.exists());
+
+ yield promiseShutdownManager();
+});
+
+// Fake a mid-cycle install
+add_task(function* test_updated() {
+ // Create a random dir to install into
+ let dirname = makeUUID();
+ FileUtils.getDir("ProfD", ["features", dirname], true);
+ updatesDir.append(dirname);
+
+ // Copy in the system add-ons
+ let file = do_get_file("data/system_addons/system2_2.xpi");
+ file.copyTo(updatesDir, "system2@tests.mozilla.org.xpi");
+ file = do_get_file("data/system_addons/system3_2.xpi");
+ file.copyTo(updatesDir, "system3@tests.mozilla.org.xpi");
+
+ // Inject it into the system set
+ let addonSet = {
+ schema: 1,
+ directory: updatesDir.leafName,
+ addons: {
+ "system2@tests.mozilla.org": {
+ version: "2.0"
+ },
+ "system3@tests.mozilla.org": {
+ version: "2.0"
+ },
+ }
+ };
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: true, version: "2.0" },
+ { isUpgrade: true, version: "2.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Entering safe mode should disable the updated system add-ons and use the
+// default system add-ons
+add_task(function* safe_mode_disabled() {
+ gAppInfo.inSafeMode = true;
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Leaving safe mode should re-enable the updated system add-ons
+add_task(function* normal_mode_enabled() {
+ gAppInfo.inSafeMode = false;
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: true, version: "2.0" },
+ { isUpgrade: true, version: "2.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// An additional add-on in the directory should be ignored
+add_task(function* test_skips_additional() {
+ // Copy in the system add-ons
+ let file = do_get_file("data/system_addons/system4_1.xpi");
+ file.copyTo(updatesDir, "system4@tests.mozilla.org.xpi");
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: true, version: "2.0" },
+ { isUpgrade: true, version: "2.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Missing add-on should revert to the default set
+add_task(function* test_revert() {
+ manuallyUninstall(updatesDir, "system2@tests.mozilla.org");
+
+ // With the add-on physically gone from disk we won't see uninstall events
+ BootstrapMonitor.clear("system2@tests.mozilla.org");
+
+ startupManager(false);
+
+ // With system add-on 2 gone the updated set is now invalid so it reverts to
+ // the default set which is system add-ons 1 and 2.
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Putting it back will make the set work again
+add_task(function* test_reuse() {
+ let file = do_get_file("data/system_addons/system2_2.xpi");
+ file.copyTo(updatesDir, "system2@tests.mozilla.org.xpi");
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: true, version: "2.0" },
+ { isUpgrade: true, version: "2.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Making the pref corrupt should revert to the default set
+add_task(function* test_corrupt_pref() {
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "foo");
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// An add-on with a bad certificate should cause us to use the default set
+add_task(function* test_bad_profile_cert() {
+ let file = do_get_file("data/system_addons/system1_1_badcert.xpi");
+ file.copyTo(updatesDir, "system1@tests.mozilla.org.xpi");
+
+ // Inject it into the system set
+ let addonSet = {
+ schema: 1,
+ directory: updatesDir.leafName,
+ addons: {
+ "system1@tests.mozilla.org": {
+ version: "2.0"
+ },
+ "system2@tests.mozilla.org": {
+ version: "1.0"
+ },
+ "system3@tests.mozilla.org": {
+ version: "1.0"
+ },
+ }
+ };
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// Switching to app defaults that contain a bad certificate should still work
+add_task(function* test_bad_app_cert() {
+ gAppInfo.version = "3";
+ distroDir.leafName = "app3";
+ startupManager();
+
+ // Add-on will still be present
+ let addon = yield promiseAddonByID("system1@tests.mozilla.org");
+ do_check_neq(addon, null);
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ { isUpgrade: false, version: null },
+ { isUpgrade: false, version: "1.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
+
+// A failed upgrade should revert to the default set.
+add_task(function* test_updated() {
+ // Create a random dir to install into
+ let dirname = makeUUID();
+ FileUtils.getDir("ProfD", ["features", dirname], true);
+ updatesDir.append(dirname);
+
+ // Copy in the system add-ons
+ let file = do_get_file("data/system_addons/system2_2.xpi");
+ file.copyTo(updatesDir, "system2@tests.mozilla.org.xpi");
+ file = do_get_file("data/system_addons/system_failed_update.xpi");
+ file.copyTo(updatesDir, "system_failed_update@tests.mozilla.org.xpi");
+
+ // Inject it into the system set
+ let addonSet = {
+ schema: 1,
+ directory: updatesDir.leafName,
+ addons: {
+ "system2@tests.mozilla.org": {
+ version: "2.0"
+ },
+ "system_failed_update@tests.mozilla.org": {
+ version: "1.0"
+ },
+ }
+ };
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
+
+ startupManager(false);
+
+ let conditions = [
+ { isUpgrade: false, version: "1.0" },
+ ];
+
+ yield check_installed(conditions);
+
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_system_update.js b/toolkit/mozapps/webextensions/test/xpcshell/test_system_update.js
new file mode 100644
index 000000000..c8e314427
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_system_update.js
@@ -0,0 +1,788 @@
+// Tests that we reset to the default system add-ons correctly when switching
+// application versions
+const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
+const PREF_SYSTEM_ADDON_UPDATE_URL = "extensions.systemAddon.update.url";
+const PREF_XPI_STATE = "extensions.xpiState";
+const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
+
+Components.utils.import("resource://testing-common/httpd.js");
+const { computeHash } = Components.utils.import("resource://gre/modules/addons/ProductAddonChecker.jsm");
+
+BootstrapMonitor.init();
+
+const updatesDir = FileUtils.getDir("ProfD", ["features"], false);
+
+function getCurrentUpdatesDir() {
+ let dir = updatesDir.clone();
+ let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
+ dir.append(set.directory);
+ return dir;
+}
+
+function clearUpdatesDir() {
+ // Delete any existing directories
+ if (updatesDir.exists())
+ updatesDir.remove(true);
+
+ Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
+}
+
+function buildPrefilledUpdatesDir() {
+ clearUpdatesDir();
+
+ // Build the test set
+ let dir = FileUtils.getDir("ProfD", ["features", "prefilled"], true);
+
+ do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+ do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
+
+ // Mark these in the past so the startup file scan notices when files have changed properly
+ FileUtils.getFile("ProfD", ["features", "prefilled", "system2@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
+ FileUtils.getFile("ProfD", ["features", "prefilled", "system3@tests.mozilla.org.xpi"]).lastModifiedTime -= 10000;
+
+ Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify({
+ schema: 1,
+ directory: dir.leafName,
+ addons: {
+ "system2@tests.mozilla.org": {
+ version: "2.0"
+ },
+ "system3@tests.mozilla.org": {
+ version: "2.0"
+ },
+ }
+ }));
+}
+
+let dir = FileUtils.getDir("ProfD", ["sysfeatures", "hidden"], true);
+do_get_file("data/system_addons/system1_1.xpi").copyTo(dir, "system1@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system2_1.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+
+dir = FileUtils.getDir("ProfD", ["sysfeatures", "prefilled"], true);
+do_get_file("data/system_addons/system2_2.xpi").copyTo(dir, "system2@tests.mozilla.org.xpi");
+do_get_file("data/system_addons/system3_2.xpi").copyTo(dir, "system3@tests.mozilla.org.xpi");
+
+const distroDir = FileUtils.getDir("ProfD", ["sysfeatures", "empty"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2");
+
+var testserver = new HttpServer();
+testserver.registerDirectory("/data/", do_get_file("data/system_addons"));
+testserver.start();
+var root = testserver.identity.primaryScheme + "://" +
+ testserver.identity.primaryHost + ":" +
+ testserver.identity.primaryPort + "/data/"
+Services.prefs.setCharPref(PREF_SYSTEM_ADDON_UPDATE_URL, root + "update.xml");
+
+function makeUUID() {
+ let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
+ getService(AM_Ci.nsIUUIDGenerator);
+ return uuidGen.generateUUID().toString();
+}
+
+function* check_installed(conditions) {
+ for (let i = 0; i < conditions.length; i++) {
+ let condition = conditions[i];
+ let id = "system" + (i + 1) + "@tests.mozilla.org";
+ let addon = yield promiseAddonByID(id);
+
+ if (!("isUpgrade" in condition) || !("version" in condition)) {
+ throw Error("condition must contain isUpgrade and version");
+ }
+ let isUpgrade = conditions[i].isUpgrade;
+ let version = conditions[i].version;
+
+ let expectedDir = isUpgrade ? getCurrentUpdatesDir() : distroDir;
+
+ if (version) {
+ do_print(`Checking state of add-on ${id}, expecting version ${version}`);
+
+ // Add-on should be installed
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, version);
+ do_check_true(addon.isActive);
+ do_check_false(addon.foreignInstall);
+ do_check_true(addon.hidden);
+ do_check_true(addon.isSystem);
+
+ // Verify the add-ons file is in the right place
+ let file = expectedDir.clone();
+ file.append(id + ".xpi");
+ do_check_true(file.exists());
+ do_check_true(file.isFile());
+
+ let uri = addon.getResourceURI(null);
+ do_check_true(uri instanceof AM_Ci.nsIFileURL);
+ do_check_eq(uri.file.path, file.path);
+
+ if (isUpgrade) {
+ do_check_eq(addon.signedState, AddonManager.SIGNEDSTATE_SYSTEM);
+ }
+
+ // Verify the add-on actually started
+ BootstrapMonitor.checkAddonStarted(id, version);
+ }
+ else {
+ do_print(`Checking state of add-on ${id}, expecting it to be missing`);
+
+ if (isUpgrade) {
+ // Add-on should not be installed
+ do_check_eq(addon, null);
+ }
+
+ BootstrapMonitor.checkAddonNotStarted(id);
+
+ if (addon)
+ BootstrapMonitor.checkAddonInstalled(id);
+ else
+ BootstrapMonitor.checkAddonNotInstalled(id);
+ }
+ }
+}
+
+
+/**
+ * Defines the set of initial conditions to run each test against. Each should
+ * define the following properties:
+ *
+ * setup: A task to setup the profile into the initial state.
+ * initialState: The initial expected system add-on state after setup has run.
+ */
+const TEST_CONDITIONS = {
+ // Runs tests with no updated or default system add-ons initially installed
+ blank: {
+ setup: function*() {
+ clearUpdatesDir();
+ distroDir.leafName = "empty";
+ },
+ initialState: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ },
+ // Runs tests with default system add-ons installed
+ withAppSet: {
+ setup: function*() {
+ clearUpdatesDir();
+ distroDir.leafName = "prefilled";
+ },
+ initialState: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ]
+ },
+
+ // Runs tests with updated system add-ons installed
+ withProfileSet: {
+ setup: function*() {
+ buildPrefilledUpdatesDir();
+ distroDir.leafName = "empty";
+ },
+ initialState: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ]
+ },
+
+ // Runs tests with both default and updated system add-ons installed
+ withBothSets: {
+ setup: function*() {
+ buildPrefilledUpdatesDir();
+ distroDir.leafName = "hidden";
+ },
+ initialState: [
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ]
+ },
+};
+
+
+/**
+ * The tests to run. Each test must define an updateList or test. The following
+ * properties are used:
+ *
+ * updateList: The set of add-ons the server should respond with.
+ * test: A function to run to perform the update check (replaces
+ * updateList)
+ * fails: An optional property, if true the update check is expected to
+ * fail.
+ * finalState: An optional property, the expected final state of system add-ons,
+ * if missing the test condition's initialState is used.
+ */
+const TESTS = {
+ // Test that a blank response does nothing
+ blank: {
+ updateList: null,
+ },
+
+ // Test that an empty list removes existing updates, leaving defaults.
+ empty: {
+ updateList: [],
+ finalState: {
+ blank: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withAppSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withProfileSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withBothSets: [
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ // Set this to `true` to so `verify_state()` expects a blank profile dir
+ { isUpgrade: true, version: null}
+ ]
+ },
+ },
+ // Tests that a new set of system add-ons gets installed
+ newset: {
+ updateList: [
+ { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" },
+ { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi" }
+ ],
+ finalState: {
+ blank: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withAppSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: false, version: "2.0"},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withProfileSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withBothSets: [
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: true, version: "1.0"}
+ ]
+ }
+ },
+
+ // Tests that an upgraded set of system add-ons gets installed
+ upgrades: {
+ updateList: [
+ { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
+ ],
+ finalState: {
+ blank: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withAppSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withProfileSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ],
+ withBothSets: [
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: false, version: null}
+ ]
+ }
+ },
+
+ // Tests that a set of system add-ons, some new, some existing gets installed
+ overlapping: {
+ updateList: [
+ { id: "system1@tests.mozilla.org", version: "2.0", path: "system1_2.xpi" },
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" },
+ { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
+ ],
+ finalState: {
+ blank: [
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: false, version: null}
+ ],
+ withAppSet: [
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: false, version: null}
+ ],
+ withProfileSet: [
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: false, version: null}
+ ],
+ withBothSets: [
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "2.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "1.0"},
+ { isUpgrade: false, version: null}
+ ]
+ }
+ },
+
+ // Specifying an incorrect version should stop us updating anything
+ badVersion: {
+ fails: true,
+ updateList: [
+ { id: "system2@tests.mozilla.org", version: "4.0", path: "system2_3.xpi" },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
+ ],
+ },
+
+ // Specifying an invalid size should stop us updating anything
+ badSize: {
+ fails: true,
+ updateList: [
+ { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 2 },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi" }
+ ],
+ },
+
+ // Specifying an incorrect hash should stop us updating anything
+ badHash: {
+ fails: true,
+ updateList: [
+ { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi" },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "205a4c49bd513ebd30594e380c19e86bba1f83e2" }
+ ],
+ },
+
+ // Correct sizes and hashes should work
+ checkSizeHash: {
+ updateList: [
+ { id: "system2@tests.mozilla.org", version: "3.0", path: "system2_3.xpi", size: 4697 },
+ { id: "system3@tests.mozilla.org", version: "3.0", path: "system3_3.xpi", hashFunction: "sha1", hashValue: "a4c7198d56deb315511c02937fd96c696de6cb84" },
+ { id: "system5@tests.mozilla.org", version: "1.0", path: "system5_1.xpi", size: 4691, hashFunction: "sha1", hashValue: "6887b916a1a9a5338b0df4181f6187f5396861eb" }
+ ],
+ finalState: {
+ blank: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withAppSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withProfileSet: [
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"}
+ ],
+ withBothSets: [
+ { isUpgrade: false, version: "1.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: true, version: "3.0"},
+ { isUpgrade: false, version: null},
+ { isUpgrade: true, version: "1.0"}
+ ]
+ }
+ },
+
+ // A bad certificate should stop updates
+ badCert: {
+ fails: true,
+ updateList: [
+ { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1_badcert.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ],
+ },
+
+ // An unpacked add-on should stop updates.
+ notPacked: {
+ fails: true,
+ updateList: [
+ { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_1_unpack.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ],
+ },
+
+ // A non-bootstrap add-on should stop updates.
+ notBootstrap: {
+ fails: true,
+ updateList: [
+ { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_2_notBootstrap.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ],
+ },
+
+ // A non-multiprocess add-on should stop updates.
+ notMultiprocess: {
+ fails: true,
+ updateList: [
+ { id: "system6@tests.mozilla.org", version: "1.0", path: "system6_3_notMultiprocess.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ],
+ }
+}
+
+add_task(function* setup() {
+ // Initialise the profile
+ startupManager();
+ yield promiseShutdownManager();
+})
+
+function* get_directories() {
+ let subdirs = [];
+
+ if (yield OS.File.exists(updatesDir.path)) {
+ let iterator = new OS.File.DirectoryIterator(updatesDir.path);
+ yield iterator.forEach(entry => {
+ if (entry.isDir) {
+ subdirs.push(entry);
+ }
+ });
+ iterator.close();
+ }
+
+ return subdirs;
+}
+
+function* setup_conditions(setup) {
+ do_print("Clearing existing database.");
+ Services.prefs.clearUserPref(PREF_SYSTEM_ADDON_SET);
+ distroDir.leafName = "empty";
+ startupManager(false);
+ yield promiseShutdownManager();
+
+ do_print("Setting up conditions.");
+ yield setup.setup();
+
+ startupManager(false);
+
+ // Make sure the initial state is correct
+ do_print("Checking initial state.");
+ yield check_installed(setup.initialState);
+}
+
+function* verify_state(initialState, finalState = undefined, alreadyUpgraded = false) {
+ let expectedDirs = 0;
+
+ // If the initial state was using the profile set then that directory will
+ // still exist.
+
+ if (initialState.some(a => a.isUpgrade)) {
+ expectedDirs++;
+ }
+
+ if (finalState == undefined) {
+ finalState = initialState;
+ }
+ else if (finalState.some(a => a.isUpgrade)) {
+ // If the new state is using the profile then that directory will exist.
+ expectedDirs++;
+ }
+
+ // Since upgrades are restartless now, the previous update dir hasn't been removed.
+ if (alreadyUpgraded) {
+ expectedDirs++;
+ }
+
+ do_print("Checking final state.");
+
+ let dirs = yield get_directories();
+ do_check_eq(dirs.length, expectedDirs);
+
+ yield check_installed(...finalState);
+
+ // Check that the new state is active after a restart
+ yield promiseRestartManager();
+ yield check_installed(finalState);
+}
+
+function* exec_test(setupName, testName) {
+ let setup = TEST_CONDITIONS[setupName];
+ let test = TESTS[testName];
+
+ yield setup_conditions(setup);
+
+ try {
+ if ("test" in test) {
+ yield test.test();
+ }
+ else {
+ yield installSystemAddons(yield buildSystemAddonUpdates(test.updateList, root), testserver);
+ }
+
+ if (test.fails) {
+ do_throw("Expected this test to fail");
+ }
+ }
+ catch (e) {
+ if (!test.fails) {
+ do_throw(e);
+ }
+ }
+
+ // some tests have a different expected combination of default
+ // and updated add-ons.
+ if (test.finalState && setupName in test.finalState) {
+ yield verify_state(setup.initialState, test.finalState[setupName]);
+ }
+ else {
+ yield verify_state(setup.initialState, test.finalState);
+ }
+
+ yield promiseShutdownManager();
+}
+
+add_task(function*() {
+ for (let setup of Object.keys(TEST_CONDITIONS)) {
+ for (let test of Object.keys(TESTS)) {
+ do_print("Running test " + setup + " " + test);
+
+ yield exec_test(setup, test);
+ }
+ }
+});
+
+// Some custom tests
+// Test that the update check is performed as part of the regular add-on update
+// check
+add_task(function* test_addon_update() {
+ yield setup_conditions(TEST_CONDITIONS.blank);
+
+ yield updateAllSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
+ ], root), testserver);
+
+ yield verify_state(TEST_CONDITIONS.blank.initialState, [
+ {isUpgrade: false, version: null},
+ {isUpgrade: true, version: "2.0"},
+ {isUpgrade: true, version: "2.0"},
+ {isUpgrade: false, version: null},
+ {isUpgrade: false, version: null}
+ ]);
+
+ yield promiseShutdownManager();
+});
+
+// Disabling app updates should block system add-on updates
+add_task(function* test_app_update_disabled() {
+ yield setup_conditions(TEST_CONDITIONS.blank);
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
+ yield updateAllSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
+ ], root), testserver);
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
+
+ yield verify_state(TEST_CONDITIONS.blank.initialState);
+
+ yield promiseShutdownManager();
+});
+
+// Safe mode should block system add-on updates
+add_task(function* test_safe_mode() {
+ gAppInfo.inSafeMode = true;
+
+ yield setup_conditions(TEST_CONDITIONS.blank);
+
+ Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, false);
+ yield updateAllSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
+ ], root), testserver);
+ Services.prefs.clearUserPref(PREF_APP_UPDATE_ENABLED);
+
+ yield verify_state(TEST_CONDITIONS.blank.initialState);
+
+ yield promiseShutdownManager();
+
+ gAppInfo.inSafeMode = false;
+});
+
+// Tests that a set that matches the default set does nothing
+add_task(function* test_match_default() {
+ yield setup_conditions(TEST_CONDITIONS.withAppSet);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
+ ], root), testserver);
+
+ // Shouldn't have installed an updated set
+ yield verify_state(TEST_CONDITIONS.withAppSet.initialState);
+
+ yield promiseShutdownManager();
+});
+
+// Tests that a set that matches the hidden default set works
+add_task(function* test_match_default_revert() {
+ yield setup_conditions(TEST_CONDITIONS.withBothSets);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system1@tests.mozilla.org", version: "1.0", path: "system1_1.xpi" },
+ { id: "system2@tests.mozilla.org", version: "1.0", path: "system2_1.xpi" }
+ ], root), testserver);
+
+ // This should revert to the default set instead of installing new versions
+ // into an updated set.
+ yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+ {isUpgrade: false, version: "1.0"},
+ {isUpgrade: false, version: "1.0"},
+ {isUpgrade: false, version: null},
+ {isUpgrade: false, version: null},
+ {isUpgrade: false, version: null}
+ ]);
+
+ yield promiseShutdownManager();
+});
+
+// Tests that a set that matches the current set works
+add_task(function* test_match_current() {
+ yield setup_conditions(TEST_CONDITIONS.withBothSets);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" }
+ ], root), testserver);
+
+ // This should remain with the current set instead of creating a new copy
+ let set = JSON.parse(Services.prefs.getCharPref(PREF_SYSTEM_ADDON_SET));
+ do_check_eq(set.directory, "prefilled");
+
+ yield verify_state(TEST_CONDITIONS.withBothSets.initialState);
+
+ yield promiseShutdownManager();
+});
+
+// Tests that a set with a minor change doesn't re-download existing files
+add_task(function* test_no_download() {
+ yield setup_conditions(TEST_CONDITIONS.withBothSets);
+
+ // The missing file here is unneeded since there is a local version already
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "missing.xpi" },
+ { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
+ ], root), testserver);
+
+ yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+ {isUpgrade: false, version: "1.0"},
+ {isUpgrade: true, version: "2.0"},
+ {isUpgrade: false, version: null},
+ {isUpgrade: true, version: "1.0"},
+ {isUpgrade: false, version: null}
+ ]);
+
+ yield promiseShutdownManager();
+});
+
+// Tests that a second update before a restart works
+add_task(function* test_double_update() {
+ yield setup_conditions(TEST_CONDITIONS.withAppSet);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ], root), testserver);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system3@tests.mozilla.org", version: "2.0", path: "system3_2.xpi" },
+ { id: "system4@tests.mozilla.org", version: "1.0", path: "system4_1.xpi" }
+ ], root), testserver);
+
+ yield verify_state(TEST_CONDITIONS.withAppSet.initialState, [
+ {isUpgrade: false, version: null},
+ {isUpgrade: false, version: "2.0"},
+ {isUpgrade: true, version: "2.0"},
+ {isUpgrade: true, version: "1.0"},
+ {isUpgrade: false, version: null}
+ ], true);
+
+ yield promiseShutdownManager();
+});
+
+// A second update after a restart will delete the original unused set
+add_task(function* test_update_purges() {
+ yield setup_conditions(TEST_CONDITIONS.withBothSets);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates([
+ { id: "system2@tests.mozilla.org", version: "2.0", path: "system2_2.xpi" },
+ { id: "system3@tests.mozilla.org", version: "1.0", path: "system3_1.xpi" }
+ ], root), testserver);
+
+ yield verify_state(TEST_CONDITIONS.withBothSets.initialState, [
+ {isUpgrade: false, version: "1.0"},
+ {isUpgrade: true, version: "2.0"},
+ {isUpgrade: true, version: "1.0"},
+ {isUpgrade: false, version: null},
+ {isUpgrade: false, version: null}
+ ]);
+
+ yield installSystemAddons(yield buildSystemAddonUpdates(null), testserver);
+
+ let dirs = yield get_directories();
+ do_check_eq(dirs.length, 1);
+
+ yield promiseShutdownManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_targetPlatforms.js b/toolkit/mozapps/webextensions/test/xpcshell/test_targetPlatforms.js
new file mode 100644
index 000000000..ef4f2aee5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_targetPlatforms.js
@@ -0,0 +1,146 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that the targetPlatform entries are checked when deciding
+// if an add-on is incompatible.
+
+// No targetPlatforms so should be compatible
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS
+var addon2 = {
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 2",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ "XPCShell"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS and ABI
+var addon3 = {
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ targetPlatforms: [
+ "WINNT",
+ "XPCShell_noarch-spidermonkey"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Doesn't match
+var addon4 = {
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 4",
+ targetPlatforms: [
+ "WINNT_noarch-spidermonkey",
+ "Darwin",
+ "WINNT_noarch-spidermonkey"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+// Matches the OS but since a different entry specifies ABI this doesn't match.
+var addon5 = {
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 5",
+ targetPlatforms: [
+ "XPCShell",
+ "XPCShell_foo"
+ ],
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Set up the profile
+function run_test() {
+ do_test_pending();
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ writeInstallRDFForExtension(addon2, profileDir);
+ writeInstallRDFForExtension(addon3, profileDir);
+ writeInstallRDFForExtension(addon4, profileDir);
+ writeInstallRDFForExtension(addon5, profileDir);
+
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org"],
+ function([a1, a2, a3, a4, a5]) {
+
+ do_check_neq(a1, null);
+ do_check_false(a1.appDisabled);
+ do_check_true(a1.isPlatformCompatible);
+ do_check_true(a1.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ do_check_neq(a2, null);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isPlatformCompatible);
+ do_check_true(a2.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+ do_check_in_crash_annotation(addon2.id, addon2.version);
+
+ do_check_neq(a3, null);
+ do_check_false(a3.appDisabled);
+ do_check_true(a3.isPlatformCompatible);
+ do_check_true(a3.isActive);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+ do_check_in_crash_annotation(addon3.id, addon3.version);
+
+ do_check_neq(a4, null);
+ do_check_true(a4.appDisabled);
+ do_check_false(a4.isPlatformCompatible);
+ do_check_false(a4.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+ do_check_not_in_crash_annotation(addon4.id, addon4.version);
+
+ do_check_neq(a5, null);
+ do_check_true(a5.appDisabled);
+ do_check_false(a5.isPlatformCompatible);
+ do_check_false(a5.isActive);
+ do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+ do_check_not_in_crash_annotation(addon5.id, addon5.version);
+
+ do_execute_soon(do_test_finished);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_temporary.js b/toolkit/mozapps/webextensions/test/xpcshell/test_temporary.js
new file mode 100644
index 000000000..ec9e25a0b
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_temporary.js
@@ -0,0 +1,760 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "bootstrap1@tests.mozilla.org";
+const sampleRDFManifest = {
+ id: ID,
+ version: "1.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (temporary)",
+};
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+const {Management} = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseAddonStartup() {
+ return new Promise(resolve => {
+ let listener = (extension) => {
+ Management.off("startup", listener);
+ resolve(extension);
+ };
+
+ Management.on("startup", listener);
+ });
+}
+
+BootstrapMonitor.init();
+
+// Partial list of bootstrap reasons from XPIProvider.jsm
+const BOOTSTRAP_REASONS = {
+ ADDON_INSTALL: 5,
+ ADDON_UPGRADE: 7,
+ ADDON_DOWNGRADE: 8,
+};
+
+function waitForBootstrapEvent(expectedEvent, addonId) {
+ return new Promise(resolve => {
+ const observer = {
+ observe: (subject, topic, data) => {
+ const info = JSON.parse(data);
+ const targetAddonId = info.data.id;
+ if (targetAddonId === addonId && info.event === expectedEvent) {
+ resolve(info);
+ Services.obs.removeObserver(observer);
+ } else {
+ do_print(
+ `Ignoring bootstrap event: ${info.event} for ${targetAddonId}`);
+ }
+ },
+ };
+ Services.obs.addObserver(observer, "bootstrapmonitor-event", false);
+ });
+}
+
+// Install a temporary add-on with no existing add-on present.
+// Restart and make sure it has gone away.
+add_task(function*() {
+ let extInstallCalled = false;
+ AddonManager.addInstallListener({
+ onExternalInstall: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ extInstallCalled = true;
+ },
+ });
+
+ let installingCalled = false;
+ let installedCalled = false;
+ AddonManager.addAddonListener({
+ onInstalling: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ installingCalled = true;
+ },
+ onInstalled: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "1.0");
+ installedCalled = true;
+ },
+ onInstallStarted: (aInstall) => {
+ do_throw("onInstallStarted called unexpectedly");
+ }
+ });
+
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
+
+ do_check_true(extInstallCalled);
+ do_check_true(installingCalled);
+ do_check_true(installedCalled);
+
+ const install = BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ equal(install.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ yield promiseRestartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ yield promiseRestartManager();
+});
+
+// Install a temporary add-on over the top of an existing add-on.
+// Restart and make sure the existing add-on comes back.
+add_task(function*() {
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let tempdir = gTmpD.clone();
+
+ // test that an unpacked add-on works too
+ writeInstallRDFToDir({
+ id: ID,
+ version: "3.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (temporary)",
+ }, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
+
+ let unpacked_addon = tempdir.clone();
+ unpacked_addon.append(ID);
+ do_get_file("data/test_temporary/bootstrap.js")
+ .copyTo(unpacked_addon, "bootstrap.js");
+
+ yield AddonManager.installTemporaryAddon(unpacked_addon);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "3.0");
+ BootstrapMonitor.checkAddonStarted(ID, "3.0");
+
+ addon = yield promiseAddonByID(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "3.0");
+ do_check_eq(addon.name, "Test Bootstrap 1 (temporary)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ addon = yield promiseAddonByID(ID);
+
+ // existing add-on is back
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ unpacked_addon.remove(true);
+
+ // on Windows XPI files will be locked by the JAR cache, skip this test there.
+ if (!("nsIWindowsRegKey" in Components.interfaces)) {
+ // test that a packed (XPI) add-on works
+ writeInstallRDFToXPI({
+ id: ID,
+ version: "2.0",
+ bootstrap: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (temporary)",
+ }, tempdir, "bootstrap1@tests.mozilla.org");
+
+ let packed_addon = tempdir.clone();
+ packed_addon.append(ID + ".xpi");
+
+ yield AddonManager.installTemporaryAddon(packed_addon);
+
+ addon = yield promiseAddonByID(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "2.0");
+ do_check_eq(addon.name, "Test Bootstrap 1 (temporary)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ addon = yield promiseAddonByID(ID);
+
+ // existing add-on is back
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ packed_addon.remove(false);
+
+ // test that a webextension works
+ let webext = createTempWebExtensionFile({
+ manifest: {
+ version: "4.0",
+ name: "Test WebExtension 1 (temporary)",
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }
+ });
+
+ yield Promise.all([
+ AddonManager.installTemporaryAddon(webext),
+ promiseAddonStartup(),
+ ]);
+ addon = yield promiseAddonByID(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "4.0");
+ do_check_eq(addon.name, "Test WebExtension 1 (temporary)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ // test that re-loading a webextension works, using the same filename
+ webext.remove(false);
+ webext = createTempWebExtensionFile({
+ manifest: {
+ version: "5.0",
+ name: "Test WebExtension 1 (temporary)",
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }
+ });
+
+ yield Promise.all([
+ AddonManager.installTemporaryAddon(webext),
+ promiseAddonStartup(),
+ ]);
+ addon = yield promiseAddonByID(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "5.0");
+ do_check_eq(addon.name, "Test WebExtension 1 (temporary)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ addon = yield promiseAddonByID(ID);
+
+ // existing add-on is back
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+ }
+
+ // remove original add-on
+ addon.uninstall();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseRestartManager();
+});
+
+// Install a temporary add-on over the top of an existing add-on.
+// Uninstall it and make sure the existing add-on comes back.
+add_task(function*() {
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let tempdir = gTmpD.clone();
+ writeInstallRDFToDir({
+ id: ID,
+ version: "2.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (temporary)",
+ }, tempdir);
+
+ let unpacked_addon = tempdir.clone();
+ unpacked_addon.append(ID);
+
+ let extInstallCalled = false;
+ AddonManager.addInstallListener({
+ onExternalInstall: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "2.0");
+ extInstallCalled = true;
+ },
+ });
+
+ let installingCalled = false;
+ let installedCalled = false;
+ AddonManager.addAddonListener({
+ onInstalling: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ if (!installingCalled)
+ do_check_eq(aInstall.version, "2.0");
+ installingCalled = true;
+ },
+ onInstalled: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ if (!installedCalled)
+ do_check_eq(aInstall.version, "2.0");
+ installedCalled = true;
+ },
+ onInstallStarted: (aInstall) => {
+ do_throw("onInstallStarted called unexpectedly");
+ }
+ });
+
+ yield AddonManager.installTemporaryAddon(unpacked_addon);
+
+ do_check_true(extInstallCalled);
+ do_check_true(installingCalled);
+ do_check_true(installedCalled);
+
+ let addon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "2.0");
+ do_check_eq(addon.name, "Test Bootstrap 1 (temporary)");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ addon.uninstall();
+
+ addon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ // existing add-on is back
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ unpacked_addon.remove(true);
+ addon.uninstall();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseRestartManager();
+});
+
+// Install a temporary add-on as a version upgrade over the top of an
+// existing temporary add-on.
+add_task(function*() {
+ const tempdir = gTmpD.clone();
+
+ writeInstallRDFToDir(sampleRDFManifest, tempdir,
+ "bootstrap1@tests.mozilla.org", "bootstrap.js");
+
+ const unpackedAddon = tempdir.clone();
+ unpackedAddon.append(ID);
+ do_get_file("data/test_temporary/bootstrap.js")
+ .copyTo(unpackedAddon, "bootstrap.js");
+
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ // Increment the version number, re-install it, and make sure it
+ // gets marked as an upgrade.
+ writeInstallRDFToDir(Object.assign({}, sampleRDFManifest, {
+ version: "2.0"
+ }), tempdir, "bootstrap1@tests.mozilla.org");
+
+ const onUninstall = waitForBootstrapEvent("uninstall", ID);
+ const onInstall = waitForBootstrapEvent("install", ID);
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ const uninstall = yield onUninstall;
+ equal(uninstall.data.version, "1.0");
+ equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+
+ const install = yield onInstall;
+ equal(install.data.version, "2.0");
+ equal(install.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+
+ const addon = yield promiseAddonByID(ID);
+ addon.uninstall();
+
+ unpackedAddon.remove(true);
+ yield promiseRestartManager();
+});
+
+// Install a temporary add-on as a version downgrade over the top of an
+// existing temporary add-on.
+add_task(function*() {
+ const tempdir = gTmpD.clone();
+
+ writeInstallRDFToDir(sampleRDFManifest, tempdir,
+ "bootstrap1@tests.mozilla.org", "bootstrap.js");
+
+ const unpackedAddon = tempdir.clone();
+ unpackedAddon.append(ID);
+ do_get_file("data/test_temporary/bootstrap.js")
+ .copyTo(unpackedAddon, "bootstrap.js");
+
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ // Decrement the version number, re-install, and make sure
+ // it gets marked as a downgrade.
+ writeInstallRDFToDir(Object.assign({}, sampleRDFManifest, {
+ version: "0.8"
+ }), tempdir, "bootstrap1@tests.mozilla.org");
+
+ const onUninstall = waitForBootstrapEvent("uninstall", ID);
+ const onInstall = waitForBootstrapEvent("install", ID);
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ const uninstall = yield onUninstall;
+ equal(uninstall.data.version, "1.0");
+ equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
+
+ const install = yield onInstall;
+ equal(install.data.version, "0.8");
+ equal(install.reason, BOOTSTRAP_REASONS.ADDON_DOWNGRADE);
+
+ const addon = yield promiseAddonByID(ID);
+ addon.uninstall();
+
+ unpackedAddon.remove(true);
+ yield promiseRestartManager();
+});
+
+// Installing a temporary add-on over an existing add-on with the same
+// version number should be installed as an upgrade.
+add_task(function*() {
+ const tempdir = gTmpD.clone();
+
+ writeInstallRDFToDir(sampleRDFManifest, tempdir,
+ "bootstrap1@tests.mozilla.org", "bootstrap.js");
+
+ const unpackedAddon = tempdir.clone();
+ unpackedAddon.append(ID);
+ do_get_file("data/test_temporary/bootstrap.js")
+ .copyTo(unpackedAddon, "bootstrap.js");
+
+ const onInitialInstall = waitForBootstrapEvent("install", ID);
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ const initialInstall = yield onInitialInstall;
+ equal(initialInstall.data.version, "1.0");
+ equal(initialInstall.reason, BOOTSTRAP_REASONS.ADDON_INSTALL);
+
+ // Install it again.
+ const onUninstall = waitForBootstrapEvent("uninstall", ID);
+ const onInstall = waitForBootstrapEvent("install", ID);
+ yield AddonManager.installTemporaryAddon(unpackedAddon);
+
+ const uninstall = yield onUninstall;
+ equal(uninstall.data.version, "1.0");
+ equal(uninstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+
+ const reInstall = yield onInstall;
+ equal(reInstall.data.version, "1.0");
+ equal(reInstall.reason, BOOTSTRAP_REASONS.ADDON_UPGRADE);
+
+ const addon = yield promiseAddonByID(ID);
+ addon.uninstall();
+
+ unpackedAddon.remove(true);
+ yield promiseRestartManager();
+});
+
+// Install a temporary add-on over the top of an existing disabled add-on.
+// After restart, the existing add-on should continue to be installed and disabled.
+add_task(function*() {
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let addon = yield promiseAddonByID(ID);
+
+ addon.userDisabled = true;
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ let tempdir = gTmpD.clone();
+ writeInstallRDFToDir({
+ id: ID,
+ version: "2.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Bootstrap 1 (temporary)",
+ }, tempdir, "bootstrap1@tests.mozilla.org", "bootstrap.js");
+
+ let unpacked_addon = tempdir.clone();
+ unpacked_addon.append(ID);
+ do_get_file("data/test_temporary/bootstrap.js")
+ .copyTo(unpacked_addon, "bootstrap.js");
+
+ let extInstallCalled = false;
+ AddonManager.addInstallListener({
+ onExternalInstall: (aInstall) => {
+ do_check_eq(aInstall.id, ID);
+ do_check_eq(aInstall.version, "2.0");
+ extInstallCalled = true;
+ },
+ });
+
+ yield AddonManager.installTemporaryAddon(unpacked_addon);
+
+ do_check_true(extInstallCalled);
+
+ let tempAddon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "2.0");
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ // temporary add-on is installed and started
+ do_check_neq(tempAddon, null);
+ do_check_eq(tempAddon.version, "2.0");
+ do_check_eq(tempAddon.name, "Test Bootstrap 1 (temporary)");
+ do_check_true(tempAddon.isCompatible);
+ do_check_false(tempAddon.appDisabled);
+ do_check_true(tempAddon.isActive);
+ do_check_eq(tempAddon.type, "extension");
+ do_check_eq(tempAddon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ tempAddon.uninstall();
+ unpacked_addon.remove(true);
+
+ addon.userDisabled = false;
+ addon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID);
+
+ // existing add-on is back
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ addon.uninstall();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseRestartManager();
+});
+
+// Installing a temporary add-on over a non-restartless add-on should fail.
+add_task(function*() {
+ yield promiseInstallAllFiles([do_get_addon("test_install1")], true);
+
+ let non_restartless_ID = "addon1@tests.mozilla.org";
+
+ BootstrapMonitor.checkAddonNotInstalled(non_restartless_ID);
+ BootstrapMonitor.checkAddonNotStarted(non_restartless_ID);
+
+ restartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(non_restartless_ID);
+ BootstrapMonitor.checkAddonNotStarted(non_restartless_ID);
+
+ let addon = yield promiseAddonByID(non_restartless_ID);
+
+ // non-restartless add-on is installed and started
+ do_check_neq(addon, null);
+ do_check_eq(addon.id, non_restartless_ID);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let tempdir = gTmpD.clone();
+ writeInstallRDFToDir({
+ id: non_restartless_ID,
+ version: "2.0",
+ bootstrap: true,
+ unpack: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test 1 (temporary)",
+ }, tempdir);
+
+ let unpacked_addon = tempdir.clone();
+ unpacked_addon.append(non_restartless_ID);
+
+ try {
+ yield AddonManager.installTemporaryAddon(unpacked_addon);
+ do_throw("Installing over a non-restartless add-on should return"
+ + " a rejected promise");
+ } catch (err) {
+ do_check_eq(err.message,
+ "Non-restartless add-on with ID addon1@tests.mozilla.org is"
+ + " already installed");
+ }
+
+ unpacked_addon.remove(true);
+ addon.uninstall();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+
+ yield promiseRestartManager();
+});
+
+// Installing a temporary add-on when there is already a temporary
+// add-on should fail.
+add_task(function*() {
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
+
+ let addon = yield promiseAddonByID(ID);
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Test Bootstrap 1");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_eq(addon.type, "extension");
+ do_check_false(addon.isWebExtension);
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ yield promiseRestartManager();
+
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+});
+
+// Check that a temporary add-on is marked as such.
+add_task(function*() {
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_bootstrap1_1"));
+ const addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null);
+ equal(addon.temporarilyInstalled, true);
+
+ yield promiseRestartManager();
+});
+
+// Check that a permanent add-on is not marked as temporarily installed.
+add_task(function*() {
+ yield promiseInstallAllFiles([do_get_addon("test_bootstrap1_1")], true);
+ const addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null);
+ equal(addon.temporarilyInstalled, false);
+
+ yield promiseRestartManager();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_theme.js b/toolkit/mozapps/webextensions/test/xpcshell/test_theme.js
new file mode 100644
index 000000000..84d6f1d0d
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_theme.js
@@ -0,0 +1,1139 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+
+// The maximum allowable time since install. If an add-on claims to have been
+// installed longer ago than this the the test will fail.
+const MAX_INSTALL_TIME = 10000;
+
+// This verifies that themes behave as expected
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Observer to ensure a "lightweight-theme-styling-update" notification is sent
+// when expected
+var gLWThemeChanged = false;
+var LightweightThemeObserver = {
+ observe: function(aSubject, aTopic, aData) {
+ if (aTopic != "lightweight-theme-styling-update")
+ return;
+
+ gLWThemeChanged = true;
+ }
+};
+
+AM_Cc["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService)
+ .addObserver(LightweightThemeObserver, "lightweight-theme-styling-update", false);
+
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+ writeInstallRDFForExtension({
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ type: 4,
+ skinnable: true,
+ internalName: "theme1/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ skinnable: false,
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ // We need a default theme for some of these things to work but we have hidden
+ // the one in the application directory.
+ writeInstallRDFForExtension({
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Default",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ startupManager();
+ // Make sure we only register once despite multiple calls
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+ AddonManager.addAddonListener(AddonListener);
+ AddonManager.addInstallListener(InstallListener);
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"],
+ function([d, t1, t2]) {
+ do_check_neq(d, null);
+ do_check_false(d.skinnable);
+ do_check_false(d.foreignInstall);
+ do_check_eq(d.signedState, undefined);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_eq(t1.signedState, undefined);
+ do_check_true(t1.isActive);
+ do_check_true(t1.skinnable);
+ do_check_true(t1.foreignInstall);
+ do_check_eq(t1.screenshots, null);
+ do_check_true(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL |
+ AddonManager.OP_NEEDS_RESTART_DISABLE);
+
+ do_check_neq(t2, null);
+ do_check_true(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_eq(t2.signedState, undefined);
+ do_check_false(t2.isActive);
+ do_check_false(t2.skinnable);
+ do_check_true(t2.foreignInstall);
+ do_check_eq(t2.screenshots, null);
+ do_check_false(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ do_execute_soon(run_test_1);
+ });
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Checks enabling one theme disables the others
+function run_test_1() {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onDisabling"
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_true(t1.userDisabled);
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_execute_soon(check_test_1);
+ });
+}
+
+function check_test_1() {
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme2/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
+
+ do_check_neq(t2, null);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_true(t2.isActive);
+ do_check_true(isThemeInAddonsList(profileDir, t2.id));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_eq(t2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL |
+ AddonManager.OP_NEEDS_RESTART_DISABLE);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Removing the active theme should fall back to the default (not ideal in this
+// case since we don't have the default theme installed)
+function run_test_2() {
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("theme2@tests.mozilla.org"));
+ dest.remove(true);
+
+ restartManager();
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ AddonManager.getAddonsByIDs(["theme1@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([t1, t2]) {
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+ do_check_false(t1.appDisabled);
+ do_check_false(t1.isActive);
+ do_check_false(isThemeInAddonsList(profileDir, t1.id));
+ do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
+ do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
+
+ do_check_eq(t2, null);
+ do_check_false(isThemeInAddonsList(profileDir, "theme2@tests.mozilla.org"));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Installing a lightweight theme should happen instantly and disable the default theme
+function run_test_3() {
+ writeInstallRDFForExtension({
+ id: "theme2@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme2/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+ restartManager();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(null, p1);
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.type, "theme");
+ do_check_eq(p1.description, "A test theme");
+ do_check_eq(p1.creator, "Mozilla");
+ do_check_eq(p1.homepageURL, "http://localhost/data/index.html");
+ do_check_eq(p1.iconURL, "http://localhost/data/icon.png");
+ do_check_eq(p1.screenshots.length, 1);
+ do_check_eq(p1.screenshots[0], "http://localhost/data/preview.png");
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.userDisabled);
+ do_check_true(p1.isCompatible);
+ do_check_true(p1.providesUpdatesSecurely);
+ do_check_eq(p1.blocklistState, 0);
+ do_check_true(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p1.scope, AddonManager.SCOPE_PROFILE);
+ do_check_true("isCompatibleWith" in p1);
+ do_check_true("findUpdates" in p1);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // Should have been installed sometime in the last few seconds.
+ let difference = Date.now() - p1.installDate.getTime();
+ if (difference > MAX_INSTALL_TIME)
+ do_throw("Add-on was installed " + difference + "ms ago");
+ else if (difference < 0)
+ do_throw("Add-on was installed " + difference + "ms in the future");
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "1@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_4);
+ });
+ });
+}
+
+// Installing a second lightweight theme should disable the first with no restart
+function run_test_4() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled",
+ ],
+ "2@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled",
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ }, [
+ "onExternalInstall"
+ ]);
+
+ LightweightThemeManager.currentTheme = {
+ id: "2",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A second test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsByIDs(["1@personas.mozilla.org",
+ "2@personas.mozilla.org"], function([p1, p2]) {
+ do_check_neq(null, p2);
+ do_check_false(p2.appDisabled);
+ do_check_false(p2.userDisabled);
+ do_check_true(p2.isActive);
+ do_check_eq(p2.pendingOperations, 0);
+ do_check_eq(p2.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_DISABLE);
+ do_check_eq(p2.installDate.getTime(), p2.updateDate.getTime());
+
+ // Should have been installed sometime in the last few seconds.
+ let difference = Date.now() - p2.installDate.getTime();
+ if (difference > MAX_INSTALL_TIME)
+ do_throw("Add-on was installed " + difference + "ms ago");
+ else if (difference < 0)
+ do_throw("Add-on was installed " + difference + "ms in the future");
+
+ do_check_neq(null, p1);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.isActive);
+ do_check_eq(p1.pendingOperations, 0);
+ do_check_eq(p1.permissions, AddonManager.PERM_CAN_UNINSTALL | AddonManager.PERM_CAN_ENABLE);
+
+ AddonManager.getAddonsByTypes(["theme"], function(addons) {
+ let seen = false;
+ addons.forEach(function(a) {
+ if (a.id == "2@personas.mozilla.org") {
+ seen = true;
+ }
+ else {
+ dump("Checking theme " + a.id + "\n");
+ do_check_false(a.isActive);
+ do_check_true(a.userDisabled);
+ }
+ });
+ do_check_true(seen);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_5);
+ });
+ });
+}
+
+// Switching to a custom theme should disable the lightweight theme and require
+// a restart. Cancelling that should also be possible.
+function run_test_5() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onDisabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onDisabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_true(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+ do_check_true(hasFlag(AddonManager.PERM_CAN_ENABLE, p2.permissions));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_5);
+ });
+}
+
+function check_test_5() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, t2.pendingOperations));
+ do_check_false(p2.isActive);
+ do_check_true(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, p2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_6);
+ });
+}
+
+// Switching from a custom theme to a lightweight theme should require a restart
+function run_test_6() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onOperationCancelled",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+
+ t2.userDisabled = false;
+
+ ensure_test_completed();
+
+ prepare_test({
+ "2@personas.mozilla.org": [
+ "onEnabling",
+ ],
+ "theme2@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ p2.userDisabled = false;
+
+ ensure_test_completed();
+
+ do_check_false(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_true(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(check_test_6);
+ });
+}
+
+function check_test_6() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["2@personas.mozilla.org",
+ "theme2@tests.mozilla.org"], function([p2, t2]) {
+ do_check_true(p2.isActive);
+ do_check_false(p2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_ENABLE, p2.pendingOperations));
+ do_check_false(t2.isActive);
+ do_check_true(t2.userDisabled);
+ do_check_false(hasFlag(AddonManager.PENDING_DISABLE, t2.pendingOperations));
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_7);
+ });
+}
+
+// Uninstalling a lightweight theme should not require a restart
+function run_test_7() {
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ p1.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 1);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_8);
+ });
+}
+
+// Uninstalling a lightweight theme in use should not require a restart and it
+// should reactivate the default theme
+// Also, uninstalling a lightweight theme in use should send a
+// "lightweight-theme-styling-update" notification through the observer service
+function run_test_8() {
+ prepare_test({
+ "2@personas.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ AddonManager.getAddonByID("2@personas.mozilla.org", function(p2) {
+ p2.uninstall();
+
+ ensure_test_completed();
+ do_check_eq(LightweightThemeManager.usedThemes.length, 0);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_9);
+ });
+}
+
+// Uninstalling a theme not in use should not require a restart
+function run_test_9() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+
+ t1.uninstall();
+
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(newt1) {
+ do_check_eq(newt1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_10);
+ });
+ });
+}
+
+// Uninstalling a custom theme in use should require a restart
+function run_test_10() {
+ AddonManager.getAddonByID("theme2@tests.mozilla.org", callback_soon(function(oldt2) {
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ "onEnabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ oldt2.userDisabled = false;
+
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme2@tests.mozilla.org"], function([d, t2]) {
+ do_check_true(t2.isActive);
+ do_check_false(t2.userDisabled);
+ do_check_false(t2.appDisabled);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+
+ prepare_test({
+ "theme2@tests.mozilla.org": [
+ "onUninstalling",
+ ],
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ]
+ });
+
+ t2.uninstall();
+
+ ensure_test_completed();
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_11);
+ });
+ }));
+}
+
+// Installing a custom theme not in use should not require a restart
+function run_test_11() {
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(install.addon.skinnable, true);
+ do_check_false(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_11);
+ install.install();
+ });
+}
+
+function check_test_11() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ var previewSpec = do_get_addon_root_uri(profileDir, "theme1@tests.mozilla.org") + "preview.png";
+ do_check_eq(t1.screenshots.length, 1);
+ do_check_eq(t1.screenshots[0], previewSpec);
+ do_check_true(t1.skinnable);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_12);
+ });
+}
+
+// Updating a custom theme not in use should not require a restart
+function run_test_12() {
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_false(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_12);
+ install.install();
+ });
+}
+
+function check_test_12() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", function(t1) {
+ do_check_neq(t1, null);
+ do_check_false(gLWThemeChanged);
+
+ do_execute_soon(run_test_13);
+ });
+}
+
+// Updating a custom theme in use should require a restart
+function run_test_13() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onEnabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+ restartManager();
+
+ prepare_test({ }, [
+ "onNewInstall"
+ ]);
+
+ AddonManager.getInstallForFile(do_get_addon("test_theme"), function(install) {
+ ensure_test_completed();
+
+ do_check_neq(install, null);
+ do_check_eq(install.type, "theme");
+ do_check_eq(install.version, "1.0");
+ do_check_eq(install.name, "Test Theme 1");
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ do_check_true(hasFlag(install.addon.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_INSTALL));
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onInstalling",
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_13));
+ install.install();
+ });
+ }));
+}
+
+function check_test_13() {
+ restartManager();
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(gLWThemeChanged);
+ t1.uninstall();
+ restartManager();
+
+ do_execute_soon(run_test_14);
+ }));
+}
+
+// Switching from a lightweight theme to the default theme should not require
+// a restart
+function run_test_14() {
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost/data/index.html",
+ headerURL: "http://localhost/data/header.png",
+ footerURL: "http://localhost/data/footer.png",
+ previewURL: "http://localhost/data/preview.png",
+ iconURL: "http://localhost/data/icon.png"
+ };
+
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+
+ d.userDisabled = false;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(gLWThemeChanged);
+ gLWThemeChanged = false;
+
+ do_execute_soon(run_test_15);
+ });
+}
+
+// Upgrading the application with a custom theme in use should not disable it
+function run_test_15() {
+ restartManager();
+
+ installAllFiles([do_get_addon("test_theme")], function() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ t1.userDisabled = false;
+
+ restartManager();
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ callback_soon(function([d_2, t1_2]) {
+ do_check_true(d_2.userDisabled);
+ do_check_false(d_2.appDisabled);
+ do_check_false(d_2.isActive);
+
+ do_check_false(t1_2.userDisabled);
+ do_check_false(t1_2.appDisabled);
+ do_check_true(t1_2.isActive);
+
+ restartManager("2");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d_3, t1_3]) {
+ do_check_true(d_3.userDisabled);
+ do_check_false(d_3.appDisabled);
+ do_check_false(d_3.isActive);
+
+ do_check_false(t1_3.userDisabled);
+ do_check_false(t1_3.appDisabled);
+ do_check_true(t1_3.isActive);
+
+ do_execute_soon(run_test_16);
+ });
+ }));
+ }));
+ });
+}
+
+// Upgrading the application with a custom theme in use should disable it if it
+// is no longer compatible
+function run_test_16() {
+ restartManager("3");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_true(t1.appDisabled);
+ do_check_false(t1.isActive);
+
+ do_execute_soon(run_test_17);
+ });
+}
+
+// Verifies that if the selected theme pref is changed by a different version
+// of the application that we correctly reset it when it points to an
+// incompatible theme
+function run_test_17() {
+ restartManager("2");
+ shutdownManager();
+
+ Services.prefs.setCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN, "theme1/1.0");
+
+ restartManager("3");
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d, t1]) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(t1.userDisabled);
+ do_check_true(t1.appDisabled);
+ do_check_false(t1.isActive);
+
+ do_execute_soon(run_test_18);
+ });
+}
+
+// Disabling the active theme should switch back to the default theme
+function run_test_18() {
+ restartManager(2);
+
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ t1.userDisabled = false;
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"],
+ callback_soon(function([d_2, t1_2]) {
+ do_check_true(d_2.userDisabled);
+ do_check_false(d_2.appDisabled);
+ do_check_false(d_2.isActive);
+
+ do_check_false(t1_2.userDisabled);
+ do_check_false(t1_2.appDisabled);
+ do_check_true(t1_2.isActive);
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onDisabling",
+ ],
+ "default@tests.mozilla.org": [
+ "onEnabling",
+ ]
+ });
+ t1_2.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_false(d_2.userDisabled);
+ do_check_false(d_2.appDisabled);
+ do_check_false(d_2.isActive);
+
+ do_check_true(t1_2.userDisabled);
+ do_check_false(t1_2.appDisabled);
+ do_check_true(t1_2.isActive);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "theme1@tests.mozilla.org"], function([d_3, t1_3]) {
+ do_check_false(d_3.userDisabled);
+ do_check_false(d_3.appDisabled);
+ do_check_true(d_3.isActive);
+
+ do_check_true(t1_3.userDisabled);
+ do_check_false(t1_3.appDisabled);
+ do_check_false(t1_3.isActive);
+
+ do_execute_soon(run_test_19);
+ });
+ }));
+ }));
+}
+
+// Disabling the active persona should switch back to the default theme
+function run_test_19() {
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "1@personas.mozilla.org"], function([d, p1]) {
+ p1.userDisabled = false;
+
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(p1.userDisabled);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.isActive);
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ],
+ "default@tests.mozilla.org": [
+ ["onEnabling", false],
+ "onEnabled"
+ ]
+ });
+ p1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ do_check_true(p1.userDisabled);
+ do_check_false(p1.appDisabled);
+ do_check_false(p1.isActive);
+
+ do_execute_soon(run_test_20);
+ });
+}
+
+// Tests that you cannot disable the default theme
+function run_test_20() {
+ AddonManager.getAddonByID("default@tests.mozilla.org", function(d) {
+ do_check_false(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_true(d.isActive);
+
+ try {
+ d.userDisabled = true;
+ do_throw("Disabling the default theme should throw an exception");
+ }
+ catch (e) {
+ }
+
+ do_execute_soon(run_test_21);
+ });
+}
+
+// Tests that cached copies of a lightweight theme have the right permissions
+// and pendingOperations during the onEnabling event
+function run_test_21() {
+ AddonManager.getAddonByID("theme1@tests.mozilla.org", callback_soon(function(t1) {
+ // Switch to a custom theme so we can test pendingOperations properly.
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "default@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ t1.userDisabled = false;
+ ensure_test_completed();
+
+ restartManager();
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ AddonManager.addAddonListener({
+ onEnabling: function(aAddon) {
+ do_check_false(hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
+ do_check_true(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
+
+ do_check_eq(aAddon.permissions, p1.permissions);
+ do_check_eq(aAddon.pendingOperations, p1.pendingOperations);
+ }
+ });
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+
+ p1.userDisabled = false;
+ ensure_test_completed();
+
+ run_test_22();
+ });
+ }));
+}
+
+// Detecting a new add-on during the startup file check should not disable an
+// active lightweight theme
+function run_test_22() {
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "1@personas.mozilla.org"], function([d, p1]) {
+ do_check_true(d.userDisabled);
+ do_check_false(d.appDisabled);
+ do_check_false(d.isActive);
+
+ do_check_false(p1.userDisabled);
+ do_check_false(p1.appDisabled);
+ do_check_true(p1.isActive);
+
+ writeInstallRDFForExtension({
+ id: "theme3@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 3",
+ internalName: "theme3/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }]
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
+ "1@personas.mozilla.org"], function([d_2, p1_2]) {
+ do_check_true(d_2.userDisabled);
+ do_check_false(d_2.appDisabled);
+ do_check_false(d_2.isActive);
+
+ do_check_false(p1_2.userDisabled);
+ do_check_false(p1_2.appDisabled);
+ do_check_true(p1_2.isActive);
+
+ end_test();
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_types.js b/toolkit/mozapps/webextensions/test/xpcshell/test_types.js
new file mode 100644
index 000000000..679f4808c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_types.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that custom types can be defined and undefined
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+function run_test() {
+ startupManager();
+
+ do_check_false("test" in AddonManager.addonTypes);
+ let types = AddonManager.addonTypes;
+
+ // The dumbest provider possible
+ var provider = {
+ };
+
+ var expectedAdd = "test";
+ var expectedRemove = null;
+
+ AddonManager.addTypeListener({
+ onTypeAdded: function(aType) {
+ do_check_eq(aType.id, expectedAdd);
+ expectedAdd = null;
+ },
+
+ onTypeRemoved: function(aType) {
+ do_check_eq(aType.id, expectedRemove);
+ expectedRemove = null;
+ }
+ });
+
+ AddonManagerPrivate.registerProvider(provider, [{
+ id: "test",
+ name: "Test",
+ uiPriority: 1
+ }, {
+ id: "t$e%st",
+ name: "Test",
+ uiPriority: 1
+ }]);
+
+ do_check_eq(expectedAdd, null);
+
+ do_check_true("test" in types);
+ do_check_eq(types["test"].name, "Test");
+ do_check_false("t$e%st" in types);
+
+ delete types["test"];
+ do_check_true("test" in types);
+
+ types["foo"] = "bar";
+ do_check_false("foo" in types);
+
+ expectedRemove = "test";
+
+ AddonManagerPrivate.unregisterProvider(provider);
+
+ do_check_eq(expectedRemove, null);
+
+ do_check_false("test" in AddonManager.addonTypes);
+ // The cached reference to addonTypes is live
+ do_check_false("test" in types);
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_undothemeuninstall.js b/toolkit/mozapps/webextensions/test/xpcshell/test_undothemeuninstall.js
new file mode 100644
index 000000000..36ca95aec
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_undothemeuninstall.js
@@ -0,0 +1,423 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that forcing undo for uninstall works for themes
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
+
+var defaultTheme = {
+ id: "default@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "classic/1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+var theme1 = {
+ id: "theme1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ internalName: "theme1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function dummyLWTheme(id) {
+ return {
+ id: id || Math.random().toString(),
+ name: Math.random().toString(),
+ headerURL: "http://lwttest.invalid/a.png",
+ footerURL: "http://lwttest.invalid/b.png",
+ textcolor: Math.random().toString(),
+ accentcolor: Math.random().toString()
+ };
+}
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ do_register_cleanup(promiseShutdownManager);
+
+ run_next_test();
+}
+
+add_task(function* checkDefault() {
+ writeInstallRDFForExtension(defaultTheme, profileDir);
+ yield promiseRestartManager();
+
+ let d = yield promiseAddonByID("default@tests.mozilla.org");
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+// Tests that uninstalling an enabled theme offers the option to undo
+add_task(function* uninstallEnabledOffersUndo() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
+
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+
+ t1.userDisabled = false;
+
+ yield promiseRestartManager();
+
+ let d = null;
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+// Tests that uninstalling an enabled theme can be undone
+add_task(function* canUndoUninstallEnabled() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let t1 = yield promiseAddonByID("theme1@tests.mozilla.org");
+
+ do_check_neq(t1, null);
+ do_check_true(t1.userDisabled);
+
+ t1.userDisabled = false;
+
+ yield promiseRestartManager();
+
+ let d = null;
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onOperationCancelled"
+ ],
+ "theme1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ t1.cancelUninstall();
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "theme1");
+
+ t1.uninstall();
+ yield promiseRestartManager();
+});
+
+// Tests that uninstalling a disabled theme offers the option to undo
+add_task(function* uninstallDisabledOffersUndo() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+});
+
+// Tests that uninstalling a disabled theme can be undone
+add_task(function* canUndoUninstallDisabled() {
+ writeInstallRDFForExtension(theme1, profileDir);
+
+ yield promiseRestartManager();
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "theme1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ t1.cancelUninstall();
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@tests.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_false(t1.isActive);
+ do_check_true(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ t1.uninstall();
+ yield promiseRestartManager();
+});
+
+// Tests that uninstalling an enabled lightweight theme offers the option to undo
+add_task(function* uninstallLWTOffersUndo() {
+ // skipped since lightweight themes don't support undoable uninstall yet
+ return;
+ /*
+ LightweightThemeManager.currentTheme = dummyLWTheme("theme1");
+
+ let [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_true(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_neq(t1, null);
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ prepare_test({
+ "default@tests.mozilla.org": [
+ "onEnabling"
+ ],
+ "theme1@personas.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ t1.uninstall(true);
+ ensure_test_completed();
+
+ do_check_neq(d, null);
+ do_check_false(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_ENABLE);
+
+ do_check_true(t1.isActive);
+ do_check_false(t1.userDisabled);
+ do_check_true(hasFlag(t1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+
+ yield promiseRestartManager();
+
+ [ t1, d ] = yield promiseAddonsByIDs(["theme1@personas.mozilla.org",
+ "default@tests.mozilla.org"]);
+
+ do_check_neq(d, null);
+ do_check_true(d.isActive);
+ do_check_false(d.userDisabled);
+ do_check_eq(d.pendingOperations, AddonManager.PENDING_NONE);
+
+ do_check_eq(t1, null);
+
+ do_check_eq(Services.prefs.getCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN), "classic/1.0");
+ */
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_undouninstall.js b/toolkit/mozapps/webextensions/test/xpcshell/test_undouninstall.js
new file mode 100644
index 000000000..4680a3c4a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_undouninstall.js
@@ -0,0 +1,792 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that forcing undo for uninstall works
+
+const APP_STARTUP = 1;
+const APP_SHUTDOWN = 2;
+const ADDON_ENABLE = 3;
+const ADDON_DISABLE = 4;
+const ADDON_INSTALL = 5;
+const ADDON_UNINSTALL = 6;
+const ADDON_UPGRADE = 7;
+const ADDON_DOWNGRADE = 8;
+
+const ID = "undouninstall1@tests.mozilla.org";
+const INCOMPAT_ID = "incompatible@tests.mozilla.org";
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+BootstrapMonitor.init();
+
+function getStartupReason(id) {
+ let info = BootstrapMonitor.started.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getShutdownReason(id) {
+ let info = BootstrapMonitor.stopped.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getInstallReason(id) {
+ let info = BootstrapMonitor.installed.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getUninstallReason(id) {
+ let info = BootstrapMonitor.uninstalled.get(id);
+ return info ? info.reason : undefined;
+}
+
+function getStartupOldVersion(id) {
+ let info = BootstrapMonitor.started.get(id);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getShutdownNewVersion(id) {
+ let info = BootstrapMonitor.stopped.get(id);
+ return info ? info.data.newVersion : undefined;
+}
+
+function getInstallOldVersion(id) {
+ let info = BootstrapMonitor.installed.get(id);
+ return info ? info.data.oldVersion : undefined;
+}
+
+function getUninstallNewVersion(id) {
+ let info = BootstrapMonitor.uninstalled.get(id);
+ return info ? info.data.newVersion : undefined;
+}
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ startupManager();
+ do_register_cleanup(promiseShutdownManager);
+
+ run_next_test();
+}
+
+add_task(function* installAddon() {
+ let olda1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(olda1, null);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+ yield promiseRestartManager();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+});
+
+// Uninstalling an add-on should work.
+add_task(function* uninstallAddon() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ a1.uninstall(true);
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ let list = yield promiseAddonsWithOperationsByTypes(null);
+
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dest.exists());
+ writeInstallRDFForExtension(addon1, profileDir);
+ yield promiseRestartManager();
+});
+
+// Cancelling the uninstall should send onOperationCancelled
+add_task(function* cancelUninstall() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ a1.uninstall(true);
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ do_check_eq(a1.pendingOperations, 0);
+
+ ensure_test_completed();
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+});
+
+// Uninstalling an item pending disable should still require a restart
+add_task(function* pendingDisableRequestRestart() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+ do_check_true(a1.isActive);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+
+ yield promiseRestartManager();
+});
+
+// Test that uninstalling an inactive item should still allow cancelling
+add_task(function* uninstallInactiveIsCancellable() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ yield promiseRestartManager();
+});
+
+// Test that an inactive item can be uninstalled
+add_task(function* uninstallInactive() {
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ [ "onUninstalling", false ],
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_eq(a1, null);
+});
+
+// Tests that an enabled restartless add-on can be uninstalled and goes away
+// when the uninstall is committed
+add_task(function* uninstallRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ let a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ // complete the uinstall
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(ID);
+
+ do_check_eq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+});
+
+// Tests that an enabled restartless add-on can be uninstalled and then cancelled
+add_task(function* cancelUninstallOfRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ let a1 = yield promiseAddonByID(ID);
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
+ do_check_eq(getShutdownNewVersion(ID), undefined);
+
+ startupManager(false);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+// Tests that reinstalling an enabled restartless add-on waiting to be
+// uninstalled aborts the uninstall and leaves the add-on enabled
+add_task(function* reinstallAddonAwaitingUninstall() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_UNINSTALL);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getStartupReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ shutdownManager();
+
+ do_check_eq(getShutdownReason(ID), APP_SHUTDOWN);
+
+ startupManager(false);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getStartupReason(ID), APP_STARTUP);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+// Tests that a disabled restartless add-on can be uninstalled and goes away
+// when the uninstall is committed
+add_task(function* uninstallDisabledRestartless() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ // commit the uninstall
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_eq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonNotInstalled(ID);
+ do_check_eq(getUninstallReason(ID), ADDON_UNINSTALL);
+});
+
+// Tests that a disabled restartless add-on can be uninstalled and then cancelled
+add_task(function* cancelUninstallDisabledRestartless() {
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+ ensure_test_completed();
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onDisabling", false],
+ "onDisabled"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ BootstrapMonitor.checkAddonInstalled(ID);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+// Tests that reinstalling a disabled restartless add-on waiting to be
+// uninstalled aborts the uninstall and leaves the add-on disabled
+add_task(function* reinstallDisabledAddonAwaitingUninstall() {
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_INSTALL);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ a1.userDisabled = true;
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_eq(getShutdownReason(ID), ADDON_DISABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onNewInstall",
+ "onInstallStarted",
+ "onInstallEnded"
+ ]);
+
+ yield promiseInstallAllFiles([do_get_addon("test_undouninstall1")]);
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_eq(getUninstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(getInstallReason(ID), ADDON_DOWNGRADE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ yield promiseRestartManager();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+
+ a1.uninstall();
+});
+
+
+// Test that uninstalling a temporary addon can be canceled
+add_task(function* cancelUninstallTemporary() {
+ yield AddonManager.installTemporaryAddon(do_get_addon("test_undouninstall1"));
+
+ let a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(getInstallReason(ID), ADDON_INSTALL);
+ do_check_eq(getStartupReason(ID), ADDON_ENABLE);
+ do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "undouninstall1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID("undouninstall1@tests.mozilla.org");
+
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+ do_check_eq(a1.pendingOperations, 0);
+
+ yield promiseRestartManager();
+});
+
+// Tests that cancelling the uninstall of an incompatible restartless addon
+// does not start the addon
+add_task(function* cancelUninstallIncompatibleRestartless() {
+ yield promiseInstallAllFiles([do_get_addon("test_undoincompatible")]);
+
+ let a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
+ do_check_false(a1.isActive);
+
+ prepare_test({
+ "incompatible@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall(true);
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+ do_check_false(a1.isActive);
+
+ prepare_test({
+ "incompatible@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+
+ a1 = yield promiseAddonByID(INCOMPAT_ID);
+ do_check_neq(a1, null);
+ BootstrapMonitor.checkAddonNotStarted(INCOMPAT_ID);
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_false(a1.isActive);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_uninstall.js b/toolkit/mozapps/webextensions/test/xpcshell/test_uninstall.js
new file mode 100644
index 000000000..6b12489f2
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_uninstall.js
@@ -0,0 +1,216 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-ons can be uninstalled.
+
+var addon1 = {
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ name: "Test 1",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }]
+};
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Sets up the profile by installing an add-on.
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ do_test_pending();
+ startupManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_eq(olda1, null);
+
+ writeInstallRDFForExtension(addon1, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ do_execute_soon(run_test_1);
+ });
+ }));
+}
+
+function end_test() {
+ do_execute_soon(do_test_finished);
+}
+
+// Uninstalling an add-on should work.
+function run_test_1() {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1.pendingOperations, 0);
+ do_check_neq(a1.operationsRequiringRestart &
+ AddonManager.OP_NEEDS_RESTART_UNINSTALL, 0);
+ a1.uninstall();
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+ do_check_in_crash_annotation(addon1.id, addon1.version);
+
+ ensure_test_completed();
+
+ AddonManager.getAddonsWithOperationsByTypes(null, function(list) {
+ do_check_eq(list.length, 1);
+ do_check_eq(list[0].id, "addon1@tests.mozilla.org");
+
+ do_execute_soon(check_test_1);
+ });
+ });
+}
+
+function check_test_1() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+ do_check_not_in_crash_annotation(addon1.id, addon1.version);
+
+ var dest = profileDir.clone();
+ dest.append(do_get_expected_addon_name("addon1@tests.mozilla.org"));
+ do_check_false(dest.exists());
+ writeInstallRDFForExtension(addon1, profileDir);
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Cancelling the uninstall should send onOperationCancelled
+function run_test_2() {
+ restartManager();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.pendingOperations, 0);
+ a1.uninstall();
+ do_check_true(hasFlag(a1.pendingOperations, AddonManager.PENDING_UNINSTALL));
+
+ ensure_test_completed();
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ do_check_eq(a1.pendingOperations, 0);
+
+ ensure_test_completed();
+
+ do_execute_soon(check_test_2);
+ });
+}
+
+function check_test_2() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(a1.isActive);
+ do_check_false(a1.userDisabled);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ run_test_3();
+ });
+}
+
+// Uninstalling an item pending disable should still require a restart
+function run_test_3() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onDisabling"
+ ]
+ });
+ a1.userDisabled = true;
+ ensure_test_completed();
+
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+ do_check_true(a1.isActive);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onUninstalling"
+ ]
+ });
+ a1.uninstall();
+
+ check_test_3();
+ });
+}
+
+function check_test_3() {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_true(hasFlag(AddonManager.PENDING_UNINSTALL, a1.pendingOperations));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onOperationCancelled"
+ ]
+ });
+ a1.cancelUninstall();
+ ensure_test_completed();
+ do_check_true(hasFlag(AddonManager.PENDING_DISABLE, a1.pendingOperations));
+
+ do_execute_soon(run_test_4);
+ });
+}
+
+// Test that uninstalling an inactive item should happen without a restart
+function run_test_4() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_false(a1.isActive);
+ do_check_true(a1.userDisabled);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onUninstalling", false],
+ "onUninstalled"
+ ]
+ });
+ a1.uninstall();
+ ensure_test_completed();
+
+ check_test_4();
+ });
+}
+
+function check_test_4() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_eq(a1, null);
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_update.js b/toolkit/mozapps/webextensions/test/xpcshell/test_update.js
new file mode 100644
index 000000000..4db488ab5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_update.js
@@ -0,0 +1,1398 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+// This test requires lightweight themes update to be enabled even if the app
+// doesn't support lightweight themes.
+Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
+ "%ITEM_STATUS%/%APP_ID%/%APP_VERSION%/%CURRENT_APP_VERSION%/" +
+ "%APP_OS%/%APP_ABI%/%APP_LOCALE%/%UPDATE_TYPE%";
+
+var gInstallDate;
+
+var testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.json", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+var originalSyncGUID;
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ run_next_test();
+}
+
+let testParams = [
+ { updateFile: "test_update.rdf",
+ appId: "xpcshell@tests.mozilla.org" },
+ { updateFile: "test_update.json",
+ appId: "toolkit@mozilla.org" },
+];
+
+for (let test of testParams) {
+ let { updateFile, appId } = test;
+
+ add_test(function() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "5",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ startupManager();
+
+ run_next_test();
+ });
+
+ // Verify that an update is available and can be installed.
+ let check_test_1;
+ add_test(function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_eq(a1.releaseNotesURI, null);
+ do_check_true(a1.foreignInstall);
+ do_check_neq(a1.syncGUID, null);
+
+ originalSyncGUID = a1.syncGUID;
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["applyBackgroundUpdates"]]
+ ]
+ });
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ check_test_completed();
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ do_check_eq(addon, a1);
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+ do_check_eq(install.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ // Verify that another update check returns the same AddonInstall
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function() {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(newAddon, newInstall) {
+ AddonManager.getAllInstalls(function(aInstalls2) {
+ do_check_eq(aInstalls2.length, 1);
+ do_check_eq(aInstalls2[0], install);
+ do_check_eq(newAddon, addon);
+ do_check_eq(newInstall, install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_1);
+ install.install();
+ });
+ },
+
+ onNoUpdateAvailable: function() {
+ ok(false, "Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ let run_test_2;
+ check_test_1 = (install) => {
+ ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ run_test_2(install);
+ return false;
+ };
+
+ // Continue installing the update.
+ let check_test_2;
+ run_test_2 = (install) => {
+ // Verify that another update check returns no new update
+ install.existingAddon.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should find no available update when one is already downloading");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_2);
+ install.install();
+ });
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ };
+
+ check_test_2 = () => {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_neq(olda1, null);
+ do_check_eq(olda1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ shutdownManager();
+
+ startupManager();
+
+ do_check_true(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ do_check_true(a1.foreignInstall);
+ do_check_neq(a1.syncGUID, null);
+ do_check_eq(originalSyncGUID, a1.syncGUID);
+
+ // Make sure that the extension lastModifiedTime was updated.
+ let testURI = a1.getResourceURI(TEST_UNPACKED ? "install.rdf" : "");
+ let testFile = testURI.QueryInterface(Components.interfaces.nsIFileURL).file;
+ let difference = testFile.lastModifiedTime - Date.now();
+ do_check_true(Math.abs(difference) < MAX_TIME_DIFFERENCE);
+
+ a1.uninstall();
+ run_next_test();
+ });
+ }));
+ };
+
+ // Check that an update check finds compatibility updates and applies them
+ let check_test_3;
+ add_test(function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isCompatibleWith("0", "0"));
+
+ a2.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_true(a2.isActive);
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_eq(addon, a2);
+ do_execute_soon(check_test_3);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ check_test_3 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ a2.uninstall();
+
+ run_next_test();
+ });
+ }
+
+ // Checks that we see no compatibility information when there is none.
+ add_test(function run_test_4() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5", "5"));
+ do_check_false(a3.isCompatibleWith("2", "2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen compatibility information");
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ this.sawUpdate = true;
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ // Checks that compatibility info for future apps are detected but don't make
+ // the item compatibile.
+ let check_test_5;
+ add_test(function run_test_5() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5", "5"));
+ do_check_false(a3.isCompatibleWith("2", "2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ this.sawUpdate = true;
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should have seen some compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ do_execute_soon(check_test_5);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED, "3.0", "3.0");
+ });
+ });
+
+ check_test_5 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ a3.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks work
+ let continue_test_6;
+ add_test(function run_test_6() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_6);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+
+ let check_test_6;
+ continue_test_6 = (install) => {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_6));
+ }
+
+ check_test_6 = (install) => {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ a1.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Verify the parameter escaping in update urls.
+ add_test(function run_test_8() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "67.0.5b1",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.3+",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }, {
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "0.5ab6",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 4",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 5",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ a2.userDisabled = true;
+ restartManager();
+
+ testserver.registerPathHandler("/data/param_test.rdf", function(request, response) {
+ do_check_neq(request.queryString, "");
+ let [req_version, item_id, item_version,
+ item_maxappversion, item_status,
+ app_id, app_version, current_app_version,
+ app_os, app_abi, app_locale, update_type] =
+ request.queryString.split("/").map(a => decodeURIComponent(a));
+
+ do_check_eq(req_version, "2");
+
+ switch (item_id) {
+ case "addon1@tests.mozilla.org":
+ do_check_eq(item_version, "5.0");
+ do_check_eq(item_maxappversion, "2");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "97");
+ break;
+ case "addon2@tests.mozilla.org":
+ do_check_eq(item_version, "67.0.5b1");
+ do_check_eq(item_maxappversion, "3");
+ do_check_eq(item_status, "userDisabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "49");
+ break;
+ case "addon3@tests.mozilla.org":
+ do_check_eq(item_version, "1.3+");
+ do_check_eq(item_maxappversion, "0");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "112");
+ break;
+ case "addon4@tests.mozilla.org":
+ do_check_eq(item_version, "0.5ab6");
+ do_check_eq(item_maxappversion, "5");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "2");
+ do_check_eq(update_type, "98");
+ break;
+ case "addon5@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "35");
+ break;
+ case "addon6@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "99");
+ break;
+ default:
+ ok(false, "Update request for unexpected add-on " + item_id);
+ }
+
+ do_check_eq(app_id, "xpcshell@tests.mozilla.org");
+ do_check_eq(current_app_version, "1");
+ do_check_eq(app_os, "XPCShell");
+ do_check_eq(app_abi, "noarch-spidermonkey");
+ do_check_eq(app_locale, "fr-FR");
+
+ request.setStatusLine(null, 500, "Server Error");
+ });
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1_2, a2_2, a3_2, a4_2, a5_2, a6_2]) {
+ let count = 6;
+
+ function next_test() {
+ a1_2.uninstall();
+ a2_2.uninstall();
+ a3_2.uninstall();
+ a4_2.uninstall();
+ a5_2.uninstall();
+ a6_2.uninstall();
+
+ restartManager();
+ run_next_test();
+ }
+
+ let compatListener = {
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(next_test);
+ }
+ };
+
+ let updateListener = {
+ onUpdateAvailable: function(addon, update) {
+ // Dummy so the update checker knows we care about new versions
+ },
+
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(next_test);
+ }
+ };
+
+ a1_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ a2_2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ a3_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ a4_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "2");
+ a5_2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ a6_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }));
+ });
+
+ // Tests that if an install.rdf claims compatibility then the add-on will be
+ // seen as compatible regardless of what the update.rdf says.
+ add_test(function run_test_9() {
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive, "addon4 is active");
+ do_check_true(a4.isCompatible, "addon4 is compatible");
+
+ run_next_test();
+ });
+ });
+
+ // Tests that a normal update check won't decrease a targetApplication's
+ // maxVersion.
+ add_test(function run_test_10() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible, "addon4 is compatible");
+
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+ });
+
+ // Tests that an update check for a new application will decrease a
+ // targetApplication's maxVersion.
+ add_test(function run_test_11() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible, "addon4 is not compatible");
+
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ });
+
+ // Check that the decreased maxVersion applied and disables the add-on
+ add_test(function run_test_12() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive);
+ do_check_true(a4.isCompatible);
+
+ a4.uninstall();
+ run_next_test();
+ });
+ });
+
+ // Tests that a compatibility update is passed to the listener when there is
+ // compatibility info for the current version of the app but not for the
+ // version of the app that the caller requested an update check for, when
+ // strict compatibility checking is disabled.
+ let check_test_13;
+ add_test(function run_test_13() {
+ restartManager();
+
+ // Not initially compatible but the update check will make it compatible
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+ do_check_true(a7.isCompatibleWith("0", "0"));
+
+ a7.findUpdates({
+ sawUpdate: false,
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should have seen compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+ do_execute_soon(check_test_13);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "3.0", "3.0");
+ });
+ });
+
+ check_test_13 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ a7.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks doesn't update an add-on that isn't
+ // allowed to update automatically.
+ let check_test_14;
+ add_test(function run_test_14() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ let id = aInstall.existingAddon.id;
+ ok((id == "addon1@tests.mozilla.org" || id == "addon8@tests.mozilla.org"),
+ "Saw unexpected onNewInstall for " + id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ ok(false, "Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ ok(false, "Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_check_eq(aInstall.existingAddon.pendingUpgrade.install, aInstall);
+
+ do_execute_soon(check_test_14);
+ },
+
+ onInstallFailed: function(aInstall) {
+ ok(false, "Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ ok(false, "Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+ });
+
+ check_test_14 = () => {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_neq(a8, null);
+ do_check_eq(a8.version, "1.0");
+ a8.uninstall();
+
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks doesn't update an add-on that is
+ // pending uninstall
+ let check_test_15;
+ add_test(function run_test_15() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.uninstall();
+ do_check_false(hasFlag(a8.permissions, AddonManager.PERM_CAN_UPGRADE));
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ let id = aInstall.existingAddon.id;
+ ok((id == "addon1@tests.mozilla.org" || id == "addon8@tests.mozilla.org"),
+ "Saw unexpected onNewInstall for " + id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ ok(false, "Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ ok(false, "Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_execute_soon(check_test_15);
+ },
+
+ onInstallFailed: function(aInstall) {
+ ok(false, "Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ ok(false, "Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+ });
+
+ check_test_15 = () => {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_eq(a8, null);
+
+ run_next_test();
+ });
+ }
+
+ add_test(function run_test_16() {
+ restartManager();
+
+ restartManager();
+
+ let url = "http://localhost:" + gPort + "/addons/test_install2_1.xpi";
+ AddonManager.getInstallForURL(url, function(aInstall) {
+ aInstall.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_1_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a1) {
+ do_check_neq(a1.syncGUID, null);
+ let oldGUID = a1.syncGUID;
+
+ let url_2 = "http://localhost:" + gPort + "/addons/test_install2_2.xpi";
+ AddonManager.getInstallForURL(url_2, function(aInstall_2) {
+ aInstall_2.addListener({
+ onInstallEnded: function() {
+ do_execute_soon(function install_2_2_ended() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2.syncGUID, null);
+ do_check_eq(oldGUID, a2.syncGUID);
+
+ a2.uninstall();
+ run_next_test();
+ });
+ });
+ }
+ });
+ aInstall_2.install();
+ }, "application/x-xpinstall");
+ });
+ });
+ }
+ });
+ aInstall.install();
+ }, "application/x-xpinstall");
+ });
+
+ // Test that the update check correctly observes the
+ // extensions.strictCompatibility pref and compatibility overrides.
+ add_test(function run_test_17() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ equal(aInstall.existingAddon.id, "addon9@tests.mozilla.org",
+ "Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "3.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ AddonManager.getAddonByID("addon9@tests.mozilla.org", function(a9) {
+ a9.uninstall();
+ run_next_test();
+ });
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+
+ // Tests that compatibility updates are applied to addons when the updated
+ // compatibility data wouldn't match with strict compatibility enabled.
+ add_test(function run_test_18() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon10@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 10",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon10@tests.mozilla.org", function(a10) {
+ do_check_neq(a10, null);
+
+ a10.findUpdates({
+ onNoCompatibilityUpdateAvailable: function() {
+ ok(false, "Should have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ a10.uninstall();
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ // Test that the update check correctly observes when an addon opts-in to
+ // strict compatibility checking.
+ add_test(function run_test_19() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ ok(false, "Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ a11.uninstall();
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ // Test that the update succeeds when the update.rdf URN contains a type prefix
+ // different from the add-on type
+ let continue_test_20;
+ add_test(function run_test_20() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon12@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 12",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_20);
+
+ AddonManagerPrivate.backgroundUpdateCheck();
+ });
+
+ let check_test_20;
+ continue_test_20 = (install) => {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon12@tests.mozilla.org");
+
+ prepare_test({
+ "addon12@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_20));
+ }
+
+ check_test_20 = (install) => {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon12@tests.mozilla.org", function(a12) {
+ do_check_neq(a12, null);
+ do_check_eq(a12.version, "2.0");
+ do_check_eq(a12.type, "extension");
+ a12.uninstall();
+
+ do_execute_soon(() => {
+ restartManager();
+ run_next_test()
+ });
+ });
+ }
+
+ add_task(function* cleanup() {
+ let addons = yield new Promise(resolve => {
+ AddonManager.getAddonsByTypes(["extension"], resolve);
+ });
+
+ for (let addon of addons)
+ addon.uninstall();
+
+ yield promiseRestartManager();
+
+ shutdownManager();
+
+ yield new Promise(do_execute_soon);
+ });
+}
+
+// Test that background update checks work for lightweight themes
+add_test(function run_test_7() {
+ startupManager();
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ };
+
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ // Server will specify an expiry in one year.
+ let expiry = new Date();
+ expiry.setFullYear(expiry.getFullYear() + 1);
+ response.setHeader("Expires", expiry.toUTCString(), false);
+ response.write(JSON.stringify({
+ id: "1",
+ version: "2",
+ name: "Updated Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index2.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.installDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+});
+
+function check_test_7() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.updateDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ run_next_test();
+ });
+}
+
+// Test that background update checks for lightweight themes do not use the cache
+// The update body from test 7 shouldn't be used since the cache should be bypassed.
+add_test(function () {
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ response.write(JSON.stringify({
+ id: "1",
+ version: "3",
+ name: "Updated Theme v.3",
+ description: "A test theme v.3",
+ author: "John Smith",
+ homepageURL: "http://localhost:" + gPort + "/data/index3.html?v=3",
+ headerURL: "http://localhost:" + gPort + "/data/header.png?v=3",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png?v=3",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png?v=3",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png?v=3",
+ updateURL: "https://localhost:" + gPort + "/data/lwtheme.js?v=3"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7_cache);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+});
+
+function check_test_7_cache() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ let currentTheme = LightweightThemeManager.currentTheme;
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "3");
+ do_check_eq(p1.name, "Updated Theme v.3");
+ do_check_eq(p1.description, "A test theme v.3");
+ do_print(JSON.stringify(p1));
+ do_check_eq(p1.creator.name, "John Smith");
+ do_check_eq(p1.homepageURL, "http://localhost:" + gPort + "/data/index3.html?v=3");
+ do_check_eq(p1.screenshots[0].url, "http://localhost:" + gPort + "/data/preview.png?v=3");
+ do_check_eq(p1.iconURL, "http://localhost:" + gPort + "/data/icon2.png?v=3");
+ do_check_eq(currentTheme.headerURL, "http://localhost:" + gPort + "/data/header.png?v=3");
+ do_check_eq(currentTheme.footerURL, "http://localhost:" + gPort + "/data/footer.png?v=3");
+ do_check_eq(currentTheme.updateURL, "https://localhost:" + gPort + "/data/lwtheme.js?v=3");
+
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ run_next_test();
+ });
+}
+
+// Test that the update check returns nothing for addons in locked install
+// locations.
+add_test(function run_test_locked_install() {
+ const lockedDir = gProfD.clone();
+ lockedDir.append("locked_extensions");
+ registerDirectory("XREAppFeat", lockedDir);
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon13@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 13",
+ }, lockedDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon13@tests.mozilla.org", function(a13) {
+ do_check_neq(a13, null);
+
+ a13.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ ok(false, "Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ ok(true, "Should have seen an onUpdateFinished");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+
+ AddonManager.getAllInstalls(aInstalls => {
+ do_check_eq(aInstalls.length, 0);
+ });
+ run_next_test();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_updateCancel.js b/toolkit/mozapps/webextensions/test/xpcshell/test_updateCancel.js
new file mode 100644
index 000000000..b50fd4a55
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_updateCancel.js
@@ -0,0 +1,138 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Test cancelling add-on update checks while in progress (bug 925389)
+
+Components.utils.import("resource://gre/modules/Promise.jsm");
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+// Set up an HTTP server to respond to update requests
+Components.utils.import("resource://testing-common/httpd.js");
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+function run_test() {
+ // Kick off the task-based tests...
+ run_next_test();
+}
+
+// Install one extension
+// Start download of update check (but delay HTTP response)
+// Cancel update check
+// - ensure we get cancel notification
+// complete HTTP response
+// - ensure no callbacks after cancel
+// - ensure update is gone
+
+// Create an addon update listener containing a promise
+// that resolves when the update is cancelled
+function makeCancelListener() {
+ let updated = Promise.defer();
+ return {
+ onUpdateAvailable: function(addon, install) {
+ updated.reject("Should not have seen onUpdateAvailable notification");
+ },
+
+ onUpdateFinished: function(aAddon, aError) {
+ do_print("onUpdateCheckFinished: " + aAddon.id + " " + aError);
+ updated.resolve(aError);
+ },
+ promise: updated.promise
+ };
+}
+
+// Set up the HTTP server so that we can control when it responds
+var httpReceived = Promise.defer();
+function dataHandler(aRequest, aResponse) {
+ aResponse.processAsync();
+ httpReceived.resolve([aRequest, aResponse]);
+}
+var testserver = new HttpServer();
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+testserver.registerPathHandler("/data/test_update.rdf", dataHandler);
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// Set up an add-on for update check
+writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_update.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+}, profileDir);
+
+add_task(function* cancel_during_check() {
+ startupManager();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_neq(a1, null);
+
+ let listener = makeCancelListener();
+ a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+
+ // Wait for the http request to arrive
+ let [request, response] = yield httpReceived.promise;
+
+ // cancelUpdate returns true if there is an update check in progress
+ do_check_true(a1.cancelUpdate());
+
+ let updateResult = yield listener.promise;
+ do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
+
+ // Now complete the HTTP request
+ let file = do_get_cwd();
+ file.append("data");
+ file.append("test_update.rdf");
+ let data = loadFile(file);
+ response.write(data);
+ response.finish();
+
+ // trying to cancel again should return false, i.e. nothing to cancel
+ do_check_false(a1.cancelUpdate());
+
+ yield true;
+});
+
+// Test that update check is cancelled if the XPI provider shuts down while
+// the update check is in progress
+add_task(function* shutdown_during_check() {
+ // Reset our HTTP listener
+ httpReceived = Promise.defer();
+
+ let a1 = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_neq(a1, null);
+
+ let listener = makeCancelListener();
+ a1.findUpdates(listener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+
+ // Wait for the http request to arrive
+ let [request, response] = yield httpReceived.promise;
+
+ shutdownManager();
+
+ let updateResult = yield listener.promise;
+ do_check_eq(AddonManager.UPDATE_STATUS_CANCELLED, updateResult);
+
+ // Now complete the HTTP request
+ let file = do_get_cwd();
+ file.append("data");
+ file.append("test_update.rdf");
+ let data = loadFile(file);
+ response.write(data);
+ response.finish();
+
+ yield testserver.stop(Promise.defer().resolve);
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_update_compatmode.js b/toolkit/mozapps/webextensions/test/xpcshell/test_update_compatmode.js
new file mode 100644
index 000000000..a1d872009
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_update_compatmode.js
@@ -0,0 +1,184 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update check correctly fills in the
+// %COMPATIBILITY_MODE% token in the update URL.
+
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+Components.utils.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_updatecompatmode_ignore.rdf", testserver);
+mapFile("/data/test_updatecompatmode_normal.rdf", testserver);
+mapFile("/data/test_updatecompatmode_strict.rdf", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ do_test_pending();
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ writeInstallRDFForExtension({
+ id: "compatmode-normal@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - normal"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-strict@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - strict"
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-strict-optin@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - strict opt-in",
+ strictCompatibility: true
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "compatmode-ignore@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/test_updatecompatmode_%COMPATIBILITY_MODE%.rdf",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon - ignore",
+ }, profileDir);
+
+ startupManager();
+ run_test_1();
+}
+
+function end_test() {
+ testserver.stop(do_test_finished);
+}
+
+
+// Strict compatibility checking disabled.
+function run_test_1() {
+ do_print("Testing with strict compatibility checking disabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ AddonManager.getAddonByID("compatmode-normal@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(unused, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ run_test_2();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Strict compatibility checking enabled.
+function run_test_2() {
+ do_print("Testing with strict compatibility checking enabled");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+ AddonManager.getAddonByID("compatmode-strict@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(unused, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ run_test_3();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Strict compatibility checking opt-in.
+function run_test_3() {
+ do_print("Testing with strict compatibility disabled, but addon opt-in");
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+ AddonManager.getAddonByID("compatmode-strict-optin@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ run_test_4();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
+
+// Compatibility checking disabled.
+function run_test_4() {
+ do_print("Testing with all compatibility checking disabled");
+ AddonManager.checkCompatibility = false;
+ AddonManager.getAddonByID("compatmode-ignore@tests.mozilla.org", function(addon) {
+ do_check_neq(addon, null);
+ addon.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should have not have seen compatibility information");
+ },
+
+ onNoUpdateAvailable: function() {
+ do_throw("Should have seen an available update");
+ },
+
+ onUpdateAvailable: function(unused, install) {
+ do_check_eq(install.version, "2.0")
+ },
+
+ onUpdateFinished: function() {
+ end_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_update_ignorecompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_update_ignorecompat.js
new file mode 100644
index 000000000..e899619d6
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_update_ignorecompat.js
@@ -0,0 +1,107 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work correctly when compatibility
+// check is disabled.
+
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+var testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.json", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+let testParams = [
+ { updateFile: "test_update.rdf",
+ appId: "xpcshell@tests.mozilla.org" },
+ { updateFile: "test_update.json",
+ appId: "toolkit@mozilla.org" },
+];
+
+for (let test of testParams) {
+ let { updateFile, appId } = test;
+
+ // Test that the update check correctly observes the
+ // extensions.strictCompatibility pref and compatibility overrides.
+ add_test(function () {
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ if (aInstall.existingAddon.id != "addon9@tests.mozilla.org")
+ do_throw("Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "4.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ run_next_test();
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/" + updateFile);
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+
+ // Test that the update check correctly observes when an addon opts-in to
+ // strict compatibility checking.
+ add_test(function () {
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ do_throw("Should not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ do_throw("Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_update_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_update_strictcompat.js
new file mode 100644
index 000000000..2b1f65a9f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_update_strictcompat.js
@@ -0,0 +1,1126 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that add-on update checks work
+
+const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS";
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+// This test requires lightweight themes update to be enabled even if the app
+// doesn't support lightweight themes.
+Services.prefs.setBoolPref("lightweightThemes.update.enabled", true);
+
+Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
+
+const PARAMS = "?%REQ_VERSION%/%ITEM_ID%/%ITEM_VERSION%/%ITEM_MAXAPPVERSION%/" +
+ "%ITEM_STATUS%/%APP_ID%/%APP_VERSION%/%CURRENT_APP_VERSION%/" +
+ "%APP_OS%/%APP_ABI%/%APP_LOCALE%/%UPDATE_TYPE%";
+
+var gInstallDate;
+
+var testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+mapFile("/data/test_update.rdf", testserver);
+mapFile("/data/test_update.json", testserver);
+mapFile("/data/test_update.xml", testserver);
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ Services.prefs.setBoolPref(PREF_MATCH_OS_LOCALE, false);
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+
+ run_next_test();
+}
+
+let testParams = [
+ { updateFile: "test_update.rdf",
+ appId: "xpcshell@tests.mozilla.org" },
+ { updateFile: "test_update.json",
+ appId: "toolkit@mozilla.org" },
+];
+
+for (let test of testParams) {
+ let { updateFile, appId } = test;
+
+ add_test(function() {
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "5",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ startupManager();
+
+ run_next_test();
+ });
+
+ // Verify that an update is available and can be installed.
+ let check_test_1;
+ add_test(function run_test_1() {
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "1.0");
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
+ do_check_eq(a1.releaseNotesURI, null);
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ ["onPropertyChanged", ["applyBackgroundUpdates"]]
+ ]
+ });
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+ check_test_completed();
+
+ a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ prepare_test({}, [
+ "onNewInstall",
+ ]);
+
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ensure_test_completed();
+
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ do_check_eq(addon, a1);
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+ do_check_eq(install.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ // Verify that another update check returns the same AddonInstall
+ a1.findUpdates({
+ onNoCompatibilityUpdateAvailable: function() {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function(newAddon, newInstall) {
+ AddonManager.getAllInstalls(function(aInstalls2) {
+ do_check_eq(aInstalls2.length, 1);
+ do_check_eq(aInstalls2[0], install);
+ do_check_eq(newAddon, addon);
+ do_check_eq(newInstall, install);
+
+ prepare_test({}, [
+ "onDownloadStarted",
+ "onDownloadEnded",
+ ], check_test_1);
+ install.install();
+ });
+ },
+
+ onNoUpdateAvailable: function() {
+ ok(false, "Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoUpdateAvailable notification");
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ let run_test_2;
+ check_test_1 = (install) => {
+ ensure_test_completed();
+ do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
+ run_test_2(install);
+ return false;
+ };
+
+ // Continue installing the update.
+ let check_test_2;
+ run_test_2 = (install) => {
+ // Verify that another update check returns no new update
+ install.existingAddon.findUpdates({
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen onNoCompatibilityUpdateAvailable notification");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should find no available update when one is already downloading");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ AddonManager.getAllInstalls(function(aInstalls) {
+ do_check_eq(aInstalls.length, 1);
+ do_check_eq(aInstalls[0], install);
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], check_test_2);
+ install.install();
+ });
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ };
+
+ check_test_2 = () => {
+ ensure_test_completed();
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", callback_soon(function(olda1) {
+ do_check_neq(olda1, null);
+ do_check_eq(olda1.version, "1.0");
+ do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
+
+ shutdownManager();
+
+ startupManager();
+
+ do_check_true(isExtensionInAddonsList(profileDir, "addon1@tests.mozilla.org"));
+
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+ do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+
+ a1.uninstall();
+ run_next_test();
+ });
+ }));
+ };
+
+
+ // Check that an update check finds compatibility updates and applies them
+ let check_test_3;
+ add_test(function run_test_3() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_false(a2.isActive);
+ do_check_false(a2.isCompatible);
+ do_check_true(a2.appDisabled);
+ do_check_true(a2.isCompatibleWith("0", "0"));
+
+ a2.findUpdates({
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ do_check_false(a2.isActive);
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_eq(addon, a2);
+ do_execute_soon(check_test_3);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ check_test_3 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) {
+ do_check_neq(a2, null);
+ do_check_true(a2.isActive);
+ do_check_true(a2.isCompatible);
+ do_check_false(a2.appDisabled);
+ a2.uninstall();
+
+ run_next_test();
+ });
+ }
+
+ // Checks that we see no compatibility information when there is none.
+ add_test(function run_test_4() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5", "5"));
+ do_check_false(a3.isCompatibleWith("2", "2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen compatibility information");
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ this.sawUpdate = true;
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ // Checks that compatibility info for future apps are detected but don't make
+ // the item compatibile.
+ let check_test_5;
+ add_test(function run_test_5() {
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_true(a3.isCompatibleWith("5", "5"));
+ do_check_false(a3.isCompatibleWith("2", "2"));
+
+ a3.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+ do_check_false(a3.isActive);
+ this.sawUpdate = true;
+ },
+
+ onNoCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should have seen some compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onNoUpdateAvailable: function(addon) {
+ do_check_true(this.sawUpdate);
+ do_execute_soon(check_test_5);
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED, "3.0", "3.0");
+ });
+ });
+
+ check_test_5 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon3@tests.mozilla.org", function(a3) {
+ do_check_neq(a3, null);
+ do_check_false(a3.isActive);
+ do_check_false(a3.isCompatible);
+ do_check_true(a3.appDisabled);
+
+ a3.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks work
+ let continue_test_6;
+ add_test(function run_test_6() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+ restartManager();
+
+ prepare_test({}, [
+ "onNewInstall",
+ "onDownloadStarted",
+ "onDownloadEnded"
+ ], continue_test_6);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+
+ let check_test_6;
+ continue_test_6 = (install) => {
+ do_check_neq(install.existingAddon, null);
+ do_check_eq(install.existingAddon.id, "addon1@tests.mozilla.org");
+
+ prepare_test({
+ "addon1@tests.mozilla.org": [
+ "onInstalling"
+ ]
+ }, [
+ "onInstallStarted",
+ "onInstallEnded",
+ ], callback_soon(check_test_6));
+ }
+
+ check_test_6 = (install) => {
+ do_check_eq(install.existingAddon.pendingUpgrade.install, install);
+
+ restartManager();
+ AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+ a1.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Verify the parameter escaping in update urls.
+ add_test(function run_test_8() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "67.0.5b1",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 2",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.3+",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }, {
+ id: "toolkit@mozilla.org",
+ minVersion: "0",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "0.5ab6",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "5"
+ }],
+ name: "Test Addon 4",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon5@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 5",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon6@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/param_test.rdf" + PARAMS,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 6",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon2@tests.mozilla.org", callback_soon(function(a2) {
+ a2.userDisabled = true;
+ restartManager();
+
+ testserver.registerPathHandler("/data/param_test.rdf", function(request, response) {
+ do_check_neq(request.queryString, "");
+ let [req_version, item_id, item_version,
+ item_maxappversion, item_status,
+ app_id, app_version, current_app_version,
+ app_os, app_abi, app_locale, update_type] =
+ request.queryString.split("/").map(a => decodeURIComponent(a));
+
+ do_check_eq(req_version, "2");
+
+ switch (item_id) {
+ case "addon1@tests.mozilla.org":
+ do_check_eq(item_version, "5.0");
+ do_check_eq(item_maxappversion, "2");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "97");
+ break;
+ case "addon2@tests.mozilla.org":
+ do_check_eq(item_version, "67.0.5b1");
+ do_check_eq(item_maxappversion, "3");
+ do_check_eq(item_status, "userDisabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "49");
+ break;
+ case "addon3@tests.mozilla.org":
+ do_check_eq(item_version, "1.3+");
+ do_check_eq(item_maxappversion, "0");
+ do_check_eq(item_status, "userEnabled,incompatible");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "112");
+ break;
+ case "addon4@tests.mozilla.org":
+ do_check_eq(item_version, "0.5ab6");
+ do_check_eq(item_maxappversion, "5");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "2");
+ do_check_eq(update_type, "98");
+ break;
+ case "addon5@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "35");
+ break;
+ case "addon6@tests.mozilla.org":
+ do_check_eq(item_version, "1.0");
+ do_check_eq(item_maxappversion, "1");
+ do_check_eq(item_status, "userEnabled");
+ do_check_eq(app_version, "1");
+ do_check_eq(update_type, "99");
+ break;
+ default:
+ ok(false, "Update request for unexpected add-on " + item_id);
+ }
+
+ do_check_eq(app_id, "xpcshell@tests.mozilla.org");
+ do_check_eq(current_app_version, "1");
+ do_check_eq(app_os, "XPCShell");
+ do_check_eq(app_abi, "noarch-spidermonkey");
+ do_check_eq(app_locale, "fr-FR");
+
+ request.setStatusLine(null, 500, "Server Error");
+ });
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org",
+ "addon5@tests.mozilla.org",
+ "addon6@tests.mozilla.org"],
+ function([a1_2, a2_2, a3_2, a4_2, a5_2, a6_2]) {
+ let count = 6;
+
+ function next_test() {
+ a1_2.uninstall();
+ a2_2.uninstall();
+ a3_2.uninstall();
+ a4_2.uninstall();
+ a5_2.uninstall();
+ a6_2.uninstall();
+
+ restartManager();
+ run_next_test();
+ }
+
+ let compatListener = {
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(next_test);
+ }
+ };
+
+ let updateListener = {
+ onUpdateAvailable: function(addon, update) {
+ // Dummy so the update checker knows we care about new versions
+ },
+
+ onUpdateFinished: function(addon, error) {
+ if (--count == 0)
+ do_execute_soon(next_test);
+ }
+ };
+
+ a1_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ a2_2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_ADDON_INSTALLED);
+ a3_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ a4_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "2");
+ a5_2.findUpdates(compatListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ a6_2.findUpdates(updateListener, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ }));
+ });
+
+ // Tests that if an install.rdf claims compatibility then the add-on will be
+ // seen as compatible regardless of what the update.rdf says.
+ add_test(function run_test_9() {
+ writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "5.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_true(a4.isActive, "addon4 is active");
+ do_check_true(a4.isCompatible, "addon4 is compatible");
+
+ run_next_test();
+ });
+ });
+
+ // Tests that a normal update check won't decrease a targetApplication's
+ // maxVersion.
+ add_test(function run_test_10() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible, "addon4 is compatible");
+
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
+ });
+ });
+
+ // Tests that an update check for a new application will decrease a
+ // targetApplication's maxVersion.
+ add_test(function run_test_11() {
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ a4.findUpdates({
+ onUpdateFinished: function(addon) {
+ do_check_false(addon.isCompatible, "addon4 is compatible");
+
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED);
+ });
+ });
+
+ // Check that the decreased maxVersion applied and disables the add-on
+ add_test(function run_test_12() {
+ restartManager();
+
+ AddonManager.getAddonByID("addon4@tests.mozilla.org", function(a4) {
+ do_check_false(a4.isActive, "addon4 is active");
+ do_check_false(a4.isCompatible, "addon4 is compatible");
+
+ a4.uninstall();
+ run_next_test();
+ });
+ });
+
+ // Tests that no compatibility update is passed to the listener when there is
+ // compatibility info for the current version of the app but not for the
+ // version of the app that the caller requested an update check for.
+ let check_test_13;
+ add_test(function run_test_13() {
+ restartManager();
+
+ // Not initially compatible but the update check will make it compatible
+ writeInstallRDFForExtension({
+ id: "addon7@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0",
+ maxVersion: "0"
+ }],
+ name: "Test Addon 7",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_false(a7.isActive);
+ do_check_false(a7.isCompatible);
+ do_check_true(a7.appDisabled);
+ do_check_true(a7.isCompatibleWith("0", "0"));
+
+ a7.findUpdates({
+ sawUpdate: false,
+ onCompatibilityUpdateAvailable: function(addon) {
+ ok(false, "Should not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function(addon, install) {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function(addon) {
+ do_check_true(addon.isCompatible);
+ do_execute_soon(check_test_13);
+ }
+ }, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED, "3.0", "3.0");
+ });
+ });
+
+ check_test_13 = () => {
+ restartManager();
+ AddonManager.getAddonByID("addon7@tests.mozilla.org", function(a7) {
+ do_check_neq(a7, null);
+ do_check_true(a7.isActive);
+ do_check_true(a7.isCompatible);
+ do_check_false(a7.appDisabled);
+
+ a7.uninstall();
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks doesn't update an add-on that isn't
+ // allowed to update automatically.
+ let check_test_14;
+ add_test(function run_test_14() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ let id = aInstall.existingAddon.id;
+ ok((id == "addon1@tests.mozilla.org" || id == "addon8@tests.mozilla.org"),
+ "Saw unexpected onNewInstall for " + id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ ok(false, "Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ ok(false, "Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_check_eq(aInstall.existingAddon.pendingUpgrade.install, aInstall);
+
+ do_execute_soon(check_test_14);
+ },
+
+ onInstallFailed: function(aInstall) {
+ ok(false, "Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ ok(false, "Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+ });
+
+ check_test_14 = () => {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_neq(a8, null);
+ do_check_eq(a8.version, "1.0");
+ a8.uninstall();
+
+ run_next_test();
+ });
+ }
+
+ // Test that background update checks doesn't update an add-on that is
+ // pending uninstall
+ let check_test_15;
+ add_test(function run_test_15() {
+ restartManager();
+
+ // Have an add-on there that will be updated so we see some events from it
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ }, profileDir);
+
+ writeInstallRDFForExtension({
+ id: "addon8@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 8",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon8@tests.mozilla.org", function(a8) {
+ a8.uninstall();
+ do_check_false(hasFlag(a8.permissions, AddonManager.PERM_CAN_UPGRADE));
+
+ // The background update check will find updates for both add-ons but only
+ // proceed to install one of them.
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ let id = aInstall.existingAddon.id;
+ ok((id == "addon1@tests.mozilla.org" || id == "addon8@tests.mozilla.org"),
+ "Saw unexpected onNewInstall for " + id);
+ },
+
+ onDownloadStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onDownloadFailed: function(aInstall) {
+ ok(false, "Should not have seen onDownloadFailed event");
+ },
+
+ onDownloadCancelled: function(aInstall) {
+ ok(false, "Should not have seen onDownloadCancelled event");
+ },
+
+ onInstallStarted: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ },
+
+ onInstallEnded: function(aInstall) {
+ do_check_eq(aInstall.existingAddon.id, "addon1@tests.mozilla.org");
+ do_execute_soon(check_test_15);
+ },
+
+ onInstallFailed: function(aInstall) {
+ ok(false, "Should not have seen onInstallFailed event");
+ },
+
+ onInstallCancelled: function(aInstall) {
+ ok(false, "Should not have seen onInstallCancelled event");
+ },
+ });
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+ });
+
+ check_test_15 = () => {
+ restartManager();
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon8@tests.mozilla.org"], function([a1, a8]) {
+ do_check_neq(a1, null);
+ do_check_eq(a1.version, "2.0");
+ a1.uninstall();
+
+ do_check_eq(a8, null);
+
+ run_next_test();
+ });
+ }
+
+ // Test that the update check correctly observes the
+ // extensions.strictCompatibility pref and compatibility overrides.
+ add_test(function run_test_17() {
+ restartManager();
+
+ writeInstallRDFForExtension({
+ id: "addon9@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 9",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.addInstallListener({
+ onNewInstall: function(aInstall) {
+ equal(aInstall.existingAddon.id, "addon9@tests.mozilla.org",
+ "Saw unexpected onNewInstall for " + aInstall.existingAddon.id);
+ do_check_eq(aInstall.version, "2.0");
+ },
+ onDownloadFailed: function(aInstall) {
+ AddonManager.getAddonByID("addon9@tests.mozilla.org", function(a9) {
+ a9.uninstall();
+ run_next_test();
+ });
+ }
+ });
+
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setCharPref(PREF_GETADDONS_BYIDS_PERFORMANCE,
+ "http://localhost:" + gPort + "/data/test_update.xml");
+ Services.prefs.setBoolPref(PREF_GETADDONS_CACHE_ENABLED, true);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+
+ // Test that the update check correctly observes when an addon opts-in to
+ // strict compatibility checking.
+ add_test(function run_test_19() {
+ restartManager();
+ writeInstallRDFForExtension({
+ id: "addon11@tests.mozilla.org",
+ version: "1.0",
+ updateURL: "http://localhost:" + gPort + "/data/" + updateFile,
+ targetApplications: [{
+ id: appId,
+ minVersion: "0.1",
+ maxVersion: "0.2"
+ }],
+ name: "Test Addon 11",
+ }, profileDir);
+ restartManager();
+
+ AddonManager.getAddonByID("addon11@tests.mozilla.org", function(a11) {
+ do_check_neq(a11, null);
+
+ a11.findUpdates({
+ onCompatibilityUpdateAvailable: function() {
+ ok(false, "Should have not have seen compatibility information");
+ },
+
+ onUpdateAvailable: function() {
+ ok(false, "Should not have seen an available update");
+ },
+
+ onUpdateFinished: function() {
+ run_next_test();
+ }
+ }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ });
+ });
+
+ add_task(function* cleanup() {
+ let addons = yield new Promise(resolve => {
+ AddonManager.getAddonsByTypes(["extension"], resolve);
+ });
+
+ for (let addon of addons)
+ addon.uninstall();
+
+ yield promiseRestartManager();
+
+ shutdownManager();
+
+ yield new Promise(do_execute_soon);
+ });
+}
+
+// Test that background update checks work for lightweight themes
+add_test(function run_test_7() {
+ startupManager();
+
+ LightweightThemeManager.currentTheme = {
+ id: "1",
+ version: "1",
+ name: "Test LW Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ };
+
+ // XXX The lightweight theme manager strips non-https updateURLs so hack it
+ // back in.
+ let themes = JSON.parse(Services.prefs.getCharPref("lightweightThemes.usedThemes"));
+ do_check_eq(themes.length, 1);
+ themes[0].updateURL = "http://localhost:" + gPort + "/data/lwtheme.js";
+ Services.prefs.setCharPref("lightweightThemes.usedThemes", JSON.stringify(themes));
+
+ testserver.registerPathHandler("/data/lwtheme.js", function(request, response) {
+ response.write(JSON.stringify({
+ id: "1",
+ version: "2",
+ name: "Updated Theme",
+ description: "A test theme",
+ author: "Mozilla",
+ homepageURL: "http://localhost:" + gPort + "/data/index2.html",
+ headerURL: "http://localhost:" + gPort + "/data/header.png",
+ footerURL: "http://localhost:" + gPort + "/data/footer.png",
+ previewURL: "http://localhost:" + gPort + "/data/preview.png",
+ iconURL: "http://localhost:" + gPort + "/data/icon2.png",
+ updateURL: "http://localhost:" + gPort + "/data/lwtheme.js"
+ }));
+ });
+
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "1");
+ do_check_eq(p1.name, "Test LW Theme");
+ do_check_true(p1.isActive);
+ do_check_eq(p1.installDate.getTime(), p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.installDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ prepare_test({
+ "1@personas.mozilla.org": [
+ ["onInstalling", false],
+ "onInstalled"
+ ]
+ }, [
+ "onExternalInstall"
+ ], check_test_7);
+
+ AddonManagerInternal.backgroundUpdateCheck();
+ });
+});
+
+function check_test_7() {
+ AddonManager.getAddonByID("1@personas.mozilla.org", function(p1) {
+ do_check_neq(p1, null);
+ do_check_eq(p1.version, "2");
+ do_check_eq(p1.name, "Updated Theme");
+ do_check_eq(p1.installDate.getTime(), gInstallDate);
+ do_check_true(p1.installDate.getTime() < p1.updateDate.getTime());
+
+ // 5 seconds leeway seems like a lot, but tests can run slow and really if
+ // this is within 5 seconds it is fine. If it is going to be wrong then it
+ // is likely to be hours out at least
+ do_check_true((Date.now() - p1.updateDate.getTime()) < 5000);
+
+ gInstallDate = p1.installDate.getTime();
+
+ run_next_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_update_webextensions.js b/toolkit/mozapps/webextensions/test/xpcshell/test_update_webextensions.js
new file mode 100644
index 000000000..ff5d5d321
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_update_webextensions.js
@@ -0,0 +1,248 @@
+"use strict";
+
+const TOOLKIT_ID = "toolkit@mozilla.org";
+
+// We don't have an easy way to serve update manifests from a secure URL.
+Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
+
+var testserver = createHttpServer();
+gPort = testserver.identity.primaryPort;
+
+const uuidGenerator = AM_Cc["@mozilla.org/uuid-generator;1"].getService(AM_Ci.nsIUUIDGenerator);
+
+const extensionsDir = gProfD.clone();
+extensionsDir.append("extensions");
+
+const addonsDir = gTmpD.clone();
+addonsDir.append("addons");
+addonsDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+
+do_register_cleanup(() => addonsDir.remove(true));
+
+testserver.registerDirectory("/addons/", addonsDir);
+
+
+let gUpdateManifests = {};
+
+function mapManifest(aPath, aManifestData) {
+ gUpdateManifests[aPath] = aManifestData;
+ testserver.registerPathHandler(aPath, serveManifest);
+}
+
+function serveManifest(request, response) {
+ let manifest = gUpdateManifests[request.path];
+
+ response.setHeader("Content-Type", manifest.contentType, false);
+ response.write(manifest.data);
+}
+
+
+function promiseInstallWebExtension(aData) {
+ let addonFile = createTempWebExtensionFile(aData);
+
+ return promiseInstallAllFiles([addonFile]).then(() => {
+ Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
+ return promiseAddonByID(aData.id);
+ });
+}
+
+var checkUpdates = Task.async(function* (aData, aReason = AddonManager.UPDATE_WHEN_PERIODIC_UPDATE) {
+ function provide(obj, path, value) {
+ path = path.split(".");
+ let prop = path.pop();
+
+ for (let key of path) {
+ if (!(key in obj))
+ obj[key] = {};
+ obj = obj[key];
+ }
+
+ if (!(prop in obj))
+ obj[prop] = value;
+ }
+
+ let id = uuidGenerator.generateUUID().number;
+ provide(aData, "addon.id", id);
+ provide(aData, "addon.manifest.applications.gecko.id", id);
+
+ let updatePath = `/updates/${id}.json`.replace(/[{}]/g, "");
+ let updateUrl = `http://localhost:${gPort}${updatePath}`
+
+ let addonData = { updates: [] };
+ let manifestJSON = {
+ addons: { [id]: addonData }
+ };
+
+
+ provide(aData, "addon.manifest.applications.gecko.update_url", updateUrl);
+ let awaitInstall = promiseInstallWebExtension(aData.addon);
+
+
+ for (let version of Object.keys(aData.updates)) {
+ let update = aData.updates[version];
+ update.version = version;
+
+ provide(update, "addon.id", id);
+ provide(update, "addon.manifest.applications.gecko.id", id);
+ let addon = update.addon;
+
+ delete update.addon;
+
+ let file;
+ if (addon.rdf) {
+ provide(addon, "version", version);
+ provide(addon, "targetApplications", [{id: TOOLKIT_ID,
+ minVersion: "42",
+ maxVersion: "*"}]);
+
+ file = createTempXPIFile(addon);
+ } else {
+ provide(addon, "manifest.version", version);
+ file = createTempWebExtensionFile(addon);
+ }
+ file.moveTo(addonsDir, `${id}-${version}.xpi`.replace(/[{}]/g, ""));
+
+ let path = `/addons/${file.leafName}`;
+ provide(update, "update_link", `http://localhost:${gPort}${path}`);
+
+ addonData.updates.push(update);
+ }
+
+ mapManifest(updatePath, { data: JSON.stringify(manifestJSON),
+ contentType: "application/json" });
+
+
+ let addon = yield awaitInstall;
+
+ let updates = yield promiseFindAddonUpdates(addon, aReason);
+ updates.addon = addon;
+
+ return updates;
+});
+
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "42.0", "42.0");
+
+ startupManager();
+ do_register_cleanup(promiseShutdownManager);
+
+ run_next_test();
+}
+
+
+// Check that compatibility updates are applied.
+add_task(function* checkUpdateMetadata() {
+ let update = yield checkUpdates({
+ addon: {
+ manifest: {
+ version: "1.0",
+ applications: { gecko: { strict_max_version: "45" } },
+ }
+ },
+ updates: {
+ "1.0": {
+ applications: { gecko: { strict_min_version: "40",
+ strict_max_version: "48" } },
+ }
+ }
+ });
+
+ ok(update.compatibilityUpdate, "have compat update");
+ ok(!update.updateAvailable, "have no add-on update");
+
+ ok(update.addon.isCompatibleWith("40", "40"), "compatible min");
+ ok(update.addon.isCompatibleWith("48", "48"), "compatible max");
+ ok(!update.addon.isCompatibleWith("49", "49"), "not compatible max");
+
+ update.addon.uninstall();
+});
+
+
+// Check that updates from web extensions to web extensions succeed.
+add_task(function* checkUpdateToWebExt() {
+ let update = yield checkUpdates({
+ addon: { manifest: { version: "1.0" } },
+ updates: {
+ "1.1": { },
+ "1.2": { },
+ "1.3": { "applications": { "gecko": { "strict_min_version": "48" } } },
+ }
+ });
+
+ ok(!update.compatibilityUpdate, "have no compat update");
+ ok(update.updateAvailable, "have add-on update");
+
+ equal(update.addon.version, "1.0", "add-on version");
+
+ yield promiseCompleteAllInstalls([update.updateAvailable]);
+
+ let addon = yield promiseAddonByID(update.addon.id);
+ equal(addon.version, "1.2", "new add-on version");
+
+ addon.uninstall();
+});
+
+
+// Check that updates from web extensions to XUL extensions fail.
+add_task(function* checkUpdateToRDF() {
+ let update = yield checkUpdates({
+ addon: { manifest: { version: "1.0" } },
+ updates: {
+ "1.1": { addon: { rdf: true } },
+ }
+ });
+
+ ok(!update.compatibilityUpdate, "have no compat update");
+ ok(update.updateAvailable, "have add-on update");
+
+ equal(update.addon.version, "1.0", "add-on version");
+
+ let result = yield new Promise((resolve, reject) => {
+ update.updateAvailable.addListener({
+ onDownloadFailed: resolve,
+ onDownloadEnded: reject,
+ onInstalling: reject,
+ onInstallStarted: reject,
+ onInstallEnded: reject,
+ });
+ update.updateAvailable.install();
+ });
+
+ equal(result.error, AddonManager.ERROR_UNEXPECTED_ADDON_TYPE, "error: unexpected add-on type");
+
+ let addon = yield promiseAddonByID(update.addon.id);
+ equal(addon.version, "1.0", "new add-on version");
+
+ addon.uninstall();
+});
+
+
+// Check that illegal update URLs are rejected.
+add_task(function* checkIllegalUpdateURL() {
+ const URLS = ["chrome://browser/content/",
+ "data:text/json,...",
+ "javascript:;",
+ "/"];
+
+ for (let url of URLS) {
+ let { messages } = yield promiseConsoleOutput(() => {
+ return new Promise((resolve, reject) => {
+ let addonFile = createTempWebExtensionFile({
+ manifest: { applications: { gecko: { update_url: url } } },
+ });
+
+ AddonManager.getInstallForFile(addonFile, install => {
+ Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
+
+ if (install && install.state == AddonManager.STATE_DOWNLOAD_FAILED)
+ resolve();
+ reject(new Error("Unexpected state: " + (install && install.state)))
+ });
+ });
+ });
+
+ ok(messages.some(msg => /Access denied for URL|may not load or link to|is not a valid URL/.test(msg)),
+ "Got checkLoadURI error");
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_updatecheck.js b/toolkit/mozapps/webextensions/test/xpcshell/test_updatecheck.js
new file mode 100644
index 000000000..e8529bb2c
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_updatecheck.js
@@ -0,0 +1,236 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that AddonUpdateChecker works correctly
+
+Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+var testserver = createHttpServer(4444);
+testserver.registerDirectory("/data/", do_get_file("data"));
+
+function checkUpdates(aId, aUpdateKey, aUpdateFile) {
+ return new Promise((resolve, reject) => {
+ AddonUpdateChecker.checkForUpdates(aId, aUpdateKey, `http://localhost:4444/data/${aUpdateFile}`, {
+ onUpdateCheckComplete: resolve,
+
+ onUpdateCheckError: function(status) {
+ let error = new Error("Update check failed with status " + status);
+ error.status = status;
+ reject(error);
+ }
+ });
+ });
+}
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+ run_next_test();
+}
+
+// Test that a basic update check returns the expected available updates
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ let updates = yield checkUpdates("updatecheck1@tests.mozilla.org", null, file);
+
+ equal(updates.length, 5);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(updates);
+ notEqual(update, null);
+ equal(update.version, "3.0");
+ update = AddonUpdateChecker.getCompatibilityUpdate(updates, "2");
+ notEqual(update, null);
+ equal(update.version, "2.0");
+ equal(update.targetApplications[0].minVersion, "1");
+ equal(update.targetApplications[0].maxVersion, "2");
+ }
+});
+
+/*
+ * Tests that the security checks are applied correctly
+ *
+ * Test signature updateHash updateLink expected
+ *--------------------------------------------------------
+ * 2 absent absent http fail
+ * 3 broken absent http fail
+ * 4 correct absent http no update
+ * 5 correct sha1 http update
+ * 6 corrent absent https update
+ * 7 corrent sha1 https update
+ * 8 corrent md2 http no update
+ * 9 corrent md2 https update
+ */
+
+var updateKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDK426erD/H3XtsjvaB5+PJqbhj" +
+ "Zc9EDI5OCJS8R3FIObJ9ZHJK1TXeaE7JWqt9WUmBWTEFvwS+FI9vWu8058N9CHhD" +
+ "NyeP6i4LuUYjTURnn7Yw/IgzyIJ2oKsYa32RuxAyteqAWqPT/J63wBixIeCxmysf" +
+ "awB/zH4KaPiY3vnrzQIDAQAB";
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ try {
+ yield checkUpdates("test_bug378216_5@tests.mozilla.org",
+ updateKey, file);
+ throw "Expected the update check to fail";
+ } catch (e) {}
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ try {
+ yield checkUpdates("test_bug378216_7@tests.mozilla.org",
+ updateKey, file);
+
+ throw "Expected the update check to fail";
+ } catch (e) {}
+ }
+});
+
+add_task(function* () {
+ // Make sure that the JSON manifest is rejected when an update key is
+ // required, but perform the remaining tests which aren't expected to fail
+ // because of the update key, without requiring one for the JSON variant.
+
+ try {
+ let updates = yield checkUpdates("test_bug378216_8@tests.mozilla.org",
+ updateKey, "test_updatecheck.json");
+
+ throw "Expected the update check to fail";
+ } catch (e) {}
+
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_8@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ ok(!("updateURL" in updates[0]));
+ }
+});
+
+add_task(function* () {
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_9@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ equal(updates[0].version, "2.0");
+ ok("updateURL" in updates[0]);
+ }
+});
+
+add_task(function* () {
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_10@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ equal(updates[0].version, "2.0");
+ ok("updateURL" in updates[0]);
+ }
+});
+
+add_task(function* () {
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_11@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ equal(updates[0].version, "2.0");
+ ok("updateURL" in updates[0]);
+ }
+});
+
+add_task(function* () {
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_12@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ do_check_false("updateURL" in updates[0]);
+ }
+});
+
+add_task(function* () {
+ for (let [file, key] of [["test_updatecheck.rdf", updateKey],
+ ["test_updatecheck.json", null]]) {
+ let updates = yield checkUpdates("test_bug378216_13@tests.mozilla.org",
+ key, file);
+ equal(updates.length, 1);
+ equal(updates[0].version, "2.0");
+ ok("updateURL" in updates[0]);
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ let updates = yield checkUpdates("test_bug378216_14@tests.mozilla.org",
+ null, file);
+ equal(updates.length, 0);
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ try {
+ yield checkUpdates("test_bug378216_15@tests.mozilla.org",
+ null, file);
+
+ throw "Update check should have failed";
+ } catch (e) {
+ equal(e.status, AddonUpdateChecker.ERROR_PARSE_ERROR);
+ }
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ let updates = yield checkUpdates("ignore-compat@tests.mozilla.org",
+ null, file);
+ equal(updates.length, 3);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(
+ updates, null, null, true);
+ notEqual(update, null);
+ equal(update.version, 2);
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ let updates = yield checkUpdates("compat-override@tests.mozilla.org",
+ null, file);
+ equal(updates.length, 3);
+ let overrides = [{
+ type: "incompatible",
+ minVersion: 1,
+ maxVersion: 2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 0.1,
+ appMaxVersion: 0.2
+ }, {
+ type: "incompatible",
+ minVersion: 2,
+ maxVersion: 2,
+ appID: "xpcshell@tests.mozilla.org",
+ appMinVersion: 1,
+ appMaxVersion: 2
+ }];
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(
+ updates, null, null, true, false, overrides);
+ notEqual(update, null);
+ equal(update.version, 1);
+ }
+});
+
+add_task(function* () {
+ for (let file of ["test_updatecheck.rdf", "test_updatecheck.json"]) {
+ let updates = yield checkUpdates("compat-strict-optin@tests.mozilla.org",
+ null, file);
+ equal(updates.length, 1);
+ let update = AddonUpdateChecker.getNewestCompatibleUpdate(
+ updates, null, null, true, false);
+ equal(update, null);
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_updateid.js b/toolkit/mozapps/webextensions/test/xpcshell/test_updateid.js
new file mode 100644
index 000000000..f7e3e21e5
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_updateid.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that updating an add-on to a new ID works
+
+// The test extension uses an insecure update url.
+Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function promiseInstallUpdate(install) {
+ return new Promise((resolve, reject) => {
+ install.addListener({
+ onDownloadFailed: () => {
+ let err = new Error("download error");
+ err.code = install.error;
+ reject(err);
+ },
+ onInstallFailed: () => {
+ let err = new Error("install error");
+ err.code = install.error;
+ reject(err);
+ },
+ onInstallEnded: resolve,
+ });
+
+ install.install();
+ });
+}
+
+// Create and configure the HTTP server.
+let testserver = createHttpServer(4444);
+testserver.registerDirectory("/data/", do_get_file("data"));
+testserver.registerDirectory("/addons/", do_get_file("addons"));
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+ run_next_test();
+}
+
+// Verify that an update to an add-on with a new ID fails
+add_task(function* test_update_new_id() {
+ yield promiseInstallFile(do_get_addon("test_updateid1"));
+
+ let addon = yield promiseAddonByID("addon1@tests.mozilla.org");
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+
+ let update = yield promiseFindAddonUpdates(addon, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ let install = update.updateAvailable;
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+
+ yield Assert.rejects(promiseInstallUpdate(install),
+ function(err) { return err.code == AddonManager.ERROR_INCORRECT_ID },
+ "Upgrade to a different ID fails");
+
+ addon.uninstall();
+});
+
+// Verify that an update to a multi-package xpi fails
+add_task(function* test_update_new_id() {
+ yield promiseInstallFile(do_get_addon("test_update_multi1"));
+
+ let addon = yield promiseAddonByID("updatemulti@tests.mozilla.org");
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+
+ let update = yield promiseFindAddonUpdates(addon, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+ let install = update.updateAvailable;
+ do_check_eq(install.name, addon.name);
+ do_check_eq(install.version, "2.0");
+ do_check_eq(install.state, AddonManager.STATE_AVAILABLE);
+ do_check_eq(install.existingAddon, addon);
+
+ yield Assert.rejects(promiseInstallUpdate(install),
+ function(err) { return err.code == AddonManager.ERROR_UNEXPECTED_ADDON_TYPE },
+ "Upgrade to a multipackage xpi fails");
+
+ addon.uninstall();
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade.js b/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade.js
new file mode 100644
index 000000000..dc3d9438a
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade.js
@@ -0,0 +1,206 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that app upgrades produce the expected behaviours,
+// with strict compatibility checking disabled.
+
+Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, false);
+
+// Enable loading extensions from the application scope
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE +
+ AddonManager.SCOPE_APPLICATION);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+const globalDir = Services.dirsvc.get("XREAddonAppDir", AM_Ci.nsIFile);
+globalDir.append("extensions");
+
+var gGlobalExisted = globalDir.exists();
+var gInstallTime = Date.now();
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Will be compatible in the first version and incompatible in subsequent versions
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ ]
+ }, profileDir);
+
+ // Works in all tested versions
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 2",
+ targetPlatforms: [
+ "XPCShell_noarch-spidermonkey"
+ ]
+ }, profileDir);
+
+ // Will be disabled in the first version and enabled in the second.
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Will be compatible in both versions but will change version in between
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ do_test_pending();
+
+ run_test_1();
+}
+
+function end_test() {
+ if (!gGlobalExisted) {
+ globalDir.remove(true);
+ }
+ else {
+ globalDir.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ globalDir.remove(true);
+ }
+ do_execute_soon(do_test_finished);
+}
+
+// Test that the test extensions are all installed
+function run_test_1() {
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "1.0");
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Test that upgrading the application doesn't disable now incompatible add-ons
+function run_test_2() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ restartManager("2");
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that nothing changes when only the build ID changes.
+function run_test_3() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "3.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ // Simulates a simple Build ID change, the platform deletes extensions.ini
+ // whenever the application is changed.
+ gExtensionsINI.remove(true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade_strictcompat.js b/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade_strictcompat.js
new file mode 100644
index 000000000..c2203d097
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_upgrade_strictcompat.js
@@ -0,0 +1,209 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// This verifies that app upgrades produce the expected behaviours,
+// with strict compatibility checking enabled.
+
+// Enable loading extensions from the application scope
+Services.prefs.setIntPref("extensions.enabledScopes",
+ AddonManager.SCOPE_PROFILE +
+ AddonManager.SCOPE_APPLICATION);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+const globalDir = Services.dirsvc.get("XREAddonAppDir", AM_Ci.nsIFile);
+globalDir.append("extensions");
+
+var gGlobalExisted = globalDir.exists();
+var gInstallTime = Date.now();
+
+function run_test() {
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+
+ // Will be enabled in the first version and disabled in subsequent versions
+ writeInstallRDFForExtension({
+ id: "addon1@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 1",
+ targetPlatforms: [
+ "XPCShell",
+ "WINNT_x86",
+ ]
+ }, profileDir);
+
+ // Works in all tested versions
+ writeInstallRDFForExtension({
+ id: "addon2@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 2",
+ targetPlatforms: [
+ "XPCShell_noarch-spidermonkey"
+ ]
+ }, profileDir);
+
+ // Will be disabled in the first version and enabled in the second.
+ writeInstallRDFForExtension({
+ id: "addon3@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 3",
+ }, profileDir);
+
+ // Will be enabled in both versions but will change version in between
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "1.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ do_test_pending();
+
+ Services.prefs.setBoolPref(PREF_EM_STRICT_COMPATIBILITY, true);
+
+ run_test_1();
+}
+
+function end_test() {
+ if (!gGlobalExisted) {
+ globalDir.remove(true);
+ }
+ else {
+ globalDir.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
+ globalDir.remove(true);
+ }
+
+ Services.prefs.clearUserPref(PREF_EM_STRICT_COMPATIBILITY);
+
+ do_execute_soon(do_test_finished);
+}
+
+// Test that the test extensions are all installed
+function run_test_1() {
+ startupManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "1.0");
+
+ do_execute_soon(run_test_2);
+ });
+}
+
+// Test that upgrading the application disables now incompatible add-ons
+function run_test_2() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "2.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "2",
+ maxVersion: "2"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ restartManager("2");
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ do_execute_soon(run_test_3);
+ });
+}
+
+// Test that nothing changes when only the build ID changes.
+function run_test_3() {
+ // Upgrade the extension
+ var dest = writeInstallRDFForExtension({
+ id: "addon4@tests.mozilla.org",
+ version: "3.0",
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "3",
+ maxVersion: "3"
+ }],
+ name: "Test Addon 4",
+ }, globalDir);
+ setExtensionModifiedTime(dest, gInstallTime);
+
+ // Simulates a simple Build ID change, the platform deletes extensions.ini
+ // whenever the application is changed.
+ gExtensionsINI.remove(true);
+ restartManager();
+
+ AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+ "addon2@tests.mozilla.org",
+ "addon3@tests.mozilla.org",
+ "addon4@tests.mozilla.org"],
+ function([a1, a2, a3, a4]) {
+
+ do_check_neq(a1, null);
+ do_check_false(isExtensionInAddonsList(profileDir, a1.id));
+
+ do_check_neq(a2, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+ do_check_neq(a3, null);
+ do_check_true(isExtensionInAddonsList(profileDir, a3.id));
+
+ do_check_neq(a4, null);
+ do_check_true(isExtensionInAddonsList(globalDir, a4.id));
+ do_check_eq(a4.version, "2.0");
+
+ end_test();
+ });
+}
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_webextension.js b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension.js
new file mode 100644
index 000000000..b51f47977
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension.js
@@ -0,0 +1,421 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
+
+const ID = "webextension1@tests.mozilla.org";
+
+const PREF_SELECTED_LOCALE = "general.useragent.locale";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+const { GlobalManager, Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseAddonStartup() {
+ return new Promise(resolve => {
+ let listener = (evt, extension) => {
+ Management.off("ready", listener);
+ resolve(extension);
+ };
+
+ Management.on("ready", listener);
+ });
+}
+
+function promiseInstallWebExtension(aData) {
+ let addonFile = createTempWebExtensionFile(aData);
+
+ return promiseInstallAllFiles([addonFile]).then(() => {
+ Services.obs.notifyObservers(addonFile, "flush-cache-entry", null);
+ return promiseAddonStartup();
+ });
+}
+
+add_task(function*() {
+ equal(GlobalManager.extensionMap.size, 0);
+
+ yield Promise.all([
+ promiseInstallAllFiles([do_get_addon("webextension_1")], true),
+ promiseAddonStartup()
+ ]);
+
+ equal(GlobalManager.extensionMap.size, 1);
+ ok(GlobalManager.extensionMap.has(ID));
+
+ let chromeReg = AM_Cc["@mozilla.org/chrome/chrome-registry;1"].
+ getService(AM_Ci.nsIChromeRegistry);
+ try {
+ chromeReg.convertChromeURL(NetUtil.newURI("chrome://webex/content/webex.xul"));
+ do_throw("Chrome manifest should not have been registered");
+ }
+ catch (e) {
+ // Expected the chrome url to not be registered
+ }
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Web Extension Name");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_false(addon.isSystem);
+ do_check_eq(addon.type, "extension");
+ do_check_true(addon.isWebExtension);
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let uri = do_get_addon_root_uri(profileDir, ID);
+
+ do_check_eq(addon.iconURL, uri + "icon48.png");
+ do_check_eq(addon.icon64URL, uri + "icon64.png");
+
+ // Should persist through a restart
+ yield promiseShutdownManager();
+
+ equal(GlobalManager.extensionMap.size, 0);
+
+ startupManager();
+ yield promiseAddonStartup();
+
+ equal(GlobalManager.extensionMap.size, 1);
+ ok(GlobalManager.extensionMap.has(ID));
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Web Extension Name");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_false(addon.isSystem);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let file = getFileForAddon(profileDir, ID);
+ do_check_true(file.exists());
+
+ uri = do_get_addon_root_uri(profileDir, ID);
+
+ do_check_eq(addon.iconURL, uri + "icon48.png");
+ do_check_eq(addon.icon64URL, uri + "icon64.png");
+
+ addon.userDisabled = true;
+
+ equal(GlobalManager.extensionMap.size, 0);
+
+ addon.userDisabled = false;
+ yield promiseAddonStartup();
+
+ equal(GlobalManager.extensionMap.size, 1);
+ ok(GlobalManager.extensionMap.has(ID));
+
+ addon.uninstall();
+
+ equal(GlobalManager.extensionMap.size, 0);
+ do_check_false(GlobalManager.extensionMap.has(ID));
+
+ yield promiseShutdownManager();
+});
+
+// Writing the manifest direct to the profile should work
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ version: "1.0",
+ manifest_version: 2,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }, profileDir);
+
+ startupManager();
+ yield promiseAddonStartup();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+ do_check_eq(addon.version, "1.0");
+ do_check_eq(addon.name, "Web Extension Name");
+ do_check_true(addon.isCompatible);
+ do_check_false(addon.appDisabled);
+ do_check_true(addon.isActive);
+ do_check_false(addon.isSystem);
+ do_check_eq(addon.type, "extension");
+ do_check_eq(addon.signedState, mozinfo.addon_signing ? AddonManager.SIGNEDSTATE_SIGNED : AddonManager.SIGNEDSTATE_NOT_REQUIRED);
+
+ let file = getFileForAddon(profileDir, ID);
+ do_check_true(file.exists());
+
+ addon.uninstall();
+
+ yield promiseRestartManager();
+});
+
+add_task(function* test_manifest_localization() {
+ const extensionId = "webextension3@tests.mozilla.org";
+
+ yield promiseInstallAllFiles([do_get_addon("webextension_3")], true);
+ yield promiseAddonStartup();
+
+ let addon = yield promiseAddonByID(extensionId);
+ addon.userDisabled = true;
+
+ equal(addon.name, "Web Extensiøn foo ☹");
+ equal(addon.description, "Descriptïon bar ☹ of add-on");
+
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "fr-FR");
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(extensionId);
+
+ equal(addon.name, "Web Extensiøn le foo ☺");
+ equal(addon.description, "Descriptïon le bar ☺ of add-on");
+
+ Services.prefs.setCharPref(PREF_SELECTED_LOCALE, "de");
+ yield promiseRestartManager();
+
+ addon = yield promiseAddonByID(extensionId);
+
+ equal(addon.name, "Web Extensiøn foo ☹");
+ equal(addon.description, "Descriptïon bar ☹ of add-on");
+
+ addon.uninstall();
+});
+
+// Missing version should cause a failure
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ manifest_version: 2,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }, profileDir);
+
+ yield promiseRestartManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ let file = getFileForAddon(profileDir, ID);
+ do_check_false(file.exists());
+
+ yield promiseRestartManager();
+});
+
+// Incorrect manifest version should cause a failure
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ version: "1.0",
+ manifest_version: 1,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }, profileDir);
+
+ yield promiseRestartManager();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_eq(addon, null);
+
+ let file = getFileForAddon(profileDir, ID);
+ do_check_false(file.exists());
+
+ yield promiseRestartManager();
+});
+
+// install.rdf should be read before manifest.json
+add_task(function*() {
+
+ yield Promise.all([
+ promiseInstallAllFiles([do_get_addon("webextension_2")], true)
+ ]);
+
+ yield promiseRestartManager();
+
+ let installrdf_id = "first-webextension2@tests.mozilla.org";
+ let first_addon = yield promiseAddonByID(installrdf_id);
+ do_check_neq(first_addon, null);
+ do_check_false(first_addon.appDisabled);
+ do_check_true(first_addon.isActive);
+ do_check_false(first_addon.isSystem);
+
+ let manifestjson_id= "last-webextension2@tests.mozilla.org";
+ let last_addon = yield promiseAddonByID(manifestjson_id);
+ do_check_eq(last_addon, null);
+
+ yield promiseRestartManager();
+});
+
+// Test that the "options_ui" manifest section is processed correctly.
+add_task(function* test_options_ui() {
+ let OPTIONS_RE = /^moz-extension:\/\/[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}\/options\.html$/;
+
+ const extensionId = "webextension@tests.mozilla.org";
+ yield promiseInstallWebExtension({
+ manifest: {
+ applications: {gecko: {id: extensionId}},
+ "options_ui": {
+ "page": "options.html",
+ },
+ },
+ });
+
+ let addon = yield promiseAddonByID(extensionId);
+ equal(addon.optionsType, AddonManager.OPTIONS_TYPE_INLINE_BROWSER,
+ "Addon should have an INLINE_BROWSER options type");
+
+ ok(OPTIONS_RE.test(addon.optionsURL),
+ "Addon should have a moz-extension: options URL for /options.html");
+
+ addon.uninstall();
+
+ const ID2 = "webextension2@tests.mozilla.org";
+ yield promiseInstallWebExtension({
+ manifest: {
+ applications: {gecko: {id: ID2}},
+ "options_ui": {
+ "page": "options.html",
+ "open_in_tab": true,
+ },
+ },
+ });
+
+ addon = yield promiseAddonByID(ID2);
+ equal(addon.optionsType, AddonManager.OPTIONS_TYPE_TAB,
+ "Addon should have a TAB options type");
+
+ ok(OPTIONS_RE.test(addon.optionsURL),
+ "Addon should have a moz-extension: options URL for /options.html");
+
+ addon.uninstall();
+});
+
+// Test that experiments permissions add the appropriate dependencies.
+add_task(function* test_experiments_dependencies() {
+ if (AppConstants.RELEASE_OR_BETA)
+ // Experiments are not enabled on release builds.
+ return;
+
+ let addonFile = createTempWebExtensionFile({
+ manifest: {
+ applications: {gecko: {id: "meh@experiment"}},
+ "permissions": ["experiments.meh"],
+ },
+ });
+
+ yield promiseInstallAllFiles([addonFile]);
+
+ let addon = yield new Promise(resolve => AddonManager.getAddonByID("meh@experiment", resolve));
+
+ deepEqual(addon.dependencies, ["meh@experiments.addons.mozilla.org"],
+ "Addon should have the expected dependencies");
+
+ equal(addon.appDisabled, true, "Add-on should be app disabled due to missing dependencies");
+
+ addon.uninstall();
+});
+
+// Test that experiments API extensions install correctly.
+add_task(function* test_experiments_api() {
+ if (AppConstants.RELEASE_OR_BETA)
+ // Experiments are not enabled on release builds.
+ return;
+
+ const extensionId = "meh@experiments.addons.mozilla.org";
+
+ let addonFile = createTempXPIFile({
+ id: extensionId,
+ type: 256,
+ version: "0.1",
+ name: "Meh API",
+ });
+
+ yield promiseInstallAllFiles([addonFile]);
+
+ let addons = yield new Promise(resolve => AddonManager.getAddonsByTypes(["apiextension"], resolve));
+ let addon = addons.pop();
+ equal(addon.id, extensionId, "Add-on should be installed as an API extension");
+
+ addons = yield new Promise(resolve => AddonManager.getAddonsByTypes(["extension"], resolve));
+ equal(addons.pop().id, extensionId, "Add-on type should be aliased to extension");
+
+ addon.uninstall();
+});
+
+add_task(function* developerShouldOverride() {
+ let addon = yield promiseInstallWebExtension({
+ manifest: {
+ default_locale: "en",
+ developer: {
+ name: "__MSG_name__",
+ url: "__MSG_url__"
+ },
+ author: "Will be overridden by developer",
+ homepage_url: "https://will.be.overridden",
+ },
+ files: {
+ "_locales/en/messages.json": `{
+ "name": {
+ "message": "en name"
+ },
+ "url": {
+ "message": "https://example.net/en"
+ }
+ }`
+ }
+ });
+
+ addon = yield promiseAddonByID(addon.id);
+ equal(addon.creator, "en name");
+ equal(addon.homepageURL, "https://example.net/en");
+ addon.uninstall();
+});
+
+add_task(function* developerEmpty() {
+ for (let developer of [{}, null, {name: null, url: null}]) {
+ let addon = yield promiseInstallWebExtension({
+ manifest: {
+ author: "Some author",
+ developer: developer,
+ homepage_url: "https://example.net",
+ manifest_version: 2,
+ name: "Web Extension Name",
+ version: "1.0",
+ }
+ });
+
+ addon = yield promiseAddonByID(addon.id);
+ equal(addon.creator, "Some author");
+ equal(addon.homepageURL, "https://example.net");
+ addon.uninstall();
+ }
+});
+
+add_task(function* authorNotString() {
+ for (let author of [{}, [], 42]) {
+ let addon = yield promiseInstallWebExtension({
+ manifest: {
+ author: author,
+ manifest_version: 2,
+ name: "Web Extension Name",
+ version: "1.0",
+ }
+ });
+
+ addon = yield promiseAddonByID(addon.id);
+ equal(addon.creator, null);
+ addon.uninstall();
+ }
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_embedded.js b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_embedded.js
new file mode 100644
index 000000000..3bd8a2bd8
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_embedded.js
@@ -0,0 +1,306 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+BootstrapMonitor.init();
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "49");
+startupManager();
+
+// NOTE: the following import needs to be called after the `createAppInfo`
+// or it will fail Extension.jsm internally imports AddonManager.jsm and
+// AddonManager will raise a ReferenceError exception because it tried to
+// access an undefined `Services.appinfo` object.
+const { Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+const {
+ EmbeddedExtensionManager,
+ LegacyExtensionsUtils,
+} = Components.utils.import("resource://gre/modules/LegacyExtensionsUtils.jsm");
+
+// Wait the startup of the embedded webextension.
+function promiseWebExtensionStartup() {
+ return new Promise(resolve => {
+ let listener = (event, extension) => {
+ Management.off("startup", listener);
+ resolve(extension);
+ };
+
+ Management.on("startup", listener);
+ });
+}
+
+function promiseWebExtensionShutdown() {
+ return new Promise(resolve => {
+ let listener = (event, extension) => {
+ Management.off("shutdown", listener);
+ resolve(extension);
+ };
+
+ Management.on("shutdown", listener);
+ });
+}
+
+const BOOTSTRAP = String.raw`
+ Components.utils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
+`;
+
+const EMBEDDED_WEBEXT_MANIFEST = JSON.stringify({
+ name: "embedded webextension addon",
+ manifest_version: 2,
+ version: "1.0",
+});
+
+/**
+ * This test case checks that an hasEmbeddedWebExtension addon property
+ * is persisted and restored correctly across restarts.
+ */
+add_task(function* has_embedded_webextension_persisted() {
+ const ID = "embedded-webextension-addon-persist@tests.mozilla.org";
+
+ const xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ hasEmbeddedWebExtension: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ }, {
+ "bootstrap.js": BOOTSTRAP,
+ "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
+ });
+
+ yield promiseInstallFile(xpiFile);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+ equal(addon.hasEmbeddedWebExtension, true,
+ "Got the expected hasEmbeddedWebExtension value");
+
+ // Check that the addon has been installed and started.
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let startupInfo = BootstrapMonitor.started.get(ID);
+ ok(("webExtension" in startupInfo.data),
+ "Got an webExtension property in the startup bootstrap method params");
+ ok(("startup" in startupInfo.data.webExtension),
+ "Got the expected 'startup' property in the webExtension object");
+
+ // After restarting the manager, the add-on should still have the
+ // hasEmbeddedWebExtension property as expected.
+ yield promiseRestartManager();
+
+ let persisted = JSON.parse(Services.prefs.getCharPref("extensions.bootstrappedAddons"));
+ ok(ID in persisted, "Hybrid add-on persisted to bootstrappedAddons.");
+ equal(persisted[ID].hasEmbeddedWebExtension, true,
+ "hasEmbeddedWebExtension flag persisted to bootstrappedAddons.");
+
+ // Check that the addon has been installed and started.
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.hasEmbeddedWebExtension, true, "Got the expected hasEmbeddedWebExtension value");
+
+ // Check that the addon has been installed and started.
+ let newStartupInfo = BootstrapMonitor.started.get(ID);
+ ok(("webExtension" in newStartupInfo.data),
+ "Got an webExtension property in the startup bootstrap method params");
+ ok(("startup" in newStartupInfo.data.webExtension),
+ "Got the expected 'startup' property in the webExtension object");
+
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstall;
+});
+
+/**
+ * This test case checks that an addon with hasEmbeddedWebExtension set to true
+ * in its install.rdf gets the expected `embeddedWebExtension` object in the
+ * parameters of its bootstrap methods.
+ */
+add_task(function* run_embedded_webext_bootstrap() {
+ const ID = "embedded-webextension-addon2@tests.mozilla.org";
+
+ const xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ hasEmbeddedWebExtension: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ }, {
+ "bootstrap.js": BOOTSTRAP,
+ "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
+ });
+
+ yield AddonManager.installTemporaryAddon(xpiFile);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+ equal(addon.hasEmbeddedWebExtension, true,
+ "Got the expected hasEmbeddedWebExtension value");
+
+ // Check that the addon has been installed and started.
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+
+ let installInfo = BootstrapMonitor.installed.get(ID);
+ ok(!("webExtension" in installInfo.data),
+ "No webExtension property is expected in the install bootstrap method params");
+
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ let startupInfo = BootstrapMonitor.started.get(ID);
+
+ ok(("webExtension" in startupInfo.data),
+ "Got an webExtension property in the startup bootstrap method params");
+
+ ok(("startup" in startupInfo.data.webExtension),
+ "Got the expected 'startup' property in the webExtension object");
+
+ const waitForWebExtensionStartup = promiseWebExtensionStartup();
+
+ const embeddedAPI = yield startupInfo.data.webExtension.startup();
+
+ // WebExtension startup should have been fully resolved.
+ yield waitForWebExtensionStartup;
+
+ Assert.deepEqual(
+ Object.keys(embeddedAPI.browser.runtime).sort(),
+ ["onConnect", "onMessage"],
+ `Got the expected 'runtime' in the 'browser' API object`
+ );
+
+ // Uninstall the addon and wait that the embedded webextension has been stopped and
+ // test the params of the shutdown and uninstall bootstrap method.
+ let waitForWebExtensionShutdown = promiseWebExtensionShutdown();
+ let waitUninstall = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitForWebExtensionShutdown;
+ yield waitUninstall;
+
+ BootstrapMonitor.checkAddonNotStarted(ID, "1.0");
+
+ let shutdownInfo = BootstrapMonitor.stopped.get(ID);
+ ok(!("webExtension" in shutdownInfo.data),
+ "No webExtension property is expected in the shutdown bootstrap method params");
+
+ let uninstallInfo = BootstrapMonitor.uninstalled.get(ID);
+ ok(!("webExtension" in uninstallInfo.data),
+ "No webExtension property is expected in the uninstall bootstrap method params");
+});
+
+/**
+ * This test case checks that an addon with hasEmbeddedWebExtension can be reloaded
+ * without raising unexpected exceptions due to race conditions.
+ */
+add_task(function* reload_embedded_webext_bootstrap() {
+ const ID = "embedded-webextension-addon2@tests.mozilla.org";
+
+ // No embedded webextension should be currently around.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 0,
+ "No embedded extension instance should be tracked here");
+
+ const xpiFile = createTempXPIFile({
+ id: ID,
+ name: "Test Add-on",
+ version: "1.0",
+ bootstrap: true,
+ hasEmbeddedWebExtension: true,
+ targetApplications: [{
+ id: "xpcshell@tests.mozilla.org",
+ minVersion: "1",
+ maxVersion: "1.9.2"
+ }]
+ }, {
+ "bootstrap.js": BOOTSTRAP,
+ "webextension/manifest.json": EMBEDDED_WEBEXT_MANIFEST,
+ });
+
+ yield AddonManager.installTemporaryAddon(xpiFile);
+
+ let addon = yield promiseAddonByID(ID);
+
+ notEqual(addon, null, "Got an addon object as expected");
+ equal(addon.version, "1.0", "Got the expected version");
+ equal(addon.isActive, true, "The Addon is active");
+ equal(addon.appDisabled, false, "The addon is not app disabled");
+ equal(addon.userDisabled, false, "The addon is not user disabled");
+
+ // Check that the addon has been installed and started.
+ BootstrapMonitor.checkAddonInstalled(ID, "1.0");
+ BootstrapMonitor.checkAddonStarted(ID, "1.0");
+
+ // Only one embedded extension.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 1,
+ "Got the expected number of tracked extension instances");
+
+ const embeddedWebExtension = EmbeddedExtensionManager.embeddedExtensionsByAddonId.get(ID);
+
+ let startupInfo = BootstrapMonitor.started.get(ID);
+ yield startupInfo.data.webExtension.startup();
+
+ const waitForAddonDisabled = promiseAddonEvent("onDisabled");
+ addon.userDisabled = true;
+ yield waitForAddonDisabled;
+
+ // No embedded webextension should be currently around.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 0,
+ "No embedded extension instance should be tracked here");
+
+ const waitForAddonEnabled = promiseAddonEvent("onEnabled");
+ addon.userDisabled = false;
+ yield waitForAddonEnabled;
+
+ // Only one embedded extension.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 1,
+ "Got the expected number of tracked extension instances");
+
+ const embeddedWebExtensionAfterEnabled = EmbeddedExtensionManager.embeddedExtensionsByAddonId.get(ID);
+ notEqual(embeddedWebExtensionAfterEnabled, embeddedWebExtension,
+ "Got a new EmbeddedExtension instance after the addon has been disabled and then enabled");
+
+ startupInfo = BootstrapMonitor.started.get(ID);
+ yield startupInfo.data.webExtension.startup();
+
+ const waitForReinstalled = promiseAddonEvent("onInstalled");
+ addon.reload();
+ yield waitForReinstalled;
+
+ // No leaked embedded extension after the previous reloads.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 1,
+ "Got the expected number of tracked extension instances");
+
+ const embeddedWebExtensionAfterReload = EmbeddedExtensionManager.embeddedExtensionsByAddonId.get(ID);
+ notEqual(embeddedWebExtensionAfterReload, embeddedWebExtensionAfterEnabled,
+ "Got a new EmbeddedExtension instance after the addon has been reloaded");
+
+ startupInfo = BootstrapMonitor.started.get(ID);
+ yield startupInfo.data.webExtension.startup();
+
+ // Uninstall the test addon
+ let waitUninstalled = promiseAddonEvent("onUninstalled");
+ addon.uninstall();
+ yield waitUninstalled;
+
+ // No leaked embedded extension after uninstalling.
+ equal(EmbeddedExtensionManager.embeddedExtensionsByAddonId.size, 0,
+ "No embedded extension instance should be tracked after the addon uninstall");
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_icons.js b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_icons.js
new file mode 100644
index 000000000..25fb4115f
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_icons.js
@@ -0,0 +1,169 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+const ID = "webextension1@tests.mozilla.org";
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+profileDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
+startupManager();
+
+const { Management } = Components.utils.import("resource://gre/modules/Extension.jsm", {});
+
+function promiseAddonStartup() {
+ return new Promise(resolve => {
+ let listener = (evt, extension) => {
+ Management.off("startup", listener);
+ resolve(extension);
+ };
+
+ Management.on("startup", listener);
+ });
+}
+
+// Test simple icon set parsing
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ version: "1.0",
+ manifest_version: 2,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ },
+ icons: {
+ 16: "icon16.png",
+ 32: "icon32.png",
+ 48: "icon48.png",
+ 64: "icon64.png"
+ }
+ }, profileDir);
+
+ yield promiseRestartManager();
+ yield promiseAddonStartup();
+
+ let uri = do_get_addon_root_uri(profileDir, ID);
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ function check_icons(addon_copy) {
+ deepEqual(addon_copy.icons, {
+ 16: uri + "icon16.png",
+ 32: uri + "icon32.png",
+ 48: uri + "icon48.png",
+ 64: uri + "icon64.png"
+ });
+
+ // iconURL should map to icons[48] and icons[64]
+ equal(addon.iconURL, uri + "icon48.png");
+ equal(addon.icon64URL, uri + "icon64.png");
+
+ // AddonManager gets the correct icon sizes from addon.icons
+ equal(AddonManager.getPreferredIconURL(addon, 1), uri + "icon16.png");
+ equal(AddonManager.getPreferredIconURL(addon, 16), uri + "icon16.png");
+ equal(AddonManager.getPreferredIconURL(addon, 30), uri + "icon32.png");
+ equal(AddonManager.getPreferredIconURL(addon, 48), uri + "icon48.png");
+ equal(AddonManager.getPreferredIconURL(addon, 64), uri + "icon64.png");
+ equal(AddonManager.getPreferredIconURL(addon, 128), uri + "icon64.png");
+ }
+
+ check_icons(addon);
+
+ // check if icons are persisted through a restart
+ yield promiseRestartManager();
+ yield promiseAddonStartup();
+
+ addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ check_icons(addon);
+
+ addon.uninstall();
+
+ yield promiseRestartManager();
+});
+
+// Test AddonManager.getPreferredIconURL for retina screen sizes
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ version: "1.0",
+ manifest_version: 2,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ },
+ icons: {
+ 32: "icon32.png",
+ 48: "icon48.png",
+ 64: "icon64.png",
+ 128: "icon128.png",
+ 256: "icon256.png"
+ }
+ }, profileDir);
+
+ yield promiseRestartManager();
+ yield promiseAddonStartup();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ let uri = do_get_addon_root_uri(profileDir, ID);
+
+ // AddonManager displays larger icons for higher pixel density
+ equal(AddonManager.getPreferredIconURL(addon, 32, {
+ devicePixelRatio: 2
+ }), uri + "icon64.png");
+
+ equal(AddonManager.getPreferredIconURL(addon, 48, {
+ devicePixelRatio: 2
+ }), uri + "icon128.png");
+
+ equal(AddonManager.getPreferredIconURL(addon, 64, {
+ devicePixelRatio: 2
+ }), uri + "icon128.png");
+
+ addon.uninstall();
+
+ yield promiseRestartManager();
+});
+
+// Handles no icons gracefully
+add_task(function*() {
+ yield promiseWriteWebManifestForExtension({
+ name: "Web Extension Name",
+ version: "1.0",
+ manifest_version: 2,
+ applications: {
+ gecko: {
+ id: ID
+ }
+ }
+ }, profileDir);
+
+ yield promiseRestartManager();
+ yield promiseAddonStartup();
+
+ let addon = yield promiseAddonByID(ID);
+ do_check_neq(addon, null);
+
+ let uri = do_get_addon_root_uri(profileDir, ID);
+
+ deepEqual(addon.icons, {});
+
+ equal(addon.iconURL, null);
+ equal(addon.icon64URL, null);
+
+ equal(AddonManager.getPreferredIconURL(addon, 128), null);
+
+ addon.uninstall();
+
+ yield promiseRestartManager();
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_install.js b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_install.js
new file mode 100644
index 000000000..1e7c9d9b7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_install.js
@@ -0,0 +1,478 @@
+
+const {ADDON_SIGNING} = AM_Cu.import("resource://gre/modules/addons/AddonConstants.jsm", {});
+
+function run_test() {
+ run_next_test();
+}
+
+let profileDir;
+add_task(function* setup() {
+ profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ if (!profileDir.exists())
+ profileDir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+});
+
+const IMPLICIT_ID_XPI = "data/webext-implicit-id.xpi";
+const IMPLICIT_ID_ID = "webext_implicit_id@tests.mozilla.org";
+
+// webext-implicit-id.xpi has a minimal manifest with no
+// applications or browser_specific_settings, so its id comes
+// from its signature, which should be the ID constant defined below.
+add_task(function* test_implicit_id() {
+ // This test needs to read the xpi certificate which only works
+ // if signing is enabled.
+ ok(ADDON_SIGNING, "Add-on signing is enabled");
+
+ let addon = yield promiseAddonByID(IMPLICIT_ID_ID);
+ equal(addon, null, "Add-on is not installed");
+
+ let xpifile = do_get_file(IMPLICIT_ID_XPI);
+ yield promiseInstallAllFiles([xpifile]);
+
+ addon = yield promiseAddonByID(IMPLICIT_ID_ID);
+ notEqual(addon, null, "Add-on is installed");
+
+ addon.uninstall();
+});
+
+// We should also be able to install webext-implicit-id.xpi temporarily
+// and it should look just like the regular install (ie, the ID should
+// come from the signature)
+add_task(function* test_implicit_id_temp() {
+ // This test needs to read the xpi certificate which only works
+ // if signing is enabled.
+ ok(ADDON_SIGNING, "Add-on signing is enabled");
+
+ let addon = yield promiseAddonByID(IMPLICIT_ID_ID);
+ equal(addon, null, "Add-on is not installed");
+
+ let xpifile = do_get_file(IMPLICIT_ID_XPI);
+ yield AddonManager.installTemporaryAddon(xpifile);
+
+ addon = yield promiseAddonByID(IMPLICIT_ID_ID);
+ notEqual(addon, null, "Add-on is installed");
+
+ // The sourceURI of a temporary installed addon should be equal to the
+ // file url of the installed xpi file.
+ equal(addon.sourceURI && addon.sourceURI.spec,
+ Services.io.newFileURI(xpifile).spec,
+ "SourceURI of the add-on has the expected value");
+
+ addon.uninstall();
+});
+
+// We should be able to temporarily install an unsigned web extension
+// that does not have an ID in its manifest.
+add_task(function* test_unsigned_no_id_temp_install() {
+ AddonTestUtils.useRealCertChecks = true;
+ const manifest = {
+ name: "no ID",
+ description: "extension without an ID",
+ manifest_version: 2,
+ version: "1.0"
+ };
+
+ const addonDir = yield promiseWriteWebManifestForExtension(manifest, gTmpD,
+ "the-addon-sub-dir");
+ const addon = yield AddonManager.installTemporaryAddon(addonDir);
+ ok(addon.id, "ID should have been auto-generated");
+
+ // The sourceURI of a temporary installed addon should be equal to the
+ // file url of the installed source dir.
+ equal(addon.sourceURI && addon.sourceURI.spec,
+ Services.io.newFileURI(addonDir).spec,
+ "SourceURI of the add-on has the expected value");
+
+ // Install the same directory again, as if re-installing or reloading.
+ const secondAddon = yield AddonManager.installTemporaryAddon(addonDir);
+ // The IDs should be the same.
+ equal(secondAddon.id, addon.id, "Reinstalled add-on has the expected ID");
+
+ secondAddon.uninstall();
+ Services.obs.notifyObservers(addonDir, "flush-cache-entry", null);
+ addonDir.remove(true);
+ AddonTestUtils.useRealCertChecks = false;
+});
+
+// We should be able to install two extensions from manifests without IDs
+// at different locations and get two unique extensions.
+add_task(function* test_multiple_no_id_extensions() {
+ AddonTestUtils.useRealCertChecks = true;
+ const manifest = {
+ name: "no ID",
+ description: "extension without an ID",
+ manifest_version: 2,
+ version: "1.0"
+ };
+
+ let extension1 = ExtensionTestUtils.loadExtension({
+ manifest: manifest,
+ useAddonManager: "temporary",
+ });
+
+ let extension2 = ExtensionTestUtils.loadExtension({
+ manifest: manifest,
+ useAddonManager: "temporary",
+ });
+
+ yield Promise.all([extension1.startup(), extension2.startup()]);
+
+ const allAddons = yield new Promise(resolve => {
+ AddonManager.getAllAddons(addons => resolve(addons));
+ });
+ do_print(`Found these add-ons: ${allAddons.map(a => a.name).join(", ")}`);
+ const filtered = allAddons.filter(addon => addon.name === manifest.name);
+ // Make sure we have two add-ons by the same name.
+ equal(filtered.length, 2, "Two add-ons are installed with the same name");
+
+ yield extension1.unload();
+ yield extension2.unload();
+ AddonTestUtils.useRealCertChecks = false;
+});
+
+// Test that we can get the ID from browser_specific_settings
+add_task(function* test_bss_id() {
+ const ID = "webext_bss_id@tests.mozilla.org";
+
+ let manifest = {
+ name: "bss test",
+ description: "test that ID may be in browser_specific_settings",
+ manifest_version: 2,
+ version: "1.0",
+
+ browser_specific_settings: {
+ gecko: {
+ id: ID
+ }
+ }
+ };
+
+ let addon = yield promiseAddonByID(ID);
+ equal(addon, null, "Add-on is not installed");
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: manifest,
+ useAddonManager: "temporary",
+ });
+ yield extension.startup();
+
+ addon = yield promiseAddonByID(ID);
+ notEqual(addon, null, "Add-on is installed");
+
+ yield extension.unload();
+});
+
+// Test that if we have IDs in both browser_specific_settings and applications,
+// that we prefer the ID in browser_specific_settings.
+add_task(function* test_two_ids() {
+ const GOOD_ID = "two_ids@tests.mozilla.org";
+ const BAD_ID = "i_am_obsolete@tests.mozilla.org";
+
+ let manifest = {
+ name: "two id test",
+ description: "test a web extension with ids in both applications and browser_specific_settings",
+ manifest_version: 2,
+ version: "1.0",
+
+ applications: {
+ gecko: {
+ id: BAD_ID
+ }
+ },
+
+ browser_specific_settings: {
+ gecko: {
+ id: GOOD_ID
+ }
+ }
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: manifest,
+ useAddonManager: "temporary",
+ });
+ yield extension.startup();
+
+ let addon = yield promiseAddonByID(BAD_ID);
+ equal(addon, null, "Add-on is not found using bad ID");
+ addon = yield promiseAddonByID(GOOD_ID);
+ notEqual(addon, null, "Add-on is found using good ID");
+
+ yield extension.unload();
+});
+
+// Test that strict_min_version and strict_max_version are enforced for
+// loading temporary extension.
+add_task(function* test_strict_min_max() {
+ // the app version being compared to is 1.9.2
+ const addonId = "strict_min_max@tests.mozilla.org";
+ const MANIFEST = {
+ name: "strict min max test",
+ description: "test strict min and max with temporary loading",
+ manifest_version: 2,
+ version: "1.0",
+ };
+
+ // bad max good min
+ let apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_min_version: "1",
+ strict_max_version: "1"
+ },
+ },
+ }
+ let testManifest = Object.assign(apps, MANIFEST);
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ let expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
+ "add-on minVersion: 1. add-on maxVersion: 1.");
+ yield Assert.rejects(extension.startup(),
+ expectedMsg,
+ "Install rejects when specified maxVersion is not valid");
+
+ let addon = yield promiseAddonByID(addonId);
+ equal(addon, null, "Add-on is not installed");
+
+ // bad min good max
+ apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_min_version: "2",
+ strict_max_version: "2"
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
+ "add-on minVersion: 2. add-on maxVersion: 2.");
+ yield Assert.rejects(extension.startup(),
+ expectedMsg,
+ "Install rejects when specified minVersion is not valid");
+
+ addon = yield promiseAddonByID(addonId);
+ equal(addon, null, "Add-on is not installed");
+
+ // bad both
+ apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_min_version: "2",
+ strict_max_version: "1"
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version. " +
+ "add-on minVersion: 2. add-on maxVersion: 1.");
+ yield Assert.rejects(extension.startup(),
+ expectedMsg,
+ "Install rejects when specified minVersion and maxVersion are not valid");
+
+ addon = yield promiseAddonByID(addonId);
+ equal(addon, null, "Add-on is not installed");
+
+ // bad only min
+ apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_min_version: "2"
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version\. " +
+ "add-on minVersion: 2\.");
+ yield Assert.rejects(extension.startup(),
+ expectedMsg,
+ "Install rejects when specified minVersion and maxVersion are not valid");
+
+ addon = yield promiseAddonByID(addonId);
+ equal(addon, null, "Add-on is not installed");
+
+ // bad only max
+ apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_max_version: "1"
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ expectedMsg = new RegExp("Add-on strict_min_max@tests.mozilla.org is not compatible with application version\. " +
+ "add-on maxVersion: 1\.");
+ yield Assert.rejects(extension.startup(),
+ expectedMsg,
+ "Install rejects when specified minVersion and maxVersion are not valid");
+
+ addon = yield promiseAddonByID(addonId);
+ equal(addon, null, "Add-on is not installed");
+
+ // good both
+ apps = {
+ applications: {
+ gecko: {
+ id: addonId,
+ strict_min_version: "1",
+ strict_max_version: "2"
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ yield extension.startup();
+ addon = yield promiseAddonByID(addonId);
+
+ notEqual(addon, null, "Add-on is installed");
+ equal(addon.id, addonId, "Installed add-on has the expected ID");
+ yield extension.unload();
+
+ // good only min
+ let newId = "strict_min_only@tests.mozilla.org";
+ apps = {
+ applications: {
+ gecko: {
+ id: newId,
+ strict_min_version: "1",
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ yield extension.startup();
+ addon = yield promiseAddonByID(newId);
+
+ notEqual(addon, null, "Add-on is installed");
+ equal(addon.id, newId, "Installed add-on has the expected ID");
+
+ yield extension.unload();
+
+ // good only max
+ newId = "strict_max_only@tests.mozilla.org";
+ apps = {
+ applications: {
+ gecko: {
+ id: newId,
+ strict_max_version: "2",
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ yield extension.startup();
+ addon = yield promiseAddonByID(newId);
+
+ notEqual(addon, null, "Add-on is installed");
+ equal(addon.id, newId, "Installed add-on has the expected ID");
+
+ yield extension.unload();
+
+ // * in min will throw an error
+ for (let version of ["0.*", "0.*.0"]) {
+ newId = "strict_min_star@tests.mozilla.org";
+ let minStarApps = {
+ applications: {
+ gecko: {
+ id: newId,
+ strict_min_version: version,
+ },
+ },
+ }
+
+ let minStarTestManifest = Object.assign(minStarApps, MANIFEST);
+
+ let minStarExtension = ExtensionTestUtils.loadExtension({
+ manifest: minStarTestManifest,
+ useAddonManager: "temporary",
+ });
+
+ yield Assert.rejects(
+ minStarExtension.startup(),
+ /The use of '\*' in strict_min_version is invalid/,
+ "loading an extension with a * in strict_min_version throws an exception");
+
+ let minStarAddon = yield promiseAddonByID(newId);
+ equal(minStarAddon, null, "Add-on is not installed");
+ }
+
+ // incompatible extension but with compatibility checking off
+ newId = "checkCompatibility@tests.mozilla.org";
+ apps = {
+ applications: {
+ gecko: {
+ id: newId,
+ strict_max_version: "1",
+ },
+ },
+ }
+ testManifest = Object.assign(apps, MANIFEST);
+
+ extension = ExtensionTestUtils.loadExtension({
+ manifest: testManifest,
+ useAddonManager: "temporary",
+ });
+
+ let savedCheckCompatibilityValue = AddonManager.checkCompatibility;
+ AddonManager.checkCompatibility = false;
+ yield extension.startup();
+ addon = yield promiseAddonByID(newId);
+
+ notEqual(addon, null, "Add-on is installed");
+ equal(addon.id, newId, "Installed add-on has the expected ID");
+
+ yield extension.unload();
+ AddonManager.checkCompatibility = savedCheckCompatibilityValue;
+});
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_paths.js b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_paths.js
new file mode 100644
index 000000000..29a3dacf7
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/test_webextension_paths.js
@@ -0,0 +1,55 @@
+function run_test() {
+ run_next_test();
+}
+
+let profileDir;
+add_task(function* setup() {
+ profileDir = gProfD.clone();
+ profileDir.append("extensions");
+
+ createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
+ startupManager();
+});
+
+// When installing an unpacked addon we derive the ID from the
+// directory name. Make sure that if the directoy name is not a valid
+// addon ID that we reject it.
+add_task(function* test_bad_unpacked_path() {
+ let MANIFEST_ID = "webext_bad_path@tests.mozilla.org";
+
+ let manifest = {
+ name: "path test",
+ description: "test of a bad directory name",
+ manifest_version: 2,
+ version: "1.0",
+
+ browser_specific_settings: {
+ gecko: {
+ id: MANIFEST_ID
+ }
+ }
+ };
+
+ const directories = [
+ "not a valid ID",
+ '"quotes"@tests.mozilla.org',
+ ];
+
+ for (let dir of directories) {
+ try {
+ yield promiseWriteWebManifestForExtension(manifest, profileDir, dir);
+ } catch (ex) {
+ // This can fail if the underlying filesystem (looking at you windows)
+ // doesn't handle some of the characters in the ID. In that case,
+ // just ignore this test on this platform.
+ continue;
+ }
+ yield promiseRestartManager();
+
+ let addon = yield promiseAddonByID(dir);
+ do_check_eq(addon, null);
+ addon = yield promiseAddonByID(MANIFEST_ID);
+ do_check_eq(addon, null);
+ }
+});
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-shared.ini b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-shared.ini
new file mode 100644
index 000000000..1e86e5861
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-shared.ini
@@ -0,0 +1,334 @@
+# The file is shared between the two main xpcshell manifest files.
+[DEFAULT]
+skip-if = toolkit == 'android'
+tags = addons
+
+[test_AddonRepository.js]
+[test_reload.js]
+# Bug 676992: test consistently hangs on Android
+# There's a problem removing a temp file without manually clearing the cache on Windows
+skip-if = os == "android" || os == "win"
+tags = webextensions
+[test_AddonRepository_cache.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_AddonRepository_compatmode.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_LightweightThemeManager.js]
+[test_backgroundupdate.js]
+[test_bad_json.js]
+[test_badschema.js]
+[test_blocklistchange.js]
+# Times out during parallel runs on desktop
+requesttimeoutfactor = 2
+[test_blocklist_prefs.js]
+[test_blocklist_metadata_filters.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_blocklist_regexp.js]
+skip-if = os == "android"
+[test_bootstrap.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bootstrap_const.js]
+[test_bootstrap_resource.js]
+[test_bug299716.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug299716_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Hardcoded port in install.rdf.
+[test_bug324121.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug335238.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug371495.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug384052.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug393285.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug394300.js]
+# Bug 676992: test consistently hangs on Android
+# Bug 1026805: frequent hangs on OSX 10.8
+skip-if = os == "android" || os == "mac"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug397778.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug406118.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug424262.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug425657.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug430120.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug449027.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug455906.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug465190.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug468528.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_1.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_1_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_3.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_3_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug470377_4.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug514327_1.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug514327_2.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug514327_3.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_bug521905.js]
+[test_bug526598.js]
+[test_bug541420.js]
+[test_bug542391.js]
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_bug554133.js]
+[test_bug559800.js]
+[test_bug563256.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_bug564030.js]
+[test_bug566626.js]
+[test_bug567184.js]
+[test_bug569138.js]
+[test_bug570173.js]
+[test_bug576735.js]
+[test_bug587088.js]
+[test_bug594058.js]
+[test_bug595081.js]
+[test_bug595573.js]
+[test_bug596607.js]
+[test_bug616841.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_bug619730.js]
+[test_bug620837.js]
+[test_bug655254.js]
+[test_bug659772.js]
+[test_bug675371.js]
+[test_bug740612.js]
+[test_bug753900.js]
+[test_bug757663.js]
+[test_bug953156.js]
+[test_checkcompatibility.js]
+[test_checkCompatibility_themeOverride.js]
+[test_childprocess.js]
+[test_ChromeManifestParser.js]
+[test_compatoverrides.js]
+[test_corrupt.js]
+[test_corrupt_strictcompat.js]
+[test_corruptfile.js]
+[test_dataDirectory.js]
+[test_default_providers_pref.js]
+[test_dictionary.js]
+[test_langpack.js]
+[test_disable.js]
+[test_distribution.js]
+[test_dss.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_duplicateplugins.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_error.js]
+[test_experiment.js]
+[test_filepointer.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_fuel.js]
+[test_general.js]
+[test_getresource.js]
+[test_gfxBlacklist_Device.js]
+[test_gfxBlacklist_DriverNew.js]
+[test_gfxBlacklist_Equal_DriverNew.js]
+[test_gfxBlacklist_Equal_DriverOld.js]
+[test_gfxBlacklist_Equal_OK.js]
+[test_gfxBlacklist_GTE_DriverOld.js]
+[test_gfxBlacklist_GTE_OK.js]
+[test_gfxBlacklist_No_Comparison.js]
+[test_gfxBlacklist_OK.js]
+[test_gfxBlacklist_OS.js]
+[test_gfxBlacklist_OSVersion_match.js]
+[test_gfxBlacklist_OSVersion_mismatch_OSVersion.js]
+[test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js]
+[test_gfxBlacklist_Vendor.js]
+[test_gfxBlacklist_Version.js]
+[test_gfxBlacklist_prefs.js]
+# Bug 1248787 - consistently fails
+skip-if = true
+[test_hasbinarycomponents.js]
+[test_hotfix.js]
+[test_install.js]
+[test_install_icons.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_install_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_isDebuggable.js]
+[test_locale.js]
+[test_locked.js]
+[test_locked2.js]
+[test_locked_strictcompat.js]
+[test_manifest.js]
+[test_mapURIToAddonID.js]
+# Same as test_bootstrap.js
+skip-if = os == "android"
+[test_migrate1.js]
+[test_migrate2.js]
+[test_migrate3.js]
+[test_migrate4.js]
+# Times out during parallel runs on desktop
+requesttimeoutfactor = 2
+[test_migrate5.js]
+[test_migrateAddonRepository.js]
+[test_migrate_max_version.js]
+[test_multiprocessCompatible.js]
+[test_no_addons.js]
+[test_onPropertyChanged_appDisabled.js]
+[test_permissions.js]
+[test_permissions_prefs.js]
+[test_plugins.js]
+[test_pluginchange.js]
+# PluginProvider.jsm is not shipped on Android
+skip-if = os == "android"
+[test_pluginBlocklistCtp.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_pref_properties.js]
+[test_registry.js]
+[test_safemode.js]
+[test_signed_updatepref.js]
+run-if = addon_signing
+skip-if = require_signing
+[test_signed_verify.js]
+run-if = addon_signing
+[test_signed_inject.js]
+run-if = addon_signing
+[test_signed_install.js]
+run-if = addon_signing
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_signed_long.js]
+run-if = addon_signing
+[test_signed_migrate.js]
+run-if = addon_signing
+[test_signed_multi.js]
+run-if = addon_signing
+[test_startup.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_syncGUID.js]
+[test_strictcompatibility.js]
+[test_targetPlatforms.js]
+[test_theme.js]
+# Bug 676992: test consistently fails on Android
+fail-if = os == "android"
+[test_types.js]
+[test_undothemeuninstall.js]
+[test_undouninstall.js]
+[test_uninstall.js]
+[test_update.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_update_webextensions.js]
+tags = webextensions
+[test_updateCancel.js]
+[test_update_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_update_ignorecompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+[test_updatecheck.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_json_updatecheck.js]
+[test_seen.js]
+[test_seen_newprofile.js]
+[test_updateid.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses hardcoded ports in xpi files.
+[test_update_compatmode.js]
+[test_upgrade.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses global XCurProcD dir.
+[test_upgrade_strictcompat.js]
+# Bug 676992: test consistently hangs on Android
+skip-if = os == "android"
+run-sequentially = Uses global XCurProcD dir.
+[test_overrideblocklist.js]
+run-sequentially = Uses global XCurProcD dir.
+[test_sourceURI.js]
+[test_webextension_icons.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+[test_webextension.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+[test_webextension_install.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+[test_webextension_embedded.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+[test_bootstrap_globals.js]
+[test_bug1180901_2.js]
+skip-if = os != "win"
+[test_bug1180901.js]
+skip-if = os != "win"
+[test_e10s_restartless.js]
+[test_switch_os.js]
+# Bug 1246231
+skip-if = os == "mac" && debug
+[test_softblocked.js]
+[test_ext_management.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-unpack.ini b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-unpack.ini
new file mode 100644
index 000000000..42a0ca1ca
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell-unpack.ini
@@ -0,0 +1,12 @@
+ [DEFAULT]
+head = head_addons.js head_unpack.js
+tail =
+firefox-appdir = browser
+skip-if = toolkit == 'android'
+dupe-manifest =
+tags = addons
+
+[test_webextension_paths.js]
+tags = webextensions
+
+[include:xpcshell-shared.ini]
diff --git a/toolkit/mozapps/webextensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell.ini
new file mode 100644
index 000000000..2b95eb158
--- /dev/null
+++ b/toolkit/mozapps/webextensions/test/xpcshell/xpcshell.ini
@@ -0,0 +1,50 @@
+[DEFAULT]
+skip-if = toolkit == 'android'
+tags = addons
+head = head_addons.js
+tail =
+firefox-appdir = browser
+dupe-manifest =
+support-files =
+ data/**
+ xpcshell-shared.ini
+
+[test_addon_path_service.js]
+[test_asyncBlocklistLoad.js]
+[test_blocklist_gfx.js]
+[test_cache_certdb.js]
+run-if = addon_signing
+[test_cacheflush.js]
+[test_DeferredSave.js]
+[test_gmpProvider.js]
+skip-if = appname != "firefox"
+[test_hotfix_cert.js]
+[test_isReady.js]
+[test_metadata_update.js]
+[test_pluginInfoURL.js]
+[test_provider_markSafe.js]
+[test_provider_shutdown.js]
+[test_provider_unsafe_access_shutdown.js]
+[test_provider_unsafe_access_startup.js]
+[test_ProductAddonChecker.js]
+[test_shutdown.js]
+[test_system_update.js]
+[test_system_reset.js]
+[test_XPIcancel.js]
+[test_XPIStates.js]
+[test_temporary.js]
+tags = webextensions
+[test_install_from_sources.js]
+[test_proxies.js]
+[test_proxy.js]
+[test_pass_symbol.js]
+[test_delay_update.js]
+[test_nodisable_hidden.js]
+[test_delay_update_webextension.js]
+skip-if = appname == "thunderbird"
+tags = webextensions
+[test_dependencies.js]
+[test_schema_change.js]
+[test_system_delay_update.js]
+
+[include:xpcshell-shared.ini]