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: 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 45 virtual ~Loader() {} 46 47 protected: 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 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: 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 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: 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 144 virtual ~NetworkLoader() { 145 if (!file_.empty()) 146 base::DeleteFile(file_, false); 147 } 148 149 private: 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 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 194 DynamicApplicationLoader::~DynamicApplicationLoader() { 195 } 196 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 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 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 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