e2f0607f3f79ebeec0cec33b9fccc57fcc0c3268
[platform/framework/web/crosswalk.git] / src / extensions / browser / extension_protocols_unittest.cc
1 // Copyright 2014 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 <string>
6
7 #include "base/file_util.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "base/values.h"
12 #include "chrome/common/chrome_paths.h"
13 //#include "chrome/common/url_constants.h"
14 #include "content/public/browser/resource_request_info.h"
15 #include "content/public/test/mock_resource_context.h"
16 #include "content/public/test/test_browser_thread_bundle.h"
17 #include "extensions/browser/extension_protocols.h"
18 #include "extensions/browser/info_map.h"
19 #include "extensions/common/constants.h"
20 #include "extensions/common/extension.h"
21 #include "net/base/request_priority.h"
22 #include "net/url_request/url_request.h"
23 #include "net/url_request/url_request_job_factory_impl.h"
24 #include "net/url_request/url_request_status.h"
25 #include "net/url_request/url_request_test_util.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 using content::ResourceType;
29
30 namespace extensions {
31
32 scoped_refptr<Extension> CreateTestExtension(const std::string& name,
33                                              bool incognito_split_mode) {
34   base::DictionaryValue manifest;
35   manifest.SetString("name", name);
36   manifest.SetString("version", "1");
37   manifest.SetInteger("manifest_version", 2);
38   manifest.SetString("incognito", incognito_split_mode ? "split" : "spanning");
39
40   base::FilePath path;
41   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
42   path = path.AppendASCII("extensions").AppendASCII("response_headers");
43
44   std::string error;
45   scoped_refptr<Extension> extension(
46       Extension::Create(path, Manifest::INTERNAL, manifest,
47                         Extension::NO_FLAGS, &error));
48   EXPECT_TRUE(extension.get()) << error;
49   return extension;
50 }
51
52 scoped_refptr<Extension> CreateWebStoreExtension() {
53   base::DictionaryValue manifest;
54   manifest.SetString("name", "WebStore");
55   manifest.SetString("version", "1");
56   manifest.SetString("icons.16", "webstore_icon_16.png");
57
58   base::FilePath path;
59   EXPECT_TRUE(PathService::Get(chrome::DIR_RESOURCES, &path));
60   path = path.AppendASCII("web_store");
61
62   std::string error;
63   scoped_refptr<Extension> extension(
64       Extension::Create(path, Manifest::COMPONENT, manifest,
65                         Extension::NO_FLAGS, &error));
66   EXPECT_TRUE(extension.get()) << error;
67   return extension;
68 }
69
70 scoped_refptr<Extension> CreateTestResponseHeaderExtension() {
71   base::DictionaryValue manifest;
72   manifest.SetString("name", "An extension with web-accessible resources");
73   manifest.SetString("version", "2");
74
75   base::ListValue* web_accessible_list = new base::ListValue();
76   web_accessible_list->AppendString("test.dat");
77   manifest.Set("web_accessible_resources", web_accessible_list);
78
79   base::FilePath path;
80   EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &path));
81   path = path.AppendASCII("extensions").AppendASCII("response_headers");
82
83   std::string error;
84   scoped_refptr<Extension> extension(
85       Extension::Create(path, Manifest::UNPACKED, manifest,
86                         Extension::NO_FLAGS, &error));
87   EXPECT_TRUE(extension.get()) << error;
88   return extension;
89 }
90
91 class ExtensionProtocolTest : public testing::Test {
92  public:
93   ExtensionProtocolTest()
94       : thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
95         old_factory_(NULL),
96         resource_context_(&test_url_request_context_) {}
97
98   virtual void SetUp() OVERRIDE {
99     testing::Test::SetUp();
100     extension_info_map_ = new InfoMap();
101     net::URLRequestContext* request_context =
102         resource_context_.GetRequestContext();
103     old_factory_ = request_context->job_factory();
104   }
105
106   virtual void TearDown() {
107     net::URLRequestContext* request_context =
108         resource_context_.GetRequestContext();
109     request_context->set_job_factory(old_factory_);
110   }
111
112   void SetProtocolHandler(bool is_incognito) {
113     net::URLRequestContext* request_context =
114         resource_context_.GetRequestContext();
115     job_factory_.SetProtocolHandler(
116         kExtensionScheme,
117         CreateExtensionProtocolHandler(is_incognito,
118                                        extension_info_map_.get()));
119     request_context->set_job_factory(&job_factory_);
120   }
121
122   void StartRequest(net::URLRequest* request,
123                     ResourceType resource_type) {
124     content::ResourceRequestInfo::AllocateForTesting(request,
125                                                      resource_type,
126                                                      &resource_context_,
127                                                      -1,
128                                                      -1,
129                                                      -1,
130                                                      false);
131     request->Start();
132     base::MessageLoop::current()->Run();
133   }
134
135  protected:
136   content::TestBrowserThreadBundle thread_bundle_;
137   scoped_refptr<InfoMap> extension_info_map_;
138   net::URLRequestJobFactoryImpl job_factory_;
139   const net::URLRequestJobFactory* old_factory_;
140   net::TestDelegate test_delegate_;
141   net::TestURLRequestContext test_url_request_context_;
142   content::MockResourceContext resource_context_;
143 };
144
145 // Tests that making a chrome-extension request in an incognito context is
146 // only allowed under the right circumstances (if the extension is allowed
147 // in incognito, and it's either a non-main-frame request or a split-mode
148 // extension).
149 TEST_F(ExtensionProtocolTest, IncognitoRequest) {
150   // Register an incognito extension protocol handler.
151   SetProtocolHandler(true);
152
153   struct TestCase {
154     // Inputs.
155     std::string name;
156     bool incognito_split_mode;
157     bool incognito_enabled;
158
159     // Expected results.
160     bool should_allow_main_frame_load;
161     bool should_allow_sub_frame_load;
162   } cases[] = {
163     {"spanning disabled", false, false, false, false},
164     {"split disabled", true, false, false, false},
165     {"spanning enabled", false, true, false, true},
166     {"split enabled", true, true, true, true},
167   };
168
169   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
170     scoped_refptr<Extension> extension =
171         CreateTestExtension(cases[i].name, cases[i].incognito_split_mode);
172     extension_info_map_->AddExtension(
173         extension.get(), base::Time::Now(), cases[i].incognito_enabled, false);
174
175     // First test a main frame request.
176     {
177       // It doesn't matter that the resource doesn't exist. If the resource
178       // is blocked, we should see ADDRESS_UNREACHABLE. Otherwise, the request
179       // should just fail because the file doesn't exist.
180       net::URLRequest request(extension->GetResourceURL("404.html"),
181                               net::DEFAULT_PRIORITY,
182                               &test_delegate_,
183                               resource_context_.GetRequestContext());
184       StartRequest(&request, content::RESOURCE_TYPE_MAIN_FRAME);
185       EXPECT_EQ(net::URLRequestStatus::FAILED, request.status().status());
186
187       if (cases[i].should_allow_main_frame_load) {
188         EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request.status().error()) <<
189             cases[i].name;
190       } else {
191         EXPECT_EQ(net::ERR_ADDRESS_UNREACHABLE, request.status().error()) <<
192             cases[i].name;
193       }
194     }
195
196     // Now do a subframe request.
197     {
198       net::URLRequest request(extension->GetResourceURL("404.html"),
199                               net::DEFAULT_PRIORITY,
200                               &test_delegate_,
201                               resource_context_.GetRequestContext());
202       StartRequest(&request, content::RESOURCE_TYPE_SUB_FRAME);
203       EXPECT_EQ(net::URLRequestStatus::FAILED, request.status().status());
204
205       if (cases[i].should_allow_sub_frame_load) {
206         EXPECT_EQ(net::ERR_FILE_NOT_FOUND, request.status().error()) <<
207             cases[i].name;
208       } else {
209         EXPECT_EQ(net::ERR_ADDRESS_UNREACHABLE, request.status().error()) <<
210             cases[i].name;
211       }
212     }
213   }
214 }
215
216 void CheckForContentLengthHeader(net::URLRequest* request) {
217   std::string content_length;
218   request->GetResponseHeaderByName(net::HttpRequestHeaders::kContentLength,
219                                   &content_length);
220   EXPECT_FALSE(content_length.empty());
221   int length_value = 0;
222   EXPECT_TRUE(base::StringToInt(content_length, &length_value));
223   EXPECT_GT(length_value, 0);
224 }
225
226 // Tests getting a resource for a component extension works correctly, both when
227 // the extension is enabled and when it is disabled.
228 TEST_F(ExtensionProtocolTest, ComponentResourceRequest) {
229   // Register a non-incognito extension protocol handler.
230   SetProtocolHandler(false);
231
232   scoped_refptr<Extension> extension = CreateWebStoreExtension();
233   extension_info_map_->AddExtension(extension.get(),
234                                     base::Time::Now(),
235                                     false,
236                                     false);
237
238   // First test it with the extension enabled.
239   {
240     net::URLRequest request(extension->GetResourceURL("webstore_icon_16.png"),
241                             net::DEFAULT_PRIORITY,
242                             &test_delegate_,
243                             resource_context_.GetRequestContext());
244     StartRequest(&request, content::RESOURCE_TYPE_MEDIA);
245     EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status());
246     CheckForContentLengthHeader(&request);
247   }
248
249   // And then test it with the extension disabled.
250   extension_info_map_->RemoveExtension(extension->id(),
251                                        UnloadedExtensionInfo::REASON_DISABLE);
252   {
253     net::URLRequest request(extension->GetResourceURL("webstore_icon_16.png"),
254                             net::DEFAULT_PRIORITY,
255                             &test_delegate_,
256                             resource_context_.GetRequestContext());
257     StartRequest(&request, content::RESOURCE_TYPE_MEDIA);
258     EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status());
259     CheckForContentLengthHeader(&request);
260   }
261 }
262
263 // Tests that a URL request for resource from an extension returns a few
264 // expected response headers.
265 TEST_F(ExtensionProtocolTest, ResourceRequestResponseHeaders) {
266   // Register a non-incognito extension protocol handler.
267   SetProtocolHandler(false);
268
269   scoped_refptr<Extension> extension = CreateTestResponseHeaderExtension();
270   extension_info_map_->AddExtension(extension.get(),
271                                     base::Time::Now(),
272                                     false,
273                                     false);
274
275   {
276     net::URLRequest request(extension->GetResourceURL("test.dat"),
277                             net::DEFAULT_PRIORITY,
278                             &test_delegate_,
279                             resource_context_.GetRequestContext());
280     StartRequest(&request, content::RESOURCE_TYPE_MEDIA);
281     EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status());
282
283     // Check that cache-related headers are set.
284     std::string etag;
285     request.GetResponseHeaderByName("ETag", &etag);
286     EXPECT_TRUE(StartsWithASCII(etag, "\"", false));
287     EXPECT_TRUE(EndsWith(etag, "\"", false));
288
289     std::string revalidation_header;
290     request.GetResponseHeaderByName("cache-control", &revalidation_header);
291     EXPECT_EQ("no-cache", revalidation_header);
292
293     // We set test.dat as web-accessible, so it should have a CORS header.
294     std::string access_control;
295     request.GetResponseHeaderByName("Access-Control-Allow-Origin",
296                                     &access_control);
297     EXPECT_EQ("*", access_control);
298   }
299 }
300
301 // Tests that a URL request for main frame or subframe from an extension
302 // succeeds, but subresources fail. See http://crbug.com/312269.
303 TEST_F(ExtensionProtocolTest, AllowFrameRequests) {
304   // Register a non-incognito extension protocol handler.
305   SetProtocolHandler(false);
306
307   scoped_refptr<Extension> extension = CreateTestExtension("foo", false);
308   extension_info_map_->AddExtension(extension.get(),
309                                     base::Time::Now(),
310                                     false,
311                                     false);
312
313   // All MAIN_FRAME and SUB_FRAME requests should succeed.
314   {
315     net::URLRequest request(extension->GetResourceURL("test.dat"),
316                             net::DEFAULT_PRIORITY,
317                             &test_delegate_,
318                             resource_context_.GetRequestContext());
319     StartRequest(&request, content::RESOURCE_TYPE_MAIN_FRAME);
320     EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status());
321   }
322   {
323     net::URLRequest request(extension->GetResourceURL("test.dat"),
324                             net::DEFAULT_PRIORITY,
325                             &test_delegate_,
326                             resource_context_.GetRequestContext());
327     StartRequest(&request, content::RESOURCE_TYPE_SUB_FRAME);
328     EXPECT_EQ(net::URLRequestStatus::SUCCESS, request.status().status());
329   }
330
331   // And subresource types, such as media, should fail.
332   {
333     net::URLRequest request(extension->GetResourceURL("test.dat"),
334                             net::DEFAULT_PRIORITY,
335                             &test_delegate_,
336                             resource_context_.GetRequestContext());
337     StartRequest(&request, content::RESOURCE_TYPE_MEDIA);
338     EXPECT_EQ(net::URLRequestStatus::FAILED, request.status().status());
339   }
340 }
341
342 }  // namespace extensions