1// Copyright (c) 2010 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 "chrome/browser/extensions/extension_browsertest.h"
6#include "chrome/browser/extensions/extension_service.h"
7#include "chrome/browser/extensions/extension_toolbar_model.h"
8#include "chrome/browser/profiles/profile.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/common/chrome_switches.h"
11#include "chrome/test/in_process_browser_test.h"
12
13// An InProcessBrowserTest for testing the ExtensionToolbarModel.
14// TODO(erikkay) It's unfortunate that this needs to be an in-proc browser test.
15// It would be nice to refactor things so that ExtensionService could run
16// without so much of the browser in place.
17class ExtensionToolbarModelTest : public ExtensionBrowserTest,
18                                  public ExtensionToolbarModel::Observer {
19 public:
20  virtual void SetUp() {
21    inserted_count_ = 0;
22    removed_count_ = 0;
23    moved_count_ = 0;
24
25    ExtensionBrowserTest::SetUp();
26  }
27
28  virtual Browser* CreateBrowser(Profile* profile) {
29    Browser* b = InProcessBrowserTest::CreateBrowser(profile);
30    ExtensionService* service = b->profile()->GetExtensionService();
31    model_ = service->toolbar_model();
32    model_->AddObserver(this);
33    return b;
34  }
35
36  virtual void CleanUpOnMainThread() {
37    model_->RemoveObserver(this);
38  }
39
40  virtual void BrowserActionAdded(const Extension* extension, int index) {
41    inserted_count_++;
42  }
43
44  virtual void BrowserActionRemoved(const Extension* extension) {
45    removed_count_++;
46  }
47
48  virtual void BrowserActionMoved(const Extension* extension, int index) {
49    moved_count_++;
50  }
51
52  const Extension* ExtensionAt(int index) {
53    for (ExtensionList::iterator i = model_->begin(); i < model_->end(); ++i) {
54      if (index-- == 0)
55        return *i;
56    }
57    return NULL;
58  }
59
60 protected:
61  ExtensionToolbarModel* model_;
62
63  int inserted_count_;
64  int removed_count_;
65  int moved_count_;
66};
67
68IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, Basic) {
69  CommandLine::ForCurrentProcess()->AppendSwitch(
70      switches::kEnableExperimentalExtensionApis);
71
72  // Load an extension with no browser action.
73  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
74                                          .AppendASCII("browser_action")
75                                          .AppendASCII("none")));
76
77  // This extension should not be in the model (has no browser action).
78  EXPECT_EQ(0, inserted_count_);
79  EXPECT_EQ(0u, model_->size());
80  ASSERT_EQ(NULL, ExtensionAt(0));
81
82  // Load an extension with a browser action.
83  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
84                                          .AppendASCII("browser_action")
85                                          .AppendASCII("basics")));
86
87  // We should now find our extension in the model.
88  EXPECT_EQ(1, inserted_count_);
89  EXPECT_EQ(1u, model_->size());
90  const Extension* extension = ExtensionAt(0);
91  ASSERT_TRUE(NULL != extension);
92  EXPECT_STREQ("A browser action with no icon that makes the page red",
93               extension->name().c_str());
94
95  // Should be a no-op, but still fires the events.
96  model_->MoveBrowserAction(extension, 0);
97  EXPECT_EQ(1, moved_count_);
98  EXPECT_EQ(1u, model_->size());
99  const Extension* extension2 = ExtensionAt(0);
100  EXPECT_EQ(extension, extension2);
101
102  UnloadExtension(extension->id());
103  EXPECT_EQ(1, removed_count_);
104  EXPECT_EQ(0u, model_->size());
105  EXPECT_EQ(NULL, ExtensionAt(0));
106}
107
108IN_PROC_BROWSER_TEST_F(ExtensionToolbarModelTest, ReorderAndReinsert) {
109  CommandLine::ForCurrentProcess()->AppendSwitch(
110      switches::kEnableExperimentalExtensionApis);
111
112  // Load an extension with a browser action.
113  FilePath extension_a_path(test_data_dir_.AppendASCII("api_test")
114                                          .AppendASCII("browser_action")
115                                          .AppendASCII("basics"));
116  ASSERT_TRUE(LoadExtension(extension_a_path));
117
118  // First extension loaded.
119  EXPECT_EQ(1, inserted_count_);
120  EXPECT_EQ(1u, model_->size());
121  const Extension* extensionA = ExtensionAt(0);
122  ASSERT_TRUE(NULL != extensionA);
123  EXPECT_STREQ("A browser action with no icon that makes the page red",
124               extensionA->name().c_str());
125
126  // Load another extension with a browser action.
127  FilePath extension_b_path(test_data_dir_.AppendASCII("api_test")
128                                          .AppendASCII("browser_action")
129                                          .AppendASCII("popup"));
130  ASSERT_TRUE(LoadExtension(extension_b_path));
131
132  // Second extension loaded.
133  EXPECT_EQ(2, inserted_count_);
134  EXPECT_EQ(2u, model_->size());
135  const Extension* extensionB = ExtensionAt(1);
136  ASSERT_TRUE(NULL != extensionB);
137  EXPECT_STREQ("Popup tester", extensionB->name().c_str());
138
139  // Load yet another extension with a browser action.
140  FilePath extension_c_path(test_data_dir_.AppendASCII("api_test")
141                                          .AppendASCII("browser_action")
142                                          .AppendASCII("remove_popup"));
143  ASSERT_TRUE(LoadExtension(extension_c_path));
144
145  // Third extension loaded.
146  EXPECT_EQ(3, inserted_count_);
147  EXPECT_EQ(3u, model_->size());
148  const Extension* extensionC = ExtensionAt(2);
149  ASSERT_TRUE(NULL != extensionC);
150  EXPECT_STREQ("A page action which removes a popup.",
151               extensionC->name().c_str());
152
153  // Order is now A, B, C. Let's put C first.
154  model_->MoveBrowserAction(extensionC, 0);
155  EXPECT_EQ(1, moved_count_);
156  EXPECT_EQ(3u, model_->size());
157  EXPECT_EQ(extensionC, ExtensionAt(0));
158  EXPECT_EQ(extensionA, ExtensionAt(1));
159  EXPECT_EQ(extensionB, ExtensionAt(2));
160  EXPECT_EQ(NULL, ExtensionAt(3));
161
162  // Order is now C, A, B. Let's put A last.
163  model_->MoveBrowserAction(extensionA, 2);
164  EXPECT_EQ(2, moved_count_);
165  EXPECT_EQ(3u, model_->size());
166  EXPECT_EQ(extensionC, ExtensionAt(0));
167  EXPECT_EQ(extensionB, ExtensionAt(1));
168  EXPECT_EQ(extensionA, ExtensionAt(2));
169  EXPECT_EQ(NULL, ExtensionAt(3));
170
171  // Order is now C, B, A. Let's remove B.
172  std::string idB = extensionB->id();
173  UnloadExtension(idB);
174  EXPECT_EQ(1, removed_count_);
175  EXPECT_EQ(2u, model_->size());
176  EXPECT_EQ(extensionC, ExtensionAt(0));
177  EXPECT_EQ(extensionA, ExtensionAt(1));
178  EXPECT_EQ(NULL, ExtensionAt(2));
179
180  // Load extension B again.
181  ASSERT_TRUE(LoadExtension(extension_b_path));
182
183  // Extension B loaded again.
184  EXPECT_EQ(4, inserted_count_);
185  EXPECT_EQ(3u, model_->size());
186  // Make sure it gets its old spot in the list. We should get the same
187  // extension again, otherwise the order has changed.
188  ASSERT_STREQ(idB.c_str(), ExtensionAt(1)->id().c_str());
189
190  // Unload B again.
191  UnloadExtension(idB);
192  EXPECT_EQ(2, removed_count_);
193  EXPECT_EQ(2u, model_->size());
194  EXPECT_EQ(extensionC, ExtensionAt(0));
195  EXPECT_EQ(extensionA, ExtensionAt(1));
196  EXPECT_EQ(NULL, ExtensionAt(2));
197
198  // Order is now C, A. Flip it.
199  model_->MoveBrowserAction(extensionA, 0);
200  EXPECT_EQ(3, moved_count_);
201  EXPECT_EQ(2u, model_->size());
202  EXPECT_EQ(extensionA, ExtensionAt(0));
203  EXPECT_EQ(extensionC, ExtensionAt(1));
204  EXPECT_EQ(NULL, ExtensionAt(2));
205
206  // Move A to the location it already occupies.
207  model_->MoveBrowserAction(extensionA, 0);
208  EXPECT_EQ(4, moved_count_);
209  EXPECT_EQ(2u, model_->size());
210  EXPECT_EQ(extensionA, ExtensionAt(0));
211  EXPECT_EQ(extensionC, ExtensionAt(1));
212  EXPECT_EQ(NULL, ExtensionAt(2));
213
214  // Order is now A, C. Remove C.
215  std::string idC = extensionC->id();
216  UnloadExtension(idC);
217  EXPECT_EQ(3, removed_count_);
218  EXPECT_EQ(1u, model_->size());
219  EXPECT_EQ(extensionA, ExtensionAt(0));
220  EXPECT_EQ(NULL, ExtensionAt(1));
221
222  // Load extension C again.
223  ASSERT_TRUE(LoadExtension(extension_c_path));
224
225  // Extension C loaded again.
226  EXPECT_EQ(5, inserted_count_);
227  EXPECT_EQ(2u, model_->size());
228  // Make sure it gets its old spot in the list (at the very end).
229  ASSERT_STREQ(idC.c_str(), ExtensionAt(1)->id().c_str());
230}
231