Chromium Code Reviews| 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 std::unique_ptr<TestExtensionDir> dir(new TestExtensionDir); | |
|
Devlin
2017/06/12 20:15:05
prefer auto dir = base::MakeUnique<TestExtensionDi
ncarter (slow)
2017/06/12 22:52:07
Done.
| |
| 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 ASSERT_TRUE(ExecuteScriptAndExtractString( | |
| 211 web_contents, | |
| 212 base::StringPrintf(R"( | |
| 213 HostedAppWriteData(%s, %d) | |
| 214 .then(() => domAutomationController.send('write_done')) | |
| 215 .catch(e => domAutomationController.send('write_error: ' + e)); | |
| 216 )", | |
| 217 filesystem.c_str(), num_bytes), | |
|
Devlin
2017/06/12 20:15:05
... this formatting's a bit odd. Was this git cl
ncarter (slow)
2017/06/12 22:52:07
Fixed by extracting the literal to a local variabl
| |
| 218 &result)); | |
| 219 | |
| 220 ASSERT_EQ("write_done", result); | |
| 221 } | |
| 222 | |
| 223 // Write bytes for the extension loaded from: | |
| 224 // //chrome/test/data/extensions/storage_monitor/write_data | |
| 225 void WriteBytesForExtension(const Extension* extension, int num_bytes) { | |
| 226 ExtensionTestMessageListener launched_listener("launched", true); | |
| 227 ExtensionTestMessageListener write_complete_listener("write_complete", | |
| 228 false); | |
| 229 | |
| 230 OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE, | |
| 231 WindowOpenDisposition::NEW_WINDOW, | |
| 232 extensions::SOURCE_TEST)); | |
| 233 | |
| 234 ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); | |
| 235 | |
| 236 // Instruct the app to write |num_bytes| of data. | |
| 237 launched_listener.Reply(base::IntToString(num_bytes)); | |
| 238 ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied()); | |
| 239 } | |
| 240 | |
| 152 // Write a number of bytes to persistent storage. | 241 // Write a number of bytes to persistent storage. |
| 153 void WriteBytes(const Extension* extension, | 242 void WriteBytes(const Extension* extension, |
| 154 int num_bytes, | 243 int num_bytes, |
| 244 const std::string& filesystem, | |
| 155 bool expected_notification) { | 245 bool expected_notification) { |
| 156 ExtensionTestMessageListener launched_listener("launched", true); | |
| 157 ExtensionTestMessageListener write_complete_listener( | |
| 158 "write_complete", false); | |
| 159 NotificationObserver notification_observer( | 246 NotificationObserver notification_observer( |
| 160 GetNotificationId(extension->id())); | 247 GetNotificationId(extension->id())); |
| 161 | 248 |
| 162 OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE, | 249 if (extension->is_hosted_app()) { |
| 163 WindowOpenDisposition::NEW_WINDOW, | 250 WriteBytesForHostedApp(extension, num_bytes, filesystem); |
| 164 extensions::SOURCE_TEST)); | 251 } else { |
| 165 ASSERT_TRUE(launched_listener.WaitUntilSatisfied()); | 252 ASSERT_EQ("PERSISTENT", filesystem) << "Not implemented in the js code."; |
| 166 | 253 WriteBytesForExtension(extension, num_bytes); |
| 167 // Instruct the app to write |num_bytes| of data. | 254 } |
| 168 launched_listener.Reply(base::IntToString(num_bytes)); | |
| 169 ASSERT_TRUE(write_complete_listener.WaitUntilSatisfied()); | |
| 170 | 255 |
| 171 if (expected_notification) { | 256 if (expected_notification) { |
| 172 EXPECT_TRUE(notification_observer.WaitForNotification()); | 257 ASSERT_TRUE(notification_observer.WaitForNotification()); |
| 173 } else { | 258 } else { |
| 174 base::RunLoop().RunUntilIdle(); | 259 base::RunLoop().RunUntilIdle(); |
| 175 EXPECT_FALSE(notification_observer.HasReceivedNotification()); | 260 ASSERT_FALSE(notification_observer.HasReceivedNotification()); |
| 176 } | 261 } |
| 177 } | 262 } |
| 178 | 263 |
| 179 ExtensionStorageMonitor* storage_monitor_; | 264 ExtensionStorageMonitor* storage_monitor_; |
| 265 std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_; | |
| 180 }; | 266 }; |
| 181 | 267 |
| 182 // Control - No notifications should be shown if usage remains under the | 268 // Control - No notifications should be shown if usage remains under the |
| 183 // threshold. | 269 // threshold. |
| 184 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) { | 270 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, UnderThreshold) { |
| 185 const Extension* extension = InitWriteDataApp(); | 271 const Extension* extension = InitWriteDataApp(); |
| 186 ASSERT_TRUE(extension); | 272 ASSERT_TRUE(extension); |
| 187 WriteBytesNotExpectingNotification(extension, 1); | 273 WriteBytesNotExpectingNotification(extension, 1); |
| 188 } | 274 } |
| 189 | 275 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 246 // is eventually enabled for all extensions. | 332 // is eventually enabled for all extensions. |
| 247 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, | 333 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, |
| 248 DisableForInstalledExtensions) { | 334 DisableForInstalledExtensions) { |
| 249 DisableForInstalledExtensions(); | 335 DisableForInstalledExtensions(); |
| 250 | 336 |
| 251 const Extension* extension = InitWriteDataApp(); | 337 const Extension* extension = InitWriteDataApp(); |
| 252 ASSERT_TRUE(extension); | 338 ASSERT_TRUE(extension); |
| 253 WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold()); | 339 WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold()); |
| 254 } | 340 } |
| 255 | 341 |
| 342 // Regression test for https://crbug.com/716426 | |
| 343 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, | |
| 344 HostedAppTemporaryFilesystem) { | |
| 345 ASSERT_TRUE(embedded_test_server()->Start()); | |
| 346 | |
| 347 GURL url = embedded_test_server()->GetURL( | |
| 348 "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html"); | |
| 349 const Extension* app = | |
| 350 CreateHostedApp("Hosted App", url, {"unlimitedStorage"}); | |
| 351 | |
| 352 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( | |
| 353 app, GetInitialExtensionThreshold(), "TEMPORARY")); | |
| 354 EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification( | |
| 355 app, GetInitialExtensionThreshold(), "PERSISTENT")); | |
| 356 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( | |
| 357 app, GetInitialExtensionThreshold(), "TEMPORARY")); | |
| 358 | |
| 359 // Bug 716426 was a shutdown crash due to not removing a | |
| 360 // storage::StorageObserver registration before deleting the observer. To | |
| 361 // recreate that scenario, first disable the app (which leaks the observer | |
| 362 // registration), then simulate the step of profile exit where we delete the | |
| 363 // StorageObserver. | |
| 364 DisableExtension(app->id()); | |
| 365 SimulateProfileShutdown(); | |
| 366 | |
| 367 // Now generate more storage activity for the hosted app's temporary | |
| 368 // filesystem. Note that it's not a hosted app anymore -- it's just a webpage. | |
| 369 // Bug 716426 caused this to crash the browser. | |
| 370 EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification( | |
| 371 app, GetInitialExtensionThreshold(), "TEMPORARY")); | |
| 372 } | |
| 373 | |
| 374 // Exercises the case where two hosted apps are same-origin but have non- | |
| 375 // overlapping extents. Disabling one should not suppress storage monitoring for | |
| 376 // the other. | |
| 377 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, TwoHostedAppsInSameOrigin) { | |
| 378 ASSERT_TRUE(embedded_test_server()->Start()); | |
| 379 | |
| 380 GURL url1 = embedded_test_server()->GetURL( | |
| 381 "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html"); | |
| 382 const Extension* app1 = CreateHostedApp("App 1", url1, {"unlimitedStorage"}); | |
| 383 | |
| 384 GURL url2 = embedded_test_server()->GetURL( | |
| 385 "chromium.org", "/extensions/storage_monitor/hosted_apps/two/index.html"); | |
| 386 const Extension* app2 = CreateHostedApp("App 2", url2, {"unlimitedStorage"}); | |
| 387 | |
| 388 EXPECT_EQ(url1.GetOrigin(), url2.GetOrigin()); | |
| 389 | |
| 390 EXPECT_NO_FATAL_FAILURE( | |
| 391 WriteBytesExpectingNotification(app1, GetInitialExtensionThreshold())); | |
| 392 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( | |
| 393 app2, GetInitialExtensionThreshold() * 2)); | |
| 394 | |
| 395 // Disable app2. We should still be monitoring the origin on behalf of app1. | |
| 396 DisableExtension(app2->id()); | |
| 397 | |
| 398 // Writing a bunch of data in app1 should trigger the warning. | |
| 399 EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification( | |
| 400 app1, GetInitialExtensionThreshold() * 4)); | |
| 401 } | |
| 402 | |
| 256 // Verify that notifications are disabled when the user clicks the action button | 403 // Verify that notifications are disabled when the user clicks the action button |
| 257 // in the notification. | 404 // in the notification. |
| 258 // Flaky: https://crbug.com/617801 | 405 // Flaky: https://crbug.com/617801 |
| 259 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, | 406 IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, |
| 260 DISABLED_UninstallExtension) { | 407 DISABLED_UninstallExtension) { |
| 261 const Extension* extension = InitWriteDataApp(); | 408 const Extension* extension = InitWriteDataApp(); |
| 262 ASSERT_TRUE(extension); | 409 ASSERT_TRUE(extension); |
| 263 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); | 410 WriteBytesExpectingNotification(extension, GetInitialExtensionThreshold()); |
| 264 | 411 |
| 265 // Fake clicking the notification button to uninstall and accepting the | 412 // Fake clicking the notification button to uninstall and accepting the |
| 266 // uninstall. | 413 // uninstall. |
| 267 ScopedTestDialogAutoConfirm scoped_autoconfirm( | 414 ScopedTestDialogAutoConfirm scoped_autoconfirm( |
| 268 ScopedTestDialogAutoConfirm::ACCEPT); | 415 ScopedTestDialogAutoConfirm::ACCEPT); |
| 269 TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()), | 416 TestExtensionRegistryObserver observer(ExtensionRegistry::Get(profile()), |
| 270 extension->id()); | 417 extension->id()); |
| 271 message_center::MessageCenter::Get()->ClickOnNotificationButton( | 418 message_center::MessageCenter::Get()->ClickOnNotificationButton( |
| 272 GetNotificationId(extension->id()), | 419 GetNotificationId(extension->id()), |
| 273 ExtensionStorageMonitor::BUTTON_UNINSTALL); | 420 ExtensionStorageMonitor::BUTTON_UNINSTALL); |
| 274 observer.WaitForExtensionUninstalled(); | 421 observer.WaitForExtensionUninstalled(); |
| 275 } | 422 } |
| 276 | 423 |
| 277 } // namespace extensions | 424 } // namespace extensions |
| OLD | NEW |