blob: 1a75caebba29b0cde1d7d222fc44aaaaa91ca8bb [file] [log] [blame]
Torne (Richard Coles)6e8cce62014-08-19 13:00:08 +01001// 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/application_manager/application_manager.h"
6
7#include <stdio.h>
8
9#include "base/bind.h"
10#include "base/lazy_instance.h"
11#include "base/logging.h"
12#include "base/macros.h"
13#include "base/stl_util.h"
14#include "mojo/application_manager/application_loader.h"
15#include "mojo/common/common_type_converters.h"
16#include "mojo/public/cpp/application/connect.h"
17#include "mojo/public/interfaces/application/application.mojom.h"
18#include "mojo/public/interfaces/application/shell.mojom.h"
19#include "mojo/services/public/interfaces/content_handler/content_handler.mojom.h"
20
21namespace mojo {
22
23namespace {
24// Used by TestAPI.
25bool has_created_instance = false;
26
27class StubServiceProvider : public InterfaceImpl<ServiceProvider> {
28 public:
29 ServiceProvider* GetRemoteServiceProvider() { return client(); }
30
31 private:
32 virtual void ConnectToService(const String& service_name,
33 ScopedMessagePipeHandle client_handle)
34 MOJO_OVERRIDE {}
35};
36
37} // namespace
38
39class ApplicationManager::LoadCallbacksImpl
40 : public ApplicationLoader::LoadCallbacks {
41 public:
42 LoadCallbacksImpl(base::WeakPtr<ApplicationManager> manager,
43 const GURL& requested_url,
44 const GURL& requestor_url,
45 ServiceProviderPtr service_provider)
46 : manager_(manager),
47 requested_url_(requested_url),
48 requestor_url_(requestor_url),
49 service_provider_(service_provider.Pass()) {}
50
51 private:
52 virtual ~LoadCallbacksImpl() {}
53
54 // LoadCallbacks implementation
55 virtual ScopedMessagePipeHandle RegisterApplication() OVERRIDE {
56 ScopedMessagePipeHandle shell_handle;
57 if (manager_) {
58 manager_->RegisterLoadedApplication(requested_url_,
59 requestor_url_,
60 service_provider_.Pass(),
61 &shell_handle);
62 }
63 return shell_handle.Pass();
64 }
65
66 virtual void LoadWithContentHandler(const GURL& content_handler_url,
67 URLResponsePtr content) OVERRIDE {
68 if (manager_) {
69 manager_->LoadWithContentHandler(requested_url_,
70 requestor_url_,
71 content_handler_url,
72 content.Pass(),
73 service_provider_.Pass());
74 }
75 }
76
77 base::WeakPtr<ApplicationManager> manager_;
78 GURL requested_url_;
79 GURL requestor_url_;
80 ServiceProviderPtr service_provider_;
81};
82
83class ApplicationManager::ShellImpl : public InterfaceImpl<Shell> {
84 public:
85 ShellImpl(ApplicationManager* manager, const GURL& url)
86 : manager_(manager), url_(url) {}
87
88 virtual ~ShellImpl() {}
89
90 void ConnectToClient(const GURL& requestor_url,
91 ServiceProviderPtr service_provider) {
92 client()->AcceptConnection(String::From(requestor_url),
93 service_provider.Pass());
94 }
95
96 // ServiceProvider implementation:
97 virtual void ConnectToApplication(
98 const String& app_url,
99 InterfaceRequest<ServiceProvider> in_service_provider) OVERRIDE {
100 ServiceProviderPtr out_service_provider;
101 out_service_provider.Bind(in_service_provider.PassMessagePipe());
102 manager_->ConnectToApplication(
103 app_url.To<GURL>(), url_, out_service_provider.Pass());
104 }
105
106 const GURL& url() const { return url_; }
107
108 private:
109 virtual void OnConnectionError() OVERRIDE {
110 manager_->OnShellImplError(this);
111 }
112
113 ApplicationManager* const manager_;
114 const GURL url_;
115
116 DISALLOW_COPY_AND_ASSIGN(ShellImpl);
117};
118
119struct ApplicationManager::ContentHandlerConnection {
120 ContentHandlerConnection(ApplicationManager* manager,
121 const GURL& content_handler_url) {
122 ServiceProviderPtr service_provider;
123 BindToProxy(&service_provider_impl, &service_provider);
124 manager->ConnectToApplication(
125 content_handler_url, GURL(), service_provider.Pass());
126 mojo::ConnectToService(service_provider_impl.client(), &content_handler);
127 }
128
129 StubServiceProvider service_provider_impl;
130 ContentHandlerPtr content_handler;
131};
132
133// static
134ApplicationManager::TestAPI::TestAPI(ApplicationManager* manager)
135 : manager_(manager) {
136}
137
138ApplicationManager::TestAPI::~TestAPI() {
139}
140
141bool ApplicationManager::TestAPI::HasCreatedInstance() {
142 return has_created_instance;
143}
144
145bool ApplicationManager::TestAPI::HasFactoryForURL(const GURL& url) const {
146 return manager_->url_to_shell_impl_.find(url) !=
147 manager_->url_to_shell_impl_.end();
148}
149
150ApplicationManager::ApplicationManager()
151 : interceptor_(NULL), weak_ptr_factory_(this) {
152}
153
154ApplicationManager::~ApplicationManager() {
155 STLDeleteValues(&url_to_content_handler_);
156 TerminateShellConnections();
157 STLDeleteValues(&url_to_loader_);
158 STLDeleteValues(&scheme_to_loader_);
159}
160
161void ApplicationManager::TerminateShellConnections() {
162 STLDeleteValues(&url_to_shell_impl_);
163}
164
165// static
166ApplicationManager* ApplicationManager::GetInstance() {
167 static base::LazyInstance<ApplicationManager> instance =
168 LAZY_INSTANCE_INITIALIZER;
169 has_created_instance = true;
170 return &instance.Get();
171}
172
173void ApplicationManager::ConnectToApplication(
174 const GURL& url,
175 const GURL& requestor_url,
176 ServiceProviderPtr service_provider) {
177 URLToShellImplMap::const_iterator shell_it = url_to_shell_impl_.find(url);
178 if (shell_it != url_to_shell_impl_.end()) {
179 ConnectToClient(
180 shell_it->second, url, requestor_url, service_provider.Pass());
181 return;
182 }
183
184 scoped_refptr<LoadCallbacksImpl> callbacks(
185 new LoadCallbacksImpl(weak_ptr_factory_.GetWeakPtr(),
186 url,
187 requestor_url,
188 service_provider.Pass()));
189 GetLoaderForURL(url)->Load(this, url, callbacks);
190}
191
192void ApplicationManager::ConnectToClient(ShellImpl* shell_impl,
193 const GURL& url,
194 const GURL& requestor_url,
195 ServiceProviderPtr service_provider) {
196 if (interceptor_) {
197 shell_impl->ConnectToClient(
198 requestor_url,
199 interceptor_->OnConnectToClient(url, service_provider.Pass()));
200 } else {
201 shell_impl->ConnectToClient(requestor_url, service_provider.Pass());
202 }
203}
204
205void ApplicationManager::RegisterLoadedApplication(
206 const GURL& url,
207 const GURL& requestor_url,
208 ServiceProviderPtr service_provider,
209 ScopedMessagePipeHandle* shell_handle) {
210 ShellImpl* shell_impl = NULL;
211 URLToShellImplMap::iterator iter = url_to_shell_impl_.find(url);
212 if (iter != url_to_shell_impl_.end()) {
213 // This can happen because services are loaded asynchronously. So if we get
214 // two requests for the same service close to each other, we might get here
215 // and find that we already have it.
216 shell_impl = iter->second;
217 } else {
218 MessagePipe pipe;
219 shell_impl = WeakBindToPipe(new ShellImpl(this, url), pipe.handle1.Pass());
220 url_to_shell_impl_[url] = shell_impl;
221 *shell_handle = pipe.handle0.Pass();
222 }
223
224 ConnectToClient(shell_impl, url, requestor_url, service_provider.Pass());
225}
226
227void ApplicationManager::LoadWithContentHandler(
228 const GURL& content_url,
229 const GURL& requestor_url,
230 const GURL& content_handler_url,
231 URLResponsePtr content,
232 ServiceProviderPtr service_provider) {
233 ContentHandlerConnection* connection = NULL;
234 URLToContentHandlerMap::iterator iter =
235 url_to_content_handler_.find(content_handler_url);
236 if (iter != url_to_content_handler_.end()) {
237 connection = iter->second;
238 } else {
239 connection = new ContentHandlerConnection(this, content_handler_url);
240 url_to_content_handler_[content_handler_url] = connection;
241 }
242 connection->content_handler->OnConnect(
243 content_url.spec(), content.Pass(), service_provider.Pass());
244}
245
246void ApplicationManager::SetLoaderForURL(scoped_ptr<ApplicationLoader> loader,
247 const GURL& url) {
248 URLToLoaderMap::iterator it = url_to_loader_.find(url);
249 if (it != url_to_loader_.end())
250 delete it->second;
251 url_to_loader_[url] = loader.release();
252}
253
254void ApplicationManager::SetLoaderForScheme(
255 scoped_ptr<ApplicationLoader> loader,
256 const std::string& scheme) {
257 SchemeToLoaderMap::iterator it = scheme_to_loader_.find(scheme);
258 if (it != scheme_to_loader_.end())
259 delete it->second;
260 scheme_to_loader_[scheme] = loader.release();
261}
262
263void ApplicationManager::SetInterceptor(Interceptor* interceptor) {
264 interceptor_ = interceptor;
265}
266
267ApplicationLoader* ApplicationManager::GetLoaderForURL(const GURL& url) {
268 URLToLoaderMap::const_iterator url_it = url_to_loader_.find(url);
269 if (url_it != url_to_loader_.end())
270 return url_it->second;
271 SchemeToLoaderMap::const_iterator scheme_it =
272 scheme_to_loader_.find(url.scheme());
273 if (scheme_it != scheme_to_loader_.end())
274 return scheme_it->second;
275 return default_loader_.get();
276}
277
278void ApplicationManager::OnShellImplError(ShellImpl* shell_impl) {
279 // Called from ~ShellImpl, so we do not need to call Destroy here.
280 const GURL url = shell_impl->url();
281 URLToShellImplMap::iterator it = url_to_shell_impl_.find(url);
282 DCHECK(it != url_to_shell_impl_.end());
283 delete it->second;
284 url_to_shell_impl_.erase(it);
285 ApplicationLoader* loader = GetLoaderForURL(url);
286 if (loader)
287 loader->OnServiceError(this, url);
288}
289
290ScopedMessagePipeHandle ApplicationManager::ConnectToServiceByName(
291 const GURL& application_url,
292 const std::string& interface_name) {
293 StubServiceProvider* stub_sp = new StubServiceProvider;
294 ServiceProviderPtr spp;
295 BindToProxy(stub_sp, &spp);
296 ConnectToApplication(application_url, GURL(), spp.Pass());
297 MessagePipe pipe;
298 stub_sp->GetRemoteServiceProvider()->ConnectToService(interface_name,
299 pipe.handle1.Pass());
300 return pipe.handle0.Pass();
301}
302} // namespace mojo