| Index: chrome/browser/extensions/extension_storage_monitor_browsertest.cc
|
| diff --git a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
|
| index b9671a8945d6d1e405271ba4c42003ae989915d1..c70d5253e128097d2e3016c9bb534ecee057f5c5 100644
|
| --- a/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
|
| +++ b/chrome/browser/extensions/extension_storage_monitor_browsertest.cc
|
| @@ -5,14 +5,18 @@
|
| #include <stdint.h>
|
|
|
| #include <set>
|
| +#include <string>
|
| +#include <vector>
|
|
|
| #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/extensions/test_extension_dir.h"
|
| #include "chrome/browser/ui/extensions/app_launch_params.h"
|
| #include "chrome/browser/ui/extensions/application_launch.h"
|
| +#include "content/public/test/browser_test_utils.h"
|
| #include "content/public/test/test_utils.h"
|
| #include "extensions/browser/extension_dialog_auto_confirm.h"
|
| #include "extensions/browser/extension_prefs.h"
|
| @@ -20,7 +24,9 @@
|
| #include "extensions/browser/extension_system.h"
|
| #include "extensions/browser/test_extension_registry_observer.h"
|
| #include "extensions/common/constants.h"
|
| +#include "extensions/common/value_builder.h"
|
| #include "extensions/test/extension_test_message_listener.h"
|
| +#include "net/dns/mock_host_resolver.h"
|
| #include "ui/message_center/message_center.h"
|
| #include "ui/message_center/message_center_observer.h"
|
|
|
| @@ -86,6 +92,8 @@ class ExtensionStorageMonitorTest : public ExtensionBrowserTest {
|
| void SetUpOnMainThread() override {
|
| ExtensionBrowserTest::SetUpOnMainThread();
|
|
|
| + host_resolver()->AddRule("*", "127.0.0.1");
|
| +
|
| InitStorageMonitor();
|
| }
|
|
|
| @@ -111,6 +119,40 @@ class ExtensionStorageMonitorTest : public ExtensionBrowserTest {
|
| return extension;
|
| }
|
|
|
| + const Extension* CreateHostedApp(const std::string& name,
|
| + GURL app_url,
|
| + std::vector<std::string> permissions) {
|
| + auto dir = base::MakeUnique<TestExtensionDir>();
|
| +
|
| + url::Replacements<char> clear_port;
|
| + clear_port.ClearPort();
|
| +
|
| + DictionaryBuilder manifest;
|
| + manifest.Set("name", name)
|
| + .Set("version", "1.0")
|
| + .Set("manifest_version", 2)
|
| + .Set(
|
| + "app",
|
| + DictionaryBuilder()
|
| + .Set("urls",
|
| + ListBuilder()
|
| + .Append(app_url.ReplaceComponents(clear_port).spec())
|
| + .Build())
|
| + .Set("launch",
|
| + DictionaryBuilder().Set("web_url", app_url.spec()).Build())
|
| + .Build());
|
| + ListBuilder permissions_builder;
|
| + for (const std::string& permission : permissions)
|
| + permissions_builder.Append(permission);
|
| + manifest.Set("permissions", permissions_builder.Build());
|
| + dir->WriteManifest(manifest.ToJSON());
|
| +
|
| + const Extension* extension = LoadExtension(dir->UnpackedPath());
|
| + EXPECT_TRUE(extension);
|
| + temp_dirs_.push_back(std::move(dir));
|
| + return extension;
|
| + }
|
| +
|
| std::string GetNotificationId(const std::string& extension_id) {
|
| return monitor()->GetNotificationId(extension_id);
|
| }
|
| @@ -124,17 +166,22 @@ class ExtensionStorageMonitorTest : public ExtensionBrowserTest {
|
| }
|
|
|
| void WriteBytesExpectingNotification(const Extension* extension,
|
| - int num_bytes) {
|
| + int num_bytes,
|
| + const char* filesystem = "PERSISTENT") {
|
| int64_t previous_threshold = GetNextStorageThreshold(extension->id());
|
| - WriteBytes(extension, num_bytes, true);
|
| - EXPECT_GT(GetNextStorageThreshold(extension->id()), previous_threshold);
|
| + WriteBytes(extension, num_bytes, filesystem, true);
|
| + ASSERT_GT(GetNextStorageThreshold(extension->id()), previous_threshold);
|
| }
|
|
|
| - void WriteBytesNotExpectingNotification(const Extension* extension,
|
| - int num_bytes) {
|
| - WriteBytes(extension, num_bytes, false);
|
| + void WriteBytesNotExpectingNotification(
|
| + const Extension* extension,
|
| + int num_bytes,
|
| + const char* filesystem = "PERSISTENT") {
|
| + WriteBytes(extension, num_bytes, filesystem, false);
|
| }
|
|
|
| + void SimulateProfileShutdown() { storage_monitor_->StopMonitoringAll(); }
|
| +
|
| private:
|
| void InitStorageMonitor() {
|
| storage_monitor_ = ExtensionStorageMonitor::Get(profile());
|
| @@ -149,34 +196,71 @@ class ExtensionStorageMonitorTest : public ExtensionBrowserTest {
|
| 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) {
|
| + // Write bytes for a hosted app page that's loaded the script:
|
| + // //chrome/test/data/extensions/storage_monitor/hosted_apps/common.js
|
| + void WriteBytesForHostedApp(const Extension* extension,
|
| + int num_bytes,
|
| + const std::string& filesystem) {
|
| + content::WebContents* web_contents = OpenApplication(AppLaunchParams(
|
| + profile(), extension, LAUNCH_CONTAINER_TAB,
|
| + WindowOpenDisposition::SINGLETON_TAB, extensions::SOURCE_TEST));
|
| +
|
| + ASSERT_TRUE(WaitForLoadStop(web_contents));
|
| + std::string result;
|
| + const char* script = R"(
|
| + HostedAppWriteData(%s, %d)
|
| + .then(() => domAutomationController.send('write_done'))
|
| + .catch(e => domAutomationController.send('write_error: ' + e));
|
| + )";
|
| + ASSERT_TRUE(ExecuteScriptAndExtractString(
|
| + web_contents, base::StringPrintf(script, filesystem.c_str(), num_bytes),
|
| + &result));
|
| + ASSERT_EQ("write_done", result);
|
| + }
|
| +
|
| + // Write bytes for the extension loaded from:
|
| + // //chrome/test/data/extensions/storage_monitor/write_data
|
| + void WriteBytesForExtension(const Extension* extension, int num_bytes) {
|
| ExtensionTestMessageListener launched_listener("launched", true);
|
| - ExtensionTestMessageListener write_complete_listener(
|
| - "write_complete", false);
|
| - NotificationObserver notification_observer(
|
| - GetNotificationId(extension->id()));
|
| + ExtensionTestMessageListener write_complete_listener("write_complete",
|
| + false);
|
|
|
| OpenApplication(AppLaunchParams(profile(), extension, LAUNCH_CONTAINER_NONE,
|
| WindowOpenDisposition::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());
|
| + }
|
| +
|
| + // Write a number of bytes to persistent storage.
|
| + void WriteBytes(const Extension* extension,
|
| + int num_bytes,
|
| + const std::string& filesystem,
|
| + bool expected_notification) {
|
| + NotificationObserver notification_observer(
|
| + GetNotificationId(extension->id()));
|
| +
|
| + if (extension->is_hosted_app()) {
|
| + WriteBytesForHostedApp(extension, num_bytes, filesystem);
|
| + } else {
|
| + ASSERT_EQ("PERSISTENT", filesystem) << "Not implemented in the js code.";
|
| + WriteBytesForExtension(extension, num_bytes);
|
| + }
|
|
|
| if (expected_notification) {
|
| - EXPECT_TRUE(notification_observer.WaitForNotification());
|
| + ASSERT_TRUE(notification_observer.WaitForNotification());
|
| } else {
|
| base::RunLoop().RunUntilIdle();
|
| - EXPECT_FALSE(notification_observer.HasReceivedNotification());
|
| + ASSERT_FALSE(notification_observer.HasReceivedNotification());
|
| }
|
| }
|
|
|
| ExtensionStorageMonitor* storage_monitor_;
|
| + std::vector<std::unique_ptr<TestExtensionDir>> temp_dirs_;
|
| };
|
|
|
| // Control - No notifications should be shown if usage remains under the
|
| @@ -253,6 +337,67 @@ IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest,
|
| WriteBytesNotExpectingNotification(extension, GetInitialExtensionThreshold());
|
| }
|
|
|
| +// Regression test for https://crbug.com/716426
|
| +IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest,
|
| + HostedAppTemporaryFilesystem) {
|
| + ASSERT_TRUE(embedded_test_server()->Start());
|
| +
|
| + GURL url = embedded_test_server()->GetURL(
|
| + "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html");
|
| + const Extension* app =
|
| + CreateHostedApp("Hosted App", url, {"unlimitedStorage"});
|
| +
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
|
| + app, GetInitialExtensionThreshold(), "TEMPORARY"));
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification(
|
| + app, GetInitialExtensionThreshold(), "PERSISTENT"));
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
|
| + app, GetInitialExtensionThreshold(), "TEMPORARY"));
|
| +
|
| + // Bug 716426 was a shutdown crash due to not removing a
|
| + // storage::StorageObserver registration before deleting the observer. To
|
| + // recreate that scenario, first disable the app (which leaks the observer
|
| + // registration), then simulate the step of profile exit where we delete the
|
| + // StorageObserver.
|
| + DisableExtension(app->id());
|
| + SimulateProfileShutdown();
|
| +
|
| + // Now generate more storage activity for the hosted app's temporary
|
| + // filesystem. Note that it's not a hosted app anymore -- it's just a webpage.
|
| + // Bug 716426 caused this to crash the browser.
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesNotExpectingNotification(
|
| + app, GetInitialExtensionThreshold(), "TEMPORARY"));
|
| +}
|
| +
|
| +// Exercises the case where two hosted apps are same-origin but have non-
|
| +// overlapping extents. Disabling one should not suppress storage monitoring for
|
| +// the other.
|
| +IN_PROC_BROWSER_TEST_F(ExtensionStorageMonitorTest, TwoHostedAppsInSameOrigin) {
|
| + ASSERT_TRUE(embedded_test_server()->Start());
|
| +
|
| + GURL url1 = embedded_test_server()->GetURL(
|
| + "chromium.org", "/extensions/storage_monitor/hosted_apps/one/index.html");
|
| + const Extension* app1 = CreateHostedApp("App 1", url1, {"unlimitedStorage"});
|
| +
|
| + GURL url2 = embedded_test_server()->GetURL(
|
| + "chromium.org", "/extensions/storage_monitor/hosted_apps/two/index.html");
|
| + const Extension* app2 = CreateHostedApp("App 2", url2, {"unlimitedStorage"});
|
| +
|
| + EXPECT_EQ(url1.GetOrigin(), url2.GetOrigin());
|
| +
|
| + EXPECT_NO_FATAL_FAILURE(
|
| + WriteBytesExpectingNotification(app1, GetInitialExtensionThreshold()));
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
|
| + app2, GetInitialExtensionThreshold() * 2));
|
| +
|
| + // Disable app2. We should still be monitoring the origin on behalf of app1.
|
| + DisableExtension(app2->id());
|
| +
|
| + // Writing a bunch of data in app1 should trigger the warning.
|
| + EXPECT_NO_FATAL_FAILURE(WriteBytesExpectingNotification(
|
| + app1, GetInitialExtensionThreshold() * 4));
|
| +}
|
| +
|
| // Verify that notifications are disabled when the user clicks the action button
|
| // in the notification.
|
| // Flaky: https://crbug.com/617801
|
|
|