1// Copyright (c) 2011 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/browser_action_test_util.h"
6#include "chrome/browser/extensions/extension_browsertest.h"
7#include "chrome/browser/extensions/extension_service.h"
8#include "chrome/browser/profiles/profile.h"
9#include "chrome/browser/ui/browser.h"
10#include "chrome/browser/ui/views/browser_actions_container.h"
11#include "chrome/common/extensions/extension_action.h"
12#include "chrome/common/extensions/extension_resource.h"
13
14class BrowserActionsContainerTest : public ExtensionBrowserTest {
15 public:
16  BrowserActionsContainerTest() : browser_(NULL) {
17  }
18  virtual ~BrowserActionsContainerTest() {}
19
20  virtual Browser* CreateBrowser(Profile* profile) {
21    browser_ = InProcessBrowserTest::CreateBrowser(profile);
22    browser_actions_bar_.reset(new BrowserActionTestUtil(browser_));
23    return browser_;
24  }
25
26  Browser* browser() { return browser_; }
27
28  BrowserActionTestUtil* browser_actions_bar() {
29    return browser_actions_bar_.get();
30  }
31
32  // Make sure extension with index |extension_index| has an icon.
33  void EnsureExtensionHasIcon(int extension_index) {
34    if (!browser_actions_bar_->HasIcon(extension_index)) {
35      // The icon is loaded asynchronously and a notification is then sent to
36      // observers. So we wait on it.
37      browser_actions_bar_->WaitForBrowserActionUpdated(extension_index);
38    }
39    EXPECT_TRUE(browser_actions_bar()->HasIcon(extension_index));
40  }
41
42 private:
43  scoped_ptr<BrowserActionTestUtil> browser_actions_bar_;
44
45  Browser* browser_;  // Weak.
46};
47
48// Test the basic functionality.
49IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Basic) {
50  BrowserActionsContainer::disable_animations_during_testing_ = true;
51
52  // Load an extension with no browser action.
53  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
54                                          .AppendASCII("browser_action")
55                                          .AppendASCII("none")));
56  // This extension should not be in the model (has no browser action).
57  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
58
59  // Load an extension with a browser action.
60  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
61                                          .AppendASCII("browser_action")
62                                          .AppendASCII("basics")));
63  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
64  EnsureExtensionHasIcon(0);
65
66  // Unload the extension.
67  std::string id = browser_actions_bar()->GetExtensionId(0);
68  UnloadExtension(id);
69  EXPECT_EQ(0, browser_actions_bar()->NumberOfBrowserActions());
70}
71
72// TODO(mpcomplete): http://code.google.com/p/chromium/issues/detail?id=38992
73IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, Visibility) {
74  BrowserActionsContainer::disable_animations_during_testing_ = true;
75
76  base::TimeTicks start_time = base::TimeTicks::Now();
77
78  // Load extension A (contains browser action).
79  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
80                                          .AppendASCII("browser_action")
81                                          .AppendASCII("basics")));
82  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
83  EnsureExtensionHasIcon(0);
84  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
85  std::string idA = browser_actions_bar()->GetExtensionId(0);
86
87  LOG(INFO) << "Load extension A done  : "
88            << (base::TimeTicks::Now() - start_time).InMilliseconds()
89            << " ms" << std::flush;
90
91  // Load extension B (contains browser action).
92  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
93                                          .AppendASCII("browser_action")
94                                          .AppendASCII("add_popup")));
95  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
96  EnsureExtensionHasIcon(0);
97  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
98  std::string idB = browser_actions_bar()->GetExtensionId(1);
99
100  LOG(INFO) << "Load extension B done  : "
101            << (base::TimeTicks::Now() - start_time).InMilliseconds()
102            << " ms" << std::flush;
103
104  EXPECT_NE(idA, idB);
105
106  // Load extension C (contains browser action).
107  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
108                                          .AppendASCII("browser_action")
109                                          .AppendASCII("remove_popup")));
110  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
111  EnsureExtensionHasIcon(2);
112  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
113  std::string idC = browser_actions_bar()->GetExtensionId(2);
114
115  LOG(INFO) << "Load extension C done  : "
116            << (base::TimeTicks::Now() - start_time).InMilliseconds()
117            << " ms" << std::flush;
118
119  // Change container to show only one action, rest in overflow: A, [B, C].
120  browser_actions_bar()->SetIconVisibilityCount(1);
121  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
122
123  LOG(INFO) << "Icon visibility count 1: "
124            << (base::TimeTicks::Now() - start_time).InMilliseconds()
125            << " ms" << std::flush;
126
127  // Disable extension A (should disappear). State becomes: B [C].
128  DisableExtension(idA);
129  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
130  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
131  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
132
133  LOG(INFO) << "Disable extension A    : "
134            << (base::TimeTicks::Now() - start_time).InMilliseconds()
135            << " ms" << std::flush;
136
137  // Enable A again. A should get its spot in the same location and the bar
138  // should not grow (chevron is showing). For details: http://crbug.com/35349.
139  // State becomes: A, [B, C].
140  EnableExtension(idA);
141  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
142  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
143  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
144
145  LOG(INFO) << "Enable extension A     : "
146            << (base::TimeTicks::Now() - start_time).InMilliseconds()
147            << " ms" << std::flush;
148
149  // Disable C (in overflow). State becomes: A, [B].
150  DisableExtension(idC);
151  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
152  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
153  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
154
155  LOG(INFO) << "Disable extension C    : "
156            << (base::TimeTicks::Now() - start_time).InMilliseconds()
157            << " ms" << std::flush;
158
159  // Enable C again. State becomes: A, [B, C].
160  EnableExtension(idC);
161  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
162  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
163  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(0));
164
165  LOG(INFO) << "Enable extension C     : "
166            << (base::TimeTicks::Now() - start_time).InMilliseconds()
167            << " ms" << std::flush;
168
169  // Now we have 3 extensions. Make sure they are all visible. State: A, B, C.
170  browser_actions_bar()->SetIconVisibilityCount(3);
171  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
172
173  LOG(INFO) << "Checkpoint             : "
174            << (base::TimeTicks::Now() - start_time).InMilliseconds()
175            << " ms" << std::flush;
176
177  // Disable extension A (should disappear). State becomes: B, C.
178  DisableExtension(idA);
179  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
180  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
181  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
182
183  LOG(INFO) << "Disable extension A    : "
184            << (base::TimeTicks::Now() - start_time).InMilliseconds()
185            << " ms" << std::flush;
186
187  // Disable extension B (should disappear). State becomes: C.
188  DisableExtension(idB);
189  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
190  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
191  EXPECT_EQ(idC, browser_actions_bar()->GetExtensionId(0));
192
193  LOG(INFO) << "Disable extension B    : "
194            << (base::TimeTicks::Now() - start_time).InMilliseconds()
195            << " ms" << std::flush;
196
197  // Enable B (makes B and C showing now). State becomes: B, C.
198  EnableExtension(idB);
199  EXPECT_EQ(2, browser_actions_bar()->NumberOfBrowserActions());
200  EXPECT_EQ(2, browser_actions_bar()->VisibleBrowserActions());
201  EXPECT_EQ(idB, browser_actions_bar()->GetExtensionId(0));
202
203  LOG(INFO) << "Enable extension B     : "
204            << (base::TimeTicks::Now() - start_time).InMilliseconds()
205            << " ms" << std::flush;
206
207  // Enable A (makes A, B and C showing now). State becomes: B, C, A.
208  EnableExtension(idA);
209  EXPECT_EQ(3, browser_actions_bar()->NumberOfBrowserActions());
210  EXPECT_EQ(3, browser_actions_bar()->VisibleBrowserActions());
211  EXPECT_EQ(idA, browser_actions_bar()->GetExtensionId(2));
212
213  LOG(INFO) << "Test complete          : "
214            << (base::TimeTicks::Now() - start_time).InMilliseconds()
215            << " ms" << std::flush;
216}
217
218IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, ForceHide) {
219  BrowserActionsContainer::disable_animations_during_testing_ = true;
220
221  // Load extension A (contains browser action).
222  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
223                                          .AppendASCII("browser_action")
224                                          .AppendASCII("basics")));
225  EXPECT_EQ(1, browser_actions_bar()->NumberOfBrowserActions());
226  EnsureExtensionHasIcon(0);
227  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
228  std::string idA = browser_actions_bar()->GetExtensionId(0);
229
230  // Force hide this browser action.
231  ExtensionService* service = browser()->profile()->GetExtensionService();
232  service->SetBrowserActionVisibility(service->GetExtensionById(idA, false),
233                                      false);
234  EXPECT_EQ(0, browser_actions_bar()->VisibleBrowserActions());
235
236  ReloadExtension(idA);
237
238  // The browser action should become visible again.
239  EXPECT_EQ(1, browser_actions_bar()->VisibleBrowserActions());
240}
241
242IN_PROC_BROWSER_TEST_F(BrowserActionsContainerTest, TestCrash57536) {
243  LOG(INFO) << "Test starting\n" << std::flush;
244
245  ExtensionService* service = browser()->profile()->GetExtensionService();
246  const size_t size_before = service->extensions()->size();
247
248  LOG(INFO) << "Loading extension\n" << std::flush;
249
250  // Load extension A (contains browser action).
251  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("api_test")
252                                          .AppendASCII("browser_action")
253                                          .AppendASCII("crash_57536")));
254
255  const Extension* extension = service->extensions()->at(size_before);
256
257  LOG(INFO) << "Creating bitmap\n" << std::flush;
258
259  // Create and cache and empty bitmap.
260  SkBitmap bitmap;
261  bitmap.setConfig(SkBitmap::kARGB_8888_Config,
262                    Extension::kBrowserActionIconMaxSize,
263                    Extension::kBrowserActionIconMaxSize);
264  bitmap.allocPixels();
265
266  LOG(INFO) << "Set as cached image\n" << std::flush;
267
268  gfx::Size size(Extension::kBrowserActionIconMaxSize,
269                 Extension::kBrowserActionIconMaxSize);
270  extension->SetCachedImage(
271      extension->GetResource(extension->browser_action()->default_icon_path()),
272      bitmap,
273      size);
274
275  LOG(INFO) << "Disabling extension\n" << std::flush;
276  DisableExtension(extension->id());
277  LOG(INFO) << "Enabling extension\n" << std::flush;
278  EnableExtension(extension->id());
279  LOG(INFO) << "Test ending\n" << std::flush;
280}
281