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.
6 #include "base/json/json_writer.h"
7 #include "base/memory/ref_counted.h"
8 #include "base/run_loop.h"
9 #include "chrome/browser/extensions/api/storage/settings_frontend.h"
10 #include "chrome/browser/extensions/api/storage/settings_namespace.h"
11 #include "chrome/browser/extensions/api/storage/settings_sync_util.h"
12 #include "chrome/browser/extensions/extension_apitest.h"
13 #include "chrome/browser/extensions/extension_service.h"
14 #include "chrome/browser/extensions/extension_system.h"
15 #include "chrome/browser/extensions/extension_system_factory.h"
16 #include "chrome/browser/extensions/extension_test_message_listener.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/common/extensions/value_builder.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "sync/api/sync_change.h"
22 #include "sync/api/sync_change_processor.h"
23 #include "sync/api/sync_error_factory.h"
24 #include "sync/api/sync_error_factory_mock.h"
25 #include "testing/gmock/include/gmock/gmock.h"
27 #if defined(ENABLE_CONFIGURATION_POLICY)
28 #include "chrome/browser/policy/browser_policy_connector.h"
29 #include "chrome/browser/policy/mock_configuration_policy_provider.h"
30 #include "chrome/browser/policy/policy_bundle.h"
31 #include "chrome/browser/policy/policy_domain_descriptor.h"
32 #include "chrome/browser/policy/policy_map.h"
33 #include "chrome/browser/policy/policy_service.h"
34 #include "chrome/browser/policy/profile_policy_connector.h"
35 #include "chrome/browser/policy/profile_policy_connector_factory.h"
38 namespace extensions {
40 using settings_namespace::FromString;
41 using settings_namespace::LOCAL;
42 using settings_namespace::MANAGED;
43 using settings_namespace::Namespace;
44 using settings_namespace::SYNC;
45 using settings_namespace::ToString;
46 using testing::AnyNumber;
47 using testing::Return;
52 // TODO(kalman): test both EXTENSION_SETTINGS and APP_SETTINGS.
53 const syncer::ModelType kModelType = syncer::EXTENSION_SETTINGS;
55 // The managed_storage extension has a key defined in its manifest, so that
56 // its extension ID is well-known and the policy system can push policies for
58 const char kManagedStorageExtensionId[] = "kjmkgkdkpedkejedfhmfcenooemhbpbo";
60 class NoopSyncChangeProcessor : public syncer::SyncChangeProcessor {
62 virtual syncer::SyncError ProcessSyncChanges(
63 const tracked_objects::Location& from_here,
64 const syncer::SyncChangeList& change_list) OVERRIDE {
65 return syncer::SyncError();
68 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
70 return syncer::SyncDataList();
73 virtual ~NoopSyncChangeProcessor() {};
76 class SyncChangeProcessorDelegate : public syncer::SyncChangeProcessor {
78 explicit SyncChangeProcessorDelegate(syncer::SyncChangeProcessor* recipient)
79 : recipient_(recipient) {
82 virtual ~SyncChangeProcessorDelegate() {}
84 // syncer::SyncChangeProcessor implementation.
85 virtual syncer::SyncError ProcessSyncChanges(
86 const tracked_objects::Location& from_here,
87 const syncer::SyncChangeList& change_list) OVERRIDE {
88 return recipient_->ProcessSyncChanges(from_here, change_list);
91 virtual syncer::SyncDataList GetAllSyncData(syncer::ModelType type) const
93 return recipient_->GetAllSyncData(type);
97 // The recipient of all sync changes.
98 syncer::SyncChangeProcessor* recipient_;
100 DISALLOW_COPY_AND_ASSIGN(SyncChangeProcessorDelegate);
105 class ExtensionSettingsApiTest : public ExtensionApiTest {
107 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
108 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
110 #if defined(ENABLE_CONFIGURATION_POLICY)
111 EXPECT_CALL(policy_provider_, IsInitializationComplete(_))
112 .WillRepeatedly(Return(true));
113 EXPECT_CALL(policy_provider_, RegisterPolicyDomain(_)).Times(AnyNumber());
114 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
119 void ReplyWhenSatisfied(
120 Namespace settings_namespace,
121 const std::string& normal_action,
122 const std::string& incognito_action) {
123 MaybeLoadAndReplyWhenSatisfied(
124 settings_namespace, normal_action, incognito_action, NULL, false);
127 const Extension* LoadAndReplyWhenSatisfied(
128 Namespace settings_namespace,
129 const std::string& normal_action,
130 const std::string& incognito_action,
131 const std::string& extension_dir) {
132 return MaybeLoadAndReplyWhenSatisfied(
140 void FinalReplyWhenSatisfied(
141 Namespace settings_namespace,
142 const std::string& normal_action,
143 const std::string& incognito_action) {
144 MaybeLoadAndReplyWhenSatisfied(
145 settings_namespace, normal_action, incognito_action, NULL, true);
148 void InitSync(syncer::SyncChangeProcessor* sync_processor) {
149 base::MessageLoop::current()->RunUntilIdle();
150 InitSyncWithSyncableService(
152 browser()->profile()->GetExtensionService()->settings_frontend()->
153 GetBackendForSync(kModelType));
156 void SendChanges(const syncer::SyncChangeList& change_list) {
157 base::MessageLoop::current()->RunUntilIdle();
158 SendChangesToSyncableService(
160 browser()->profile()->GetExtensionService()->settings_frontend()->
161 GetBackendForSync(kModelType));
164 #if defined(ENABLE_CONFIGURATION_POLICY)
165 void SetPolicies(const base::DictionaryValue& policies) {
166 scoped_ptr<policy::PolicyBundle> bundle(new policy::PolicyBundle());
167 policy::PolicyMap& policy_map = bundle->Get(policy::PolicyNamespace(
168 policy::POLICY_DOMAIN_EXTENSIONS, kManagedStorageExtensionId));
170 &policies, policy::POLICY_LEVEL_MANDATORY, policy::POLICY_SCOPE_USER);
171 policy_provider_.UpdatePolicy(bundle.Pass());
176 const Extension* MaybeLoadAndReplyWhenSatisfied(
177 Namespace settings_namespace,
178 const std::string& normal_action,
179 const std::string& incognito_action,
180 // May be NULL to imply not loading the extension.
181 const std::string* extension_dir,
182 bool is_final_action) {
183 ExtensionTestMessageListener listener("waiting", true);
184 ExtensionTestMessageListener listener_incognito("waiting_incognito", true);
186 // Only load the extension after the listeners have been set up, to avoid
187 // initialisation race conditions.
188 const Extension* extension = NULL;
190 extension = LoadExtensionIncognito(
191 test_data_dir_.AppendASCII("settings").AppendASCII(*extension_dir));
192 EXPECT_TRUE(extension);
195 EXPECT_TRUE(listener.WaitUntilSatisfied());
196 EXPECT_TRUE(listener_incognito.WaitUntilSatisfied());
199 CreateMessage(settings_namespace, normal_action, is_final_action));
200 listener_incognito.Reply(
201 CreateMessage(settings_namespace, incognito_action, is_final_action));
205 std::string CreateMessage(
206 Namespace settings_namespace,
207 const std::string& action,
208 bool is_final_action) {
209 scoped_ptr<base::DictionaryValue> message(new base::DictionaryValue());
210 message->SetString("namespace", ToString(settings_namespace));
211 message->SetString("action", action);
212 message->SetBoolean("isFinalAction", is_final_action);
213 std::string message_json;
214 base::JSONWriter::Write(message.get(), &message_json);
218 void InitSyncWithSyncableService(
219 syncer::SyncChangeProcessor* sync_processor,
220 syncer::SyncableService* settings_service) {
221 EXPECT_FALSE(settings_service->MergeDataAndStartSyncing(
223 syncer::SyncDataList(),
224 scoped_ptr<syncer::SyncChangeProcessor>(
225 new SyncChangeProcessorDelegate(sync_processor)),
226 scoped_ptr<syncer::SyncErrorFactory>(
227 new syncer::SyncErrorFactoryMock())).error().IsSet());
230 void SendChangesToSyncableService(
231 const syncer::SyncChangeList& change_list,
232 syncer::SyncableService* settings_service) {
234 settings_service->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
238 #if defined(ENABLE_CONFIGURATION_POLICY)
239 policy::MockConfigurationPolicyProvider policy_provider_;
243 // Flaky. http://crbug.com/248032
244 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, DISABLED_SimpleTest) {
245 ASSERT_TRUE(RunExtensionTest("settings/simple_test")) << message_;
248 // Structure of this test taken from IncognitoSplitMode.
249 // Note that only split-mode incognito is tested, because spanning mode
250 // incognito looks the same as normal mode when the only API activity comes
251 // from background pages.
252 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, SplitModeIncognito) {
253 // We need 2 ResultCatchers because we'll be running the same test in both
254 // regular and incognito mode.
255 ResultCatcher catcher, catcher_incognito;
256 catcher.RestrictToProfile(browser()->profile());
257 catcher_incognito.RestrictToProfile(
258 browser()->profile()->GetOffTheRecordProfile());
260 LoadAndReplyWhenSatisfied(SYNC,
261 "assertEmpty", "assertEmpty", "split_incognito");
262 ReplyWhenSatisfied(SYNC, "noop", "setFoo");
263 ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo");
264 ReplyWhenSatisfied(SYNC, "clear", "noop");
265 ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty");
266 ReplyWhenSatisfied(SYNC, "setFoo", "noop");
267 ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo");
268 ReplyWhenSatisfied(SYNC, "noop", "removeFoo");
269 FinalReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty");
271 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
272 EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message();
275 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest,
276 OnChangedNotificationsBetweenBackgroundPages) {
277 // We need 2 ResultCatchers because we'll be running the same test in both
278 // regular and incognito mode.
279 ResultCatcher catcher, catcher_incognito;
280 catcher.RestrictToProfile(browser()->profile());
281 catcher_incognito.RestrictToProfile(
282 browser()->profile()->GetOffTheRecordProfile());
284 LoadAndReplyWhenSatisfied(SYNC,
285 "assertNoNotifications", "assertNoNotifications", "split_incognito");
286 ReplyWhenSatisfied(SYNC, "noop", "setFoo");
287 ReplyWhenSatisfied(SYNC,
288 "assertAddFooNotification", "assertAddFooNotification");
289 ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications");
290 ReplyWhenSatisfied(SYNC, "removeFoo", "noop");
291 FinalReplyWhenSatisfied(SYNC,
292 "assertDeleteFooNotification", "assertDeleteFooNotification");
294 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
295 EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message();
298 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest,
299 SyncAndLocalAreasAreSeparate) {
300 // We need 2 ResultCatchers because we'll be running the same test in both
301 // regular and incognito mode.
302 ResultCatcher catcher, catcher_incognito;
303 catcher.RestrictToProfile(browser()->profile());
304 catcher_incognito.RestrictToProfile(
305 browser()->profile()->GetOffTheRecordProfile());
307 LoadAndReplyWhenSatisfied(SYNC,
308 "assertNoNotifications", "assertNoNotifications", "split_incognito");
310 ReplyWhenSatisfied(SYNC, "noop", "setFoo");
311 ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo");
312 ReplyWhenSatisfied(SYNC,
313 "assertAddFooNotification", "assertAddFooNotification");
314 ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty");
315 ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications");
317 ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications");
319 ReplyWhenSatisfied(LOCAL, "setFoo", "noop");
320 ReplyWhenSatisfied(LOCAL, "assertFoo", "assertFoo");
321 ReplyWhenSatisfied(LOCAL,
322 "assertAddFooNotification", "assertAddFooNotification");
323 ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo");
324 ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications");
326 ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications");
328 ReplyWhenSatisfied(LOCAL, "noop", "removeFoo");
329 ReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty");
330 ReplyWhenSatisfied(LOCAL,
331 "assertDeleteFooNotification", "assertDeleteFooNotification");
332 ReplyWhenSatisfied(SYNC, "assertFoo", "assertFoo");
333 ReplyWhenSatisfied(SYNC, "assertNoNotifications", "assertNoNotifications");
335 ReplyWhenSatisfied(LOCAL, "clearNotifications", "clearNotifications");
337 ReplyWhenSatisfied(SYNC, "removeFoo", "noop");
338 ReplyWhenSatisfied(SYNC, "assertEmpty", "assertEmpty");
339 ReplyWhenSatisfied(SYNC,
340 "assertDeleteFooNotification", "assertDeleteFooNotification");
341 ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications");
342 FinalReplyWhenSatisfied(LOCAL, "assertEmpty", "assertEmpty");
344 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
345 EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message();
348 // Disabled, see crbug.com/101110
349 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest,
350 DISABLED_OnChangedNotificationsFromSync) {
351 // We need 2 ResultCatchers because we'll be running the same test in both
352 // regular and incognito mode.
353 ResultCatcher catcher, catcher_incognito;
354 catcher.RestrictToProfile(browser()->profile());
355 catcher_incognito.RestrictToProfile(
356 browser()->profile()->GetOffTheRecordProfile());
358 const Extension* extension =
359 LoadAndReplyWhenSatisfied(SYNC,
360 "assertNoNotifications", "assertNoNotifications", "split_incognito");
361 const std::string& extension_id = extension->id();
363 NoopSyncChangeProcessor sync_processor;
364 InitSync(&sync_processor);
366 // Set "foo" to "bar" via sync.
367 syncer::SyncChangeList sync_changes;
368 StringValue bar("bar");
369 sync_changes.push_back(settings_sync_util::CreateAdd(
370 extension_id, "foo", bar, kModelType));
371 SendChanges(sync_changes);
373 ReplyWhenSatisfied(SYNC,
374 "assertAddFooNotification", "assertAddFooNotification");
375 ReplyWhenSatisfied(SYNC, "clearNotifications", "clearNotifications");
377 // Remove "foo" via sync.
378 sync_changes.clear();
379 sync_changes.push_back(settings_sync_util::CreateDelete(
380 extension_id, "foo", kModelType));
381 SendChanges(sync_changes);
383 FinalReplyWhenSatisfied(SYNC,
384 "assertDeleteFooNotification", "assertDeleteFooNotification");
386 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
387 EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message();
390 // Disabled, see crbug.com/101110
392 // TODO: boring test, already done in the unit tests. What we really should be
393 // be testing is that the areas don't overlap.
394 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest,
395 DISABLED_OnChangedNotificationsFromSyncNotSentToLocal) {
396 // We need 2 ResultCatchers because we'll be running the same test in both
397 // regular and incognito mode.
398 ResultCatcher catcher, catcher_incognito;
399 catcher.RestrictToProfile(browser()->profile());
400 catcher_incognito.RestrictToProfile(
401 browser()->profile()->GetOffTheRecordProfile());
403 const Extension* extension =
404 LoadAndReplyWhenSatisfied(LOCAL,
405 "assertNoNotifications", "assertNoNotifications", "split_incognito");
406 const std::string& extension_id = extension->id();
408 NoopSyncChangeProcessor sync_processor;
409 InitSync(&sync_processor);
411 // Set "foo" to "bar" via sync.
412 syncer::SyncChangeList sync_changes;
413 StringValue bar("bar");
414 sync_changes.push_back(settings_sync_util::CreateAdd(
415 extension_id, "foo", bar, kModelType));
416 SendChanges(sync_changes);
418 ReplyWhenSatisfied(LOCAL, "assertNoNotifications", "assertNoNotifications");
420 // Remove "foo" via sync.
421 sync_changes.clear();
422 sync_changes.push_back(settings_sync_util::CreateDelete(
423 extension_id, "foo", kModelType));
424 SendChanges(sync_changes);
426 FinalReplyWhenSatisfied(LOCAL,
427 "assertNoNotifications", "assertNoNotifications");
429 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
430 EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message();
433 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, IsStorageEnabled) {
434 SettingsFrontend* frontend =
435 browser()->profile()->GetExtensionService()->settings_frontend();
436 EXPECT_TRUE(frontend->IsStorageEnabled(LOCAL));
437 EXPECT_TRUE(frontend->IsStorageEnabled(SYNC));
439 #if defined(ENABLE_CONFIGURATION_POLICY)
440 EXPECT_TRUE(frontend->IsStorageEnabled(MANAGED));
442 EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED));
446 #if defined(ENABLE_CONFIGURATION_POLICY)
448 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, PolicyDomainDescriptor) {
449 // Verifies that the PolicyDomainDescriptor for the extensions domain is
450 // created on startup.
451 Profile* profile = browser()->profile();
452 ExtensionSystem* extension_system =
453 ExtensionSystemFactory::GetForProfile(profile);
454 if (!extension_system->ready().is_signaled()) {
455 // Wait until the extension system is ready.
456 base::RunLoop run_loop;
457 extension_system->ready().Post(FROM_HERE, run_loop.QuitClosure());
459 ASSERT_TRUE(extension_system->ready().is_signaled());
462 policy::ProfilePolicyConnector* connector =
463 policy::ProfilePolicyConnectorFactory::GetForProfile(profile);
464 policy::PolicyService* service = connector->policy_service();
465 scoped_refptr<const policy::PolicyDomainDescriptor> descriptor =
466 service->GetPolicyDomainDescriptor(policy::POLICY_DOMAIN_EXTENSIONS);
467 EXPECT_TRUE(descriptor.get());
470 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorage) {
471 // Set policies for the test extension.
472 scoped_ptr<base::DictionaryValue> policy = extensions::DictionaryBuilder()
473 .Set("string-policy", "value")
474 .Set("int-policy", -123)
475 .Set("double-policy", 456e7)
476 .SetBoolean("boolean-policy", true)
477 .Set("list-policy", extensions::ListBuilder()
481 .Set("dict-policy", extensions::DictionaryBuilder()
482 .Set("list", extensions::ListBuilder()
483 .Append(extensions::DictionaryBuilder()
486 .Append(extensions::DictionaryBuilder()
489 SetPolicies(*policy);
490 // Now run the extension.
491 ASSERT_TRUE(RunExtensionTest("settings/managed_storage")) << message_;
494 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, PRE_ManagedStorageEvents) {
495 ResultCatcher catcher;
497 // This test starts without any test extensions installed.
498 EXPECT_FALSE(GetSingleLoadedExtension());
501 // Set policies for the test extension.
502 scoped_ptr<base::DictionaryValue> policy = extensions::DictionaryBuilder()
503 .Set("constant-policy", "aaa")
504 .Set("changes-policy", "bbb")
505 .Set("deleted-policy", "ccc")
507 SetPolicies(*policy);
509 ExtensionTestMessageListener ready_listener("ready", false);
510 // Load the extension to install the event listener.
511 const Extension* extension = LoadExtension(
512 test_data_dir_.AppendASCII("settings/managed_storage_events"));
513 ASSERT_TRUE(extension);
514 // Wait until the extension sends the "ready" message.
515 ASSERT_TRUE(ready_listener.WaitUntilSatisfied());
517 // Now change the policies and wait until the extension is done.
518 policy = extensions::DictionaryBuilder()
519 .Set("constant-policy", "aaa")
520 .Set("changes-policy", "ddd")
521 .Set("new-policy", "eee")
523 SetPolicies(*policy);
524 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
527 #if defined(OS_CHROMEOS) || defined(OS_WIN) || \
528 (defined(OS_LINUX) && defined(USE_AURA))
529 // Flakily times out. http://crbug.com/171477
530 #define MAYBE_ManagedStorageEvents DISABLED_ManagedStorageEvents
532 #define MAYBE_ManagedStorageEvents ManagedStorageEvents
535 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, MAYBE_ManagedStorageEvents) {
536 // This test runs after PRE_ManagedStorageEvents without having deleted the
537 // profile, so the extension is still around. While the browser restarted the
538 // policy went back to the empty default, and so the extension should receive
539 // the corresponding change events.
541 ResultCatcher catcher;
543 // Verify that the test extension is still installed.
544 const Extension* extension = GetSingleLoadedExtension();
545 ASSERT_TRUE(extension);
546 EXPECT_EQ(kManagedStorageExtensionId, extension->id());
548 // Running the test again skips the onInstalled callback, and just triggers
549 // the onChanged notification.
550 EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
553 #endif // defined(ENABLE_CONFIGURATION_POLICY)
555 IN_PROC_BROWSER_TEST_F(ExtensionSettingsApiTest, ManagedStorageDisabled) {
556 // Disable the 'managed' namespace. This is redundant when
557 // ENABLE_CONFIGURATION_POLICY is not defined.
558 SettingsFrontend* frontend =
559 browser()->profile()->GetExtensionService()->settings_frontend();
560 frontend->DisableStorageForTesting(MANAGED);
561 EXPECT_FALSE(frontend->IsStorageEnabled(MANAGED));
562 // Now run the extension.
563 ASSERT_TRUE(RunExtensionTest("settings/managed_storage_disabled"))
567 } // namespace extensions