f96f844152913df0e4e8944544a389c037c7172e
[platform/framework/web/crosswalk.git] / src / xwalk / application / browser / application_protocols.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 "xwalk/application/browser/application_protocols.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <list>
10 #include <string>
11 #include <utility>
12 #include <vector>
13
14 #include "base/files/file_path.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/string_util.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "base/threading/worker_pool.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/resource_request_info.h"
23 #include "url/url_util.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_response_headers.h"
26 #include "net/http/http_response_info.h"
27 #include "net/url_request/url_request_error_job.h"
28 #include "net/url_request/url_request_file_job.h"
29 #include "net/url_request/url_request_simple_job.h"
30 #include "xwalk/runtime/browser/xwalk_runner.h"
31 #include "xwalk/application/browser/application_service.h"
32 #include "xwalk/application/common/application_data.h"
33 #include "xwalk/application/common/application_file_util.h"
34 #include "xwalk/application/common/application_manifest_constants.h"
35 #include "xwalk/application/common/application_resource.h"
36 #include "xwalk/application/common/constants.h"
37 #include "xwalk/application/common/manifest_handlers/csp_handler.h"
38 #include "xwalk/application/common/manifest_handlers/main_document_handler.h"
39
40 using content::BrowserThread;
41 using content::ResourceRequestInfo;
42
43 namespace xwalk {
44
45 namespace keys = application_manifest_keys;
46
47 namespace application {
48
49 namespace {
50
51 net::HttpResponseHeaders* BuildHttpHeaders(
52     const std::string& content_security_policy,
53     const std::string& mime_type, const std::string& method,
54     const base::FilePath& file_path, const base::FilePath& relative_path,
55     bool is_authority_match) {
56   std::string raw_headers;
57   if (method == "GET") {
58     if (relative_path.empty())
59       raw_headers.append("HTTP/1.1 400 Bad Request");
60     else if (!is_authority_match)
61       raw_headers.append("HTTP/1.1 403 Forbidden");
62     else if (file_path.empty())
63       raw_headers.append("HTTP/1.1 404 Not Found");
64     else
65       raw_headers.append("HTTP/1.1 200 OK");
66   } else {
67     raw_headers.append("HTTP/1.1 501 Not Implemented");
68   }
69
70   if (!content_security_policy.empty()) {
71     raw_headers.append(1, '\0');
72     raw_headers.append("Content-Security-Policy: ");
73     raw_headers.append(content_security_policy);
74   }
75
76   raw_headers.append(1, '\0');
77   raw_headers.append("Access-Control-Allow-Origin: *");
78
79   if (!mime_type.empty()) {
80     raw_headers.append(1, '\0');
81     raw_headers.append("Content-Type: ");
82     raw_headers.append(mime_type);
83   }
84
85   raw_headers.append(2, '\0');
86   return new net::HttpResponseHeaders(raw_headers);
87 }
88
89 class GeneratedMainDocumentJob: public net::URLRequestSimpleJob {
90  public:
91   GeneratedMainDocumentJob(
92       net::URLRequest* request,
93       net::NetworkDelegate* network_delegate,
94       const base::FilePath& relative_path,
95       const scoped_refptr<const ApplicationData> application,
96       const std::string& content_security_policy)
97     : net::URLRequestSimpleJob(request, network_delegate),
98       application_(application),
99       mime_type_("text/html"),
100       relative_path_(relative_path),
101       content_security_policy_(content_security_policy) {
102   }
103
104   // Overridden from URLRequestSimpleJob:
105   virtual int GetData(std::string* mime_type,
106                       std::string* charset,
107                       std::string* data,
108                       const net::CompletionCallback& callback) const OVERRIDE {
109     *mime_type = mime_type_;
110     *charset = "utf-8";
111     *data = "<!DOCTYPE html>\n<body>\n";
112
113     MainDocumentInfo* main_info = xwalk::application::ToMainDocumentInfo(
114         application_->GetManifestData(keys::kAppMainKey));
115     const std::vector<std::string>& main_scripts = main_info->GetMainScripts();
116     for (size_t i = 0; i < main_scripts.size(); ++i) {
117       *data += "<script src=\"";
118       *data += main_scripts[i];
119       *data += "\"></script>\n";
120     }
121     return net::OK;
122   }
123
124   virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
125     response_info_.headers = BuildHttpHeaders(content_security_policy_,
126                                               mime_type_, "GET", relative_path_,
127                                               relative_path_, true);
128     *info = response_info_;
129   }
130
131  private:
132   virtual ~GeneratedMainDocumentJob() {}
133
134   scoped_refptr<const ApplicationData> application_;
135   const std::string mime_type_;
136   const base::FilePath relative_path_;
137   net::HttpResponseInfo response_info_;
138   std::string content_security_policy_;
139 };
140
141 void ReadResourceFilePath(
142     const ApplicationResource& resource,
143     base::FilePath* file_path) {
144   *file_path = resource.GetFilePath();
145 }
146
147 class URLRequestApplicationJob : public net::URLRequestFileJob {
148  public:
149   URLRequestApplicationJob(
150       net::URLRequest* request,
151       net::NetworkDelegate* network_delegate,
152       const scoped_refptr<base::TaskRunner>& file_task_runner,
153       const std::string& application_id,
154       const base::FilePath& directory_path,
155       const base::FilePath& relative_path,
156       const std::string& content_security_policy,
157       const std::list<std::string>& locales,
158       bool is_authority_match)
159       : net::URLRequestFileJob(
160           request, network_delegate, base::FilePath(), file_task_runner),
161         relative_path_(relative_path),
162         content_security_policy_(content_security_policy),
163         is_authority_match_(is_authority_match),
164         resource_(application_id, directory_path, relative_path),
165         locales_(locales),
166         weak_factory_(this) {
167   }
168
169   virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
170     std::string mime_type;
171     GetMimeType(&mime_type);
172     std::string method = request()->method();
173     response_info_.headers = BuildHttpHeaders(
174         content_security_policy_, mime_type, method, file_path_,
175         relative_path_, is_authority_match_);
176     *info = response_info_;
177   }
178
179   virtual void Start() OVERRIDE {
180     base::FilePath* read_file_path = new base::FilePath;
181
182     resource_.SetLocales(locales_);
183     bool posted = base::WorkerPool::PostTaskAndReply(
184         FROM_HERE,
185         base::Bind(&ReadResourceFilePath, resource_,
186                    base::Unretained(read_file_path)),
187         base::Bind(&URLRequestApplicationJob::OnFilePathRead,
188                    weak_factory_.GetWeakPtr(),
189                    base::Owned(read_file_path)),
190         true /* task is slow */);
191     DCHECK(posted);
192   }
193
194  private:
195   virtual ~URLRequestApplicationJob() {}
196
197   void OnFilePathRead(base::FilePath* read_file_path) {
198     file_path_ = *read_file_path;
199     if (file_path_.empty())
200       NotifyHeadersComplete();
201     else
202       URLRequestFileJob::Start();
203   }
204
205   net::HttpResponseInfo response_info_;
206   base::FilePath relative_path_;
207   std::string content_security_policy_;
208   bool is_authority_match_;
209   ApplicationResource resource_;
210   std::list<std::string> locales_;
211   base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
212 };
213
214 // This class is a thread-safe cache of active application's data.
215 // This class is used by ApplicationProtocolHandler as it lives on IO thread
216 // and hence cannot access ApplicationService directly.
217 class ApplicationDataCache : public ApplicationService::Observer {
218  public:
219   scoped_refptr<ApplicationData> GetApplicationData(
220       const std::string& application_id) const {
221     base::AutoLock lock(lock_);
222     ApplicationData::ApplicationDataMap::const_iterator it =
223         cache_.find(application_id);
224     if (it != cache_.end()) {
225       return it->second;
226     }
227     return NULL;
228   }
229
230   virtual void DidLaunchApplication(Application* app) OVERRIDE {
231     base::AutoLock lock(lock_);
232     cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
233         app->id(), app->data()));
234   }
235
236   virtual void WillDestroyApplication(Application* app) OVERRIDE {
237     base::AutoLock lock(lock_);
238     cache_.erase(app->id());
239   }
240
241  private:
242   ApplicationData::ApplicationDataMap cache_;
243   mutable base::Lock lock_;
244 };
245
246 class ApplicationProtocolHandler
247     : public net::URLRequestJobFactory::ProtocolHandler {
248  public:
249   explicit ApplicationProtocolHandler(ApplicationService* service) {
250     DCHECK(service);
251     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252     // ApplicationProtocolHandler lives longer than ApplicationService,
253     // so we do not need to remove cache_ from ApplicationService
254     // observers list.
255     service->AddObserver(&cache_);
256   }
257
258   virtual ~ApplicationProtocolHandler() {}
259
260   virtual net::URLRequestJob* MaybeCreateJob(
261       net::URLRequest* request,
262       net::NetworkDelegate* network_delegate) const OVERRIDE;
263
264  private:
265   ApplicationDataCache cache_;
266   DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
267 };
268
269 // The |locale| should be expanded to user agent locale.
270 // Such as, "en-us" will be expaned as "en-us, en".
271 void GetUserAgentLocales(const std::string& sys_locale,
272                          std::list<std::string>& ua_locales) {
273   if (sys_locale.empty())
274     return;
275
276   std::string locale = StringToLowerASCII(sys_locale);
277   size_t position;
278   do {
279     ua_locales.push_back(locale);
280     position = locale.find_last_of("-");
281     locale = locale.substr(0, position);
282   } while (position != std::string::npos);
283 }
284
285 net::URLRequestJob*
286 ApplicationProtocolHandler::MaybeCreateJob(
287     net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
288   const std::string& application_id = request->url().host();
289   scoped_refptr<ApplicationData> application =
290       cache_.GetApplicationData(application_id);
291   base::FilePath relative_path =
292       ApplicationURLToRelativeFilePath(request->url());
293   base::FilePath directory_path;
294   std::string content_security_policy;
295   if (application) {
296     directory_path = application->Path();
297
298     const char* csp_key = GetCSPKey(application->GetPackageType());
299     const CSPInfo* csp_info = static_cast<CSPInfo*>(
300           application->GetManifestData(csp_key));
301     if (csp_info) {
302       const std::map<std::string, std::vector<std::string> >& policies =
303           csp_info->GetDirectives();
304       std::map<std::string, std::vector<std::string> >::const_iterator it =
305           policies.begin();
306       for (; it != policies.end(); ++it) {
307         content_security_policy.append(
308             it->first + ' ' + JoinString(it->second, ' ') + ';');
309       }
310     }
311   }
312
313   const std::string& path = request->url().path();
314   if (application &&
315       path.size() > 1 &&
316       path.substr(1) == kGeneratedMainDocumentFilename) {
317     return new GeneratedMainDocumentJob(request, network_delegate,
318                                         relative_path, application,
319                                         content_security_policy);
320   }
321
322   std::list<std::string> locales;
323   if (application && application->GetPackageType() == Manifest::TYPE_WGT) {
324     GetUserAgentLocales(
325         xwalk::XWalkRunner::GetInstance()->GetLocale(), locales);
326     GetUserAgentLocales(application->GetManifest()->default_locale(), locales);
327   }
328
329   return new URLRequestApplicationJob(
330       request,
331       network_delegate,
332       content::BrowserThread::GetBlockingPool()->
333       GetTaskRunnerWithShutdownBehavior(
334           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
335       application_id,
336       directory_path,
337       relative_path,
338       content_security_policy,
339       locales,
340       application);
341 }
342
343 }  // namespace
344
345 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
346 CreateApplicationProtocolHandler(ApplicationService* service) {
347   return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
348       new ApplicationProtocolHandler(service));
349 }
350
351 }  // namespace application
352 }  // namespace xwalk