Chromium Code Reviews| Index: mojo/shell/dynamic_application_loader.cc |
| diff --git a/mojo/shell/dynamic_application_loader.cc b/mojo/shell/dynamic_application_loader.cc |
| index 718ece4263714b9f81fd388f3b683eb554efaf84..d7e42e472cf44eadcee96bc86ccadc5f2de5985c 100644 |
| --- a/mojo/shell/dynamic_application_loader.cc |
| +++ b/mojo/shell/dynamic_application_loader.cc |
| @@ -9,6 +9,7 @@ |
| #include "base/file_util.h" |
| #include "base/files/file_path.h" |
| #include "base/memory/scoped_ptr.h" |
| +#include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "mojo/common/common_type_converters.h" |
| #include "mojo/common/data_pipe_utils.h" |
| @@ -20,12 +21,176 @@ |
| namespace mojo { |
| namespace shell { |
| +// Encapsulates loading and running one individual application. |
| +// |
| +// Loaders are owned by DynamicApplicationLoader. DynamicApplicationLoader must |
| +// ensure that all the parameters passed to Loader subclasses stay valid through |
| +// Loader's lifetime. |
| +// |
| +// Async operations are done with WeakPtr to protect against |
| +// DynamicApplicationLoader going away (and taking all the Loaders with it) |
| +// while the async operation is outstanding. |
| +class DynamicApplicationLoader::Loader { |
| + public: |
| + Loader(Context* context, |
| + DynamicServiceRunnerFactory* runner_factory, |
| + scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, |
| + const LoaderCompleteCallback& loader_complete_callback) |
| + : load_callbacks_(load_callbacks), |
| + loader_complete_callback_(loader_complete_callback), |
| + context_(context), |
| + runner_factory_(runner_factory), |
| + weak_ptr_factory_(this) {} |
| + |
| + virtual ~Loader() {} |
| + |
| + protected: |
| + void RunLibrary(const base::FilePath& path, bool path_exists) { |
| + ScopedMessagePipeHandle shell_handle = |
| + load_callbacks_->RegisterApplication(); |
| + if (!shell_handle.is_valid()) { |
| + AppCompleted(); |
| + return; |
| + } |
| + |
| + if (!path_exists) { |
| + DVLOG(1) << "Library not started because library path '" << path.value() |
| + << "' does not exist."; |
| + AppCompleted(); |
| + return; |
| + } |
| + |
| + runner_ = runner_factory_->Create(context_); |
| + runner_->Start( |
| + path, |
| + shell_handle.Pass(), |
| + base::Bind(&Loader::AppCompleted, weak_ptr_factory_.GetWeakPtr())); |
|
tim (not reviewing)
2014/08/28 19:11:14
nit: I kind of liked the RunLibrary / OnRunLibrary
Aaron Boodman
2014/08/28 19:49:22
Yeah, but it is run in other situations than when
|
| + } |
| + |
| + void AppCompleted() { loader_complete_callback_.Run(this); } |
| + |
| + scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks_; |
| + LoaderCompleteCallback loader_complete_callback_; |
| + Context* context_; |
| + |
| + private: |
| + DynamicServiceRunnerFactory* runner_factory_; |
| + scoped_ptr<DynamicServiceRunner> runner_; |
| + base::WeakPtrFactory<Loader> weak_ptr_factory_; |
| +}; |
| + |
| +// A loader for local files. |
| +class DynamicApplicationLoader::LocalLoader : public Loader { |
| + public: |
| + LocalLoader(const GURL& url, |
| + Context* context, |
| + DynamicServiceRunnerFactory* runner_factory, |
| + scoped_refptr<ApplicationLoader::LoadCallbacks> load_callbacks, |
| + const LoaderCompleteCallback& loader_complete_callback) |
| + : Loader(context, |
| + runner_factory, |
| + load_callbacks, |
| + loader_complete_callback), |
| + weak_ptr_factory_(this) { |
| + base::FilePath path; |
| + net::FileURLToFilePath(url, &path); |
| + |
| + // Async for consistency with network case. |
| + base::MessageLoop::current()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&LocalLoader::RunLibrary, |
| + weak_ptr_factory_.GetWeakPtr(), |
| + path, |
| + base::PathExists(path))); |
| + } |
| + |
| + virtual ~LocalLoader() {} |
| + |
| + private: |
| + base::WeakPtrFactory<LocalLoader> weak_ptr_factory_; |
| +}; |
| + |
| +// A loader for network files. |
| +class DynamicApplicationLoader::NetworkLoader : public Loader { |
| + 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) |
| + : Loader(context, |
| + runner_factory, |
| + load_callbacks, |
| + loader_complete_callback), |
| + mime_type_to_url_(mime_type_to_url), |
| + weak_ptr_factory_(this) { |
| + URLRequestPtr request(URLRequest::New()); |
| + request->url = String::From(url); |
| + request->auto_follow_redirects = true; |
| + |
| + if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kDisableCache)) { |
| + request->bypass_cache = true; |
| + } |
| + |
| + network_service->CreateURLLoader(Get(&url_loader_)); |
| + url_loader_->Start(request.Pass(), |
| + base::Bind(&NetworkLoader::OnLoadComplete, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + } |
| + |
| + virtual ~NetworkLoader() { |
| + if (!file_.empty()) |
| + base::DeleteFile(file_, false); |
| + } |
| + |
| + private: |
| + void OnLoadComplete(URLResponsePtr response) { |
| + if (response->error) { |
| + LOG(ERROR) << "Error (" << response->error->code << ": " |
| + << response->error->description << ") while fetching " |
| + << response->url; |
| + AppCompleted(); |
| + return; |
| + } |
| + |
| + MimeTypeToURLMap::iterator iter = |
| + mime_type_to_url_->find(response->mime_type); |
| + if (iter != mime_type_to_url_->end()) { |
| + load_callbacks_->LoadWithContentHandler( |
| + iter->second, response.Pass(), url_loader_.Pass()); |
| + AppCompleted(); |
| + return; |
| + } |
| + |
| + base::CreateTemporaryFile(&file_); |
| + common::CopyToFile( |
| + response->body.Pass(), |
| + file_, |
| + context_->task_runners()->blocking_pool(), |
| + base::Bind( |
| + &NetworkLoader::RunLibrary, weak_ptr_factory_.GetWeakPtr(), file_)); |
| + } |
| + |
| + MimeTypeToURLMap* mime_type_to_url_; |
| + URLLoaderPtr url_loader_; |
| + base::FilePath file_; |
| + base::WeakPtrFactory<NetworkLoader> weak_ptr_factory_; |
| +}; |
| + |
| DynamicApplicationLoader::DynamicApplicationLoader( |
| Context* context, |
| scoped_ptr<DynamicServiceRunnerFactory> runner_factory) |
| : context_(context), |
| runner_factory_(runner_factory.Pass()), |
| - weak_ptr_factory_(this) { |
| + |
| + // Unretained() is correct here because DynamicApplicationLoader owns the |
| + // loaders that we pass this callback to. |
| + loader_complete_callback_( |
| + base::Bind(&DynamicApplicationLoader::LoaderComplete, |
| + base::Unretained(this))) { |
| } |
| DynamicApplicationLoader::~DynamicApplicationLoader() { |
| @@ -37,9 +202,10 @@ void DynamicApplicationLoader::RegisterContentHandler( |
| mime_type_to_url_[mime_type] = content_handler_url; |
| } |
| -void DynamicApplicationLoader::Load(ApplicationManager* manager, |
| - const GURL& url, |
| - scoped_refptr<LoadCallbacks> callbacks) { |
| +void DynamicApplicationLoader::Load( |
| + ApplicationManager* manager, |
| + const GURL& url, |
| + scoped_refptr<LoadCallbacks> load_callbacks) { |
| GURL resolved_url; |
| if (url.SchemeIs("mojo")) { |
| resolved_url = context_->mojo_url_resolver()->Resolve(url); |
| @@ -47,124 +213,27 @@ void DynamicApplicationLoader::Load(ApplicationManager* manager, |
| resolved_url = url; |
| } |
| - if (resolved_url.SchemeIsFile()) |
| - LoadLocalService(resolved_url, callbacks); |
| - else |
| - LoadNetworkService(resolved_url, callbacks); |
| -} |
| - |
| -void DynamicApplicationLoader::LoadLocalService( |
| - const GURL& resolved_url, |
| - scoped_refptr<LoadCallbacks> callbacks) { |
| - base::FilePath path; |
| - net::FileURLToFilePath(resolved_url, &path); |
| - const bool kDeleteFileAfter = false; |
| - |
| - // Async for consistency with network case. |
| - base::MessageLoop::current()->PostTask( |
| - FROM_HERE, |
| - base::Bind(&DynamicApplicationLoader::RunLibrary, |
| - weak_ptr_factory_.GetWeakPtr(), |
| - path, |
| - callbacks, |
| - kDeleteFileAfter, |
| - base::PathExists(path))); |
| -} |
| + if (resolved_url.SchemeIsFile()) { |
| + loaders_.push_back(new LocalLoader(resolved_url, |
| + context_, |
| + runner_factory_.get(), |
| + load_callbacks, |
| + loader_complete_callback_)); |
| + return; |
| + } |
| -void DynamicApplicationLoader::LoadNetworkService( |
| - const GURL& resolved_url, |
| - scoped_refptr<LoadCallbacks> callbacks) { |
| if (!network_service_) { |
| context_->application_manager()->ConnectToService( |
| GURL("mojo:mojo_network_service"), &network_service_); |
| } |
| - if (!url_loader_) |
| - network_service_->CreateURLLoader(Get(&url_loader_)); |
| - |
| - URLRequestPtr request(URLRequest::New()); |
| - request->url = String::From(resolved_url); |
| - request->auto_follow_redirects = true; |
| - |
| - if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| - switches::kDisableCache)) { |
| - request->bypass_cache = true; |
| - } |
| - |
| - url_loader_->Start( |
| - request.Pass(), |
| - base::Bind(&DynamicApplicationLoader::OnLoadNetworkServiceComplete, |
| - weak_ptr_factory_.GetWeakPtr(), |
| - callbacks)); |
| -} |
| - |
| -void DynamicApplicationLoader::OnLoadNetworkServiceComplete( |
| - scoped_refptr<LoadCallbacks> callbacks, |
| - URLResponsePtr response) { |
| - if (response->error) { |
| - LOG(ERROR) << "Error (" << response->error->code << ": " |
| - << response->error->description << ") while fetching " |
| - << response->url; |
| - } |
| - |
| - MimeTypeToURLMap::iterator iter = mime_type_to_url_.find(response->mime_type); |
| - if (iter != mime_type_to_url_.end()) { |
| - callbacks->LoadWithContentHandler(iter->second, response.Pass()); |
| - return; |
| - } |
| - |
| - base::FilePath file; |
| - base::CreateTemporaryFile(&file); |
| - |
| - const bool kDeleteFileAfter = true; |
| - common::CopyToFile(response->body.Pass(), |
| - file, |
| - context_->task_runners()->blocking_pool(), |
| - base::Bind(&DynamicApplicationLoader::RunLibrary, |
| - weak_ptr_factory_.GetWeakPtr(), |
| - file, |
| - callbacks, |
| - kDeleteFileAfter)); |
| -} |
| - |
| -void DynamicApplicationLoader::RunLibrary( |
| - const base::FilePath& path, |
| - scoped_refptr<LoadCallbacks> callbacks, |
| - bool delete_file_after, |
| - bool path_exists) { |
| - |
| - ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication(); |
| - if (!shell_handle.is_valid()) |
| - return; |
| - |
| - if (!path_exists) { |
| - DVLOG(1) << "Library not started because library path '" |
| - << path.value() << "' does not exist."; |
| - return; |
| - } |
| - scoped_ptr<DynamicServiceRunner> runner = |
| - runner_factory_->Create(context_).Pass(); |
| - runner->Start(path, |
| - shell_handle.Pass(), |
| - base::Bind(&DynamicApplicationLoader::OnRunLibraryComplete, |
| - weak_ptr_factory_.GetWeakPtr(), |
| - base::Unretained(runner.get()), |
| - delete_file_after ? path : base::FilePath())); |
| - runners_.push_back(runner.release()); |
| -} |
| - |
| -void DynamicApplicationLoader::OnRunLibraryComplete( |
| - DynamicServiceRunner* runner, |
| - const base::FilePath& temp_file) { |
| - for (ScopedVector<DynamicServiceRunner>::iterator it = runners_.begin(); |
| - it != runners_.end(); ++it) { |
| - if (*it == runner) { |
| - runners_.erase(it); |
| - if (!temp_file.empty()) |
| - base::DeleteFile(temp_file, false); |
| - return; |
| - } |
| - } |
| + loaders_.push_back(new NetworkLoader(resolved_url, |
| + &mime_type_to_url_, |
| + context_, |
| + runner_factory_.get(), |
| + network_service_.get(), |
| + load_callbacks, |
| + loader_complete_callback_)); |
| } |
| void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager, |
| @@ -173,5 +242,9 @@ void DynamicApplicationLoader::OnApplicationError(ApplicationManager* manager, |
| // the app closed its handle to the service manager. Maybe we don't care? |
| } |
| +void DynamicApplicationLoader::LoaderComplete(Loader* loader) { |
| + loaders_.erase(std::find(loaders_.begin(), loaders_.end(), loader)); |
| +} |
| + |
| } // namespace shell |
| } // namespace mojo |