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