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