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