Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / extension_apitest.cc
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 "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/extensions/api/test/test_api.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/unpacked_installer.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/extensions/application_launch.h"
17 #include "chrome/test/base/ui_test_utils.h"
18 #include "content/public/browser/notification_registrar.h"
19 #include "content/public/browser/notification_service.h"
20 #include "extensions/browser/extension_system.h"
21 #include "extensions/common/extension.h"
22 #include "extensions/common/extension_set.h"
23 #include "net/base/escape.h"
24 #include "net/base/net_util.h"
25 #include "net/test/embedded_test_server/embedded_test_server.h"
26 #include "net/test/embedded_test_server/http_request.h"
27 #include "net/test/embedded_test_server/http_response.h"
28 #include "net/test/spawned_test_server/spawned_test_server.h"
29
30 namespace {
31
32 const char kTestCustomArg[] = "customArg";
33 const char kTestServerPort[] = "testServer.port";
34 const char kTestDataDirectory[] = "testDataDirectory";
35 const char kTestWebSocketPort[] = "testWebSocketPort";
36 const char kSpawnedTestServerPort[] = "spawnedTestServer.port";
37
38 scoped_ptr<net::test_server::HttpResponse> HandleServerRedirectRequest(
39     const net::test_server::HttpRequest& request) {
40   if (!StartsWithASCII(request.relative_url, "/server-redirect?", true))
41     return scoped_ptr<net::test_server::HttpResponse>();
42
43   size_t query_string_pos = request.relative_url.find('?');
44   std::string redirect_target =
45       request.relative_url.substr(query_string_pos + 1);
46
47   scoped_ptr<net::test_server::BasicHttpResponse> http_response(
48       new net::test_server::BasicHttpResponse);
49   http_response->set_code(net::HTTP_MOVED_PERMANENTLY);
50   http_response->AddCustomHeader("Location", redirect_target);
51   return http_response.PassAs<net::test_server::HttpResponse>();
52 }
53
54 scoped_ptr<net::test_server::HttpResponse> HandleEchoHeaderRequest(
55     const net::test_server::HttpRequest& request) {
56   if (!StartsWithASCII(request.relative_url, "/echoheader?", true))
57     return scoped_ptr<net::test_server::HttpResponse>();
58
59   size_t query_string_pos = request.relative_url.find('?');
60   std::string header_name =
61       request.relative_url.substr(query_string_pos + 1);
62
63   std::string header_value;
64   std::map<std::string, std::string>::const_iterator it = request.headers.find(
65       header_name);
66   if (it != request.headers.end())
67     header_value = it->second;
68
69   scoped_ptr<net::test_server::BasicHttpResponse> http_response(
70       new net::test_server::BasicHttpResponse);
71   http_response->set_code(net::HTTP_OK);
72   http_response->set_content(header_value);
73   return http_response.PassAs<net::test_server::HttpResponse>();
74 }
75
76 scoped_ptr<net::test_server::HttpResponse> HandleSetCookieRequest(
77     const net::test_server::HttpRequest& request) {
78   if (!StartsWithASCII(request.relative_url, "/set-cookie?", true))
79     return scoped_ptr<net::test_server::HttpResponse>();
80
81   scoped_ptr<net::test_server::BasicHttpResponse> http_response(
82       new net::test_server::BasicHttpResponse);
83   http_response->set_code(net::HTTP_OK);
84
85   size_t query_string_pos = request.relative_url.find('?');
86   std::string cookie_value =
87       request.relative_url.substr(query_string_pos + 1);
88
89   std::vector<std::string> cookies;
90   base::SplitString(cookie_value, '&', &cookies);
91
92   for (size_t i = 0; i < cookies.size(); i++)
93     http_response->AddCustomHeader("Set-Cookie", cookies[i]);
94
95   return http_response.PassAs<net::test_server::HttpResponse>();
96 }
97
98 scoped_ptr<net::test_server::HttpResponse> HandleSetHeaderRequest(
99     const net::test_server::HttpRequest& request) {
100   if (!StartsWithASCII(request.relative_url, "/set-header?", true))
101     return scoped_ptr<net::test_server::HttpResponse>();
102
103   size_t query_string_pos = request.relative_url.find('?');
104   std::string escaped_header =
105       request.relative_url.substr(query_string_pos + 1);
106
107   std::string header =
108       net::UnescapeURLComponent(escaped_header,
109                                 net::UnescapeRule::NORMAL |
110                                 net::UnescapeRule::SPACES |
111                                 net::UnescapeRule::URL_SPECIAL_CHARS);
112
113   size_t colon_pos = header.find(':');
114   if (colon_pos == std::string::npos)
115     return scoped_ptr<net::test_server::HttpResponse>();
116
117   std::string header_name = header.substr(0, colon_pos);
118   // Skip space after colon.
119   std::string header_value = header.substr(colon_pos + 2);
120
121   scoped_ptr<net::test_server::BasicHttpResponse> http_response(
122       new net::test_server::BasicHttpResponse);
123   http_response->set_code(net::HTTP_OK);
124   http_response->AddCustomHeader(header_name, header_value);
125   return http_response.PassAs<net::test_server::HttpResponse>();
126 }
127
128 };  // namespace
129
130 ExtensionApiTest::ExtensionApiTest() {
131   embedded_test_server()->RegisterRequestHandler(
132       base::Bind(&HandleServerRedirectRequest));
133   embedded_test_server()->RegisterRequestHandler(
134       base::Bind(&HandleEchoHeaderRequest));
135   embedded_test_server()->RegisterRequestHandler(
136       base::Bind(&HandleSetCookieRequest));
137   embedded_test_server()->RegisterRequestHandler(
138       base::Bind(&HandleSetHeaderRequest));
139 }
140
141 ExtensionApiTest::~ExtensionApiTest() {}
142
143 ExtensionApiTest::ResultCatcher::ResultCatcher()
144     : profile_restriction_(NULL),
145       waiting_(false) {
146   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_TEST_PASSED,
147                  content::NotificationService::AllSources());
148   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_TEST_FAILED,
149                  content::NotificationService::AllSources());
150 }
151
152 ExtensionApiTest::ResultCatcher::~ResultCatcher() {
153 }
154
155 bool ExtensionApiTest::ResultCatcher::GetNextResult() {
156   // Depending on the tests, multiple results can come in from a single call
157   // to RunMessageLoop(), so we maintain a queue of results and just pull them
158   // off as the test calls this, going to the run loop only when the queue is
159   // empty.
160   if (results_.empty()) {
161     waiting_ = true;
162     content::RunMessageLoop();
163     waiting_ = false;
164   }
165
166   if (!results_.empty()) {
167     bool ret = results_.front();
168     results_.pop_front();
169     message_ = messages_.front();
170     messages_.pop_front();
171     return ret;
172   }
173
174   NOTREACHED();
175   return false;
176 }
177
178 void ExtensionApiTest::ResultCatcher::Observe(
179     int type, const content::NotificationSource& source,
180     const content::NotificationDetails& details) {
181   if (profile_restriction_ &&
182       content::Source<Profile>(source).ptr() != profile_restriction_) {
183     return;
184   }
185
186   switch (type) {
187     case chrome::NOTIFICATION_EXTENSION_TEST_PASSED:
188       VLOG(1) << "Got EXTENSION_TEST_PASSED notification.";
189       results_.push_back(true);
190       messages_.push_back(std::string());
191       if (waiting_)
192         base::MessageLoopForUI::current()->Quit();
193       break;
194
195     case chrome::NOTIFICATION_EXTENSION_TEST_FAILED:
196       VLOG(1) << "Got EXTENSION_TEST_FAILED notification.";
197       results_.push_back(false);
198       messages_.push_back(*(content::Details<std::string>(details).ptr()));
199       if (waiting_)
200         base::MessageLoopForUI::current()->Quit();
201       break;
202
203     default:
204       NOTREACHED();
205   }
206 }
207
208 void ExtensionApiTest::SetUpInProcessBrowserTestFixture() {
209   DCHECK(!test_config_.get()) << "Previous test did not clear config state.";
210   test_config_.reset(new base::DictionaryValue());
211   test_config_->SetString(kTestDataDirectory,
212                           net::FilePathToFileURL(test_data_dir_).spec());
213   test_config_->SetInteger(kTestWebSocketPort, 0);
214   extensions::TestGetConfigFunction::set_test_config_state(
215       test_config_.get());
216 }
217
218 void ExtensionApiTest::TearDownInProcessBrowserTestFixture() {
219   extensions::TestGetConfigFunction::set_test_config_state(NULL);
220   test_config_.reset(NULL);
221 }
222
223 bool ExtensionApiTest::RunExtensionTest(const std::string& extension_name) {
224   return RunExtensionTestImpl(
225       extension_name, std::string(), NULL, kFlagEnableFileAccess);
226 }
227
228 bool ExtensionApiTest::RunExtensionTestIncognito(
229     const std::string& extension_name) {
230   return RunExtensionTestImpl(extension_name,
231                               std::string(),
232                               NULL,
233                               kFlagEnableIncognito | kFlagEnableFileAccess);
234 }
235
236 bool ExtensionApiTest::RunExtensionTestIgnoreManifestWarnings(
237     const std::string& extension_name) {
238   return RunExtensionTestImpl(
239       extension_name, std::string(), NULL, kFlagIgnoreManifestWarnings);
240 }
241
242 bool ExtensionApiTest::RunExtensionTestAllowOldManifestVersion(
243     const std::string& extension_name) {
244   return RunExtensionTestImpl(
245       extension_name,
246       std::string(),
247       NULL,
248       kFlagEnableFileAccess | kFlagAllowOldManifestVersions);
249 }
250
251 bool ExtensionApiTest::RunComponentExtensionTest(
252     const std::string& extension_name) {
253   return RunExtensionTestImpl(extension_name,
254                               std::string(),
255                               NULL,
256                               kFlagEnableFileAccess | kFlagLoadAsComponent);
257 }
258
259 bool ExtensionApiTest::RunExtensionTestNoFileAccess(
260     const std::string& extension_name) {
261   return RunExtensionTestImpl(extension_name, std::string(), NULL, kFlagNone);
262 }
263
264 bool ExtensionApiTest::RunExtensionTestIncognitoNoFileAccess(
265     const std::string& extension_name) {
266   return RunExtensionTestImpl(
267       extension_name, std::string(), NULL, kFlagEnableIncognito);
268 }
269
270 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
271                                            const std::string& page_url) {
272   return RunExtensionSubtest(extension_name, page_url, kFlagEnableFileAccess);
273 }
274
275 bool ExtensionApiTest::RunExtensionSubtest(const std::string& extension_name,
276                                            const std::string& page_url,
277                                            int flags) {
278   DCHECK(!page_url.empty()) << "Argument page_url is required.";
279   // See http://crbug.com/177163 for details.
280 #if defined(OS_WIN) && !defined(NDEBUG)
281   LOG(WARNING) << "Workaround for 177163, prematurely returning";
282   return true;
283 #endif
284   return RunExtensionTestImpl(extension_name, page_url, NULL, flags);
285 }
286
287
288 bool ExtensionApiTest::RunPageTest(const std::string& page_url) {
289   return RunExtensionSubtest(std::string(), page_url);
290 }
291
292 bool ExtensionApiTest::RunPageTest(const std::string& page_url,
293                                    int flags) {
294   return RunExtensionSubtest(std::string(), page_url, flags);
295 }
296
297 bool ExtensionApiTest::RunPlatformAppTest(const std::string& extension_name) {
298   return RunExtensionTestImpl(
299       extension_name, std::string(), NULL, kFlagLaunchPlatformApp);
300 }
301
302 bool ExtensionApiTest::RunPlatformAppTestWithArg(
303     const std::string& extension_name, const char* custom_arg) {
304   return RunExtensionTestImpl(
305       extension_name, std::string(), custom_arg, kFlagLaunchPlatformApp);
306 }
307
308 bool ExtensionApiTest::RunPlatformAppTestWithFlags(
309     const std::string& extension_name, int flags) {
310   return RunExtensionTestImpl(
311       extension_name, std::string(), NULL, flags | kFlagLaunchPlatformApp);
312 }
313
314 // Load |extension_name| extension and/or |page_url| and wait for
315 // PASSED or FAILED notification.
316 bool ExtensionApiTest::RunExtensionTestImpl(const std::string& extension_name,
317                                             const std::string& page_url,
318                                             const char* custom_arg,
319                                             int flags) {
320   bool load_as_component = (flags & kFlagLoadAsComponent) != 0;
321   bool launch_platform_app = (flags & kFlagLaunchPlatformApp) != 0;
322   bool use_incognito = (flags & kFlagUseIncognito) != 0;
323
324   if (custom_arg && custom_arg[0])
325     test_config_->SetString(kTestCustomArg, custom_arg);
326
327   ResultCatcher catcher;
328   DCHECK(!extension_name.empty() || !page_url.empty()) <<
329       "extension_name and page_url cannot both be empty";
330
331   const extensions::Extension* extension = NULL;
332   if (!extension_name.empty()) {
333     base::FilePath extension_path = test_data_dir_.AppendASCII(extension_name);
334     if (load_as_component) {
335       extension = LoadExtensionAsComponent(extension_path);
336     } else {
337       int browser_test_flags = ExtensionBrowserTest::kFlagNone;
338       if (flags & kFlagEnableIncognito)
339         browser_test_flags |= ExtensionBrowserTest::kFlagEnableIncognito;
340       if (flags & kFlagEnableFileAccess)
341         browser_test_flags |= ExtensionBrowserTest::kFlagEnableFileAccess;
342       if (flags & kFlagIgnoreManifestWarnings)
343         browser_test_flags |= ExtensionBrowserTest::kFlagIgnoreManifestWarnings;
344       if (flags & kFlagAllowOldManifestVersions) {
345         browser_test_flags |=
346             ExtensionBrowserTest::kFlagAllowOldManifestVersions;
347       }
348       extension = LoadExtensionWithFlags(extension_path, browser_test_flags);
349     }
350     if (!extension) {
351       message_ = "Failed to load extension.";
352       return false;
353     }
354   }
355
356   // If there is a page_url to load, navigate it.
357   if (!page_url.empty()) {
358     GURL url = GURL(page_url);
359
360     // Note: We use is_valid() here in the expectation that the provided url
361     // may lack a scheme & host and thus be a relative url within the loaded
362     // extension.
363     if (!url.is_valid()) {
364       DCHECK(!extension_name.empty()) <<
365           "Relative page_url given with no extension_name";
366
367       url = extension->GetResourceURL(page_url);
368     }
369
370     if (use_incognito)
371       ui_test_utils::OpenURLOffTheRecord(browser()->profile(), url);
372     else
373       ui_test_utils::NavigateToURL(browser(), url);
374   } else if (launch_platform_app) {
375     AppLaunchParams params(browser()->profile(),
376                            extension,
377                            extensions::LAUNCH_CONTAINER_NONE,
378                            NEW_WINDOW);
379     params.command_line = *CommandLine::ForCurrentProcess();
380     OpenApplication(params);
381   }
382
383   if (!catcher.GetNextResult()) {
384     message_ = catcher.message();
385     return false;
386   }
387
388   return true;
389 }
390
391 // Test that exactly one extension is loaded, and return it.
392 const extensions::Extension* ExtensionApiTest::GetSingleLoadedExtension() {
393   ExtensionService* service = extensions::ExtensionSystem::Get(
394       browser()->profile())->extension_service();
395
396   const extensions::Extension* extension = NULL;
397   for (extensions::ExtensionSet::const_iterator it =
398            service->extensions()->begin();
399        it != service->extensions()->end(); ++it) {
400     // Ignore any component extensions. They are automatically loaded into all
401     // profiles and aren't the extension we're looking for here.
402     if ((*it)->location() == extensions::Manifest::COMPONENT)
403       continue;
404
405     if (extension != NULL) {
406       // TODO(yoz): this is misleading; it counts component extensions.
407       message_ = base::StringPrintf(
408           "Expected only one extension to be present.  Found %u.",
409           static_cast<unsigned>(service->extensions()->size()));
410       return NULL;
411     }
412
413     extension = it->get();
414   }
415
416   if (!extension) {
417     message_ = "extension pointer is NULL.";
418     return NULL;
419   }
420   return extension;
421 }
422
423 bool ExtensionApiTest::StartEmbeddedTestServer() {
424   if (!embedded_test_server()->InitializeAndWaitUntilReady())
425     return false;
426
427   // Build a dictionary of values that tests can use to build URLs that
428   // access the test server and local file system.  Tests can see these values
429   // using the extension API function chrome.test.getConfig().
430   test_config_->SetInteger(kTestServerPort,
431                            embedded_test_server()->port());
432
433   return true;
434 }
435
436 bool ExtensionApiTest::StartWebSocketServer(
437     const base::FilePath& root_directory) {
438   websocket_server_.reset(new net::SpawnedTestServer(
439       net::SpawnedTestServer::TYPE_WS,
440       net::SpawnedTestServer::kLocalhost,
441       root_directory));
442
443   if (!websocket_server_->Start())
444     return false;
445
446   test_config_->SetInteger(kTestWebSocketPort,
447                            websocket_server_->host_port_pair().port());
448
449   return true;
450 }
451
452 bool ExtensionApiTest::StartSpawnedTestServer() {
453   if (!test_server()->Start())
454     return false;
455
456   // Build a dictionary of values that tests can use to build URLs that
457   // access the test server and local file system.  Tests can see these values
458   // using the extension API function chrome.test.getConfig().
459   test_config_->SetInteger(kSpawnedTestServerPort,
460                            test_server()->host_port_pair().port());
461
462   return true;
463 }
464
465 void ExtensionApiTest::SetUpCommandLine(CommandLine* command_line) {
466   ExtensionBrowserTest::SetUpCommandLine(command_line);
467   test_data_dir_ = test_data_dir_.AppendASCII("api_test");
468 }