extension_browsertest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/file_path.h"
11#include "base/file_util.h"
12#include "base/path_service.h"
13#include "base/string_number_conversions.h"
14#include "chrome/browser/browser_window.h"
15#include "chrome/browser/extensions/crx_installer.h"
16#include "chrome/browser/extensions/extension_creator.h"
17#include "chrome/browser/extensions/extension_error_reporter.h"
18#include "chrome/browser/extensions/extension_host.h"
19#include "chrome/browser/extensions/extension_install_ui.h"
20#include "chrome/browser/extensions/extension_service.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/ui/browser.h"
23#include "chrome/browser/ui/omnibox/location_bar.h"
24#include "chrome/common/chrome_paths.h"
25#include "chrome/common/chrome_switches.h"
26#include "chrome/common/notification_registrar.h"
27#include "chrome/common/notification_service.h"
28#include "chrome/common/notification_type.h"
29#include "chrome/test/ui_test_utils.h"
30
31ExtensionBrowserTest::ExtensionBrowserTest()
32    : loaded_(false),
33      installed_(false),
34      extension_installs_observed_(0),
35      target_page_action_count_(-1),
36      target_visible_page_action_count_(-1) {
37}
38
39void ExtensionBrowserTest::SetUpCommandLine(CommandLine* command_line) {
40  // This enables DOM automation for tab contentses.
41  EnableDOMAutomation();
42
43  // This enables it for extension hosts.
44  ExtensionHost::EnableDOMAutomation();
45
46  PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
47  test_data_dir_ = test_data_dir_.AppendASCII("extensions");
48
49#if defined(OS_CHROMEOS)
50  // This makes sure that we create the Default profile first, with no
51  // ExtensionService and then the real profile with one, as we do when
52  // running on chromeos.
53  command_line->AppendSwitchASCII(switches::kLoginUser,
54                                  "TestUser@gmail.com");
55  command_line->AppendSwitchASCII(switches::kLoginProfile, "user");
56  command_line->AppendSwitch(switches::kNoFirstRun);
57#endif
58}
59
60bool ExtensionBrowserTest::LoadExtensionImpl(const FilePath& path,
61                                             bool incognito_enabled) {
62  ExtensionService* service = browser()->profile()->GetExtensionService();
63  {
64    NotificationRegistrar registrar;
65    registrar.Add(this, NotificationType::EXTENSION_LOADED,
66                  NotificationService::AllSources());
67    service->LoadExtension(path);
68    ui_test_utils::RunMessageLoop();
69  }
70
71  // Find the extension by iterating backwards since it is likely last.
72  FilePath extension_path = path;
73  file_util::AbsolutePath(&extension_path);
74  const Extension* extension = NULL;
75  for (ExtensionList::const_reverse_iterator iter =
76           service->extensions()->rbegin();
77       iter != service->extensions()->rend(); ++iter) {
78    if ((*iter)->path() == extension_path) {
79      extension = *iter;
80      break;
81    }
82  }
83  if (!extension)
84    return false;
85
86  if (incognito_enabled) {
87    // Enable the incognito bit in the extension prefs. The call to
88    // OnExtensionInstalled ensures the other extension prefs are set up with
89    // the defaults.
90    service->extension_prefs()->OnExtensionInstalled(
91        extension, Extension::ENABLED, false);
92    service->SetIsIncognitoEnabled(extension, true);
93  }
94
95  return WaitForExtensionHostsToLoad();
96}
97
98bool ExtensionBrowserTest::LoadExtension(const FilePath& path) {
99  return LoadExtensionImpl(path, false);
100}
101
102bool ExtensionBrowserTest::LoadExtensionIncognito(const FilePath& path) {
103  return LoadExtensionImpl(path, true);
104}
105
106FilePath ExtensionBrowserTest::PackExtension(const FilePath& dir_path) {
107  FilePath crx_path;
108  if (!PathService::Get(base::DIR_TEMP, &crx_path)) {
109    ADD_FAILURE() << "Failed to get DIR_TEMP from PathService.";
110    return FilePath();
111  }
112  crx_path = crx_path.AppendASCII("temp.crx");
113  if (!file_util::Delete(crx_path, false)) {
114    ADD_FAILURE() << "Failed to delete crx: " << crx_path.value();
115    return FilePath();
116  }
117
118  FilePath pem_path = crx_path.DirName().AppendASCII("temp.pem");
119  if (!file_util::Delete(pem_path, false)) {
120    ADD_FAILURE() << "Failed to delete pem: " << pem_path.value();
121    return FilePath();
122  }
123
124  if (!file_util::PathExists(dir_path)) {
125    ADD_FAILURE() << "Extension dir not found: " << dir_path.value();
126    return FilePath();
127  }
128
129  scoped_ptr<ExtensionCreator> creator(new ExtensionCreator());
130  if (!creator->Run(dir_path,
131                    crx_path,
132                    FilePath(),  // no existing pem, use empty path
133                    pem_path)) {
134    ADD_FAILURE() << "ExtensionCreator::Run() failed.";
135    return FilePath();
136  }
137
138  if (!file_util::PathExists(crx_path)) {
139    ADD_FAILURE() << crx_path.value() << " was not created.";
140    return FilePath();
141  }
142  return crx_path;
143}
144
145// This class is used to simulate an installation abort by the user.
146class MockAbortExtensionInstallUI : public ExtensionInstallUI {
147 public:
148  MockAbortExtensionInstallUI() : ExtensionInstallUI(NULL) {}
149
150  // Simulate a user abort on an extension installation.
151  virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) {
152    delegate->InstallUIAbort();
153    MessageLoopForUI::current()->Quit();
154  }
155
156  virtual void ConfirmUninstall(Delegate* delegate,
157                                const Extension* extension) {}
158
159  virtual void OnInstallSuccess(const Extension* extension, SkBitmap* icon) {}
160
161  virtual void OnInstallFailure(const std::string& error) {}
162};
163
164class MockAutoConfirmExtensionInstallUI : public ExtensionInstallUI {
165 public:
166  MockAutoConfirmExtensionInstallUI(Profile* profile) :
167      ExtensionInstallUI(profile) {}
168
169  // Proceed without confirmation prompt.
170  virtual void ConfirmInstall(Delegate* delegate, const Extension* extension) {
171    delegate->InstallUIProceed();
172  }
173};
174
175bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id,
176                                                    const FilePath& path,
177                                                    InstallUIType ui_type,
178                                                    int expected_change) {
179  return InstallOrUpdateExtension(id, path, ui_type, expected_change,
180                                  browser()->profile());
181}
182
183bool ExtensionBrowserTest::InstallOrUpdateExtension(const std::string& id,
184                                                    const FilePath& path,
185                                                    InstallUIType ui_type,
186                                                    int expected_change,
187                                                    Profile* profile) {
188  ExtensionService* service = profile->GetExtensionService();
189  service->set_show_extensions_prompts(false);
190  size_t num_before = service->extensions()->size();
191
192  {
193    NotificationRegistrar registrar;
194    registrar.Add(this, NotificationType::EXTENSION_LOADED,
195                  NotificationService::AllSources());
196    registrar.Add(this, NotificationType::EXTENSION_UPDATE_DISABLED,
197                  NotificationService::AllSources());
198    registrar.Add(this, NotificationType::EXTENSION_INSTALL_ERROR,
199                  NotificationService::AllSources());
200
201    ExtensionInstallUI* install_ui = NULL;
202    if (ui_type == INSTALL_UI_TYPE_CANCEL)
203      install_ui = new MockAbortExtensionInstallUI();
204    else if (ui_type == INSTALL_UI_TYPE_NORMAL)
205      install_ui = new ExtensionInstallUI(profile);
206    else if (ui_type == INSTALL_UI_TYPE_AUTO_CONFIRM)
207      install_ui = new MockAutoConfirmExtensionInstallUI(profile);
208
209    // TODO(tessamac): Update callers to always pass an unpacked extension
210    //                 and then always pack the extension here.
211    FilePath crx_path = path;
212    if (crx_path.Extension() != FILE_PATH_LITERAL(".crx")) {
213      crx_path = PackExtension(path);
214    }
215    if (crx_path.empty())
216      return false;
217
218    scoped_refptr<CrxInstaller> installer(
219        new CrxInstaller(service, install_ui));
220    installer->set_expected_id(id);
221    installer->InstallCrx(crx_path);
222
223    ui_test_utils::RunMessageLoop();
224  }
225
226  size_t num_after = service->extensions()->size();
227  if (num_after != (num_before + expected_change)) {
228    VLOG(1) << "Num extensions before: " << base::IntToString(num_before)
229            << " num after: " << base::IntToString(num_after)
230            << " Installed extensions follow:";
231
232    for (size_t i = 0; i < service->extensions()->size(); ++i)
233      VLOG(1) << "  " << (*service->extensions())[i]->id();
234
235    VLOG(1) << "Errors follow:";
236    const std::vector<std::string>* errors =
237        ExtensionErrorReporter::GetInstance()->GetErrors();
238    for (std::vector<std::string>::const_iterator iter = errors->begin();
239         iter != errors->end(); ++iter)
240      VLOG(1) << *iter;
241
242    return false;
243  }
244
245  return WaitForExtensionHostsToLoad();
246}
247
248void ExtensionBrowserTest::ReloadExtension(const std::string& extension_id) {
249  ExtensionService* service = browser()->profile()->GetExtensionService();
250  service->ReloadExtension(extension_id);
251  ui_test_utils::RegisterAndWait(this,
252                                 NotificationType::EXTENSION_LOADED,
253                                 NotificationService::AllSources());
254}
255
256void ExtensionBrowserTest::UnloadExtension(const std::string& extension_id) {
257  ExtensionService* service = browser()->profile()->GetExtensionService();
258  service->UnloadExtension(extension_id, UnloadedExtensionInfo::DISABLE);
259}
260
261void ExtensionBrowserTest::UninstallExtension(const std::string& extension_id) {
262  ExtensionService* service = browser()->profile()->GetExtensionService();
263  service->UninstallExtension(extension_id, false);
264}
265
266void ExtensionBrowserTest::DisableExtension(const std::string& extension_id) {
267  ExtensionService* service = browser()->profile()->GetExtensionService();
268  service->DisableExtension(extension_id);
269}
270
271void ExtensionBrowserTest::EnableExtension(const std::string& extension_id) {
272  ExtensionService* service = browser()->profile()->GetExtensionService();
273  service->EnableExtension(extension_id);
274}
275
276bool ExtensionBrowserTest::WaitForPageActionCountChangeTo(int count) {
277  LocationBarTesting* location_bar =
278      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
279  if (location_bar->PageActionCount() != count) {
280    target_page_action_count_ = count;
281    ui_test_utils::RegisterAndWait(this,
282        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
283        NotificationService::AllSources());
284  }
285  return location_bar->PageActionCount() == count;
286}
287
288bool ExtensionBrowserTest::WaitForPageActionVisibilityChangeTo(int count) {
289  LocationBarTesting* location_bar =
290      browser()->window()->GetLocationBar()->GetLocationBarForTesting();
291  if (location_bar->PageActionVisibleCount() != count) {
292    target_visible_page_action_count_ = count;
293    ui_test_utils::RegisterAndWait(this,
294        NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
295        NotificationService::AllSources());
296  }
297  return location_bar->PageActionVisibleCount() == count;
298}
299
300bool ExtensionBrowserTest::WaitForExtensionHostsToLoad() {
301  // Wait for all the extension hosts that exist to finish loading.
302  NotificationRegistrar registrar;
303  registrar.Add(this, NotificationType::EXTENSION_HOST_DID_STOP_LOADING,
304                NotificationService::AllSources());
305
306  ExtensionProcessManager* manager =
307        browser()->profile()->GetExtensionProcessManager();
308  for (ExtensionProcessManager::const_iterator iter = manager->begin();
309       iter != manager->end();) {
310    if ((*iter)->did_stop_loading()) {
311      ++iter;
312    } else {
313      ui_test_utils::RunMessageLoop();
314
315      // Test activity may have modified the set of extension processes during
316      // message processing, so re-start the iteration to catch added/removed
317      // processes.
318      iter = manager->begin();
319    }
320  }
321  return true;
322}
323
324bool ExtensionBrowserTest::WaitForExtensionInstall() {
325  int before = extension_installs_observed_;
326  ui_test_utils::RegisterAndWait(this, NotificationType::EXTENSION_INSTALLED,
327                                 NotificationService::AllSources());
328  return extension_installs_observed_ == (before + 1);
329}
330
331bool ExtensionBrowserTest::WaitForExtensionInstallError() {
332  int before = extension_installs_observed_;
333  ui_test_utils::RegisterAndWait(this,
334                                 NotificationType::EXTENSION_INSTALL_ERROR,
335                                 NotificationService::AllSources());
336  return extension_installs_observed_ == before;
337}
338
339void ExtensionBrowserTest::WaitForExtensionLoad() {
340  ui_test_utils::RegisterAndWait(this, NotificationType::EXTENSION_LOADED,
341                                 NotificationService::AllSources());
342  WaitForExtensionHostsToLoad();
343}
344
345bool ExtensionBrowserTest::WaitForExtensionCrash(
346    const std::string& extension_id) {
347  ExtensionService* service = browser()->profile()->GetExtensionService();
348
349  if (!service->GetExtensionById(extension_id, true)) {
350    // The extension is already unloaded, presumably due to a crash.
351    return true;
352  }
353  ui_test_utils::RegisterAndWait(this,
354                                 NotificationType::EXTENSION_PROCESS_TERMINATED,
355                                 NotificationService::AllSources());
356  return (service->GetExtensionById(extension_id, true) == NULL);
357}
358
359void ExtensionBrowserTest::Observe(NotificationType type,
360                                   const NotificationSource& source,
361                                   const NotificationDetails& details) {
362  switch (type.value) {
363    case NotificationType::EXTENSION_LOADED:
364      last_loaded_extension_id_ = Details<const Extension>(details).ptr()->id();
365      VLOG(1) << "Got EXTENSION_LOADED notification.";
366      MessageLoopForUI::current()->Quit();
367      break;
368
369    case NotificationType::EXTENSION_UPDATE_DISABLED:
370      VLOG(1) << "Got EXTENSION_UPDATE_DISABLED notification.";
371      MessageLoopForUI::current()->Quit();
372      break;
373
374    case NotificationType::EXTENSION_HOST_DID_STOP_LOADING:
375      VLOG(1) << "Got EXTENSION_HOST_DID_STOP_LOADING notification.";
376      MessageLoopForUI::current()->Quit();
377      break;
378
379    case NotificationType::EXTENSION_INSTALLED:
380      VLOG(1) << "Got EXTENSION_INSTALLED notification.";
381      ++extension_installs_observed_;
382      MessageLoopForUI::current()->Quit();
383      break;
384
385    case NotificationType::EXTENSION_INSTALL_ERROR:
386      VLOG(1) << "Got EXTENSION_INSTALL_ERROR notification.";
387      MessageLoopForUI::current()->Quit();
388      break;
389
390    case NotificationType::EXTENSION_PROCESS_CREATED:
391      VLOG(1) << "Got EXTENSION_PROCESS_CREATED notification.";
392      MessageLoopForUI::current()->Quit();
393      break;
394
395    case NotificationType::EXTENSION_PROCESS_TERMINATED:
396      VLOG(1) << "Got EXTENSION_PROCESS_TERMINATED notification.";
397      MessageLoopForUI::current()->Quit();
398      break;
399
400    case NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED: {
401      LocationBarTesting* location_bar =
402          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
403      VLOG(1) << "Got EXTENSION_PAGE_ACTION_COUNT_CHANGED notification. Number "
404                 "of page actions: " << location_bar->PageActionCount();
405      if (location_bar->PageActionCount() ==
406          target_page_action_count_) {
407        target_page_action_count_ = -1;
408        MessageLoopForUI::current()->Quit();
409      }
410      break;
411    }
412
413    case NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED: {
414      LocationBarTesting* location_bar =
415          browser()->window()->GetLocationBar()->GetLocationBarForTesting();
416      VLOG(1) << "Got EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED notification. "
417                 "Number of visible page actions: "
418              << location_bar->PageActionVisibleCount();
419      if (location_bar->PageActionVisibleCount() ==
420          target_visible_page_action_count_) {
421        target_visible_page_action_count_ = -1;
422        MessageLoopForUI::current()->Quit();
423      }
424      break;
425    }
426
427    default:
428      NOTREACHED();
429      break;
430  }
431}
432