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