diff options
author | sorin <sorin@chromium.org> | 2015-05-26 12:59:09 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-26 20:00:01 +0000 |
commit | 7c7176234e553c35a2d8ec014f2caa29f7278065 (patch) | |
tree | 70eb3c58c82369cc13c939c17ae63344644c4b31 | |
parent | 92780e77b6e0755e1d4bacbd493032969d04293b (diff) | |
download | chromium_src-7c7176234e553c35a2d8ec014f2caa29f7278065.zip chromium_src-7c7176234e553c35a2d8ec014f2caa29f7278065.tar.gz chromium_src-7c7176234e553c35a2d8ec014f2caa29f7278065.tar.bz2 |
Rewrite component update service in terms of components/update_client.
The goal of this change is to re-implement the component updater by
reusing the common code in components/update_client while keeping
the its public interface the same as before, in order to minimize
changes in its existing clients.
BUG=450337
Review URL: https://codereview.chromium.org/1133443002
Cr-Commit-Position: refs/heads/master@{#331412}
43 files changed, 1342 insertions, 2673 deletions
diff --git a/chrome/browser/chrome_browser_main.cc b/chrome/browser/chrome_browser_main.cc index 58235fa..a9a1e5f 100644 --- a/chrome/browser/chrome_browser_main.cc +++ b/chrome/browser/chrome_browser_main.cc @@ -471,8 +471,6 @@ void RegisterComponentsForUpdate() { RegisterSwReporterComponent(cus, g_browser_process->local_state()); RegisterCAPSComponent(cus); #endif // defined(OS_WIN) - - cus->Start(); } #if !defined(OS_ANDROID) diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator.cc b/chrome/browser/component_updater/chrome_component_updater_configurator.cc index 687f0e7..cc0fb61 100644 --- a/chrome/browser/component_updater/chrome_component_updater_configurator.cc +++ b/chrome/browser/component_updater/chrome_component_updater_configurator.cc @@ -106,10 +106,8 @@ class ChromeConfigurator : public Configurator { net::URLRequestContextGetter* url_request_getter); int InitialDelay() const override; - int NextCheckDelay() override; + int NextCheckDelay() const override; int StepDelay() const override; - int StepDelayMedium() override; - int MinimumReCheckWait() const override; int OnDemandDelay() const override; int UpdateDelay() const override; std::vector<GURL> UpdateUrl() const override; @@ -183,31 +181,23 @@ ChromeConfigurator::ChromeConfigurator( } int ChromeConfigurator::InitialDelay() const { - return fast_update_ ? 1 : (6 * kDelayOneMinute); + return fast_update_ ? 10 : (6 * kDelayOneMinute); } -int ChromeConfigurator::NextCheckDelay() { - return fast_update_ ? 3 : (6 * kDelayOneHour); -} - -int ChromeConfigurator::StepDelayMedium() { - return fast_update_ ? 3 : (15 * kDelayOneMinute); +int ChromeConfigurator::NextCheckDelay() const { + return fast_update_ ? 60 : (6 * kDelayOneHour); } int ChromeConfigurator::StepDelay() const { return fast_update_ ? 1 : 1; } -int ChromeConfigurator::MinimumReCheckWait() const { - return fast_update_ ? 30 : (6 * kDelayOneHour); -} - int ChromeConfigurator::OnDemandDelay() const { return fast_update_ ? 2 : (30 * kDelayOneMinute); } int ChromeConfigurator::UpdateDelay() const { - return fast_update_ ? 1 : (15 * kDelayOneMinute); + return fast_update_ ? 10 : (15 * kDelayOneMinute); } std::vector<GURL> ChromeConfigurator::UpdateUrl() const { diff --git a/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc b/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc index 5a7589e..ebc99d8 100644 --- a/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc +++ b/chrome/browser/component_updater/chrome_component_updater_configurator_unittest.cc @@ -31,7 +31,7 @@ TEST(ChromeComponentUpdaterConfiguratorTest, TestFastUpdate) { const auto config(MakeChromeComponentUpdaterConfigurator(cmdline, NULL)); - ASSERT_EQ(1, config->InitialDelay()); + ASSERT_EQ(10, config->InitialDelay()); } TEST(ChromeComponentUpdaterConfiguratorTest, TestOverrideUrl) { diff --git a/chrome/browser/component_updater/component_updater_service_unittest.cc b/chrome/browser/component_updater/component_updater_service_unittest.cc deleted file mode 100644 index 49d86bd..0000000 --- a/chrome/browser/component_updater/component_updater_service_unittest.cc +++ /dev/null @@ -1,1398 +0,0 @@ -// Copyright 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/component_updater/component_updater_service_unittest.h" - -#include <vector> - -#include "base/files/file_util.h" -#include "base/memory/scoped_ptr.h" -#include "base/path_service.h" -#include "base/run_loop.h" -#include "base/strings/string_number_conversions.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -#include "base/values.h" -#include "chrome/browser/component_updater/component_updater_resource_throttle.h" -#include "chrome/common/chrome_paths.h" -#include "components/update_client/test_configurator.h" -#include "components/update_client/test_installer.h" -#include "components/update_client/url_request_post_interceptor.h" -#include "components/update_client/utils.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/resource_controller.h" -#include "content/public/browser/resource_request_info.h" -#include "content/public/browser/resource_throttle.h" -#include "libxml/globals.h" -#include "net/base/upload_bytes_element_reader.h" -#include "net/url_request/test_url_request_interceptor.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_test_util.h" -#include "url/gurl.h" - -using ::testing::_; -using ::testing::AnyNumber; -using ::testing::InSequence; -using ::testing::Mock; - -using content::BrowserThread; - -using std::string; - -using update_client::CrxComponent; -using update_client::PartialMatch; -using update_client::InterceptorFactory; -using update_client::TestConfigurator; -using update_client::TestInstaller; -using update_client::URLRequestPostInterceptor; -using update_client::VersionedTestInstaller; - -using update_client::abag_hash; -using update_client::ihfo_hash; -using update_client::jebg_hash; - -using Events = component_updater::ServiceObserver::Events; -using Status = component_updater::ComponentUpdateService::Status; - -namespace component_updater { - -MockServiceObserver::MockServiceObserver() { -} - -MockServiceObserver::~MockServiceObserver() { -} - -ComponentUpdaterTest::ComponentUpdaterTest() - : post_interceptor_(NULL), - thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP), - test_config_(NULL) { - // The component updater instance under test. - test_config_ = new TestConfigurator( - BrowserThread::GetBlockingPool() - ->GetSequencedTaskRunnerWithShutdownBehavior( - BrowserThread::GetBlockingPool()->GetSequenceToken(), - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN), - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)); - component_updater_.reset( - ComponentUpdateServiceFactory(test_config_).release()); -} - -ComponentUpdaterTest::~ComponentUpdaterTest() { -} - -void ComponentUpdaterTest::SetUp() { - get_interceptor_.reset(new GetInterceptor( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), - BrowserThread::GetBlockingPool()->GetTaskRunnerWithShutdownBehavior( - base::SequencedWorkerPool::SKIP_ON_SHUTDOWN))); - interceptor_factory_.reset(new InterceptorFactory( - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))); - post_interceptor_ = interceptor_factory_->CreateInterceptor(); - EXPECT_TRUE(post_interceptor_); -} - -void ComponentUpdaterTest::TearDown() { - interceptor_factory_.reset(); - get_interceptor_.reset(); - xmlCleanupGlobals(); -} - -ComponentUpdateService* ComponentUpdaterTest::component_updater() { - return component_updater_.get(); -} - -// Makes the full path to a component updater test file. -const base::FilePath ComponentUpdaterTest::test_file(const char* file) { - base::FilePath path; - PathService::Get(base::DIR_SOURCE_ROOT, &path); - return path.AppendASCII("components") - .AppendASCII("test") - .AppendASCII("data") - .AppendASCII("update_client") - .AppendASCII(file); -} - -scoped_refptr<update_client::TestConfigurator> -ComponentUpdaterTest::test_configurator() { - return test_config_; -} - -ComponentUpdateService::Status ComponentUpdaterTest::RegisterComponent( - CrxComponent* com, - TestComponents component, - const Version& version, - const scoped_refptr<TestInstaller>& installer) { - switch (component) { - case kTestComponent_abag: { - com->name = "test_abag"; - com->pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); - break; - } - case kTestComponent_jebg: { - com->name = "test_jebg"; - com->pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); - break; - } - case kTestComponent_ihfo: { - com->name = "test_ihfo"; - com->pk_hash.assign(ihfo_hash, ihfo_hash + arraysize(ihfo_hash)); - break; - } - } - com->version = version; - com->installer = installer; - return component_updater_->RegisterComponent(*com); -} - -void ComponentUpdaterTest::RunThreads() { - base::RunLoop runloop; - test_configurator()->SetQuitClosure(runloop.QuitClosure()); - runloop.Run(); - - // Since some tests need to drain currently enqueued tasks such as network - // intercepts on the IO thread, run the threads until they are - // idle. The component updater service won't loop again until the loop count - // is set and the service is started. - RunThreadsUntilIdle(); -} - -void ComponentUpdaterTest::RunThreadsUntilIdle() { - base::RunLoop().RunUntilIdle(); -} - -ComponentUpdateService::Status OnDemandTester::OnDemand( - ComponentUpdateService* cus, - const std::string& component_id) { - return cus->GetOnDemandUpdater().OnDemandUpdate(component_id); -} - -// Verify that our test fixture work and the component updater can -// be created and destroyed with no side effects. -TEST_F(ComponentUpdaterTest, VerifyFixture) { - EXPECT_TRUE(component_updater() != NULL); -} - -// Verify that the component updater can be caught in a quick -// start-shutdown situation. Failure of this test will be a crash. -TEST_F(ComponentUpdaterTest, StartStop) { - component_updater()->Start(); - RunThreadsUntilIdle(); - component_updater()->Stop(); -} - -// Verify that when the server has no updates, we go back to sleep and -// the COMPONENT_UPDATER_STARTED and COMPONENT_UPDATER_SLEEPING notifications -// are generated. No pings are sent. -TEST_F(ComponentUpdaterTest, CheckCrxSleep) { - MockServiceObserver observer; - - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(2); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(2); - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - component_updater()->AddObserver(&observer); - EXPECT_EQ(Status::kOk, RegisterComponent(&com, kTestComponent_abag, - Version("1.1"), installer)); - - // We loop twice, but there are no updates so we expect two sleep messages. - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - // Expect to see the two update check requests and no other requests, - // including pings. - EXPECT_EQ(2, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[1].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); - - // Loop twice again but this case we simulate a server error by returning - // an empty file. Expect the behavior of the service to be the same as before. - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(2); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(2); - - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_empty"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_empty"))); - - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - EXPECT_EQ(2, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[1].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"1.1\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); -} - -// Verify that we can check for updates and install one component. Besides -// the notifications above COMPONENT_UPDATE_FOUND and COMPONENT_UPDATE_READY -// should have been fired. We do two loops so the second time around there -// should be nothing left to do. -// We also check that the following network requests are issued: -// 1- update check -// 2- download crx -// 3- ping -// 4- second update check. -TEST_F(ComponentUpdaterTest, InstallCrx) { - MockServiceObserver observer; - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, - "jebgalgnebhfojomionfpkfelancnnkf")) - .Times(AnyNumber()); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - get_interceptor_->SetResponse( - GURL(expected_crx_url), - test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); - - component_updater()->AddObserver(&observer); - - scoped_refptr<TestInstaller> installer1(new TestInstaller); - CrxComponent com1; - RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), installer1); - scoped_refptr<TestInstaller> installer2(new TestInstaller); - CrxComponent com2; - RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), installer2); - - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer1->error()); - EXPECT_EQ(1, installer1->install_count()); - EXPECT_EQ(0, installer2->error()); - EXPECT_EQ(0, installer2->install_count()); - - // Expect three request in total: two update checks and one ping. - EXPECT_EQ(3, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(3, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - // Expect one component download. - EXPECT_EQ(1, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"2.2\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"1\"/>")) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">" - "<updatecheck /></app>")); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" version=\"2.2\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - // Test the protocol version is correct and the extra request attributes - // are included in the request. - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find( - "request protocol=\"3.0\" extra=\"foo\"")) - << post_interceptor_->GetRequestsAsString(); - - // Tokenize the request string to look for specific attributes, which - // are important for backward compatibility with the version v2 of the update - // protocol. In this case, inspect the <request>, which is the first element - // after the xml declaration of the update request body. - // Expect to find the |os|, |arch|, |prodchannel|, and |prodversion| - // attributes: - // <?xml version="1.0" encoding="UTF-8"?> - // <request... os=... arch=... prodchannel=... prodversion=...> - // ... - // </request> - const std::string update_request(post_interceptor_->GetRequests()[0]); - std::vector<base::StringPiece> elements; - Tokenize(update_request, "<>", &elements); - EXPECT_NE(string::npos, elements[1].find(" os=")); - EXPECT_NE(string::npos, elements[1].find(" arch=")); - EXPECT_NE(string::npos, elements[1].find(" prodchannel=")); - EXPECT_NE(string::npos, elements[1].find(" prodversion=")); - - // Look for additional attributes of the request, such as |version|, - // |requestid|, |lang|, and |nacl_arch|. - EXPECT_NE(string::npos, elements[1].find(" version=")); - EXPECT_NE(string::npos, elements[1].find(" requestid=")); - EXPECT_NE(string::npos, elements[1].find(" lang=")); - EXPECT_NE(string::npos, elements[1].find(" nacl_arch=")); - - component_updater()->Stop(); -} - -// This test checks that the "prodversionmin" value is handled correctly. In -// particular there should not be an install because the minimum product -// version is much higher than of chrome. -TEST_F(ComponentUpdaterTest, ProdVersionCheck) { - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_2.xml"))); - - get_interceptor_->SetResponse( - GURL(expected_crx_url), - test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), installer); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - // Expect one update check and no ping. - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - // Expect no download to occur. - EXPECT_EQ(0, get_interceptor_->GetHitCount()); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - component_updater()->Stop(); -} - -// Test that a update check due to an on demand call can cause installs. -// Here is the timeline: -// - First loop: we return a reply that indicates no update, so -// nothing happens. -// - We make an on demand call. -// - This triggers a second loop, which has a reply that triggers an install. -#if defined(OS_LINUX) -// http://crbug.com/396488 -#define MAYBE_OnDemandUpdate DISABLED_OnDemandUpdate -#else -#define MAYBE_OnDemandUpdate OnDemandUpdate -#endif -TEST_F(ComponentUpdaterTest, MAYBE_OnDemandUpdate) { - MockServiceObserver observer; - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, - "jebgalgnebhfojomionfpkfelancnnkf")) - .Times(AnyNumber()); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_empty"))); - - get_interceptor_->SetResponse( - GURL(expected_crx_url), - test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); - - component_updater()->AddObserver(&observer); - - scoped_refptr<TestInstaller> installer1(new TestInstaller); - CrxComponent com1; - RegisterComponent(&com1, kTestComponent_abag, Version("2.2"), installer1); - scoped_refptr<TestInstaller> installer2(new TestInstaller); - CrxComponent com2; - RegisterComponent(&com2, kTestComponent_jebg, Version("0.9"), installer2); - - // No update normally. - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - component_updater()->Stop(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_EQ(0, get_interceptor_->GetHitCount()); - - // Update after an on-demand check is issued. - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - - EXPECT_EQ(Status::kOk, OnDemandTester::OnDemand(component_updater(), - GetCrxComponentID(com2))); - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer1->error()); - EXPECT_EQ(0, installer1->install_count()); - EXPECT_EQ(0, installer2->error()); - EXPECT_EQ(1, installer2->install_count()); - - EXPECT_EQ(2, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_EQ(1, get_interceptor_->GetHitCount()); - - // Expect the update check to contain an "ondemand" request for the - // second component (com2) and a normal request for the other component. - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[0].find( - "<app appid=\"abagagagagagagagagagagagagagagag\" " - "version=\"2.2\"><updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" installsource=\"ondemand\"><updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"1\"/>")) - << post_interceptor_->GetRequestsAsString(); - - // Also check what happens if previous check too soon. It works, since this - // direct OnDemand call does not implement a cooldown. - test_configurator()->SetOnDemandTime(60 * 60); - EXPECT_EQ(Status::kOk, OnDemandTester::OnDemand(component_updater(), - GetCrxComponentID(com2))); - // Okay, now reset to 0 for the other tests. - test_configurator()->SetOnDemandTime(0); - component_updater()->Stop(); - - // Test a few error cases. NOTE: We don't have callbacks for - // when the updates failed yet. - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer)); - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - // No update: error from no server response - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_empty"))); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - EXPECT_EQ(Status::kOk, OnDemandTester::OnDemand(component_updater(), - GetCrxComponentID(com2))); - RunThreads(); - component_updater()->Stop(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - // No update: already updated to 1.0 so nothing new - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer)); - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - EXPECT_EQ(Status::kOk, OnDemandTester::OnDemand(component_updater(), - GetCrxComponentID(com2))); - RunThreads(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); -} - -// Verify that a previously registered component can get re-registered -// with a different version. -TEST_F(ComponentUpdaterTest, CheckReRegistration) { - MockServiceObserver observer; - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, - "jebgalgnebhfojomionfpkfelancnnkf")) - .Times(AnyNumber()); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - get_interceptor_->SetResponse( - GURL(expected_crx_url), - test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); - - component_updater()->AddObserver(&observer); - - scoped_refptr<TestInstaller> installer1(new TestInstaller); - CrxComponent com1; - RegisterComponent(&com1, kTestComponent_jebg, Version("0.9"), installer1); - scoped_refptr<TestInstaller> installer2(new TestInstaller); - CrxComponent com2; - RegisterComponent(&com2, kTestComponent_abag, Version("2.2"), installer2); - - // Loop twice to issue two checks: (1) with original 0.9 version, update to - // 1.0, and do the second check (2) with the updated 1.0 version. - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer1->error()); - EXPECT_EQ(1, installer1->install_count()); - EXPECT_EQ(0, installer2->error()); - EXPECT_EQ(0, installer2->install_count()); - - EXPECT_EQ(3, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"1\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"1.0\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); - - // Now re-register, pretending to be an even newer version (2.2) - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer)); - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - scoped_refptr<TestInstaller> installer3(new TestInstaller); - EXPECT_EQ(Status::kReplaced, RegisterComponent(&com1, kTestComponent_jebg, - Version("2.2"), installer3)); - - // Loop once just to notice the check happening with the re-register version. - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - // We created a new installer, so the counts go back to 0. - EXPECT_EQ(0, installer3->error()); - EXPECT_EQ(0, installer3->install_count()); - EXPECT_EQ(0, installer2->error()); - EXPECT_EQ(0, installer2->install_count()); - - // One update check and no additional pings are expected. - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"2.2\">" - "<updatecheck /></app>")); - - component_updater()->Stop(); -} - -// Verify that we can download and install a component and a differential -// update to that component. We do three loops; the final loop should do -// nothing. -// We also check that exactly 5 non-ping network requests are issued: -// 1- update check (response: v1 available) -// 2- download crx (v1) -// 3- update check (response: v2 available) -// 4- download differential crx (v1 to v2) -// 5- update check (response: no further update available) -// There should be two pings, one for each update. The second will bear a -// diffresult=1, while the first will not. -TEST_F(ComponentUpdaterTest, DifferentialUpdate) { - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_2.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_3.xml"))); - - get_interceptor_->SetResponse( - GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx")); - get_interceptor_->SetResponse( - GURL( - "http://localhost/download/" - "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx")); - - scoped_refptr<TestInstaller> installer(new VersionedTestInstaller); - CrxComponent com; - RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), installer); - - test_configurator()->SetLoopCount(3); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(2, installer->install_count()); - - EXPECT_EQ(5, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(5, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"0.0\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, - post_interceptor_->GetRequests()[1].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" " - "version=\"0.0\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"1\" nextfp=\"1\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">" - "<updatecheck /><packages><package fp=\"1\"/></packages></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, - post_interceptor_->GetRequests()[3].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" " - "version=\"1.0\" nextversion=\"2.0\">" - "<event eventtype=\"3\" eventresult=\"1\" diffresult=\"1\" " - "previousfp=\"1\" nextfp=\"22\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[4].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">" - "<updatecheck /><packages><package fp=\"22\"/></packages></app>")) - << post_interceptor_->GetRequestsAsString(); - component_updater()->Stop(); -} - -// Verify that component installation falls back to downloading and installing -// a full update if the differential update fails (in this case, because the -// installer does not know about the existing files). We do two loops; the final -// loop should do nothing. -// We also check that exactly 4 non-ping network requests are issued: -// 1- update check (loop 1) -// 2- download differential crx -// 3- download full crx -// 4- update check (loop 2 - no update available) -// There should be one ping for the first attempted update. -// This test is flaky on Android. crbug.com/329883 -#if defined(OS_ANDROID) -#define MAYBE_DifferentialUpdateFails DISABLED_DifferentialUpdateFails -#else -#define MAYBE_DifferentialUpdateFails DifferentialUpdateFails -#endif -TEST_F(ComponentUpdaterTest, MAYBE_DifferentialUpdateFails) { - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_2.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_3.xml"))); - - get_interceptor_->SetResponse( - GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx")); - get_interceptor_->SetResponse( - GURL( - "http://localhost/download/" - "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx")); - get_interceptor_->SetResponse( - GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx")); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - RegisterComponent(&com, kTestComponent_ihfo, Version("1.0"), installer); - - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - // A failed differential update does not count as a failed install. - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(1, installer->install_count()); - - EXPECT_EQ(3, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(3, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, - post_interceptor_->GetRequests()[1].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" " - "version=\"1.0\" nextversion=\"2.0\">" - "<event eventtype=\"3\" eventresult=\"1\" diffresult=\"0\" " - "differrorcat=\"2\" differrorcode=\"16\" nextfp=\"22\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">" - "<updatecheck /><packages><package fp=\"22\"/></packages></app>")) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); -} - -// Test is flakey on Android bots. See crbug.com/331420. -#if defined(OS_ANDROID) -#define MAYBE_CheckFailedInstallPing DISABLED_CheckFailedInstallPing -#else -#define MAYBE_CheckFailedInstallPing CheckFailedInstallPing -#endif -// Verify that a failed installation causes an install failure ping. -TEST_F(ComponentUpdaterTest, MAYBE_CheckFailedInstallPing) { - // This test installer reports installation failure. - class FailingTestInstaller : public TestInstaller { - bool Install(const base::DictionaryValue& manifest, - const base::FilePath& unpack_path) override { - ++install_count_; - base::DeleteFile(unpack_path, true); - return false; - } - - private: - ~FailingTestInstaller() override {} - }; - scoped_refptr<FailingTestInstaller> installer(new FailingTestInstaller); - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - get_interceptor_->SetResponse( - GURL(expected_crx_url), - test_file("jebgalgnebhfojomionfpkfelancnnkf.crx")); - - // Start with 0.9, and attempt update to 1.0. - // Loop twice to issue two checks: (1) with original 0.9 version - // and (2), which should retry with 0.9. - CrxComponent com; - RegisterComponent(&com, kTestComponent_jebg, Version("0.9"), installer); - - test_configurator()->SetLoopCount(2); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(4, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(2, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[1].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"0\" " - "errorcat=\"3\" errorcode=\"9\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[3].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" " - "version=\"0.9\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"0\" " - "errorcat=\"3\" errorcode=\"9\"/>")) - << post_interceptor_->GetRequestsAsString(); - - // Loop once more, but expect no ping because a noupdate response is issued. - // This is necessary to clear out the fire-and-forget ping from the previous - // iteration. - post_interceptor_->Reset(); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_reply_noupdate.xml"))); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(2, installer->install_count()); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(1, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"jebgalgnebhfojomionfpkfelancnnkf\" version=\"0.9\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - - component_updater()->Stop(); -} - -// Verify that we successfully propagate a patcher error. -// ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx contains an incorrect -// patching instruction that should fail. -TEST_F(ComponentUpdaterTest, DifferentialUpdateFailErrorcode) { - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_1.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_2.xml"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest(new PartialMatch("event"))); - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), - test_file("updatecheck_diff_reply_3.xml"))); - - get_interceptor_->SetResponse( - GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_1.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1.crx")); - // This intercept returns a different file than what is specified in the - // update check response and requested in the download. The file that is - // actually dowloaded contains a patching error, an therefore, an error - // is injected at the time of patching. - get_interceptor_->SetResponse( - GURL( - "http://localhost/download/" - "ihfokbkgjpifnbbojhneepfflplebdkc_1to2.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_1to2_bad.crx")); - get_interceptor_->SetResponse( - GURL("http://localhost/download/ihfokbkgjpifnbbojhneepfflplebdkc_2.crx"), - test_file("ihfokbkgjpifnbbojhneepfflplebdkc_2.crx")); - - scoped_refptr<TestInstaller> installer(new VersionedTestInstaller); - CrxComponent com; - RegisterComponent(&com, kTestComponent_ihfo, Version("0.0"), installer); - - test_configurator()->SetLoopCount(3); - component_updater()->Start(); - RunThreads(); - component_updater()->Stop(); - - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(2, installer->install_count()); - - EXPECT_EQ(5, post_interceptor_->GetHitCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(5, post_interceptor_->GetCount()) - << post_interceptor_->GetRequestsAsString(); - EXPECT_EQ(3, get_interceptor_->GetHitCount()); - - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[0].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"0.0\">" - "<updatecheck /></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, - post_interceptor_->GetRequests()[1].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" " - "version=\"0.0\" nextversion=\"1.0\">" - "<event eventtype=\"3\" eventresult=\"1\" nextfp=\"1\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[2].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"1.0\">" - "<updatecheck /><packages><package fp=\"1\"/></packages></app>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE(string::npos, post_interceptor_->GetRequests()[3].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" " - "version=\"1.0\" nextversion=\"2.0\">" - "<event eventtype=\"3\" eventresult=\"1\" " - "diffresult=\"0\" differrorcat=\"2\" " - "differrorcode=\"14\" diffextracode1=\"305\" " - "previousfp=\"1\" nextfp=\"22\"/>")) - << post_interceptor_->GetRequestsAsString(); - EXPECT_NE( - string::npos, - post_interceptor_->GetRequests()[4].find( - "<app appid=\"ihfokbkgjpifnbbojhneepfflplebdkc\" version=\"2.0\">" - "<updatecheck /><packages><package fp=\"22\"/></packages></app>")) - << post_interceptor_->GetRequestsAsString(); -} - -class TestResourceController : public content::ResourceController { - public: - virtual void SetThrottle(content::ResourceThrottle* throttle) {} -}; - -content::ResourceThrottle* RequestTestResourceThrottle( - ComponentUpdateService* cus, - TestResourceController* controller, - const char* crx_id) { - net::TestURLRequestContext context; - scoped_ptr<net::URLRequest> url_request(context.CreateRequest( - GURL("http://foo.example.com/thing.bin"), net::DEFAULT_PRIORITY, NULL)); - - content::ResourceThrottle* rt = GetOnDemandResourceThrottle(cus, crx_id); - rt->set_controller_for_testing(controller); - controller->SetThrottle(rt); - return rt; -} - -void RequestAndDeleteResourceThrottle(ComponentUpdateService* cus, - const char* crx_id) { - // By requesting a throttle and deleting it immediately we ensure that we - // hit the case where the component updater tries to use the weak - // pointer to a dead Resource throttle. - class NoCallResourceController : public TestResourceController { - public: - ~NoCallResourceController() override {} - void Cancel() override { CHECK(false); } - void CancelAndIgnore() override { CHECK(false); } - void CancelWithError(int error_code) override { CHECK(false); } - void Resume() override { CHECK(false); } - } controller; - - delete RequestTestResourceThrottle(cus, &controller, crx_id); -} - -TEST_F(ComponentUpdaterTest, ResourceThrottleDeletedNoUpdate) { - MockServiceObserver observer; - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - component_updater()->AddObserver(&observer); - EXPECT_EQ(Status::kOk, RegisterComponent(&com, kTestComponent_abag, - Version("1.1"), installer)); - // The following two calls ensure that we don't do an update check via the - // timer, so the only update check should be the on-demand one. - test_configurator()->SetInitialDelay(1000000); - test_configurator()->SetRecheckTime(1000000); - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - - RunThreadsUntilIdle(); - - EXPECT_EQ(0, post_interceptor_->GetHitCount()); - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&RequestAndDeleteResourceThrottle, component_updater(), - "abagagagagagagagagagagagagagagag")); - - RunThreads(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()); - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - component_updater()->Stop(); -} - -class CancelResourceController : public TestResourceController { - public: - CancelResourceController() : throttle_(NULL), resume_called_(0) {} - ~CancelResourceController() override { - // Check that the throttle has been resumed by the time we - // exit the test. - CHECK_EQ(1, resume_called_); - delete throttle_; - } - void Cancel() override { CHECK(false); } - void CancelAndIgnore() override { CHECK(false); } - void CancelWithError(int error_code) override { CHECK(false); } - void Resume() override { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, - base::Bind(&CancelResourceController::ResumeCalled, - base::Unretained(this))); - } - void SetThrottle(content::ResourceThrottle* throttle) override { - throttle_ = throttle; - bool defer = false; - // Initially the throttle is blocked. The CUS needs to run a - // task on the UI thread to decide if it should unblock. - throttle_->WillStartRequest(&defer); - CHECK(defer); - } - - private: - void ResumeCalled() { ++resume_called_; } - - content::ResourceThrottle* throttle_; - int resume_called_; -}; - -// Tests the on-demand update with resource throttle, including the -// cooldown interval between calls. -TEST_F(ComponentUpdaterTest, ResourceThrottleLiveNoUpdate) { - MockServiceObserver observer; - { - InSequence seq; - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")).Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - component_updater()->AddObserver(&observer); - EXPECT_EQ(Status::kOk, RegisterComponent(&com, kTestComponent_abag, - Version("1.1"), installer)); - // The following two calls ensure that we don't do an update check via the - // timer, so the only update check should be the on-demand one. - test_configurator()->SetInitialDelay(1000000); - test_configurator()->SetRecheckTime(1000000); - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - - RunThreadsUntilIdle(); - - EXPECT_EQ(0, post_interceptor_->GetHitCount()); - - { - // First on-demand update check is expected to succeeded. - CancelResourceController controller; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(base::IgnoreResult(&RequestTestResourceThrottle), - component_updater(), &controller, - "abagagagagagagagagagagagagagagag")); - - RunThreads(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()); - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - component_updater()->Stop(); - } - - { - // Second on-demand update check is expected to succeed as well, since there - // is no cooldown interval between calls, due to calling SetOnDemandTime. - test_configurator()->SetOnDemandTime(0); - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - - CancelResourceController controller; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(base::IgnoreResult(&RequestTestResourceThrottle), - component_updater(), &controller, - "abagagagagagagagagagagagagagagag")); - - RunThreads(); - - EXPECT_EQ(1, post_interceptor_->GetHitCount()); - EXPECT_EQ(0, installer->error()); - EXPECT_EQ(0, installer->install_count()); - - component_updater()->Stop(); - } - - { - // This on-demand call is expected not to trigger a component update check. - test_configurator()->SetOnDemandTime(1000000); - component_updater()->Start(); - - CancelResourceController controller; - - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(base::IgnoreResult(&RequestTestResourceThrottle), - component_updater(), &controller, - "abagagagagagagagagagagagagagagag")); - RunThreadsUntilIdle(); - } -} - -// Tests adding and removing observers. -TEST_F(ComponentUpdaterTest, Observer) { - MockServiceObserver observer1, observer2; - - // Expect that two observers see the events. - { - InSequence seq; - EXPECT_CALL(observer1, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer1, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")) - .Times(1); - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")) - .Times(1); - EXPECT_CALL(observer1, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - EXPECT_TRUE(post_interceptor_->ExpectRequest( - new PartialMatch("updatecheck"), test_file("updatecheck_reply_1.xml"))); - - component_updater()->AddObserver(&observer1); - component_updater()->AddObserver(&observer2); - - scoped_refptr<TestInstaller> installer(new TestInstaller); - CrxComponent com; - EXPECT_EQ(Status::kOk, RegisterComponent(&com, kTestComponent_abag, - Version("1.1"), installer)); - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - // After removing the first observer, it's only the second observer that - // gets the events. - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1)); - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer2)); - { - InSequence seq; - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_UPDATER_STARTED, "")) - .Times(1); - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_NOT_UPDATED, - "abagagagagagagagagagagagagagagag")) - .Times(1); - EXPECT_CALL(observer2, OnEvent(Events::COMPONENT_UPDATER_SLEEPING, "")) - .Times(1); - } - - component_updater()->RemoveObserver(&observer1); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - // Both observers are removed and no one gets the events. - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer1)); - EXPECT_TRUE(Mock::VerifyAndClearExpectations(&observer2)); - component_updater()->RemoveObserver(&observer2); - - test_configurator()->SetLoopCount(1); - component_updater()->Start(); - RunThreads(); - - component_updater()->Stop(); -} - -} // namespace component_updater diff --git a/chrome/browser/component_updater/component_updater_service_unittest.h b/chrome/browser/component_updater/component_updater_service_unittest.h deleted file mode 100644 index e0d1c41..0000000 --- a/chrome/browser/component_updater/component_updater_service_unittest.h +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_UNITTEST_H_ -#define CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_UNITTEST_H_ - -#include <string> - -#include "base/compiler_specific.h" -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" -#include "components/component_updater/component_updater_service.h" -#include "content/public/test/test_browser_thread_bundle.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace net { -class LocalHostTestURLRequestInterceptor; -} - -namespace update_client { -struct CrxComponent; -class InterceptorFactory; -class TestConfigurator; -class TestInstaller; -class URLRequestPostInterceptor; -} - -namespace component_updater { - -// Intercepts HTTP GET requests sent to "localhost". -typedef net::LocalHostTestURLRequestInterceptor GetInterceptor; - -class ComponentUpdaterTest : public testing::Test { - public: - enum TestComponents { - kTestComponent_abag, - kTestComponent_jebg, - kTestComponent_ihfo, - }; - - ComponentUpdaterTest(); - - ~ComponentUpdaterTest() override; - - void SetUp() override; - - void TearDown() override; - - ComponentUpdateService* component_updater(); - - // Makes the full path to a component updater test file. - const base::FilePath test_file(const char* file); - - scoped_refptr<update_client::TestConfigurator> test_configurator(); - - ComponentUpdateService::Status RegisterComponent( - update_client::CrxComponent* com, - TestComponents component, - const Version& version, - const scoped_refptr<update_client::TestInstaller>& installer); - - protected: - void RunThreads(); - void RunThreadsUntilIdle(); - - scoped_ptr<update_client::InterceptorFactory> interceptor_factory_; - - // Owned by the factory. - update_client::URLRequestPostInterceptor* post_interceptor_; - - scoped_ptr<GetInterceptor> get_interceptor_; - - private: - content::TestBrowserThreadBundle thread_bundle_; - scoped_refptr<update_client::TestConfigurator> test_config_; - scoped_ptr<ComponentUpdateService> component_updater_; -}; - -const char expected_crx_url[] = - "http://localhost/download/jebgalgnebhfojomionfpkfelancnnkf.crx"; - -class MockServiceObserver : public ServiceObserver { - public: - MockServiceObserver(); - ~MockServiceObserver(); - MOCK_METHOD2(OnEvent, void(Events event, const std::string&)); -}; - -class OnDemandTester { - public: - static ComponentUpdateService::Status OnDemand( - ComponentUpdateService* cus, - const std::string& component_id); -}; - -} // namespace component_updater - -#endif // CHROME_BROWSER_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_UNITTEST_H_ diff --git a/chrome/browser/component_updater/pepper_flash_component_installer.cc b/chrome/browser/component_updater/pepper_flash_component_installer.cc index 80d5715..80138da 100644 --- a/chrome/browser/component_updater/pepper_flash_component_installer.cc +++ b/chrome/browser/component_updater/pepper_flash_component_installer.cc @@ -263,9 +263,8 @@ void FinishPepperFlashUpdateRegistration(ComponentUpdateService* cus, pepflash.installer = new PepperFlashComponentInstaller(version); pepflash.version = version; pepflash.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); - if (cus->RegisterComponent(pepflash) != ComponentUpdateService::Status::kOk) { + if (!cus->RegisterComponent(pepflash)) NOTREACHED() << "Pepper Flash component registration failed."; - } } void StartPepperFlashUpdateRegistration(ComponentUpdateService* cus) { diff --git a/chrome/browser/component_updater/pnacl_component_installer.cc b/chrome/browser/component_updater/pnacl_component_installer.cc index d9da419..4f2c822 100644 --- a/chrome/browser/component_updater/pnacl_component_installer.cc +++ b/chrome/browser/component_updater/pnacl_component_installer.cc @@ -307,12 +307,8 @@ void FinishPnaclUpdateRegistration( pci->set_current_fingerprint(current_fingerprint); CrxComponent pnacl_component = pci->GetCrxComponent(); - ComponentUpdateService::Status status = - pci->cus()->RegisterComponent(pnacl_component); - if (status != ComponentUpdateService::Status::kOk && - status != ComponentUpdateService::Status::kReplaced) { + if (!pci->cus()->RegisterComponent(pnacl_component)) NOTREACHED() << "Pnacl component registration failed."; - } } // Check if there is an existing version on disk first to know when diff --git a/chrome/browser/component_updater/recovery_component_installer.cc b/chrome/browser/component_updater/recovery_component_installer.cc index 0b901e6..3657d64 100644 --- a/chrome/browser/component_updater/recovery_component_installer.cc +++ b/chrome/browser/component_updater/recovery_component_installer.cc @@ -235,7 +235,7 @@ void RecoveryRegisterHelper(ComponentUpdateService* cus, PrefService* prefs) { recovery.installer = new RecoveryComponentInstaller(version, prefs); recovery.version = version; recovery.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); - if (cus->RegisterComponent(recovery) != ComponentUpdateService::Status::kOk) { + if (!cus->RegisterComponent(recovery)) { NOTREACHED() << "Recovery component registration failed."; } } diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer.cc index aa2a2c9..c41c144d 100644 --- a/chrome/browser/component_updater/supervised_user_whitelist_installer.cc +++ b/chrome/browser/component_updater/supervised_user_whitelist_installer.cc @@ -252,16 +252,14 @@ bool SupervisedUserWhitelistInstallerImpl::UnregisterWhitelistInternal( base::ListValue* clients = nullptr; success = whitelist_dict->GetList(kClients, &clients); - bool removed = clients->Remove(base::StringValue(client_id), nullptr); + const bool removed = clients->Remove(base::StringValue(client_id), nullptr); if (!clients->empty()) return removed; pref_dict->RemoveWithoutPathExpansion(crx_id, nullptr); - const ComponentUpdateService::Status status = - cus_->UnregisterComponent(crx_id); - DCHECK_EQ(static_cast<int>(ComponentUpdateService::Status::kOk), - static_cast<int>(status)); + const bool result = cus_->UnregisterComponent(crx_id); + DCHECK(result); return removed; } @@ -433,9 +431,8 @@ std::vector<uint8_t> SupervisedUserWhitelistInstaller::GetHashFromCrxId( void SupervisedUserWhitelistInstaller::TriggerComponentUpdate( OnDemandUpdater* updater, const std::string& crx_id) { - ComponentUpdateService::Status status = updater->OnDemandUpdate(crx_id); - DCHECK_EQ(static_cast<int>(ComponentUpdateService::Status::kOk), - static_cast<int>(status)); + const bool result = updater->OnDemandUpdate(crx_id); + DCHECK(result); } } // namespace component_updater diff --git a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc b/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc index c5e897f..a80e3a0 100644 --- a/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc +++ b/chrome/browser/component_updater/supervised_user_whitelist_installer_unittest.cc @@ -78,44 +78,34 @@ class MockComponentUpdateService : public ComponentUpdateService, void AddObserver(Observer* observer) override { ADD_FAILURE(); } void RemoveObserver(Observer* observer) override { ADD_FAILURE(); } - Status Start() override { - ADD_FAILURE(); - return Status::kError; - } - - Status Stop() override { - ADD_FAILURE(); - return Status::kError; - } - std::vector<std::string> GetComponentIDs() const override { ADD_FAILURE(); return std::vector<std::string>(); } - Status RegisterComponent(const CrxComponent& component) override { + bool RegisterComponent(const CrxComponent& component) override { EXPECT_EQ(nullptr, component_.get()); component_.reset(new CrxComponent(component)); if (!registration_callback_.is_null()) registration_callback_.Run(); - return Status::kOk; + return true; } - Status UnregisterComponent(const std::string& crx_id) override { + bool UnregisterComponent(const std::string& crx_id) override { if (!component_) { ADD_FAILURE(); - return Status::kError; + return false; } EXPECT_EQ(GetCrxComponentID(*component_), crx_id); if (!component_->installer->Uninstall()) { ADD_FAILURE(); - return Status::kError; + return false; } component_.reset(); - return Status::kOk; + return true; } OnDemandUpdater& GetOnDemandUpdater() override { return *this; } @@ -136,16 +126,16 @@ class MockComponentUpdateService : public ComponentUpdateService, } // OnDemandUpdater implementation: - Status OnDemandUpdate(const std::string& crx_id) override { + bool OnDemandUpdate(const std::string& crx_id) override { on_demand_update_called_ = true; if (!component_) { ADD_FAILURE() << "Trying to update unregistered component " << crx_id; - return Status::kError; + return false; } EXPECT_EQ(GetCrxComponentID(*component_), crx_id); - return Status::kOk; + return true; } private: diff --git a/chrome/browser/component_updater/swiftshader_component_installer.cc b/chrome/browser/component_updater/swiftshader_component_installer.cc index 34355ca..1e82d3e 100644 --- a/chrome/browser/component_updater/swiftshader_component_installer.cc +++ b/chrome/browser/component_updater/swiftshader_component_installer.cc @@ -178,8 +178,7 @@ void FinishSwiftShaderUpdateRegistration(ComponentUpdateService* cus, swiftshader.installer = new SwiftShaderComponentInstaller(version); swiftshader.version = version; swiftshader.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]); - if (cus->RegisterComponent(swiftshader) != - ComponentUpdateService::Status::kOk) { + if (!cus->RegisterComponent(swiftshader)) { NOTREACHED() << "SwiftShader component registration fail"; } } diff --git a/chrome/browser/net/crl_set_fetcher.cc b/chrome/browser/net/crl_set_fetcher.cc index 4118952..5c402cd 100644 --- a/chrome/browser/net/crl_set_fetcher.cc +++ b/chrome/browser/net/crl_set_fetcher.cc @@ -151,10 +151,8 @@ void CRLSetFetcher::RegisterComponent(uint32 sequence_of_loaded_crl) { component.version = Version("0"); } - if (cus_->RegisterComponent(component) != - ComponentUpdateService::Status::kOk) { + if (!cus_->RegisterComponent(component)) NOTREACHED() << "RegisterComponent returned error"; - } } void CRLSetFetcher::DoDeleteFromDisk() { diff --git a/chrome/browser/ui/webui/components_ui.cc b/chrome/browser/ui/webui/components_ui.cc index da8d1fb..a929a16 100644 --- a/chrome/browser/ui/webui/components_ui.cc +++ b/chrome/browser/ui/webui/components_ui.cc @@ -189,9 +189,9 @@ base::RefCountedMemory* ComponentsUI::GetFaviconResourceBytes( base::string16 ComponentsUI::ComponentEventToString(Events event) { switch (event) { - case Events::COMPONENT_UPDATER_STARTED: + case Events::COMPONENT_CHECKING_FOR_UPDATES: return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_STARTED); - case Events::COMPONENT_UPDATER_SLEEPING: + case Events::COMPONENT_WAIT: return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_SLEEPING); case Events::COMPONENT_UPDATE_FOUND: return l10n_util::GetStringUTF16(IDS_COMPONENTS_EVT_STATUS_FOUND); diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi index 0648d0c..9c28425 100644 --- a/chrome/chrome_tests_unit.gypi +++ b/chrome/chrome_tests_unit.gypi @@ -62,7 +62,6 @@ 'browser/command_updater_unittest.cc', 'browser/component_updater/chrome_component_updater_configurator_unittest.cc', 'browser/component_updater/cld_component_installer_unittest.cc', - 'browser/component_updater/component_updater_service_unittest.cc', 'browser/component_updater/supervised_user_whitelist_installer_unittest.cc', 'browser/content_settings/content_settings_default_provider_unittest.cc', 'browser/content_settings/content_settings_mock_observer.cc', diff --git a/components/BUILD.gn b/components/BUILD.gn index e57a779..cef6b23 100644 --- a/components/BUILD.gn +++ b/components/BUILD.gn @@ -267,6 +267,7 @@ test("components_unittests") { "//components/bookmarks/managed:unit_tests", "//components/captive_portal:unit_tests", "//components/cloud_devices/common:unit_tests", + "//components/component_updater:unit_tests", "//components/content_settings/core/browser:unit_tests", "//components/content_settings/core/common:unit_tests", "//components/crx_file:unit_tests", diff --git a/components/component_updater.gypi b/components/component_updater.gypi index cfc5a9f..124eb21 100644 --- a/components/component_updater.gypi +++ b/components/component_updater.gypi @@ -22,12 +22,15 @@ 'component_updater/component_updater_paths.h', 'component_updater/component_updater_service.cc', 'component_updater/component_updater_service.h', + 'component_updater/component_updater_service_internal.h', 'component_updater/component_updater_switches.cc', 'component_updater/component_updater_switches.h', 'component_updater/default_component_installer.cc', 'component_updater/default_component_installer.h', 'component_updater/pref_names.cc', 'component_updater/pref_names.h', + 'component_updater/timer.cc', + 'component_updater/timer.h', ], }, ], diff --git a/components/component_updater/BUILD.gn b/components/component_updater/BUILD.gn index fb756a7..46fea6a 100644 --- a/components/component_updater/BUILD.gn +++ b/components/component_updater/BUILD.gn @@ -8,12 +8,15 @@ source_set("component_updater") { "component_updater_paths.h", "component_updater_service.cc", "component_updater_service.h", + "component_updater_service_internal.h", "component_updater_switches.cc", "component_updater_switches.h", "default_component_installer.cc", "default_component_installer.h", "pref_names.cc", "pref_names.h", + "timer.cc", + "timer.h", ] deps = [ @@ -23,3 +26,18 @@ source_set("component_updater") { "//url", ] } + +source_set("unit_tests") { + testonly = true + sources = [ + "component_updater_service_unittest.cc", + "timer_unittest.cc", + ] + + deps = [ + ":component_updater", + "//base", + "//testing/gtest", + "//testing/gmock", + ] +} diff --git a/components/component_updater/component_updater_service.cc b/components/component_updater/component_updater_service.cc index ed49787..29b120e 100644 --- a/components/component_updater/component_updater_service.cc +++ b/components/component_updater/component_updater_service.cc @@ -5,10 +5,11 @@ #include "components/component_updater/component_updater_service.h" #include <algorithm> -#include <set> +#include <map> +#include <string> +#include <utility> #include <vector> -#include "base/at_exit.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/callback.h" @@ -18,1035 +19,339 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_loop_proxy.h" -#include "base/observer_list.h" #include "base/sequenced_task_runner.h" -#include "base/stl_util.h" +#include "base/single_thread_task_runner.h" +#include "base/thread_task_runner_handle.h" #include "base/threading/sequenced_worker_pool.h" #include "base/threading/thread_checker.h" #include "base/timer/timer.h" -#include "components/update_client/component_patcher_operation.h" -#include "components/update_client/component_unpacker.h" +#include "components/component_updater/component_updater_service_internal.h" +#include "components/component_updater/timer.h" #include "components/update_client/configurator.h" -#include "components/update_client/crx_downloader.h" #include "components/update_client/crx_update_item.h" -#include "components/update_client/ping_manager.h" -#include "components/update_client/update_checker.h" #include "components/update_client/update_client.h" -#include "components/update_client/update_response.h" #include "components/update_client/utils.h" #include "url/gurl.h" -using update_client::CrxInstaller; -using update_client::ComponentUnpacker; -using update_client::Configurator; -using update_client::CrxComponent; -using update_client::CrxDownloader; -using update_client::CrxUpdateItem; -using update_client::PingManager; -using update_client::UpdateChecker; -using update_client::UpdateResponse; +using CrxInstaller = update_client::CrxInstaller; +using UpdateClient = update_client::UpdateClient; namespace component_updater { -// The component updater is designed to live until process shutdown, so -// base::Bind() calls are not refcounted. - -namespace { - -// Returns true if the |proposed| version is newer than |current| version. -bool IsVersionNewer(const Version& current, const std::string& proposed) { - Version proposed_ver(proposed); - return proposed_ver.IsValid() && current.CompareTo(proposed_ver) < 0; -} - -// Returns true if a differential update is available, it has not failed yet, -// and the configuration allows it. -bool CanTryDiffUpdate(const CrxUpdateItem* update_item, - const Configurator& config) { - return HasDiffUpdate(update_item) && !update_item->diff_update_failed && - config.DeltasEnabled(); -} - -void AppendDownloadMetrics( - const std::vector<CrxDownloader::DownloadMetrics>& source, - std::vector<CrxDownloader::DownloadMetrics>* destination) { - destination->insert(destination->end(), source.begin(), source.end()); +CrxUpdateService::CrxUpdateService( + const scoped_refptr<Configurator>& config, + const scoped_refptr<UpdateClient>& update_client) + : config_(config), + update_client_(update_client), + blocking_task_runner_(config->GetSequencedTaskRunner()) { + AddObserver(this); } -} // namespace - -////////////////////////////////////////////////////////////////////////////// -// The one and only implementation of the ComponentUpdateService interface. In -// charge of running the show. The main method is ProcessPendingItems() which -// is called periodically to do the upgrades/installs or the update checks. -// An important consideration here is to be as "low impact" as we can to the -// rest of the browser, so even if we have many components registered and -// eligible for update, we only do one thing at a time with pauses in between -// the tasks. Also when we do network requests there is only one |url_fetcher_| -// in flight at a time. -// There are no locks in this code, the main structure |work_items_| is mutated -// only from the main thread. The unpack and installation is done in a blocking -// pool thread. The network requests are done in the IO thread or in the file -// thread. -class CrxUpdateService : public ComponentUpdateService, public OnDemandUpdater { - public: - explicit CrxUpdateService(const scoped_refptr<Configurator>& config); - ~CrxUpdateService() override; - - // Overrides for ComponentUpdateService. - void AddObserver(Observer* observer) override; - void RemoveObserver(Observer* observer) override; - Status Start() override; - Status Stop() override; - Status RegisterComponent(const CrxComponent& component) override; - Status UnregisterComponent(const std::string& crx_id) override; - std::vector<std::string> GetComponentIDs() const override; - OnDemandUpdater& GetOnDemandUpdater() override; - void MaybeThrottle(const std::string& crx_id, - const base::Closure& callback) override; - scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() override; - - // Context for a crx download url request. - struct CRXContext { - scoped_refptr<CrxInstaller> installer; - std::vector<uint8_t> pk_hash; - std::string id; - std::string fingerprint; - CRXContext() : installer(NULL) {} - }; - - private: - enum ErrorCategory { - kErrorNone = 0, - kNetworkError, - kUnpackError, - kInstallError, - }; - - enum StepDelayInterval { - kStepDelayShort = 0, - kStepDelayMedium, - kStepDelayLong, - }; - - // Overrides for ComponentUpdateService. - bool GetComponentDetails(const std::string& component_id, - CrxUpdateItem* item) const override; - - // Overrides for OnDemandUpdater. - Status OnDemandUpdate(const std::string& component_id) override; - - void UpdateCheckComplete(const GURL& original_url, - int error, - const std::string& error_message, - const UpdateResponse::Results& results); - void OnUpdateCheckSucceeded(const UpdateResponse::Results& results); - void OnUpdateCheckFailed(int error, const std::string& error_message); - - void DownloadProgress(const std::string& component_id, - const CrxDownloader::Result& download_result); - - void DownloadComplete(scoped_ptr<CRXContext> crx_context, - const CrxDownloader::Result& download_result); - - Status OnDemandUpdateInternal(CrxUpdateItem* item); - Status OnDemandUpdateWithCooldown(CrxUpdateItem* item); - - void ProcessPendingItems(); - - // Uninstall and remove all unregistered work items. - void UninstallUnregisteredItems(); - - // Find a component that is ready to update. - CrxUpdateItem* FindReadyComponent() const; - - // Prepares the components for an update check and initiates the request. - // Returns true if an update check request has been made. Returns false if - // no update check was needed or an error occured. - bool CheckForUpdates(); - - void UpdateComponent(CrxUpdateItem* workitem); - - void ScheduleNextRun(StepDelayInterval step_delay); - - void ParseResponse(const std::string& xml); - - void Install(scoped_ptr<CRXContext> context, const base::FilePath& crx_path); - - void EndUnpacking(const std::string& component_id, - const base::FilePath& crx_path, - ComponentUnpacker::Error error, - int extended_error); - - void DoneInstalling(const std::string& component_id, - ComponentUnpacker::Error error, - int extended_error); - - void ChangeItemState(CrxUpdateItem* item, CrxUpdateItem::State to); - - size_t ChangeItemStatus(CrxUpdateItem::State from, CrxUpdateItem::State to); - - CrxUpdateItem* FindUpdateItemById(const std::string& id) const; - - void NotifyObservers(Observer::Events event, const std::string& id); - - bool HasOnDemandItems() const; - - Status GetServiceStatus(const CrxUpdateItem::State state); - - scoped_refptr<Configurator> config_; - - scoped_ptr<UpdateChecker> update_checker_; - - scoped_ptr<PingManager> ping_manager_; - - scoped_refptr<ComponentUnpacker> unpacker_; - - scoped_ptr<CrxDownloader> crx_downloader_; - - // A collection of every work item. - typedef std::vector<CrxUpdateItem*> UpdateItems; - UpdateItems work_items_; - - base::OneShotTimer<CrxUpdateService> timer_; - - base::ThreadChecker thread_checker_; - - // Used to post responses back to the main thread. - scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; - - scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; - - bool running_; - - ObserverList<Observer> observer_list_; - - DISALLOW_COPY_AND_ASSIGN(CrxUpdateService); -}; +CrxUpdateService::~CrxUpdateService() { + DCHECK(thread_checker_.CalledOnValidThread()); -////////////////////////////////////////////////////////////////////////////// + for (const auto item : ready_callbacks_) { + item.second.Run(); + } -CrxUpdateService::CrxUpdateService(const scoped_refptr<Configurator>& config) - : config_(config), - ping_manager_(new PingManager(*config)), - main_task_runner_(base::MessageLoopProxy::current()), - blocking_task_runner_(config->GetSequencedTaskRunner()), - running_(false) { -} + RemoveObserver(this); -CrxUpdateService::~CrxUpdateService() { - // Because we are a singleton, at this point only the main thread should be - // alive, this simplifies the management of the work that could be in - // flight in other threads. Stop(); - STLDeleteElements(&work_items_); } void CrxUpdateService::AddObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); - observer_list_.AddObserver(observer); + update_client_->AddObserver(observer); } void CrxUpdateService::RemoveObserver(Observer* observer) { DCHECK(thread_checker_.CalledOnValidThread()); - observer_list_.RemoveObserver(observer); + update_client_->RemoveObserver(observer); } -ComponentUpdateService::Status CrxUpdateService::Start() { - // Note that RegisterComponent will call Start() when the first - // component is registered, so it can be called twice. This way - // we avoid scheduling the timer if there is no work to do. - VLOG(1) << "CrxUpdateService starting up"; - running_ = true; - if (work_items_.empty()) - return Status::kOk; - - NotifyObservers(Observer::Events::COMPONENT_UPDATER_STARTED, ""); - - VLOG(1) << "First update attempt will take place in " - << config_->InitialDelay() << " seconds"; - timer_.Start(FROM_HERE, - base::TimeDelta::FromSeconds(config_->InitialDelay()), - this, - &CrxUpdateService::ProcessPendingItems); - return Status::kOk; +void CrxUpdateService::Start() { + DCHECK(thread_checker_.CalledOnValidThread()); + VLOG(1) << "CrxUpdateService starting up. " + << "First update attempt will take place in " + << config_->InitialDelay() << " seconds. " + << "Next update attempt will take place in " + << config_->NextCheckDelay() << " seconds. "; + + timer_.Start( + base::TimeDelta::FromSeconds(config_->InitialDelay()), + base::TimeDelta::FromSeconds(config_->NextCheckDelay()), + base::Bind(base::IgnoreResult(&CrxUpdateService::CheckForUpdates), + base::Unretained(this))); } -// Stop the main check + update loop. In flight operations will be -// completed. -ComponentUpdateService::Status CrxUpdateService::Stop() { +// Stops the update loop. In flight operations will be completed. +void CrxUpdateService::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); VLOG(1) << "CrxUpdateService stopping"; - running_ = false; timer_.Stop(); - return Status::kOk; -} - -bool CrxUpdateService::HasOnDemandItems() const { - class Helper { - public: - static bool IsOnDemand(CrxUpdateItem* item) { return item->on_demand; } - }; - return std::find_if(work_items_.begin(), - work_items_.end(), - Helper::IsOnDemand) != work_items_.end(); } -// This function sets the timer which will call ProcessPendingItems() or -// ProcessRequestedItem() if there is an on_demand item. There -// are three kinds of waits: -// - a short delay, when there is immediate work to be done. -// - a medium delay, when there are updates to be applied within the current -// update cycle, or there are components that are still unchecked. -// - a long delay when a full check/update cycle has completed for all -// components. -void CrxUpdateService::ScheduleNextRun(StepDelayInterval step_delay) { +// Adds a component to be checked for upgrades. If the component exists it +// it will be replaced. +bool CrxUpdateService::RegisterComponent(const CrxComponent& component) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(!update_checker_); - CHECK(!timer_.IsRunning()); - // It could be the case that Stop() had been called while a url request - // or unpacking was in flight, if so we arrive here but |running_| is - // false. In that case do not loop again. - if (!running_) - return; + if (component.pk_hash.empty() || !component.version.IsValid() || + !component.installer) { + return false; + } - // Keep the delay short if in the middle of an update (step_delay), - // or there are new requested_work_items_ that have not been processed yet. - int64_t delay_seconds = 0; - if (!HasOnDemandItems()) { - switch (step_delay) { - case kStepDelayShort: - delay_seconds = config_->StepDelay(); - break; - case kStepDelayMedium: - delay_seconds = config_->StepDelayMedium(); - break; - case kStepDelayLong: - delay_seconds = config_->NextCheckDelay(); - break; - } - } else { - delay_seconds = config_->StepDelay(); + // Update the registration data if the component has been registered before. + const std::string id(GetCrxComponentID(component)); + auto it = components_.find(id); + if (it != components_.end()) { + it->second = component; + return true; } - if (step_delay != kStepDelayShort) { - NotifyObservers(Observer::Events::COMPONENT_UPDATER_SLEEPING, ""); + components_.insert(std::make_pair(id, component)); + components_order_.push_back(id); - // Zero is only used for unit tests. - if (0 == delay_seconds) - return; - } + // Create an initial state for this component. The state is mutated in + // response to events from the UpdateClient instance. + CrxUpdateItem item; + item.id = id; + item.component = component; + const auto inserted = component_states_.insert(std::make_pair(id, item)); + DCHECK(inserted.second); - VLOG(1) << "Scheduling next run to occur in " << delay_seconds << " seconds"; - timer_.Start(FROM_HERE, - base::TimeDelta::FromSeconds(delay_seconds), - this, - &CrxUpdateService::ProcessPendingItems); -} + // Start the timer if this is the first component registered. The first timer + // event occurs after an interval defined by the component update + // configurator. The subsequent timer events are repeated with a period + // defined by the same configurator. + if (components_.size() == 1) + Start(); -// Given a extension-like component id, find the associated component. -CrxUpdateItem* CrxUpdateService::FindUpdateItemById( - const std::string& id) const { - DCHECK(thread_checker_.CalledOnValidThread()); - CrxUpdateItem::FindById finder(id); - UpdateItems::const_iterator it = - std::find_if(work_items_.begin(), work_items_.end(), finder); - return it != work_items_.end() ? *it : NULL; + return true; } -// Changes a component's state, clearing on_demand and firing notifications as -// necessary. By convention, this is the only function that can change a -// CrxUpdateItem's |state|. -// TODO(waffles): Do we want to add DCHECKS for valid state transitions here? -void CrxUpdateService::ChangeItemState(CrxUpdateItem* item, - CrxUpdateItem::State to) { +bool CrxUpdateService::UnregisterComponent(const std::string& id) { DCHECK(thread_checker_.CalledOnValidThread()); - if (to == CrxUpdateItem::State::kNoUpdate || - to == CrxUpdateItem::State::kUpdated || - to == CrxUpdateItem::State::kUpToDate) { - item->on_demand = false; - } + auto it = components_.find(id); + if (it == components_.end()) + return false; - item->state = to; - - switch (to) { - case CrxUpdateItem::State::kCanUpdate: - NotifyObservers(Observer::Events::COMPONENT_UPDATE_FOUND, item->id); - break; - case CrxUpdateItem::State::kUpdatingDiff: - case CrxUpdateItem::State::kUpdating: - NotifyObservers(Observer::Events::COMPONENT_UPDATE_READY, item->id); - break; - case CrxUpdateItem::State::kUpdated: - NotifyObservers(Observer::Events::COMPONENT_UPDATED, item->id); - break; - case CrxUpdateItem::State::kUpToDate: - case CrxUpdateItem::State::kNoUpdate: - NotifyObservers(Observer::Events::COMPONENT_NOT_UPDATED, item->id); - break; - case CrxUpdateItem::State::kNew: - case CrxUpdateItem::State::kChecking: - case CrxUpdateItem::State::kDownloading: - case CrxUpdateItem::State::kDownloadingDiff: - case CrxUpdateItem::State::kDownloaded: - case CrxUpdateItem::State::kLastStatus: - // No notification for these states. - break; - } + DCHECK_EQ(id, it->first); - // Free possible pending network requests. - if ((to == CrxUpdateItem::State::kUpdated) || - (to == CrxUpdateItem::State::kUpToDate) || - (to == CrxUpdateItem::State::kNoUpdate)) { - for (std::vector<base::Closure>::iterator it = - item->ready_callbacks.begin(); - it != item->ready_callbacks.end(); - ++it) { - it->Run(); - } - item->ready_callbacks.clear(); + // Delay the uninstall of the component if the component is being updated. + if (update_client_->IsUpdating(id)) { + components_pending_unregistration_.push_back(id); + return true; } -} -// Changes all the components in |work_items_| that have |from| state to -// |to| state and returns how many have been changed. -size_t CrxUpdateService::ChangeItemStatus(CrxUpdateItem::State from, - CrxUpdateItem::State to) { - DCHECK(thread_checker_.CalledOnValidThread()); - size_t count = 0; - for (UpdateItems::iterator it = work_items_.begin(); - it != work_items_.end(); - ++it) { - CrxUpdateItem* item = *it; - if (item->state == from) { - ChangeItemState(item, to); - ++count; - } - } - return count; + return DoUnregisterComponent(it->second); } -// Adds a component to be checked for upgrades. If the component exists it -// it will be replaced and the return code is Status::kReplaced. -ComponentUpdateService::Status CrxUpdateService::RegisterComponent( - const CrxComponent& component) { +bool CrxUpdateService::DoUnregisterComponent(const CrxComponent& component) { DCHECK(thread_checker_.CalledOnValidThread()); - if (component.pk_hash.empty() || !component.version.IsValid() || - !component.installer) - return Status::kError; - - std::string id(GetCrxComponentID(component)); - CrxUpdateItem* uit = FindUpdateItemById(id); - if (uit) { - uit->component = component; - uit->unregistered = false; - return Status::kReplaced; - } - uit = new CrxUpdateItem; - uit->id.swap(id); - uit->component = component; - - work_items_.push_back(uit); - - // If this is the first component registered we call Start to - // schedule the first timer. Otherwise, reset the timer to trigger another - // pass over the work items, if the component updater is sleeping, fact - // indicated by a running timer. If the timer is not running, it means that - // the service is busy updating something, and in that case, this component - // will be picked up at the next pass. - if (running_) { - if (work_items_.size() == 1) { - Start(); - } else if (timer_.IsRunning()) { - timer_.Start(FROM_HERE, - base::TimeDelta::FromSeconds(config_->InitialDelay()), - this, - &CrxUpdateService::ProcessPendingItems); - } - } + const auto id = GetCrxComponentID(component); + DCHECK(ready_callbacks_.find(id) == ready_callbacks_.end()); - return Status::kOk; -} + const bool result = component.installer->Uninstall(); -ComponentUpdateService::Status CrxUpdateService::UnregisterComponent( - const std::string& crx_id) { - auto it = std::find_if(work_items_.begin(), work_items_.end(), - CrxUpdateItem::FindById(crx_id)); - if (it == work_items_.end()) - return Status::kError; + const auto pos = + std::find(components_order_.begin(), components_order_.end(), id); + if (pos != components_order_.end()) + components_order_.erase(pos); - (*it)->unregistered = true; + components_.erase(id); + component_states_.erase(id); - ScheduleNextRun(kStepDelayShort); - return Status::kOk; + return result; } std::vector<std::string> CrxUpdateService::GetComponentIDs() const { DCHECK(thread_checker_.CalledOnValidThread()); - std::vector<std::string> component_ids; - for (UpdateItems::const_iterator it = work_items_.begin(); - it != work_items_.end(); - ++it) { - const CrxUpdateItem* item = *it; - component_ids.push_back(item->id); - } - return component_ids; + std::vector<std::string> ids; + for (const auto& it : components_) + ids.push_back(it.first); + return ids; } OnDemandUpdater& CrxUpdateService::GetOnDemandUpdater() { + DCHECK(thread_checker_.CalledOnValidThread()); return *this; } -void CrxUpdateService::MaybeThrottle(const std::string& crx_id, - const base::Closure& callback) { +const CrxComponent* CrxUpdateService::GetComponent( + const std::string& id) const { DCHECK(thread_checker_.CalledOnValidThread()); - // Check if we can on-demand update, else unblock the request anyway. - CrxUpdateItem* item = FindUpdateItemById(crx_id); - Status status = OnDemandUpdateWithCooldown(item); - if (status == Status::kOk || status == Status::kInProgress) { - item->ready_callbacks.push_back(callback); - return; - } - callback.Run(); + const auto it(components_.find(id)); + return it != components_.end() ? &(it->second) : NULL; } -scoped_refptr<base::SequencedTaskRunner> -CrxUpdateService::GetSequencedTaskRunner() { - return blocking_task_runner_; -} - -bool CrxUpdateService::GetComponentDetails(const std::string& component_id, - CrxUpdateItem* item) const { +const CrxUpdateItem* CrxUpdateService::GetComponentState( + const std::string& id) const { DCHECK(thread_checker_.CalledOnValidThread()); - const CrxUpdateItem* crx_update_item(FindUpdateItemById(component_id)); - if (crx_update_item) - *item = *crx_update_item; - return crx_update_item != NULL; + const auto it(component_states_.find(id)); + return it != component_states_.end() ? &it->second : NULL; } -// Start the process of checking for an update, for a particular component -// that was previously registered. -// |component_id| is a value returned from GetCrxComponentID(). -ComponentUpdateService::Status CrxUpdateService::OnDemandUpdate( - const std::string& component_id) { - return OnDemandUpdateInternal(FindUpdateItemById(component_id)); -} - -// This is the main loop of the component updater. It updates one component -// at a time if updates are available. Otherwise, it does an update check or -// takes a long sleep until the loop runs again. -void CrxUpdateService::ProcessPendingItems() { +void CrxUpdateService::MaybeThrottle(const std::string& id, + const base::Closure& callback) { DCHECK(thread_checker_.CalledOnValidThread()); - - CrxUpdateItem* ready_upgrade = FindReadyComponent(); - if (ready_upgrade) { - UpdateComponent(ready_upgrade); - return; - } - - UninstallUnregisteredItems(); - - if (!CheckForUpdates()) - ScheduleNextRun(kStepDelayLong); -} - -void CrxUpdateService::UninstallUnregisteredItems() { - std::vector<CrxUpdateItem*> new_work_items; - for (CrxUpdateItem* item : work_items_) { - scoped_ptr<CrxUpdateItem> owned_item(item); - if (owned_item->unregistered) { - const bool success = owned_item->component.installer->Uninstall(); - DCHECK(success); - } else { - new_work_items.push_back(owned_item.release()); + auto it = components_.find(id); + if (it != components_.end()) { + DCHECK_EQ(it->first, id); + if (OnDemandUpdateWithCooldown(id)) { + ready_callbacks_.insert(std::make_pair(id, callback)); + return; } } - new_work_items.swap(work_items_); -} -CrxUpdateItem* CrxUpdateService::FindReadyComponent() const { - class Helper { - public: - static bool IsReadyOnDemand(CrxUpdateItem* item) { - return item->on_demand && IsReady(item); - } - static bool IsReady(CrxUpdateItem* item) { - return item->state == CrxUpdateItem::State::kCanUpdate; - } - }; - - std::vector<CrxUpdateItem*>::const_iterator it = std::find_if( - work_items_.begin(), work_items_.end(), Helper::IsReadyOnDemand); - if (it != work_items_.end()) - return *it; - it = std::find_if(work_items_.begin(), work_items_.end(), Helper::IsReady); - if (it != work_items_.end()) - return *it; - return NULL; + callback.Run(); // Unblock the request if the request can't be throttled. } -// Prepares the components for an update check and initiates the request. -// On demand components are always included in the update check request. -// Otherwise, only include components that have not been checked recently. -bool CrxUpdateService::CheckForUpdates() { - const base::TimeDelta minimum_recheck_wait_time = - base::TimeDelta::FromSeconds(config_->MinimumReCheckWait()); - const base::Time now(base::Time::Now()); - - std::vector<CrxUpdateItem*> items_to_check; - for (size_t i = 0; i != work_items_.size(); ++i) { - CrxUpdateItem* item = work_items_[i]; - DCHECK(item->state == CrxUpdateItem::State::kNew || - item->state == CrxUpdateItem::State::kNoUpdate || - item->state == CrxUpdateItem::State::kUpToDate || - item->state == CrxUpdateItem::State::kUpdated); - - const base::TimeDelta time_since_last_checked(now - item->last_check); - - if (!item->on_demand && - time_since_last_checked < minimum_recheck_wait_time) { - VLOG(1) << "Skipping check for component update: id=" << item->id - << ", time_since_last_checked=" - << time_since_last_checked.InSeconds() - << " seconds: too soon to check for an update"; - continue; - } - - VLOG(1) << "Scheduling update check for component id=" << item->id - << ", time_since_last_checked=" - << time_since_last_checked.InSeconds() << " seconds"; - - item->last_check = now; - item->crx_urls.clear(); - item->crx_diffurls.clear(); - item->previous_version = item->component.version; - item->next_version = Version(); - item->previous_fp = item->component.fingerprint; - item->next_fp.clear(); - item->diff_update_failed = false; - item->error_category = 0; - item->error_code = 0; - item->extra_code1 = 0; - item->diff_error_category = 0; - item->diff_error_code = 0; - item->diff_extra_code1 = 0; - item->download_metrics.clear(); - - items_to_check.push_back(item); - - ChangeItemState(item, CrxUpdateItem::State::kChecking); - } +bool CrxUpdateService::OnDemandUpdate(const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); - if (items_to_check.empty()) + if (!GetComponent(id)) return false; - update_checker_ = UpdateChecker::Create(*config_).Pass(); - return update_checker_->CheckForUpdates( - items_to_check, - config_->ExtraRequestParams(), - base::Bind(&CrxUpdateService::UpdateCheckComplete, - base::Unretained(this))); + return OnDemandUpdateInternal(id); } -void CrxUpdateService::UpdateComponent(CrxUpdateItem* workitem) { - scoped_ptr<CRXContext> crx_context(new CRXContext); - crx_context->pk_hash = workitem->component.pk_hash; - crx_context->id = workitem->id; - crx_context->installer = workitem->component.installer; - crx_context->fingerprint = workitem->next_fp; - const std::vector<GURL>* urls = NULL; - bool allow_background_download = false; - if (CanTryDiffUpdate(workitem, *config_)) { - urls = &workitem->crx_diffurls; - ChangeItemState(workitem, CrxUpdateItem::State::kDownloadingDiff); - } else { - // Background downloads are enabled only for selected components and - // only for full downloads (see issue 340448). - allow_background_download = workitem->component.allow_background_download; - urls = &workitem->crx_urls; - ChangeItemState(workitem, CrxUpdateItem::State::kDownloading); - } - - // On demand component updates are always downloaded in foreground. - const bool is_background_download = !workitem->on_demand && - allow_background_download && - config_->UseBackgroundDownloader(); - - crx_downloader_.reset( - CrxDownloader::Create(is_background_download, config_->RequestContext(), - blocking_task_runner_, - config_->GetSingleThreadTaskRunner()).release()); - crx_downloader_->set_progress_callback( - base::Bind(&CrxUpdateService::DownloadProgress, - base::Unretained(this), - crx_context->id)); - crx_downloader_->StartDownload(*urls, - base::Bind(&CrxUpdateService::DownloadComplete, - base::Unretained(this), - base::Passed(&crx_context))); -} - -void CrxUpdateService::UpdateCheckComplete( - const GURL& original_url, - int error, - const std::string& error_message, - const UpdateResponse::Results& results) { +bool CrxUpdateService::OnDemandUpdateWithCooldown(const std::string& id) { DCHECK(thread_checker_.CalledOnValidThread()); - VLOG(1) << "Update check completed from: " << original_url.spec(); - update_checker_.reset(); - if (!error) - OnUpdateCheckSucceeded(results); - else - OnUpdateCheckFailed(error, error_message); -} - -// Handles a valid Omaha update check response by matching the results with -// the registered components which were checked for updates. -// If updates are found, prepare the components for the actual version upgrade. -// One of these components will be drafted for the upgrade next time -// ProcessPendingItems is called. -void CrxUpdateService::OnUpdateCheckSucceeded( - const UpdateResponse::Results& results) { - size_t num_updates_pending = 0; - DCHECK(thread_checker_.CalledOnValidThread()); - VLOG(1) << "Update check succeeded."; - std::vector<UpdateResponse::Result>::const_iterator it; - for (it = results.list.begin(); it != results.list.end(); ++it) { - CrxUpdateItem* crx = FindUpdateItemById(it->extension_id); - if (!crx) - continue; - - if (crx->state != CrxUpdateItem::State::kChecking) { - NOTREACHED(); - continue; // Not updating this component now. - } - - if (it->manifest.version.empty()) { - // No version means no update available. - ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); - VLOG(1) << "No update available for component: " << crx->id; - continue; - } - - if (!IsVersionNewer(crx->component.version, it->manifest.version)) { - // The component is up to date. - ChangeItemState(crx, CrxUpdateItem::State::kUpToDate); - VLOG(1) << "Component already up-to-date: " << crx->id; - continue; - } - - if (!it->manifest.browser_min_version.empty()) { - if (IsVersionNewer(config_->GetBrowserVersion(), - it->manifest.browser_min_version)) { - // The component is not compatible with this Chrome version. - VLOG(1) << "Ignoring incompatible component: " << crx->id; - ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); - continue; - } - } - - if (it->manifest.packages.size() != 1) { - // Assume one and only one package per component. - VLOG(1) << "Ignoring multiple packages for component: " << crx->id; - ChangeItemState(crx, CrxUpdateItem::State::kNoUpdate); - continue; - } - - // Parse the members of the result and queue an upgrade for this component. - crx->next_version = Version(it->manifest.version); - - VLOG(1) << "Update found for component: " << crx->id; - - typedef UpdateResponse::Result::Manifest::Package Package; - const Package& package(it->manifest.packages[0]); - crx->next_fp = package.fingerprint; - // Resolve the urls by combining the base urls with the package names. - for (size_t i = 0; i != it->crx_urls.size(); ++i) { - const GURL url(it->crx_urls[i].Resolve(package.name)); - if (url.is_valid()) - crx->crx_urls.push_back(url); - } - for (size_t i = 0; i != it->crx_diffurls.size(); ++i) { - const GURL url(it->crx_diffurls[i].Resolve(package.namediff)); - if (url.is_valid()) - crx->crx_diffurls.push_back(url); - } + DCHECK(GetComponent(id)); - ChangeItemState(crx, CrxUpdateItem::State::kCanUpdate); - ++num_updates_pending; + // Check if the request is too soon. + const auto component_state(GetComponentState(id)); + if (component_state) { + base::TimeDelta delta = base::Time::Now() - component_state->last_check; + if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay())) + return false; } - // All components that are not included in the update response are - // considered up to date. - ChangeItemStatus(CrxUpdateItem::State::kChecking, - CrxUpdateItem::State::kUpToDate); - - // If there are updates pending we do a short wait, otherwise we take - // a longer delay until we check the components again. - ScheduleNextRun(num_updates_pending > 0 ? kStepDelayShort : kStepDelayLong); + return OnDemandUpdateInternal(id); } -void CrxUpdateService::OnUpdateCheckFailed(int error, - const std::string& error_message) { +bool CrxUpdateService::OnDemandUpdateInternal(const std::string& id) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(error); - size_t count = ChangeItemStatus(CrxUpdateItem::State::kChecking, - CrxUpdateItem::State::kNoUpdate); - DCHECK_GT(count, 0ul); - VLOG(1) << "Update check failed."; - ScheduleNextRun(kStepDelayLong); -} -// Called when progress is being made downloading a CRX. The progress may -// not monotonically increase due to how the CRX downloader switches between -// different downloaders and fallback urls. -void CrxUpdateService::DownloadProgress( - const std::string& component_id, - const CrxDownloader::Result& download_result) { - DCHECK(thread_checker_.CalledOnValidThread()); - NotifyObservers(Observer::Events::COMPONENT_UPDATE_DOWNLOADING, component_id); + update_client_->Install( + id, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)), + base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this))); + + return true; } -// Called when the CRX package has been downloaded to a temporary location. -// Here we fire the notifications and schedule the component-specific installer -// to be called in the file thread. -void CrxUpdateService::DownloadComplete( - scoped_ptr<CRXContext> crx_context, - const CrxDownloader::Result& download_result) { +bool CrxUpdateService::CheckForUpdates() { DCHECK(thread_checker_.CalledOnValidThread()); - - CrxUpdateItem* crx = FindUpdateItemById(crx_context->id); - - DCHECK(crx->state == CrxUpdateItem::State::kDownloadingDiff || - crx->state == CrxUpdateItem::State::kDownloading); - - AppendDownloadMetrics(crx_downloader_->download_metrics(), - &crx->download_metrics); - - crx_downloader_.reset(); - - if (download_result.error) { - if (crx->state == CrxUpdateItem::State::kDownloadingDiff) { - crx->diff_error_category = kNetworkError; - crx->diff_error_code = download_result.error; - crx->diff_update_failed = true; - size_t count = ChangeItemStatus(CrxUpdateItem::State::kDownloadingDiff, - CrxUpdateItem::State::kCanUpdate); - DCHECK_EQ(count, 1ul); - - ScheduleNextRun(kStepDelayShort); - return; - } - crx->error_category = kNetworkError; - crx->error_code = download_result.error; - size_t count = ChangeItemStatus(CrxUpdateItem::State::kDownloading, - CrxUpdateItem::State::kNoUpdate); - DCHECK_EQ(count, 1ul); - - // At this point, since both the differential and the full downloads failed, - // the update for this component has finished with an error. - ping_manager_->OnUpdateComplete(crx); - - // Move on to the next update, if there is one available. - ScheduleNextRun(kStepDelayMedium); - } else { - size_t count = 0; - if (crx->state == CrxUpdateItem::State::kDownloadingDiff) { - count = ChangeItemStatus(CrxUpdateItem::State::kDownloadingDiff, - CrxUpdateItem::State::kUpdatingDiff); - } else { - count = ChangeItemStatus(CrxUpdateItem::State::kDownloading, - CrxUpdateItem::State::kUpdating); - } - DCHECK_EQ(count, 1ul); - - // Why unretained? See comment at top of file. - blocking_task_runner_->PostDelayedTask( - FROM_HERE, - base::Bind(&CrxUpdateService::Install, - base::Unretained(this), - base::Passed(&crx_context), - download_result.response), - base::TimeDelta::FromMilliseconds(config_->StepDelay())); + std::vector<std::string> ids; + for (const auto id : components_order_) { + DCHECK(components_.find(id) != components_.end()); + ids.push_back(id); } -} -// Install consists of digital signature verification, unpacking and then -// calling the component specific installer. All that is handled by the -// |unpacker_|. If there is an error this function is in charge of deleting -// the files created. -void CrxUpdateService::Install(scoped_ptr<CRXContext> context, - const base::FilePath& crx_path) { - // This function owns the file at |crx_path| and the |context| object. - unpacker_ = new ComponentUnpacker(context->pk_hash, - crx_path, - context->fingerprint, - context->installer, - config_->CreateOutOfProcessPatcher(), - blocking_task_runner_); - unpacker_->Unpack(base::Bind(&CrxUpdateService::EndUnpacking, - base::Unretained(this), - context->id, - crx_path)); + update_client_->Update( + ids, base::Bind(&CrxUpdateService::OnUpdate, base::Unretained(this)), + base::Bind(&CrxUpdateService::OnUpdateComplete, base::Unretained(this))); + + return true; } -void CrxUpdateService::EndUnpacking(const std::string& component_id, - const base::FilePath& crx_path, - ComponentUnpacker::Error error, - int extended_error) { - if (!update_client::DeleteFileAndEmptyParentDirectory(crx_path)) - NOTREACHED() << crx_path.value(); - main_task_runner_->PostDelayedTask( - FROM_HERE, - base::Bind(&CrxUpdateService::DoneInstalling, - base::Unretained(this), - component_id, - error, - extended_error), - base::TimeDelta::FromMilliseconds(config_->StepDelay())); - // Reset the unpacker last, otherwise we free our own arguments. - unpacker_ = NULL; +scoped_refptr<base::SequencedTaskRunner> +CrxUpdateService::GetSequencedTaskRunner() { + DCHECK(thread_checker_.CalledOnValidThread()); + return blocking_task_runner_; } -// Installation has been completed. Adjust the component state and -// schedule the next check. Schedule a short delay before trying the full -// update when the differential update failed. -void CrxUpdateService::DoneInstalling(const std::string& component_id, - ComponentUnpacker::Error error, - int extra_code) { +bool CrxUpdateService::GetComponentDetails(const std::string& id, + CrxUpdateItem* item) const { DCHECK(thread_checker_.CalledOnValidThread()); - ErrorCategory error_category = kErrorNone; - switch (error) { - case ComponentUnpacker::kNone: - break; - case ComponentUnpacker::kInstallerError: - error_category = kInstallError; - break; - default: - error_category = kUnpackError; - break; + // First, if this component is currently being updated, return its state from + // the update client. + if (update_client_->GetCrxUpdateState(id, item)) + return true; + + // Otherwise, return the last seen state of the component, if such a + // state exists. + const auto component_states_it = component_states_.find(id); + if (component_states_it != component_states_.end()) { + *item = component_states_it->second; + return true; } - const bool is_success = error == ComponentUnpacker::kNone; - - CrxUpdateItem* item = FindUpdateItemById(component_id); + return false; +} - if (item->state == CrxUpdateItem::State::kUpdatingDiff && !is_success) { - item->diff_error_category = error_category; - item->diff_error_code = error; - item->diff_extra_code1 = extra_code; - item->diff_update_failed = true; - size_t count = ChangeItemStatus(CrxUpdateItem::State::kUpdatingDiff, - CrxUpdateItem::State::kCanUpdate); - DCHECK_EQ(count, 1ul); - ScheduleNextRun(kStepDelayShort); - return; - } +void CrxUpdateService::OnUpdate(const std::vector<std::string>& ids, + std::vector<CrxComponent>* components) { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(components->empty()); - if (is_success) { - item->component.version = item->next_version; - item->component.fingerprint = item->next_fp; - ChangeItemState(item, CrxUpdateItem::State::kUpdated); - } else { - item->error_category = error_category; - item->error_code = error; - item->extra_code1 = extra_code; - ChangeItemState(item, CrxUpdateItem::State::kNoUpdate); + for (const auto& id : ids) { + const auto registered_component(GetComponent(id)); + if (registered_component) { + components->push_back(*registered_component); + } } - - ping_manager_->OnUpdateComplete(item); - - // Move on to the next update, if there is one available. - ScheduleNextRun(kStepDelayMedium); } -void CrxUpdateService::NotifyObservers(Observer::Events event, - const std::string& id) { +void CrxUpdateService::OnUpdateComplete(int error) { DCHECK(thread_checker_.CalledOnValidThread()); - FOR_EACH_OBSERVER(Observer, observer_list_, OnEvent(event, id)); -} + VLOG(1) << "Update completed with error " << error; -ComponentUpdateService::Status CrxUpdateService::OnDemandUpdateWithCooldown( - CrxUpdateItem* uit) { - if (!uit) - return Status::kError; - - // Check if the request is too soon. - base::TimeDelta delta = base::Time::Now() - uit->last_check; - if (delta < base::TimeDelta::FromSeconds(config_->OnDemandDelay())) - return Status::kError; - - return OnDemandUpdateInternal(uit); + for (const auto id : components_pending_unregistration_) { + if (!update_client_->IsUpdating(id)) { + const auto component = GetComponent(id); + if (component) + DoUnregisterComponent(*component); + } + } } -ComponentUpdateService::Status CrxUpdateService::OnDemandUpdateInternal( - CrxUpdateItem* uit) { - if (!uit) - return Status::kError; - - uit->on_demand = true; - - // If there is an update available for this item, then continue processing - // the update. This is an artifact of how update checks are done: in addition - // to the on-demand item, the update check may include other items as well. - if (uit->state != CrxUpdateItem::State::kCanUpdate) { - Status service_status = GetServiceStatus(uit->state); - // If the item is already in the process of being updated, there is - // no point in this call, so return Status::kInProgress. - if (service_status == Status::kInProgress) - return service_status; - - // Otherwise the item was already checked a while back (or it is new), - // set its state to kNew to give it a slightly higher priority. - ChangeItemState(uit, CrxUpdateItem::State::kNew); - } +void CrxUpdateService::OnEvent(Events event, const std::string& id) { + DCHECK(thread_checker_.CalledOnValidThread()); - // In case the current delay is long, set the timer to a shorter value - // to get the ball rolling. - if (timer_.IsRunning()) { - timer_.Stop(); - timer_.Start(FROM_HERE, - base::TimeDelta::FromSeconds(config_->StepDelay()), - this, - &CrxUpdateService::ProcessPendingItems); + // Unblock all throttles for the component. + if (event == Observer::Events::COMPONENT_UPDATED || + event == Observer::Events::COMPONENT_NOT_UPDATED) { + auto callbacks = ready_callbacks_.equal_range(id); + for (auto it = callbacks.first; it != callbacks.second; ++it) { + it->second.Run(); + } + ready_callbacks_.erase(id); } - return Status::kOk; -} + CrxUpdateItem update_item; + if (!update_client_->GetCrxUpdateState(id, &update_item)) + return; -ComponentUpdateService::Status CrxUpdateService::GetServiceStatus( - CrxUpdateItem::State state) { - switch (state) { - case CrxUpdateItem::State::kChecking: - case CrxUpdateItem::State::kCanUpdate: - case CrxUpdateItem::State::kDownloadingDiff: - case CrxUpdateItem::State::kDownloading: - case CrxUpdateItem::State::kDownloaded: - case CrxUpdateItem::State::kUpdatingDiff: - case CrxUpdateItem::State::kUpdating: - return Status::kInProgress; - case CrxUpdateItem::State::kNew: - case CrxUpdateItem::State::kUpdated: - case CrxUpdateItem::State::kUpToDate: - case CrxUpdateItem::State::kNoUpdate: - return Status::kOk; - case CrxUpdateItem::State::kLastStatus: - NOTREACHED() << static_cast<int>(state); + // Update the state of the item. + auto it = component_states_.find(id); + DCHECK(it != component_states_.end()); + it->second = update_item; + + // Update the component registration with the new version. + if (event == Observer::Events::COMPONENT_UPDATED) { + auto component(const_cast<CrxComponent*>(GetComponent(id))); + if (component) { + component->version = update_item.next_version; + component->fingerprint = update_item.next_fp; + } } - return Status::kError; } /////////////////////////////////////////////////////////////////////////////// // The component update factory. Using the component updater as a singleton // is the job of the browser process. +// TODO(sorin): consider making this a singleton. scoped_ptr<ComponentUpdateService> ComponentUpdateServiceFactory( const scoped_refptr<Configurator>& config) { DCHECK(config); - return scoped_ptr<ComponentUpdateService>(new CrxUpdateService(config)); + auto update_client = update_client::UpdateClientFactory(config); + return scoped_ptr<ComponentUpdateService>( + new CrxUpdateService(config, update_client.Pass())); } } // namespace component_updater diff --git a/components/component_updater/component_updater_service.h b/components/component_updater/component_updater_service.h index 450508a..e667445 100644 --- a/components/component_updater/component_updater_service.h +++ b/components/component_updater/component_updater_service.h @@ -15,6 +15,7 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/version.h" +#include "components/update_client/update_client.h" #include "url/gurl.h" class ComponentsUI; @@ -46,6 +47,10 @@ namespace component_updater { class OnDemandUpdater; +using Configurator = update_client::Configurator; +using CrxComponent = update_client::CrxComponent; +using CrxUpdateItem = update_client::CrxUpdateItem; + // The component update service is in charge of installing or upgrading // select parts of chrome. Each part is called a component and managed by // instances of CrxComponent registered using RegisterComponent(). On the @@ -63,47 +68,7 @@ class OnDemandUpdater; // All methods are safe to call ONLY from the browser's main thread. class ComponentUpdateService { public: - enum class Status { kOk, kReplaced, kInProgress, kError }; - - // Defines an interface to observe ComponentUpdateService. It provides - // notifications when state changes occur for the service or for the - // registered components. - class Observer { - public: - enum class Events { - // Sent when the component updater starts doing update checks. - COMPONENT_UPDATER_STARTED, - - // Sent when the component updater is going to take a long nap. - COMPONENT_UPDATER_SLEEPING, - - // Sent when there is a new version of a registered component. After - // the notification is sent the component will be downloaded. - COMPONENT_UPDATE_FOUND, - - // Sent when the new component has been downloaded and an installation - // or upgrade is about to be attempted. - COMPONENT_UPDATE_READY, - - // Sent when a component has been successfully updated. - COMPONENT_UPDATED, - - // Sent when a component has not been updated following an update check: - // either there was no update available, or an update failed. - COMPONENT_NOT_UPDATED, - - // Sent when component bytes are being downloaded. - COMPONENT_UPDATE_DOWNLOADING, - }; - - virtual ~Observer() {} - - // The component updater service will call this function when an interesting - // state change happens. If the |id| is specified, then the event is fired - // on behalf of a specific component. The implementors of this interface are - // expected to filter the relevant events based on the component id. - virtual void OnEvent(Events event, const std::string& id) = 0; - }; + using Observer = update_client::UpdateClient::Observer; // Adds an observer for this class. An observer should not be added more // than once. The caller retains the ownership of the observer object. @@ -113,26 +78,20 @@ class ComponentUpdateService { // the observers are being notified. virtual void RemoveObserver(Observer* observer) = 0; - // Start doing update checks and installing new versions of registered - // components after Configurator::InitialDelay() seconds. - virtual Status Start() = 0; - - // Stop doing update checks. In-flight requests and pending installations - // will not be canceled. - virtual Status Stop() = 0; - - // Add component to be checked for updates. You can call this method - // before calling Start(). - virtual Status RegisterComponent( - const update_client::CrxComponent& component) = 0; + // Add component to be checked for updates. + virtual bool RegisterComponent(const CrxComponent& component) = 0; // Unregisters the component with the given ID. This means that the component // is not going to be included in future update checks. If a download or // update operation for the component is currently in progress, it will // silently finish without triggering the next step. // Note that the installer for the component is responsible for removing any - // existing versions of the component from disk. - virtual Status UnregisterComponent(const std::string& crx_id) = 0; + // existing versions of the component from disk. Returns true if the + // uninstall has completed successfully and the component files have been + // removed, or if the uninstalled has been deferred because the component + // is being updated. Returns false if the component id is not known or the + /// uninstall encountered an error. + virtual bool UnregisterComponent(const std::string& id) = 0; // Returns a list of registered components. virtual std::vector<std::string> GetComponentIDs() const = 0; @@ -141,7 +100,7 @@ class ComponentUpdateService { // proactively triggered outside the normal component update service schedule. virtual OnDemandUpdater& GetOnDemandUpdater() = 0; - // This method is used to trigger an on-demand update for component |crx_id|. + // This method is used to trigger an on-demand update for component |id|. // This can be used when loading a resource that depends on this component. // // |callback| is called on the main thread once the on-demand update is @@ -153,7 +112,7 @@ class ComponentUpdateService { // to be defensive against programming bugs, usually triggered by web fetches, // where the on-demand functionality is invoked too often. If this function // is called while still on cooldown, |callback| will be called immediately. - virtual void MaybeThrottle(const std::string& crx_id, + virtual void MaybeThrottle(const std::string& id, const base::Closure& callback) = 0; // Returns a task runner suitable for use by component installers. @@ -164,9 +123,8 @@ class ComponentUpdateService { private: // Returns details about registered component in the |item| parameter. The // function returns true in case of success and false in case of errors. - virtual bool GetComponentDetails( - const std::string& component_id, - update_client::CrxUpdateItem* item) const = 0; + virtual bool GetComponentDetails(const std::string& id, + CrxUpdateItem* item) const = 0; friend class ::ComponentsUI; }; @@ -182,19 +140,20 @@ class OnDemandUpdater { friend class SupervisedUserWhitelistInstaller; friend class ::ComponentsUI; - // Triggers an update check for a component. |component_id| is a value + // Triggers an update check for a component. |id| is a value // returned by GetCrxComponentID(). If an update for this component is already // in progress, the function returns |kInProgress|. If an update is available, // the update will be applied. The caller can subscribe to component update // service notifications to get an indication about the outcome of the // on-demand update. The function does not implement any cooldown interval. - virtual ComponentUpdateService::Status OnDemandUpdate( - const std::string& component_id) = 0; + // TODO(sorin): improve this API so that the result of this non-blocking + // call is provided by a callback. + virtual bool OnDemandUpdate(const std::string& id) = 0; }; // Creates the component updater. scoped_ptr<ComponentUpdateService> ComponentUpdateServiceFactory( - const scoped_refptr<update_client::Configurator>& config); + const scoped_refptr<Configurator>& config); } // namespace component_updater diff --git a/components/component_updater/component_updater_service_internal.h b/components/component_updater/component_updater_service_internal.h new file mode 100644 index 0000000..c91332c --- /dev/null +++ b/components/component_updater/component_updater_service_internal.h @@ -0,0 +1,111 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_INTERNAL_H_ +#define COMPONENTS_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_INTERNAL_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/sequenced_task_runner.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_checker.h" +#include "components/component_updater/timer.h" + +namespace component_updater { + +class OnDemandUpdater; + +using CrxInstaller = update_client::CrxInstaller; +using UpdateClient = update_client::UpdateClient; + +class CrxUpdateService : public ComponentUpdateService, + public ComponentUpdateService::Observer, + public OnDemandUpdater { + using Observer = ComponentUpdateService::Observer; + + public: + CrxUpdateService(const scoped_refptr<Configurator>& config, + const scoped_refptr<UpdateClient>& update_client); + ~CrxUpdateService() override; + + // Overrides for ComponentUpdateService. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + bool RegisterComponent(const CrxComponent& component) override; + bool UnregisterComponent(const std::string& id) override; + std::vector<std::string> GetComponentIDs() const override; + OnDemandUpdater& GetOnDemandUpdater() override; + void MaybeThrottle(const std::string& id, + const base::Closure& callback) override; + scoped_refptr<base::SequencedTaskRunner> GetSequencedTaskRunner() override; + bool OnDemandUpdate(const std::string& id) override; + bool GetComponentDetails(const std::string& id, + CrxUpdateItem* item) const override; + + // Overrides for Observer. + void OnEvent(Events event, const std::string& id) override; + + private: + void Start(); + void Stop(); + + bool CheckForUpdates(); + + bool OnDemandUpdateInternal(const std::string& id); + bool OnDemandUpdateWithCooldown(const std::string& id); + + bool DoUnregisterComponent(const CrxComponent& component); + + const CrxComponent* GetComponent(const std::string& id) const; + + const CrxUpdateItem* GetComponentState(const std::string& id) const; + + void OnUpdate(const std::vector<std::string>& ids, + std::vector<CrxComponent>* components); + void OnUpdateComplete(int error); + + base::ThreadChecker thread_checker_; + + scoped_refptr<Configurator> config_; + + scoped_refptr<UpdateClient> update_client_; + + Timer timer_; + + // A collection of every registered component. + using Components = std::map<std::string, CrxComponent>; + Components components_; + + // Maintains the order in which components have been registered. The position + // of a component id in this sequence indicates the priority of the component. + // The sooner the component gets registered, the higher its priority, and + // the closer this component is to the beginning of the vector. + std::vector<std::string> components_order_; + + // Contains the components pending unregistration. If a component is not + // busy installing or updating, it can be unregistered right away. Otherwise, + // the component will be lazily unregistered after the its operations have + // completed. + std::vector<std::string> components_pending_unregistration_; + + // Contains the active resource throttles associated with a given component. + using ResourceThrottleCallbacks = std::multimap<std::string, base::Closure>; + ResourceThrottleCallbacks ready_callbacks_; + + // Contains the state of the component. + using ComponentStates = std::map<std::string, CrxUpdateItem>; + ComponentStates component_states_; + + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(CrxUpdateService); +}; + +} // namespace component_updater + +#endif // COMPONENTS_COMPONENT_UPDATER_COMPONENT_UPDATER_SERVICE_INTERNAL_H_ diff --git a/components/component_updater/component_updater_service_unittest.cc b/components/component_updater/component_updater_service_unittest.cc new file mode 100644 index 0000000..597135b --- /dev/null +++ b/components/component_updater/component_updater_service_unittest.cc @@ -0,0 +1,355 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <limits> +#include <string> +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/sequenced_worker_pool.h" +#include "base/values.h" +#include "components/component_updater/component_updater_service.h" +#include "components/component_updater/component_updater_service_internal.h" +#include "components/update_client/test_configurator.h" +#include "components/update_client/test_installer.h" +#include "components/update_client/update_client.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using Configurator = update_client::Configurator; +using TestConfigurator = update_client::TestConfigurator; +using UpdateClient = update_client::UpdateClient; + +using ::testing::_; +using ::testing::AnyNumber; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Return; + +namespace component_updater { + +class MockInstaller : public CrxInstaller { + public: + MockInstaller(); + + MOCK_METHOD1(OnUpdateError, void(int error)); + MOCK_METHOD2(Install, + bool(const base::DictionaryValue& manifest, + const base::FilePath& unpack_path)); + MOCK_METHOD2(GetInstalledFile, + bool(const std::string& file, base::FilePath* installed_file)); + MOCK_METHOD0(Uninstall, bool()); + + private: + ~MockInstaller() override; +}; + +class MockUpdateClient : public UpdateClient { + public: + MockUpdateClient(); + MOCK_METHOD1(AddObserver, void(Observer* observer)); + MOCK_METHOD1(RemoveObserver, void(Observer* observer)); + MOCK_METHOD3(Install, + void(const std::string& id, + const CrxDataCallback& crx_data_callback, + const CompletionCallback& completion_callback)); + MOCK_METHOD3(Update, + void(const std::vector<std::string>& ids, + const CrxDataCallback& crx_data_callback, + const CompletionCallback& completion_callback)); + MOCK_CONST_METHOD2(GetCrxUpdateState, + bool(const std::string& id, CrxUpdateItem* update_item)); + MOCK_CONST_METHOD1(IsUpdating, bool(const std::string& id)); + + private: + ~MockUpdateClient() override; +}; + +class MockServiceObserver : public ServiceObserver { + public: + MockServiceObserver(); + ~MockServiceObserver() override; + + MOCK_METHOD2(OnEvent, void(Events event, const std::string&)); +}; + +class ComponentUpdaterTest : public testing::Test { + public: + ComponentUpdaterTest(); + ~ComponentUpdaterTest() override; + + void SetUp() override; + + void TearDown() override; + + // Makes the full path to a component updater test file. + const base::FilePath test_file(const char* file); + + MockUpdateClient& update_client() { return *update_client_; } + ComponentUpdateService& component_updater() { return *component_updater_; } + scoped_refptr<TestConfigurator> configurator() const { return config_; } + base::Closure quit_closure() const { return quit_closure_; } + static void ReadyCallback() {} + + protected: + void RunThreads(); + + private: + static const int kNumWorkerThreads_ = 2; + + base::MessageLoopForUI message_loop_; + base::RunLoop runloop_; + base::Closure quit_closure_; + + scoped_refptr<base::SequencedWorkerPool> worker_pool_; + + scoped_refptr<TestConfigurator> config_; + scoped_refptr<MockUpdateClient> update_client_; + scoped_ptr<ComponentUpdateService> component_updater_; + + DISALLOW_COPY_AND_ASSIGN(ComponentUpdaterTest); +}; + +class OnDemandTester { + public: + static bool OnDemand(ComponentUpdateService* cus, const std::string& id); +}; + +MockInstaller::MockInstaller() { +} + +MockInstaller::~MockInstaller() { +} + +MockUpdateClient::MockUpdateClient() { +} + +MockUpdateClient::~MockUpdateClient() { +} + +MockServiceObserver::MockServiceObserver() { +} + +MockServiceObserver::~MockServiceObserver() { +} + +bool OnDemandTester::OnDemand(ComponentUpdateService* cus, + const std::string& id) { + return cus->GetOnDemandUpdater().OnDemandUpdate(id); +} + +scoped_ptr<ComponentUpdateService> TestComponentUpdateServiceFactory( + const scoped_refptr<Configurator>& config) { + DCHECK(config); + return scoped_ptr<ComponentUpdateService>( + new CrxUpdateService(config, new MockUpdateClient())); +} + +ComponentUpdaterTest::ComponentUpdaterTest() + : worker_pool_(new base::SequencedWorkerPool(kNumWorkerThreads_, "test")) { + quit_closure_ = runloop_.QuitClosure(); + + config_ = new TestConfigurator( + worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()), + message_loop_.task_runner()); + + update_client_ = new MockUpdateClient(); + EXPECT_CALL(update_client(), AddObserver(_)).Times(1); + component_updater_.reset(new CrxUpdateService(config_, update_client_)); +} + +ComponentUpdaterTest::~ComponentUpdaterTest() { + EXPECT_CALL(update_client(), RemoveObserver(_)).Times(1); + worker_pool_->Shutdown(); + component_updater_.reset(); +} + +void ComponentUpdaterTest::SetUp() { +} + +void ComponentUpdaterTest::TearDown() { +} + +void ComponentUpdaterTest::RunThreads() { + runloop_.Run(); +} + +TEST_F(ComponentUpdaterTest, AddObserver) { + MockServiceObserver observer; + EXPECT_CALL(update_client(), AddObserver(&observer)).Times(1); + component_updater().AddObserver(&observer); +} + +TEST_F(ComponentUpdaterTest, RemoveObserver) { + MockServiceObserver observer; + EXPECT_CALL(update_client(), RemoveObserver(&observer)).Times(1); + component_updater().RemoveObserver(&observer); +} + +// Tests that UpdateClient::Update is called by the timer loop when +// components are registered, and the component update starts. +// Also tests that Uninstall is called when a component is unregistered. +TEST_F(ComponentUpdaterTest, RegisterComponent) { + class LoopHandler { + public: + LoopHandler(int max_cnt, const base::Closure& quit_closure) + : max_cnt_(max_cnt), quit_closure_(quit_closure) {} + + void OnUpdate(const std::vector<std::string>& ids, + const UpdateClient::CrxDataCallback& crx_data_callback, + const UpdateClient::CompletionCallback& completion_callback) { + static int cnt = 0; + ++cnt; + if (cnt >= max_cnt_) + quit_closure_.Run(); + } + + private: + const int max_cnt_; + base::Closure quit_closure_; + }; + + scoped_refptr<MockInstaller> installer(new MockInstaller()); + EXPECT_CALL(*installer, Uninstall()).WillOnce(Return(true)); + + using update_client::jebg_hash; + using update_client::abag_hash; + + const std::string id1 = "abagagagagagagagagagagagagagagag"; + const std::string id2 = "jebgalgnebhfojomionfpkfelancnnkf"; + std::vector<std::string> ids; + ids.push_back(id1); + ids.push_back(id2); + + CrxComponent crx_component1; + crx_component1.pk_hash.assign(abag_hash, abag_hash + arraysize(abag_hash)); + crx_component1.version = Version("1.0"); + crx_component1.installer = installer; + + CrxComponent crx_component2; + crx_component2.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx_component2.version = Version("0.9"); + crx_component2.installer = installer; + + // Quit after two update checks have fired. + LoopHandler loop_handler(2, quit_closure()); + EXPECT_CALL(update_client(), Update(ids, _, _)) + .WillRepeatedly(Invoke(&loop_handler, &LoopHandler::OnUpdate)); + + EXPECT_CALL(update_client(), IsUpdating(id1)).Times(1); + + EXPECT_TRUE(component_updater().RegisterComponent(crx_component1)); + EXPECT_TRUE(component_updater().RegisterComponent(crx_component2)); + + RunThreads(); + + EXPECT_TRUE(component_updater().UnregisterComponent(id1)); +} + +// Tests that on-demand updates invoke UpdateClient::Install. +TEST_F(ComponentUpdaterTest, OnDemandUpdate) { + class LoopHandler { + public: + LoopHandler(int max_cnt, const base::Closure& quit_closure) + : max_cnt_(max_cnt), quit_closure_(quit_closure) {} + + void OnInstall( + const std::string& ids, + const UpdateClient::CrxDataCallback& crx_data_callback, + const UpdateClient::CompletionCallback& completion_callback) { + static int cnt = 0; + ++cnt; + if (cnt >= max_cnt_) + quit_closure_.Run(); + } + + private: + const int max_cnt_; + base::Closure quit_closure_; + }; + + auto config = configurator(); + config->SetInitialDelay(3600); + + auto& cus = component_updater(); + + const std::string id = "jebgalgnebhfojomionfpkfelancnnkf"; + EXPECT_FALSE(OnDemandTester::OnDemand(&cus, id)); + + scoped_refptr<MockInstaller> installer(new MockInstaller()); + + using update_client::jebg_hash; + CrxComponent crx_component; + crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx_component.version = Version("0.9"); + crx_component.installer = installer; + + LoopHandler loop_handler(1, quit_closure()); + EXPECT_CALL(update_client(), + Install("jebgalgnebhfojomionfpkfelancnnkf", _, _)) + .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall)); + + EXPECT_TRUE(cus.RegisterComponent(crx_component)); + EXPECT_TRUE(OnDemandTester::OnDemand(&cus, id)); + + RunThreads(); +} + +// Tests that throttling an update invokes UpdateClient::Install. +TEST_F(ComponentUpdaterTest, MaybeThrottle) { + class LoopHandler { + public: + LoopHandler(int max_cnt, const base::Closure& quit_closure) + : max_cnt_(max_cnt), quit_closure_(quit_closure) {} + + void OnInstall( + const std::string& ids, + const UpdateClient::CrxDataCallback& crx_data_callback, + const UpdateClient::CompletionCallback& completion_callback) { + static int cnt = 0; + ++cnt; + if (cnt >= max_cnt_) + quit_closure_.Run(); + } + + private: + const int max_cnt_; + base::Closure quit_closure_; + }; + + auto config = configurator(); + config->SetInitialDelay(3600); + + scoped_refptr<MockInstaller> installer(new MockInstaller()); + + using update_client::jebg_hash; + CrxComponent crx_component; + crx_component.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx_component.version = Version("0.9"); + crx_component.installer = installer; + + LoopHandler loop_handler(1, quit_closure()); + EXPECT_CALL(update_client(), + Install("jebgalgnebhfojomionfpkfelancnnkf", _, _)) + .WillOnce(Invoke(&loop_handler, &LoopHandler::OnInstall)); + + EXPECT_TRUE(component_updater().RegisterComponent(crx_component)); + component_updater().MaybeThrottle( + "jebgalgnebhfojomionfpkfelancnnkf", + base::Bind(&ComponentUpdaterTest::ReadyCallback)); + + RunThreads(); +} + +} // namespace component_updater diff --git a/components/component_updater/default_component_installer.cc b/components/component_updater/default_component_installer.cc index 36844fb..56f5ca1 100644 --- a/components/component_updater/default_component_installer.cc +++ b/components/component_updater/default_component_installer.cc @@ -82,8 +82,6 @@ bool DefaultComponentInstaller::InstallHelper( bool DefaultComponentInstaller::Install(const base::DictionaryValue& manifest, const base::FilePath& unpack_path) { - DCHECK(task_runner_->RunsTasksOnCurrentThread()); - std::string manifest_version; manifest.GetStringASCII("version", &manifest_version); base::Version version(manifest_version.c_str()); @@ -127,6 +125,7 @@ bool DefaultComponentInstaller::GetInstalledFile( } bool DefaultComponentInstaller::Uninstall() { + DCHECK(thread_checker_.CalledOnValidThread()); task_runner_->PostTask( FROM_HERE, base::Bind(&DefaultComponentInstaller::UninstallOnTaskRunner, this)); @@ -248,9 +247,7 @@ void DefaultComponentInstaller::FinishRegistration( crx.version = current_version_; crx.fingerprint = current_fingerprint_; installer_traits_->GetHash(&crx.pk_hash); - ComponentUpdateService::Status status = cus->RegisterComponent(crx); - if (status != ComponentUpdateService::Status::kOk && - status != ComponentUpdateService::Status::kReplaced) { + if (!cus->RegisterComponent(crx)) { NOTREACHED() << "Component registration failed for " << installer_traits_->GetName(); return; diff --git a/components/component_updater/timer.cc b/components/component_updater/timer.cc new file mode 100644 index 0000000..e3c79ec --- /dev/null +++ b/components/component_updater/timer.cc @@ -0,0 +1,47 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/component_updater/timer.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/location.h" + +namespace component_updater { + +Timer::Timer() : timer_(false, false) { +} + +Timer::~Timer() { + DCHECK(thread_checker_.CalledOnValidThread()); + Stop(); +} + +void Timer::Start(base::TimeDelta initial_delay, + base::TimeDelta delay, + const base::Closure& user_task) { + DCHECK(thread_checker_.CalledOnValidThread()); + + delay_ = delay; + user_task_ = user_task; + + timer_.Start(FROM_HERE, initial_delay, + base::Bind(&Timer::OnDelay, base::Unretained(this))); +} + +void Timer::Stop() { + DCHECK(thread_checker_.CalledOnValidThread()); + timer_.Stop(); +} + +void Timer::OnDelay() { + DCHECK(thread_checker_.CalledOnValidThread()); + + user_task_.Run(); + + timer_.Start(FROM_HERE, delay_, + base::Bind(&Timer::OnDelay, base::Unretained(this))); +} + +} // namespace component_updater diff --git a/components/component_updater/timer.h b/components/component_updater/timer.h new file mode 100644 index 0000000..8b72899 --- /dev/null +++ b/components/component_updater/timer.h @@ -0,0 +1,42 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_COMPONENT_UPDATER_TIMER_H_ +#define COMPONENTS_COMPONENT_UPDATER_TIMER_H_ + +#include "base/callback.h" +#include "base/macros.h" +#include "base/threading/thread_checker.h" +#include "base/time/time.h" +#include "base/timer/timer.h" + +namespace component_updater { + +class Timer { + public: + Timer(); + ~Timer(); + + void Start(base::TimeDelta initial_delay, + base::TimeDelta delay, + const base::Closure& user_task); + + void Stop(); + + private: + void OnDelay(); + + base::ThreadChecker thread_checker_; + + base::Timer timer_; + + base::TimeDelta delay_; + base::Closure user_task_; + + DISALLOW_COPY_AND_ASSIGN(Timer); +}; + +} // namespace component_updater + +#endif // COMPONENTS_COMPONENT_UPDATER_TIMER_H_ diff --git a/components/component_updater/timer_unittest.cc b/components/component_updater/timer_unittest.cc new file mode 100644 index 0000000..2c80e84 --- /dev/null +++ b/components/component_updater/timer_unittest.cc @@ -0,0 +1,61 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <string> + +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" +#include "base/time/time.h" +#include "components/component_updater/timer.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace component_updater { + +class ComponentUpdaterTimerTest : public testing::Test { + public: + ComponentUpdaterTimerTest() {} + ~ComponentUpdaterTimerTest() override {} + + private: + base::MessageLoopForUI message_loop_; +}; + +TEST_F(ComponentUpdaterTimerTest, Start) { + class TimerClientFake { + public: + TimerClientFake(int max_count, const base::Closure& quit_closure) + : max_count_(max_count), quit_closure_(quit_closure), count_(0) {} + + void OnTimerEvent() { + ++count_; + if (count_ >= max_count_) + quit_closure_.Run(); + } + + int count() const { return count_; } + + private: + const int max_count_; + const base::Closure quit_closure_; + + int count_; + }; + + base::RunLoop run_loop; + TimerClientFake timer_client_fake(3, run_loop.QuitClosure()); + EXPECT_EQ(0, timer_client_fake.count()); + + Timer timer; + const base::TimeDelta delay(base::TimeDelta::FromMilliseconds(1)); + timer.Start(delay, delay, base::Bind(&TimerClientFake::OnTimerEvent, + base::Unretained(&timer_client_fake))); + run_loop.Run(); + timer.Stop(); + + EXPECT_EQ(3, timer_client_fake.count()); +} + +} // namespace component_updater diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 00d8805..a32b943 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -90,6 +90,10 @@ 'cloud_devices/common/cloud_devices_urls_unittest.cc', 'cloud_devices/common/printer_description_unittest.cc', ], + 'component_updater_unittest_sources': [ + 'component_updater/component_updater_service_unittest.cc', + 'component_updater/timer_unittest.cc', + ], 'content_settings_unittest_sources': [ 'content_settings/core/browser/content_settings_mock_provider.cc', 'content_settings/core/browser/content_settings_mock_provider.h', @@ -662,6 +666,7 @@ '<@(browser_watcher_unittest_sources)', '<@(captive_portal_unittest_sources)', '<@(cloud_devices_unittest_sources)', + '<@(component_updater_unittest_sources)', '<@(content_settings_unittest_sources)', '<@(crash_unittest_sources)', '<@(crx_file_unittest_sources)', @@ -748,6 +753,7 @@ 'components.gyp:bookmarks_test_support', 'components.gyp:captive_portal_test_support', 'components.gyp:cloud_devices_common', + 'components.gyp:component_updater', 'components.gyp:content_settings_core_browser', 'components.gyp:content_settings_core_common', 'components.gyp:content_settings_core_test_support', diff --git a/components/update_client/action.cc b/components/update_client/action.cc index 4a11efe..3cdce79 100644 --- a/components/update_client/action.cc +++ b/components/update_client/action.cc @@ -20,6 +20,8 @@ namespace update_client { +using Events = UpdateClient::Observer::Events; + namespace { // Returns true if a differential update is available, it has not failed yet, diff --git a/components/update_client/action_update.cc b/components/update_client/action_update.cc index 7aefb43..ed912c8 100644 --- a/components/update_client/action_update.cc +++ b/components/update_client/action_update.cc @@ -88,22 +88,22 @@ void ActionUpdate::Run(UpdateContext* update_context, Callback callback) { } void ActionUpdate::DownloadProgress( - const std::string& crx_id, + const std::string& id, const CrxDownloader::Result& download_result) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(crx_id == update_context_->queue.front()); + DCHECK(id == update_context_->queue.front()); using Events = UpdateClient::Observer::Events; - NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING, crx_id); + NotifyObservers(Events::COMPONENT_UPDATE_DOWNLOADING, id); } void ActionUpdate::DownloadComplete( - const std::string& crx_id, + const std::string& id, const CrxDownloader::Result& download_result) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(crx_id == update_context_->queue.front()); + DCHECK(id == update_context_->queue.front()); - CrxUpdateItem* item = FindUpdateItemById(crx_id); + CrxUpdateItem* item = FindUpdateItemById(id); DCHECK(item); AppendDownloadMetrics(update_context_->crx_downloader->download_metrics(), @@ -115,7 +115,7 @@ void ActionUpdate::DownloadComplete( OnDownloadSuccess(item, download_result); update_context_->main_task_runner->PostDelayedTask( FROM_HERE, base::Bind(&ActionUpdate::Install, base::Unretained(this), - crx_id, download_result.response), + id, download_result.response), base::TimeDelta::FromMilliseconds( update_context_->config->StepDelay())); } @@ -123,12 +123,12 @@ void ActionUpdate::DownloadComplete( update_context_->crx_downloader.reset(); } -void ActionUpdate::Install(const std::string& crx_id, +void ActionUpdate::Install(const std::string& id, const base::FilePath& crx_path) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(crx_id == update_context_->queue.front()); + DCHECK(id == update_context_->queue.front()); - CrxUpdateItem* item = FindUpdateItemById(crx_id); + CrxUpdateItem* item = FindUpdateItemById(id); DCHECK(item); OnInstallStart(item); @@ -168,13 +168,13 @@ void ActionUpdate::EndUnpackingOnBlockingTaskRunner( base::TimeDelta::FromMilliseconds(update_context->config->StepDelay())); } -void ActionUpdate::DoneInstalling(const std::string& crx_id, +void ActionUpdate::DoneInstalling(const std::string& id, ComponentUnpacker::Error error, int extended_error) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(crx_id == update_context_->queue.front()); + DCHECK(id == update_context_->queue.front()); - CrxUpdateItem* item = FindUpdateItemById(crx_id); + CrxUpdateItem* item = FindUpdateItemById(id); DCHECK(item); if (error == ComponentUnpacker::kNone) diff --git a/components/update_client/action_update.h b/components/update_client/action_update.h index 8b33444..ddee94b 100644 --- a/components/update_client/action_update.h +++ b/components/update_client/action_update.h @@ -53,14 +53,14 @@ class ActionUpdate : public Action, protected ActionImpl { // Called when progress is being made downloading a CRX. The progress may // not monotonically increase due to how the CRX downloader switches between // different downloaders and fallback urls. - void DownloadProgress(const std::string& crx_id, + void DownloadProgress(const std::string& id, const CrxDownloader::Result& download_result); // Called when the CRX package has been downloaded to a temporary location. - void DownloadComplete(const std::string& crx_id, + void DownloadComplete(const std::string& id, const CrxDownloader::Result& download_result); - void Install(const std::string& crx_id, const base::FilePath& crx_path); + void Install(const std::string& id, const base::FilePath& crx_path); // TODO(sorin): refactor the public interface of ComponentUnpacker so // that these calls can run on the main thread. @@ -74,7 +74,7 @@ class ActionUpdate : public Action, protected ActionImpl { ComponentUnpacker::Error error, int extended_error); - void DoneInstalling(const std::string& crx_id, + void DoneInstalling(const std::string& id, ComponentUnpacker::Error error, int extended_error); diff --git a/components/update_client/action_update_check.cc b/components/update_client/action_update_check.cc index c480de8..d68940e 100644 --- a/components/update_client/action_update_check.cc +++ b/components/update_client/action_update_check.cc @@ -71,6 +71,7 @@ void ActionUpdateCheck::Run(UpdateContext* update_context, Callback callback) { item->next_version = Version(); item->previous_fp = crx_component.fingerprint; item->next_fp.clear(); + item->on_demand = update_context->is_foreground; item->diff_update_failed = false; item->error_category = 0; item->error_code = 0; @@ -80,9 +81,10 @@ void ActionUpdateCheck::Run(UpdateContext* update_context, Callback callback) { item->diff_extra_code1 = 0; item->download_metrics.clear(); - ChangeItemState(item.get(), CrxUpdateItem::State::kChecking); + update_context_->update_items.push_back(item.get()); - update_context_->update_items.push_back(item.release()); + ChangeItemState(item.get(), CrxUpdateItem::State::kChecking); + ignore_result(item.release()); } update_checker_->CheckForUpdates( diff --git a/components/update_client/configurator.h b/components/update_client/configurator.h index c4c763f..4bb103e 100644 --- a/components/update_client/configurator.h +++ b/components/update_client/configurator.h @@ -37,20 +37,11 @@ class Configurator : public base::RefCountedThreadSafe<Configurator> { virtual int InitialDelay() const = 0; // Delay in seconds to every subsequent update check. 0 means don't check. - // This function is a mutator for testing purposes. - virtual int NextCheckDelay() = 0; + virtual int NextCheckDelay() const = 0; // Delay in seconds from each task step. Used to smooth out CPU/IO usage. virtual int StepDelay() const = 0; - // Delay in seconds between applying updates for different components, if - // several updates are available at a given time. This function is a mutator - // for testing purposes. - virtual int StepDelayMedium() = 0; - - // Minimum delta time in seconds before checking again the same component. - virtual int MinimumReCheckWait() const = 0; - // Minimum delta time in seconds before an on-demand check is allowed // for the same component. virtual int OnDemandDelay() const = 0; diff --git a/components/update_client/crx_update_item.h b/components/update_client/crx_update_item.h index f066ca2..a9cf171 100644 --- a/components/update_client/crx_update_item.h +++ b/components/update_client/crx_update_item.h @@ -75,10 +75,6 @@ struct CrxUpdateItem { // enforce conditions or notify observers of the change. State state; - // True if the component was recently unregistered and will be uninstalled - // soon (after the currently operation is finished, if there is one). - bool unregistered; - std::string id; CrxComponent component; @@ -115,8 +111,6 @@ struct CrxUpdateItem { std::vector<CrxDownloader::DownloadMetrics> download_metrics; - std::vector<base::Closure> ready_callbacks; - CrxUpdateItem(); ~CrxUpdateItem(); diff --git a/components/update_client/task.h b/components/update_client/task.h index 51b6890..52af752 100644 --- a/components/update_client/task.h +++ b/components/update_client/task.h @@ -19,10 +19,9 @@ class Task; // together. class Task { public: - using Callback = base::Callback<void(Task* task, int error)>; virtual ~Task() {} - virtual void Run(const Callback& callback) = 0; + virtual void Run() = 0; }; } // namespace update_client diff --git a/components/update_client/task_update.cc b/components/update_client/task_update.cc index 2cbf001..ca8e35f 100644 --- a/components/update_client/task_update.cc +++ b/components/update_client/task_update.cc @@ -13,27 +13,29 @@ namespace update_client { TaskUpdate::TaskUpdate(UpdateEngine* update_engine, + bool is_foreground, const std::vector<std::string>& ids, - const UpdateClient::CrxDataCallback& crx_data_callback) + const UpdateClient::CrxDataCallback& crx_data_callback, + const Callback& callback) : update_engine_(update_engine), + is_foreground_(is_foreground), ids_(ids), - crx_data_callback_(crx_data_callback) { + crx_data_callback_(crx_data_callback), + callback_(callback) { } TaskUpdate::~TaskUpdate() { DCHECK(thread_checker_.CalledOnValidThread()); } -void TaskUpdate::Run(const Callback& callback) { +void TaskUpdate::Run() { DCHECK(thread_checker_.CalledOnValidThread()); - callback_ = callback; - if (ids_.empty()) RunComplete(-1); update_engine_->Update( - ids_, crx_data_callback_, + is_foreground_, ids_, crx_data_callback_, base::Bind(&TaskUpdate::RunComplete, base::Unretained(this))); } diff --git a/components/update_client/task_update.h b/components/update_client/task_update.h index 7a91c28..2cfac4f 100644 --- a/components/update_client/task_update.h +++ b/components/update_client/task_update.h @@ -22,12 +22,23 @@ class UpdateEngine; // Defines a specialized task for updating a group of CRXs. class TaskUpdate : public Task { public: + using Callback = base::Callback<void(Task* task, int error)>; + + // |update_engine| is injected here to handle the task. + // |is_foreground| is true when the update task is initiated by the user, + // most likely as a result of an on-demand call. + // |ids| represents the CRXs to be updated by this task. + // |crx_data_callback| is called to get update data for the these CRXs. + // |callback| is called to return the execution flow back to creator of + // this task when the task is done. TaskUpdate(UpdateEngine* update_engine, + bool is_foreground, const std::vector<std::string>& ids, - const UpdateClient::CrxDataCallback& crx_data_callback); + const UpdateClient::CrxDataCallback& crx_data_callback, + const Callback& callback); ~TaskUpdate() override; - void Run(const Callback& callback) override; + void Run() override; private: // Called when the Run function associated with this task has completed. @@ -37,12 +48,10 @@ class TaskUpdate : public Task { UpdateEngine* update_engine_; // Not owned by this class. + const bool is_foreground_; const std::vector<std::string> ids_; const UpdateClient::CrxDataCallback crx_data_callback_; - - // Called to return the execution flow back to the caller of the - // Run function when this task has completed . - Callback callback_; + const Callback callback_; DISALLOW_COPY_AND_ASSIGN(TaskUpdate); }; diff --git a/components/update_client/test_configurator.cc b/components/update_client/test_configurator.cc index 9cd59e4..a1dc0e5 100644 --- a/components/update_client/test_configurator.cc +++ b/components/update_client/test_configurator.cc @@ -4,7 +4,6 @@ #include "components/update_client/test_configurator.h" -#include "base/run_loop.h" #include "base/version.h" #include "components/update_client/component_patcher_operation.h" #include "url/gurl.h" @@ -27,8 +26,6 @@ TestConfigurator::TestConfigurator( const scoped_refptr<base::SingleThreadTaskRunner>& network_task_runner) : worker_task_runner_(worker_task_runner), initial_time_(0), - times_(1), - recheck_time_(0), ondemand_time_(0), context_(new net::TestURLRequestContextGetter(network_task_runner)) { } @@ -40,15 +37,7 @@ int TestConfigurator::InitialDelay() const { return initial_time_; } -int TestConfigurator::NextCheckDelay() { - // This is called when a new full cycle of checking for updates is going - // to happen. In test we normally only test one cycle so it is a good - // time to break from the test messageloop Run() method so the test can - // finish. - if (--times_ <= 0) { - quit_closure_.Run(); - return 0; - } +int TestConfigurator::NextCheckDelay() const { return 1; } @@ -56,14 +45,6 @@ int TestConfigurator::StepDelay() const { return 0; } -int TestConfigurator::StepDelayMedium() { - return NextCheckDelay(); -} - -int TestConfigurator::MinimumReCheckWait() const { - return recheck_time_; -} - int TestConfigurator::OnDemandDelay() const { return ondemand_time_; } @@ -122,23 +103,10 @@ bool TestConfigurator::UseBackgroundDownloader() const { return false; } -// Set how many update checks are called, the default value is just once. -void TestConfigurator::SetLoopCount(int times) { - times_ = times; -} - -void TestConfigurator::SetRecheckTime(int seconds) { - recheck_time_ = seconds; -} - void TestConfigurator::SetOnDemandTime(int seconds) { ondemand_time_ = seconds; } -void TestConfigurator::SetQuitClosure(const base::Closure& quit_closure) { - quit_closure_ = quit_closure; -} - void TestConfigurator::SetInitialDelay(int seconds) { initial_time_ = seconds; } diff --git a/components/update_client/test_configurator.h b/components/update_client/test_configurator.h index 6e441e7..8d67046 100644 --- a/components/update_client/test_configurator.h +++ b/components/update_client/test_configurator.h @@ -9,8 +9,6 @@ #include <utility> #include <vector> -#include "base/callback.h" -#include "base/compiler_specific.h" #include "base/macros.h" #include "base/memory/ref_counted.h" #include "components/update_client/configurator.h" @@ -58,10 +56,8 @@ class TestConfigurator : public Configurator { // Overrrides for Configurator. int InitialDelay() const override; - int NextCheckDelay() override; + int NextCheckDelay() const override; int StepDelay() const override; - int StepDelayMedium() override; - int MinimumReCheckWait() const override; int OnDemandDelay() const override; int UpdateDelay() const override; std::vector<GURL> UpdateUrl() const override; @@ -81,10 +77,7 @@ class TestConfigurator : public Configurator { scoped_refptr<base::SingleThreadTaskRunner> GetSingleThreadTaskRunner() const override; - void SetLoopCount(int times); - void SetRecheckTime(int seconds); void SetOnDemandTime(int seconds); - void SetQuitClosure(const base::Closure& quit_closure); void SetInitialDelay(int seconds); private: @@ -96,12 +89,9 @@ class TestConfigurator : public Configurator { scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; int initial_time_; - int times_; - int recheck_time_; int ondemand_time_; scoped_refptr<net::TestURLRequestContextGetter> context_; - base::Closure quit_closure_; DISALLOW_COPY_AND_ASSIGN(TestConfigurator); }; diff --git a/components/update_client/update_client.cc b/components/update_client/update_client.cc index ce857e1..55b096e 100644 --- a/components/update_client/update_client.cc +++ b/components/update_client/update_client.cc @@ -38,7 +38,6 @@ namespace update_client { CrxUpdateItem::CrxUpdateItem() : state(State::kNew), - unregistered(false), on_demand(false), diff_update_failed(false), error_category(0), @@ -58,6 +57,12 @@ CrxComponent::CrxComponent() : allow_background_download(true) { CrxComponent::~CrxComponent() { } +// It is important that an instance of the UpdateClient binds an unretained +// pointer to itself. Otherwise, a life time circular dependency between this +// instance and its inner members prevents the destruction of this instance. +// Using unretained references is allowed in this case since the life time of +// the UpdateClient instance exceeds the life time of its inner members, +// including any thread objects that might execute callbacks bound to it. UpdateClientImpl::UpdateClientImpl( const scoped_refptr<Configurator>& config, scoped_ptr<PingManager> ping_manager, @@ -92,11 +97,15 @@ void UpdateClientImpl::Install(const std::string& id, std::vector<std::string> ids; ids.push_back(id); - scoped_ptr<TaskUpdate> task( - new TaskUpdate(update_engine_.get(), ids, crx_data_callback)); + // Partially applies |completion_callback| to OnTaskComplete, so this + // argument is available when the task completes, along with the task itself. + const auto callback = + base::Bind(&UpdateClientImpl::OnTaskComplete, this, completion_callback); + scoped_ptr<TaskUpdate> task(new TaskUpdate(update_engine_.get(), true, ids, + crx_data_callback, callback)); auto it = tasks_.insert(task.release()).first; - RunTask(*it, completion_callback); + RunTask(*it); } void UpdateClientImpl::Update(const std::vector<std::string>& ids, @@ -104,24 +113,23 @@ void UpdateClientImpl::Update(const std::vector<std::string>& ids, const CompletionCallback& completion_callback) { DCHECK(thread_checker_.CalledOnValidThread()); - scoped_ptr<TaskUpdate> task( - new TaskUpdate(update_engine_.get(), ids, crx_data_callback)); + const auto callback = + base::Bind(&UpdateClientImpl::OnTaskComplete, this, completion_callback); + scoped_ptr<TaskUpdate> task(new TaskUpdate(update_engine_.get(), false, ids, + crx_data_callback, callback)); + if (tasks_.empty()) { auto it = tasks_.insert(task.release()).first; - RunTask(*it, completion_callback); + RunTask(*it); } else { task_queue_.push(task.release()); } } -void UpdateClientImpl::RunTask(Task* task, - const CompletionCallback& completion_callback) { +void UpdateClientImpl::RunTask(Task* task) { DCHECK(thread_checker_.CalledOnValidThread()); base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::Bind(&Task::Run, base::Unretained(task), - base::Bind(&UpdateClientImpl::OnTaskComplete, - base::Unretained(this), completion_callback))); + FROM_HERE, base::Bind(&Task::Run, base::Unretained(task))); } void UpdateClientImpl::OnTaskComplete( @@ -138,7 +146,7 @@ void UpdateClientImpl::OnTaskComplete( delete task; if (!task_queue_.empty()) { - RunTask(task_queue_.front(), completion_callback); + RunTask(task_queue_.front()); task_queue_.pop(); } } @@ -168,12 +176,11 @@ bool UpdateClientImpl::IsUpdating(const std::string& id) const { return update_engine_->IsUpdating(id); } -scoped_ptr<UpdateClient> UpdateClientFactory( +scoped_refptr<UpdateClient> UpdateClientFactory( const scoped_refptr<Configurator>& config) { scoped_ptr<PingManager> ping_manager(new PingManager(*config)); - return scoped_ptr<UpdateClient>( - new UpdateClientImpl(config, ping_manager.Pass(), &UpdateChecker::Create, - &CrxDownloader::Create)); + return new UpdateClientImpl(config, ping_manager.Pass(), + &UpdateChecker::Create, &CrxDownloader::Create); } } // namespace update_client diff --git a/components/update_client/update_client.h b/components/update_client/update_client.h index 100bbcc..29b80e0 100644 --- a/components/update_client/update_client.h +++ b/components/update_client/update_client.h @@ -200,8 +200,11 @@ struct CrxComponent { bool allow_background_download; }; -// All methods are safe to call only from the browser's main thread. -class UpdateClient { +// All methods are safe to call only from the browser's main thread. Once an +// instance of this class is created, the reference to it must be released +// only after the thread pools of the browser process have been destroyed and +// the browser process has gone single-threaded. +class UpdateClient : public base::RefCounted<UpdateClient> { public: using CrxDataCallback = base::Callback<void(const std::vector<std::string>& ids, @@ -283,12 +286,14 @@ class UpdateClient { virtual bool IsUpdating(const std::string& id) const = 0; + protected: + friend class base::RefCounted<UpdateClient>; + virtual ~UpdateClient() {} }; // Creates an instance of the update client. -// TODO(sorin): make UpdateClient a ref counted type. -scoped_ptr<UpdateClient> UpdateClientFactory( +scoped_refptr<UpdateClient> UpdateClientFactory( const scoped_refptr<Configurator>& config); } // namespace update_client diff --git a/components/update_client/update_client_internal.h b/components/update_client/update_client_internal.h index 94caf04..0ace882 100644 --- a/components/update_client/update_client_internal.h +++ b/components/update_client/update_client_internal.h @@ -37,7 +37,6 @@ class UpdateClientImpl : public UpdateClient { scoped_ptr<PingManager> ping_manager, UpdateChecker::Factory update_checker_factory, CrxDownloader::Factory crx_downloader_factory); - ~UpdateClientImpl() override; // Overrides for UpdateClient. void AddObserver(Observer* observer) override; @@ -53,7 +52,9 @@ class UpdateClientImpl : public UpdateClient { bool IsUpdating(const std::string& id) const override; private: - void RunTask(Task* task, const CompletionCallback& completion_callback); + ~UpdateClientImpl() override; + + void RunTask(Task* task); void OnTaskComplete(const CompletionCallback& completion_callback, Task* task, int error); diff --git a/components/update_client/update_client_unittest.cc b/components/update_client/update_client_unittest.cc index 52f43fa..bda4008 100644 --- a/components/update_client/update_client_unittest.cc +++ b/components/update_client/update_client_unittest.cc @@ -12,9 +12,8 @@ #include "base/message_loop/message_loop.h" #include "base/path_service.h" #include "base/run_loop.h" -#include "base/test/sequenced_worker_pool_owner.h" #include "base/thread_task_runner_handle.h" -#include "base/threading/thread.h" +#include "base/threading/sequenced_worker_pool.h" #include "base/values.h" #include "base/version.h" #include "components/update_client/crx_update_item.h" @@ -65,6 +64,31 @@ class MockObserver : public UpdateClient::Observer { MOCK_METHOD2(OnEvent, void(Events event, const std::string&)); }; +class OnDemandTester { + public: + OnDemandTester(const scoped_refptr<UpdateClient>& update_client, + bool expected_value); + + void CheckOnDemand(Events event, const std::string&); + + private: + const scoped_refptr<UpdateClient> update_client_; + const bool expected_value_; +}; + +OnDemandTester::OnDemandTester(const scoped_refptr<UpdateClient>& update_client, + bool expected_value) + : update_client_(update_client), expected_value_(expected_value) { +} + +void OnDemandTester::CheckOnDemand(Events event, const std::string& id) { + if (event == Events::COMPONENT_CHECKING_FOR_UPDATES) { + CrxUpdateItem update_item; + EXPECT_TRUE(update_client_->GetCrxUpdateState(id, &update_item)); + EXPECT_EQ(update_item.on_demand, expected_value_); + } +} + class FakePingManagerImpl : public PingManager { public: explicit FakePingManagerImpl(const Configurator& config); @@ -113,6 +137,7 @@ class UpdateClientTest : public testing::Test { protected: void RunThreads(); + void StopWorkerPool(); // Returns the full path to a test file. static base::FilePath TestFilePath(const char* file); @@ -128,8 +153,7 @@ class UpdateClientTest : public testing::Test { base::RunLoop runloop_; base::Closure quit_closure_; - scoped_ptr<base::SequencedWorkerPoolOwner> worker_pool_; - scoped_ptr<base::Thread> thread_; + scoped_refptr<base::SequencedWorkerPool> worker_pool_; scoped_refptr<update_client::Configurator> config_; @@ -137,29 +161,25 @@ class UpdateClientTest : public testing::Test { }; UpdateClientTest::UpdateClientTest() - : worker_pool_( - new base::SequencedWorkerPoolOwner(kNumWorkerThreads_, "test")), - thread_(new base::Thread("test")) { + : worker_pool_(new base::SequencedWorkerPool(kNumWorkerThreads_, "test")) { quit_closure_ = runloop_.QuitClosure(); - thread_->Start(); - - auto pool = worker_pool_->pool(); config_ = new TestConfigurator( - pool->GetSequencedTaskRunner(pool->GetSequenceToken()), - thread_->task_runner()); + worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()), + message_loop_.task_runner()); } UpdateClientTest::~UpdateClientTest() { - config_ = nullptr; - thread_->Stop(); - worker_pool_->pool()->Shutdown(); } void UpdateClientTest::RunThreads() { runloop_.Run(); } +void UpdateClientTest::StopWorkerPool() { + worker_pool_->Shutdown(); +} + base::FilePath UpdateClientTest::TestFilePath(const char* file) { base::FilePath path; PathService::Get(base::DIR_SOURCE_ROOT, &path); @@ -237,11 +257,17 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) { }; scoped_ptr<PingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); + // Verify that calling Update does not set ondemand. + OnDemandTester ondemand_tester(update_client, false); + MockObserver observer; + ON_CALL(observer, OnEvent(_, _)) + .WillByDefault(Invoke(&ondemand_tester, &OnDemandTester::CheckOnDemand)); + InSequence seq; EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); @@ -260,6 +286,8 @@ TEST_F(UpdateClientTest, OneCrxNoUpdate) { RunThreads(); update_client->RemoveObserver(&observer); + + StopWorkerPool(); } // Tests the scenario where two CRXs are checked for updates. On CRX has @@ -403,7 +431,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { }; scoped_ptr<PingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); @@ -442,6 +470,8 @@ TEST_F(UpdateClientTest, TwoCrxUpdateNoUpdate) { RunThreads(); update_client->RemoveObserver(&observer); + + StopWorkerPool(); } // Tests the update check for two CRXs scenario. Both CRXs have updates. @@ -632,7 +662,7 @@ TEST_F(UpdateClientTest, TwoCrxUpdate) { }; scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); @@ -679,6 +709,8 @@ TEST_F(UpdateClientTest, TwoCrxUpdate) { RunThreads(); update_client->RemoveObserver(&observer); + + StopWorkerPool(); } // Tests the differential update scenario for one CRX. @@ -893,7 +925,7 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) { }; scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); @@ -944,6 +976,8 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdate) { } update_client->RemoveObserver(&observer); + + StopWorkerPool(); } // Tests the update scenario for one CRX where the CRX installer returns @@ -1106,7 +1140,7 @@ TEST_F(UpdateClientTest, OneCrxInstallError) { }; scoped_ptr<PingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); @@ -1137,6 +1171,8 @@ TEST_F(UpdateClientTest, OneCrxInstallError) { RunThreads(); update_client->RemoveObserver(&observer); + + StopWorkerPool(); } // Tests the fallback from differential to full update scenario for one CRX. @@ -1367,7 +1403,7 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { }; scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config())); - scoped_ptr<UpdateClient> update_client(new UpdateClientImpl( + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( config(), ping_manager.Pass(), &FakeUpdateChecker::Create, &FakeCrxDownloader::Create)); @@ -1421,6 +1457,289 @@ TEST_F(UpdateClientTest, OneCrxDiffUpdateFailsFullUpdateSucceeds) { } update_client->RemoveObserver(&observer); + + StopWorkerPool(); +} + +// Tests the queuing of update checks. In this scenario, two update checks are +// done for one CRX. The second update check call is queued up and will run +// after the first check has completed. The CRX has no updates. +TEST_F(UpdateClientTest, OneCrxNoUpdateQueuedCall) { + class DataCallbackFake { + public: + static void Callback(const std::vector<std::string>& ids, + std::vector<CrxComponent>* components) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx.version = Version("0.9"); + crx.installer = new TestInstaller; + components->push_back(crx); + } + }; + + class CompletionCallbackFake { + public: + static void Callback(const base::Closure& quit_closure, int error) { + static int num_call = 0; + ++num_call; + + EXPECT_EQ(0, error); + + if (num_call == 2) + quit_closure.Run(); + } + }; + + class FakeUpdateChecker : public UpdateChecker { + public: + static scoped_ptr<UpdateChecker> Create(const Configurator& config) { + return scoped_ptr<UpdateChecker>(new FakeUpdateChecker()); + } + + bool CheckForUpdates( + const std::vector<CrxUpdateItem*>& items_to_check, + const std::string& additional_attributes, + const UpdateCheckCallback& update_check_callback) override { + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", + UpdateResponse::Results())); + return true; + } + }; + + class FakeCrxDownloader : public CrxDownloader { + public: + static scoped_ptr<CrxDownloader> Create( + bool is_background_download, + net::URLRequestContextGetter* context_getter, + const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner, + const scoped_refptr<base::SingleThreadTaskRunner>& + background_task_runner) { + return scoped_ptr<CrxDownloader>(new FakeCrxDownloader()); + } + + private: + FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {} + ~FakeCrxDownloader() override {} + + void DoStartDownload(const GURL& url) override { EXPECT_TRUE(false); } + }; + + class FakePingManager : public FakePingManagerImpl { + public: + explicit FakePingManager(const Configurator& config) + : FakePingManagerImpl(config) {} + ~FakePingManager() override { EXPECT_TRUE(items().empty()); } + }; + + scoped_ptr<PingManager> ping_manager(new FakePingManager(*config())); + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( + config(), ping_manager.Pass(), &FakeUpdateChecker::Create, + &FakeCrxDownloader::Create)); + + MockObserver observer; + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_NOT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + + update_client->AddObserver(&observer); + + std::vector<std::string> ids; + ids.push_back(std::string("jebgalgnebhfojomionfpkfelancnnkf")); + + update_client->Update( + ids, base::Bind(&DataCallbackFake::Callback), + base::Bind(&CompletionCallbackFake::Callback, quit_closure())); + update_client->Update( + ids, base::Bind(&DataCallbackFake::Callback), + base::Bind(&CompletionCallbackFake::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); + + StopWorkerPool(); +} + +// Tests the install of one CRX. +TEST_F(UpdateClientTest, OneCrxInstall) { + class DataCallbackFake { + public: + static void Callback(const std::vector<std::string>& ids, + std::vector<CrxComponent>* components) { + CrxComponent crx; + crx.name = "test_jebg"; + crx.pk_hash.assign(jebg_hash, jebg_hash + arraysize(jebg_hash)); + crx.version = Version("0.0"); + crx.installer = new TestInstaller; + + components->push_back(crx); + } + }; + + class CompletionCallbackFake { + public: + static void Callback(const base::Closure& quit_closure, int error) { + EXPECT_EQ(0, error); + quit_closure.Run(); + } + }; + + class FakeUpdateChecker : public UpdateChecker { + public: + static scoped_ptr<UpdateChecker> Create(const Configurator& config) { + return scoped_ptr<UpdateChecker>(new FakeUpdateChecker()); + } + + bool CheckForUpdates( + const std::vector<CrxUpdateItem*>& items_to_check, + const std::string& additional_attributes, + const UpdateCheckCallback& update_check_callback) override { + /* + Fake the following response: + + <?xml version='1.0' encoding='UTF-8'?> + <response protocol='3.0'> + <app appid='jebgalgnebhfojomionfpkfelancnnkf'> + <updatecheck status='ok'> + <urls> + <url codebase='http://localhost/download/'/> + </urls> + <manifest version='1.0' prodversionmin='11.0.1.0'> + <packages> + <package name='jebgalgnebhfojomionfpkfelancnnkf.crx'/> + </packages> + </manifest> + </updatecheck> + </app> + </response> + */ + UpdateResponse::Result::Manifest::Package package; + package.name = "jebgalgnebhfojomionfpkfelancnnkf.crx"; + + UpdateResponse::Result result; + result.extension_id = "jebgalgnebhfojomionfpkfelancnnkf"; + result.crx_urls.push_back(GURL("http://localhost/download/")); + result.manifest.version = "1.0"; + result.manifest.browser_min_version = "11.0.1.0"; + result.manifest.packages.push_back(package); + + UpdateResponse::Results results; + results.list.push_back(result); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(update_check_callback, GURL(), 0, "", results)); + return true; + } + }; + + class FakeCrxDownloader : public CrxDownloader { + public: + static scoped_ptr<CrxDownloader> Create( + bool is_background_download, + net::URLRequestContextGetter* context_getter, + const scoped_refptr<base::SequencedTaskRunner>& url_fetcher_task_runner, + const scoped_refptr<base::SingleThreadTaskRunner>& + background_task_runner) { + return scoped_ptr<CrxDownloader>(new FakeCrxDownloader()); + } + + private: + FakeCrxDownloader() : CrxDownloader(scoped_ptr<CrxDownloader>().Pass()) {} + ~FakeCrxDownloader() override {} + + void DoStartDownload(const GURL& url) override { + DownloadMetrics download_metrics; + FilePath path; + Result result; + if (url.path() == "/download/jebgalgnebhfojomionfpkfelancnnkf.crx") { + download_metrics.url = url; + download_metrics.downloader = DownloadMetrics::kNone; + download_metrics.error = 0; + download_metrics.downloaded_bytes = 1843; + download_metrics.total_bytes = 1843; + download_metrics.download_time_ms = 1000; + + EXPECT_TRUE(MakeTestFile( + TestFilePath("jebgalgnebhfojomionfpkfelancnnkf.crx"), &path)); + + result.error = 0; + result.response = path; + result.downloaded_bytes = 1843; + result.total_bytes = 1843; + } else { + NOTREACHED(); + } + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::Bind(&FakeCrxDownloader::OnDownloadProgress, + base::Unretained(this), result)); + + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::Bind(&FakeCrxDownloader::OnDownloadComplete, + base::Unretained(this), true, result, download_metrics)); + } + }; + + class FakePingManager : public FakePingManagerImpl { + public: + explicit FakePingManager(const Configurator& config) + : FakePingManagerImpl(config) {} + ~FakePingManager() override { + const auto& ping_items = items(); + EXPECT_EQ(1U, ping_items.size()); + EXPECT_EQ("jebgalgnebhfojomionfpkfelancnnkf", ping_items[0].id); + EXPECT_TRUE(base::Version("0.0").Equals(ping_items[0].previous_version)); + EXPECT_TRUE(base::Version("1.0").Equals(ping_items[0].next_version)); + EXPECT_EQ(0, ping_items[0].error_category); + EXPECT_EQ(0, ping_items[0].error_code); + } + }; + + scoped_ptr<FakePingManager> ping_manager(new FakePingManager(*config())); + scoped_refptr<UpdateClient> update_client(new UpdateClientImpl( + config(), ping_manager.Pass(), &FakeUpdateChecker::Create, + &FakeCrxDownloader::Create)); + + // Verify that calling Install sets ondemand. + OnDemandTester ondemand_tester(update_client, true); + + MockObserver observer; + ON_CALL(observer, OnEvent(_, _)) + .WillByDefault(Invoke(&ondemand_tester, &OnDemandTester::CheckOnDemand)); + + InSequence seq; + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_CHECKING_FOR_UPDATES, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_FOUND, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_DOWNLOADING, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATE_READY, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + EXPECT_CALL(observer, OnEvent(Events::COMPONENT_UPDATED, + "jebgalgnebhfojomionfpkfelancnnkf")).Times(1); + + update_client->AddObserver(&observer); + + update_client->Install( + std::string("jebgalgnebhfojomionfpkfelancnnkf"), + base::Bind(&DataCallbackFake::Callback), + base::Bind(&CompletionCallbackFake::Callback, quit_closure())); + + RunThreads(); + + update_client->RemoveObserver(&observer); + + StopWorkerPool(); } } // namespace update_client diff --git a/components/update_client/update_engine.cc b/components/update_client/update_engine.cc index 4c64fde..d4515d6 100644 --- a/components/update_client/update_engine.cc +++ b/components/update_client/update_engine.cc @@ -20,6 +20,7 @@ namespace update_client { UpdateContext::UpdateContext( const scoped_refptr<Configurator>& config, + bool is_foreground, const std::vector<std::string>& ids, const UpdateClient::CrxDataCallback& crx_data_callback, const UpdateEngine::NotifyObserversCallback& notify_observers_callback, @@ -28,6 +29,7 @@ UpdateContext::UpdateContext( CrxDownloader::Factory crx_downloader_factory, PingManager* ping_manager) : config(config), + is_foreground(is_foreground), ids(ids), crx_data_callback(crx_data_callback), notify_observers_callback(notify_observers_callback), @@ -93,14 +95,16 @@ bool UpdateEngine::GetUpdateState(const std::string& id, } void UpdateEngine::Update( + bool is_foreground, const std::vector<std::string>& ids, const UpdateClient::CrxDataCallback& crx_data_callback, const CompletionCallback& callback) { DCHECK(thread_checker_.CalledOnValidThread()); scoped_ptr<UpdateContext> update_context(new UpdateContext( - config_, ids, crx_data_callback, notify_observers_callback_, callback, - update_checker_factory_, crx_downloader_factory_, ping_manager_)); + config_, is_foreground, ids, crx_data_callback, + notify_observers_callback_, callback, update_checker_factory_, + crx_downloader_factory_, ping_manager_)); CrxUpdateItem update_item; scoped_ptr<ActionUpdateCheck> update_check_action(new ActionUpdateCheck( diff --git a/components/update_client/update_engine.h b/components/update_client/update_engine.h index e902a54..c5457f4 100644 --- a/components/update_client/update_engine.h +++ b/components/update_client/update_engine.h @@ -61,7 +61,8 @@ class UpdateEngine { bool GetUpdateState(const std::string& id, CrxUpdateItem* update_state); - void Update(const std::vector<std::string>& ids, + void Update(bool is_foreground, + const std::vector<std::string>& ids, const UpdateClient::CrxDataCallback& crx_data_callback, const CompletionCallback& update_callback); @@ -91,6 +92,7 @@ class UpdateEngine { struct UpdateContext { UpdateContext( const scoped_refptr<Configurator>& config, + bool is_foreground, const std::vector<std::string>& ids, const UpdateClient::CrxDataCallback& crx_data_callback, const UpdateEngine::NotifyObserversCallback& notify_observers_callback, @@ -103,6 +105,9 @@ struct UpdateContext { scoped_refptr<Configurator> config; + // True if this update has been initiated by the user. + bool is_foreground; + // Contains the ids of all CRXs in this context. const std::vector<std::string> ids; |