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