| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <map> | 5 #include <map> |
| 6 | 6 |
| 7 #include "base/compiler_specific.h" | 7 #include "base/compiler_specific.h" |
| 8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/memory/weak_ptr.h" |
| 10 #include "base/stl_util-inl.h" | 11 #include "base/stl_util-inl.h" |
| 11 #include "base/string_number_conversions.h" | 12 #include "base/string_number_conversions.h" |
| 12 #include "base/string_split.h" | 13 #include "base/string_split.h" |
| 13 #include "base/string_util.h" | 14 #include "base/string_util.h" |
| 14 #include "base/stringprintf.h" | 15 #include "base/stringprintf.h" |
| 15 #include "base/threading/thread.h" | 16 #include "base/threading/thread.h" |
| 16 #include "base/version.h" | 17 #include "base/version.h" |
| 18 #include "chrome/browser/extensions/crx_installer.h" |
| 17 #include "chrome/browser/extensions/extension_error_reporter.h" | 19 #include "chrome/browser/extensions/extension_error_reporter.h" |
| 18 #include "chrome/browser/extensions/extension_sync_data.h" | 20 #include "chrome/browser/extensions/extension_sync_data.h" |
| 19 #include "chrome/browser/extensions/extension_updater.h" | 21 #include "chrome/browser/extensions/extension_updater.h" |
| 20 #include "chrome/browser/extensions/test_extension_prefs.h" | 22 #include "chrome/browser/extensions/test_extension_prefs.h" |
| 21 #include "chrome/browser/extensions/test_extension_service.h" | 23 #include "chrome/browser/extensions/test_extension_service.h" |
| 22 #include "chrome/browser/prefs/pref_service.h" | 24 #include "chrome/browser/prefs/pref_service.h" |
| 23 #include "chrome/common/extensions/extension.h" | 25 #include "chrome/common/extensions/extension.h" |
| 24 #include "chrome/common/extensions/extension_constants.h" | 26 #include "chrome/common/extensions/extension_constants.h" |
| 25 #include "chrome/common/net/test_url_fetcher_factory.h" | 27 #include "chrome/common/net/test_url_fetcher_factory.h" |
| 26 #include "chrome/common/pref_names.h" | 28 #include "chrome/common/pref_names.h" |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 169 void set_extensions(ExtensionList extensions) { | 171 void set_extensions(ExtensionList extensions) { |
| 170 extensions_ = extensions; | 172 extensions_ = extensions; |
| 171 } | 173 } |
| 172 | 174 |
| 173 private: | 175 private: |
| 174 ExtensionList extensions_; | 176 ExtensionList extensions_; |
| 175 }; | 177 }; |
| 176 | 178 |
| 177 class ServiceForDownloadTests : public MockService { | 179 class ServiceForDownloadTests : public MockService { |
| 178 public: | 180 public: |
| 179 virtual void UpdateExtension(const std::string& id, | 181 ServiceForDownloadTests() |
| 180 const FilePath& extension_path, | 182 : MockService() { |
| 181 const GURL& download_url) { | 183 } |
| 184 |
| 185 // Add a fake crx installer to be returned by a call to UpdateExtension() |
| 186 // with a specific ID. Caller keeps ownership of |crx_installer|. |
| 187 void AddFakeCrxInstaller(std::string& id, |
| 188 CrxInstaller* crx_installer) { |
| 189 fake_crx_installers_[id] = crx_installer; |
| 190 } |
| 191 |
| 192 bool UpdateExtension( |
| 193 const std::string& id, |
| 194 const FilePath& extension_path, |
| 195 const GURL& download_url, |
| 196 CrxInstaller** out_crx_installer) OVERRIDE { |
| 182 extension_id_ = id; | 197 extension_id_ = id; |
| 183 install_path_ = extension_path; | 198 install_path_ = extension_path; |
| 184 download_url_ = download_url; | 199 download_url_ = download_url; |
| 200 |
| 201 if (ContainsKey(fake_crx_installers_, id)) { |
| 202 *out_crx_installer = fake_crx_installers_[id]; |
| 203 return true; |
| 204 } |
| 205 |
| 206 return false; |
| 185 } | 207 } |
| 186 | 208 |
| 187 virtual PendingExtensionManager* pending_extension_manager() OVERRIDE { | 209 virtual PendingExtensionManager* pending_extension_manager() OVERRIDE { |
| 188 return &pending_extension_manager_; | 210 return &pending_extension_manager_; |
| 189 } | 211 } |
| 190 | 212 |
| 191 virtual const Extension* GetExtensionById( | 213 virtual const Extension* GetExtensionById( |
| 192 const std::string& id, bool) const OVERRIDE { | 214 const std::string& id, bool) const OVERRIDE { |
| 193 last_inquired_extension_id_ = id; | 215 last_inquired_extension_id_ = id; |
| 194 return NULL; | 216 return NULL; |
| 195 } | 217 } |
| 196 | 218 |
| 197 const std::string& extension_id() const { return extension_id_; } | 219 const std::string& extension_id() const { return extension_id_; } |
| 198 const FilePath& install_path() const { return install_path_; } | 220 const FilePath& install_path() const { return install_path_; } |
| 199 const GURL& download_url() const { return download_url_; } | 221 const GURL& download_url() const { return download_url_; } |
| 200 const std::string& last_inquired_extension_id() const { | 222 const std::string& last_inquired_extension_id() const { |
| 201 return last_inquired_extension_id_; | 223 return last_inquired_extension_id_; |
| 202 } | 224 } |
| 203 | 225 |
| 204 private: | 226 private: |
| 227 // Hold the set of ids that UpdateExtension() should fake success on. |
| 228 // UpdateExtension(id, ...) will return true iff fake_crx_installers_ |
| 229 // contains key |id|. |out_install_notification_source| will be set |
| 230 // to Source<CrxInstaller(fake_crx_installers_[i]). |
| 231 std::map<std::string, CrxInstaller*> fake_crx_installers_; |
| 232 |
| 205 std::string extension_id_; | 233 std::string extension_id_; |
| 206 FilePath install_path_; | 234 FilePath install_path_; |
| 207 GURL download_url_; | 235 GURL download_url_; |
| 208 | 236 |
| 209 // The last extension ID that GetExtensionById was called with. | 237 // The last extension ID that GetExtensionById was called with. |
| 210 // Mutable because the method that sets it (GetExtensionById) is const | 238 // Mutable because the method that sets it (GetExtensionById) is const |
| 211 // in the actual extension service, but must record the last extension | 239 // in the actual extension service, but must record the last extension |
| 212 // ID in this test class. | 240 // ID in this test class. |
| 213 mutable std::string last_inquired_extension_id_; | 241 mutable std::string last_inquired_extension_id_; |
| 214 }; | 242 }; |
| (...skipping 331 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 546 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); | 574 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); |
| 547 BrowserThread file_thread(BrowserThread::FILE); | 575 BrowserThread file_thread(BrowserThread::FILE); |
| 548 file_thread.Start(); | 576 file_thread.Start(); |
| 549 BrowserThread io_thread(BrowserThread::IO); | 577 BrowserThread io_thread(BrowserThread::IO); |
| 550 io_thread.Start(); | 578 io_thread.Start(); |
| 551 | 579 |
| 552 TestURLFetcherFactory factory; | 580 TestURLFetcherFactory factory; |
| 553 TestURLFetcher* fetcher = NULL; | 581 TestURLFetcher* fetcher = NULL; |
| 554 URLFetcher::set_factory(&factory); | 582 URLFetcher::set_factory(&factory); |
| 555 scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests); | 583 scoped_ptr<ServiceForDownloadTests> service(new ServiceForDownloadTests); |
| 556 ExtensionUpdater updater(service.get(), service->extension_prefs(), | 584 ExtensionUpdater updater(service.get(), |
| 585 service->extension_prefs(), |
| 557 service->pref_service(), | 586 service->pref_service(), |
| 558 service->profile(), | 587 service->profile(), |
| 559 kUpdateFrequencySecs); | 588 kUpdateFrequencySecs); |
| 560 updater.Start(); | 589 updater.Start(); |
| 561 | 590 |
| 562 GURL url1("http://localhost/manifest1"); | 591 GURL url1("http://localhost/manifest1"); |
| 563 GURL url2("http://localhost/manifest2"); | 592 GURL url2("http://localhost/manifest2"); |
| 564 | 593 |
| 565 // Request 2 update checks - the first should begin immediately and the | 594 // Request 2 update checks - the first should begin immediately and the |
| 566 // second one should be queued up. | 595 // second one should be queued up. |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 732 // The updater should have called extension service to process the | 761 // The updater should have called extension service to process the |
| 733 // blacklist. | 762 // blacklist. |
| 734 EXPECT_TRUE(service.processed_blacklist()); | 763 EXPECT_TRUE(service.processed_blacklist()); |
| 735 | 764 |
| 736 EXPECT_EQ(version, service.pref_service()-> | 765 EXPECT_EQ(version, service.pref_service()-> |
| 737 GetString(prefs::kExtensionBlacklistUpdateVersion)); | 766 GetString(prefs::kExtensionBlacklistUpdateVersion)); |
| 738 | 767 |
| 739 URLFetcher::set_factory(NULL); | 768 URLFetcher::set_factory(NULL); |
| 740 } | 769 } |
| 741 | 770 |
| 742 static void TestMultipleExtensionDownloading() { | 771 // Two extensions are updated. If |updates_start_running| is true, the |
| 772 // mock extensions service has UpdateExtension(...) return true, and |
| 773 // the test is responsible for creating fake CrxInstallers. Otherwise, |
| 774 // UpdateExtension() returns false, signaling install failures. |
| 775 static void TestMultipleExtensionDownloading(bool updates_start_running) { |
| 743 MessageLoopForUI message_loop; | 776 MessageLoopForUI message_loop; |
| 744 BrowserThread ui_thread(BrowserThread::UI, &message_loop); | 777 BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| 745 BrowserThread file_thread(BrowserThread::FILE, &message_loop); | 778 BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| 746 BrowserThread io_thread(BrowserThread::IO); | 779 BrowserThread io_thread(BrowserThread::IO); |
| 747 io_thread.Start(); | 780 io_thread.Start(); |
| 748 | 781 |
| 749 TestURLFetcherFactory factory; | 782 TestURLFetcherFactory factory; |
| 750 TestURLFetcher* fetcher = NULL; | 783 TestURLFetcher* fetcher = NULL; |
| 751 URLFetcher::set_factory(&factory); | 784 URLFetcher::set_factory(&factory); |
| 752 ServiceForDownloadTests service; | 785 ServiceForDownloadTests service; |
| 753 ExtensionUpdater updater( | 786 ExtensionUpdater updater( |
| 754 &service, service.extension_prefs(), service.pref_service(), | 787 &service, service.extension_prefs(), service.pref_service(), |
| 755 service.profile(), kUpdateFrequencySecs); | 788 service.profile(), kUpdateFrequencySecs); |
| 756 updater.Start(); | 789 updater.Start(); |
| 757 | 790 |
| 791 EXPECT_FALSE(updater.crx_install_is_running_); |
| 792 |
| 758 GURL url1("http://localhost/extension1.crx"); | 793 GURL url1("http://localhost/extension1.crx"); |
| 759 GURL url2("http://localhost/extension2.crx"); | 794 GURL url2("http://localhost/extension2.crx"); |
| 760 | 795 |
| 761 std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | 796 std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| 762 std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; | 797 std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; |
| 763 | 798 |
| 764 std::string hash1 = ""; | 799 std::string hash1 = ""; |
| 765 std::string hash2 = ""; | 800 std::string hash2 = ""; |
| 766 | 801 |
| 767 std::string version1 = "0.1"; | 802 std::string version1 = "0.1"; |
| 768 std::string version2 = "0.1"; | 803 std::string version2 = "0.1"; |
| 769 // Start two fetches | 804 // Start two fetches |
| 770 updater.FetchUpdatedExtension(id1, url1, hash1, version1); | 805 updater.FetchUpdatedExtension(id1, url1, hash1, version1); |
| 771 updater.FetchUpdatedExtension(id2, url2, hash2, version2); | 806 updater.FetchUpdatedExtension(id2, url2, hash2, version2); |
| 772 | 807 |
| 773 // Make the first fetch complete. | 808 // Make the first fetch complete. |
| 774 FilePath extension_file_path(FILE_PATH_LITERAL("/whatever")); | 809 FilePath extension_file_path(FILE_PATH_LITERAL("/whatever")); |
| 775 | 810 |
| 776 fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); | 811 fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| 777 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); | 812 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| 778 EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); | 813 EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| 779 | 814 |
| 815 // We need some CrxInstallers, and CrxInstallers require a real |
| 816 // ExtensionService. Create one on the testing profile. Any action |
| 817 // the CrxInstallers take is on the testing profile's extension |
| 818 // service, not on our mock |service|. This allows us to fake |
| 819 // the CrxInstaller actions we want. |
| 820 TestingProfile profile; |
| 821 profile.CreateExtensionService( |
| 822 CommandLine::ForCurrentProcess(), |
| 823 FilePath(), |
| 824 false); |
| 825 profile.GetExtensionService()->set_extensions_enabled(true); |
| 826 profile.GetExtensionService()->set_show_extensions_prompts(false); |
| 827 |
| 828 scoped_refptr<CrxInstaller> fake_crx1( |
| 829 profile.GetExtensionService()->MakeCrxInstaller(NULL)); |
| 830 scoped_refptr<CrxInstaller> fake_crx2( |
| 831 profile.GetExtensionService()->MakeCrxInstaller(NULL)); |
| 832 |
| 833 if (updates_start_running) { |
| 834 // Add fake CrxInstaller to be returned by service.UpdateExtension(). |
| 835 service.AddFakeCrxInstaller(id1, fake_crx1.get()); |
| 836 service.AddFakeCrxInstaller(id2, fake_crx2.get()); |
| 837 } else { |
| 838 // If we don't add fake CRX installers, the mock service fakes a failure |
| 839 // starting the install. |
| 840 } |
| 841 |
| 780 fetcher->set_url(url1); | 842 fetcher->set_url(url1); |
| 781 fetcher->set_status(net::URLRequestStatus()); | 843 fetcher->set_status(net::URLRequestStatus()); |
| 782 fetcher->set_response_code(200); | 844 fetcher->set_response_code(200); |
| 783 fetcher->SetResponseFilePath(extension_file_path); | 845 fetcher->SetResponseFilePath(extension_file_path); |
| 784 fetcher->delegate()->OnURLFetchComplete(fetcher); | 846 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 785 | 847 |
| 786 message_loop.RunAllPending(); | 848 message_loop.RunAllPending(); |
| 787 | 849 |
| 788 // Expect that the service was asked to do an install with the right data. | 850 // Expect that the service was asked to do an install with the right data. |
| 789 FilePath tmpfile_path = service.install_path(); | 851 FilePath tmpfile_path = service.install_path(); |
| 790 EXPECT_FALSE(tmpfile_path.empty()); | 852 EXPECT_FALSE(tmpfile_path.empty()); |
| 791 EXPECT_EQ(id1, service.extension_id()); | 853 EXPECT_EQ(id1, service.extension_id()); |
| 792 EXPECT_EQ(url1, service.download_url()); | 854 EXPECT_EQ(url1, service.download_url()); |
| 793 message_loop.RunAllPending(); | 855 message_loop.RunAllPending(); |
| 794 | 856 |
| 795 // Make sure the second fetch finished and asked the service to do an | 857 // Make sure the second fetch finished and asked the service to do an |
| 796 // update. | 858 // update. |
| 797 FilePath extension_file_path2(FILE_PATH_LITERAL("/whatever2")); | 859 FilePath extension_file_path2(FILE_PATH_LITERAL("/whatever2")); |
| 798 fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); | 860 fetcher = factory.GetFetcherByID(ExtensionUpdater::kExtensionFetcherId); |
| 799 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); | 861 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| 800 EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); | 862 EXPECT_TRUE(fetcher->load_flags() == expected_load_flags); |
| 801 | 863 |
| 802 fetcher->set_url(url2); | 864 fetcher->set_url(url2); |
| 803 fetcher->set_status(net::URLRequestStatus()); | 865 fetcher->set_status(net::URLRequestStatus()); |
| 804 fetcher->set_response_code(200); | 866 fetcher->set_response_code(200); |
| 805 fetcher->SetResponseFilePath(extension_file_path2); | 867 fetcher->SetResponseFilePath(extension_file_path2); |
| 806 fetcher->delegate()->OnURLFetchComplete(fetcher); | 868 fetcher->delegate()->OnURLFetchComplete(fetcher); |
| 807 message_loop.RunAllPending(); | 869 message_loop.RunAllPending(); |
| 870 |
| 871 if (updates_start_running) { |
| 872 EXPECT_TRUE(updater.crx_install_is_running_); |
| 873 |
| 874 // The second install should not have run, because the first has not |
| 875 // sent a notification that it finished. |
| 876 EXPECT_EQ(id1, service.extension_id()); |
| 877 EXPECT_EQ(url1, service.download_url()); |
| 878 |
| 879 // Fake install notice. This should start the second installation, |
| 880 // which will be checked below. |
| 881 fake_crx1->NotifyCrxInstallComplete(); |
| 882 |
| 883 EXPECT_TRUE(updater.crx_install_is_running_); |
| 884 } |
| 885 |
| 808 EXPECT_EQ(id2, service.extension_id()); | 886 EXPECT_EQ(id2, service.extension_id()); |
| 809 EXPECT_EQ(url2, service.download_url()); | 887 EXPECT_EQ(url2, service.download_url()); |
| 810 EXPECT_FALSE(service.install_path().empty()); | 888 EXPECT_FALSE(service.install_path().empty()); |
| 811 | 889 |
| 812 // Make sure the correct crx contents were passed for the update call. | 890 // Make sure the correct crx contents were passed for the update call. |
| 813 EXPECT_EQ(extension_file_path2, service.install_path()); | 891 EXPECT_EQ(extension_file_path2, service.install_path()); |
| 892 |
| 893 if (updates_start_running) { |
| 894 EXPECT_TRUE(updater.crx_install_is_running_); |
| 895 fake_crx2->NotifyCrxInstallComplete(); |
| 896 } |
| 897 EXPECT_FALSE(updater.crx_install_is_running_); |
| 814 } | 898 } |
| 815 | 899 |
| 816 // Test requests to both a Google server and a non-google server. This allows | 900 // Test requests to both a Google server and a non-google server. This allows |
| 817 // us to test various combinations of installed (ie roll call) and active | 901 // us to test various combinations of installed (ie roll call) and active |
| 818 // (ie app launch) ping scenarios. The invariant is that each type of ping | 902 // (ie app launch) ping scenarios. The invariant is that each type of ping |
| 819 // value should be present at most once per day, and can be calculated based | 903 // value should be present at most once per day, and can be calculated based |
| 820 // on the delta between now and the last ping time (or in the case of active | 904 // on the delta between now and the last ping time (or in the case of active |
| 821 // pings, that delta plus whether the app has been active). | 905 // pings, that delta plus whether the app has been active). |
| 822 static void TestGalleryRequests(int rollcall_ping_days, | 906 static void TestGalleryRequests(int rollcall_ping_days, |
| 823 int active_ping_days, | 907 int active_ping_days, |
| (...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1011 | 1095 |
| 1012 TEST(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) { | 1096 TEST(ExtensionUpdaterTest, TestSingleExtensionDownloadingPending) { |
| 1013 ExtensionUpdaterTest::TestSingleExtensionDownloading(true); | 1097 ExtensionUpdaterTest::TestSingleExtensionDownloading(true); |
| 1014 } | 1098 } |
| 1015 | 1099 |
| 1016 // This test is disabled on Mac, see http://crbug.com/26035. | 1100 // This test is disabled on Mac, see http://crbug.com/26035. |
| 1017 TEST(ExtensionUpdaterTest, TestBlacklistDownloading) { | 1101 TEST(ExtensionUpdaterTest, TestBlacklistDownloading) { |
| 1018 ExtensionUpdaterTest::TestBlacklistDownloading(); | 1102 ExtensionUpdaterTest::TestBlacklistDownloading(); |
| 1019 } | 1103 } |
| 1020 | 1104 |
| 1021 TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloading) { | 1105 TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesFail) { |
| 1022 ExtensionUpdaterTest::TestMultipleExtensionDownloading(); | 1106 ExtensionUpdaterTest::TestMultipleExtensionDownloading(false); |
| 1107 } |
| 1108 TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloadingUpdatesSucceed) { |
| 1109 ExtensionUpdaterTest::TestMultipleExtensionDownloading(true); |
| 1023 } | 1110 } |
| 1024 | 1111 |
| 1025 TEST(ExtensionUpdaterTest, TestGalleryRequests) { | 1112 TEST(ExtensionUpdaterTest, TestGalleryRequests) { |
| 1026 // We want to test a variety of combinations of expected ping conditions for | 1113 // We want to test a variety of combinations of expected ping conditions for |
| 1027 // rollcall and active pings. | 1114 // rollcall and active pings. |
| 1028 int ping_cases[] = { ManifestFetchData::kNeverPinged, 0, 1, 5 }; | 1115 int ping_cases[] = { ManifestFetchData::kNeverPinged, 0, 1, 5 }; |
| 1029 | 1116 |
| 1030 for (size_t i = 0; i < arraysize(ping_cases); i++) { | 1117 for (size_t i = 0; i < arraysize(ping_cases); i++) { |
| 1031 for (size_t j = 0; j < arraysize(ping_cases); j++) { | 1118 for (size_t j = 0; j < arraysize(ping_cases); j++) { |
| 1032 for (size_t k = 0; k < 2; k++) { | 1119 for (size_t k = 0; k < 2; k++) { |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1154 | 1241 |
| 1155 // TODO(asargent) - (http://crbug.com/12780) add tests for: | 1242 // TODO(asargent) - (http://crbug.com/12780) add tests for: |
| 1156 // -prodversionmin (shouldn't update if browser version too old) | 1243 // -prodversionmin (shouldn't update if browser version too old) |
| 1157 // -manifests & updates arriving out of order / interleaved | 1244 // -manifests & updates arriving out of order / interleaved |
| 1158 // -malformed update url (empty, file://, has query, has a # fragment, etc.) | 1245 // -malformed update url (empty, file://, has query, has a # fragment, etc.) |
| 1159 // -An extension gets uninstalled while updates are in progress (so it doesn't | 1246 // -An extension gets uninstalled while updates are in progress (so it doesn't |
| 1160 // "come back from the dead") | 1247 // "come back from the dead") |
| 1161 // -An extension gets manually updated to v3 while we're downloading v2 (ie | 1248 // -An extension gets manually updated to v3 while we're downloading v2 (ie |
| 1162 // you don't get downgraded accidentally) | 1249 // you don't get downgraded accidentally) |
| 1163 // -An update manifest mentions multiple updates | 1250 // -An update manifest mentions multiple updates |
| OLD | NEW |