From 4fb11cd5966461bccc3ed1599b808237be6b0de9 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Sat, 10 Feb 2018 02:49:12 -0500 Subject: Move WebExtensions enabled Add-ons Manager --- .../webextensions/test/xpcshell/.eslintrc.js | 7 + .../test/xpcshell/data/BootstrapMonitor.jsm | 30 + .../xpcshell/data/blocklistchange/addon_change.xml | 31 + .../data/blocklistchange/addon_update1.rdf | 144 ++ .../data/blocklistchange/addon_update2.rdf | 144 ++ .../data/blocklistchange/addon_update3.rdf | 144 ++ .../xpcshell/data/blocklistchange/app_update.xml | 62 + .../data/blocklistchange/blocklist_update1.xml | 3 + .../data/blocklistchange/blocklist_update2.xml | 26 + .../data/blocklistchange/manual_update.xml | 27 + .../test/xpcshell/data/bug455906_block.xml | 18 + .../test/xpcshell/data/bug455906_empty.xml | 7 + .../test/xpcshell/data/bug455906_start.xml | 30 + .../test/xpcshell/data/bug455906_warn.xml | 33 + .../webextensions/test/xpcshell/data/corrupt.xpi | 1 + .../test/xpcshell/data/corruptfile.xpi | Bin 0 -> 633 bytes .../webextensions/test/xpcshell/data/empty.xpi | Bin 0 -> 197 bytes .../test/xpcshell/data/from_sources/bootstrap.js | 1 + .../test/xpcshell/data/from_sources/install.rdf | 28 + .../test/xpcshell/data/pluginInfoURL_block.xml | 45 + .../test/xpcshell/data/productaddons/bad.txt | 1 + .../test/xpcshell/data/productaddons/bad.xml | 3 + .../test/xpcshell/data/productaddons/bad2.xml | 3 + .../test/xpcshell/data/productaddons/empty.xml | 5 + .../test/xpcshell/data/productaddons/good.xml | 11 + .../test/xpcshell/data/productaddons/missing.xml | 3 + .../test/xpcshell/data/productaddons/unsigned.xpi | Bin 0 -> 452 bytes .../data/signing_checks/bootstrap_1/bootstrap.js | 29 + .../data/signing_checks/bootstrap_1/install.rdf | 24 + .../data/signing_checks/bootstrap_1/test.txt | 1 + .../data/signing_checks/bootstrap_2/bootstrap.js | 29 + .../data/signing_checks/bootstrap_2/install.rdf | 24 + .../data/signing_checks/bootstrap_2/test.txt | 1 + .../xpcshell/data/signing_checks/hotfix_badid.xpi | Bin 0 -> 5151 bytes .../xpcshell/data/signing_checks/hotfix_broken.xpi | Bin 0 -> 5298 bytes .../xpcshell/data/signing_checks/hotfix_good.xpi | Bin 0 -> 5158 bytes .../xpcshell/data/signing_checks/long_63_hash.xpi | Bin 0 -> 4471 bytes .../xpcshell/data/signing_checks/long_63_plain.xpi | Bin 0 -> 4433 bytes .../xpcshell/data/signing_checks/long_64_hash.xpi | Bin 0 -> 4474 bytes .../xpcshell/data/signing_checks/long_64_plain.xpi | Bin 0 -> 4436 bytes .../xpcshell/data/signing_checks/long_65_hash.xpi | Bin 0 -> 4487 bytes .../xpcshell/data/signing_checks/multi_badid.xpi | Bin 0 -> 6443 bytes .../xpcshell/data/signing_checks/multi_broken.xpi | Bin 0 -> 6563 bytes .../xpcshell/data/signing_checks/multi_signed.xpi | Bin 0 -> 6425 bytes .../data/signing_checks/multi_unsigned.xpi | Bin 0 -> 2436 bytes .../data/signing_checks/nonbootstrap_1/install.rdf | 23 + .../data/signing_checks/nonbootstrap_1/test.txt | 1 + .../data/signing_checks/nonbootstrap_2/install.rdf | 23 + .../data/signing_checks/nonbootstrap_2/test.txt | 1 + .../signing_checks/preliminary_bootstrap_2.xpi | Bin 0 -> 5161 bytes .../data/signing_checks/signed_bootstrap_1.xpi | Bin 0 -> 5150 bytes .../data/signing_checks/signed_bootstrap_2.xpi | Bin 0 -> 5149 bytes .../signing_checks/signed_bootstrap_badid_2.xpi | Bin 0 -> 5155 bytes .../data/signing_checks/signed_nonbootstrap_2.xpi | Bin 0 -> 4627 bytes .../signing_checks/signed_nonbootstrap_badid_2.xpi | Bin 0 -> 4634 bytes .../data/signing_checks/unsigned_bootstrap_2.xpi | Bin 0 -> 1156 bytes .../signing_checks/unsigned_nonbootstrap_2.xpi | Bin 0 -> 691 bytes .../test/xpcshell/data/system_addons/bootstrap.js | 1 + .../test/xpcshell/data/system_addons/system1_1.xpi | Bin 0 -> 4692 bytes .../data/system_addons/system1_1_badcert.xpi | Bin 0 -> 4808 bytes .../test/xpcshell/data/system_addons/system1_2.xpi | Bin 0 -> 4695 bytes .../test/xpcshell/data/system_addons/system2_1.xpi | Bin 0 -> 4692 bytes .../test/xpcshell/data/system_addons/system2_2.xpi | Bin 0 -> 4695 bytes .../test/xpcshell/data/system_addons/system2_3.xpi | Bin 0 -> 4697 bytes .../test/xpcshell/data/system_addons/system3_1.xpi | Bin 0 -> 4689 bytes .../test/xpcshell/data/system_addons/system3_2.xpi | Bin 0 -> 4691 bytes .../test/xpcshell/data/system_addons/system3_3.xpi | Bin 0 -> 4693 bytes .../test/xpcshell/data/system_addons/system4_1.xpi | Bin 0 -> 4692 bytes .../test/xpcshell/data/system_addons/system5_1.xpi | Bin 0 -> 4691 bytes .../data/system_addons/system6_1_unpack.xpi | Bin 0 -> 4708 bytes .../data/system_addons/system6_2_notBootstrap.xpi | Bin 0 -> 4682 bytes .../system_addons/system6_3_notMultiprocess.xpi | Bin 0 -> 4675 bytes .../data/system_addons/system_delay_complete.xpi | Bin 0 -> 5090 bytes .../data/system_addons/system_delay_complete_2.xpi | Bin 0 -> 4706 bytes .../data/system_addons/system_delay_defer.xpi | Bin 0 -> 5095 bytes .../data/system_addons/system_delay_defer_2.xpi | Bin 0 -> 4701 bytes .../data/system_addons/system_delay_defer_also.xpi | Bin 0 -> 5117 bytes .../system_addons/system_delay_defer_also_2.xpi | Bin 0 -> 4716 bytes .../data/system_addons/system_delay_ignore.xpi | Bin 0 -> 5098 bytes .../data/system_addons/system_delay_ignore_2.xpi | Bin 0 -> 4707 bytes .../data/system_addons/system_failed_update.xpi | Bin 0 -> 735 bytes .../test/xpcshell/data/test_AddonRepository.xml | 820 +++++++++ .../xpcshell/data/test_AddonRepository_cache.xml | 182 ++ .../test_AddonRepository_compatmode_ignore.xml | 23 + .../test_AddonRepository_compatmode_normal.xml | 23 + .../test_AddonRepository_compatmode_strict.xml | 23 + .../xpcshell/data/test_AddonRepository_empty.xml | 3 + .../xpcshell/data/test_AddonRepository_failed.xml | 21 + .../data/test_AddonRepository_getAddonsByIDs.xml | 187 ++ .../test/xpcshell/data/test_backgroundupdate.rdf | 70 + .../data/test_blocklist_metadata_filters_1.xml | 21 + .../test/xpcshell/data/test_blocklist_prefs_1.xml | 28 + .../test/xpcshell/data/test_blocklist_regexp_1.xml | 20 + .../test/xpcshell/data/test_bug299716.rdf | 181 ++ .../test/xpcshell/data/test_bug299716_2.rdf | 23 + .../test/xpcshell/data/test_bug324121.rdf | 91 + .../test/xpcshell/data/test_bug393285.xml | 30 + .../test/xpcshell/data/test_bug394300.rdf | 159 ++ .../test/xpcshell/data/test_bug424262.xml | 185 ++ .../test/xpcshell/data/test_bug449027_app.xml | 333 ++++ .../test/xpcshell/data/test_bug449027_toolkit.xml | 208 +++ .../test/xpcshell/data/test_bug468528.xml | 15 + .../xpcshell/data/test_bug470377/install_1.rdf | 17 + .../xpcshell/data/test_bug470377/install_2.rdf | 17 + .../xpcshell/data/test_bug470377/install_3.rdf | 17 + .../xpcshell/data/test_bug470377/install_4.rdf | 17 + .../xpcshell/data/test_bug470377/install_5.rdf | 17 + .../test/xpcshell/data/test_bug470377/update_1.rdf | 26 + .../test/xpcshell/data/test_bug470377/update_2.rdf | 26 + .../test/xpcshell/data/test_bug470377/update_3.rdf | 26 + .../test/xpcshell/data/test_bug470377/update_4.rdf | 26 + .../test/xpcshell/data/test_bug470377/update_5.rdf | 26 + .../test/xpcshell/data/test_bug514327_1.xml | 17 + .../test/xpcshell/data/test_bug514327_2.xml | 10 + .../test/xpcshell/data/test_bug514327_3_empty.xml | 4 + .../xpcshell/data/test_bug514327_3_outdated_1.xml | 13 + .../xpcshell/data/test_bug514327_3_outdated_2.xml | 13 + .../test/xpcshell/data/test_bug526598_1.xpi | Bin 0 -> 458 bytes .../test/xpcshell/data/test_bug526598_2.xpi | Bin 0 -> 458 bytes .../test/xpcshell/data/test_bug541420.xpi | Bin 0 -> 577 bytes .../test/xpcshell/data/test_bug542391.rdf | 25 + .../test/xpcshell/data/test_bug554133.xml | 292 ++++ .../test/xpcshell/data/test_bug619730.xml | 7 + .../test/xpcshell/data/test_bug655254.rdf | 26 + .../test/xpcshell/data/test_compatoverrides.xml | 228 +++ .../test/xpcshell/data/test_corrupt.rdf | 44 + .../data/test_delay_update_complete/bootstrap.js | 24 + .../data/test_delay_update_defer/bootstrap.js | 34 + .../data/test_delay_update_ignore/bootstrap.js | 26 + .../xpcshell/data/test_delay_updates_complete.json | 11 + .../xpcshell/data/test_delay_updates_complete.rdf | 26 + .../xpcshell/data/test_delay_updates_defer.json | 11 + .../xpcshell/data/test_delay_updates_defer.rdf | 26 + .../xpcshell/data/test_delay_updates_ignore.json | 11 + .../xpcshell/data/test_delay_updates_ignore.rdf | 26 + .../test/xpcshell/data/test_dictionary.rdf | 65 + .../data/test_distribution2_2/bootstrap.js | 21 + .../xpcshell/data/test_distribution2_2/install.rdf | 23 + .../data/test_distribution2_2/subdir/dummy.txt | 1 + .../test_distribution2_2/subdir/subdir2/dummy2.txt | 1 + .../test/xpcshell/data/test_gfxBlacklist.xml | 304 ++++ .../test/xpcshell/data/test_gfxBlacklist2.xml | 31 + .../test/xpcshell/data/test_gfxBlacklist_AllOS.xml | 783 +++++++++ .../xpcshell/data/test_gfxBlacklist_OSVersion.xml | 32 + .../test/xpcshell/data/test_hotfix_1.rdf | 26 + .../test/xpcshell/data/test_hotfix_2.rdf | 26 + .../test/xpcshell/data/test_hotfix_3.rdf | 26 + .../test/xpcshell/data/test_install.rdf | 63 + .../test/xpcshell/data/test_install.xml | 53 + .../test/xpcshell/data/test_migrate.rdf | 125 ++ .../test/xpcshell/data/test_migrate4.rdf | 46 + .../test/xpcshell/data/test_no_update.json | 7 + .../data/test_overrideblocklist/ancient.xml | 8 + .../xpcshell/data/test_overrideblocklist/new.xml | 8 + .../xpcshell/data/test_overrideblocklist/old.xml | 8 + .../test/xpcshell/data/test_pluginBlocklistCtp.xml | 26 + .../xpcshell/data/test_pluginBlocklistCtpUndo.xml | 10 + .../test/xpcshell/data/test_proxy/bootstrap.js | 1 + .../test/xpcshell/data/test_softblocked1.xml | 9 + .../test/xpcshell/data/test_sourceURI.xml | 18 + .../test/xpcshell/data/test_temporary/bootstrap.js | 1 + .../test/xpcshell/data/test_update.json | 215 +++ .../test/xpcshell/data/test_update.rdf | 270 +++ .../test/xpcshell/data/test_update.xml | 26 + .../test/xpcshell/data/test_update_multi.rdf | 26 + .../test/xpcshell/data/test_updatecheck.json | 327 ++++ .../test/xpcshell/data/test_updatecheck.rdf | 419 +++++ .../xpcshell/data/test_updatecompatmode_ignore.rdf | 26 + .../xpcshell/data/test_updatecompatmode_normal.rdf | 26 + .../xpcshell/data/test_updatecompatmode_strict.rdf | 26 + .../test/xpcshell/data/test_updateid.rdf | 26 + .../webextensions/test/xpcshell/data/unsigned.xpi | Bin 0 -> 452 bytes .../test/xpcshell/data/webext-implicit-id.xpi | Bin 0 -> 4182 bytes .../webextensions/test/xpcshell/head_addons.js | 1345 ++++++++++++++ .../webextensions/test/xpcshell/head_unpack.js | 3 + .../test/xpcshell/test_AddonRepository.js | 625 +++++++ .../test/xpcshell/test_AddonRepository_cache.js | 704 ++++++++ .../xpcshell/test_AddonRepository_compatmode.js | 90 + .../test/xpcshell/test_ChromeManifestParser.js | 108 ++ .../test/xpcshell/test_DeferredSave.js | 549 ++++++ .../test/xpcshell/test_LightweightThemeManager.js | 598 +++++++ .../test/xpcshell/test_ProductAddonChecker.js | 244 +++ .../webextensions/test/xpcshell/test_XPIStates.js | 299 ++++ .../webextensions/test/xpcshell/test_XPIcancel.js | 66 + .../test/xpcshell/test_addon_path_service.js | 38 + .../test/xpcshell/test_asyncBlocklistLoad.js | 44 + .../test/xpcshell/test_backgroundupdate.js | 126 ++ .../webextensions/test/xpcshell/test_bad_json.js | 54 + .../webextensions/test/xpcshell/test_badschema.js | 404 +++++ .../test/xpcshell/test_blocklist_gfx.js | 157 ++ .../xpcshell/test_blocklist_metadata_filters.js | 147 ++ .../test/xpcshell/test_blocklist_prefs.js | 148 ++ .../test/xpcshell/test_blocklist_regexp.js | 114 ++ .../test/xpcshell/test_blocklistchange.js | 1305 ++++++++++++++ .../webextensions/test/xpcshell/test_bootstrap.js | 1403 +++++++++++++++ .../test/xpcshell/test_bootstrap_const.js | 17 + .../test/xpcshell/test_bootstrap_globals.js | 37 + .../test/xpcshell/test_bootstrap_resource.js | 56 + .../webextensions/test/xpcshell/test_bug1180901.js | 35 + .../test/xpcshell/test_bug1180901_2.js | 60 + .../webextensions/test/xpcshell/test_bug299716.js | 208 +++ .../test/xpcshell/test_bug299716_2.js | 50 + .../webextensions/test/xpcshell/test_bug324121.js | 178 ++ .../webextensions/test/xpcshell/test_bug335238.js | 173 ++ .../webextensions/test/xpcshell/test_bug371495.js | 35 + .../webextensions/test/xpcshell/test_bug384052.js | 103 ++ .../webextensions/test/xpcshell/test_bug393285.js | 316 ++++ .../webextensions/test/xpcshell/test_bug394300.js | 56 + .../webextensions/test/xpcshell/test_bug397778.js | 117 ++ .../webextensions/test/xpcshell/test_bug406118.js | 155 ++ .../webextensions/test/xpcshell/test_bug424262.js | 62 + .../webextensions/test/xpcshell/test_bug425657.js | 27 + .../webextensions/test/xpcshell/test_bug430120.js | 135 ++ .../webextensions/test/xpcshell/test_bug449027.js | 429 +++++ .../webextensions/test/xpcshell/test_bug455906.js | 517 ++++++ .../webextensions/test/xpcshell/test_bug465190.js | 39 + .../webextensions/test/xpcshell/test_bug468528.js | 58 + .../test/xpcshell/test_bug470377_1.js | 49 + .../test/xpcshell/test_bug470377_1_strictcompat.js | 49 + .../test/xpcshell/test_bug470377_2.js | 49 + .../test/xpcshell/test_bug470377_3.js | 95 + .../test/xpcshell/test_bug470377_3_strictcompat.js | 94 + .../test/xpcshell/test_bug470377_4.js | 92 + .../test/xpcshell/test_bug514327_1.js | 59 + .../test/xpcshell/test_bug514327_2.js | 41 + .../test/xpcshell/test_bug514327_3.js | 139 ++ .../webextensions/test/xpcshell/test_bug521905.js | 59 + .../webextensions/test/xpcshell/test_bug526598.js | 54 + .../webextensions/test/xpcshell/test_bug541420.js | 37 + .../webextensions/test/xpcshell/test_bug542391.js | 464 +++++ .../webextensions/test/xpcshell/test_bug554133.js | 86 + .../webextensions/test/xpcshell/test_bug559800.js | 71 + .../webextensions/test/xpcshell/test_bug563256.js | 259 +++ .../webextensions/test/xpcshell/test_bug564030.js | 63 + .../webextensions/test/xpcshell/test_bug566626.js | 112 ++ .../webextensions/test/xpcshell/test_bug567184.js | 53 + .../webextensions/test/xpcshell/test_bug569138.js | 147 ++ .../webextensions/test/xpcshell/test_bug570173.js | 61 + .../webextensions/test/xpcshell/test_bug576735.js | 66 + .../webextensions/test/xpcshell/test_bug587088.js | 174 ++ .../webextensions/test/xpcshell/test_bug594058.js | 88 + .../webextensions/test/xpcshell/test_bug595081.js | 27 + .../webextensions/test/xpcshell/test_bug595573.js | 40 + .../webextensions/test/xpcshell/test_bug596607.js | 147 ++ .../webextensions/test/xpcshell/test_bug616841.js | 26 + .../webextensions/test/xpcshell/test_bug619730.js | 64 + .../webextensions/test/xpcshell/test_bug620837.js | 145 ++ .../webextensions/test/xpcshell/test_bug655254.js | 164 ++ .../webextensions/test/xpcshell/test_bug659772.js | 340 ++++ .../webextensions/test/xpcshell/test_bug675371.js | 91 + .../webextensions/test/xpcshell/test_bug740612.js | 40 + .../webextensions/test/xpcshell/test_bug753900.js | 86 + .../webextensions/test/xpcshell/test_bug757663.js | 112 ++ .../webextensions/test/xpcshell/test_bug953156.js | 51 + .../test/xpcshell/test_cache_certdb.js | 82 + .../webextensions/test/xpcshell/test_cacheflush.js | 127 ++ .../test_checkCompatibility_themeOverride.js | 93 + .../test/xpcshell/test_checkcompatibility.js | 196 +++ .../test/xpcshell/test_childprocess.js | 21 + .../test/xpcshell/test_compatoverrides.js | 259 +++ .../webextensions/test/xpcshell/test_corrupt.js | 406 +++++ .../test/xpcshell/test_corrupt_strictcompat.js | 405 +++++ .../test/xpcshell/test_corruptfile.js | 83 + .../test/xpcshell/test_dataDirectory.js | 50 + .../test/xpcshell/test_default_providers_pref.js | 13 + .../test/xpcshell/test_delay_update.js | 260 +++ .../xpcshell/test_delay_update_webextension.js | 344 ++++ .../test/xpcshell/test_dependencies.js | 144 ++ .../webextensions/test/xpcshell/test_dictionary.js | 811 +++++++++ .../webextensions/test/xpcshell/test_disable.js | 194 +++ .../test/xpcshell/test_distribution.js | 273 +++ .../webextensions/test/xpcshell/test_dss.js | 824 +++++++++ .../test/xpcshell/test_duplicateplugins.js | 187 ++ .../test/xpcshell/test_e10s_restartless.js | 429 +++++ .../webextensions/test/xpcshell/test_error.js | 90 + .../webextensions/test/xpcshell/test_experiment.js | 131 ++ .../test/xpcshell/test_ext_management.js | 137 ++ .../test/xpcshell/test_filepointer.js | 403 +++++ .../webextensions/test/xpcshell/test_fuel.js | 165 ++ .../webextensions/test/xpcshell/test_general.js | 58 + .../test/xpcshell/test_getresource.js | 94 + .../test/xpcshell/test_gfxBlacklist_Device.js | 96 + .../test/xpcshell/test_gfxBlacklist_DriverNew.js | 92 + .../xpcshell/test_gfxBlacklist_Equal_DriverNew.js | 123 ++ .../xpcshell/test_gfxBlacklist_Equal_DriverOld.js | 93 + .../test/xpcshell/test_gfxBlacklist_Equal_OK.js | 93 + .../xpcshell/test_gfxBlacklist_GTE_DriverOld.js | 93 + .../test/xpcshell/test_gfxBlacklist_GTE_OK.js | 93 + .../xpcshell/test_gfxBlacklist_No_Comparison.js | 89 + .../test/xpcshell/test_gfxBlacklist_OK.js | 94 + .../test/xpcshell/test_gfxBlacklist_OS.js | 93 + .../xpcshell/test_gfxBlacklist_OSVersion_match.js | 95 + ...fxBlacklist_OSVersion_mismatch_DriverVersion.js | 95 + ...st_gfxBlacklist_OSVersion_mismatch_OSVersion.js | 96 + .../test/xpcshell/test_gfxBlacklist_Vendor.js | 93 + .../test/xpcshell/test_gfxBlacklist_Version.js | 145 ++ .../test/xpcshell/test_gfxBlacklist_prefs.js | 135 ++ .../test/xpcshell/test_gmpProvider.js | 416 +++++ .../test/xpcshell/test_hasbinarycomponents.js | 82 + .../webextensions/test/xpcshell/test_hotfix.js | 309 ++++ .../test/xpcshell/test_hotfix_cert.js | 167 ++ .../webextensions/test/xpcshell/test_install.js | 1843 ++++++++++++++++++++ .../test/xpcshell/test_install_from_sources.js | 80 + .../test/xpcshell/test_install_icons.js | 61 + .../test/xpcshell/test_install_strictcompat.js | 1726 ++++++++++++++++++ .../test/xpcshell/test_isDebuggable.js | 36 + .../webextensions/test/xpcshell/test_isReady.js | 49 + .../test/xpcshell/test_json_updatecheck.js | 372 ++++ .../webextensions/test/xpcshell/test_langpack.js | 339 ++++ .../webextensions/test/xpcshell/test_locale.js | 149 ++ .../webextensions/test/xpcshell/test_locked.js | 544 ++++++ .../webextensions/test/xpcshell/test_locked2.js | 297 ++++ .../test/xpcshell/test_locked_strictcompat.js | 567 ++++++ .../webextensions/test/xpcshell/test_manifest.js | 562 ++++++ .../test/xpcshell/test_mapURIToAddonID.js | 347 ++++ .../test/xpcshell/test_metadata_update.js | 159 ++ .../webextensions/test/xpcshell/test_migrate1.js | 231 +++ .../webextensions/test/xpcshell/test_migrate2.js | 267 +++ .../webextensions/test/xpcshell/test_migrate3.js | 229 +++ .../webextensions/test/xpcshell/test_migrate4.js | 321 ++++ .../webextensions/test/xpcshell/test_migrate5.js | 139 ++ .../test/xpcshell/test_migrateAddonRepository.js | 127 ++ .../test/xpcshell/test_migrate_max_version.js | 103 ++ .../test/xpcshell/test_multiprocessCompatible.js | 120 ++ .../webextensions/test/xpcshell/test_no_addons.js | 98 ++ .../test/xpcshell/test_nodisable_hidden.js | 107 ++ .../xpcshell/test_onPropertyChanged_appDisabled.js | 66 + .../test/xpcshell/test_overrideblocklist.js | 200 +++ .../test/xpcshell/test_pass_symbol.js | 43 + .../test/xpcshell/test_permissions.js | 86 + .../test/xpcshell/test_permissions_prefs.js | 74 + .../test/xpcshell/test_pluginBlocklistCtp.js | 182 ++ .../test/xpcshell/test_pluginInfoURL.js | 90 + .../test/xpcshell/test_pluginchange.js | 283 +++ .../webextensions/test/xpcshell/test_plugins.js | 210 +++ .../test/xpcshell/test_pref_properties.js | 221 +++ .../test/xpcshell/test_provider_markSafe.js | 49 + .../test/xpcshell/test_provider_shutdown.js | 99 ++ .../test_provider_unsafe_access_shutdown.js | 64 + .../test_provider_unsafe_access_startup.js | 55 + .../webextensions/test/xpcshell/test_proxies.js | 240 +++ .../webextensions/test/xpcshell/test_proxy.js | 106 ++ .../webextensions/test/xpcshell/test_registry.js | 158 ++ .../webextensions/test/xpcshell/test_reload.js | 235 +++ .../webextensions/test/xpcshell/test_safemode.js | 115 ++ .../test/xpcshell/test_schema_change.js | 317 ++++ .../webextensions/test/xpcshell/test_seen.js | 211 +++ .../test/xpcshell/test_seen_newprofile.js | 41 + .../webextensions/test/xpcshell/test_shutdown.js | 85 + .../test/xpcshell/test_signed_inject.js | 382 ++++ .../test/xpcshell/test_signed_install.js | 265 +++ .../test/xpcshell/test_signed_long.js | 49 + .../test/xpcshell/test_signed_migrate.js | 194 +++ .../test/xpcshell/test_signed_multi.js | 55 + .../test/xpcshell/test_signed_updatepref.js | 136 ++ .../test/xpcshell/test_signed_verify.js | 234 +++ .../test/xpcshell/test_softblocked.js | 109 ++ .../webextensions/test/xpcshell/test_sourceURI.js | 66 + .../webextensions/test/xpcshell/test_startup.js | 932 ++++++++++ .../test/xpcshell/test_strictcompatibility.js | 203 +++ .../webextensions/test/xpcshell/test_switch_os.js | 52 + .../webextensions/test/xpcshell/test_syncGUID.js | 156 ++ .../test/xpcshell/test_system_delay_update.js | 461 +++++ .../test/xpcshell/test_system_reset.js | 418 +++++ .../test/xpcshell/test_system_update.js | 788 +++++++++ .../test/xpcshell/test_targetPlatforms.js | 146 ++ .../webextensions/test/xpcshell/test_temporary.js | 760 ++++++++ .../webextensions/test/xpcshell/test_theme.js | 1139 ++++++++++++ .../webextensions/test/xpcshell/test_types.js | 65 + .../test/xpcshell/test_undothemeuninstall.js | 423 +++++ .../test/xpcshell/test_undouninstall.js | 792 +++++++++ .../webextensions/test/xpcshell/test_uninstall.js | 216 +++ .../webextensions/test/xpcshell/test_update.js | 1398 +++++++++++++++ .../test/xpcshell/test_updateCancel.js | 138 ++ .../test/xpcshell/test_update_compatmode.js | 184 ++ .../test/xpcshell/test_update_ignorecompat.js | 107 ++ .../test/xpcshell/test_update_strictcompat.js | 1126 ++++++++++++ .../test/xpcshell/test_update_webextensions.js | 248 +++ .../test/xpcshell/test_updatecheck.js | 236 +++ .../webextensions/test/xpcshell/test_updateid.js | 86 + .../webextensions/test/xpcshell/test_upgrade.js | 206 +++ .../test/xpcshell/test_upgrade_strictcompat.js | 209 +++ .../test/xpcshell/test_webextension.js | 421 +++++ .../test/xpcshell/test_webextension_embedded.js | 306 ++++ .../test/xpcshell/test_webextension_icons.js | 169 ++ .../test/xpcshell/test_webextension_install.js | 478 +++++ .../test/xpcshell/test_webextension_paths.js | 55 + .../test/xpcshell/xpcshell-shared.ini | 334 ++++ .../test/xpcshell/xpcshell-unpack.ini | 12 + .../webextensions/test/xpcshell/xpcshell.ini | 50 + 390 files changed, 57814 insertions(+) create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/.eslintrc.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/BootstrapMonitor.jsm create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_change.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update1.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update2.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/addon_update3.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/app_update.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/blocklist_update2.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/blocklistchange/manual_update.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_block.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_empty.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_start.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/bug455906_warn.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/corrupt.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/from_sources/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/pluginInfoURL_block.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/bad2.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/empty.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/good.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/missing.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_1/test.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/bootstrap_2/test.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_1/test.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/nonbootstrap_2/test.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_cache.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_ignore.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_normal.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_compatmode_strict.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_empty.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_failed.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_AddonRepository_getAddonsByIDs.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_backgroundupdate.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_prefs_1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_blocklist_regexp_1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug299716_2.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug324121.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug393285.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug394300.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug424262.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_app.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug449027_toolkit.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug468528.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_1.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_2.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_3.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_4.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/install_5.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_1.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_2.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_3.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_4.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug470377/update_5.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_2.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_empty.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug514327_3_outdated_2.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug542391.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug554133.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug619730.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_bug655254.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_compatoverrides.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_corrupt.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_complete/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_defer/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_update_ignore/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_complete.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_defer.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_delay_updates_ignore.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_dictionary.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/dummy.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_distribution2_2/subdir/subdir2/dummy2.txt create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist2.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_AllOS.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_gfxBlacklist_OSVersion.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_1.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_2.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_hotfix_3.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_install.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_install.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_migrate4.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_no_update.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/ancient.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/new.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_overrideblocklist/old.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtp.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_pluginBlocklistCtpUndo.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_proxy/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_softblocked1.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_sourceURI.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_temporary/bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_update.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_update.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_update.xml create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_update_multi.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.json create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecheck.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_ignore.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_normal.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updatecompatmode_strict.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/test_updateid.rdf create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpi create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/head_addons.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/head_unpack.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_cache.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_AddonRepository_compatmode.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_ChromeManifestParser.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_DeferredSave.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_LightweightThemeManager.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_ProductAddonChecker.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_XPIStates.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_XPIcancel.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_addon_path_service.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_asyncBlocklistLoad.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_backgroundupdate.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bad_json.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_badschema.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_gfx.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_metadata_filters.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_prefs.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_blocklist_regexp.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_blocklistchange.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_const.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_globals.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bootstrap_resource.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug1180901_2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug299716.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug299716_2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug324121.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug335238.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug371495.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug384052.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug393285.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug394300.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug397778.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug406118.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug424262.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug425657.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug430120.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug449027.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug455906.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug465190.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug468528.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_1_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_3_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug470377_4.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_1.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug514327_3.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug521905.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug526598.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug541420.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug542391.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug554133.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug559800.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug563256.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug564030.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug566626.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug567184.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug569138.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug570173.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug576735.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug587088.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug594058.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug595081.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug595573.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug596607.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug616841.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug619730.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug620837.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug655254.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug659772.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug675371.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug740612.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug753900.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug757663.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_bug953156.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_cache_certdb.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_cacheflush.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_checkCompatibility_themeOverride.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_checkcompatibility.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_childprocess.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_compatoverrides.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_corrupt.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_corrupt_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_corruptfile.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_dataDirectory.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_default_providers_pref.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_delay_update.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_delay_update_webextension.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_dependencies.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_dictionary.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_disable.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_distribution.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_dss.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_duplicateplugins.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_e10s_restartless.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_error.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_experiment.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_ext_management.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_filepointer.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_fuel.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_general.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_getresource.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Device.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_DriverNew.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverNew.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_DriverOld.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Equal_OK.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_DriverOld.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_GTE_OK.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_No_Comparison.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OK.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OS.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_match.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_DriverVersion.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_OSVersion_mismatch_OSVersion.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Vendor.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_Version.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gfxBlacklist_prefs.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_gmpProvider.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_hasbinarycomponents.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_hotfix.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_hotfix_cert.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_install.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_install_from_sources.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_install_icons.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_install_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_isDebuggable.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_isReady.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_json_updatecheck.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_langpack.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_locale.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_locked.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_locked2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_locked_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_manifest.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_mapURIToAddonID.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_metadata_update.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate1.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate2.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate3.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate4.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate5.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrateAddonRepository.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_migrate_max_version.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_multiprocessCompatible.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_no_addons.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_nodisable_hidden.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_onPropertyChanged_appDisabled.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_overrideblocklist.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_pass_symbol.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_permissions.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_permissions_prefs.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_pluginBlocklistCtp.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_pluginInfoURL.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_pluginchange.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_plugins.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_pref_properties.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_provider_markSafe.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_provider_shutdown.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_shutdown.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_provider_unsafe_access_startup.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_proxies.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_proxy.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_registry.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_reload.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_safemode.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_schema_change.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_seen.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_seen_newprofile.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_shutdown.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_inject.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_install.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_long.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_migrate.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_multi.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_updatepref.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_signed_verify.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_softblocked.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_sourceURI.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_startup.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_strictcompatibility.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_switch_os.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_syncGUID.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_system_delay_update.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_system_reset.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_system_update.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_targetPlatforms.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_temporary.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_theme.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_types.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_undothemeuninstall.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_undouninstall.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_uninstall.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_update.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_updateCancel.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_update_compatmode.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_update_ignorecompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_update_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_update_webextensions.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_updatecheck.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_updateid.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_upgrade.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_upgrade_strictcompat.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_webextension.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_webextension_embedded.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_webextension_icons.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_webextension_install.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/test_webextension_paths.js create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/xpcshell-shared.ini create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/xpcshell-unpack.ini create mode 100644 toolkit/mozapps/webextensions/test/xpcshell/xpcshell.ini (limited to 'toolkit/mozapps/webextensions/test/xpcshell') 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft1_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft2_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft3_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft4_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft5_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_hard1_2.xpi + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_regexp1_2.xpi + + + + + + + + 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 @@ + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft1_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft2_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft3_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft4_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft5_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_hard1_3.xpi + + + + + + + + + + + + + + 3 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_regexp1_3.xpi + + + + + + + + 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 @@ + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft1_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft2_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft3_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft4_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_soft5_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_hard1_1.xpi + + + + + + + + + + + + + + 4 + + + toolkit@mozilla.org + 0 + * + http://localhost:%PORT%/addons/blocklist_regexp1_1.xpi + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/corruptfile.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/empty.xpi 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 @@ + + + + + + bootstrap1@tests.mozilla.org + 1.0 + true + + + Test Bootstrap 1 + Test Description + + chrome://foo/skin/icon.png + chrome://foo/content/about.xul + chrome://foo/content/options.xul + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + 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 @@ + + + + + + + + + + + + + + + http://test.url.com/ + + + + + + + + + + http://alt.test.url.com/ + + + + + + + + + + + + http://test.url2.com/ + + + + + + + + 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 @@ + + + \ 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 @@ + + + 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 @@ + + + + + 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 @@ + + + + + + + + + + + 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 @@ + + + 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/productaddons/unsigned.xpi 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 @@ + + + + + + test@tests.mozilla.org + 1.0 + true + + + Test Add-on + http://localhost:4444/update.rdf + + + + xpcshell@tests.mozilla.org + 2 + 5 + + + + + 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 @@ + + + + + + test@tests.mozilla.org + 2.0 + true + + + Test Add-on + http://localhost:4444/update.rdf + + + + xpcshell@tests.mozilla.org + 4 + 6 + + + + + 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_badid.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_broken.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/hotfix_good.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_hash.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_63_plain.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_hash.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_64_plain.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/long_65_hash.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_badid.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_broken.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_signed.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/multi_unsigned.xpi 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 @@ + + + + + + test@tests.mozilla.org + 1.0 + + + Test Add-on + http://localhost:4444/update.rdf + + + + xpcshell@tests.mozilla.org + 2 + 5 + + + + + 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 @@ + + + + + + test@tests.mozilla.org + 2.0 + + + Test Add-on + http://localhost:4444/update.rdf + + + + xpcshell@tests.mozilla.org + 4 + 6 + + + + + 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/preliminary_bootstrap_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_bootstrap_badid_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/signed_nonbootstrap_badid_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_bootstrap_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/signing_checks/unsigned_nonbootstrap_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_1_badcert.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system1_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system2_3.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system3_3.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system4_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system5_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_1_unpack.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_2_notBootstrap.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system6_3_notMultiprocess.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_complete_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_defer_also_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_delay_ignore_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/system_addons/system_failed_update.xpi 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 @@ + + + + + PASS + Extension + test1@tests.mozilla.org + 1.1 + + + Test Creator 1 + http://localhost:%PORT%/creator1.html + + + Preliminarily Reviewed + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + -2 + + + + -2 + http://localhost:%PORT%/test1.xpi + + + + + PASS + + Extension + test2@tests.mozilla.org + 1.2 + + + + Test Creator 2 + http://localhost:%PORT%/creator2.html + + + Test Developer 2 + http://localhost:%PORT%/developer2.html + + + <h1>Test Summary 2</h1><p>paragraph</p> + Test Description 2<br>newline + Test Developer + Comments 2 + Test EULA 2 + http://localhost:%PORT%/icon2-64.png + http://localhost:%PORT%/icon2-48.png + http://localhost:%PORT%/icon2-32.png + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + http://localhost:%PORT%/full1-2.png + http://localhost:%PORT%/thumbnail1-2.png + + + http://localhost:%PORT%/full2-2.png + http://localhost:%PORT%/thumbnail2-2.png + Caption 2 + + + NaN + + http://localhost:%PORT%/learnmore2.html + + http://localhost:%PORT%/support2.html + + http://localhost:%PORT%/contribution2.html + http://localhost:%PORT%/meetDevelopers2.html + + http://localhost:%PORT%/review2.html + NaN + NaN + NaN + Not an acual date + http://localhost:%PORT%/test2.xpi + + + + + PASS + + Theme + test3@tests.mozilla.org + 1.3 + + + + + http://localhost:%PORT%/ignore3.html + + + + Test Creator Ignore + + + + Test Creator 3 + http://localhost:%PORT%/creator3.html + + + First Test Developer 3 + http://localhost:%PORT%/developer1-3.html + + + + + + + Second Test Developer 3 + http://localhost:%PORT%/developer2-3.html + + + Test Summary 3 + Test Description 3<br><ul><li>List item 1<li>List item 2</ul> + Test Developer Comments 3 + Test EULA 3 + http://localhost:%PORT%/icon3.png + Preliminarily Reviewed + + + + unknown@tests.mozilla.org + 1 + 1 + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + http://localhost:%PORT%/full2-3.png + Caption 2 - 3 + + + + Caption ignore - 3 + + + + + Caption ignore - 3 + + http://localhost:%PORT%/full1-3.png + http://localhost:%PORT%/thumbnail1-3.png + Caption 1 - 3 + + + http://localhost:%PORT%/full3-3.png + http://localhost:%PORT%/thumbnail3-3.png + Caption 3 - 3 + + + + + 2 + + http://localhost:%PORT%/learnmore3.html + http://localhost:%PORT%/homepage3.html + http://localhost:%PORT%/support3.html + + http://localhost:%PORT%/contribution3.html + $11.11 + http://localhost:%PORT%/meetDevelopers3.html + + http://localhost:%PORT%/review3.html + 2222 + 3333 + 4444 + 2010-02-01T14:04:05Z + + http://localhost:%PORT%/fail3.xpi + + http://localhost:%PORT%/test3.xpi + + + + + Extension + test4@tests.mozilla.org + 1.4 + Test Creator 4 + Public + Add-on with undefined name should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test4.xpi + + + + + + Extension + test5@tests.mozilla.org + 1.5 + Test Creator 5 + Public + Add-on with empty name should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test5.xpi + + + + + FAIL + test6@tests.mozilla.org + 1.6 + Test Creator 6 + Public + Add-on with undefined type should be ignored + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test6.xpi + + + + + FAIL + Empty id attribute + test7@tests.mozilla.org + 1.7 + Test Creator 7 + Public + Add-on with empty type should be ignored + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test7.xpi + + + + + FAIL + Unknown + test8@tests.mozilla.org + 1.8 + Test Creator 8 + Public + Add-on with unknown type should be ignored + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test8.xpi + + + + + FAIL + Extension + 1.9 + Test Creator 9 + Public + Add-on with undefined guid should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test9.xpi + + + + + FAIL + Extension + + 1.10 + Test Creator 10 + Public + Add-on with empty guid should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test10.xpi + + + + + FAIL + Extension + test1@tests.mozilla.org + 1.11 + Test Creator 11 + Public + Add-on with a guid that matches a previously successful result should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test11.xpi + + + + + FAIL + Extension + test_AddonRepository_1@tests.mozilla.org + 1.12 + Test Creator 12 + Public + Add-on with a guid that matches an installed Addon should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test12.xpi + + + + + FAIL + Extension + test13@tests.mozilla.org + Test Creator 13 + Public + Add-on with undefined version should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test13.xpi + + + + + FAIL + Extension + test14@tests.mozilla.org + + Test Creator 14 + Public + Add-on with empty version should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test14.xpi + + + + + FAIL + Extension + test15@tests.mozilla.org + 1.15 + Public + Add-on with undefined authors should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test15.xpi + + + + + FAIL + Extension + test16@tests.mozilla.org + 1.16 + + Public + Add-on with no defined author elements should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test16.xpi + + + + + FAIL + Extension + test17@tests.mozilla.org + 1.17 + + + + + Public + Add-on with no non-empty author elements should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test17.xpi + + + + + FAIL + Extension + test18@tests.mozilla.org + 1.18 + Test Creator 18 + Add-on with undefined status should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test18.xpi + + + + + FAIL + Extension + test19@tests.mozilla.org + 1.19 + Test Creator 19 + Unknown + Add-on with non-Public status should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test19.xpi + + + + + FAIL + Extension + test20@tests.mozilla.org + 1.20 + Test Creator 20 + Public + Add-on with undefined compatible_applications should be ignored. + http://localhost:%PORT%/test20.xpi + + + + + FAIL + Extension + test21@tests.mozilla.org + 1.21 + Test Creator 21 + Public + Add-on with no compatible applications should be ignored. + + + unknown@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test21.xpi + + + + + FAIL + Extension + test22@tests.mozilla.org + 1.22 + Test Creator 22 + Public + Add-on with too high of a compatible application min version should be ignored. + + + xpcshell@tests.mozilla.org + 2.0 + + + http://localhost:%PORT%/test22.xpi + + + + + FAIL + Extension + test23@tests.mozilla.org + 1.23 + Test Creator 23 + Public + Add-on with too high of a compatible application min version should be ignored. + + + xpcshell@tests.mozilla.org + 1.1 + 2.0 + + + http://localhost:%PORT%/test23.xpi + + + + + FAIL + Extension + test24@tests.mozilla.org + 1.24 + Test Creator 24 + Public + Add-on with too low of a compatible application max version should be ignored. + + + xpcshell@tests.mozilla.org + 0.9 + + + http://localhost:%PORT%/test24.xpi + + + + + FAIL + Extension + test25@tests.mozilla.org + 1.25 + Test Creator 25 + Public + Add-on with too low of a compatible application max version should be ignored. + + + xpcshell@tests.mozilla.org + 0.9 + 0.9.9 + + + http://localhost:%PORT%/test25.xpi + + + + + FAIL + Extension + test26@tests.mozilla.org + 1.26 + Test Creator 26 + Public + Add-on with undefined XPI URL should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + + FAIL + Extension + test27@tests.mozilla.org + 1.27 + Test Creator 27 + Public + Add-on with an empty XPI URL should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + + + FAIL + Extension + test28@tests.mozilla.org + 1.28 + Test Creator 28 + Public + Add-on with no installs with compatible OS should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test28.xpi + http://localhost:%PORT%/test28.xpi + + + + + FAIL + Extension + test29@tests.mozilla.org + 1.29 + Test Creator 29 + Public + Add-on with an XPI URL that matches an installing AddonInstall should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/addons/test_AddonRepository_2.xpi + + + + + PASS + Extension + purchase1@tests.mozilla.org + 2.0 + + + Test Creator - Last Passing + http://localhost:%PORT%/creatorLastPassing.html + + + Public + + ALL + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + 5 + + http://localhost:%PORT%/purchaseURL1 + $5 + + + + + + PASS + Extension + purchase2@tests.mozilla.org + 2.0 + + + Test Creator - Last Passing + http://localhost:%PORT%/creatorLastPassing.html + + + Public + + XPCShell + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + 5 + + http://localhost:%PORT%/purchaseURL2 + $10 + + + + + + FAIL + Extension + purchase3@tests.mozilla.org + 2.0 + + + Test Creator - Last Passing + http://localhost:%PORT%/creatorLastPassing.html + + + Public + + FOO + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + 5 + + http://localhost:%PORT%/purchaseURL3 + $10 + + + + + + PASS + Extension + test-lastPassing@tests.mozilla.org + 2.0 + + + Test Creator - Last Passing + http://localhost:%PORT%/creatorLastPassing.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + 10 + http://localhost:%PORT%/addons/test_AddonRepository_3.xpi + + + + + FAIL + Extension + test-surpassesLimit@tests.mozilla.org + 9.9 + Test Creator - Surpasses Limit + Public + Add-on should not be added because doing so would surpass MAX_RESULTS limit + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test-surpassesLimit.xpi + + + 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 @@ + + + + Repo Add-on 1 + Extension + test_AddonRepository_1@tests.mozilla.org + 2.1 + + + Repo Add-on 1 - Creator + http://localhost:4444/repo/1/creator.html + + + Repo Add-on 1 - First Developer + http://localhost:4444/repo/1/firstDeveloper.html + + + Repo Add-on 1 - Second Developer + http://localhost:4444/repo/1/secondDeveloper.html + + + Repo Add-on 1 - Description<br>Second line + <p>Repo Add-on 1 - Full Description &amp; some extra</p> + Repo Add-on 1 - EULA + Repo Add-on 1 + Developer Comments + http://localhost/repo/1/icon.png + Public + 1 + http://localhost/repo/1/learnmore.html + http://localhost/repo/1/homepage.html + http://localhost/repo/1/support.html + + http://localhost/repo/1/contribution.html + $11.11 + http://localhost/repo/1/meetDevelopers.html + + http://localhost/repo/1/review.html + 2221 + 3331 + 4441 + 1970-01-01T00:00:09Z + http://localhost:4444/repo/1/install.xpi + + + + Repo Add-on 2 + Theme + test_AddonRepository_2@tests.mozilla.org + 2.2 + + + Repo Add-on 2 - Creator + http://localhost:4444/repo/2/creator.html + + + Repo Add-on 2 - First Developer + http://localhost:4444/repo/2/firstDeveloper.html + + + Repo Add-on 2 - Second Developer + http://localhost:4444/repo/2/secondDeveloper.html + + + Repo Add-on 2 - Description + Repo Add-on 2 - Full Description + Repo Add-on 2 - EULA + Repo Add-on 2 - Developer Comments + http://localhost/repo/2/icon.png + Unknown + + + http://localhost:4444/repo/2/firstFull.png + http://localhost:4444/repo/2/firstThumbnail.png + Repo Add-on 2 - First Caption + + + http://localhost:4444/repo/2/secondFull.png + http://localhost:4444/repo/2/secondThumbnail.png + Repo Add-on 2 - Second Caption + + + 2 + http://localhost/repo/2/learnmore.html + http://localhost/repo/2/homepage.html + http://localhost/repo/2/support.html + + http://localhost/repo/2/contribution.html + http://localhost/repo/2/meetDevelopers.html + + http://localhost/repo/2/review.html + 2222 + 3332 + 4442 + 1970-01-01T00:00:09Z + http://localhost:4444/repo/2/install.xpi + + + + Repo Add-on 3 + Theme + test_AddonRepository_3@tests.mozilla.org + 2.3 + http://localhost/repo/3/icon.png + + + http://localhost:4444/repo/3/firstFull.png + http://localhost:4444/repo/3/firstThumbnail.png + Repo Add-on 3 - First Caption + + + http://localhost:4444/repo/3/secondFull.png + http://localhost:4444/repo/3/secondThumbnail.png + Repo Add-on 3 - Second Caption + + + + + + test_AddonRepository_1@tests.mozilla.org + PASS + + + + 0.1 + 0.2 + + + XPCShell + 666 + 3.0 + 4.0 + xpcshell@tests.mozilla.org + + + + + + 0.2 + 0.3 + + + XPCShell + 666 + 5.0 + 6.0 + xpcshell@tests.mozilla.org + + + + + + 9 + 10 + + + XPCShell + 666 + 10.0 + 11.0 + xpcshell@tests.mozilla.org + + + + + + 0.2 + 0.3 + + + Unknown App + 123 + 1.0 + 999.0 + unknown-app@tests.mozilla.org + + + + + + + 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 @@ + + + + Test Repo Add-on - ignore + Extension + compatmode-ignore@tests.mozilla.org + 1.1 + + + Test Creator 1 + http://localhost:%PORT%/creator1.html + + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test1.xpi + + 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 @@ + + + + Test Repo Add-on - normal + Extension + compatmode-normal@tests.mozilla.org + 1.1 + + + Test Creator 1 + http://localhost:%PORT%/creator1.html + + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test1.xpi + + 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 @@ + + + + Test Repo Add-on - strict + Extension + compatmode-strict@tests.mozilla.org + 1.1 + + + Test Creator 1 + http://localhost:%PORT%/creator1.html + + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test1.xpi + + 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 @@ + + + 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 @@ + + + + + PASS + Extension + test1@tests.mozilla.org + 1.1 + Test Creator 1 + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test1.xpi + + + 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 @@ + + + + + PASS + Extension + test1@tests.mozilla.org + 1.1 + + + Test Creator 1 + http://localhost:%PORT%/creator1.html + + + Test Developer 1 + http://localhost:%PORT%/developer1.html + + + Test Summary 1 + Test Description 1 + Test EULA 1 + Test Developer Comments 1 + http://localhost:%PORT%/icon1.png + Preliminarily Reviewed + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + http://localhost:%PORT%/full1-1.png + + + http://localhost:%PORT%/thumbnail1-1.png + + Caption 1 - 1 + + + http://localhost:%PORT%/full2-1.png + http://localhost:%PORT%/thumbnail2-1.png + Caption 2 - 1 + + + 4 + http://localhost:%PORT%/learnmore1.html + http://localhost:%PORT%/support1.html + + http://localhost:%PORT%/contribution1.html + $11.11 + http://localhost:%PORT%/meetDevelopers1.html + + http://localhost:%PORT%/review1.html + 2222 + 3333 + 4444 + 2010-02-01T14:04:05Z + http://localhost:%PORT%/addons/test_AddonRepository_2.xpi + + + + test1@tests.mozilla.org + PASS + + + + 0.1 + 0.2 + + + XPCShell + 666 + 3.0 + 4.0 + xpcshell@tests.mozilla.org + + + + + + 0.2 + 0.3 + + + XPCShell + 666 + 5.0 + 6.0 + xpcshell@tests.mozilla.org + + + + + + 9 + 10 + + + XPCShell + 666 + 10.0 + 11.0 + xpcshell@tests.mozilla.org + + + + + + 0.2 + 0.3 + + + Unknown App + 123 + 1.0 + 999.0 + unknown-app@tests.mozilla.org + + + + + + + + + FAIL + Extension + test1@tests.mozilla.org + 1.2 + Test Creator 2 + Public + Add-on with a guid that matches a previously successful result should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test2.xpi + + + + + FAIL + Extension + notRequested@tests.mozilla.org + 1.3 + Test Creator 3 + Public + Add-on with a guid that wasn't requested should be ignored. + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test3.xpi + + + + + PASS + Theme + test_AddonRepository_1@tests.mozilla.org + 1.4 + Unknown + + + unknown@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test4.xpi + http://localhost:%PORT%/test4.xpi + + + 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 @@ + + + + + + + + + + + 2 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/broken.xpi + + + + + + + + + + + + + + + 2 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/broken.xpi + + + + + + + + + + + + + + + 2 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/broken.xpi + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + test.blocklist.pref1 + test.blocklist.pref2 + + + + + + + + + + test.blocklist.pref3 + test.blocklist.pref4 + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + 0.2"> + + xpcshell@tests.mozilla.org + 5 + 5 + "> + + toolkit@mozilla.org + 1.9 + 1.9 + "> + + 30 + 30 + "> + + xpcshell@tests.mozilla.org + &invalidRange; + "> + + toolkit@mozilla.org + &invalidRange; + "> +]> + + + + + + + + + + + + &v0.2; + + + &xpcshell.app; + + + + + + + + + + + + + + + &v0.2; + + + &toolkit.app; + + + + + + + + + + + + + + + &v0.2; + + + &xpcshell.app; + + + + + &toolkit.app; + + + + + + + + + + + + + + + &v0.2; + + + &xpcshell.app; + + + + + &toolkit.invalid; + + + + + + + + + + + + + + + &v0.2; + + + &xpcshell.invalid; + + + + + &toolkit.app; + + + + + + + + + + + + + + + &v0.2; + + + &xpcshell.invalid; + + + + + &toolkit.invalid; + + + + + + + + + + + + + + + &v0.2; + + + &toolkit.invalid; + + + + 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 @@ + + + + + + + + + 0.1 + + + toolkit@mozilla.org + 1.9 + 2.0.* + + + + + + + + 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 @@ + + + + + + + + + + + 2 + + + xpcshell@tests.mozilla.org + 3 + 3 + http://localhost:4444/broken.xpi + + + + + + + + + + + + + + + 2 + + + xpcshell@tests.mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 3 + 3 + http://localhost:4444/broken.xpi + + + + + + + + + + + + + + + 2 + + + toolkit@mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + 20 + + + xpcshell@tests.mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + 10 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + + + + + + + + 6 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + + + + + + + + 40 + + + xpcshell@tests.mozilla.org + http://localhost:4444/broken.xpi + + + + + + + + 30 + + + xpcshell@tests.mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + + + + + + + + 20 + + + toolkit@mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + 10 + + + toolkit@mozilla.org + 1.9 + 1.9 + http://localhost:4444/broken.xpi + + + + + + + + 6 + + + toolkit@mozilla.org + 1.9 + 1.9 + http://localhost:4444/broken.xpi + + + + + + + + 40 + + + toolkit@mozilla.org + http://localhost:4444/broken.xpi + + + + + + + + 30 + + + toolkit@mozilla.org + 2 + 2 + http://localhost:4444/broken.xpi + + + + + + + + + + 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 @@ + + + + TEST + Extension + test1@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + -5 + Extension + test2@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 0 + Extension + test3@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 2 + Extension + test4@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 4 + Extension + test5@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 5 + Extension + test6@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 10 + Extension + test7@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + TEST + 100 + Extension + test8@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + 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 @@ + + + + + bug470377_1@tests.mozilla.org + 1 + + + unknown@tests.mozilla.org + 1 + 1 + + + Test for Bug 470377 + + 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 @@ + + + + + bug470377_2@tests.mozilla.org + 1 + + + toolkit@mozilla.org + 1 + 1 + + + Test for Bug 470377 + + 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 @@ + + + + + bug470377_3@tests.mozilla.org + 1 + + + xpcshell@tests.mozilla.org + 1 + 1 + + + Test for Bug 470377 + + 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 @@ + + + + + bug470377_4@tests.mozilla.org + 1 + + + toolkit@mozilla.org + 1 + 2 + + + Test for Bug 470377 + + 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 @@ + + + + + bug470377_5@tests.mozilla.org + 1 + + + xpcshell@tests.mozilla.org + 1 + 3 + + + Test for Bug 470377 + + 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 @@ + + + + + + + + + + 1 + + + unknown@tests.mozilla.org + 1 + 2 + http://localhost:%PORT%/broken.xpi + + + + + + + + + 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 @@ + + + + + + + + + + 1 + + + toolkit@mozilla.org + 1 + 1 + http://localhost:%PORT%/broken.xpi + + + + + + + + + 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 @@ + + + + + + + + + + 1 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/broken.xpi + + + + + + + + + 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 @@ + + + + + + + + + + 1 + + + toolkit@mozilla.org + 1 + 2 + http://localhost:%PORT%/broken.xpi + + + + + + + + + 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 @@ + + + + + + + + + + 1 + + + xpcshell@tests.mozilla.org + 1 + 3 + http://localhost:%PORT%/broken.xpi + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + 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 @@ + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_1.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug526598_2.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/test_bug541420.xpi 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 @@ + + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 1 + 3 + + + +
  • +
    +
    +
    + +
    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 @@ + + + + PASS + Extension + test1@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + FAIL + Extension + test2@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + Should not return an incompatible add-on + + + xpcshell@tests.mozilla.org + 2 + 2 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test3@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + FAIL + Extension + test4@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + Should not return an add-on for a different OS + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test5@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + FAIL + Extension + test5@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + Should not include the same result twice + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test6@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test7@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test8@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test9@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test10@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test11@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + + PASS + Extension + test12@tests.mozilla.org + 1.0 + + + Test Creator + http://localhost:%PORT%/creator.html + + + Public + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://localhost:%PORT%/test.xpi + + + 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 @@ + + + + + + + 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 @@ + + + + + + + + + + 1 + + + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + + + + 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 @@ + + + + Test addon 2 + Extension + addon2@tests.mozilla.org + 1.0 + + + + Test addon 3 + Extension + addon3@tests.mozilla.org + 1.0 + + + Test addon 3 + addon3@tests.mozilla.org + + + 0.9 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + + Test addon 4 + Extension + addon4@tests.mozilla.org + 1.0 + + + Test addon 4 + addon4@tests.mozilla.org + + + 0.9 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + + Test addon 5 + Extension + addon5@tests.mozilla.org + 1.0 + + + Test addon 5 + addon5@tests.mozilla.org + + + 0.9 + 1.0 + + + Unknown App + unknown-app@tests.mozilla.org + 1 + 2 + + + + + + + + Test addon 6 + Extension + addon6@tests.mozilla.org + 1.0 + + + Test addon 6 + addon6@tests.mozilla.org + + + 0.5 + 0.9 + + + XPCShell + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + + Test addon 7 + Extension + addon7@tests.mozilla.org + 1.0 + + + Test addon 7 + addon7@tests.mozilla.org + + + 0.5 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 0.1 + 0.9 + + + + + + + + Test addon 8 + Extension + addon8@tests.mozilla.org + 1.0 + + + Test addon 8 + addon8@tests.mozilla.org + + + 6 + 6.2 + + + XPCShell + xpcshell@tests.mozilla.org + 0.9 + 9 + + + + + 0.5 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 0.1 + 9 + + + Unknown app + unknown-app@tests.mozilla.org + 0.1 + 9 + + + + + 0.1 + 0.2 + + + XPCShell + xpcshell@tests.mozilla.org + 0.1 + 0.9 + + + + + + + + Test addon 9 + addon9@tests.mozilla.org + + + 0.5 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + + Test addon 10 + addon10@tests.mozilla.org + + + 0.5 + 1.0 + + + XPCShell + xpcshell@tests.mozilla.org + 1 + 2 + + + + + + + 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 @@ + + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_delay_update_complete_v2.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_delay_update_defer_v2.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_delay_update_ignore_v2.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_dictionary_3.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_dictionary_4.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_dictionary_5.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + addon2@tests.mozilla.org + 2.0 + + + Distributed add-ons test + true + + + + xpcshell@tests.mozilla.org + 1 + 5 + + + + + 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 @@ + + + + + + WINNT 6.1 + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + WINNT 6.0 + 0xdcba + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + WINNT 6.1 + 0xabab + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + GREATER_THAN_OR_EQUAL + + + WINNT 6.1 + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + 8.52.322.1111 + EQUAL + + + Darwin 13 + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + Linux + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + Android + abcd + + wxyz + asdf + erty + + DIRECT2D + BLOCKED_DRIVER_VERSION + 5 + LESS_THAN_OR_EQUAL + + + Android + dcdc + + uiop + vbnm + hjkl + + DIRECT2D + BLOCKED_DRIVER_VERSION + 5 + EQUAL + + + Android + abab + + ghjk + cvbn + + DIRECT2D + BLOCKED_DRIVER_VERSION + 7 + GREATER_THAN_OR_EQUAL + + + WINNT 6.1 + 0xabcd + + 0x6666 + + DIRECT2D + BLOCKED_DEVICE + + + Darwin 13 + 0xabcd + + 0x6666 + + DIRECT2D + BLOCKED_DEVICE + + + Linux + 0xabcd + + 0x6666 + + DIRECT2D + BLOCKED_DEVICE + + + Android + 0xabcd + + 0x6666 + + DIRECT2D + BLOCKED_DEVICE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_LAYERS + BLOCKED_DRIVER_VERSION + 8.52.322.1112 + 8.52.323.1000 + BETWEEN_EXCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + 8.50.322.1000 + 8.52.322.1112 + BETWEEN_EXCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_ANGLE + BLOCKED_DRIVER_VERSION + 8.52.322.1000 + 9.52.322.1000 + BETWEEN_EXCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + HARDWARE_VIDEO_DECODING + BLOCKED_DRIVER_VERSION + 7.82.322.1000 + 9.25.322.1001 + BETWEEN_INCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION + BLOCKED_DRIVER_VERSION + 8.52.322.1112 + 9.52.322.1300 + BETWEEN_INCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + 8.52.322.1000 + 8.52.322.1112 + BETWEEN_INCLUSIVE + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + 8.52.322.1112 + 8.52.322.1200 + BETWEEN_INCLUSIVE_START + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_MSAA + BLOCKED_DRIVER_VERSION + 8.52.322.1000 + 8.52.322.1200 + BETWEEN_INCLUSIVE_START + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_ANGLE + BLOCKED_DRIVER_VERSION + 8.52.322.1000 + 8.52.322.1112 + BETWEEN_INCLUSIVE_START + + + + All + 0xdcdc + + 0x2783 + 0x1234 + 0x2782 + + CANVAS2D_ACCELERATION + BLOCKED_DRIVER_VERSION + 8.52.322.1000 + 9.52.322.1000 + BETWEEN_EXCLUSIVE + + + + 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 @@ + + + + + + WINNT 6.1 + 0xabcd + + 0x2783 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + WINNT 6.0 + 0xdcba + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + 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 @@ + + + + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_LAYERS + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_1_LAYERS + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_OPENGL + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_ANGLE + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_MSAA + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + STAGEFRIGHT + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_LAYERS + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + HARDWARE_VIDEO_DECODING + BLOCKED_DRIVER_VERSION + + + + All + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_ANGLE + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_LAYERS + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_1_LAYERS + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_OPENGL + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_ANGLE + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_MSAA + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + STAGEFRIGHT + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_LAYERS + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + HARDWARE_VIDEO_DECODING + BLOCKED_DRIVER_VERSION + + + + Darwin 13 + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_ANGLE + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_LAYERS + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_1_LAYERS + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_OPENGL + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_ANGLE + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_MSAA + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + STAGEFRIGHT + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_LAYERS + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + HARDWARE_VIDEO_DECODING + BLOCKED_DRIVER_VERSION + + + + Linux + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_ANGLE + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_9_LAYERS + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_LAYERS + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_10_1_LAYERS + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_OPENGL + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_ANGLE + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBGL_MSAA + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + STAGEFRIGHT + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_ENCODE + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + WEBRTC_HW_ACCELERATION_DECODE + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_LAYERS + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + HARDWARE_VIDEO_DECODING + BLOCKED_DRIVER_VERSION + + + + Android + 0xabcd + + + 0x2783 + 0x1234 + 0x2782 + + DIRECT3D_11_ANGLE + BLOCKED_DRIVER_VERSION + + + + 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 @@ + + + + + + WINNT 6.2 + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + DIRECT2D + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + Darwin 13 + 0xabcd + + 0x2783 + 0x1234 + 0x2782 + + OPENGL_LAYERS + BLOCKED_DRIVER_VERSION + 8.52.322.2202 + LESS_THAN + + + 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 @@ + + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_hotfix_1.xpi + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_hotfix_2.xpi + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + +
  • + + 3.0 + + + xpcshell@tests.mozilla.org + 2 + 2 + http://localhost:%PORT%/addons/test_hotfix_3.xpi + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0 + 1 + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0 + 1 + + + +
  • +
    +
    +
    + + + + +
  • + + 5.0 + + + xpcshell@tests.mozilla.org + 0 + 1 + + + +
  • +
    +
    +
    + +
    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 @@ + + + + Real Test 2 + Extension + addon2@tests.mozilla.org + 1.0 + + + Test Creator + http://example.com/creator.html + + + Public + Repository summary + Repository description + + + Firefox + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 0 + * + + + SeaMonkey + {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} + 0 + * + + + ALL + http://example.com/browser/toolkit/mozapps/extensions/test/browser/addons/browser_install1_2.xpi + + + + addon6@tests.mozilla.org + Addon Test 6 + + + 1.0 + 1.0 + + + XPCShell + 1.0 + 1.0 + xpcshell@tests.mozilla.org + + + + + + 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 @@ + + + + + + + + + + + + + + + + 2 + + + + 2 + + + + 2 + + + + 2 + + + + 2 + + + + 2 + + + + 4 + + + + 4 + + + + + + + + + + + + + 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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 0 + 2 + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 0 + 2 + http://localhost:%PORT%/addons/test_migrate4_6.xpi + http://example.com/updateInfo.xhtml + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + + 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 @@ + + + + + + + + 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 @@ + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + 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 @@ + + + + + + + + + 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 @@ + + + + + Test + Extension + addon@tests.mozilla.org + 1 + + + xpcshell@tests.mozilla.org + 1 + 1 + + + http://www.example.com/testaddon.xpi + + 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 @@ + + + + + + + +
  • + + 1.0 + + + + xpcshell@tests.mozilla.org + 1 + 1 + + + + + + + xpcshell@tests.mozilla.org + 2 + 2 + + + +
  • + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_update.xpi + http://example.com/updateInfo.xhtml + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0 + 1 + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 3 + 3 + + + +
  • +
    +
    +
    + + + + +
  • + + 5.0 + + + xpcshell@tests.mozilla.org + 0 + 0 + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0 + 1 + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_update8.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_update9_2.xpi + + + +
  • + +
  • + + 3.0 + + + xpcshell@tests.mozilla.org + 0.9 + 0.9 + http://localhost:%PORT%/addons/test_update9_3.xpi + + + +
  • + +
  • + + 4.0 + + + xpcshell@tests.mozilla.org + 0.9 + 0.9 + http://localhost:%PORT%/addons/test_update9_4.xpi + + + +
  • + +
  • + + 5.0 + + + xpcshell@tests.mozilla.org + 5 + 6 + http://localhost:%PORT%/addons/test_update9_5.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0.1 + 0.4 + http://localhost:%PORT%/addons/test_update10.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 0.1 + 0.2 + true + http://localhost:%PORT%/addons/test_update11.xpi + + + +
  • +
    +
    +
    + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:%PORT%/addons/test_update12.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + Test Addon 9 + Extension + addon9@tests.mozilla.org + + + addon9@tests.mozilla.org + Test Addon 9 + + + 4 + 4 + + + XPCShell + 1 + 1 + xpcshell@tests.mozilla.org + + + + + + 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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/addons/test_update_multi2.xpi + + + +
  • +
    +
    +
    + +
    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 @@ + + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + https://localhost:4444/addons/test1.xpi + + + +
  • + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 2 + 2 + https://localhost:4444/addons/test2.xpi + + + +
  • +
  • + + 3.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + https://localhost:4444/addons/test3.xpi + + + +
  • +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + https://localhost:4444/addons/test2.xpi + + + +
  • + +
  • + + 4.0 + + + xpcshell@tests.mozilla.org + 2 + 2 + https://localhost:4444/addons/test4.xpi + + + +
  • +
    +
    +
    + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + + + + + + + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + http://localhost:4444/broken.xpi + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMO1O2gwSCCth1GwYMgscfaNakpN40PJfOWt + ub2HVdg8+OXMciF8d/9eVWm8eH/IxuxyZlmRZTs3O5tv9eWAY5uBCtqDf1WgTsGk + jrgZow1fITkZI7w0//C8eKdMLAtGueGfNs2IlTd5P/0KH/hf1rPc1wUqEqKCd4+L + BcVq13ad + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBAMH/33P/bn148mVkAB8i5X8c4LhY52E+MPUT + yKHGpowZnRLgL2O0dfpm+rljOBfKi51322PFrsc6VIFml6x4Lrb5foxSyB0Vs9pb + SEDFWUKquOQvceQ9iEx5Pe0VzrmUZgcQxd8ksSunWL4wJaBZ/evE5amFC6sw3pv/ + fjt8p3GN + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6 + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJ5Dv3Zd7/j5dLchCw9iO/cxPq8oOhOYD2M+ + jUKvmHCyTBRIEaJrE4N7yVbRYk++ERIfyVepLivsVi4pBmF7JTdw0NaKUA0LiOoT + mRL8I7s5NPjCiiNcdqbncWyiZwIj1w1nkbWGTlH/gEjRW/LbvT4JAuec8yNFDa4S + X8mOMf7k + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + https://localhost:4444/broken.xpi + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBAGvf7XqqoTl5WofrNq55E7W+UttOEDXLB3Oi + XDiXe0i6njlozilseaUo1hgfQhhzN9gkyetP5tGBVcLRrVyliKpJmD6ABCVGW1lS + qS+SEw7gDHyHkvwKMyWKedpRGChqLYnnf+Y+CX3MWLZLkwPXMKdTYgN3Rx0lEnJk + 37LSEMKE + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + https://localhost:4444/broken.xpi + sha1:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6 + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBACMX/KReOGSJ8CMGRroH1v3Gjv/Qs/pqH+Ow + o+hCKWLUKx7hpJgVJkXXdAHW0U88NXlp1S2H0WqA7I/CdmNXJSPzzV/J4z1dZgXh + JbW6mqNb0pj6nIe7g8OLzSxDgBmO4DUP5DAmnmqciJLWQzN7OdbcwrWz6xPN5kZF + A90eF5zy + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/broken.xpi + md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6 + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBAJRfcFvHIWxVyycCw8IjNmEhabc2uqA1zQwp + 5oKh3Y23hwIsQ6xy68Wtjte1NEYFRt5fWkbMXj9YQj6LpVbzBKiGATcrq6MycZKK + o5N22cWbrKKRweJezTyN4eLfQg21pG7r8mdfS0bIA28ZVFtQOmORejoUesEouCGy + eKYk9nS2 + + + + + + + + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + https://localhost:4444/broken.xpi + md2:78fc1d2887eda35b4ad2e3a0b60120ca271ce6e6 + + + + + + + MIGTMA0GCSqGSIb3DQEBBQUAA4GBALQKwzLFr/VOw3gJvv/LCh3/PWDd9FqmFnX+ + hJjBmCaUDtG7CXn1i0h8ed8IeRHpLLT7FCzVwU3bH9BUjdm8wc3ObtlNbd8go01a + CoXz50r3rYPcYz4WS+7/+lvrUqsuWd9Wj+q0NeCPiNaaro6/AolE2Qf5JFRL3lxY + lsKWAnVO + + + + + + + Foo + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0.1 + 0.2 + https://localhost:4444/addons/test1.xpi + + + +
  • +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 0.5 + 0.6 + https://localhost:4444/addons/test2.xpi + + + +
  • + +
  • + + 3.0 + + + xpcshell@tests.mozilla.org + 2 + 3 + https://localhost:4444/addons/test3.xpi + + + +
  • +
    +
    +
    + + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0.1 + 0.2 + https://localhost:4444/addons/test1.xpi + + + +
  • + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 0.5 + 0.6 + https://localhost:4444/addons/test2.xpi + + + +
  • + +
  • + + 3.0 + + + xpcshell@tests.mozilla.org + 2 + 3 + https://localhost:4444/addons/test3.xpi + + + +
  • +
    +
    +
    + + + + + +
  • + + 1.0 + + + xpcshell@tests.mozilla.org + 0.1 + 0.2 + true + https://localhost:4444/addons/test1.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + https://localhost:%PORT%/addons/test1.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + https://localhost:%PORT%/addons/test1.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 2 + https://localhost:%PORT%/addons/test1.xpi + + + +
  • +
    +
    +
    +
    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 @@ + + + + + + + +
  • + + 2.0 + + + xpcshell@tests.mozilla.org + 1 + 1 + http://localhost:4444/addons/test_updateid2.xpi + + + +
  • +
    +
    +
    + +
    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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/unsigned.xpi 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 Binary files /dev/null and b/toolkit/mozapps/webextensions/test/xpcshell/data/webext-implicit-id.xpi 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 = `\n\n\n`; + if (addons) { + xml += ` \n`; + for (let addon of addons) { + xml += ` \n`; + } + xml += ` \n`; + } + xml += `\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 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 = "" + + "" + + " " + + " " + + " 0x2,582" + + " 0x2782" + + " " + + " " + + "" + + ""; + 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 = "" + + "" + + " " + + " " + + " " + + "" + + ""; + 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 = "" + + "" + + " " + + " " + + " " + + "" + + ""; + 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 = "" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "" + + ""; + 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 = "" + + "" + + " " + + " 0x10de " + + " " + + " " + + " DIRECT3D_9_LAYERS " + + " " + + "" + + ""; + 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 = "\n" + + "\n" + + "\n" + + " \n" + + " " + addon.id + "\n" + + " " + addon.version + "\n" + + " \n" + + " \n" + + " xpcshell@tests.mozilla.org\n" + + " 3\n" + + " 3\n" + + " \n" + + " \n" + + " " + addon.name + "\n" + + " \n" + + "\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 = "\n" + + "\n" + + "\n" + + " \n" + + " " + addon.id + "\n" + + " " + addon.version + "\n" + + " \n" + + " \n" + + " xpcshell@tests.mozilla.org\n" + + " " + addon.appVersion + "\n" + + " " + addon.appVersion + "\n" + + " \n" + + " \n" + + " " + addon.name + "\n" + + " \n" + + "\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 + // 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] -- cgit v1.2.3