• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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