| // Copyright 2013 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "extensions/browser/lazy_background_task_queue.h" | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/memory/raw_ptr.h" | 
 | #include "components/keyed_service/content/browser_context_dependency_manager.h" | 
 | #include "components/prefs/testing_pref_service.h" | 
 | #include "components/sync_preferences/testing_pref_service_syncable.h" | 
 | #include "components/user_prefs/user_prefs.h" | 
 | #include "content/public/test/test_browser_context.h" | 
 | #include "extensions/browser/extension_registry.h" | 
 | #include "extensions/browser/extension_registry_factory.h" | 
 | #include "extensions/browser/extensions_test.h" | 
 | #include "extensions/browser/lazy_context_id.h" | 
 | #include "extensions/browser/process_manager.h" | 
 | #include "extensions/browser/process_manager_factory.h" | 
 | #include "extensions/browser/test_extensions_browser_client.h" | 
 | #include "extensions/common/extension.h" | 
 | #include "extensions/common/extension_builder.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | using content::BrowserContext; | 
 |  | 
 | namespace extensions { | 
 | namespace { | 
 |  | 
 | // A ProcessManager that doesn't create background host pages. | 
 | class TestProcessManager : public ProcessManager { | 
 |  public: | 
 |   explicit TestProcessManager(BrowserContext* context) | 
 |       : ProcessManager(context, context, ExtensionRegistry::Get(context)), | 
 |         create_count_(0) { | 
 |     // ProcessManager constructor above assumes non-incognito. | 
 |     DCHECK(!context->IsOffTheRecord()); | 
 |   } | 
 |  | 
 |   TestProcessManager(const TestProcessManager&) = delete; | 
 |   TestProcessManager& operator=(const TestProcessManager&) = delete; | 
 |  | 
 |   ~TestProcessManager() override {} | 
 |  | 
 |   int create_count() { return create_count_; } | 
 |  | 
 |   // ProcessManager overrides: | 
 |   bool CreateBackgroundHost(const Extension* extension, | 
 |                             const GURL& url) override { | 
 |     // Don't actually try to create a web contents. | 
 |     create_count_++; | 
 |     return false; | 
 |   } | 
 |  | 
 |  private: | 
 |   int create_count_; | 
 | }; | 
 |  | 
 | std::unique_ptr<KeyedService> CreateTestProcessManager( | 
 |     BrowserContext* context) { | 
 |   return std::make_unique<TestProcessManager>(context); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Derives from ExtensionsTest to provide content module and keyed service | 
 | // initialization. | 
 | class LazyBackgroundTaskQueueTest : public ExtensionsTest { | 
 |  public: | 
 |   LazyBackgroundTaskQueueTest() : task_run_count_(0) {} | 
 |  | 
 |   LazyBackgroundTaskQueueTest(const LazyBackgroundTaskQueueTest&) = delete; | 
 |   LazyBackgroundTaskQueueTest& operator=(const LazyBackgroundTaskQueueTest&) = | 
 |       delete; | 
 |  | 
 |   ~LazyBackgroundTaskQueueTest() override {} | 
 |  | 
 |   int task_run_count() { return task_run_count_; } | 
 |   TestProcessManager* process_manager() { return process_manager_; } | 
 |  | 
 |   // A simple callback for AddPendingTask. | 
 |   void RunPendingTask(std::unique_ptr<LazyContextTaskQueue::ContextInfo>) { | 
 |     task_run_count_++; | 
 |   } | 
 |  | 
 |   // Creates and registers an extension without a background page. | 
 |   scoped_refptr<const Extension> CreateSimpleExtension() { | 
 |     scoped_refptr<const Extension> extension = | 
 |         ExtensionBuilder("No background") | 
 |             .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") | 
 |             .Build(); | 
 |     ExtensionRegistry::Get(browser_context())->AddEnabled(extension); | 
 |     return extension; | 
 |   } | 
 |  | 
 |   // Creates and registers an extension with a lazy background page. | 
 |   scoped_refptr<const Extension> CreateLazyBackgroundExtension() { | 
 |     scoped_refptr<const Extension> extension = | 
 |         ExtensionBuilder("Lazy background") | 
 |             .SetBackgroundContext( | 
 |                 ExtensionBuilder::BackgroundContext::EVENT_PAGE) | 
 |             .SetID("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") | 
 |             .Build(); | 
 |     ExtensionRegistry::Get(browser_context())->AddEnabled(extension); | 
 |     return extension; | 
 |   } | 
 |  | 
 |  protected: | 
 |   void SetUp() override { | 
 |     ExtensionsTest::SetUp(); | 
 |     user_prefs::UserPrefs::Set(browser_context(), &testing_pref_service_); | 
 |  | 
 |     process_manager_ = static_cast<TestProcessManager*>( | 
 |         ProcessManagerFactory::GetInstance()->SetTestingFactoryAndUse( | 
 |             browser_context(), base::BindRepeating(&CreateTestProcessManager))); | 
 |   } | 
 |  | 
 |   void TearDown() override { | 
 |     process_manager_ = nullptr; | 
 |     ExtensionsTest::TearDown(); | 
 |   } | 
 |  | 
 |  private: | 
 |   sync_preferences::TestingPrefServiceSyncable testing_pref_service_; | 
 |  | 
 |   // The total number of pending tasks that have been executed. | 
 |   int task_run_count_; | 
 |   raw_ptr<TestProcessManager> process_manager_ = nullptr; | 
 | }; | 
 |  | 
 | // Tests that only extensions with background pages should have tasks queued. | 
 | TEST_F(LazyBackgroundTaskQueueTest, ShouldEnqueueTask) { | 
 |   LazyBackgroundTaskQueue queue(browser_context()); | 
 |  | 
 |   // Build a simple extension with no background page. | 
 |   scoped_refptr<const Extension> no_background = CreateSimpleExtension(); | 
 |   EXPECT_FALSE(queue.ShouldEnqueueTask(browser_context(), no_background.get())); | 
 |  | 
 |   // Build another extension with a background page. | 
 |   scoped_refptr<const Extension> with_background = | 
 |       CreateLazyBackgroundExtension(); | 
 |   EXPECT_TRUE( | 
 |       queue.ShouldEnqueueTask(browser_context(), with_background.get())); | 
 | } | 
 |  | 
 | // Tests that adding tasks actually increases the pending task count, and that | 
 | // multiple extensions can have pending tasks. | 
 | TEST_F(LazyBackgroundTaskQueueTest, AddPendingTask) { | 
 |   LazyBackgroundTaskQueue queue(browser_context()); | 
 |  | 
 |   // Build a simple extension with no background page. | 
 |   scoped_refptr<const Extension> no_background = CreateSimpleExtension(); | 
 |  | 
 |   // Adding a pending task increases the number of extensions with tasks, but | 
 |   // doesn't run the task. | 
 |   const LazyContextId no_background_context_id(browser_context(), | 
 |                                                no_background->id()); | 
 |   queue.AddPendingTask( | 
 |       no_background_context_id, | 
 |       base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |  | 
 |   // Another task on the same extension doesn't increase the number of | 
 |   // extensions that have tasks and doesn't run any tasks. | 
 |   queue.AddPendingTask( | 
 |       no_background_context_id, | 
 |       base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |  | 
 |   // Adding a task on an extension with a lazy background page tries to create | 
 |   // a background host, and if that fails, runs the task immediately. | 
 |   scoped_refptr<const Extension> lazy_background = | 
 |       CreateLazyBackgroundExtension(); | 
 |   const LazyContextId lazy_background_context_id(browser_context(), | 
 |                                                  lazy_background->id()); | 
 |   queue.AddPendingTask( | 
 |       lazy_background_context_id, | 
 |       base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |   // The process manager tried to create a background host. | 
 |   EXPECT_EQ(1, process_manager()->create_count()); | 
 |   // The task ran immediately because the creation failed. | 
 |   EXPECT_EQ(1, task_run_count()); | 
 | } | 
 |  | 
 | // Tests that pending tasks are actually run. | 
 | TEST_F(LazyBackgroundTaskQueueTest, ProcessPendingTasks) { | 
 |   LazyBackgroundTaskQueue queue(browser_context()); | 
 |  | 
 |   // ProcessPendingTasks is a no-op if there are no tasks. | 
 |   scoped_refptr<const Extension> extension = CreateSimpleExtension(); | 
 |   queue.ProcessPendingTasks(nullptr, browser_context(), extension.get()); | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |  | 
 |   // Schedule a task to run. | 
 |   queue.AddPendingTask( | 
 |       LazyContextId(browser_context(), extension->id()), | 
 |       base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |  | 
 |   // Trying to run tasks for an unrelated BrowserContext should do nothing. | 
 |   content::TestBrowserContext unrelated_context; | 
 |   queue.ProcessPendingTasks(nullptr, &unrelated_context, extension.get()); | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |  | 
 |   // Processing tasks when there is one pending runs the task and removes the | 
 |   // extension from the list of extensions with pending tasks. | 
 |   queue.ProcessPendingTasks(nullptr, browser_context(), extension.get()); | 
 |   EXPECT_EQ(1, task_run_count()); | 
 |   EXPECT_EQ(0u, queue.pending_tasks_.size()); | 
 | } | 
 |  | 
 | // Tests that if a pending task was added before the extension with a lazy | 
 | // background page is loaded, then we will create the lazy background page when | 
 | // the extension is loaded. | 
 | TEST_F(LazyBackgroundTaskQueueTest, CreateLazyBackgroundPageOnExtensionLoaded) { | 
 |   LazyBackgroundTaskQueue queue(browser_context()); | 
 |  | 
 |   scoped_refptr<const Extension> lazy_background = | 
 |       ExtensionBuilder("Lazy background") | 
 |           .SetBackgroundContext(ExtensionBuilder::BackgroundContext::EVENT_PAGE) | 
 |           .SetID("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb") | 
 |           .Build(); | 
 |  | 
 |   queue.OnExtensionLoaded(browser_context(), lazy_background.get()); | 
 |   // Did not try to create a background host because there are no queued tasks. | 
 |   EXPECT_EQ(0, process_manager()->create_count()); | 
 |  | 
 |   queue.AddPendingTask( | 
 |       LazyContextId(browser_context(), lazy_background->id()), | 
 |       base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask, | 
 |                      base::Unretained(this))); | 
 |   EXPECT_EQ(1u, queue.pending_tasks_.size()); | 
 |   // Did not try to create a background host because extension is not yet | 
 |   // loaded. | 
 |   EXPECT_EQ(0, process_manager()->create_count()); | 
 |   // The task is queued. | 
 |   EXPECT_EQ(0, task_run_count()); | 
 |  | 
 |   ExtensionRegistry::Get(browser_context())->AddEnabled(lazy_background); | 
 |   queue.OnExtensionLoaded(browser_context(), lazy_background.get()); | 
 |  | 
 |   // The process manager tried to create a background host because there is a | 
 |   // queued task. | 
 |   EXPECT_EQ(1, process_manager()->create_count()); | 
 |   // The queued task ran because the creation failed. | 
 |   EXPECT_EQ(1, task_run_count()); | 
 |   EXPECT_EQ(0u, queue.pending_tasks_.size()); | 
 | } | 
 |  | 
 | }  // namespace extensions |