1 // Copyright (c) 2012 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.
5 // TODO(rickcam): Bug 73183: Add unit tests for image loading
10 #include "chrome/browser/background/background_application_list_model.h"
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/stl_util.h"
17 #include "chrome/browser/extensions/extension_service.h"
18 #include "chrome/browser/extensions/extension_service_test_base.h"
19 #include "chrome/browser/extensions/permissions_updater.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/notification_registrar.h"
22 #include "content/public/browser/notification_types.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/uninstall_reason.h"
26 #include "extensions/common/extension.h"
27 #include "extensions/common/manifest_constants.h"
28 #include "extensions/common/permissions/api_permission.h"
29 #include "extensions/common/permissions/permission_set.h"
30 #include "extensions/common/permissions/permissions_data.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 // This value is used to seed the PRNG at the beginning of a sequence of
34 // operations to produce a repeatable sequence.
35 #define RANDOM_SEED (0x33F7A7A7)
37 using extensions::APIPermission;
38 using extensions::Extension;
40 // For ExtensionService interface when it requires a path that is not used.
41 base::FilePath bogus_file_pathname(const std::string& name) {
42 return base::FilePath(FILE_PATH_LITERAL("//foobar_nonexistent"))
46 class BackgroundApplicationListModelTest
47 : public extensions::ExtensionServiceTestBase {
49 BackgroundApplicationListModelTest() {}
50 ~BackgroundApplicationListModelTest() override {}
53 void InitializeAndLoadEmptyExtensionService() {
54 InitializeEmptyExtensionService();
55 service_->Init(); /* Sends EXTENSIONS_READY */
58 bool IsBackgroundApp(const Extension& app) {
59 return BackgroundApplicationListModel::IsBackgroundApp(app,
64 enum PushMessagingOption {
66 PUSH_MESSAGING_PERMISSION,
67 PUSH_MESSAGING_BUT_NOT_BACKGROUND
70 // Returns a barebones test Extension object with the specified |name|. The
71 // returned extension will include background permission iff
72 // |background_permission| is true and pushMessaging permission if requested
73 // by |push_messaging| value. Also the extension may have a specific id set
74 // to test the case when it has a pushMessaging permission but is not
75 // considered a background app based on a whitelist.
76 static scoped_refptr<Extension> CreateExtensionBase(
77 const std::string& name,
78 bool background_permission,
79 PushMessagingOption push_messaging) {
80 base::DictionaryValue manifest;
81 manifest.SetString(extensions::manifest_keys::kVersion, "1.0.0.0");
82 manifest.SetString(extensions::manifest_keys::kName, name);
83 base::ListValue* permissions = new base::ListValue();
84 manifest.Set(extensions::manifest_keys::kPermissions, permissions);
85 if (background_permission) {
86 permissions->Append(new base::StringValue("background"));
88 if (push_messaging == PUSH_MESSAGING_PERMISSION ||
89 push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
90 permissions->Append(new base::StringValue("pushMessaging"));
94 scoped_refptr<Extension> extension;
96 // There is a whitelist for extensions that have pushMessaging permission but
97 // are not considered a background app. Create a test extension with a known
99 if (push_messaging == PUSH_MESSAGING_BUT_NOT_BACKGROUND) {
100 extension = Extension::Create(
101 bogus_file_pathname(name),
102 extensions::Manifest::INVALID_LOCATION,
105 "aaaabbbbccccddddeeeeffffgggghhhh",
108 extension = Extension::Create(
109 bogus_file_pathname(name),
110 extensions::Manifest::INVALID_LOCATION,
116 // Cannot ASSERT_* here because that attempts an illegitimate return.
117 // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ
118 EXPECT_TRUE(extension.get() != NULL) << error;
122 static scoped_refptr<Extension> CreateExtension(const std::string& name,
123 bool background_permission) {
124 return CreateExtensionBase(name, background_permission, NO_PUSH_MESSAGING);
128 std::string GenerateUniqueExtensionName() {
129 static int uniqueness = 0;
130 std::ostringstream output;
131 output << "Unique Named Extension " << uniqueness;
136 void AddBackgroundPermission(ExtensionService* service,
137 Extension* extension) {
138 if (BackgroundApplicationListModel::IsBackgroundApp(*extension,
139 service->profile())) {
143 scoped_refptr<Extension> temporary =
144 CreateExtension(GenerateUniqueExtensionName(), true);
145 scoped_refptr<const extensions::PermissionSet> permissions =
146 temporary->permissions_data()->active_permissions();
147 extensions::PermissionsUpdater(service->profile()).AddPermissions(
148 extension, permissions.get());
151 void RemoveBackgroundPermission(ExtensionService* service,
152 Extension* extension) {
153 if (!BackgroundApplicationListModel::IsBackgroundApp(*extension,
154 service->profile())) {
157 extensions::PermissionsUpdater(service->profile()).RemovePermissions(
158 extension, extension->permissions_data()->active_permissions().get());
161 void AddEphemeralApp(const Extension* extension, ExtensionService* service) {
162 extensions::ExtensionPrefs* prefs =
163 extensions::ExtensionPrefs::Get(service->profile());
165 prefs->OnExtensionInstalled(extension,
166 extensions::Extension::ENABLED,
167 syncer::StringOrdinal(),
168 extensions::kInstallFlagIsEphemeral,
171 service->AddExtension(extension);
176 // Crashes on Mac tryslaves.
177 // http://crbug.com/165458
178 #if defined(OS_MACOSX) || defined(OS_LINUX)
179 #define MAYBE_ExplicitTest DISABLED_ExplicitTest
181 #define MAYBE_ExplicitTest ExplicitTest
183 // With minimal test logic, verifies behavior over an explicit set of
184 // extensions, of which some are Background Apps and others are not.
185 TEST_F(BackgroundApplicationListModelTest, MAYBE_ExplicitTest) {
186 InitializeAndLoadEmptyExtensionService();
187 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
189 ASSERT_TRUE(service);
190 ASSERT_TRUE(service->is_ready());
191 ASSERT_TRUE(service->extensions());
192 ASSERT_TRUE(service->extensions()->is_empty());
193 scoped_ptr<BackgroundApplicationListModel> model(
194 new BackgroundApplicationListModel(profile_.get()));
195 ASSERT_EQ(0U, model->size());
197 scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
198 scoped_refptr<Extension> ext2 = CreateExtension("bravo", false);
199 scoped_refptr<Extension> ext3 = CreateExtension("charlie", false);
200 scoped_refptr<Extension> bgapp1 = CreateExtension("delta", true);
201 scoped_refptr<Extension> bgapp2 = CreateExtension("echo", true);
202 ASSERT_TRUE(service->extensions() != NULL);
203 ASSERT_EQ(0U, service->extensions()->size());
204 ASSERT_EQ(0U, model->size());
206 // Add alternating Extensions and Background Apps
207 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
208 service->AddExtension(ext1.get());
209 ASSERT_EQ(1U, service->extensions()->size());
210 ASSERT_EQ(0U, model->size());
211 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
212 service->AddExtension(bgapp1.get());
213 ASSERT_EQ(2U, service->extensions()->size());
214 ASSERT_EQ(1U, model->size());
215 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
216 service->AddExtension(ext2.get());
217 ASSERT_EQ(3U, service->extensions()->size());
218 ASSERT_EQ(1U, model->size());
219 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
220 service->AddExtension(bgapp2.get());
221 ASSERT_EQ(4U, service->extensions()->size());
222 ASSERT_EQ(2U, model->size());
223 ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
224 service->AddExtension(ext3.get());
225 ASSERT_EQ(5U, service->extensions()->size());
226 ASSERT_EQ(2U, model->size());
228 // Remove in FIFO order.
229 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
230 service->UninstallExtension(ext1->id(),
231 extensions::UNINSTALL_REASON_FOR_TESTING,
232 base::Bind(&base::DoNothing),
234 ASSERT_EQ(4U, service->extensions()->size());
235 ASSERT_EQ(2U, model->size());
236 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
237 service->UninstallExtension(bgapp1->id(),
238 extensions::UNINSTALL_REASON_FOR_TESTING,
239 base::Bind(&base::DoNothing),
241 ASSERT_EQ(3U, service->extensions()->size());
242 ASSERT_EQ(1U, model->size());
243 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
244 service->UninstallExtension(ext2->id(),
245 extensions::UNINSTALL_REASON_FOR_TESTING,
246 base::Bind(&base::DoNothing),
248 ASSERT_EQ(2U, service->extensions()->size());
249 ASSERT_EQ(1U, model->size());
250 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
251 service->UninstallExtension(bgapp2->id(),
252 extensions::UNINSTALL_REASON_FOR_TESTING,
253 base::Bind(&base::DoNothing),
255 ASSERT_EQ(1U, service->extensions()->size());
256 ASSERT_EQ(0U, model->size());
257 ASSERT_FALSE(IsBackgroundApp(*ext3.get()));
258 service->UninstallExtension(ext3->id(),
259 extensions::UNINSTALL_REASON_FOR_TESTING,
260 base::Bind(&base::DoNothing),
262 ASSERT_EQ(0U, service->extensions()->size());
263 ASSERT_EQ(0U, model->size());
266 // Verifies that pushMessaging also triggers background detection, except
267 // when extension is in a whitelist.
268 TEST_F(BackgroundApplicationListModelTest, PushMessagingTest) {
269 InitializeAndLoadEmptyExtensionService();
270 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
272 ASSERT_TRUE(service);
273 ASSERT_TRUE(service->is_ready());
274 ASSERT_TRUE(service->extensions());
275 ASSERT_TRUE(service->extensions()->is_empty());
276 scoped_ptr<BackgroundApplicationListModel> model(
277 new BackgroundApplicationListModel(profile_.get()));
278 ASSERT_EQ(0U, model->size());
280 scoped_refptr<Extension> ext1 = CreateExtension("alpha", false);
281 scoped_refptr<Extension> ext2 =
282 CreateExtensionBase("charlie", false, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
283 scoped_refptr<Extension> bgapp1 =
284 CreateExtensionBase("bravo", false, PUSH_MESSAGING_PERMISSION);
285 scoped_refptr<Extension> bgapp2 =
286 CreateExtensionBase("delta", true, PUSH_MESSAGING_PERMISSION);
287 scoped_refptr<Extension> bgapp3 =
288 CreateExtensionBase("echo", true, PUSH_MESSAGING_BUT_NOT_BACKGROUND);
289 ASSERT_TRUE(service->extensions() != NULL);
290 ASSERT_EQ(0U, service->extensions()->size());
291 ASSERT_EQ(0U, model->size());
293 // Add alternating Extensions and Background Apps
294 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
295 service->AddExtension(ext1.get());
296 ASSERT_EQ(1U, service->extensions()->size());
297 ASSERT_EQ(0U, model->size());
298 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
299 service->AddExtension(bgapp1.get());
300 ASSERT_EQ(2U, service->extensions()->size());
301 ASSERT_EQ(1U, model->size());
302 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
303 service->AddExtension(ext2.get());
304 ASSERT_EQ(3U, service->extensions()->size());
305 ASSERT_EQ(1U, model->size());
306 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
307 service->AddExtension(bgapp2.get());
308 ASSERT_EQ(4U, service->extensions()->size());
309 ASSERT_EQ(2U, model->size());
310 // Need to remove ext2 because it uses same id as bgapp3.
311 ASSERT_FALSE(IsBackgroundApp(*ext2.get()));
312 service->UninstallExtension(ext2->id(),
313 extensions::UNINSTALL_REASON_FOR_TESTING,
314 base::Bind(&base::DoNothing),
316 ASSERT_EQ(3U, service->extensions()->size());
317 ASSERT_EQ(2U, model->size());
318 ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
319 service->AddExtension(bgapp3.get());
320 ASSERT_EQ(4U, service->extensions()->size());
321 ASSERT_EQ(3U, model->size());
323 // Remove in FIFO order.
324 ASSERT_FALSE(IsBackgroundApp(*ext1.get()));
325 service->UninstallExtension(ext1->id(),
326 extensions::UNINSTALL_REASON_FOR_TESTING,
327 base::Bind(&base::DoNothing),
329 ASSERT_EQ(3U, service->extensions()->size());
330 ASSERT_EQ(3U, model->size());
331 ASSERT_TRUE(IsBackgroundApp(*bgapp1.get()));
332 service->UninstallExtension(bgapp1->id(),
333 extensions::UNINSTALL_REASON_FOR_TESTING,
334 base::Bind(&base::DoNothing),
336 ASSERT_EQ(2U, service->extensions()->size());
337 ASSERT_EQ(2U, model->size());
338 ASSERT_TRUE(IsBackgroundApp(*bgapp2.get()));
339 service->UninstallExtension(bgapp2->id(),
340 extensions::UNINSTALL_REASON_FOR_TESTING,
341 base::Bind(&base::DoNothing),
343 ASSERT_EQ(1U, service->extensions()->size());
344 ASSERT_EQ(1U, model->size());
345 ASSERT_TRUE(IsBackgroundApp(*bgapp3.get()));
346 service->UninstallExtension(bgapp3->id(),
347 extensions::UNINSTALL_REASON_FOR_TESTING,
348 base::Bind(&base::DoNothing),
350 ASSERT_EQ(0U, service->extensions()->size());
351 ASSERT_EQ(0U, model->size());
354 // Verifies that an ephemeral app cannot trigger background mode.
355 TEST_F(BackgroundApplicationListModelTest, EphemeralAppTest) {
356 InitializeAndLoadEmptyExtensionService();
357 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
359 ASSERT_TRUE(service);
360 ASSERT_TRUE(service->is_ready());
361 ASSERT_TRUE(service->extensions());
362 ASSERT_TRUE(service->extensions()->is_empty());
363 scoped_ptr<BackgroundApplicationListModel> model(
364 new BackgroundApplicationListModel(profile_.get()));
365 ASSERT_EQ(0U, model->size());
367 scoped_refptr<Extension> installed =
368 CreateExtensionBase("installed", false, PUSH_MESSAGING_PERMISSION);
369 scoped_refptr<Extension> ephemeral =
370 CreateExtensionBase("ephemeral", false, PUSH_MESSAGING_PERMISSION);
371 scoped_refptr<Extension> background = CreateExtension("background", true);
373 // Installed app with push messaging permissions can trigger background mode.
374 ASSERT_TRUE(IsBackgroundApp(*installed.get()));
375 service->AddExtension(installed.get());
376 ASSERT_EQ(1U, service->extensions()->size());
377 ASSERT_EQ(1U, model->size());
378 // An ephemeral app with push messaging permissions should not trigger
380 AddEphemeralApp(ephemeral.get(), service);
381 ASSERT_FALSE(IsBackgroundApp(*ephemeral.get()));
382 ASSERT_EQ(2U, service->extensions()->size());
383 ASSERT_EQ(1U, model->size());
384 // An ephemeral app with the background permission should not trigger
386 AddEphemeralApp(background.get(), service);
387 ASSERT_FALSE(IsBackgroundApp(*background.get()));
388 ASSERT_EQ(3U, service->extensions()->size());
389 ASSERT_EQ(1U, model->size());
391 // If the ephemeral app becomes promoted to an installed app, it can now
392 // trigger background mode.
393 service->PromoteEphemeralApp(ephemeral.get(), false /*from sync*/);
394 ASSERT_TRUE(IsBackgroundApp(*ephemeral.get()));
395 ASSERT_EQ(3U, service->extensions()->size());
396 ASSERT_EQ(2U, model->size());
399 // With minimal test logic, verifies behavior with dynamic permissions.
400 TEST_F(BackgroundApplicationListModelTest, AddRemovePermissionsTest) {
401 InitializeAndLoadEmptyExtensionService();
402 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
404 ASSERT_TRUE(service);
405 ASSERT_TRUE(service->is_ready());
406 ASSERT_TRUE(service->extensions());
407 ASSERT_TRUE(service->extensions()->is_empty());
408 scoped_ptr<BackgroundApplicationListModel> model(
409 new BackgroundApplicationListModel(profile_.get()));
410 ASSERT_EQ(0U, model->size());
412 scoped_refptr<Extension> ext = CreateExtension("extension", false);
414 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
415 scoped_refptr<Extension> bgapp = CreateExtension("application", true);
417 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
418 ASSERT_TRUE(service->extensions() != NULL);
419 ASSERT_EQ(0U, service->extensions()->size());
420 ASSERT_EQ(0U, model->size());
422 // Add one (non-background) extension and one background application
423 ASSERT_FALSE(IsBackgroundApp(*ext.get()));
424 service->AddExtension(ext.get());
425 ASSERT_EQ(1U, service->extensions()->size());
426 ASSERT_EQ(0U, model->size());
427 ASSERT_TRUE(IsBackgroundApp(*bgapp.get()));
428 service->AddExtension(bgapp.get());
429 ASSERT_EQ(2U, service->extensions()->size());
430 ASSERT_EQ(1U, model->size());
432 // Change permissions back and forth
433 AddBackgroundPermission(service, ext.get());
435 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
436 ASSERT_EQ(2U, service->extensions()->size());
437 ASSERT_EQ(2U, model->size());
438 RemoveBackgroundPermission(service, bgapp.get());
440 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
441 ASSERT_EQ(2U, service->extensions()->size());
442 ASSERT_EQ(1U, model->size());
443 RemoveBackgroundPermission(service, ext.get());
445 ext->permissions_data()->HasAPIPermission(APIPermission::kBackground));
446 ASSERT_EQ(2U, service->extensions()->size());
447 ASSERT_EQ(0U, model->size());
448 AddBackgroundPermission(service, bgapp.get());
450 bgapp->permissions_data()->HasAPIPermission(APIPermission::kBackground));
451 ASSERT_EQ(2U, service->extensions()->size());
452 ASSERT_EQ(1U, model->size());
455 typedef std::set<scoped_refptr<Extension> > ExtensionCollection;
458 void AddExtension(ExtensionService* service,
459 ExtensionCollection* extensions,
460 BackgroundApplicationListModel* model,
463 bool create_background = false;
465 create_background = true;
468 scoped_refptr<Extension> extension =
469 CreateExtension(GenerateUniqueExtensionName(), create_background);
470 ASSERT_EQ(BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
473 extensions->insert(extension);
475 ASSERT_EQ(*count, extensions->size());
476 service->AddExtension(extension.get());
477 ASSERT_EQ(*count, service->extensions()->size());
478 ASSERT_EQ(*expected, model->size());
481 void RemoveExtension(ExtensionService* service,
482 ExtensionCollection* extensions,
483 BackgroundApplicationListModel* model,
485 size_t* count) { // Maybe remove an extension.
486 ExtensionCollection::iterator cursor = extensions->begin();
487 if (cursor == extensions->end()) {
488 // Nothing to remove. Just verify accounting.
489 ASSERT_EQ(0U, *count);
490 ASSERT_EQ(0U, *expected);
491 ASSERT_EQ(0U, service->extensions()->size());
492 ASSERT_EQ(0U, model->size());
494 // Randomly select which extension to remove
495 if (extensions->size() > 1) {
496 int offset = rand() % (extensions->size() - 1);
497 for (int index = 0; index < offset; ++index)
500 scoped_refptr<Extension> extension = cursor->get();
501 std::string id = extension->id();
502 if (BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
503 service->profile())) {
506 extensions->erase(cursor);
508 ASSERT_EQ(*count, extensions->size());
509 service->UninstallExtension(extension->id(),
510 extensions::UNINSTALL_REASON_FOR_TESTING,
511 base::Bind(&base::DoNothing),
513 ASSERT_EQ(*count, service->extensions()->size());
514 ASSERT_EQ(*expected, model->size());
518 void TogglePermission(ExtensionService* service,
519 ExtensionCollection* extensions,
520 BackgroundApplicationListModel* model,
523 ExtensionCollection::iterator cursor = extensions->begin();
524 if (cursor == extensions->end()) {
525 // Nothing to toggle. Just verify accounting.
526 ASSERT_EQ(0U, *count);
527 ASSERT_EQ(0U, *expected);
528 ASSERT_EQ(0U, service->extensions()->size());
529 ASSERT_EQ(0U, model->size());
531 // Randomly select which extension to toggle.
532 if (extensions->size() > 1) {
533 int offset = rand() % (extensions->size() - 1);
534 for (int index = 0; index < offset; ++index)
537 scoped_refptr<Extension> extension = cursor->get();
538 std::string id = extension->id();
539 if (BackgroundApplicationListModel::IsBackgroundApp(*extension.get(),
540 service->profile())) {
542 ASSERT_EQ(*count, extensions->size());
543 RemoveBackgroundPermission(service, extension.get());
544 ASSERT_EQ(*count, service->extensions()->size());
545 ASSERT_EQ(*expected, model->size());
548 ASSERT_EQ(*count, extensions->size());
549 AddBackgroundPermission(service, extension.get());
550 ASSERT_EQ(*count, service->extensions()->size());
551 ASSERT_EQ(*expected, model->size());
557 // Verifies behavior with a pseudo-randomly generated set of actions: Adding and
558 // removing extensions, of which some are Background Apps and others are not.
559 TEST_F(BackgroundApplicationListModelTest, RandomTest) {
560 InitializeAndLoadEmptyExtensionService();
561 ExtensionService* service = extensions::ExtensionSystem::Get(profile_.get())->
563 ASSERT_TRUE(service);
564 ASSERT_TRUE(service->is_ready());
565 ASSERT_TRUE(service->extensions());
566 ASSERT_TRUE(service->extensions()->is_empty());
567 scoped_ptr<BackgroundApplicationListModel> model(
568 new BackgroundApplicationListModel(profile_.get()));
569 ASSERT_EQ(0U, model->size());
571 static const int kIterations = 20;
572 ExtensionCollection extensions;
576 for (int index = 0; index < kIterations; ++index) {
577 switch (rand() % 3) {
579 AddExtension(service, &extensions, model.get(), &expected, &count);
582 RemoveExtension(service, &extensions, model.get(), &expected, &count);
585 TogglePermission(service, &extensions, model.get(), &expected, &count);