• 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_service_loader.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/file_util.h"
10 #include "base/files/file_path.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "mojo/common/data_pipe_utils.h"
14 #include "mojo/services/public/interfaces/network/url_loader.mojom.h"
15 #include "mojo/shell/context.h"
16 #include "mojo/shell/keep_alive.h"
17 #include "mojo/shell/switches.h"
18 #include "net/base/filename_util.h"
19 
20 namespace mojo {
21 namespace shell {
22 namespace {
23 
24 class Loader {
25  public:
Loader(scoped_ptr<DynamicServiceRunner> runner)26   explicit Loader(scoped_ptr<DynamicServiceRunner> runner)
27       : runner_(runner.Pass()) {
28   }
29 
30   virtual void Start(const GURL& url,
31                      ScopedMessagePipeHandle service_handle,
32                      Context* context) = 0;
33 
StartService(const base::FilePath & path,ScopedMessagePipeHandle service_handle,bool path_is_valid)34   void StartService(const base::FilePath& path,
35                     ScopedMessagePipeHandle service_handle,
36                     bool path_is_valid) {
37     if (path_is_valid) {
38       runner_->Start(path, service_handle.Pass(),
39                      base::Bind(&Loader::AppCompleted, base::Unretained(this)));
40     } else {
41       AppCompleted();
42     }
43   }
44 
45  protected:
~Loader()46   virtual ~Loader() {}
47 
48  private:
AppCompleted()49   void AppCompleted() {
50     delete this;
51   }
52 
53   scoped_ptr<DynamicServiceRunner> runner_;
54 };
55 
56 // For loading services via file:// URLs.
57 class LocalLoader : public Loader {
58  public:
LocalLoader(scoped_ptr<DynamicServiceRunner> runner)59   explicit LocalLoader(scoped_ptr<DynamicServiceRunner> runner)
60       : Loader(runner.Pass()) {
61   }
62 
Start(const GURL & url,ScopedMessagePipeHandle service_handle,Context * context)63   virtual void Start(const GURL& url,
64                      ScopedMessagePipeHandle service_handle,
65                      Context* context) OVERRIDE {
66     base::FilePath path;
67     net::FileURLToFilePath(url, &path);
68 
69     // TODO(darin): Check if the given file path exists.
70 
71     // Complete asynchronously for consistency with NetworkServiceLoader.
72     base::MessageLoop::current()->PostTask(
73         FROM_HERE,
74         base::Bind(&Loader::StartService,
75                    base::Unretained(this),
76                    path,
77                    base::Passed(&service_handle),
78                    true));
79   }
80 };
81 
82 // For loading services via the network stack.
83 class NetworkLoader : public Loader, public URLLoaderClient {
84  public:
NetworkLoader(scoped_ptr<DynamicServiceRunner> runner,NetworkService * network_service)85   explicit NetworkLoader(scoped_ptr<DynamicServiceRunner> runner,
86                          NetworkService* network_service)
87       : Loader(runner.Pass()) {
88     network_service->CreateURLLoader(Get(&url_loader_));
89     url_loader_.set_client(this);
90   }
91 
Start(const GURL & url,ScopedMessagePipeHandle service_handle,Context * context)92   virtual void Start(const GURL& url,
93                     ScopedMessagePipeHandle service_handle,
94                     Context* context) OVERRIDE {
95     service_handle_ = service_handle.Pass();
96 
97     URLRequestPtr request(URLRequest::New());
98     request->url = url.spec();
99     request->auto_follow_redirects = true;
100 
101     if (base::CommandLine::ForCurrentProcess()->HasSwitch(
102             switches::kDisableCache)) {
103       request->bypass_cache = true;
104     }
105 
106     DataPipe data_pipe;
107     url_loader_->Start(request.Pass(), data_pipe.producer_handle.Pass());
108 
109     base::CreateTemporaryFile(&file_);
110     common::CopyToFile(data_pipe.consumer_handle.Pass(),
111                        file_,
112                        context->task_runners()->blocking_pool(),
113                        base::Bind(&Loader::StartService,
114                                   base::Unretained(this),
115                                   file_,
116                                   base::Passed(&service_handle_)));
117   }
118 
119  private:
~NetworkLoader()120   virtual ~NetworkLoader() {
121     if (!file_.empty())
122       base::DeleteFile(file_, false);
123   }
124 
125   // URLLoaderClient methods:
OnReceivedRedirect(URLResponsePtr response,const String & new_url,const String & new_method)126   virtual void OnReceivedRedirect(URLResponsePtr response,
127                                   const String& new_url,
128                                   const String& new_method) OVERRIDE {
129     // TODO(darin): Handle redirects properly!
130   }
OnReceivedResponse(URLResponsePtr response)131   virtual void OnReceivedResponse(URLResponsePtr response) OVERRIDE {}
OnReceivedError(NetworkErrorPtr error)132   virtual void OnReceivedError(NetworkErrorPtr error) OVERRIDE {}
OnReceivedEndOfResponseBody()133   virtual void OnReceivedEndOfResponseBody() OVERRIDE {}
134 
135   NetworkServicePtr network_service_;
136   URLLoaderPtr url_loader_;
137   ScopedMessagePipeHandle service_handle_;
138   base::FilePath file_;
139 };
140 
141 }  // namespace
142 
DynamicServiceLoader(Context * context,scoped_ptr<DynamicServiceRunnerFactory> runner_factory)143 DynamicServiceLoader::DynamicServiceLoader(
144     Context* context,
145     scoped_ptr<DynamicServiceRunnerFactory> runner_factory)
146     : context_(context),
147       runner_factory_(runner_factory.Pass()) {
148 }
149 
~DynamicServiceLoader()150 DynamicServiceLoader::~DynamicServiceLoader() {
151 }
152 
LoadService(ServiceManager * manager,const GURL & url,ScopedMessagePipeHandle service_handle)153 void DynamicServiceLoader::LoadService(ServiceManager* manager,
154                                        const GURL& url,
155                                        ScopedMessagePipeHandle service_handle) {
156   scoped_ptr<DynamicServiceRunner> runner = runner_factory_->Create(context_);
157 
158   GURL resolved_url;
159   if (url.SchemeIs("mojo")) {
160     resolved_url = context_->mojo_url_resolver()->Resolve(url);
161   } else {
162     resolved_url = url;
163   }
164 
165   Loader* loader;
166   if (resolved_url.SchemeIsFile()) {
167     loader = new LocalLoader(runner.Pass());
168   } else {
169     if (!network_service_.get()) {
170       context_->service_manager()->ConnectTo(GURL("mojo:mojo_network_service"),
171                                              &network_service_,
172                                              GURL());
173     }
174     loader = new NetworkLoader(runner.Pass(), network_service_.get());
175   }
176   loader->Start(resolved_url, service_handle.Pass(), context_);
177 }
178 
OnServiceError(ServiceManager * manager,const GURL & url)179 void DynamicServiceLoader::OnServiceError(ServiceManager* manager,
180                                           const GURL& url) {
181   // TODO(darin): What should we do about service errors? This implies that
182   // the app closed its handle to the service manager. Maybe we don't care?
183 }
184 
185 }  // namespace shell
186 }  // namespace mojo
187