cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / mojo / shell / dynamic_application_loader.cc
blob1627aa99a00c8f2405b6f030564fa3fad070cf69
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.
5 #include "mojo/shell/dynamic_application_loader.h"
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"
23 namespace mojo {
24 namespace shell {
26 // Encapsulates loading and running one individual application.
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.
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) {}
47 virtual ~Loader() {}
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;
58 if (!path_exists) {
59 DVLOG(1) << "Library not started because library path '" << path.value()
60 << "' does not exist.";
61 LoaderComplete();
62 return;
65 runner_ = runner_factory_->Create(context_);
66 runner_->Start(
67 path,
68 shell_handle.Pass(),
69 base::Bind(&Loader::LoaderComplete, weak_ptr_factory_.GetWeakPtr()));
72 void LoaderComplete() { loader_complete_callback_.Run(this); }
74 scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks_;
75 LoaderCompleteCallback loader_complete_callback_;
76 Context* context_;
78 private:
79 DynamicServiceRunnerFactory* runner_factory_;
80 scoped_ptr<DynamicServiceRunner> runner_;
81 base::WeakPtrFactory<Loader> weak_ptr_factory_;
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
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)));
118 ~LocalLoader() override {}
120 private:
121 base::WeakPtrFactory<LocalLoader> weak_ptr_factory_;
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;
144 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
145 switches::kDisableCache)) {
146 request->bypass_cache = true;
149 network_service->CreateURLLoader(GetProxy(&url_loader_));
150 url_loader_->Start(request.Pass(),
151 base::Bind(&NetworkLoader::OnLoadComplete,
152 weak_ptr_factory_.GetWeakPtr()));
155 ~NetworkLoader() override {
156 if (!file_.empty())
157 base::DeleteFile(file_, false);
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;
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;
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_));
186 MimeTypeToURLMap* mime_type_to_url_;
187 URLLoaderPtr url_loader_;
188 base::FilePath file_;
189 base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_;
192 DynamicApplicationLoader::DynamicApplicationLoader(
193 Context* context,
194 scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
195 : context_(context),
196 runner_factory_(runner_factory.Pass()),
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))) {
205 DynamicApplicationLoader::~DynamicApplicationLoader() {
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;
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;
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;
234 if (!network_service_) {
235 context_->application_manager()->ConnectToService(
236 GURL("mojo:network_service"), &network_service_);
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_));
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?
254 void DynamicApplicationLoader::LoaderComplete(Loader* loader) {
255 loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader));
258 } // namespace shell
259 } // namespace mojo