Home | History | Annotate | Download | only in shell
      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