Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / extensions / extension_apitest.cc
blob261ee75355bd0f3a42470791faf6aacf7d9eb847
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.
5 #include "chrome/browser/extensions/extension_apitest.h"
7 #include "base/base_switches.h"
8 #include "base/strings/string_split.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/unpacked_installer.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/extensions/app_launch_params.h"
16 #include "chrome/browser/ui/extensions/application_launch.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/common/content_switches.h"
19 #include "extensions/browser/api/test/test_api.h"
20 #include "extensions/browser/extension_registry.h"
21 #include "extensions/browser/extension_system.h"
22 #include "extensions/common/constants.h"
23 #include "extensions/common/extension.h"
24 #include "extensions/common/extension_set.h"
25 #include "extensions/test/result_catcher.h"
26 #include "net/base/escape.h"
27 #include "net/base/filename_util.h"
28 #include "net/test/embedded_test_server/embedded_test_server.h"
29 #include "net/test/embedded_test_server/http_request.h"
30 #include "net/test/embedded_test_server/http_response.h"
31 #include "net/test/spawned_test_server/spawned_test_server.h"
33 namespace {
35 const char kTestCustomArg[] = "customArg";
36 const char kTestServerPort[] = "testServer.port";
37 const char kTestDataDirectory[] = "testDataDirectory";
38 const char kTestWebSocketPort[] = "testWebSocketPort";
39 const char kSitePerProcess[] = "sitePerProcess";
40 const char kFtpServerPort[] = "ftpServer.port";
41 const char kSpawnedTestServerPort[] = "spawnedTestServer.port";
43 scoped_ptr<net::test_server::HttpResponse> HandleServerRedirectRequest(
44 const net::test_server::HttpRequest& request) {
45 if (!base::StartsWith(request.relative_url, "/server-redirect?",
46 base::CompareCase::SENSITIVE))
47 return nullptr;
49 size_t query_string_pos = request.relative_url.find('?');
50 std::string redirect_target =
51 request.relative_url.substr(query_string_pos + 1);
53 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
54 new net::test_server::BasicHttpResponse);
55 http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
56 http_response->AddCustomHeader("Location", redirect_target);
57 return http_response.Pass();
60 scoped_ptr<net::test_server::HttpResponse> HandleEchoHeaderRequest(
61 const net::test_server::HttpRequest& request) {
62 if (!base::StartsWith(request.relative_url, "/echoheader?",
63 base::CompareCase::SENSITIVE))
64 return nullptr;
66 size_t query_string_pos = request.relative_url.find('?');
67 std::string header_name =
68 request.relative_url.substr(query_string_pos + 1);
70 std::string header_value;
71 std::map<std::string, std::string>::const_iterator it = request.headers.find(
72 header_name);
73 if (it != request.headers.end())
74 header_value = it->second;
76 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
77 new net::test_server::BasicHttpResponse);
78 http_response->set_code(net::HTTP_OK);
79 http_response->set_content(header_value);
80 return http_response.Pass();
83 scoped_ptr<net::test_server::HttpResponse> HandleSetCookieRequest(
84 const net::test_server::HttpRequest& request) {
85 if (!base::StartsWith(request.relative_url, "/set-cookie?",
86 base::CompareCase::SENSITIVE))
87 return nullptr;
89 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
90 new net::test_server::BasicHttpResponse);
91 http_response->set_code(net::HTTP_OK);
93 size_t query_string_pos = request.relative_url.find('?');
94 std::string cookie_value =
95 request.relative_url.substr(query_string_pos + 1);
97 for (const std::string& cookie : base::SplitString(
98 cookie_value, "&", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL))
99 http_response->AddCustomHeader("Set-Cookie", cookie);
101 return http_response.Pass();
104 scoped_ptr<net::test_server::HttpResponse> HandleSetHeaderRequest(
105 const net::test_server::HttpRequest& request) {
106 if (!base::StartsWith(request.relative_url, "/set-header?",
107 base::CompareCase::SENSITIVE))
108 return nullptr;
110 size_t query_string_pos = request.relative_url.find('?');
111 std::string escaped_header =
112 request.relative_url.substr(query_string_pos + 1);
114 std::string header =
115 net::UnescapeURLComponent(escaped_header,
116 net::UnescapeRule::NORMAL |
117 net::UnescapeRule::SPACES |
118 net::UnescapeRule::URL_SPECIAL_CHARS);
120 size_t colon_pos = header.find(':');
121 if (colon_pos == std::string::npos)
122 return scoped_ptr<net::test_server::HttpResponse>();
124 std::string header_name = header.substr(0, colon_pos);
125 // Skip space after colon.
126 std::string header_value = header.substr(colon_pos + 2);
128 scoped_ptr<net::test_server::BasicHttpResponse> http_response(
129 new net::test_server::BasicHttpResponse);
130 http_response->set_code(net::HTTP_OK);
131 http_response->AddCustomHeader(header_name, header_value);
132 return http_response.Pass();
135 }; // namespace
137 ExtensionApiTest::ExtensionApiTest() {
138 embedded_test_server()->RegisterRequestHandler(
139 base::Bind(&HandleServerRedirectRequest));
140 embedded_test_server()->RegisterRequestHandler(
141 base::Bind(&HandleEchoHeaderRequest));
142 embedded_test_server()->RegisterRequestHandler(
143 base::Bind(&HandleSetCookieRequest));
144 embedded_test_server()->RegisterRequestHandler(
145 base::Bind(&HandleSetHeaderRequest));
148 ExtensionApiTest::~ExtensionApiTest() {}
150 void ExtensionApiTest::SetUpInProcessBrowserTestFixture() {
151 DCHECK(!test_config_.get()) << "Previous test did not clear config state.";
152 test_config_.reset(new base::DictionaryValue());
153 test_config_->SetString(kTestDataDirectory,
154 net::FilePathToFileURL(test_data_dir_).spec());
155 test_config_->SetInteger(kTestWebSocketPort, 0);
156 test_config_->SetBoolean(kSitePerProcess,
157 base::CommandLine::ForCurrentProcess()->HasSwitch(
158 switches::kSitePerProcess));
159 extensions::TestGetConfigFunction::set_test_config_state(
160 test_config_.get());
163 void ExtensionApiTest::TearDownInProcessBrowserTestFixture() {
164 extensions::TestGetConfigFunction::set_test_config_state(NULL);
165 test_config_.reset(NULL);
168 bool ExtensionApiTest::RunExtensionTest(const std::string& extension_name) {
169 return RunExtensionTestImpl(
170 extension_name, std::string(), NULL, kFlagEnableFileAccess);
173 bool ExtensionApiTest::RunExtensionTestIncognito(
174 const std::string& extension_name) {
175 return RunExtensionTestImpl(extension_name,
176 std::string(),
177 NULL,
178 kFlagEnableIncognito | kFlagEnableFileAccess);
181 bool ExtensionApiTest::RunExtensionTestIgnoreManifestWarnings(
182 const std::string& extension_name) {
183 return RunExtensionTestImpl(
184 extension_name, std::string(), NULL, kFlagIgnoreManifestWarnings);
187 bool ExtensionApiTest::RunExtensionTestAllowOldManifestVersion(
188 const std::string& extension_name) {
189 return RunExtensionTestImpl(
190 extension_name,
191 std::string(),
192 NULL,
193 kFlagEnableFileAccess | kFlagAllowOldManifestVersions);
196 bool ExtensionApiTest::RunComponentExtensionTest(
197 const std::string& extension_name) {
198 return RunExtensionTestImpl(extension_name,
199 std::string(),
200 NULL,
201 kFlagEnableFileAccess | kFlagLoadAsComponent);
204 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
205 const std::string& extension_name) {
206 return RunExtensionTestImpl(extension_name, std::string(), NULL, kFlagNone);
209 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
210 const std::string& extension_name) {
211 return RunExtensionTestImpl(
212 extension_name, std::string(), NULL, kFlagEnableIncognito);
215 bool ExtensionApiTest::ExtensionSubtestsAreSkipped() {
216 // See http://crbug.com/177163 for details.
217 #if defined(OS_WIN) && !defined(NDEBUG)
218 LOG(WARNING) << "Workaround for 177163, prematurely returning";
219 return true;
220 #else
221 return false;
222 #endif
225 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
226 const std::string& page_url) {
227 return RunExtensionSubtest(extension_name, page_url, kFlagEnableFileAccess);
230 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
231 const std::string& page_url,
232 int flags) {
233 DCHECK(!page_url.empty()) << "Argument page_url is required.";
234 if (ExtensionSubtestsAreSkipped())
235 return true;
236 return RunExtensionTestImpl(extension_name, page_url, NULL, flags);
239 bool ExtensionApiTest::RunPageTest(const std::string& page_url) {
240 return RunExtensionSubtest(std::string(), page_url);
243 bool ExtensionApiTest::RunPageTest(const std::string& page_url,
244 int flags) {
245 return RunExtensionSubtest(std::string(), page_url, flags);
248 bool ExtensionApiTest::RunPlatformAppTest(const std::string& extension_name) {
249 return RunExtensionTestImpl(
250 extension_name, std::string(), NULL, kFlagLaunchPlatformApp);
253 bool ExtensionApiTest::RunPlatformAppTestWithArg(
254 const std::string& extension_name, const char* custom_arg) {
255 return RunExtensionTestImpl(
256 extension_name, std::string(), custom_arg, kFlagLaunchPlatformApp);
259 bool ExtensionApiTest::RunPlatformAppTestWithFlags(
260 const std::string& extension_name, int flags) {
261 return RunExtensionTestImpl(
262 extension_name, std::string(), NULL, flags | kFlagLaunchPlatformApp);
265 // Load |extension_name| extension and/or |page_url| and wait for
266 // PASSED or FAILED notification.
267 bool ExtensionApiTest::RunExtensionTestImpl(const std::string& extension_name,
268 const std::string& page_url,
269 const char* custom_arg,
270 int flags) {
271 bool load_as_component = (flags & kFlagLoadAsComponent) != 0;
272 bool launch_platform_app = (flags & kFlagLaunchPlatformApp) != 0;
273 bool use_incognito = (flags & kFlagUseIncognito) != 0;
275 if (custom_arg && custom_arg[0])
276 test_config_->SetString(kTestCustomArg, custom_arg);
278 extensions::ResultCatcher catcher;
279 DCHECK(!extension_name.empty() || !page_url.empty()) <<
280 "extension_name and page_url cannot both be empty";
282 const extensions::Extension* extension = NULL;
283 if (!extension_name.empty()) {
284 base::FilePath extension_path = test_data_dir_.AppendASCII(extension_name);
285 if (load_as_component) {
286 extension = LoadExtensionAsComponent(extension_path);
287 } else {
288 int browser_test_flags = ExtensionBrowserTest::kFlagNone;
289 if (flags & kFlagEnableIncognito)
290 browser_test_flags |= ExtensionBrowserTest::kFlagEnableIncognito;
291 if (flags & kFlagEnableFileAccess)
292 browser_test_flags |= ExtensionBrowserTest::kFlagEnableFileAccess;
293 if (flags & kFlagIgnoreManifestWarnings)
294 browser_test_flags |= ExtensionBrowserTest::kFlagIgnoreManifestWarnings;
295 if (flags & kFlagAllowOldManifestVersions) {
296 browser_test_flags |=
297 ExtensionBrowserTest::kFlagAllowOldManifestVersions;
299 extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
301 if (!extension) {
302 message_ = "Failed to load extension.";
303 return false;
307 // If there is a page_url to load, navigate it.
308 if (!page_url.empty()) {
309 GURL url = GURL(page_url);
311 // Note: We use is_valid() here in the expectation that the provided url
312 // may lack a scheme & host and thus be a relative url within the loaded
313 // extension.
314 if (!url.is_valid()) {
315 DCHECK(!extension_name.empty()) <<
316 "Relative page_url given with no extension_name";
318 url = extension->GetResourceURL(page_url);
321 if (use_incognito)
322 OpenURLOffTheRecord(browser()->profile(), url);
323 else
324 ui_test_utils::NavigateToURL(browser(), url);
325 } else if (launch_platform_app) {
326 AppLaunchParams params(browser()->profile(), extension,
327 extensions::LAUNCH_CONTAINER_NONE, NEW_WINDOW,
328 extensions::SOURCE_TEST);
329 params.command_line = *base::CommandLine::ForCurrentProcess();
330 OpenApplication(params);
333 if (!catcher.GetNextResult()) {
334 message_ = catcher.message();
335 return false;
338 return true;
341 // Test that exactly one extension is loaded, and return it.
342 const extensions::Extension* ExtensionApiTest::GetSingleLoadedExtension() {
343 extensions::ExtensionRegistry* registry =
344 extensions::ExtensionRegistry::Get(browser()->profile());
346 const extensions::Extension* result = NULL;
347 for (const scoped_refptr<const extensions::Extension>& extension :
348 registry->enabled_extensions()) {
349 // Ignore any component extensions. They are automatically loaded into all
350 // profiles and aren't the extension we're looking for here.
351 if (extension->location() == extensions::Manifest::COMPONENT)
352 continue;
354 if (result != NULL) {
355 // TODO(yoz): this is misleading; it counts component extensions.
356 message_ = base::StringPrintf(
357 "Expected only one extension to be present. Found %u.",
358 static_cast<unsigned>(registry->enabled_extensions().size()));
359 return NULL;
362 result = extension.get();
365 if (!result) {
366 message_ = "extension pointer is NULL.";
367 return NULL;
369 return result;
372 bool ExtensionApiTest::StartEmbeddedTestServer() {
373 if (!embedded_test_server()->InitializeAndWaitUntilReady())
374 return false;
376 // Build a dictionary of values that tests can use to build URLs that
377 // access the test server and local file system. Tests can see these values
378 // using the extension API function chrome.test.getConfig().
379 test_config_->SetInteger(kTestServerPort,
380 embedded_test_server()->port());
382 return true;
385 bool ExtensionApiTest::StartWebSocketServer(
386 const base::FilePath& root_directory) {
387 websocket_server_.reset(new net::SpawnedTestServer(
388 net::SpawnedTestServer::TYPE_WS,
389 net::SpawnedTestServer::kLocalhost,
390 root_directory));
392 if (!websocket_server_->Start())
393 return false;
395 test_config_->SetInteger(kTestWebSocketPort,
396 websocket_server_->host_port_pair().port());
398 return true;
401 bool ExtensionApiTest::StartFTPServer(const base::FilePath& root_directory) {
402 ftp_server_.reset(new net::SpawnedTestServer(
403 net::SpawnedTestServer::TYPE_FTP,
404 net::SpawnedTestServer::kLocalhost,
405 root_directory));
407 if (!ftp_server_->Start())
408 return false;
410 test_config_->SetInteger(kFtpServerPort,
411 ftp_server_->host_port_pair().port());
413 return true;
416 bool ExtensionApiTest::StartSpawnedTestServer() {
417 if (!test_server()->Start())
418 return false;
420 // Build a dictionary of values that tests can use to build URLs that
421 // access the test server and local file system. Tests can see these values
422 // using the extension API function chrome.test.getConfig().
423 test_config_->SetInteger(kSpawnedTestServerPort,
424 test_server()->host_port_pair().port());
426 return true;
429 void ExtensionApiTest::SetUpCommandLine(base::CommandLine* command_line) {
430 ExtensionBrowserTest::SetUpCommandLine(command_line);
431 test_data_dir_ = test_data_dir_.AppendASCII("api_test");
432 // Backgrounded renderer processes run at a lower priority, causing the
433 // tests to take more time to complete. Disable backgrounding so that the
434 // tests don't time out.
435 command_line->AppendSwitch(switches::kDisableRendererBackgrounding);