| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/file_util.h" | 7 #include "base/file_util.h" |
| 8 #include "base/scoped_ptr.h" | 8 #include "base/scoped_ptr.h" |
| 9 #include "base/stl_util-inl.h" | 9 #include "base/stl_util-inl.h" |
| 10 #include "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 service.set_extensions(extensions); | 301 service.set_extensions(extensions); |
| 302 } | 302 } |
| 303 | 303 |
| 304 // Setup and start the updater. | 304 // Setup and start the updater. |
| 305 MessageLoop message_loop; | 305 MessageLoop message_loop; |
| 306 BrowserThread io_thread(BrowserThread::IO); | 306 BrowserThread io_thread(BrowserThread::IO); |
| 307 io_thread.Start(); | 307 io_thread.Start(); |
| 308 | 308 |
| 309 TestURLFetcherFactory factory; | 309 TestURLFetcherFactory factory; |
| 310 URLFetcher::set_factory(&factory); | 310 URLFetcher::set_factory(&factory); |
| 311 scoped_refptr<ExtensionUpdater> updater = | 311 scoped_refptr<ExtensionUpdater> updater( |
| 312 new ExtensionUpdater(&service, service.pref_service(), 60*60*24); | 312 new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); |
| 313 updater->Start(); | 313 updater->Start(); |
| 314 | 314 |
| 315 // Tell the update that it's time to do update checks. | 315 // Tell the update that it's time to do update checks. |
| 316 SimulateTimerFired(updater.get()); | 316 SimulateTimerFired(updater.get()); |
| 317 | 317 |
| 318 // Get the url our mock fetcher was asked to fetch. | 318 // Get the url our mock fetcher was asked to fetch. |
| 319 TestURLFetcher* fetcher = | 319 TestURLFetcher* fetcher = |
| 320 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); | 320 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| 321 const GURL& url = fetcher->original_url(); | 321 const GURL& url = fetcher->original_url(); |
| 322 EXPECT_FALSE(url.is_empty()); | 322 EXPECT_FALSE(url.is_empty()); |
| (...skipping 26 matching lines...) Expand all Loading... |
| 349 static void TestBlacklistUpdateCheckRequests() { | 349 static void TestBlacklistUpdateCheckRequests() { |
| 350 ServiceForManifestTests service; | 350 ServiceForManifestTests service; |
| 351 | 351 |
| 352 // Setup and start the updater. | 352 // Setup and start the updater. |
| 353 MessageLoop message_loop; | 353 MessageLoop message_loop; |
| 354 BrowserThread io_thread(BrowserThread::IO); | 354 BrowserThread io_thread(BrowserThread::IO); |
| 355 io_thread.Start(); | 355 io_thread.Start(); |
| 356 | 356 |
| 357 TestURLFetcherFactory factory; | 357 TestURLFetcherFactory factory; |
| 358 URLFetcher::set_factory(&factory); | 358 URLFetcher::set_factory(&factory); |
| 359 scoped_refptr<ExtensionUpdater> updater = | 359 scoped_refptr<ExtensionUpdater> updater( |
| 360 new ExtensionUpdater(&service, service.pref_service(), 60*60*24); | 360 new ExtensionUpdater(&service, service.pref_service(), 60*60*24)); |
| 361 updater->Start(); | 361 updater->Start(); |
| 362 | 362 |
| 363 // Tell the updater that it's time to do update checks. | 363 // Tell the updater that it's time to do update checks. |
| 364 SimulateTimerFired(updater.get()); | 364 SimulateTimerFired(updater.get()); |
| 365 | 365 |
| 366 // No extensions installed, so nothing should have been fetched. | 366 // No extensions installed, so nothing should have been fetched. |
| 367 TestURLFetcher* fetcher = | 367 TestURLFetcher* fetcher = |
| 368 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); | 368 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| 369 EXPECT_TRUE(fetcher == NULL); | 369 EXPECT_TRUE(fetcher == NULL); |
| 370 | 370 |
| (...skipping 30 matching lines...) Expand all Loading... |
| 401 } | 401 } |
| 402 | 402 |
| 403 static void TestDetermineUpdates() { | 403 static void TestDetermineUpdates() { |
| 404 // Create a set of test extensions | 404 // Create a set of test extensions |
| 405 ServiceForManifestTests service; | 405 ServiceForManifestTests service; |
| 406 ExtensionList tmp; | 406 ExtensionList tmp; |
| 407 service.CreateTestExtensions(3, &tmp, NULL, Extension::INTERNAL); | 407 service.CreateTestExtensions(3, &tmp, NULL, Extension::INTERNAL); |
| 408 service.set_extensions(tmp); | 408 service.set_extensions(tmp); |
| 409 | 409 |
| 410 MessageLoop message_loop; | 410 MessageLoop message_loop; |
| 411 scoped_refptr<ExtensionUpdater> updater = | 411 scoped_refptr<ExtensionUpdater> updater( |
| 412 new ExtensionUpdater(&service, service.pref_service(), | 412 new ExtensionUpdater(&service, service.pref_service(), |
| 413 kUpdateFrequencySecs); | 413 kUpdateFrequencySecs)); |
| 414 | 414 |
| 415 // Check passing an empty list of parse results to DetermineUpdates | 415 // Check passing an empty list of parse results to DetermineUpdates |
| 416 ManifestFetchData fetch_data(GURL("http://localhost/foo")); | 416 ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| 417 UpdateManifest::Results updates; | 417 UpdateManifest::Results updates; |
| 418 std::vector<int> updateable = updater->DetermineUpdates(fetch_data, | 418 std::vector<int> updateable = updater->DetermineUpdates(fetch_data, |
| 419 updates); | 419 updates); |
| 420 EXPECT_TRUE(updateable.empty()); | 420 EXPECT_TRUE(updateable.empty()); |
| 421 | 421 |
| 422 // Create two updates - expect that DetermineUpdates will return the first | 422 // Create two updates - expect that DetermineUpdates will return the first |
| 423 // one (v1.0 installed, v1.1 available) but not the second one (both | 423 // one (v1.0 installed, v1.1 available) but not the second one (both |
| (...skipping 14 matching lines...) Expand all Loading... |
| 438 } | 438 } |
| 439 | 439 |
| 440 static void TestDetermineUpdatesPending() { | 440 static void TestDetermineUpdatesPending() { |
| 441 // Create a set of test extensions | 441 // Create a set of test extensions |
| 442 ServiceForManifestTests service; | 442 ServiceForManifestTests service; |
| 443 PendingExtensionMap pending_extensions; | 443 PendingExtensionMap pending_extensions; |
| 444 CreateTestPendingExtensions(3, GURL(), &pending_extensions); | 444 CreateTestPendingExtensions(3, GURL(), &pending_extensions); |
| 445 service.set_pending_extensions(pending_extensions); | 445 service.set_pending_extensions(pending_extensions); |
| 446 | 446 |
| 447 MessageLoop message_loop; | 447 MessageLoop message_loop; |
| 448 scoped_refptr<ExtensionUpdater> updater = | 448 scoped_refptr<ExtensionUpdater> updater( |
| 449 new ExtensionUpdater(&service, service.pref_service(), | 449 new ExtensionUpdater(&service, service.pref_service(), |
| 450 kUpdateFrequencySecs); | 450 kUpdateFrequencySecs)); |
| 451 | 451 |
| 452 ManifestFetchData fetch_data(GURL("http://localhost/foo")); | 452 ManifestFetchData fetch_data(GURL("http://localhost/foo")); |
| 453 UpdateManifest::Results updates; | 453 UpdateManifest::Results updates; |
| 454 for (PendingExtensionMap::const_iterator it = pending_extensions.begin(); | 454 for (PendingExtensionMap::const_iterator it = pending_extensions.begin(); |
| 455 it != pending_extensions.end(); ++it) { | 455 it != pending_extensions.end(); ++it) { |
| 456 fetch_data.AddExtension(it->first, "1.0.0.0", | 456 fetch_data.AddExtension(it->first, "1.0.0.0", |
| 457 ManifestFetchData::kNeverPinged); | 457 ManifestFetchData::kNeverPinged); |
| 458 AddParseResult(it->first, | 458 AddParseResult(it->first, |
| 459 "1.1", "http://localhost/e1_1.1.crx", &updates); | 459 "1.1", "http://localhost/e1_1.1.crx", &updates); |
| 460 } | 460 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 472 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); | 472 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); |
| 473 BrowserThread file_thread(BrowserThread::FILE); | 473 BrowserThread file_thread(BrowserThread::FILE); |
| 474 file_thread.Start(); | 474 file_thread.Start(); |
| 475 BrowserThread io_thread(BrowserThread::IO); | 475 BrowserThread io_thread(BrowserThread::IO); |
| 476 io_thread.Start(); | 476 io_thread.Start(); |
| 477 | 477 |
| 478 TestURLFetcherFactory factory; | 478 TestURLFetcherFactory factory; |
| 479 TestURLFetcher* fetcher = NULL; | 479 TestURLFetcher* fetcher = NULL; |
| 480 URLFetcher::set_factory(&factory); | 480 URLFetcher::set_factory(&factory); |
| 481 ServiceForDownloadTests service; | 481 ServiceForDownloadTests service; |
| 482 scoped_refptr<ExtensionUpdater> updater = | 482 scoped_refptr<ExtensionUpdater> updater( |
| 483 new ExtensionUpdater(&service, service.pref_service(), | 483 new ExtensionUpdater(&service, service.pref_service(), |
| 484 kUpdateFrequencySecs); | 484 kUpdateFrequencySecs)); |
| 485 | 485 |
| 486 GURL url1("http://localhost/manifest1"); | 486 GURL url1("http://localhost/manifest1"); |
| 487 GURL url2("http://localhost/manifest2"); | 487 GURL url2("http://localhost/manifest2"); |
| 488 | 488 |
| 489 // Request 2 update checks - the first should begin immediately and the | 489 // Request 2 update checks - the first should begin immediately and the |
| 490 // second one should be queued up. | 490 // second one should be queued up. |
| 491 ManifestFetchData* fetch1 = new ManifestFetchData(url1); | 491 ManifestFetchData* fetch1 = new ManifestFetchData(url1); |
| 492 ManifestFetchData* fetch2 = new ManifestFetchData(url2); | 492 ManifestFetchData* fetch2 = new ManifestFetchData(url2); |
| 493 fetch1->AddExtension("1111", "1.0", 0); | 493 fetch1->AddExtension("1111", "1.0", 0); |
| 494 fetch2->AddExtension("12345", "2.0", ManifestFetchData::kNeverPinged); | 494 fetch2->AddExtension("12345", "2.0", ManifestFetchData::kNeverPinged); |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 536 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); | 536 BrowserThread ui_thread(BrowserThread::UI, &ui_loop); |
| 537 BrowserThread file_thread(BrowserThread::FILE); | 537 BrowserThread file_thread(BrowserThread::FILE); |
| 538 file_thread.Start(); | 538 file_thread.Start(); |
| 539 BrowserThread io_thread(BrowserThread::IO); | 539 BrowserThread io_thread(BrowserThread::IO); |
| 540 io_thread.Start(); | 540 io_thread.Start(); |
| 541 | 541 |
| 542 TestURLFetcherFactory factory; | 542 TestURLFetcherFactory factory; |
| 543 TestURLFetcher* fetcher = NULL; | 543 TestURLFetcher* fetcher = NULL; |
| 544 URLFetcher::set_factory(&factory); | 544 URLFetcher::set_factory(&factory); |
| 545 ServiceForDownloadTests service; | 545 ServiceForDownloadTests service; |
| 546 scoped_refptr<ExtensionUpdater> updater = | 546 scoped_refptr<ExtensionUpdater> updater( |
| 547 new ExtensionUpdater(&service, service.pref_service(), | 547 new ExtensionUpdater(&service, service.pref_service(), |
| 548 kUpdateFrequencySecs); | 548 kUpdateFrequencySecs)); |
| 549 | 549 |
| 550 GURL test_url("http://localhost/extension.crx"); | 550 GURL test_url("http://localhost/extension.crx"); |
| 551 | 551 |
| 552 std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | 552 std::string id = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| 553 std::string hash = ""; | 553 std::string hash = ""; |
| 554 scoped_ptr<Version> version(Version::GetVersionFromString("0.0.1")); | 554 scoped_ptr<Version> version(Version::GetVersionFromString("0.0.1")); |
| 555 ASSERT_TRUE(version.get()); | 555 ASSERT_TRUE(version.get()); |
| 556 updater->FetchUpdatedExtension(id, test_url, hash, version->GetString()); | 556 updater->FetchUpdatedExtension(id, test_url, hash, version->GetString()); |
| 557 | 557 |
| 558 if (pending) { | 558 if (pending) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 599 static void TestBlacklistDownloading() { | 599 static void TestBlacklistDownloading() { |
| 600 MessageLoop message_loop; | 600 MessageLoop message_loop; |
| 601 BrowserThread ui_thread(BrowserThread::UI, &message_loop); | 601 BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| 602 BrowserThread io_thread(BrowserThread::IO); | 602 BrowserThread io_thread(BrowserThread::IO); |
| 603 io_thread.Start(); | 603 io_thread.Start(); |
| 604 | 604 |
| 605 TestURLFetcherFactory factory; | 605 TestURLFetcherFactory factory; |
| 606 TestURLFetcher* fetcher = NULL; | 606 TestURLFetcher* fetcher = NULL; |
| 607 URLFetcher::set_factory(&factory); | 607 URLFetcher::set_factory(&factory); |
| 608 ServiceForBlacklistTests service; | 608 ServiceForBlacklistTests service; |
| 609 scoped_refptr<ExtensionUpdater> updater = | 609 scoped_refptr<ExtensionUpdater> updater( |
| 610 new ExtensionUpdater(&service, service.pref_service(), | 610 new ExtensionUpdater(&service, service.pref_service(), |
| 611 kUpdateFrequencySecs); | 611 kUpdateFrequencySecs)); |
| 612 service.pref_service()-> | 612 service.pref_service()-> |
| 613 RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion, "0"); | 613 RegisterStringPref(prefs::kExtensionBlacklistUpdateVersion, "0"); |
| 614 GURL test_url("http://localhost/extension.crx"); | 614 GURL test_url("http://localhost/extension.crx"); |
| 615 | 615 |
| 616 std::string id = "com.google.crx.blacklist"; | 616 std::string id = "com.google.crx.blacklist"; |
| 617 | 617 |
| 618 std::string hash = | 618 std::string hash = |
| 619 "2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C"; | 619 "2CE109E9D0FAF820B2434E166297934E6177B65AB9951DBC3E204CAD4689B39C"; |
| 620 | 620 |
| 621 std::string version = "0.0.1"; | 621 std::string version = "0.0.1"; |
| (...skipping 24 matching lines...) Expand all Loading... |
| 646 MessageLoopForUI message_loop; | 646 MessageLoopForUI message_loop; |
| 647 BrowserThread ui_thread(BrowserThread::UI, &message_loop); | 647 BrowserThread ui_thread(BrowserThread::UI, &message_loop); |
| 648 BrowserThread file_thread(BrowserThread::FILE, &message_loop); | 648 BrowserThread file_thread(BrowserThread::FILE, &message_loop); |
| 649 BrowserThread io_thread(BrowserThread::IO); | 649 BrowserThread io_thread(BrowserThread::IO); |
| 650 io_thread.Start(); | 650 io_thread.Start(); |
| 651 | 651 |
| 652 TestURLFetcherFactory factory; | 652 TestURLFetcherFactory factory; |
| 653 TestURLFetcher* fetcher = NULL; | 653 TestURLFetcher* fetcher = NULL; |
| 654 URLFetcher::set_factory(&factory); | 654 URLFetcher::set_factory(&factory); |
| 655 ServiceForDownloadTests service; | 655 ServiceForDownloadTests service; |
| 656 scoped_refptr<ExtensionUpdater> updater = | 656 scoped_refptr<ExtensionUpdater> updater( |
| 657 new ExtensionUpdater(&service, service.pref_service(), | 657 new ExtensionUpdater(&service, service.pref_service(), |
| 658 kUpdateFrequencySecs); | 658 kUpdateFrequencySecs)); |
| 659 | 659 |
| 660 GURL url1("http://localhost/extension1.crx"); | 660 GURL url1("http://localhost/extension1.crx"); |
| 661 GURL url2("http://localhost/extension2.crx"); | 661 GURL url2("http://localhost/extension2.crx"); |
| 662 | 662 |
| 663 std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; | 663 std::string id1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; |
| 664 std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; | 664 std::string id2 = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; |
| 665 | 665 |
| 666 std::string hash1 = ""; | 666 std::string hash1 = ""; |
| 667 std::string hash2 = ""; | 667 std::string hash2 = ""; |
| 668 | 668 |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 733 if (ping_days == 0) { | 733 if (ping_days == 0) { |
| 734 service.extension_prefs()->SetLastPingDay( | 734 service.extension_prefs()->SetLastPingDay( |
| 735 tmp[0]->id(), now - TimeDelta::FromSeconds(15)); | 735 tmp[0]->id(), now - TimeDelta::FromSeconds(15)); |
| 736 } else if (ping_days > 0) { | 736 } else if (ping_days > 0) { |
| 737 Time last_ping_day = | 737 Time last_ping_day = |
| 738 now - TimeDelta::FromDays(ping_days) - TimeDelta::FromSeconds(15); | 738 now - TimeDelta::FromDays(ping_days) - TimeDelta::FromSeconds(15); |
| 739 service.extension_prefs()->SetLastPingDay(tmp[0]->id(), last_ping_day); | 739 service.extension_prefs()->SetLastPingDay(tmp[0]->id(), last_ping_day); |
| 740 } | 740 } |
| 741 | 741 |
| 742 MessageLoop message_loop; | 742 MessageLoop message_loop; |
| 743 scoped_refptr<ExtensionUpdater> updater = | 743 scoped_refptr<ExtensionUpdater> updater( |
| 744 new ExtensionUpdater(&service, service.pref_service(), | 744 new ExtensionUpdater(&service, service.pref_service(), |
| 745 kUpdateFrequencySecs); | 745 kUpdateFrequencySecs)); |
| 746 updater->set_blacklist_checks_enabled(false); | 746 updater->set_blacklist_checks_enabled(false); |
| 747 | 747 |
| 748 // Make the updater do manifest fetching, and note the urls it tries to | 748 // Make the updater do manifest fetching, and note the urls it tries to |
| 749 // fetch. | 749 // fetch. |
| 750 std::vector<GURL> fetched_urls; | 750 std::vector<GURL> fetched_urls; |
| 751 updater->CheckNow(); | 751 updater->CheckNow(); |
| 752 TestURLFetcher* fetcher = | 752 TestURLFetcher* fetcher = |
| 753 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); | 753 factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId); |
| 754 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); | 754 EXPECT_TRUE(fetcher != NULL && fetcher->delegate() != NULL); |
| 755 fetched_urls.push_back(fetcher->original_url()); | 755 fetched_urls.push_back(fetcher->original_url()); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 785 EXPECT_TRUE(pos != std::string::npos); | 785 EXPECT_TRUE(pos != std::string::npos); |
| 786 } | 786 } |
| 787 } | 787 } |
| 788 | 788 |
| 789 // This makes sure that the extension updater properly stores the results | 789 // This makes sure that the extension updater properly stores the results |
| 790 // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is | 790 // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is |
| 791 // the first time we fetched the extension, or 2) We sent a ping value of | 791 // the first time we fetched the extension, or 2) We sent a ping value of |
| 792 // >= 1 day for the extension. | 792 // >= 1 day for the extension. |
| 793 static void TestHandleManifestResults() { | 793 static void TestHandleManifestResults() { |
| 794 ServiceForManifestTests service; | 794 ServiceForManifestTests service; |
| 795 scoped_refptr<ExtensionUpdater> updater = | 795 scoped_refptr<ExtensionUpdater> updater( |
| 796 new ExtensionUpdater(&service, service.pref_service(), | 796 new ExtensionUpdater(&service, service.pref_service(), |
| 797 kUpdateFrequencySecs); | 797 kUpdateFrequencySecs)); |
| 798 | 798 |
| 799 GURL update_url("http://www.google.com/manifest"); | 799 GURL update_url("http://www.google.com/manifest"); |
| 800 ExtensionList tmp; | 800 ExtensionList tmp; |
| 801 service.CreateTestExtensions(1, &tmp, &update_url.spec(), | 801 service.CreateTestExtensions(1, &tmp, &update_url.spec(), |
| 802 Extension::INTERNAL); | 802 Extension::INTERNAL); |
| 803 service.set_extensions(tmp); | 803 service.set_extensions(tmp); |
| 804 | 804 |
| 805 ManifestFetchData fetch_data(update_url); | 805 ManifestFetchData fetch_data(update_url); |
| 806 const Extension* extension = tmp[0]; | 806 const Extension* extension = tmp[0]; |
| 807 fetch_data.AddExtension(extension->id(), extension->VersionString(), | 807 fetch_data.AddExtension(extension->id(), extension->VersionString(), |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 925 // -prodversionmin (shouldn't update if browser version too old) | 925 // -prodversionmin (shouldn't update if browser version too old) |
| 926 // -manifests & updates arriving out of order / interleaved | 926 // -manifests & updates arriving out of order / interleaved |
| 927 // -Profile::GetDefaultRequestContext() returning null | 927 // -Profile::GetDefaultRequestContext() returning null |
| 928 // (should not crash, but just do check later) | 928 // (should not crash, but just do check later) |
| 929 // -malformed update url (empty, file://, has query, has a # fragment, etc.) | 929 // -malformed update url (empty, file://, has query, has a # fragment, etc.) |
| 930 // -An extension gets uninstalled while updates are in progress (so it doesn't | 930 // -An extension gets uninstalled while updates are in progress (so it doesn't |
| 931 // "come back from the dead") | 931 // "come back from the dead") |
| 932 // -An extension gets manually updated to v3 while we're downloading v2 (ie | 932 // -An extension gets manually updated to v3 while we're downloading v2 (ie |
| 933 // you don't get downgraded accidentally) | 933 // you don't get downgraded accidentally) |
| 934 // -An update manifest mentions multiple updates | 934 // -An update manifest mentions multiple updates |
| OLD | NEW |