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 "chrome/browser/extensions/extension_apitest.h"
6 
7 #include <stddef.h>
8 #include <utility>
9 
10 #include "base/base_switches.h"
11 #include "base/bind.h"
12 #include "base/feature_list.h"
13 #include "base/files/file_path.h"
14 #include "base/path_service.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "build/build_config.h"
19 #include "chrome/browser/apps/app_service/app_launch_params.h"
20 #include "chrome/browser/apps/app_service/app_service_proxy.h"
21 #include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
22 #include "chrome/browser/apps/app_service/browser_app_launcher.h"
23 #include "chrome/browser/extensions/extension_service.h"
24 #include "chrome/browser/extensions/unpacked_installer.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/test/base/ui_test_utils.h"
28 #include "content/public/common/content_switches.h"
29 #include "extensions/browser/api/test/test_api.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/common/constants.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/extension_paths.h"
35 #include "extensions/common/extension_set.h"
36 #include "extensions/common/feature_switch.h"
37 #include "extensions/common/switches.h"
38 #include "extensions/test/result_catcher.h"
39 #include "net/base/escape.h"
40 #include "net/base/filename_util.h"
41 #include "net/test/embedded_test_server/default_handlers.h"
42 #include "net/test/embedded_test_server/embedded_test_server.h"
43 #include "net/test/embedded_test_server/http_request.h"
44 #include "net/test/embedded_test_server/http_response.h"
45 #include "net/test/embedded_test_server/request_handler_util.h"
46 #include "net/test/spawned_test_server/spawned_test_server.h"
47 
48 namespace extensions {
49 
50 namespace {
51 
52 const char kTestCustomArg[] = "customArg";
53 const char kTestDataDirectory[] = "testDataDirectory";
54 const char kTestWebSocketPort[] = "testWebSocketPort";
55 const char kFtpServerPort[] = "ftpServer.port";
56 const char kEmbeddedTestServerPort[] = "testServer.port";
57 
58 }  // namespace
59 
ExtensionApiTest()60 ExtensionApiTest::ExtensionApiTest() {
61   net::test_server::RegisterDefaultHandlers(embedded_test_server());
62 }
63 
64 ExtensionApiTest::~ExtensionApiTest() = default;
65 
SetUpOnMainThread()66 void ExtensionApiTest::SetUpOnMainThread() {
67   ExtensionBrowserTest::SetUpOnMainThread();
68   DCHECK(!test_config_.get()) << "Previous test did not clear config state.";
69   test_config_.reset(new base::DictionaryValue());
70   test_config_->SetString(kTestDataDirectory,
71                           net::FilePathToFileURL(test_data_dir_).spec());
72 
73   if (embedded_test_server()->Started()) {
74     // InitializeEmbeddedTestServer was called before |test_config_| was set.
75     // Set the missing port key.
76     test_config_->SetInteger(kEmbeddedTestServerPort,
77                              embedded_test_server()->port());
78   }
79 
80   TestGetConfigFunction::set_test_config_state(test_config_.get());
81 }
82 
TearDownOnMainThread()83 void ExtensionApiTest::TearDownOnMainThread() {
84   ExtensionBrowserTest::TearDownOnMainThread();
85   TestGetConfigFunction::set_test_config_state(NULL);
86   test_config_.reset(NULL);
87 }
88 
RunExtensionTest(const std::string & extension_name)89 bool ExtensionApiTest::RunExtensionTest(const std::string& extension_name) {
90   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
91                               kFlagEnableFileAccess, kFlagNone);
92 }
93 
RunExtensionTestWithFlags(const std::string & extension_name,int browser_test_flags,int api_test_flags)94 bool ExtensionApiTest::RunExtensionTestWithFlags(
95     const std::string& extension_name,
96     int browser_test_flags,
97     int api_test_flags) {
98   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
99                               browser_test_flags, api_test_flags);
100 }
101 
RunExtensionTestWithArg(const std::string & extension_name,const char * custom_arg)102 bool ExtensionApiTest::RunExtensionTestWithArg(
103     const std::string& extension_name,
104     const char* custom_arg) {
105   return RunExtensionTestImpl(extension_name, std::string(), custom_arg,
106                               kFlagEnableFileAccess, kFlagNone);
107 }
108 
RunExtensionTestWithFlagsAndArg(const std::string & extension_name,const char * custom_arg,int browser_test_flags,int api_test_flags)109 bool ExtensionApiTest::RunExtensionTestWithFlagsAndArg(
110     const std::string& extension_name,
111     const char* custom_arg,
112     int browser_test_flags,
113     int api_test_flags) {
114   return RunExtensionTestImpl(extension_name, std::string(), custom_arg,
115                               browser_test_flags, api_test_flags);
116 }
117 
RunExtensionTestIncognito(const std::string & extension_name)118 bool ExtensionApiTest::RunExtensionTestIncognito(
119     const std::string& extension_name) {
120   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
121                               kFlagEnableIncognito | kFlagEnableFileAccess,
122                               kFlagNone);
123 }
124 
RunExtensionTestIgnoreManifestWarnings(const std::string & extension_name)125 bool ExtensionApiTest::RunExtensionTestIgnoreManifestWarnings(
126     const std::string& extension_name) {
127   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
128                               kFlagIgnoreManifestWarnings, kFlagNone);
129 }
130 
RunExtensionTestAllowOldManifestVersion(const std::string & extension_name)131 bool ExtensionApiTest::RunExtensionTestAllowOldManifestVersion(
132     const std::string& extension_name) {
133   return RunExtensionTestImpl(
134       extension_name, std::string(), nullptr,
135       kFlagEnableFileAccess | kFlagAllowOldManifestVersions, kFlagNone);
136 }
137 
RunComponentExtensionTest(const std::string & extension_name)138 bool ExtensionApiTest::RunComponentExtensionTest(
139     const std::string& extension_name) {
140   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
141                               kFlagEnableFileAccess, kFlagLoadAsComponent);
142 }
143 
RunComponentExtensionTestWithArg(const std::string & extension_name,const char * custom_arg)144 bool ExtensionApiTest::RunComponentExtensionTestWithArg(
145     const std::string& extension_name,
146     const char* custom_arg) {
147   return RunExtensionTestImpl(extension_name, std::string(), custom_arg,
148                               kFlagEnableFileAccess, kFlagLoadAsComponent);
149 }
150 
RunExtensionTestNoFileAccess(const std::string & extension_name)151 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
152     const std::string& extension_name) {
153   return RunExtensionTestImpl(extension_name, std::string(), nullptr, kFlagNone,
154                               kFlagNone);
155 }
156 
RunExtensionTestIncognitoNoFileAccess(const std::string & extension_name)157 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
158     const std::string& extension_name) {
159   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
160                               kFlagEnableIncognito, kFlagNone);
161 }
162 
RunExtensionSubtest(const std::string & extension_name,const std::string & page_url)163 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
164                                            const std::string& page_url) {
165   return RunExtensionSubtestWithArgAndFlags(extension_name, page_url, nullptr,
166                                             kFlagEnableFileAccess, kFlagNone);
167 }
168 
RunExtensionSubtest(const std::string & extension_name,const std::string & page_url,int browser_test_flags,int api_test_flags)169 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
170                                            const std::string& page_url,
171                                            int browser_test_flags,
172                                            int api_test_flags) {
173   return RunExtensionSubtestWithArgAndFlags(extension_name, page_url, nullptr,
174                                             browser_test_flags, api_test_flags);
175 }
176 
RunExtensionSubtestWithArg(const std::string & extension_name,const std::string & page_url,const char * custom_arg)177 bool ExtensionApiTest::RunExtensionSubtestWithArg(
178     const std::string& extension_name,
179     const std::string& page_url,
180     const char* custom_arg) {
181   return RunExtensionSubtestWithArgAndFlags(
182       extension_name, page_url, custom_arg, kFlagEnableFileAccess, kFlagNone);
183 }
184 
RunExtensionSubtestWithArgAndFlags(const std::string & extension_name,const std::string & page_url,const char * custom_arg,int browser_test_flags,int api_test_flags)185 bool ExtensionApiTest::RunExtensionSubtestWithArgAndFlags(
186     const std::string& extension_name,
187     const std::string& page_url,
188     const char* custom_arg,
189     int browser_test_flags,
190     int api_test_flags) {
191   DCHECK(!page_url.empty()) << "Argument page_url is required.";
192   return RunExtensionTestImpl(extension_name, page_url, custom_arg,
193                               browser_test_flags, api_test_flags);
194 }
195 
RunPageTest(const std::string & page_url)196 bool ExtensionApiTest::RunPageTest(const std::string& page_url) {
197   return RunExtensionSubtest(std::string(), page_url);
198 }
199 
RunPageTest(const std::string & page_url,int browser_test_flags,int api_test_flags)200 bool ExtensionApiTest::RunPageTest(const std::string& page_url,
201                                    int browser_test_flags,
202                                    int api_test_flags) {
203   return RunExtensionSubtest(std::string(), page_url, browser_test_flags,
204                              api_test_flags);
205 }
206 
RunPlatformAppTest(const std::string & extension_name)207 bool ExtensionApiTest::RunPlatformAppTest(const std::string& extension_name) {
208   return RunExtensionTestImpl(extension_name, std::string(), nullptr, kFlagNone,
209                               kFlagLaunchPlatformApp);
210 }
211 
RunPlatformAppTestWithArg(const std::string & extension_name,const char * custom_arg)212 bool ExtensionApiTest::RunPlatformAppTestWithArg(
213     const std::string& extension_name, const char* custom_arg) {
214   return RunPlatformAppTestWithFlags(extension_name, custom_arg, kFlagNone,
215                                      kFlagNone);
216 }
217 
RunPlatformAppTestWithFlags(const std::string & extension_name,int browser_test_flags,int api_test_flags)218 bool ExtensionApiTest::RunPlatformAppTestWithFlags(
219     const std::string& extension_name,
220     int browser_test_flags,
221     int api_test_flags) {
222   return RunExtensionTestImpl(extension_name, std::string(), nullptr,
223                               browser_test_flags,
224                               api_test_flags | kFlagLaunchPlatformApp);
225 }
226 
RunPlatformAppTestWithFlags(const std::string & extension_name,const char * custom_arg,int browser_test_flags,int api_test_flags)227 bool ExtensionApiTest::RunPlatformAppTestWithFlags(
228     const std::string& extension_name,
229     const char* custom_arg,
230     int browser_test_flags,
231     int api_test_flags) {
232   return RunExtensionTestImpl(extension_name, std::string(), custom_arg,
233                               browser_test_flags,
234                               api_test_flags | kFlagLaunchPlatformApp);
235 }
236 
RunExtensionTestImpl(const std::string & extension_name,const std::string & page_url,const char * custom_arg,int browser_test_flags,int api_test_flags)237 bool ExtensionApiTest::RunExtensionTestImpl(const std::string& extension_name,
238                                             const std::string& page_url,
239                                             const char* custom_arg,
240                                             int browser_test_flags,
241                                             int api_test_flags) {
242   static_assert(static_cast<int>(ExtensionBrowserTest::kFlagNone) ==
243                     static_cast<int>(ExtensionApiTest::kFlagNone),
244                 "ExtensionApiTest::kFlagNone has an incorrect value");
245   // The value of "api_test_flags" should not have any of the flags from the
246   // ExtensionBrowserTest::Flags range. "kFlagNextValue - 1" is all of the
247   // bits from that range.
248   CHECK_EQ(0, api_test_flags & (kFlagNextValue - 1));
249   bool load_as_component =
250       api_test_flags & ExtensionApiTest::kFlagLoadAsComponent;
251   bool launch_platform_app =
252       api_test_flags & ExtensionApiTest::kFlagLaunchPlatformApp;
253   bool use_incognito = api_test_flags & kFlagUseIncognito;
254   bool use_root_extensions_dir =
255       api_test_flags & ExtensionApiTest::kFlagUseRootExtensionsDir;
256 
257   if (custom_arg && custom_arg[0])
258     SetCustomArg(custom_arg);
259 
260   ResultCatcher catcher;
261   DCHECK(!extension_name.empty() || !page_url.empty()) <<
262       "extension_name and page_url cannot both be empty";
263 
264   const Extension* extension = NULL;
265   if (!extension_name.empty()) {
266     const base::FilePath& root_path =
267         use_root_extensions_dir ? shared_test_data_dir_ : test_data_dir_;
268     base::FilePath extension_path = root_path.AppendASCII(extension_name);
269     if (load_as_component) {
270       extension = LoadExtensionAsComponent(extension_path);
271     } else {
272       extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
273     }
274     if (!extension) {
275       message_ = "Failed to load extension.";
276       return false;
277     }
278   }
279 
280   // If there is a page_url to load, navigate it.
281   if (!page_url.empty()) {
282     GURL url = GURL(page_url);
283 
284     // Note: We use is_valid() here in the expectation that the provided url
285     // may lack a scheme & host and thus be a relative url within the loaded
286     // extension.
287     if (!url.is_valid()) {
288       DCHECK(!extension_name.empty()) <<
289           "Relative page_url given with no extension_name";
290 
291       url = extension->GetResourceURL(page_url);
292     }
293 
294     if (use_incognito)
295       OpenURLOffTheRecord(browser()->profile(), url);
296     else
297       ui_test_utils::NavigateToURL(browser(), url);
298   } else if (launch_platform_app) {
299     apps::AppLaunchParams params(
300         extension->id(), LaunchContainer::kLaunchContainerNone,
301         WindowOpenDisposition::NEW_WINDOW, AppLaunchSource::kSourceTest);
302     params.command_line = *base::CommandLine::ForCurrentProcess();
303     apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
304         ->BrowserAppLauncher()
305         ->LaunchAppWithParams(std::move(params));
306   }
307 
308   if (!catcher.GetNextResult()) {
309     message_ = catcher.message();
310     return false;
311   }
312 
313   return true;
314 }
315 
316 // Test that exactly one extension is loaded, and return it.
GetSingleLoadedExtension()317 const Extension* ExtensionApiTest::GetSingleLoadedExtension() {
318   ExtensionRegistry* registry = ExtensionRegistry::Get(browser()->profile());
319 
320   const Extension* result = NULL;
321   for (const scoped_refptr<const Extension>& extension :
322        registry->enabled_extensions()) {
323     // Ignore any component extensions. They are automatically loaded into all
324     // profiles and aren't the extension we're looking for here.
325     if (extension->location() == Manifest::COMPONENT)
326       continue;
327 
328     if (result != NULL) {
329       // TODO(yoz): this is misleading; it counts component extensions.
330       message_ = base::StringPrintf(
331           "Expected only one extension to be present.  Found %u.",
332           static_cast<unsigned>(registry->enabled_extensions().size()));
333       return NULL;
334     }
335 
336     result = extension.get();
337   }
338 
339   if (!result) {
340     message_ = "extension pointer is NULL.";
341     return NULL;
342   }
343   return result;
344 }
345 
StartEmbeddedTestServer()346 bool ExtensionApiTest::StartEmbeddedTestServer() {
347   if (!InitializeEmbeddedTestServer())
348     return false;
349 
350   EmbeddedTestServerAcceptConnections();
351   return true;
352 }
353 
InitializeEmbeddedTestServer()354 bool ExtensionApiTest::InitializeEmbeddedTestServer() {
355   if (!embedded_test_server()->InitializeAndListen())
356     return false;
357 
358   // Build a dictionary of values that tests can use to build URLs that
359   // access the test server and local file system.  Tests can see these values
360   // using the extension API function chrome.test.getConfig().
361   if (test_config_) {
362     test_config_->SetInteger(kEmbeddedTestServerPort,
363                              embedded_test_server()->port());
364   }
365   // else SetUpOnMainThread has not been called yet. Possibly because the
366   // caller needs a valid port in an overridden SetUpCommandLine method.
367 
368   return true;
369 }
370 
EmbeddedTestServerAcceptConnections()371 void ExtensionApiTest::EmbeddedTestServerAcceptConnections() {
372   embedded_test_server()->StartAcceptingConnections();
373 }
374 
StartWebSocketServer(const base::FilePath & root_directory,bool enable_basic_auth)375 bool ExtensionApiTest::StartWebSocketServer(
376     const base::FilePath& root_directory,
377     bool enable_basic_auth) {
378   websocket_server_.reset(new net::SpawnedTestServer(
379       net::SpawnedTestServer::TYPE_WS, root_directory));
380   websocket_server_->set_websocket_basic_auth(enable_basic_auth);
381 
382   if (!websocket_server_->Start())
383     return false;
384 
385   test_config_->SetInteger(kTestWebSocketPort,
386                            websocket_server_->host_port_pair().port());
387 
388   return true;
389 }
390 
StartFTPServer(const base::FilePath & root_directory)391 bool ExtensionApiTest::StartFTPServer(const base::FilePath& root_directory) {
392   ftp_server_.reset(new net::SpawnedTestServer(net::SpawnedTestServer::TYPE_FTP,
393                                                root_directory));
394 
395   if (!ftp_server_->Start())
396     return false;
397 
398   test_config_->SetInteger(kFtpServerPort,
399                            ftp_server_->host_port_pair().port());
400 
401   return true;
402 }
403 
SetCustomArg(base::StringPiece custom_arg)404 void ExtensionApiTest::SetCustomArg(base::StringPiece custom_arg) {
405   test_config_->SetKey(kTestCustomArg, base::Value(custom_arg));
406 }
407 
SetUpCommandLine(base::CommandLine * command_line)408 void ExtensionApiTest::SetUpCommandLine(base::CommandLine* command_line) {
409   ExtensionBrowserTest::SetUpCommandLine(command_line);
410 
411   test_data_dir_ = test_data_dir_.AppendASCII("api_test");
412 
413   RegisterPathProvider();
414   base::PathService::Get(DIR_TEST_DATA, &shared_test_data_dir_);
415   shared_test_data_dir_ = shared_test_data_dir_.AppendASCII("api_test");
416 
417   // Backgrounded renderer processes run at a lower priority, causing the
418   // tests to take more time to complete. Disable backgrounding so that the
419   // tests don't time out.
420   command_line->AppendSwitch(::switches::kDisableRendererBackgrounding);
421 }
422 
423 }  // namespace extensions
424