Upstream version 7.35.139.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/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
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 void ReadResourceFilePath(
89     const ApplicationResource& resource,
90     base::FilePath* file_path) {
91   *file_path = resource.GetFilePath();
92 }
93
94 class URLRequestApplicationJob : public net::URLRequestFileJob {
95  public:
96   URLRequestApplicationJob(
97       net::URLRequest* request,
98       net::NetworkDelegate* network_delegate,
99       const scoped_refptr<base::TaskRunner>& file_task_runner,
100       const std::string& application_id,
101       const base::FilePath& directory_path,
102       const base::FilePath& relative_path,
103       const std::string& content_security_policy,
104       const std::list<std::string>& locales,
105       bool is_authority_match)
106       : net::URLRequestFileJob(
107           request, network_delegate, base::FilePath(), file_task_runner),
108         relative_path_(relative_path),
109         content_security_policy_(content_security_policy),
110         is_authority_match_(is_authority_match),
111         resource_(application_id, directory_path, relative_path),
112         locales_(locales),
113         weak_factory_(this) {
114   }
115
116   virtual void GetResponseInfo(net::HttpResponseInfo* info) OVERRIDE {
117     std::string mime_type;
118     GetMimeType(&mime_type);
119     std::string method = request()->method();
120     response_info_.headers = BuildHttpHeaders(
121         content_security_policy_, mime_type, method, file_path_,
122         relative_path_, is_authority_match_);
123     *info = response_info_;
124   }
125
126   virtual void Start() OVERRIDE {
127     base::FilePath* read_file_path = new base::FilePath;
128
129     resource_.SetLocales(locales_);
130     bool posted = base::WorkerPool::PostTaskAndReply(
131         FROM_HERE,
132         base::Bind(&ReadResourceFilePath, resource_,
133                    base::Unretained(read_file_path)),
134         base::Bind(&URLRequestApplicationJob::OnFilePathRead,
135                    weak_factory_.GetWeakPtr(),
136                    base::Owned(read_file_path)),
137         true /* task is slow */);
138     DCHECK(posted);
139   }
140
141  private:
142   virtual ~URLRequestApplicationJob() {}
143
144   void OnFilePathRead(base::FilePath* read_file_path) {
145     file_path_ = *read_file_path;
146     if (file_path_.empty())
147       NotifyHeadersComplete();
148     else
149       URLRequestFileJob::Start();
150   }
151
152   net::HttpResponseInfo response_info_;
153   base::FilePath relative_path_;
154   std::string content_security_policy_;
155   bool is_authority_match_;
156   ApplicationResource resource_;
157   std::list<std::string> locales_;
158   base::WeakPtrFactory<URLRequestApplicationJob> weak_factory_;
159 };
160
161 // This class is a thread-safe cache of active application's data.
162 // This class is used by ApplicationProtocolHandler as it lives on IO thread
163 // and hence cannot access ApplicationService directly.
164 class ApplicationDataCache : public ApplicationService::Observer {
165  public:
166   scoped_refptr<ApplicationData> GetApplicationData(
167       const std::string& application_id) const {
168     base::AutoLock lock(lock_);
169     ApplicationData::ApplicationDataMap::const_iterator it =
170         cache_.find(application_id);
171     if (it != cache_.end()) {
172       return it->second;
173     }
174     return NULL;
175   }
176
177   virtual void DidLaunchApplication(Application* app) OVERRIDE {
178     base::AutoLock lock(lock_);
179     cache_.insert(std::pair<std::string, scoped_refptr<ApplicationData> >(
180         app->id(), app->data()));
181   }
182
183   virtual void WillDestroyApplication(Application* app) OVERRIDE {
184     base::AutoLock lock(lock_);
185     cache_.erase(app->id());
186   }
187
188  private:
189   ApplicationData::ApplicationDataMap cache_;
190   mutable base::Lock lock_;
191 };
192
193 class ApplicationProtocolHandler
194     : public net::URLRequestJobFactory::ProtocolHandler {
195  public:
196   explicit ApplicationProtocolHandler(ApplicationService* service) {
197     DCHECK(service);
198     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
199     // ApplicationProtocolHandler lives longer than ApplicationService,
200     // so we do not need to remove cache_ from ApplicationService
201     // observers list.
202     service->AddObserver(&cache_);
203   }
204
205   virtual ~ApplicationProtocolHandler() {}
206
207   virtual net::URLRequestJob* MaybeCreateJob(
208       net::URLRequest* request,
209       net::NetworkDelegate* network_delegate) const OVERRIDE;
210
211  private:
212   ApplicationDataCache cache_;
213   DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
214 };
215
216 // The |locale| should be expanded to user agent locale.
217 // Such as, "en-us" will be expaned as "en-us, en".
218 void GetUserAgentLocales(const std::string& sys_locale,
219                          std::list<std::string>& ua_locales) {
220   if (sys_locale.empty())
221     return;
222
223   std::string locale = StringToLowerASCII(sys_locale);
224   size_t position;
225   do {
226     ua_locales.push_back(locale);
227     position = locale.find_last_of("-");
228     locale = locale.substr(0, position);
229   } while (position != std::string::npos);
230 }
231
232 net::URLRequestJob*
233 ApplicationProtocolHandler::MaybeCreateJob(
234     net::URLRequest* request, net::NetworkDelegate* network_delegate) const {
235   const std::string& application_id = request->url().host();
236   scoped_refptr<ApplicationData> application =
237       cache_.GetApplicationData(application_id);
238   base::FilePath relative_path =
239       ApplicationURLToRelativeFilePath(request->url());
240   base::FilePath directory_path;
241   std::string content_security_policy;
242   if (application) {
243     directory_path = application->Path();
244
245     const char* csp_key = GetCSPKey(application->GetPackageType());
246     const CSPInfo* csp_info = static_cast<CSPInfo*>(
247           application->GetManifestData(csp_key));
248     if (csp_info) {
249       const std::map<std::string, std::vector<std::string> >& policies =
250           csp_info->GetDirectives();
251       std::map<std::string, std::vector<std::string> >::const_iterator it =
252           policies.begin();
253       for (; it != policies.end(); ++it) {
254         content_security_policy.append(
255             it->first + ' ' + JoinString(it->second, ' ') + ';');
256       }
257     }
258   }
259
260   const std::string& path = request->url().path();
261
262   std::list<std::string> locales;
263   if (application && application->GetPackageType() == Manifest::TYPE_WGT) {
264     GetUserAgentLocales(
265         xwalk::XWalkRunner::GetInstance()->GetLocale(), locales);
266     GetUserAgentLocales(application->GetManifest()->default_locale(), locales);
267   }
268
269   return new URLRequestApplicationJob(
270       request,
271       network_delegate,
272       content::BrowserThread::GetBlockingPool()->
273       GetTaskRunnerWithShutdownBehavior(
274           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
275       application_id,
276       directory_path,
277       relative_path,
278       content_security_policy,
279       locales,
280       application);
281 }
282
283 }  // namespace
284
285 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
286 CreateApplicationProtocolHandler(ApplicationService* service) {
287   return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
288       new ApplicationProtocolHandler(service));
289 }
290
291 }  // namespace application
292 }  // namespace xwalk