mozart: Initial commit of the launcher.

The launcher is a simple application which binds a ViewTree to
a NativeViewport, thereby allowing Mozart based applications to be
shown on screen.

Note: This version does not support input yet.

R=abarth@google.com, jamesr@chromium.org

Review URL: https://codereview.chromium.org/1404423007 .
diff --git a/services/ui/BUILD.gn b/services/ui/BUILD.gn
index bb93fba..9480d5e 100644
--- a/services/ui/BUILD.gn
+++ b/services/ui/BUILD.gn
@@ -4,6 +4,7 @@
 
 group("ui") {
   deps = [
+    "//services/ui/launcher",
     "//services/ui/view_manager",
   ]
 }
diff --git a/services/ui/launcher/BUILD.gn b/services/ui/launcher/BUILD.gn
new file mode 100644
index 0000000..e85fda3
--- /dev/null
+++ b/services/ui/launcher/BUILD.gn
@@ -0,0 +1,28 @@
+# Copyright 2015 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("launcher") {
+  sources = [
+    "launcher_app.cc",
+    "launcher_app.h",
+    "launcher_view_tree.cc",
+    "launcher_view_tree.h",
+    "main.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common:tracing_impl",
+    "//mojo/environment:chromium",
+    "//mojo/public/cpp/bindings:bindings",
+    "//mojo/services/native_viewport/interfaces",
+    "//mojo/services/surfaces/cpp",
+    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
diff --git a/services/ui/launcher/README.md b/services/ui/launcher/README.md
new file mode 100644
index 0000000..eb4e776
--- /dev/null
+++ b/services/ui/launcher/README.md
@@ -0,0 +1,11 @@
+# Mozart Launcher
+
+This directory contains the Launcher, a simple tool for hosting a View
+inside of the NativeViewport.
+
+In other words, use this to run UI applications which implement the
+ViewProvider interface and which register their views with the ViewManager.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher <app url>"
diff --git a/services/ui/launcher/launcher_app.cc b/services/ui/launcher/launcher_app.cc
new file mode 100644
index 0000000..07e796f
--- /dev/null
+++ b/services/ui/launcher/launcher_app.cc
@@ -0,0 +1,128 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/trace_event.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/services/surfaces/cpp/surfaces_utils.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "services/ui/launcher/launcher_app.h"
+#include "services/ui/launcher/launcher_view_tree.h"
+
+namespace launcher {
+
+LauncherApp::LauncherApp()
+    : app_impl_(nullptr), viewport_event_dispatcher_binding_(this) {}
+
+LauncherApp::~LauncherApp() {}
+
+void LauncherApp::Initialize(mojo::ApplicationImpl* app) {
+  app_impl_ = app;
+  tracing_.Initialize(app);
+  TRACE_EVENT0("launcher", __func__);
+
+  if (app->args().size() != 2) {
+    LOG(ERROR) << "Invalid arguments.\n\n"
+                  "Usage: mojo_shell \"mojo:launcher <app url>\"";
+    app->Terminate();
+    return;
+  }
+
+  InitViewport();
+  LaunchClient(app->args()[1]);
+}
+
+void LauncherApp::InitViewport() {
+  app_impl_->ConnectToService("mojo:native_viewport_service",
+                              &viewport_service_);
+  viewport_service_.set_connection_error_handler(base::Bind(
+      &LauncherApp::OnViewportConnectionError, base::Unretained(this)));
+
+  mojo::NativeViewportEventDispatcherPtr dispatcher;
+  viewport_event_dispatcher_binding_.Bind(GetProxy(&dispatcher));
+  viewport_service_->SetEventDispatcher(dispatcher.Pass());
+
+  // Match the Nexus 5 aspect ratio initially.
+  auto size = mojo::Size::New();
+  size->width = 320;
+  size->height = 640;
+
+  auto requested_configuration = mojo::SurfaceConfiguration::New();
+  viewport_service_->Create(
+      size.Clone(), requested_configuration.Pass(),
+      base::Bind(&LauncherApp::OnViewportCreated, base::Unretained(this)));
+}
+
+void LauncherApp::OnViewportConnectionError() {
+  LOG(ERROR) << "Exiting due to viewport connection error.";
+  app_impl_->Terminate();
+}
+
+void LauncherApp::OnViewportCreated(mojo::ViewportMetricsPtr metrics) {
+  viewport_service_->Show();
+  mojo::ContextProviderPtr context_provider;
+  viewport_service_->GetContextProvider(GetProxy(&context_provider));
+
+  mojo::DisplayFactoryPtr display_factory;
+  app_impl_->ConnectToService("mojo:surfaces_service", &display_factory);
+
+  mojo::DisplayPtr display;
+  display_factory->Create(context_provider.Pass(), nullptr, GetProxy(&display));
+
+  view_tree_.reset(
+      new LauncherViewTree(app_impl_, display.Pass(), metrics.Pass()));
+  UpdateClientView();
+  RequestUpdatedViewportMetrics();
+}
+
+void LauncherApp::OnViewportMetricsChanged(mojo::ViewportMetricsPtr metrics) {
+  if (view_tree_) {
+    view_tree_->SetViewportMetrics(metrics.Pass());
+    RequestUpdatedViewportMetrics();
+  }
+}
+
+void LauncherApp::RequestUpdatedViewportMetrics() {
+  viewport_service_->RequestMetrics(base::Bind(
+      &LauncherApp::OnViewportMetricsChanged, base::Unretained(this)));
+}
+
+void LauncherApp::OnEvent(mojo::EventPtr event,
+                          const mojo::Callback<void()>& callback) {
+  if (view_tree_)
+    view_tree_->DispatchEvent(event.Pass());
+  callback.Run();
+}
+
+void LauncherApp::LaunchClient(std::string app_url) {
+  DVLOG(1) << "Launching " << app_url;
+
+  app_impl_->ConnectToService(app_url, &client_view_provider_);
+  client_view_provider_.set_connection_error_handler(base::Bind(
+      &LauncherApp::OnClientConnectionError, base::Unretained(this)));
+
+  client_view_provider_->CreateView(
+      nullptr, nullptr,
+      base::Bind(&LauncherApp::OnClientViewCreated, base::Unretained(this)));
+}
+
+void LauncherApp::OnClientConnectionError() {
+  LOG(ERROR) << "Exiting due to client application connection error.";
+  app_impl_->Terminate();
+}
+
+void LauncherApp::OnClientViewCreated(mojo::ui::ViewTokenPtr view_token) {
+  client_view_token_ = view_token.Pass();
+  UpdateClientView();
+}
+
+void LauncherApp::UpdateClientView() {
+  if (view_tree_)
+    view_tree_->SetRoot(client_view_token_.Clone());
+}
+
+}  // namespace launcher
diff --git a/services/ui/launcher/launcher_app.h b/services/ui/launcher/launcher_app.h
new file mode 100644
index 0000000..d614956
--- /dev/null
+++ b/services/ui/launcher/launcher_app.h
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
+#define SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
+
+#include <memory>
+
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+
+namespace launcher {
+
+class LauncherViewTree;
+
+class LauncherApp : public mojo::ApplicationDelegate,
+                    public mojo::NativeViewportEventDispatcher {
+ public:
+  LauncherApp();
+  ~LauncherApp() override;
+
+ private:
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app) override;
+
+  // |NativeViewportEventDispatcher|:
+  void OnEvent(mojo::EventPtr event,
+               const mojo::Callback<void()>& callback) override;
+
+  void InitViewport();
+  void OnViewportConnectionError();
+  void OnViewportCreated(mojo::ViewportMetricsPtr metrics);
+  void OnViewportMetricsChanged(mojo::ViewportMetricsPtr metrics);
+  void RequestUpdatedViewportMetrics();
+
+  void OnViewManagerConnectionError();
+
+  void LaunchClient(std::string app_url);
+  void OnClientConnectionError();
+  void OnClientViewCreated(mojo::ui::ViewTokenPtr view_token);
+
+  void UpdateClientView();
+
+  mojo::ApplicationImpl* app_impl_;
+  mojo::TracingImpl tracing_;
+
+  mojo::NativeViewportPtr viewport_service_;
+  mojo::Binding<NativeViewportEventDispatcher>
+      viewport_event_dispatcher_binding_;
+
+  std::unique_ptr<LauncherViewTree> view_tree_;
+
+  mojo::ui::ViewProviderPtr client_view_provider_;
+  mojo::ui::ViewTokenPtr client_view_token_;
+
+  DISALLOW_COPY_AND_ASSIGN(LauncherApp);
+};
+
+}  // namespace launcher
+
+#endif  // SERVICES_UI_LAUNCHER_LAUNCHER_APP_H_
diff --git a/services/ui/launcher/launcher_view_tree.cc b/services/ui/launcher/launcher_view_tree.cc
new file mode 100644
index 0000000..052b3c0
--- /dev/null
+++ b/services/ui/launcher/launcher_view_tree.cc
@@ -0,0 +1,159 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "services/ui/launcher/launcher_view_tree.h"
+
+namespace launcher {
+
+LauncherViewTree::LauncherViewTree(mojo::ApplicationImpl* app_impl,
+                                   mojo::DisplayPtr display,
+                                   mojo::ViewportMetricsPtr viewport_metrics)
+    : display_(display.Pass()),
+      viewport_metrics_(viewport_metrics.Pass()),
+      binding_(this),
+      root_key_(0),
+      frame_scheduled_(false),
+      frame_pending_(false) {
+  app_impl->ConnectToService("mojo:view_manager_service", &view_manager_);
+  view_manager_.set_connection_error_handler(base::Bind(
+      &LauncherViewTree::OnViewManagerConnectionError, base::Unretained(this)));
+
+  mojo::ui::ViewTreePtr view_tree;
+  binding_.Bind(mojo::GetProxy(&view_tree));
+  view_manager_->RegisterViewTree(
+      view_tree.Pass(), mojo::GetProxy(&view_tree_host_),
+      base::Bind(&LauncherViewTree::OnViewTreeRegistered,
+                 base::Unretained(this)));
+
+  ScheduleFrame();
+}
+
+LauncherViewTree::~LauncherViewTree() {}
+
+void LauncherViewTree::SetRoot(mojo::ui::ViewTokenPtr token) {
+  root_ = token.Pass();
+  if (root_)
+    view_tree_host_->SetRoot(++root_key_, root_.Clone());
+  else
+    view_tree_host_->ResetRoot();
+  root_layout_info_.reset();
+}
+
+void LauncherViewTree::SetViewportMetrics(
+    mojo::ViewportMetricsPtr viewport_metrics) {
+  viewport_metrics_ = viewport_metrics.Pass();
+  view_tree_host_->RequestLayout();
+}
+
+void LauncherViewTree::DispatchEvent(mojo::EventPtr event) {
+  // TODO(jeffbrown): Support input dispatch.
+}
+
+void LauncherViewTree::OnViewManagerConnectionError() {
+  LOG(ERROR) << "View manager connection error.";
+}
+
+void LauncherViewTree::OnViewTreeRegistered() {}
+
+void LauncherViewTree::OnLayout(const OnLayoutCallback& callback) {
+  LayoutRoot();
+  callback.Run();
+}
+
+void LauncherViewTree::OnRootUnavailable(
+    uint32_t root_key,
+    const OnRootUnavailableCallback& callback) {
+  if (root_key_ == root_key) {
+    // TODO(jeffbrown): We should probably shut down the launcher.
+    LOG(ERROR) << "Root view terminated unexpectedly.";
+    SetRoot(mojo::ui::ViewTokenPtr());
+  }
+  callback.Run();
+}
+
+void LauncherViewTree::LayoutRoot() {
+  if (!root_)
+    return;
+
+  auto params = mojo::ui::ViewLayoutParams::New();
+  params->constraints = mojo::ui::BoxConstraints::New();
+  params->constraints->min_width = viewport_metrics_->size->width;
+  params->constraints->max_width = viewport_metrics_->size->width;
+  params->constraints->min_height = viewport_metrics_->size->height;
+  params->constraints->max_height = viewport_metrics_->size->height;
+  params->device_pixel_ratio = viewport_metrics_->device_pixel_ratio;
+  view_tree_host_->LayoutRoot(
+      params.Pass(),
+      base::Bind(&LauncherViewTree::OnLayoutResult, base::Unretained(this)));
+}
+
+void LauncherViewTree::OnLayoutResult(mojo::ui::ViewLayoutInfoPtr info) {
+  if (!info) {
+    DVLOG(1) << "Root layout: <stale>";
+    return;
+  }
+
+  DVLOG(1) << "Root layout: size.width=" << info->size->width
+           << ", size.height=" << info->size->height
+           << ", surface_id.id_namespace=" << info->surface_id->id_namespace
+           << ", surface_id.local=" << info->surface_id->local;
+
+  root_layout_info_ = info.Pass();
+  ScheduleFrame();
+}
+
+void LauncherViewTree::ScheduleFrame() {
+  frame_scheduled_ = true;
+  FinishFrame();
+}
+
+void LauncherViewTree::FinishFrame() {
+  if (!frame_scheduled_ || frame_pending_)
+    return;
+  frame_scheduled_ = false;
+
+  mojo::FramePtr frame = mojo::Frame::New();
+  frame->resources.resize(0u);
+
+  mojo::Rect bounds;
+  bounds.width = viewport_metrics_->size->width;
+  bounds.height = viewport_metrics_->size->height;
+  mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
+  pass->shared_quad_states.push_back(
+      mojo::CreateDefaultSQS(*viewport_metrics_->size));
+
+  mojo::QuadPtr quad = mojo::Quad::New();
+  quad->rect = bounds.Clone();
+  quad->opaque_rect = bounds.Clone();
+  quad->visible_rect = bounds.Clone();
+  quad->shared_quad_state_index = 0u;
+
+  if (root_layout_info_) {
+    quad->material = mojo::Material::SURFACE_CONTENT;
+    quad->surface_quad_state = mojo::SurfaceQuadState::New();
+    quad->surface_quad_state->surface = root_layout_info_->surface_id.Clone();
+  } else {
+    quad->material = mojo::Material::SOLID_COLOR;
+    quad->solid_color_quad_state = mojo::SolidColorQuadState::New();
+    quad->solid_color_quad_state->color = mojo::Color::New();
+    quad->solid_color_quad_state->color->rgba = 0xffff0000;
+  }
+
+  pass->quads.push_back(quad.Pass());
+  frame->passes.push_back(pass.Pass());
+
+  frame_pending_ = true;
+  display_->SubmitFrame(
+      frame.Pass(),
+      base::Bind(&LauncherViewTree::OnFrameSubmitted, base::Unretained(this)));
+}
+
+void LauncherViewTree::OnFrameSubmitted() {
+  DCHECK(frame_pending_);
+  frame_pending_ = false;
+  FinishFrame();
+}
+
+}  // namespace launcher
diff --git a/services/ui/launcher/launcher_view_tree.h b/services/ui/launcher/launcher_view_tree.h
new file mode 100644
index 0000000..58ab563
--- /dev/null
+++ b/services/ui/launcher/launcher_view_tree.h
@@ -0,0 +1,67 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
+#define SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
+
+#include "base/macros.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.h"
+#include "mojo/services/surfaces/cpp/surfaces_utils.h"
+#include "mojo/services/surfaces/interfaces/display.mojom.h"
+#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+
+namespace launcher {
+
+class LauncherViewTree : public mojo::ui::ViewTree {
+ public:
+  LauncherViewTree(mojo::ApplicationImpl* app_impl,
+                   mojo::DisplayPtr display,
+                   mojo::ViewportMetricsPtr viewport_metrics);
+
+  ~LauncherViewTree() override;
+
+  void SetRoot(mojo::ui::ViewTokenPtr token);
+  void SetViewportMetrics(mojo::ViewportMetricsPtr viewport_metrics);
+  void DispatchEvent(mojo::EventPtr event);
+
+ private:
+  // |ViewTree|:
+  void OnLayout(const OnLayoutCallback& callback) override;
+  void OnRootUnavailable(uint32_t root_key,
+                         const OnRootUnavailableCallback& callback) override;
+
+  void OnViewManagerConnectionError();
+  void OnViewTreeRegistered();
+
+  void LayoutRoot();
+  void OnLayoutResult(mojo::ui::ViewLayoutInfoPtr info);
+
+  void ScheduleFrame();
+  void FinishFrame();
+  void OnFrameSubmitted();
+
+  mojo::ui::ViewManagerPtr view_manager_;
+  mojo::DisplayPtr display_;
+  mojo::ViewportMetricsPtr viewport_metrics_;
+  mojo::Binding<mojo::ui::ViewTree> binding_;
+
+  mojo::ui::ViewTreeHostPtr view_tree_host_;
+
+  mojo::ui::ViewTokenPtr root_;
+  uint32_t root_key_;
+  mojo::ui::ViewLayoutInfoPtr root_layout_info_;
+
+  bool frame_scheduled_;
+  bool frame_pending_;
+
+  DISALLOW_COPY_AND_ASSIGN(LauncherViewTree);
+};
+
+}  // namespace launcher
+
+#endif  // SERVICES_UI_LAUNCHER_VIEW_TREE_IMPL_H_
diff --git a/services/ui/launcher/main.cc b/services/ui/launcher/main.cc
new file mode 100644
index 0000000..fa82e08
--- /dev/null
+++ b/services/ui/launcher/main.cc
@@ -0,0 +1,12 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+#include "services/ui/launcher/launcher_app.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new launcher::LauncherApp);
+  return runner.Run(application_request);
+}