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