Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / menu_manager_unittest.cc
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.
4
5 #include <vector>
6
7 #include "base/files/scoped_temp_dir.h"
8 #include "base/json/json_reader.h"
9 #include "base/memory/scoped_vector.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/path_service.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/extensions/event_names.h"
17 #include "chrome/browser/extensions/extension_system_factory.h"
18 #include "chrome/browser/extensions/menu_manager.h"
19 #include "chrome/browser/extensions/test_extension_prefs.h"
20 #include "chrome/browser/extensions/test_extension_system.h"
21 #include "chrome/browser/prefs/pref_service_syncable.h"
22 #include "chrome/common/chrome_paths.h"
23 #include "chrome/common/extensions/api/context_menus.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/common/context_menu_params.h"
27 #include "content/public/test/test_browser_thread.h"
28 #include "extensions/browser/event_router.h"
29 #include "extensions/browser/extension_system.h"
30 #include "extensions/common/extension.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33
34 using content::BrowserThread;
35 using testing::_;
36 using testing::AtLeast;
37 using testing::DeleteArg;
38 using testing::InSequence;
39 using testing::Return;
40 using testing::SaveArg;
41
42 namespace extensions {
43
44 namespace context_menus = api::context_menus;
45
46 // Base class for tests.
47 class MenuManagerTest : public testing::Test {
48  public:
49   MenuManagerTest()
50       : ui_thread_(BrowserThread::UI, &message_loop_),
51         file_thread_(BrowserThread::FILE, &message_loop_),
52         manager_(&profile_, ExtensionSystem::Get(&profile_)->state_store()),
53         prefs_(message_loop_.message_loop_proxy().get()),
54         next_id_(1) {}
55
56   virtual void TearDown() OVERRIDE {
57     prefs_.pref_service()->CommitPendingWrite();
58     message_loop_.RunUntilIdle();
59   }
60
61   // Returns a test item.
62   MenuItem* CreateTestItem(Extension* extension, bool incognito = false) {
63     MenuItem::Type type = MenuItem::NORMAL;
64     MenuItem::ContextList contexts(MenuItem::ALL);
65     MenuItem::Id id(incognito, extension->id());
66     id.uid = next_id_++;
67     return new MenuItem(id, "test", false, true, type, contexts);
68   }
69
70   // Returns a test item with the given string ID.
71   MenuItem* CreateTestItemWithID(Extension* extension,
72                                  const std::string& string_id) {
73     MenuItem::Type type = MenuItem::NORMAL;
74     MenuItem::ContextList contexts(MenuItem::ALL);
75     MenuItem::Id id(false, extension->id());
76     id.string_uid = string_id;
77     return new MenuItem(id, "test", false, true, type, contexts);
78   }
79
80   // Creates and returns a test Extension. The caller does *not* own the return
81   // value.
82   Extension* AddExtension(std::string name) {
83     scoped_refptr<Extension> extension = prefs_.AddExtension(name);
84     extensions_.push_back(extension);
85     return extension.get();
86   }
87
88  protected:
89   base::MessageLoopForUI message_loop_;
90   content::TestBrowserThread ui_thread_;
91   content::TestBrowserThread file_thread_;
92   TestingProfile profile_;
93
94   MenuManager manager_;
95   ExtensionList extensions_;
96   TestExtensionPrefs prefs_;
97   int next_id_;
98
99  private:
100   DISALLOW_COPY_AND_ASSIGN(MenuManagerTest);
101 };
102
103 // Tests adding, getting, and removing items.
104 TEST_F(MenuManagerTest, AddGetRemoveItems) {
105   Extension* extension = AddExtension("test");
106
107   // Add a new item, make sure you can get it back.
108   MenuItem* item1 = CreateTestItem(extension);
109   ASSERT_TRUE(item1 != NULL);
110   ASSERT_TRUE(manager_.AddContextItem(extension, item1));
111   ASSERT_EQ(item1, manager_.GetItemById(item1->id()));
112   const MenuItem::List* items = manager_.MenuItems(item1->extension_id());
113   ASSERT_EQ(1u, items->size());
114   ASSERT_EQ(item1, items->at(0));
115
116   // Add a second item, make sure it comes back too.
117   MenuItem* item2 = CreateTestItemWithID(extension, "id2");
118   ASSERT_TRUE(manager_.AddContextItem(extension, item2));
119   ASSERT_EQ(item2, manager_.GetItemById(item2->id()));
120   items = manager_.MenuItems(item2->extension_id());
121   ASSERT_EQ(2u, items->size());
122   ASSERT_EQ(item1, items->at(0));
123   ASSERT_EQ(item2, items->at(1));
124
125   // Try adding item 3, then removing it.
126   MenuItem* item3 = CreateTestItem(extension);
127   MenuItem::Id id3 = item3->id();
128   std::string extension_id = item3->extension_id();
129   ASSERT_TRUE(manager_.AddContextItem(extension, item3));
130   ASSERT_EQ(item3, manager_.GetItemById(id3));
131   ASSERT_EQ(3u, manager_.MenuItems(extension_id)->size());
132   ASSERT_TRUE(manager_.RemoveContextMenuItem(id3));
133   ASSERT_EQ(NULL, manager_.GetItemById(id3));
134   ASSERT_EQ(2u, manager_.MenuItems(extension_id)->size());
135
136   // Make sure removing a non-existent item returns false.
137   MenuItem::Id id(false, extension->id());
138   id.uid = id3.uid + 50;
139   ASSERT_FALSE(manager_.RemoveContextMenuItem(id));
140
141   // Make sure adding an item with the same string ID returns false.
142   scoped_ptr<MenuItem> item2too(CreateTestItemWithID(extension, "id2"));
143   ASSERT_FALSE(manager_.AddContextItem(extension, item2too.get()));
144
145   // But the same string ID should not collide with another extension.
146   Extension* extension2 = AddExtension("test2");
147   MenuItem* item2other = CreateTestItemWithID(extension2, "id2");
148   ASSERT_TRUE(manager_.AddContextItem(extension2, item2other));
149 }
150
151 // Test adding/removing child items.
152 TEST_F(MenuManagerTest, ChildFunctions) {
153   Extension* extension1 = AddExtension("1111");
154   Extension* extension2 = AddExtension("2222");
155   Extension* extension3 = AddExtension("3333");
156
157   MenuItem* item1 = CreateTestItem(extension1);
158   MenuItem* item2 = CreateTestItem(extension2);
159   MenuItem* item2_child = CreateTestItemWithID(extension2, "2child");
160   MenuItem* item2_grandchild = CreateTestItem(extension2);
161
162   // This third item we expect to fail inserting, so we use a scoped_ptr to make
163   // sure it gets deleted.
164   scoped_ptr<MenuItem> item3(CreateTestItem(extension3));
165
166   // Add in the first two items.
167   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
168   ASSERT_TRUE(manager_.AddContextItem(extension2, item2));
169
170   MenuItem::Id id1 = item1->id();
171   MenuItem::Id id2 = item2->id();
172
173   // Try adding item3 as a child of item2 - this should fail because item3 has
174   // a different extension id.
175   ASSERT_FALSE(manager_.AddChildItem(id2, item3.get()));
176
177   // Add item2_child as a child of item2.
178   MenuItem::Id id2_child = item2_child->id();
179   ASSERT_TRUE(manager_.AddChildItem(id2, item2_child));
180   ASSERT_EQ(1, item2->child_count());
181   ASSERT_EQ(0, item1->child_count());
182   ASSERT_EQ(item2_child, manager_.GetItemById(id2_child));
183
184   ASSERT_EQ(1u, manager_.MenuItems(item1->extension_id())->size());
185   ASSERT_EQ(item1, manager_.MenuItems(item1->extension_id())->at(0));
186
187   // Add item2_grandchild as a child of item2_child, then remove it.
188   MenuItem::Id id2_grandchild = item2_grandchild->id();
189   ASSERT_TRUE(manager_.AddChildItem(id2_child, item2_grandchild));
190   ASSERT_EQ(1, item2->child_count());
191   ASSERT_EQ(1, item2_child->child_count());
192   ASSERT_TRUE(manager_.RemoveContextMenuItem(id2_grandchild));
193
194   // We should only get 1 thing back when asking for item2's extension id, since
195   // it has a child item.
196   ASSERT_EQ(1u, manager_.MenuItems(item2->extension_id())->size());
197   ASSERT_EQ(item2, manager_.MenuItems(item2->extension_id())->at(0));
198
199   // Remove child2_item.
200   ASSERT_TRUE(manager_.RemoveContextMenuItem(id2_child));
201   ASSERT_EQ(1u, manager_.MenuItems(item2->extension_id())->size());
202   ASSERT_EQ(item2, manager_.MenuItems(item2->extension_id())->at(0));
203   ASSERT_EQ(0, item2->child_count());
204 }
205
206 TEST_F(MenuManagerTest, PopulateFromValue) {
207   Extension* extension = AddExtension("test");
208
209   bool incognito = true;
210   int type = MenuItem::CHECKBOX;
211   std::string title("TITLE");
212   bool checked = true;
213   bool enabled = true;
214   MenuItem::ContextList contexts;
215   contexts.Add(MenuItem::PAGE);
216   contexts.Add(MenuItem::SELECTION);
217   int contexts_value = 0;
218   ASSERT_TRUE(contexts.ToValue()->GetAsInteger(&contexts_value));
219
220   base::ListValue* document_url_patterns(new base::ListValue());
221   document_url_patterns->Append(
222       new base::StringValue("http://www.google.com/*"));
223   document_url_patterns->Append(
224       new base::StringValue("http://www.reddit.com/*"));
225
226   base::ListValue* target_url_patterns(new base::ListValue());
227   target_url_patterns->Append(
228       new base::StringValue("http://www.yahoo.com/*"));
229   target_url_patterns->Append(
230       new base::StringValue("http://www.facebook.com/*"));
231
232   base::DictionaryValue value;
233   value.SetBoolean("incognito", incognito);
234   value.SetString("string_uid", std::string());
235   value.SetInteger("type", type);
236   value.SetString("title", title);
237   value.SetBoolean("checked", checked);
238   value.SetBoolean("enabled", enabled);
239   value.SetInteger("contexts", contexts_value);
240   value.Set("document_url_patterns", document_url_patterns);
241   value.Set("target_url_patterns", target_url_patterns);
242
243   std::string error;
244   scoped_ptr<MenuItem> item(MenuItem::Populate(extension->id(), value, &error));
245   ASSERT_TRUE(item.get());
246
247   EXPECT_EQ(extension->id(), item->extension_id());
248   EXPECT_EQ(incognito, item->incognito());
249   EXPECT_EQ(title, item->title());
250   EXPECT_EQ(checked, item->checked());
251   EXPECT_EQ(item->checked(), item->checked());
252   EXPECT_EQ(enabled, item->enabled());
253   EXPECT_EQ(contexts, item->contexts());
254
255   URLPatternSet document_url_pattern_set;
256   document_url_pattern_set.Populate(*document_url_patterns,
257                                     URLPattern::SCHEME_ALL,
258                                     true,
259                                     &error);
260   EXPECT_EQ(document_url_pattern_set, item->document_url_patterns());
261
262   URLPatternSet target_url_pattern_set;
263   target_url_pattern_set.Populate(*target_url_patterns,
264                                    URLPattern::SCHEME_ALL,
265                                    true,
266                                    &error);
267   EXPECT_EQ(target_url_pattern_set, item->target_url_patterns());
268 }
269
270 // Tests that deleting a parent properly removes descendants.
271 TEST_F(MenuManagerTest, DeleteParent) {
272   Extension* extension = AddExtension("1111");
273
274   // Set up 5 items to add.
275   MenuItem* item1 = CreateTestItem(extension);
276   MenuItem* item2 = CreateTestItem(extension);
277   MenuItem* item3 = CreateTestItemWithID(extension, "id3");
278   MenuItem* item4 = CreateTestItemWithID(extension, "id4");
279   MenuItem* item5 = CreateTestItem(extension);
280   MenuItem* item6 = CreateTestItem(extension);
281   MenuItem::Id item1_id = item1->id();
282   MenuItem::Id item2_id = item2->id();
283   MenuItem::Id item3_id = item3->id();
284   MenuItem::Id item4_id = item4->id();
285   MenuItem::Id item5_id = item5->id();
286   MenuItem::Id item6_id = item6->id();
287
288   // Add the items in the hierarchy
289   // item1 -> item2 -> item3 -> item4 -> item5 -> item6.
290   ASSERT_TRUE(manager_.AddContextItem(extension, item1));
291   ASSERT_TRUE(manager_.AddChildItem(item1_id, item2));
292   ASSERT_TRUE(manager_.AddChildItem(item2_id, item3));
293   ASSERT_TRUE(manager_.AddChildItem(item3_id, item4));
294   ASSERT_TRUE(manager_.AddChildItem(item4_id, item5));
295   ASSERT_TRUE(manager_.AddChildItem(item5_id, item6));
296   ASSERT_EQ(item1, manager_.GetItemById(item1_id));
297   ASSERT_EQ(item2, manager_.GetItemById(item2_id));
298   ASSERT_EQ(item3, manager_.GetItemById(item3_id));
299   ASSERT_EQ(item4, manager_.GetItemById(item4_id));
300   ASSERT_EQ(item5, manager_.GetItemById(item5_id));
301   ASSERT_EQ(item6, manager_.GetItemById(item6_id));
302   ASSERT_EQ(1u, manager_.MenuItems(extension->id())->size());
303   ASSERT_EQ(6u, manager_.items_by_id_.size());
304
305   // Remove item6 (a leaf node).
306   ASSERT_TRUE(manager_.RemoveContextMenuItem(item6_id));
307   ASSERT_EQ(item1, manager_.GetItemById(item1_id));
308   ASSERT_EQ(item2, manager_.GetItemById(item2_id));
309   ASSERT_EQ(item3, manager_.GetItemById(item3_id));
310   ASSERT_EQ(item4, manager_.GetItemById(item4_id));
311   ASSERT_EQ(item5, manager_.GetItemById(item5_id));
312   ASSERT_EQ(NULL, manager_.GetItemById(item6_id));
313   ASSERT_EQ(1u, manager_.MenuItems(extension->id())->size());
314   ASSERT_EQ(5u, manager_.items_by_id_.size());
315
316   // Remove item4 and make sure item5 is gone as well.
317   ASSERT_TRUE(manager_.RemoveContextMenuItem(item4_id));
318   ASSERT_EQ(item1, manager_.GetItemById(item1_id));
319   ASSERT_EQ(item2, manager_.GetItemById(item2_id));
320   ASSERT_EQ(item3, manager_.GetItemById(item3_id));
321   ASSERT_EQ(NULL, manager_.GetItemById(item4_id));
322   ASSERT_EQ(NULL, manager_.GetItemById(item5_id));
323   ASSERT_EQ(1u, manager_.MenuItems(extension->id())->size());
324   ASSERT_EQ(3u, manager_.items_by_id_.size());
325
326   // Now remove item1 and make sure item2 and item3 are gone as well.
327   ASSERT_TRUE(manager_.RemoveContextMenuItem(item1_id));
328   ASSERT_EQ(NULL, manager_.MenuItems(extension->id()));
329   ASSERT_EQ(0u, manager_.items_by_id_.size());
330   ASSERT_EQ(NULL, manager_.GetItemById(item1_id));
331   ASSERT_EQ(NULL, manager_.GetItemById(item2_id));
332   ASSERT_EQ(NULL, manager_.GetItemById(item3_id));
333 }
334
335 // Tests changing parents.
336 TEST_F(MenuManagerTest, ChangeParent) {
337   Extension* extension1 = AddExtension("1111");
338
339   // First create two items and add them both to the manager.
340   MenuItem* item1 = CreateTestItem(extension1);
341   MenuItem* item2 = CreateTestItem(extension1);
342
343   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
344   ASSERT_TRUE(manager_.AddContextItem(extension1, item2));
345
346   const MenuItem::List* items = manager_.MenuItems(item1->extension_id());
347   ASSERT_EQ(2u, items->size());
348   ASSERT_EQ(item1, items->at(0));
349   ASSERT_EQ(item2, items->at(1));
350
351   // Now create a third item, initially add it as a child of item1, then move
352   // it to be a child of item2.
353   MenuItem* item3 = CreateTestItem(extension1);
354
355   ASSERT_TRUE(manager_.AddChildItem(item1->id(), item3));
356   ASSERT_EQ(1, item1->child_count());
357   ASSERT_EQ(item3, item1->children()[0]);
358
359   ASSERT_TRUE(manager_.ChangeParent(item3->id(), &item2->id()));
360   ASSERT_EQ(0, item1->child_count());
361   ASSERT_EQ(1, item2->child_count());
362   ASSERT_EQ(item3, item2->children()[0]);
363
364   // Move item2 to be a child of item1.
365   ASSERT_TRUE(manager_.ChangeParent(item2->id(), &item1->id()));
366   ASSERT_EQ(1, item1->child_count());
367   ASSERT_EQ(item2, item1->children()[0]);
368   ASSERT_EQ(1, item2->child_count());
369   ASSERT_EQ(item3, item2->children()[0]);
370
371   // Since item2 was a top-level item but is no longer, we should only have 1
372   // top-level item.
373   items = manager_.MenuItems(item1->extension_id());
374   ASSERT_EQ(1u, items->size());
375   ASSERT_EQ(item1, items->at(0));
376
377   // Move item3 back to being a child of item1, so it's now a sibling of item2.
378   ASSERT_TRUE(manager_.ChangeParent(item3->id(), &item1->id()));
379   ASSERT_EQ(2, item1->child_count());
380   ASSERT_EQ(item2, item1->children()[0]);
381   ASSERT_EQ(item3, item1->children()[1]);
382
383   // Try switching item3 to be the parent of item1 - this should fail.
384   ASSERT_FALSE(manager_.ChangeParent(item1->id(), &item3->id()));
385   ASSERT_EQ(0, item3->child_count());
386   ASSERT_EQ(2, item1->child_count());
387   ASSERT_EQ(item2, item1->children()[0]);
388   ASSERT_EQ(item3, item1->children()[1]);
389   items = manager_.MenuItems(item1->extension_id());
390   ASSERT_EQ(1u, items->size());
391   ASSERT_EQ(item1, items->at(0));
392
393   // Move item2 to be a top-level item.
394   ASSERT_TRUE(manager_.ChangeParent(item2->id(), NULL));
395   items = manager_.MenuItems(item1->extension_id());
396   ASSERT_EQ(2u, items->size());
397   ASSERT_EQ(item1, items->at(0));
398   ASSERT_EQ(item2, items->at(1));
399   ASSERT_EQ(1, item1->child_count());
400   ASSERT_EQ(item3, item1->children()[0]);
401
402   // Make sure you can't move a node to be a child of another extension's item.
403   Extension* extension2 = AddExtension("2222");
404   MenuItem* item4 = CreateTestItem(extension2);
405   ASSERT_TRUE(manager_.AddContextItem(extension2, item4));
406   ASSERT_FALSE(manager_.ChangeParent(item4->id(), &item1->id()));
407   ASSERT_FALSE(manager_.ChangeParent(item1->id(), &item4->id()));
408
409   // Make sure you can't make an item be it's own parent.
410   ASSERT_FALSE(manager_.ChangeParent(item1->id(), &item1->id()));
411 }
412
413 // Tests that we properly remove an extension's menu item when that extension is
414 // unloaded.
415 TEST_F(MenuManagerTest, ExtensionUnloadRemovesMenuItems) {
416   content::NotificationService* notifier =
417       content::NotificationService::current();
418   ASSERT_TRUE(notifier != NULL);
419
420   // Create a test extension.
421   Extension* extension1 = AddExtension("1111");
422
423   // Create an MenuItem and put it into the manager.
424   MenuItem* item1 = CreateTestItem(extension1);
425   MenuItem::Id id1 = item1->id();
426   ASSERT_EQ(extension1->id(), item1->extension_id());
427   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
428   ASSERT_EQ(1u, manager_.MenuItems(extension1->id())->size());
429
430   // Create a menu item with a different extension id and add it to the manager.
431   Extension* extension2 = AddExtension("2222");
432   MenuItem* item2 = CreateTestItem(extension2);
433   ASSERT_NE(item1->extension_id(), item2->extension_id());
434   ASSERT_TRUE(manager_.AddContextItem(extension2, item2));
435
436   // Notify that the extension was unloaded, and make sure the right item is
437   // gone.
438   UnloadedExtensionInfo details(
439       extension1, UnloadedExtensionInfo::REASON_DISABLE);
440   notifier->Notify(chrome::NOTIFICATION_EXTENSION_UNLOADED,
441                    content::Source<Profile>(&profile_),
442                    content::Details<UnloadedExtensionInfo>(
443                       &details));
444   ASSERT_EQ(NULL, manager_.MenuItems(extension1->id()));
445   ASSERT_EQ(1u, manager_.MenuItems(extension2->id())->size());
446   ASSERT_TRUE(manager_.GetItemById(id1) == NULL);
447   ASSERT_TRUE(manager_.GetItemById(item2->id()) != NULL);
448 }
449
450 // A mock message service for tests of MenuManager::ExecuteCommand.
451 class MockEventRouter : public EventRouter {
452  public:
453   explicit MockEventRouter(Profile* profile) : EventRouter(profile, NULL) {}
454
455   MOCK_METHOD6(DispatchEventToExtensionMock,
456                void(const std::string& extension_id,
457                     const std::string& event_name,
458                     base::ListValue* event_args,
459                     content::BrowserContext* source_context,
460                     const GURL& event_url,
461                     EventRouter::UserGestureState state));
462
463   virtual void DispatchEventToExtension(const std::string& extension_id,
464                                         scoped_ptr<Event> event) {
465     DispatchEventToExtensionMock(extension_id,
466                                  event->event_name,
467                                  event->event_args.release(),
468                                  event->restrict_to_browser_context,
469                                  event->event_url,
470                                  event->user_gesture);
471   }
472
473  private:
474   DISALLOW_COPY_AND_ASSIGN(MockEventRouter);
475 };
476
477 // A mock ExtensionSystem to serve our MockEventRouter.
478 class MockExtensionSystem : public TestExtensionSystem {
479  public:
480   explicit MockExtensionSystem(Profile* profile)
481       : TestExtensionSystem(profile) {}
482
483   virtual EventRouter* event_router() OVERRIDE {
484     if (!mock_event_router_)
485       mock_event_router_.reset(new MockEventRouter(profile_));
486     return mock_event_router_.get();
487   }
488
489  private:
490   scoped_ptr<MockEventRouter> mock_event_router_;
491
492   DISALLOW_COPY_AND_ASSIGN(MockExtensionSystem);
493 };
494
495 BrowserContextKeyedService* BuildMockExtensionSystem(
496     content::BrowserContext* profile) {
497   return new MockExtensionSystem(static_cast<Profile*>(profile));
498 }
499
500 // Tests the RemoveAll functionality.
501 TEST_F(MenuManagerTest, RemoveAll) {
502   // Try removing all items for an extension id that doesn't have any items.
503   manager_.RemoveAllContextItems("CCCC");
504
505   // Add 2 top-level and one child item for extension 1.
506   Extension* extension1 = AddExtension("1111");
507   MenuItem* item1 = CreateTestItem(extension1);
508   MenuItem* item2 = CreateTestItem(extension1);
509   MenuItem* item3 = CreateTestItem(extension1);
510   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
511   ASSERT_TRUE(manager_.AddContextItem(extension1, item2));
512   ASSERT_TRUE(manager_.AddChildItem(item1->id(), item3));
513
514   // Add one top-level item for extension 2.
515   Extension* extension2 = AddExtension("2222");
516   MenuItem* item4 = CreateTestItem(extension2);
517   ASSERT_TRUE(manager_.AddContextItem(extension2, item4));
518
519   EXPECT_EQ(2u, manager_.MenuItems(extension1->id())->size());
520   EXPECT_EQ(1u, manager_.MenuItems(extension2->id())->size());
521
522   // Remove extension2's item.
523   manager_.RemoveAllContextItems(extension2->id());
524   EXPECT_EQ(2u, manager_.MenuItems(extension1->id())->size());
525   EXPECT_EQ(NULL, manager_.MenuItems(extension2->id()));
526
527   // Remove extension1's items.
528   manager_.RemoveAllContextItems(extension1->id());
529   EXPECT_EQ(NULL, manager_.MenuItems(extension1->id()));
530 }
531
532 // Tests that removing all items one-by-one doesn't leave an entry around.
533 TEST_F(MenuManagerTest, RemoveOneByOne) {
534   // Add 2 test items.
535   Extension* extension1 = AddExtension("1111");
536   MenuItem* item1 = CreateTestItem(extension1);
537   MenuItem* item2 = CreateTestItem(extension1);
538   MenuItem* item3 = CreateTestItemWithID(extension1, "id3");
539   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
540   ASSERT_TRUE(manager_.AddContextItem(extension1, item2));
541   ASSERT_TRUE(manager_.AddContextItem(extension1, item3));
542
543   ASSERT_FALSE(manager_.context_items_.empty());
544
545   manager_.RemoveContextMenuItem(item3->id());
546   manager_.RemoveContextMenuItem(item1->id());
547   manager_.RemoveContextMenuItem(item2->id());
548
549   ASSERT_TRUE(manager_.context_items_.empty());
550 }
551
552 TEST_F(MenuManagerTest, ExecuteCommand) {
553   TestingProfile profile;
554
555   MockExtensionSystem* mock_extension_system =
556       static_cast<MockExtensionSystem*>(ExtensionSystemFactory::GetInstance()->
557           SetTestingFactoryAndUse(&profile, &BuildMockExtensionSystem));
558   MockEventRouter* mock_event_router =
559       static_cast<MockEventRouter*>(mock_extension_system->event_router());
560
561   content::ContextMenuParams params;
562   params.media_type = blink::WebContextMenuData::MediaTypeImage;
563   params.src_url = GURL("http://foo.bar/image.png");
564   params.page_url = GURL("http://foo.bar");
565   params.selection_text = base::ASCIIToUTF16("Hello World");
566   params.is_editable = false;
567
568   Extension* extension = AddExtension("test");
569   MenuItem* parent = CreateTestItem(extension);
570   MenuItem* item = CreateTestItem(extension);
571   MenuItem::Id parent_id = parent->id();
572   MenuItem::Id id = item->id();
573   ASSERT_TRUE(manager_.AddContextItem(extension, parent));
574   ASSERT_TRUE(manager_.AddChildItem(parent->id(), item));
575
576   // Use the magic of googlemock to save a parameter to our mock's
577   // DispatchEventToExtension method into event_args.
578   base::ListValue* list = NULL;
579   {
580     InSequence s;
581     EXPECT_CALL(*mock_event_router,
582                 DispatchEventToExtensionMock(
583                     item->extension_id(),
584                     extensions::event_names::kOnContextMenus,
585                     _,
586                     &profile,
587                     GURL(),
588                     EventRouter::USER_GESTURE_ENABLED))
589       .Times(1)
590       .WillOnce(SaveArg<2>(&list));
591     EXPECT_CALL(*mock_event_router,
592               DispatchEventToExtensionMock(
593                   item->extension_id(),
594                   context_menus::OnClicked::kEventName,
595                   _,
596                   &profile,
597                   GURL(),
598                   EventRouter::USER_GESTURE_ENABLED))
599       .Times(1)
600       .WillOnce(DeleteArg<2>());
601   }
602   manager_.ExecuteCommand(&profile, NULL /* web_contents */, params, id);
603
604   ASSERT_EQ(2u, list->GetSize());
605
606   base::DictionaryValue* info;
607   ASSERT_TRUE(list->GetDictionary(0, &info));
608
609   int tmp_id = 0;
610   ASSERT_TRUE(info->GetInteger("menuItemId", &tmp_id));
611   ASSERT_EQ(id.uid, tmp_id);
612   ASSERT_TRUE(info->GetInteger("parentMenuItemId", &tmp_id));
613   ASSERT_EQ(parent_id.uid, tmp_id);
614
615   std::string tmp;
616   ASSERT_TRUE(info->GetString("mediaType", &tmp));
617   ASSERT_EQ("image", tmp);
618   ASSERT_TRUE(info->GetString("srcUrl", &tmp));
619   ASSERT_EQ(params.src_url.spec(), tmp);
620   ASSERT_TRUE(info->GetString("pageUrl", &tmp));
621   ASSERT_EQ(params.page_url.spec(), tmp);
622
623   base::string16 tmp16;
624   ASSERT_TRUE(info->GetString("selectionText", &tmp16));
625   ASSERT_EQ(params.selection_text, tmp16);
626
627   bool bool_tmp = true;
628   ASSERT_TRUE(info->GetBoolean("editable", &bool_tmp));
629   ASSERT_EQ(params.is_editable, bool_tmp);
630
631   delete list;
632 }
633
634 // Test that there is always only one radio item selected.
635 TEST_F(MenuManagerTest, SanitizeRadioButtons) {
636   Extension* extension = AddExtension("test");
637
638   // A single unchecked item should get checked
639   MenuItem* item1 = CreateTestItem(extension);
640
641   item1->set_type(MenuItem::RADIO);
642   item1->SetChecked(false);
643   ASSERT_FALSE(item1->checked());
644   manager_.AddContextItem(extension, item1);
645   ASSERT_TRUE(item1->checked());
646
647   // In a run of two unchecked items, the first should get selected.
648   item1->SetChecked(false);
649   MenuItem* item2 = CreateTestItem(extension);
650   item2->set_type(MenuItem::RADIO);
651   item2->SetChecked(false);
652   ASSERT_FALSE(item1->checked());
653   ASSERT_FALSE(item2->checked());
654   manager_.AddContextItem(extension, item2);
655   ASSERT_TRUE(item1->checked());
656   ASSERT_FALSE(item2->checked());
657
658   // If multiple items are checked, only the last item should get checked.
659   item1->SetChecked(true);
660   item2->SetChecked(true);
661   ASSERT_TRUE(item1->checked());
662   ASSERT_TRUE(item2->checked());
663   manager_.ItemUpdated(item1->id());
664   ASSERT_FALSE(item1->checked());
665   ASSERT_TRUE(item2->checked());
666
667   // If the checked item is removed, the new first item should get checked.
668   item1->SetChecked(false);
669   item2->SetChecked(true);
670   ASSERT_FALSE(item1->checked());
671   ASSERT_TRUE(item2->checked());
672   manager_.RemoveContextMenuItem(item2->id());
673   item2 = NULL;
674   ASSERT_TRUE(item1->checked());
675
676   // If a checked item is added to a run that already has a checked item,
677   // then the new item should get checked.
678   item1->SetChecked(true);
679   MenuItem* new_item = CreateTestItem(extension);
680   new_item->set_type(MenuItem::RADIO);
681   new_item->SetChecked(true);
682   ASSERT_TRUE(item1->checked());
683   ASSERT_TRUE(new_item->checked());
684   manager_.AddContextItem(extension, new_item);
685   ASSERT_FALSE(item1->checked());
686   ASSERT_TRUE(new_item->checked());
687   // Make sure that children are checked as well.
688   MenuItem* parent = CreateTestItem(extension);
689   manager_.AddContextItem(extension, parent);
690   MenuItem* child1 = CreateTestItem(extension);
691   child1->set_type(MenuItem::RADIO);
692   child1->SetChecked(false);
693   MenuItem* child2 = CreateTestItem(extension);
694   child2->set_type(MenuItem::RADIO);
695   child2->SetChecked(true);
696   ASSERT_FALSE(child1->checked());
697   ASSERT_TRUE(child2->checked());
698
699   manager_.AddChildItem(parent->id(), child1);
700   ASSERT_TRUE(child1->checked());
701
702   manager_.AddChildItem(parent->id(), child2);
703   ASSERT_FALSE(child1->checked());
704   ASSERT_TRUE(child2->checked());
705
706   // Removing the checked item from the children should cause the
707   // remaining child to be checked.
708   manager_.RemoveContextMenuItem(child2->id());
709   child2 = NULL;
710   ASSERT_TRUE(child1->checked());
711
712   // This should NOT cause |new_item| to be deseleted because
713   // |parent| will be seperating the two runs of radio items.
714   manager_.ChangeParent(child1->id(), NULL);
715   ASSERT_TRUE(new_item->checked());
716   ASSERT_TRUE(child1->checked());
717
718   // Removing |parent| should cause only |child1| to be selected.
719   manager_.RemoveContextMenuItem(parent->id());
720   parent = NULL;
721   ASSERT_FALSE(new_item->checked());
722   ASSERT_TRUE(child1->checked());
723 }
724
725 // Tests the RemoveAllIncognitoContextItems functionality.
726 TEST_F(MenuManagerTest, RemoveAllIncognito) {
727   Extension* extension1 = AddExtension("1111");
728   // Add 2 top-level and one child item for extension 1
729   // with incognito 'true'.
730   MenuItem* item1 = CreateTestItem(extension1, true);
731   MenuItem* item2 = CreateTestItem(extension1, true);
732   MenuItem* item3 = CreateTestItem(extension1, true);
733   ASSERT_TRUE(manager_.AddContextItem(extension1, item1));
734   ASSERT_TRUE(manager_.AddContextItem(extension1, item2));
735   ASSERT_TRUE(manager_.AddChildItem(item1->id(), item3));
736
737   // Add 2 top-level and one child item for extension 1
738   // with incognito 'false'.
739   MenuItem* item4 = CreateTestItem(extension1);
740   MenuItem* item5 = CreateTestItem(extension1);
741   MenuItem* item6 = CreateTestItem(extension1);
742   ASSERT_TRUE(manager_.AddContextItem(extension1, item4));
743   ASSERT_TRUE(manager_.AddContextItem(extension1, item5));
744   ASSERT_TRUE(manager_.AddChildItem(item4->id(), item6));
745
746   // Add one top-level item for extension 2.
747   Extension* extension2 = AddExtension("2222");
748   MenuItem* item7 = CreateTestItem(extension2);
749   ASSERT_TRUE(manager_.AddContextItem(extension2, item7));
750
751   EXPECT_EQ(4u, manager_.MenuItems(extension1->id())->size());
752   EXPECT_EQ(1u, manager_.MenuItems(extension2->id())->size());
753
754   // Remove all context menu items with incognito true.
755   manager_.RemoveAllIncognitoContextItems();
756   EXPECT_EQ(2u, manager_.MenuItems(extension1->id())->size());
757   EXPECT_EQ(1u, manager_.MenuItems(extension2->id())->size());
758 }
759
760 }  // namespace extensions