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