// 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 "chrome/browser/chromeos/power/extension_event_observer.h" #include #include #include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "chrome/browser/chromeos/login/users/fake_chrome_user_manager.h" #include "chrome/browser/chromeos/login/users/scoped_user_manager_enabler.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #include "chrome/common/extensions/api/gcm.h" #include "chrome/test/base/testing_browser_process.h" #include "chrome/test/base/testing_profile.h" #include "chrome/test/base/testing_profile_manager.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/fake_power_manager_client.h" #include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_renderer_host.h" #include "extensions/browser/extension_host.h" #include "extensions/browser/extension_host_observer.h" #include "extensions/browser/process_manager.h" #include "extensions/common/extension.h" #include "extensions/common/extension_builder.h" #include "extensions/common/manifest_handlers/background_info.h" #include "extensions/common/value_builder.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/test/test_screen.h" #include "ui/gfx/screen.h" namespace chromeos { class ExtensionEventObserverTest : public ::testing::Test { public: ExtensionEventObserverTest() : power_manager_client_(new FakePowerManagerClient()), test_screen_(aura::TestScreen::Create(gfx::Size())), fake_user_manager_(new FakeChromeUserManager()), scoped_user_manager_enabler_(fake_user_manager_) { DBusThreadManager::GetSetterForTesting()->SetPowerManagerClient( make_scoped_ptr(power_manager_client_)); profile_manager_.reset( new TestingProfileManager(TestingBrowserProcess::GetGlobal())); extension_event_observer_.reset(new ExtensionEventObserver()); test_api_ = extension_event_observer_->CreateTestApi(); } ~ExtensionEventObserverTest() override { extension_event_observer_.reset(); profile_manager_.reset(); DBusThreadManager::Shutdown(); } // ::testing::Test overrides. void SetUp() override { ::testing::Test::SetUp(); gfx::Screen::SetScreenInstance(test_screen_.get()); // Must be called from ::testing::Test::SetUp. ASSERT_TRUE(profile_manager_->SetUp()); const char kUserProfile[] = "profile1@example.com"; const AccountId account_id(AccountId::FromUserEmail(kUserProfile)); fake_user_manager_->AddUser(account_id); fake_user_manager_->LoginUser(account_id); profile_ = profile_manager_->CreateTestingProfile(account_id.GetUserEmail()); profile_manager_->SetLoggedIn(true); } void TearDown() override { profile_ = NULL; profile_manager_->DeleteAllTestingProfiles(); gfx::Screen::SetScreenInstance(nullptr); ::testing::Test::TearDown(); } protected: scoped_refptr CreateApp(const std::string& name, bool uses_gcm) { scoped_refptr app = extensions::ExtensionBuilder() .SetManifest(std::move( extensions::DictionaryBuilder() .Set("name", name) .Set("version", "1.0.0") .Set("manifest_version", 2) .Set("app", std::move(extensions::DictionaryBuilder().Set( "background", std::move(extensions::DictionaryBuilder().Set( "scripts", std::move(extensions::ListBuilder().Append( "background.js"))))))) .Set("permissions", std::move(extensions::ListBuilder().Append( uses_gcm ? "gcm" : ""))))) .Build(); created_apps_.push_back(app); return app; } extensions::ExtensionHost* CreateHostForApp(Profile* profile, extensions::Extension* app) { extensions::ProcessManager::Get(profile)->CreateBackgroundHost( app, extensions::BackgroundInfo::GetBackgroundURL(app)); base::RunLoop().RunUntilIdle(); return extensions::ProcessManager::Get(profile) ->GetBackgroundHostForExtension(app->id()); } // Owned by DBusThreadManager. FakePowerManagerClient* power_manager_client_; scoped_ptr extension_event_observer_; scoped_ptr test_api_; // Owned by |profile_manager_|. TestingProfile* profile_; scoped_ptr profile_manager_; private: scoped_ptr test_screen_; content::TestBrowserThreadBundle browser_thread_bundle_; // Needed to ensure we don't end up creating actual RenderViewHosts // and RenderProcessHosts. content::RenderViewHostTestEnabler render_view_host_test_enabler_; // Chrome OS needs extra services to run in the following order. ScopedTestDeviceSettingsService test_device_settings_service_; ScopedTestCrosSettings test_cros_settings_; // Owned by |scoped_user_manager_enabler_|. FakeChromeUserManager* fake_user_manager_; ScopedUserManagerEnabler scoped_user_manager_enabler_; std::vector> created_apps_; DISALLOW_COPY_AND_ASSIGN(ExtensionEventObserverTest); }; // Tests that the ExtensionEventObserver reports readiness for suspend when // there is nothing interesting going on. TEST_F(ExtensionEventObserverTest, BasicSuspendAndDarkSuspend) { power_manager_client_->SendSuspendImminent(); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); power_manager_client_->SendDarkSuspendImminent(); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } // Tests that the ExtensionEventObserver properly handles a canceled suspend // attempt. TEST_F(ExtensionEventObserverTest, CanceledSuspend) { power_manager_client_->SendSuspendImminent(); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); power_manager_client_->SendSuspendDone(); EXPECT_FALSE(test_api_->MaybeRunSuspendReadinessCallback()); } // Tests that the ExtensionEventObserver delays suspends and dark suspends while // there is a push message pending for an app that uses GCM. TEST_F(ExtensionEventObserverTest, PushMessagesDelaySuspend) { scoped_refptr gcm_app = CreateApp("DelaysSuspendForPushMessages", true /* uses_gcm */); extensions::ExtensionHost* host = CreateHostForApp(profile_, gcm_app.get()); ASSERT_TRUE(host); EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); // Test that a push message received before a suspend attempt delays the // attempt. const int kSuspendPushId = 23874; extension_event_observer_->OnBackgroundEventDispatched( host, extensions::api::gcm::OnMessage::kEventName, kSuspendPushId); power_manager_client_->SendSuspendImminent(); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); extension_event_observer_->OnBackgroundEventAcked(host, kSuspendPushId); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); // Now test receiving the suspend attempt before the push message. const int kDarkSuspendPushId = 56674; power_manager_client_->SendDarkSuspendImminent(); extension_event_observer_->OnBackgroundEventDispatched( host, extensions::api::gcm::OnMessage::kEventName, kDarkSuspendPushId); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); extension_event_observer_->OnBackgroundEventAcked(host, kDarkSuspendPushId); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); // Test that non-push messages do not delay the suspend. const int kNonPushId = 5687; power_manager_client_->SendDarkSuspendImminent(); extension_event_observer_->OnBackgroundEventDispatched(host, "FakeMessage", kNonPushId); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } // Tests that messages sent for apps that don't use GCM are ignored. TEST_F(ExtensionEventObserverTest, IgnoresNonGCMApps) { scoped_refptr app = CreateApp("Non-GCM", false); extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); ASSERT_TRUE(host); EXPECT_FALSE(test_api_->WillDelaySuspendForExtensionHost(host)); power_manager_client_->SendSuspendImminent(); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } // Tests that network requests started by an app while it is processing a push // message delay any suspend attempt. TEST_F(ExtensionEventObserverTest, NetworkRequestsMayDelaySuspend) { scoped_refptr app = CreateApp("NetworkRequests", true); extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); ASSERT_TRUE(host); EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); // Test that network requests started while there is no pending push message // are ignored. const uint64_t kNonPushRequestId = 5170725; extension_event_observer_->OnNetworkRequestStarted(host, kNonPushRequestId); power_manager_client_->SendSuspendImminent(); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); // Test that network requests started while a push message is pending delay // the suspend even after the push message has been acked. const int kPushMessageId = 178674; const uint64_t kNetworkRequestId = 78917089; power_manager_client_->SendDarkSuspendImminent(); extension_event_observer_->OnBackgroundEventDispatched( host, extensions::api::gcm::OnMessage::kEventName, kPushMessageId); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); extension_event_observer_->OnNetworkRequestStarted(host, kNetworkRequestId); extension_event_observer_->OnBackgroundEventAcked(host, kPushMessageId); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); extension_event_observer_->OnNetworkRequestDone(host, kNetworkRequestId); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } // Tests that any outstanding push messages or network requests for an // ExtensionHost that is destroyed do not end up blocking system suspend. TEST_F(ExtensionEventObserverTest, DeletedExtensionHostDoesNotBlockSuspend) { scoped_refptr app = CreateApp("DeletedExtensionHost", true); // The easiest way to delete an extension host is to delete the Profile it is // associated with so we create a new Profile here. const char kProfileName[] = "DeletedExtensionHostProfile"; Profile* new_profile = profile_manager_->CreateTestingProfile(kProfileName); extensions::ExtensionHost* host = CreateHostForApp(new_profile, app.get()); ASSERT_TRUE(host); EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); const int kPushId = 156178; const uint64_t kNetworkId = 791605; extension_event_observer_->OnBackgroundEventDispatched( host, extensions::api::gcm::OnMessage::kEventName, kPushId); extension_event_observer_->OnNetworkRequestStarted(host, kNetworkId); // Now delete the Profile. This has the side-effect of also deleting all the // ExtensionHosts. profile_manager_->DeleteTestingProfile(kProfileName); power_manager_client_->SendSuspendImminent(); EXPECT_TRUE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } // Tests that the ExtensionEventObserver does not delay suspend attempts when it // is disabled. TEST_F(ExtensionEventObserverTest, DoesNotDelaySuspendWhenDisabled) { scoped_refptr app = CreateApp("NoDelayWhenDisabled", true); extensions::ExtensionHost* host = CreateHostForApp(profile_, app.get()); ASSERT_TRUE(host); EXPECT_TRUE(test_api_->WillDelaySuspendForExtensionHost(host)); // Test that disabling the suspend delay while a suspend is pending will cause // the ExtensionEventObserver to immediately report readiness. const int kPushId = 416753; extension_event_observer_->OnBackgroundEventDispatched( host, extensions::api::gcm::OnMessage::kEventName, kPushId); power_manager_client_->SendSuspendImminent(); EXPECT_EQ(1, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); extension_event_observer_->SetShouldDelaySuspend(false); EXPECT_FALSE(test_api_->MaybeRunSuspendReadinessCallback()); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); // Test that the ExtensionEventObserver does not delay suspend attempts when // it is disabled. power_manager_client_->SendDarkSuspendImminent(); EXPECT_EQ(0, power_manager_client_->GetNumPendingSuspendReadinessCallbacks()); } } // namespace chromeos