| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <stdint.h> | 5 #include <stdint.h> |
| 6 | 6 |
| 7 #include <set> | 7 #include <set> |
| 8 #include <string> |
| 9 #include <vector> |
| 8 | 10 |
| 9 #include "base/run_loop.h" | 11 #include "base/run_loop.h" |
| 10 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
| 11 #include "chrome/browser/extensions/extension_browsertest.h" | 13 #include "chrome/browser/extensions/extension_browsertest.h" |
| 12 #include "chrome/browser/extensions/extension_service.h" | 14 #include "chrome/browser/extensions/extension_service.h" |
| 13 #include "chrome/browser/extensions/extension_storage_monitor.h" | 15 #include "chrome/browser/extensions/extension_storage_monitor.h" |
| 16 #include "chrome/browser/extensions/test_extension_dir.h" |
| 14 #include "chrome/browser/ui/extensions/app_launch_params.h" | 17 #include "chrome/browser/ui/extensions/app_launch_params.h" |
| 15 #include "chrome/browser/ui/extensions/application_launch.h" | 18 #include "chrome/browser/ui/extensions/application_launch.h" |
| 19 #include "content/public/test/browser_test_utils.h" |
| 16 #include "content/public/test/test_utils.h" | 20 #include "content/public/test/test_utils.h" |
| 17 #include "extensions/browser/extension_dialog_auto_confirm.h" | 21 #include "extensions/browser/extension_dialog_auto_confirm.h" |
| 18 #include "extensions/browser/extension_prefs.h" | 22 #include "extensions/browser/extension_prefs.h" |
| 19 #include "extensions/browser/extension_registry.h" | 23 #include "extensions/browser/extension_registry.h" |
| 20 #include "extensions/browser/extension_system.h" | 24 #include "extensions/browser/extension_system.h" |
| 21 #include "extensions/browser/test_extension_registry_observer.h" | 25 #include "extensions/browser/test_extension_registry_observer.h" |
| 22 #include "extensions/common/constants.h" | 26 #include "extensions/common/constants.h" |
| 27 #include "extensions/common/value_builder.h" |
| 23 #include "extensions/test/extension_test_message_listener.h" | 28 #include "extensions/test/extension_test_message_listener.h" |
| 29 #include "net/dns/mock_host_resolver.h" |
| 24 #include "ui/message_center/message_center.h" | 30 #include "ui/message_center/message_center.h" |
| 25 #include "ui/message_center/message_center_observer.h" | 31 #include "ui/message_center/message_center_observer.h" |
| 26 | 32 |
| 27 namespace extensions { | 33 namespace extensions { |
| 28 | 34 |
| 29 namespace { | 35 namespace { |
| 30 | 36 |
| 31 const int kInitialUsageThreshold = 500; | 37 const int kInitialUsageThreshold = 500; |
| 32 | 38 |
| 33 const char kWriteDataApp[] = "storage_monitor/write_data"; | 39 const char kWriteDataApp[] = "storage_monitor/write_data"; |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 79 | 85 |
| 80 class ExtensionStorageMonitorTest : public ExtensionBrowserTest { | 86 class ExtensionStorageMonitorTest : public ExtensionBrowserTest { |
| 81 public: | 87 public: |
| 82 ExtensionStorageMonitorTest() : storage_monitor_(NULL) {} | 88 ExtensionStorageMonitorTest() : storage_monitor_(NULL) {} |
| 83 | 89 |
| 84 protected: | 90 protected: |
| 85 // ExtensionBrowserTest overrides: | 91 // ExtensionBrowserTest overrides: |
| 86 void SetUpOnMainThread() override { | 92 void SetUpOnMainThread() override { |
| 87 ExtensionBrowserTest::SetUpOnMainThread(); | 93 ExtensionBrowserTest::SetUpOnMainThread(); |
| 88 | 94 |
| 95 host_resolver()->AddRule("*", "127.0.0.1"); |
| 96 |
| 89 InitStorageMonitor(); | 97 InitStorageMonitor(); |
| 90 } | 98 } |
| 91 | 99 |
| 92 ExtensionStorageMonitor* monitor() { | 100 ExtensionStorageMonitor* monitor() { |
| 93 CHECK(storage_monitor_); | 101 CHECK(storage_monitor_); |
| 94 return storage_monitor_; | 102 return storage_monitor_; |
| 95 } | 103 } |
| 96 | 104 |
| 97 int64_t GetInitialExtensionThreshold() { | 105 int64_t GetInitialExtensionThreshold() { |
| 98 CHECK(storage_monitor_); | 106 CHECK(storage_monitor_); |
| 99 return storage_monitor_->initial_extension_threshold_; | 107 return storage_monitor_->initial_extension_threshold_; |
| 100 } | 108 } |
| 101 | 109 |
| 102 void DisableForInstalledExtensions() { | 110 void DisableForInstalledExtensions() { |
| 103 CHECK(storage_monitor_); | 111 CHECK(storage_monitor_); |
| 104 storage_monitor_->enable_for_all_extensions_ = false; | 112 storage_monitor_->enable_for_all_extensions_ = false; |
| 105 } | 113 } |
| 106 | 114 |
| 107 const Extension* InitWriteDataApp() { | 115 const Extension* InitWriteDataApp() { |
| 108 base::FilePath path = test_data_dir_.AppendASCII(kWriteDataApp); | 116 base::FilePath path = test_data_dir_.AppendASCII(kWriteDataApp); |
| 109 const Extension* extension = InstallExtension(path, 1); | 117 const Extension* extension = InstallExtension(path, 1); |
| 110 EXPECT_TRUE(extension); | 118 EXPECT_TRUE(extension); |
| 111 return extension; | 119 return extension; |
| 112 } | 120 } |
| 113 | 121 |
| 122 const Extension* CreateHostedApp(const std::string& name, |
| 123 GURL app_url, |
| 124 std::vector<std::string> permissions) { |
| 125 auto dir = base::MakeUnique<TestExtensionDir>(); |
| 126 |
| 127 url::Replacements<char> clear_port; |
| 128 clear_port.ClearPort(); |
| 129 |
| 130 DictionaryBuilder manifest; |
| 131 manifest.Set("name", name) |
| 132 .Set("version", "1.0") |
| 133 .Set("manifest_version", 2) |
| 134 .Set( |
| 135 "app", |
| 136 DictionaryBuilder() |
| 137 .Set("urls", |
| 138 ListBuilder() |
| 139 .Append(app_url.ReplaceComponents(clear_port).spec()) |
| 140 .Build()) |
| 141 .Set("launch", |
| 142 DictionaryBuilder().Set("web_url", app_url.spec()).Build()) |
| 143 .Build()); |
| 144 ListBuilder permissions_builder; |
| 145 for (const std::string& permission : permissions) |
| 146 permissions_builder.Append(permission); |
| 147 manifest.Set("permissions", permissions_builder.Build()); |
| 148 dir->WriteManifest(manifest.ToJSON()); |
| 149 |
| 150 const Extension* extension = LoadExtension(dir->UnpackedPath()); |
| 151 EXPECT_TRUE(extension); |
| 152 temp_dirs_.push_back(std::move(dir)); |
| 153 return extension; |
| 154 } |
| 155 |
| 114 std::string GetNotificationId(const std::string& extension_id) { | 156 std::string GetNotificationId(const std::string& extension_id) { |
| 115 return monitor()->GetNotificationId(extension_id); | 157 return monitor()->GetNotificationId(extension_id); |
| 116 } | 158 } |
| 117 | 159 |
| 118 bool IsStorageNotificationEnabled(const std::string& extension_id) { | 160 bool IsStorageNotificationEnabled(const std::string& extension_id) { |
| 119 return monitor()->IsStorageNotificationEnabled(extension_id); | 161 return monitor()->IsStorageNotificationEnabled(extension_id); |
| 120 } | 162 } |
| 121 | 163 |
| 122 int64_t GetNextStorageThreshold(const std::string& extension_id) { | 164 int64_t GetNextStorageThreshold(const std::string& extension_id) { |
| 123 return monitor()->GetNextStorageThreshold(extension_id); | 165 return monitor()->GetNextStorageThreshold(extension_id); |
| 124 } | 166 } |
| 125 | 167 |
| 126 void WriteBytesExpectingNotification(const Extension* extension, | 168 void WriteBytesExpectingNotification(const Extension* extension, |
| 127 int num_bytes) { | 169 int num_bytes, |
| 170 const char* filesystem = "PERSISTENT") { |
| 128 int64_t previous_threshold = GetNextStorageThreshold(extension->id()); | 171 int64_t previous_threshold = GetNextStorageThreshold(extension->id()); |
| 129 WriteBytes(extension, num_bytes, true); | 172 WriteBytes(extension, num_bytes, filesystem, true); |
| 130 EXPECT_GT(GetNextStorageThreshold(extension->id()), previous_threshold); | 173 ASSERT_GT(GetNextStorageThreshold(extension->id()), previous_threshold); |
| 131 } | 174 } |
| 132 | 175 |
| 133 void WriteBytesNotExpectingNotification(const Extension* extension, | 176 void WriteBytesNotExpectingNotification( |
| 134 int num_bytes) { | 177 const Extension* extension, |
| 135 WriteBytes(extension, num_bytes, false); | 178 int num_bytes, |
| 179 const char* filesystem = "PERSISTENT") { |
| 180 WriteBytes(extension, num_bytes, filesystem, false); |
| 136 } | 181 } |
| 137 | 182 |
| 183 void SimulateProfileShutdown() { storage_monitor_->StopMonitoringAll(); } |
| 184 |
| 138 private: | 185 private: |
| 139 void InitStorageMonitor() { | 186 void InitStorageMonitor() { |
| 140 storage_monitor_ = ExtensionStorageMonitor::Get(profile()); | 187 storage_monitor_ = ExtensionStorageMonitor::Get(profile()); |
| 141 ASSERT_TRUE(storage_monitor_); | 188 ASSERT_TRUE(storage_monitor_); |
| 142 | 189 |
| 143 // Override thresholds so that we don't have to write a huge amount of data | 190 // Override thresholds so that we don't have to write a huge amount of data |
| 144 // to trigger notifications in these tests. | 191 // to trigger notifications in these tests. |
| 145 storage_monitor_->enable_for_all_extensions_ = true; | 192 storage_monitor_->enable_for_all_extensions_ = true; |
| 146 storage_monitor_->initial_extension_threshold_ = kInitialUsageThreshold; | 193 storage_monitor_->initial_extension_threshold_ = kInitialUsageThreshold; |
| 147 | 194 |
| 148 // To ensure storage events are dispatched from QuotaManager immediately. | 195 // To ensure storage events are dispatched from QuotaManager immediately. |
| 149 storage_monitor_->observer_rate_ = base::TimeDelta(); | 196 storage_monitor_->observer_rate_ = base::TimeDelta(); |
| 150 } | 197 } |
| 151 | 198 |
| 199 // Write bytes for a hosted app page that's loaded the script: |
| 200 // //chrome/test/data/extensions/storage_monitor/hosted_apps/common.js |
| 201 void WriteBytesForHostedApp(const Extension* extension, |
| 202 int num_bytes, |
| 203 const std::string& filesystem) { |
| 204 content::WebContents* web_contents = OpenApplication(AppLaunchParams( |
| 205 profile(), extension, LAUNCH_CONTAINER_TAB, |
| 206 WindowOpenDisposition::SINGLETON_TAB, extensions::SOURCE_TEST)); |
| 207 |
| 208 ASSERT_TRUE(WaitForLoadStop(web_contents)); |
| 209 std::string result; |
| 210 const char* script = R"( |
| 211 HostedAppWriteData(%s, %d) |
| 212 .then(() => domAutomationController.send('write_done')) |
| 213 .catch(e => domAutomationController.send('write_error: ' + e)); |
| 214 )"; |
| 215 ASSERT_TRUE(ExecuteScriptAndExtractString( |
| 216 web_contents, base::StringPrintf(script, filesystem.c_str(), num_bytes), |
| 217 &result)); |
| 218 ASSERT_EQ("write_done", result); |
| 219 } |
| 220 |
| 221 // Write bytes for the extension loaded from: |
| 222 // //chrome/test/data/extensions/storage_monitor/write_data |
| 223 void WriteBytesForExtension(const Extension* extension, int num_bytes) { |
| 224 ExtensionTestMessageListener launched_listener("launched", true); |
| 225 ExtensionTestMessageListener write_complete_listener("write_complete", |
| 226 false); |
| 227 |
| 228 OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE, |
| 229 WindowOpenDisposition::NEW_WINDOW, |
| 230 extensions::SOURCE_TEST)); |
| 231 |
| 232 ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); |
| 233 |
| 234 // Instruct the app to write |num_bytes| of data. |
| 235 launched_listener.Reply(base::IntToString(num_bytes)); |
| 236 ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied()); |
| 237 } |
| 238 |
| 152 // Write a number of bytes to persistent storage. | 239 // Write a number of bytes to persistent storage. |
| 153 void WriteBytes(const Extension* extension, | 240 void WriteBytes(const Extension* extension, |
| 154 int num_bytes, | 241 int num_bytes, |
| 242 const std::string& filesystem, |
| 155 bool expected_notification) { | 243 bool expected_notification) { |
| 156 ExtensionTestMessageListener launched_listener("launched", true); | |
| 157 ExtensionTestMessageListener write_complete_listener( | |
| 158 "write_complete", false); | |
| 159 NotificationObserver notification_observer( | 244 NotificationObserver notification_observer( |
| 160 GetNotificationId(extension->id())); | 245 GetNotificationId(extension->id())); |
| 161 | 246 |
| 162 OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE, | 247 if (extension->is_hosted_app()) { |
| 163 WindowOpenDisposition::NEW_WINDOW, | 248 WriteBytesForHostedApp(extension, num_bytes, filesystem); |
| 164 extensions::SOURCE_TEST)); | 249 } else { |
| 165 ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); | 250 ASSERT_EQ("PERSISTENT", filesystem) << "Not implemented in the js code."; |
| 166 | 251 WriteBytesForExtension(extension, num_bytes); |
| 167 // Instruct the app to write |num_bytes| of data. | 252 } |
| 168 launched_listener.Reply(base::IntToString(num_bytes)); | |
| 169 ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied()); | |
| 170 | 253 |
| 171 if (expected_notification) { | 254 if (expected_notification) { |
| 172 EXPECT_TRUE(notification_observer.WaitForNotification()); | 255 ASSERT_TRUE(notification_observer.WaitForNotification()); |
| 173 } else { | 256 } else { |
| 174 base::RunLoop().RunUntilIdle(); | 257 base::RunLoop().RunUntilIdle(); |
| 175 EXPECT_FALSE(notification_observer.HasReceivedNotification()); | 258 ASSERT_FALSE(notification_observer.HasReceivedNotification()); |
| 176 } | 259 } |
| 177 } | 260 } |
| 178 | 261 |
| 179 ExtensionStorageMonitor* storage_monitor_; | 262 ExtensionStorageMonitor* storage_monitor_; |
| 263 std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_; |
| 180 }; | 264 }; |
| 181 | 265 |
| 182 // Control - No notifications should be shown if usage remains under the | 266 // Control - No notifications should be shown if usage remains under the |
| 183 // threshold. | 267 // threshold. |
| 184 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) { | 268 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) { |
| 185 const Extension* extension = InitWriteDataApp(); | 269 const Extension* extension = InitWriteDataApp(); |
| 186 ASSERT_TRUE(extension); | 270 ASSERT_TRUE(extension); |
| 187 WriteBytesNotExpectingNotification(extension, 1); | 271 WriteBytesNotExpectingNotification(extension, 1); |
| 188 } | 272 } |
| 189 | 273 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 // is eventually enabled for all extensions. | 330 // is eventually enabled for all extensions. |
| 247 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, | 331 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, |
| 248 DisableForInstalledExtensions) { | 332 DisableForInstalledExtensions) { |
| 249 DisableForInstalledExtensions(); | 333 DisableForInstalledExtensions(); |
| 250 | 334 |
| 251 const Extension* extension = InitWriteDataApp(); | 335 const Extension* extension = InitWriteDataApp(); |
| 252 ASSERT_TRUE(extension); | 336 ASSERT_TRUE(extension); |
| 253 WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold()); | 337 WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold()); |
| 254 } | 338 } |
| 255 | 339 |
| 340 // Regression test for https://crbug.com/716426 |
| 341 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, |
| 342 HostedAppTemporaryFilesystem) { |
| 343 ASSERT_TRUE(embedded_test_server()->Start()); |
| 344 |
| 345 GURL url = embedded_test_server()->GetURL( |
| 346 "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html"); |
| 347 const Extension* app = |
| 348 CreateHostedApp("Hosted App", url, {"unlimitedStorage"}); |
| 349 |
| 350 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( |
| 351 app, GetInitialExtensionThreshold(), "TEMPORARY")); |
| 352 EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification( |
| 353 app, GetInitialExtensionThreshold(), "PERSISTENT")); |
| 354 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( |
| 355 app, GetInitialExtensionThreshold(), "TEMPORARY")); |
| 356 |
| 357 // Bug 716426 was a shutdown crash due to not removing a |
| 358 // storage::StorageObserver registration before deleting the observer. To |
| 359 // recreate that scenario, first disable the app (which leaks the observer |
| 360 // registration), then simulate the step of profile exit where we delete the |
| 361 // StorageObserver. |
| 362 DisableExtension(app->id()); |
| 363 SimulateProfileShutdown(); |
| 364 |
| 365 // Now generate more storage activity for the hosted app's temporary |
| 366 // filesystem. Note that it's not a hosted app anymore -- it's just a webpage. |
| 367 // Bug 716426 caused this to crash the browser. |
| 368 EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification( |
| 369 app, GetInitialExtensionThreshold(), "TEMPORARY")); |
| 370 } |
| 371 |
| 372 // Exercises the case where two hosted apps are same-origin but have non- |
| 373 // overlapping extents. Disabling one should not suppress storage monitoring for |
| 374 // the other. |
| 375 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, TwoHostedAppsInSameOrigin) { |
| 376 ASSERT_TRUE(embedded_test_server()->Start()); |
| 377 |
| 378 GURL url1 = embedded_test_server()->GetURL( |
| 379 "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html"); |
| 380 const Extension* app1 = CreateHostedApp("App 1", url1, {"unlimitedStorage"}); |
| 381 |
| 382 GURL url2 = embedded_test_server()->GetURL( |
| 383 "chromium.org", "/extensions/storage_monitor/hosted_apps/two/index.html"); |
| 384 const Extension* app2 = CreateHostedApp("App 2", url2, {"unlimitedStorage"}); |
| 385 |
| 386 EXPECT_EQ(url1.GetOrigin(), url2.GetOrigin()); |
| 387 |
| 388 EXPECT_NO_FATAL_FAILURE( |
| 389 WriteBytesExpectingNotification(app1, GetInitialExtensionThreshold())); |
| 390 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( |
| 391 app2, GetInitialExtensionThreshold() * 2)); |
| 392 |
| 393 // Disable app2. We should still be monitoring the origin on behalf of app1. |
| 394 DisableExtension(app2->id()); |
| 395 |
| 396 // Writing a bunch of data in app1 should trigger the warning. |
| 397 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( |
| 398 app1, GetInitialExtensionThreshold() * 4)); |
| 399 } |
| 400 |
| 256 // Verify that notifications are disabled when the user clicks the action button | 401 // Verify that notifications are disabled when the user clicks the action button |
| 257 // in the notification. | 402 // in the notification. |
| 258 // Flaky: https://crbug.com/617801 | 403 // Flaky: https://crbug.com/617801 |
| 259 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, | 404 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, |
| 260 DISABLED_UninstallExtension) { | 405 DISABLED_UninstallExtension) { |
| 261 const Extension* extension = InitWriteDataApp(); | 406 const Extension* extension = InitWriteDataApp(); |
| 262 ASSERT_TRUE(extension); | 407 ASSERT_TRUE(extension); |
| 263 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); | 408 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); |
| 264 | 409 |
| 265 // Fake clicking the notification button to uninstall and accepting the | 410 // Fake clicking the notification button to uninstall and accepting the |
| 266 // uninstall. | 411 // uninstall. |
| 267 ScopedTestDialogAutoConfirm scoped_autoconfirm( | 412 ScopedTestDialogAutoConfirm scoped_autoconfirm( |
| 268 ScopedTestDialogAutoConfirm::ACCEPT); | 413 ScopedTestDialogAutoConfirm::ACCEPT); |
| 269 TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()), | 414 TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()), |
| 270 extension->id()); | 415 extension->id()); |
| 271 message_center::MessageCenter::Get()->ClickOnNotificationButton( | 416 message_center::MessageCenter::Get()->ClickOnNotificationButton( |
| 272 GetNotificationId(extension->id()), | 417 GetNotificationId(extension->id()), |
| 273 ExtensionStorageMonitor::BUTTON_UNINSTALL); | 418 ExtensionStorageMonitor::BUTTON_UNINSTALL); |
| 274 observer.WaitForExtensionUninstalled(); | 419 observer.WaitForExtensionUninstalled(); |
| 275 } | 420 } |
| 276 | 421 |
| 277 } // namespace extensions | 422 } // namespace extensions |
| OLD | NEW |