// Copyright 2014 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 #include #include "base/run_loop.h" #include "base/strings/string_number_conversions.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_storage_monitor.h" #include "chrome/browser/ui/extensions/app_launch_params.h" #include "chrome/browser/ui/extensions/application_launch.h" #include "content/public/test/test_utils.h" #include "extensions/browser/extension_dialog_auto_confirm.h" #include "extensions/browser/extension_prefs.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/browser/test_extension_registry_observer.h" #include "extensions/common/constants.h" #include "extensions/test/extension_test_message_listener.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_observer.h" namespace extensions { namespace { const int kInitialUsageThreshold = 500; const char kWriteDataApp[] = "storage_monitor/write_data"; class NotificationObserver : public message_center::MessageCenterObserver { public: explicit NotificationObserver(const std::string& target_notification) : message_center_(message_center::MessageCenter::Get()), target_notification_id_(target_notification), waiting_(false) { message_center_->AddObserver(this); } ~NotificationObserver() override { message_center_->RemoveObserver(this); } bool HasReceivedNotification() const { return received_notifications_.find(target_notification_id_) != received_notifications_.end(); } // Runs the message loop and returns true if a notification is received. // Immediately returns true if a notification has already been received. bool WaitForNotification() { if (HasReceivedNotification()) return true; waiting_ = true; content::RunMessageLoop(); waiting_ = false; return HasReceivedNotification(); } private: // MessageCenterObserver implementation: void OnNotificationAdded(const std::string& notification_id) override { received_notifications_.insert(notification_id); if (waiting_ && HasReceivedNotification()) base::MessageLoopForUI::current()->QuitWhenIdle(); } message_center::MessageCenter* message_center_; std::set received_notifications_; std::string target_notification_id_; bool waiting_; }; } // namespace class ExtensionStorageMonitorTest : public ExtensionBrowserTest { public: ExtensionStorageMonitorTest() : storage_monitor_(NULL) {} protected: // ExtensionBrowserTest overrides: void SetUpOnMainThread() override { ExtensionBrowserTest::SetUpOnMainThread(); InitStorageMonitor(); } ExtensionStorageMonitor* monitor() { CHECK(storage_monitor_); return storage_monitor_; } int64_t GetInitialExtensionThreshold() { CHECK(storage_monitor_); return storage_monitor_->initial_extension_threshold_; } void DisableForInstalledExtensions() { CHECK(storage_monitor_); storage_monitor_->enable_for_all_extensions_ = false; } const Extension* InitWriteDataApp() { base::FilePath path = test_data_dir_.AppendASCII(kWriteDataApp); const Extension* extension = InstallExtension(path, 1); EXPECT_TRUE(extension); return extension; } std::string GetNotificationId(const std::string& extension_id) { return monitor()->GetNotificationId(extension_id); } bool IsStorageNotificationEnabled(const std::string& extension_id) { return monitor()->IsStorageNotificationEnabled(extension_id); } int64_t GetNextStorageThreshold(const std::string& extension_id) { return monitor()->GetNextStorageThreshold(extension_id); } void WriteBytesExpectingNotification(const Extension* extension, int num_bytes) { int64_t previous_threshold = GetNextStorageThreshold(extension->id()); WriteBytes(extension, num_bytes, true); EXPECT_GT(GetNextStorageThreshold(extension->id()), previous_threshold); } void WriteBytesNotExpectingNotification(const Extension* extension, int num_bytes) { WriteBytes(extension, num_bytes, false); } private: void InitStorageMonitor() { storage_monitor_ = ExtensionStorageMonitor::Get(profile()); ASSERT_TRUE(storage_monitor_); // Override thresholds so that we don't have to write a huge amount of data // to trigger notifications in these tests. storage_monitor_->enable_for_all_extensions_ = true; storage_monitor_->initial_extension_threshold_ = kInitialUsageThreshold; // To ensure storage events are dispatched from QuotaManager immediately. storage_monitor_->observer_rate_ = base::TimeDelta(); } // Write a number of bytes to persistent storage. void WriteBytes(const Extension* extension, int num_bytes, bool expected_notification) { ExtensionTestMessageListener launched_listener("launched", true); ExtensionTestMessageListener write_complete_listener( "write_complete", false); NotificationObserver notification_observer( GetNotificationId(extension->id())); OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE, NEW_WINDOW, extensions::SOURCE_TEST)); ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); // Instruct the app to write |num_bytes| of data. launched_listener.Reply(base::IntToString(num_bytes)); ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied()); if (expected_notification) { EXPECT_TRUE(notification_observer.WaitForNotification()); } else { base::RunLoop().RunUntilIdle(); EXPECT_FALSE(notification_observer.HasReceivedNotification()); } } ExtensionStorageMonitor* storage_monitor_; }; // Control - No notifications should be shown if usage remains under the // threshold. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesNotExpectingNotification(extension, 1); } // Ensure a notification is shown when usage reaches the first threshold. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, ExceedInitialThreshold) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); } // Ensure a notification is shown when usage immediately exceeds double the // first threshold. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, DoubleInitialThreshold) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold() * 2); } // Ensure that notifications are not fired if the next threshold has not been // reached. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, ThrottleNotifications) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); // Exceed the first threshold. WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); // Stay within the next threshold. WriteBytesNotExpectingNotification(extension, 1); } // Verify that notifications are disabled when the user clicks the action button // in the notification. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UserDisabledNotifications) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); EXPECT_TRUE(IsStorageNotificationEnabled(extension->id())); // Fake clicking the notification button to disable notifications. message_center::MessageCenter::Get()->ClickOnNotificationButton( GetNotificationId(extension->id()), ExtensionStorageMonitor::BUTTON_DISABLE_NOTIFICATION); EXPECT_FALSE(IsStorageNotificationEnabled(extension->id())); // Expect to receive no further notifications when usage continues to // increase. int64_t next_threshold = GetNextStorageThreshold(extension->id()); int64_t next_data_size = next_threshold - GetInitialExtensionThreshold(); ASSERT_GT(next_data_size, 0); WriteBytesNotExpectingNotification(extension, next_data_size); } // Ensure that monitoring is disabled for installed extensions if // |enable_for_all_extensions_| is false. This test can be removed if monitoring // is eventually enabled for all extensions. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, DisableForInstalledExtensions) { DisableForInstalledExtensions(); const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold()); } // Verify that notifications are disabled when the user clicks the action button // in the notification. IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UninstallExtension) { const Extension* extension = InitWriteDataApp(); ASSERT_TRUE(extension); WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); // Fake clicking the notification button to uninstall and accepting the // uninstall. ScopedTestDialogAutoConfirm scoped_autoconfirm( ScopedTestDialogAutoConfirm::ACCEPT); TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()), extension->id()); message_center::MessageCenter::Get()->ClickOnNotificationButton( GetNotificationId(extension->id()), ExtensionStorageMonitor::BUTTON_UNINSTALL); observer.WaitForExtensionUninstalled(); } } // namespace extensions