OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "mojo/shell/dynamic_application_loader.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/command_line.h" | |
9 #include "base/files/file_path.h" | |
10 #include "base/files/file_util.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/memory/weak_ptr.h" | |
13 #include "base/message_loop/message_loop.h" | |
14 #include "base/strings/utf_string_conversions.h" | |
15 #include "mojo/common/common_type_converters.h" | |
16 #include "mojo/common/data_pipe_utils.h" | |
17 #include "mojo/services/public/interfaces/network/url_loader.mojom.h" | |
18 #include "mojo/shell/context.h" | |
19 #include "mojo/shell/filename_util.h" | |
20 #include "mojo/shell/switches.h" | |
21 #include "url/url_util.h" | |
22 | |
23 namespace mojo { | |
24 namespace shell { | |
25 | |
26 // Encapsulates loading and running one individual application. | |
27 // | |
28 // Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must | |
29 // ensure that all the parameters passed to Loader subclasses stay valid through | |
30 // Loader's lifetime. | |
31 // | |
32 // Async operations are done with WeakPtr to protect against | |
33 // DynamicApplicationLoader going away (and taking all the Loaders with it) | |
34 // while the async operation is outstanding. | |
35 class DynamicApplicationLoader::Loader { | |
36 public: | |
37 Loader(Context* context, | |
38 DynamicServiceRunnerFactory* runner_factory, | |
39 scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, | |
40 const LoaderCompleteCallback& loader_complete_callback) | |
41 : load_callbacks_(load_callbacks), | |
42 loader_complete_callback_(loader_complete_callback), | |
43 context_(context), | |
44 runner_factory_(runner_factory), | |
45 weak_ptr_factory_(this) {} | |
46 | |
47 virtual ~Loader() {} | |
48 | |
49 protected: | |
50 void RunLibrary(const base::FilePath& path, bool path_exists) { | |
51 ScopedMessagePipeHandle shell_handle = | |
52 load_callbacks_->RegisterApplication(); | |
53 if (!shell_handle.is_valid()) { | |
54 LoaderComplete(); | |
55 return; | |
56 } | |
57 | |
58 if (!path_exists) { | |
59 DVLOG(1) << "Library not started because library path '" << path.value() | |
60 << "' does not exist."; | |
61 LoaderComplete(); | |
62 return; | |
63 } | |
64 | |
65 runner_ = runner_factory_->Create(context_); | |
66 runner_->Start( | |
67 path, | |
68 shell_handle.Pass(), | |
69 base::Bind(&Loader::LoaderComplete, weak_ptr_factory_.GetWeakPtr())); | |
70 } | |
71 | |
72 void LoaderComplete() { loader_complete_callback_.Run(this); } | |
73 | |
74 scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks_; | |
75 LoaderCompleteCallback loader_complete_callback_; | |
76 Context* context_; | |
77 | |
78 private: | |
79 DynamicServiceRunnerFactory* runner_factory_; | |
80 scoped_ptr<DynamicServiceRunner> runner_; | |
81 base::WeakPtrFactory<Loader> weak_ptr_factory_; | |
82 }; | |
83 | |
84 // A loader for local files. | |
85 class DynamicApplicationLoader::LocalLoader : public Loader { | |
86 public: | |
87 LocalLoader(const GURL& url, | |
88 Context* context, | |
89 DynamicServiceRunnerFactory* runner_factory, | |
90 scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, | |
91 const LoaderCompleteCallback& loader_complete_callback) | |
92 : Loader(context, | |
93 runner_factory, | |
94 load_callbacks, | |
95 loader_complete_callback), | |
96 weak_ptr_factory_(this) { | |
97 DCHECK(url.SchemeIsFile()); | |
98 url::RawCanonOutputW<1024> output; | |
99 url::DecodeURLEscapeSequences( | |
100 url.path().data(), static_cast<int>(url.path().length()), &output); | |
101 base::string16 decoded_path = | |
102 base::string16(output.data(), output.length()); | |
103 #if defined(OS_WIN) | |
104 base::FilePath path(decoded_path); | |
105 #else | |
106 base::FilePath path(base::UTF16ToUTF8(decoded_path)); | |
107 #endif | |
108 | |
109 // Async for consistency with network case. | |
110 base::MessageLoop::current()->PostTask( | |
111 FROM_HERE, | |
112 base::Bind(&LocalLoader::RunLibrary, | |
113 weak_ptr_factory_.GetWeakPtr(), | |
114 path, | |
115 base::PathExists(path))); | |
116 } | |
117 | |
118 ~LocalLoader() override {} | |
119 | |
120 private: | |
121 base::WeakPtrFactory<LocalLoader> weak_ptr_factory_; | |
122 }; | |
123 | |
124 // A loader for network files. | |
125 class DynamicApplicationLoader::NetworkLoader : public Loader { | |
126 public: | |
127 NetworkLoader(const GURL& url, | |
128 MimeTypeToURLMap* mime_type_to_url, | |
129 Context* context, | |
130 DynamicServiceRunnerFactory* runner_factory, | |
131 NetworkService* network_service, | |
132 scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, | |
133 const LoaderCompleteCallback& loader_complete_callback) | |
134 : Loader(context, | |
135 runner_factory, | |
136 load_callbacks, | |
137 loader_complete_callback), | |
138 mime_type_to_url_(mime_type_to_url), | |
139 weak_ptr_factory_(this) { | |
140 URLRequestPtr request(URLRequest::New()); | |
141 request->url = String::From(url); | |
142 request->auto_follow_redirects = true; | |
143 | |
144 if (base::CommandLine::ForCurrentProcess()->HasSwitch( | |
145 switches::kDisableCache)) { | |
146 request->bypass_cache = true; | |
147 } | |
148 | |
149 network_service->CreateURLLoader(GetProxy(&url_loader_)); | |
150 url_loader_->Start(request.Pass(), | |
151 base::Bind(&NetworkLoader::OnLoadComplete, | |
152 weak_ptr_factory_.GetWeakPtr())); | |
153 } | |
154 | |
155 ~NetworkLoader() override { | |
156 if (!file_.empty()) | |
157 base::DeleteFile(file_, false); | |
158 } | |
159 | |
160 private: | |
161 void OnLoadComplete(URLResponsePtr response) { | |
162 if (response->error) { | |
163 LOG(ERROR) << "Error (" << response->error->code << ": " | |
164 << response->error->description << ") while fetching " | |
165 << response->url; | |
166 LoaderComplete(); | |
167 return; | |
168 } | |
169 | |
170 MimeTypeToURLMap::iterator iter = | |
171 mime_type_to_url_->find(response->mime_type); | |
172 if (iter != mime_type_to_url_->end()) { | |
173 load_callbacks_->LoadWithContentHandler(iter->second, response.Pass()); | |
174 return; | |
175 } | |
176 | |
177 base::CreateTemporaryFile(&file_); | |
178 common::CopyToFile( | |
179 response->body.Pass(), | |
180 file_, | |
181 context_->task_runners()->blocking_pool(), | |
182 base::Bind( | |
183 &NetworkLoader::RunLibrary, weak_ptr_factory_.GetWeakPtr(), file_)); | |
184 } | |
185 | |
186 MimeTypeToURLMap* mime_type_to_url_; | |
187 URLLoaderPtr url_loader_; | |
188 base::FilePath file_; | |
189 base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_; | |
190 }; | |
191 | |
192 DynamicApplicationLoader::DynamicApplicationLoader( | |
193 Context* context, | |
194 scoped_ptr<DynamicServiceRunnerFactory> runner_factory) | |
195 : context_(context), | |
196 runner_factory_(runner_factory.Pass()), | |
197 | |
198 // Unretained() is correct here because DynamicApplicationLoader owns the | |
199 // loaders that we pass this callback to. | |
200 loader_complete_callback_( | |
201 base::Bind(&DynamicApplicationLoader::LoaderComplete, | |
202 base::Unretained(this))) { | |
203 } | |
204 | |
205 DynamicApplicationLoader::~DynamicApplicationLoader() { | |
206 } | |
207 | |
208 void DynamicApplicationLoader::RegisterContentHandler( | |
209 const std::string& mime_type, | |
210 const GURL& content_handler_url) { | |
211 mime_type_to_url_[mime_type] = content_handler_url; | |
212 } | |
213 | |
214 void DynamicApplicationLoader::Load( | |
215 ApplicationManager* manager, | |
216 const GURL& url, | |
217 scoped_refptr<LoadCallbacks> load_callbacks) { | |
218 GURL resolved_url; | |
219 if (url.SchemeIs("mojo")) { | |
220 resolved_url = context_->mojo_url_resolver()->Resolve(url); | |
221 } else { | |
222 resolved_url = url; | |
223 } | |
224 | |
225 if (resolved_url.SchemeIsFile()) { | |
226 loaders_.push_back(new LocalLoader(resolved_url, | |
227 context_, | |
228 runner_factory_.get(), | |
229 load_callbacks, | |
230 loader_complete_callback_)); | |
231 return; | |
232 } | |
233 | |
234 if (!network_service_) { | |
235 context_->application_manager()->ConnectToService( | |
236 GURL("mojo:network_service"), &network_service_); | |
237 } | |
238 | |
239 loaders_.push_back(new NetworkLoader(resolved_url, | |
240 &mime_type_to_url_, | |
241 context_, | |
242 runner_factory_.get(), | |
243 network_service_.get(), | |
244 load_callbacks, | |
245 loader_complete_callback_)); | |
246 } | |
247 | |
248 void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager, | |
249 const GURL& url) { | |
250 // TODO(darin): What should we do about service errors? This implies that | |
251 // the app closed its handle to the service manager. Maybe we don't care? | |
252 } | |
253 | |
254 void DynamicApplicationLoader::LoaderComplete(Loader* loader) { | |
255 loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader)); | |
256 } | |
257 | |
258 } // namespace shell | |
259 } // namespace mojo | |
OLD | NEW |