1// Copyright 2013 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/context.h"
6
7#include <vector>
8
9#include "base/command_line.h"
10#include "base/lazy_instance.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/memory/scoped_vector.h"
13#include "base/strings/string_split.h"
14#include "build/build_config.h"
15#include "gpu/command_buffer/service/mailbox_manager.h"
16#include "mojo/application_manager/application_loader.h"
17#include "mojo/application_manager/application_manager.h"
18#include "mojo/application_manager/background_shell_application_loader.h"
19#include "mojo/embedder/embedder.h"
20#include "mojo/embedder/simple_platform_support.h"
21#include "mojo/public/cpp/application/application_connection.h"
22#include "mojo/public/cpp/application/application_delegate.h"
23#include "mojo/public/cpp/application/application_impl.h"
24#include "mojo/shell/dynamic_application_loader.h"
25#include "mojo/shell/in_process_dynamic_service_runner.h"
26#include "mojo/shell/out_of_process_dynamic_service_runner.h"
27#include "mojo/shell/switches.h"
28#include "mojo/shell/ui_application_loader_android.h"
29#include "mojo/spy/spy.h"
30
31#if defined(OS_LINUX)
32#include "mojo/shell/dbus_application_loader_linux.h"
33#endif  // defined(OS_LINUX)
34
35#if defined(OS_ANDROID)
36#include "mojo/services/native_viewport/gpu_impl.h"
37#include "mojo/services/native_viewport/native_viewport_impl.h"
38#include "mojo/shell/network_application_loader.h"
39#include "ui/gl/gl_share_group.h"
40#endif  // defined(OS_ANDROID)
41
42namespace mojo {
43namespace shell {
44namespace {
45
46// These mojo: URLs are loaded directly from the local filesystem. They
47// correspond to shared libraries bundled alongside the mojo_shell.
48const char* kLocalMojoURLs[] = {
49  "mojo:mojo_network_service",
50};
51
52// Used to ensure we only init once.
53class Setup {
54 public:
55  Setup() {
56    embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
57        new mojo::embedder::SimplePlatformSupport()));
58  }
59
60  ~Setup() {
61  }
62
63 private:
64  DISALLOW_COPY_AND_ASSIGN(Setup);
65};
66
67static base::LazyInstance<Setup>::Leaky setup = LAZY_INSTANCE_INITIALIZER;
68
69void InitContentHandlers(DynamicApplicationLoader* loader,
70                         base::CommandLine* command_line) {
71  // Default content handlers.
72  loader->RegisterContentHandler("image/png", GURL("mojo://mojo_png_viewer/"));
73  loader->RegisterContentHandler("text/html", GURL("mojo://mojo_html_viewer/"));
74
75  // Command-line-specified content handlers.
76  std::string handlers_spec = command_line->GetSwitchValueASCII(
77      switches::kContentHandlers);
78  if (handlers_spec.empty())
79    return;
80
81  std::vector<std::string> parts;
82  base::SplitString(handlers_spec, ',', &parts);
83  if (parts.size() % 2 != 0) {
84    LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
85               << ": must be a comma-separated list of mimetype/url pairs.";
86    return;
87  }
88
89  for (size_t i = 0; i < parts.size(); i += 2) {
90    GURL url(parts[i + 1]);
91    if (!url.is_valid()) {
92      LOG(ERROR) << "Invalid value for switch " << switches::kContentHandlers
93                 << ": '" << parts[i + 1] << "' is not a valid URL.";
94      return;
95    }
96    loader->RegisterContentHandler(parts[i], url);
97  }
98}
99
100class EmptyServiceProvider : public InterfaceImpl<ServiceProvider> {
101 private:
102  virtual void ConnectToService(const mojo::String& service_name,
103                                ScopedMessagePipeHandle client_handle)
104      MOJO_OVERRIDE {
105  }
106};
107
108}  // namespace
109
110#if defined(OS_ANDROID)
111class Context::NativeViewportApplicationLoader
112    : public ApplicationLoader,
113      public ApplicationDelegate,
114      public InterfaceFactory<NativeViewport>,
115      public InterfaceFactory<Gpu> {
116 public:
117  NativeViewportApplicationLoader()
118      : share_group_(new gfx::GLShareGroup),
119        mailbox_manager_(new gpu::gles2::MailboxManager) {}
120  virtual ~NativeViewportApplicationLoader() {}
121
122 private:
123  // ApplicationLoader implementation.
124  virtual void Load(ApplicationManager* manager,
125                    const GURL& url,
126                    scoped_refptr<LoadCallbacks> callbacks) OVERRIDE {
127    ScopedMessagePipeHandle shell_handle = callbacks->RegisterApplication();
128    if (shell_handle.is_valid())
129      app_.reset(new ApplicationImpl(this, shell_handle.Pass()));
130  }
131
132  virtual void OnApplicationError(ApplicationManager* manager,
133                                  const GURL& url) OVERRIDE {}
134
135  // ApplicationDelegate implementation.
136  virtual bool ConfigureIncomingConnection(
137      mojo::ApplicationConnection* connection) OVERRIDE {
138    connection->AddService<NativeViewport>(this);
139    connection->AddService<Gpu>(this);
140    return true;
141  }
142
143  // InterfaceFactory<NativeViewport> implementation.
144  virtual void Create(ApplicationConnection* connection,
145                      InterfaceRequest<NativeViewport> request) OVERRIDE {
146    BindToRequest(new NativeViewportImpl(app_.get(), false), &request);
147  }
148
149  // InterfaceFactory<Gpu> implementation.
150  virtual void Create(ApplicationConnection* connection,
151                      InterfaceRequest<Gpu> request) OVERRIDE {
152    BindToRequest(new GpuImpl(share_group_.get(), mailbox_manager_.get()),
153                  &request);
154  }
155
156  scoped_refptr<gfx::GLShareGroup> share_group_;
157  scoped_refptr<gpu::gles2::MailboxManager> mailbox_manager_;
158  scoped_ptr<ApplicationImpl> app_;
159  DISALLOW_COPY_AND_ASSIGN(NativeViewportApplicationLoader);
160};
161#endif
162
163Context::Context() {
164  DCHECK(!base::MessageLoop::current());
165}
166
167Context::~Context() {
168  DCHECK(!base::MessageLoop::current());
169}
170
171void Context::Init() {
172  application_manager_.set_delegate(this);
173  setup.Get();
174  task_runners_.reset(
175      new TaskRunners(base::MessageLoop::current()->message_loop_proxy()));
176
177  for (size_t i = 0; i < arraysize(kLocalMojoURLs); ++i)
178    mojo_url_resolver_.AddLocalFileMapping(GURL(kLocalMojoURLs[i]));
179
180  base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
181  scoped_ptr<DynamicServiceRunnerFactory> runner_factory;
182  if (command_line->HasSwitch(switches::kEnableMultiprocess))
183    runner_factory.reset(new OutOfProcessDynamicServiceRunnerFactory());
184  else
185    runner_factory.reset(new InProcessDynamicServiceRunnerFactory());
186
187  DynamicApplicationLoader* dynamic_application_loader =
188      new DynamicApplicationLoader(this, runner_factory.Pass());
189  InitContentHandlers(dynamic_application_loader, command_line);
190  application_manager_.set_default_loader(
191      scoped_ptr<ApplicationLoader>(dynamic_application_loader));
192
193  // The native viewport service synchronously waits for certain messages. If we
194  // don't run it on its own thread we can easily deadlock. Long term native
195  // viewport should run its own process so that this isn't an issue.
196#if defined(OS_ANDROID)
197  application_manager_.SetLoaderForURL(
198      scoped_ptr<ApplicationLoader>(new UIApplicationLoader(
199          scoped_ptr<ApplicationLoader>(new NativeViewportApplicationLoader()),
200          this)),
201      GURL("mojo:mojo_native_viewport_service"));
202#endif
203
204#if defined(OS_LINUX)
205  application_manager_.SetLoaderForScheme(
206      scoped_ptr<ApplicationLoader>(new DBusApplicationLoader(this)), "dbus");
207#endif  // defined(OS_LINUX)
208
209  if (command_line->HasSwitch(switches::kSpy)) {
210    spy_.reset(
211        new mojo::Spy(&application_manager_,
212                      command_line->GetSwitchValueASCII(switches::kSpy)));
213  }
214
215#if defined(OS_ANDROID)
216  // On android, the network service is bundled with the shell because the
217  // network stack depends on the android runtime.
218  {
219    scoped_ptr<BackgroundShellApplicationLoader> loader(
220        new BackgroundShellApplicationLoader(
221            scoped_ptr<ApplicationLoader>(new NetworkApplicationLoader()),
222            "network_service",
223            base::MessageLoop::TYPE_IO));
224    application_manager_.SetLoaderForURL(loader.PassAs<ApplicationLoader>(),
225                                         GURL("mojo:mojo_network_service"));
226  }
227#endif
228}
229
230void Context::OnApplicationError(const GURL& gurl) {
231  if (app_urls_.find(gurl) != app_urls_.end()) {
232    app_urls_.erase(gurl);
233    if (app_urls_.empty() && base::MessageLoop::current()->is_running())
234      base::MessageLoop::current()->Quit();
235  }
236}
237
238void Context::Run(const GURL& url) {
239  EmptyServiceProvider* sp = new EmptyServiceProvider;
240  ServiceProviderPtr spp;
241  BindToProxy(sp, &spp);
242
243  app_urls_.insert(url);
244  application_manager_.ConnectToApplication(url, GURL(), spp.Pass());
245}
246
247ScopedMessagePipeHandle Context::ConnectToServiceByName(
248    const GURL& application_url,
249    const std::string& service_name) {
250  app_urls_.insert(application_url);
251  return application_manager_.ConnectToServiceByName(
252      application_url, service_name).Pass();
253}
254
255}  // namespace shell
256}  // namespace mojo
257