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.
5 #include "xwalk/application/browser/application_protocols.h"
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"
40 using content::BrowserThread;
41 using content::ResourceRequestInfo;
45 namespace keys = application_manifest_keys;
47 namespace application {
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");
65 raw_headers.append("HTTP/1.1 200 OK");
67 raw_headers.append("HTTP/1.1 501 Not Implemented");
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);
76 raw_headers.append(1, '\0');
77 raw_headers.append("Access-Control-Allow-Origin: *");
79 if (!mime_type.empty()) {
80 raw_headers.append(1, '\0');
81 raw_headers.append("Content-Type: ");
82 raw_headers.append(mime_type);
85 raw_headers.append(2, '\0');
86 return new net::HttpResponseHeaders(raw_headers);
89 class GeneratedMainDocumentJob: public net::URLRequestSimpleJob {
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) {
104 // Overridden from URLRequestSimpleJob:
105 virtual int GetData(std::string* mime_type,
106 std::string* charset,
108 const net::CompletionCallback& callback) const OVERRIDE {
109 *mime_type = mime_type_;
111 *data = "<!DOCTYPE html>\n<body>\n";
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";
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_;
132 virtual ~GeneratedMainDocumentJob() {}
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_;
141 void ReadResourceFilePath(
142 const ApplicationResource& resource,
143 base::FilePath* file_path) {
144 *file_path = resource.GetFilePath();
147 class URLRequestApplicationJob : public net::URLRequestFileJob {
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),
166 weak_factory_(this) {
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_;
179 virtual void Start() OVERRIDE {
180 base::FilePath* read_file_path = new base::FilePath;
182 resource_.SetLocales(locales_);
183 bool posted = base::WorkerPool::PostTaskAndReply(
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 */);
195 virtual ~URLRequestApplicationJob() {}
197 void OnFilePathRead(base::FilePath* read_file_path) {
198 file_path_ = *read_file_path;
199 if (file_path_.empty())
200 NotifyHeadersComplete();
202 URLRequestFileJob::Start();
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_;
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 {
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()) {
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()));
236 virtual void WillDestroyApplication(Application* app) OVERRIDE {
237 base::AutoLock lock(lock_);
238 cache_.erase(app->id());
242 ApplicationData::ApplicationDataMap cache_;
243 mutable base::Lock lock_;
246 class ApplicationProtocolHandler
247 : public net::URLRequestJobFactory::ProtocolHandler {
249 explicit ApplicationProtocolHandler(ApplicationService* service) {
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
252 // ApplicationProtocolHandler lives longer than ApplicationService,
253 // so we do not need to remove cache_ from ApplicationService
255 service->AddObserver(&cache_);
258 virtual ~ApplicationProtocolHandler() {}
260 virtual net::URLRequestJob* MaybeCreateJob(
261 net::URLRequest* request,
262 net::NetworkDelegate* network_delegate) const OVERRIDE;
265 ApplicationDataCache cache_;
266 DISALLOW_COPY_AND_ASSIGN(ApplicationProtocolHandler);
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())
276 std::string locale = StringToLowerASCII(sys_locale);
279 ua_locales.push_back(locale);
280 position = locale.find_last_of("-");
281 locale = locale.substr(0, position);
282 } while (position != std::string::npos);
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;
296 directory_path = application->Path();
298 const char* csp_key = GetCSPKey(application->GetPackageType());
299 const CSPInfo* csp_info = static_cast<CSPInfo*>(
300 application->GetManifestData(csp_key));
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 =
306 for (; it != policies.end(); ++it) {
307 content_security_policy.append(
308 it->first + ' ' + JoinString(it->second, ' ') + ';');
313 const std::string& path = request->url().path();
316 path.substr(1) == kGeneratedMainDocumentFilename) {
317 return new GeneratedMainDocumentJob(request, network_delegate,
318 relative_path, application,
319 content_security_policy);
322 std::list<std::string> locales;
323 if (application && application->GetPackageType() == Manifest::TYPE_WGT) {
325 xwalk::XWalkRunner::GetInstance()->GetLocale(), locales);
326 GetUserAgentLocales(application->GetManifest()->default_locale(), locales);
329 return new URLRequestApplicationJob(
332 content::BrowserThread::GetBlockingPool()->
333 GetTaskRunnerWithShutdownBehavior(
334 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN),
338 content_security_policy,
345 linked_ptr<net::URLRequestJobFactory::ProtocolHandler>
346 CreateApplicationProtocolHandler(ApplicationService* service) {
347 return linked_ptr<net::URLRequestJobFactory::ProtocolHandler>(
348 new ApplicationProtocolHandler(service));
351 } // namespace application