1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "extensions/browser/lazy_background_task_queue.h"
6
7#include "base/bind.h"
8#include "components/keyed_service/content/browser_context_dependency_manager.h"
9#include "content/public/browser/notification_service.h"
10#include "content/public/test/test_browser_context.h"
11#include "extensions/browser/extension_registry.h"
12#include "extensions/browser/extension_registry_factory.h"
13#include "extensions/browser/extension_system.h"
14#include "extensions/browser/extension_system_provider.h"
15#include "extensions/browser/extensions_test.h"
16#include "extensions/browser/mock_extension_system.h"
17#include "extensions/browser/process_manager.h"
18#include "extensions/browser/test_extensions_browser_client.h"
19#include "extensions/common/extension.h"
20#include "extensions/common/extension_builder.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23using content::BrowserContext;
24
25namespace extensions {
26namespace {
27
28// A ProcessManager that doesn't create background host pages.
29class TestProcessManager : public ProcessManager {
30 public:
31  explicit TestProcessManager(BrowserContext* context)
32      : ProcessManager(context, context, ExtensionRegistry::Get(context)),
33        create_count_(0) {
34    // ProcessManager constructor above assumes non-incognito.
35    DCHECK(!context->IsOffTheRecord());
36  }
37  virtual ~TestProcessManager() {}
38
39  int create_count() { return create_count_; }
40
41  // ProcessManager overrides:
42  virtual bool CreateBackgroundHost(const Extension* extension,
43                                    const GURL& url) OVERRIDE {
44    // Don't actually try to create a web contents.
45    create_count_++;
46    return false;
47  }
48
49 private:
50  int create_count_;
51
52  DISALLOW_COPY_AND_ASSIGN(TestProcessManager);
53};
54
55// A simple ExtensionSystem that returns a TestProcessManager.
56class MockExtensionSystemWithProcessManager : public MockExtensionSystem {
57 public:
58  explicit MockExtensionSystemWithProcessManager(BrowserContext* context)
59      : MockExtensionSystem(context), test_process_manager_(context) {}
60  virtual ~MockExtensionSystemWithProcessManager() {}
61
62  virtual ProcessManager* process_manager() OVERRIDE {
63    return &test_process_manager_;
64  }
65
66 private:
67  TestProcessManager test_process_manager_;
68};
69
70}  // namespace
71
72// Derives from ExtensionsTest to provide content module and keyed service
73// initialization.
74class LazyBackgroundTaskQueueTest : public ExtensionsTest {
75 public:
76  LazyBackgroundTaskQueueTest()
77      : notification_service_(content::NotificationService::Create()),
78        task_run_count_(0) {
79    extensions_browser_client()->set_extension_system_factory(
80        &extension_system_factory_);
81  }
82  virtual ~LazyBackgroundTaskQueueTest() {}
83
84  int task_run_count() { return task_run_count_; }
85
86  // A simple callback for AddPendingTask.
87  void RunPendingTask(ExtensionHost* host) {
88    task_run_count_++;
89  }
90
91  // Creates and registers an extension without a background page.
92  scoped_refptr<Extension> CreateSimpleExtension() {
93    scoped_refptr<Extension> extension = ExtensionBuilder()
94        .SetManifest(DictionaryBuilder()
95                     .Set("name", "No background")
96                     .Set("version", "1")
97                     .Set("manifest_version", 2))
98        .SetID("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
99        .Build();
100    ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
101    return extension;
102  }
103
104  // Creates and registers an extension with a lazy background page.
105  scoped_refptr<Extension> CreateLazyBackgroundExtension() {
106    scoped_refptr<Extension> extension = ExtensionBuilder()
107        .SetManifest(DictionaryBuilder()
108            .Set("name", "Lazy background")
109            .Set("version", "1")
110            .Set("manifest_version", 2)
111            .Set("background",
112                  DictionaryBuilder()
113                  .Set("page", "background.html")
114                  .SetBoolean("persistent", false)))
115        .SetID("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
116        .Build();
117    ExtensionRegistry::Get(browser_context())->AddEnabled(extension);
118    return extension;
119  }
120
121 private:
122  scoped_ptr<content::NotificationService> notification_service_;
123  MockExtensionSystemFactory<MockExtensionSystemWithProcessManager>
124      extension_system_factory_;
125
126  // The total number of pending tasks that have been executed.
127  int task_run_count_;
128
129  DISALLOW_COPY_AND_ASSIGN(LazyBackgroundTaskQueueTest);
130};
131
132// Tests that only extensions with background pages should have tasks queued.
133TEST_F(LazyBackgroundTaskQueueTest, ShouldEnqueueTask) {
134  LazyBackgroundTaskQueue queue(browser_context());
135
136  // Build a simple extension with no background page.
137  scoped_refptr<Extension> no_background = CreateSimpleExtension();
138  EXPECT_FALSE(queue.ShouldEnqueueTask(browser_context(), no_background.get()));
139
140  // Build another extension with a background page.
141  scoped_refptr<Extension> with_background = CreateLazyBackgroundExtension();
142  EXPECT_TRUE(
143      queue.ShouldEnqueueTask(browser_context(), with_background.get()));
144}
145
146// Tests that adding tasks actually increases the pending task count, and that
147// multiple extensions can have pending tasks.
148TEST_F(LazyBackgroundTaskQueueTest, AddPendingTask) {
149  // Get our TestProcessManager.
150  MockExtensionSystem* extension_system = static_cast<MockExtensionSystem*>(
151      ExtensionSystem::Get(browser_context()));
152  TestProcessManager* process_manager =
153      static_cast<TestProcessManager*>(extension_system->process_manager());
154
155  LazyBackgroundTaskQueue queue(browser_context());
156
157  // Build a simple extension with no background page.
158  scoped_refptr<Extension> no_background = CreateSimpleExtension();
159
160  // Adding a pending task increases the number of extensions with tasks, but
161  // doesn't run the task.
162  queue.AddPendingTask(browser_context(),
163                       no_background->id(),
164                       base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask,
165                                  base::Unretained(this)));
166  EXPECT_EQ(1u, queue.extensions_with_pending_tasks());
167  EXPECT_EQ(0, task_run_count());
168
169  // Another task on the same extension doesn't increase the number of
170  // extensions that have tasks and doesn't run any tasks.
171  queue.AddPendingTask(browser_context(),
172                       no_background->id(),
173                       base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask,
174                                  base::Unretained(this)));
175  EXPECT_EQ(1u, queue.extensions_with_pending_tasks());
176  EXPECT_EQ(0, task_run_count());
177
178  // Adding a task on an extension with a lazy background page tries to create
179  // a background host, and if that fails, runs the task immediately.
180  scoped_refptr<Extension> lazy_background = CreateLazyBackgroundExtension();
181  queue.AddPendingTask(browser_context(),
182                       lazy_background->id(),
183                       base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask,
184                                  base::Unretained(this)));
185  EXPECT_EQ(2u, queue.extensions_with_pending_tasks());
186  // The process manager tried to create a background host.
187  EXPECT_EQ(1, process_manager->create_count());
188  // The task ran immediately because the creation failed.
189  EXPECT_EQ(1, task_run_count());
190}
191
192// Tests that pending tasks are actually run.
193TEST_F(LazyBackgroundTaskQueueTest, ProcessPendingTasks) {
194  LazyBackgroundTaskQueue queue(browser_context());
195
196  // ProcessPendingTasks is a no-op if there are no tasks.
197  scoped_refptr<Extension> extension = CreateSimpleExtension();
198  queue.ProcessPendingTasks(NULL, browser_context(), extension.get());
199  EXPECT_EQ(0, task_run_count());
200
201  // Schedule a task to run.
202  queue.AddPendingTask(browser_context(),
203                       extension->id(),
204                       base::Bind(&LazyBackgroundTaskQueueTest::RunPendingTask,
205                                  base::Unretained(this)));
206  EXPECT_EQ(0, task_run_count());
207  EXPECT_EQ(1u, queue.extensions_with_pending_tasks());
208
209  // Trying to run tasks for an unrelated BrowserContext should do nothing.
210  content::TestBrowserContext unrelated_context;
211  queue.ProcessPendingTasks(NULL, &unrelated_context, extension.get());
212  EXPECT_EQ(0, task_run_count());
213  EXPECT_EQ(1u, queue.extensions_with_pending_tasks());
214
215  // Processing tasks when there is one pending runs the task and removes the
216  // extension from the list of extensions with pending tasks.
217  queue.ProcessPendingTasks(NULL, browser_context(), extension.get());
218  EXPECT_EQ(1, task_run_count());
219  EXPECT_EQ(0u, queue.extensions_with_pending_tasks());
220}
221
222}  // namespace extensions
223