Port Moterm to Mozart.

Much of the glue code has been eliminated by use of the new UI helpers.

BUG=
R=abarth@google.com, viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/1556683004 .
diff --git a/apps/BUILD.gn b/apps/BUILD.gn
index 4765a5f..7f482c9 100644
--- a/apps/BUILD.gn
+++ b/apps/BUILD.gn
@@ -18,6 +18,8 @@
     ":portable_apps",
     "//apps/benchmark",
     "//apps/benchmark:apptests",
+    "//apps/moterm",
+    "//apps/moterm:apptests",
   ]
 
   if (is_android) {
diff --git a/apps/moterm/BUILD.gn b/apps/moterm/BUILD.gn
index a69461c..73b5dc9 100644
--- a/apps/moterm/BUILD.gn
+++ b/apps/moterm/BUILD.gn
@@ -6,48 +6,40 @@
 
 mojo_native_application("moterm") {
   sources = [
-    "moterm_main.cc",
+    "main.cc",
+    "moterm_app.cc",
+    "moterm_app.h",
     "moterm_view.cc",
     "moterm_view.h",
   ]
 
   deps = [
     ":driver",
-    ":gl_helper",
     ":key_util",
     ":model",
     "//base",
     "//mojo/application",
     "//mojo/common",
+    "//mojo/public/c/gpu",
     "//mojo/public/c/system",
     "//mojo/public/cpp/application",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/bindings:callback",
     "//mojo/services/files/interfaces",
-    "//mojo/services/surfaces/interfaces:surface_id",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/terminal/interfaces",
-    "//mojo/services/view_manager/cpp",
+    "//mojo/services/ui/input/interfaces",
+    "//mojo/services/ui/views/interfaces",
+    "//mojo/skia",
+    "//mojo/ui",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
     "//skia",
     "//third_party/dejavu-fonts-ttf-2.34:DejaVuSansMonoRegular",
   ]
 }
 
-mojo_native_application("gl_helper_test_app") {
-  sources = [
-    "gl_helper_test_app.cc",
-  ]
-
-  deps = [
-    ":gl_helper",
-    "//base",
-    "//mojo/application",
-    "//mojo/services/geometry/interfaces",
-    "//mojo/services/gpu/interfaces",
-    "//mojo/services/native_viewport/interfaces",
-    "//mojo/services/surfaces/interfaces",
-  ]
-}
-
 source_set("driver") {
   sources = [
     "moterm_driver.cc",
@@ -61,33 +53,6 @@
   ]
 }
 
-source_set("gl_helper") {
-  sources = [
-    "gl_helper.cc",
-    "gl_helper.h",
-  ]
-
-  deps = [
-    "//mojo/public/cpp/application",
-    "//mojo/public/c/gpu",
-    "//mojo/public/interfaces/application",
-    "//mojo/services/gpu/interfaces",
-    "//mojo/services/surfaces/interfaces",
-  ]
-
-  public_deps = [
-    "//base",
-    "//mojo/public/c/gpu",
-    "//mojo/public/c/gpu:GLES2",
-    "//mojo/services/geometry/cpp",
-    "//mojo/services/geometry/interfaces",
-    "//mojo/services/gpu/interfaces",
-    "//mojo/services/surfaces/cpp",
-    "//mojo/services/surfaces/interfaces",
-    "//mojo/services/surfaces/interfaces:surface_id",
-  ]
-}
-
 source_set("key_util") {
   sources = [
     "key_util.cc",
@@ -125,7 +90,6 @@
   testonly = true
 
   sources = [
-    "gl_helper_unittest.cc",
     "key_util_unittest.cc",
     "moterm_driver_unittest.cc",
     "moterm_model_unittest.cc",
@@ -133,7 +97,6 @@
 
   deps = [
     ":driver",
-    ":gl_helper",
     ":key_util",
     ":model",
     "//mojo/application",
diff --git a/apps/moterm/gl_helper.cc b/apps/moterm/gl_helper.cc
deleted file mode 100644
index f1e2cb2..0000000
--- a/apps/moterm/gl_helper.cc
+++ /dev/null
@@ -1,327 +0,0 @@
-// 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 "apps/moterm/gl_helper.h"
-
-#ifndef GL_GLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2extmojo.h>
-#include <MGL/mgl.h>
-
-#include "base/bind.h"
-#include "base/location.h"
-#include "base/message_loop/message_loop.h"
-#include "base/task_runner.h"
-#include "mojo/public/cpp/application/connect.h"
-#include "mojo/public/interfaces/application/shell.mojom.h"
-#include "mojo/services/geometry/cpp/geometry_util.h"
-#include "mojo/services/surfaces/cpp/surfaces_utils.h"
-
-// Maximum number of (live) textures to keep around.
-const size_t kMaxTextures = 10;
-
-GlHelper::GlHelper(Client* client,
-                   mojo::Shell* shell,
-                   GLint texture_format,
-                   bool flipped,
-                   const mojo::Size& initial_size)
-    : client_(client),
-      texture_format_(texture_format),
-      flipped_(flipped),
-      returner_binding_(this),
-      next_surface_size_(initial_size),
-      mgl_context_(MGL_NO_CONTEXT),
-      next_frame_id_(0),
-      frame_texture_(0),
-      id_namespace_(0),
-      local_id_(0),
-      next_resource_id_(0),
-      weak_factory_(this) {
-  mojo::ServiceProviderPtr native_viewport_service_provider;
-  shell->ConnectToApplication("mojo:native_viewport_service",
-                              GetProxy(&native_viewport_service_provider),
-                              nullptr);
-  mojo::ConnectToService(native_viewport_service_provider.get(), &gpu_);
-
-  mojo::ServiceProviderPtr surfaces_service_provider;
-  shell->ConnectToApplication("mojo:surfaces_service",
-                              GetProxy(&surfaces_service_provider), nullptr);
-  mojo::ConnectToService(surfaces_service_provider.get(), &surface_);
-  surface_->GetIdNamespace(base::Bind(&GlHelper::GetIdNamespaceCallback,
-                                      weak_factory_.GetWeakPtr()));
-  mojo::ResourceReturnerPtr returner_ptr;
-  returner_binding_.Bind(GetProxy(&returner_ptr));
-  surface_->SetResourceReturner(returner_ptr.Pass());
-}
-
-GlHelper::~GlHelper() {
-  DCHECK(!frame_texture_);
-  if (mgl_context_ != MGL_NO_CONTEXT)
-    MGLDestroyContext(mgl_context_);
-}
-
-void GlHelper::SetSurfaceSize(const mojo::Size& surface_size) {
-  next_surface_size_ = surface_size;
-}
-
-void GlHelper::MakeCurrent() {
-  EnsureContext();
-}
-
-void GlHelper::StartFrame() {
-  DCHECK(!frame_texture_);
-
-  EnsureContext();
-  EnsureSurface();
-
-  TextureInfo texture_info = GetTexture();
-  DCHECK(texture_info.texture);
-  frame_texture_ = texture_info.texture;
-
-  // It's already bound.
-  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-}
-
-uint32_t GlHelper::EndFrame() {
-  DCHECK(frame_texture_);
-
-  mojo::Rect size_rect;
-  size_rect.width = current_surface_size_.width;
-  size_rect.height = current_surface_size_.height;
-
-  GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
-  glGenMailboxCHROMIUM(mailbox);
-  glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox);
-  GLuint sync_point = glInsertSyncPointCHROMIUM();
-
-  mojo::FramePtr frame = mojo::Frame::New();
-
-  // Frame resources:
-  frame->resources.push_back(mojo::TransferableResource::New());
-  mojo::TransferableResource* resource = frame->resources[0].get();
-  resource->id = next_resource_id_++;
-  textures_pending_return_.push_back(
-      TextureInfo(resource->id, frame_texture_, current_surface_size_));
-  frame_texture_ = 0;
-  // TODO(vtl): This is wrong, but doesn't seem to have an effect.
-  resource->format = mojo::ResourceFormat::RGBA_8888;
-  resource->filter = GL_LINEAR;
-  resource->size = current_surface_size_.Clone();
-  mojo::MailboxHolderPtr mailbox_holder = mojo::MailboxHolder::New();
-  mailbox_holder->mailbox = mojo::Mailbox::New();
-  for (int i = 0; i < GL_MAILBOX_SIZE_CHROMIUM; ++i)
-    mailbox_holder->mailbox->name.push_back(mailbox[i]);
-  mailbox_holder->texture_target = GL_TEXTURE_2D;
-  mailbox_holder->sync_point = sync_point;
-  resource->mailbox_holder = mailbox_holder.Pass();
-  resource->is_repeated = false;
-  resource->is_software = false;
-
-  // Frame passes:
-  frame->passes.push_back(mojo::CreateDefaultPass(1, size_rect));
-  mojo::Pass* pass = frame->passes[0].get();
-  pass->quads.push_back(mojo::Quad::New());
-  mojo::Quad* quad = pass->quads[0].get();
-  quad->material = mojo::Material::TEXTURE_CONTENT;
-  quad->rect = size_rect.Clone();
-  quad->opaque_rect = size_rect.Clone();
-  quad->visible_rect = size_rect.Clone();
-  quad->needs_blending = true;
-  quad->shared_quad_state_index = 0;
-  quad->texture_quad_state = mojo::TextureQuadState::New();
-  mojo::TextureQuadState* texture_state = quad->texture_quad_state.get();
-  texture_state->resource_id = resource->id;
-  texture_state->premultiplied_alpha = true;
-  texture_state->uv_top_left = mojo::PointF::New();
-  texture_state->uv_bottom_right = mojo::PointF::New();
-  texture_state->uv_bottom_right->x = 1.f;
-  texture_state->uv_bottom_right->y = 1.f;
-  texture_state->background_color = mojo::Color::New();
-  texture_state->background_color->rgba = 0;
-  for (int i = 0; i < 4; ++i)
-    texture_state->vertex_opacity.push_back(1.f);
-  texture_state->flipped = flipped_;
-  pass->shared_quad_states.push_back(
-      mojo::CreateDefaultSQS(current_surface_size_));
-
-  surface_->SubmitFrame(local_id_, frame.Pass(),
-                        base::Bind(&GlHelper::SubmitFrameCallback,
-                                   weak_factory_.GetWeakPtr(), next_frame_id_));
-
-  return next_frame_id_++;
-}
-
-GLuint GlHelper::GetFrameTexture() {
-  DCHECK(frame_texture_);
-  return frame_texture_;
-}
-
-void GlHelper::ReturnResources(
-    mojo::Array<mojo::ReturnedResourcePtr> resources) {
-  DCHECK(!frame_texture_);
-
-  if (mgl_context_ == MGL_NO_CONTEXT) {
-    DCHECK(textures_pending_return_.empty());
-    return;
-  }
-
-  MGLMakeCurrent(mgl_context_);
-
-  // Note: This quadratic nested loop is OK, since we expect both |resources|
-  // and |textures_pending_return_| to be small (and |resources| should
-  // usually have just a single element).
-  for (size_t i = 0; i < resources.size(); ++i) {
-    mojo::ReturnedResourcePtr resource = resources[i].Pass();
-    DCHECK_EQ(resource->count, 1);
-
-    bool found = false;
-    for (size_t j = 0; j < textures_pending_return_.size(); j++) {
-      const TextureInfo& texture_info = textures_pending_return_[j];
-      if (texture_info.resource_id == resource->id) {
-        glWaitSyncPointCHROMIUM(resource->sync_point);
-        ReturnTexture(texture_info);
-        textures_pending_return_.erase(textures_pending_return_.begin() + j);
-        found = true;
-        break;
-      }
-    }
-    if (!found) {
-      // If we don't texture ID for it, assume we lost the context.
-      // TODO(vtl): This may leak (but currently we don't know if the texture is
-      // still valid).
-      DVLOG(1) << "Returned texture not found (context lost?)";
-    }
-  }
-}
-
-void GlHelper::EnsureContext() {
-  DCHECK(!frame_texture_);
-
-  if (mgl_context_ == MGL_NO_CONTEXT) {
-    DCHECK(textures_pending_return_.empty());
-
-    mojo::CommandBufferPtr command_buffer;
-    gpu_->CreateOffscreenGLES2Context(mojo::GetProxy(&command_buffer));
-    mgl_context_ = MGLCreateContext(
-        MGL_API_VERSION_GLES2,
-        command_buffer.PassInterface().PassHandle().release().value(), nullptr,
-        &GlHelper::OnContextLostThunk, this,
-        mojo::Environment::GetDefaultAsyncWaiter());
-    CHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-  }
-
-  MGLMakeCurrent(mgl_context_);
-}
-
-void GlHelper::EnsureSurface() {
-  DCHECK(!frame_texture_);
-  DCHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-
-  if (local_id_) {
-    if (current_surface_size_ == next_surface_size_)
-      return;
-
-    surface_->DestroySurface(local_id_);
-
-    ClearTextures();
-  }
-
-  local_id_++;
-  surface_->CreateSurface(local_id_);
-  current_surface_size_ = next_surface_size_;
-  if (id_namespace_) {
-    // Don't call the client in the nested context.
-    base::MessageLoop::current()->task_runner()->PostTask(
-        FROM_HERE, base::Bind(&GlHelper::CallOnSurfaceIdChanged,
-                              weak_factory_.GetWeakPtr()));
-  }
-}
-
-void GlHelper::CallOnSurfaceIdChanged() {
-  DCHECK(id_namespace_ && local_id_);
-
-  auto qualified_id = mojo::SurfaceId::New();
-  qualified_id->id_namespace = id_namespace_;
-  qualified_id->local = local_id_;
-  client_->OnSurfaceIdChanged(qualified_id.Pass());
-}
-
-GlHelper::TextureInfo GlHelper::GetTexture() {
-  DCHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-
-  if (!textures_.empty()) {
-    TextureInfo rv = textures_.front();
-    DCHECK(rv.size == current_surface_size_);
-    textures_.pop_front();
-    glBindTexture(GL_TEXTURE_2D, rv.texture);
-    return rv;
-  }
-
-  GLuint texture = 0;
-  glGenTextures(1, &texture);
-  DCHECK(texture);
-  glBindTexture(GL_TEXTURE_2D, texture);
-  glTexImage2D(GL_TEXTURE_2D, 0, texture_format_, current_surface_size_.width,
-               current_surface_size_.height, 0, texture_format_,
-               GL_UNSIGNED_BYTE, nullptr);
-
-  return TextureInfo(0, texture, current_surface_size_);
-}
-
-void GlHelper::ReturnTexture(const TextureInfo& texture_info) {
-  DCHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-  DCHECK_NE(texture_info.texture, 0u);
-
-  if (texture_info.size == current_surface_size_ &&
-      textures_.size() < kMaxTextures)
-    textures_.push_back(texture_info);  // TODO(vtl): Is |push_front()| better?
-  else
-    glDeleteTextures(1, &texture_info.texture);
-}
-
-void GlHelper::ClearTextures() {
-  DCHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-
-  for (const auto& texture_info : textures_)
-    glDeleteTextures(1, &texture_info.texture);
-
-  textures_.clear();
-}
-
-void GlHelper::GetIdNamespaceCallback(uint32_t id_namespace) {
-  id_namespace_ = id_namespace;
-  if (local_id_) {
-    // We're in a callback, so we can just call the client directly.
-    CallOnSurfaceIdChanged();
-  }
-}
-
-// static
-void GlHelper::OnContextLostThunk(void* self) {
-  static_cast<GlHelper*>(self)->OnContextLost();
-}
-
-void GlHelper::OnContextLost() {
-  // We shouldn't get this while we're processing a frame.
-  DCHECK(!frame_texture_);
-
-  DCHECK_NE(mgl_context_, MGL_NO_CONTEXT);
-  MGLDestroyContext(mgl_context_);
-  mgl_context_ = MGL_NO_CONTEXT;
-
-  // TODO(vtl): We don't know if any of those textures will be valid when
-  // returned (if they are), so assume they aren't.
-  textures_pending_return_.clear();
-
-  // We're in a callback, so we can just call the client directly.
-  client_->OnContextLost();
-}
-
-void GlHelper::SubmitFrameCallback(uint32_t frame_id) {
-  // We're in a callback, so we can just call the client directly.
-  client_->OnFrameDisplayed(frame_id);
-}
diff --git a/apps/moterm/gl_helper.h b/apps/moterm/gl_helper.h
deleted file mode 100644
index a181815..0000000
--- a/apps/moterm/gl_helper.h
+++ /dev/null
@@ -1,170 +0,0 @@
-// 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 APPS_MOTERM_GL_HELPER_H_
-#define APPS_MOTERM_GL_HELPER_H_
-
-#include <GLES2/gl2.h>
-#include <MGL/mgl_types.h>
-
-#include <deque>
-#include <vector>
-
-#include "base/macros.h"
-#include "base/memory/weak_ptr.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/services/geometry/interfaces/geometry.mojom.h"
-#include "mojo/services/gpu/interfaces/gpu.mojom.h"
-#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
-
-namespace mojo {
-class Shell;
-}
-
-// A class that helps with drawing surfaces using GL.
-class GlHelper : public mojo::ResourceReturner {
- public:
-  class Client {
-   public:
-    // Called when the surface ID changes (including the first time it becomes
-    // available).
-    virtual void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) = 0;
-
-    // Called when the GL context is lost.
-    virtual void OnContextLost() = 0;
-
-    // Called when the frame with ID |frame_id| (from |EndFrame()|) is drawn for
-    // the first time. Use this to rate-limit draws.
-    virtual void OnFrameDisplayed(uint32_t frame_id) = 0;
-  };
-
-  // Both |client| and |shell| must outlive us. |texture_format| is the texture
-  // format to use, e.g., |GL_RGBA| or |GL_BGRA_EXT|. |flipped| means that the
-  // texture is in the usual GL orientation (origin at lower-left). This object
-  // will create a surface (of initial size |initial_size|), and call the
-  // client's |OnSurfaceIdChanged()| when it has a surface ID for it (which the
-  // client can then provide to its |View|).
-  GlHelper(Client* client,
-           mojo::Shell* shell,
-           GLint texture_format,
-           bool flipped,
-           const mojo::Size& initial_size);
-  ~GlHelper() override;
-
-  // Sets the size of the surface. (The surface will only be created at this
-  // size lazily at the next |StartFrame()|.)
-  void SetSurfaceSize(const mojo::Size& surface_size);
-
-  // Ensures that a GL context is available and makes it current. (Note that
-  // this is automatically called by |StartFrame()|, so this is mostly useful
-  // for creating GL resources outside |StartFrame()|/|EndFrame()|.
-  void MakeCurrent();
-
-  // Starts a frame; makes an appropriate GL context current, and binds a
-  // texture of the current size (creating it if necessary), returning its ID.
-  // Between |StartFrame()| and |EndFrame()|, the run loop must not be run and
-  // no other methods of this object other than |MakeCurrent()| and
-  // |GetFrameTexture()| may be called. This object must not be destroyed after
-  // |StartFrame()| before calling |EndFrame()|.
-  void StartFrame();
-  // Completes the current frame (started using |StartFrame()|). Before calling
-  // this, the frame's texture must be bound and the GL context must be current.
-  // (This is the default state after |StartFrame()|, so nothing has to be done
-  // unless another texture is bound or another GL context is made current,
-  // respectively.) Returns an ID for this frame (which will be given to the
-  // client on |OnFrameDisplayed()|).
-  uint32_t EndFrame();
-
-  // Gets the texture that will be used for the current frame, e.g., so that it
-  // may be (re)bound. Only valid between |StartFrame()| and |EndFrame()|. (Note
-  // that this texture is automatically bound by |StartFrame()|, so this is
-  // typically only needed if another texture is bound.)
-  GLuint GetFrameTexture();
-
- private:
-  struct TextureInfo {
-    TextureInfo(uint32_t resource_id, GLuint texture, const mojo::Size& size)
-        : resource_id(resource_id), texture(texture), size(size) {}
-
-    // Only interesting if it's pending return.
-    uint32_t resource_id;
-    GLuint texture;
-    mojo::Size size;
-  };
-
-  // |mojo::ResourceReturner|:
-  void ReturnResources(
-      mojo::Array<mojo::ReturnedResourcePtr> resources) override;
-
-  // Ensures that we have a GL context and that it is current.
-  void EnsureContext();
-
-  // Ensures that we have a surface of the appropriate size. This should only be
-  // called with a valid GL context which is current (e.g., after calling
-  // |EnsureContext()|).
-  void EnsureSurface();
-
-  // Calls the client's |OnSurfaceIdChanged()| if appropriate (both
-  // |id_namespace_| and |local_id_| must be set).
-  void CallOnSurfaceIdChanged();
-
-  // Texture queue functions. (For all functions, |mgl_context_| should be
-  // current.)
-  // Gets and binds a texture of size |current_surface_size_|.
-  TextureInfo GetTexture();
-  // Returns a texture to the queue (or deletes it, if it's not of the right
-  // size or there are already enough textures in the queue).
-  void ReturnTexture(const TextureInfo& texture_info);
-  // Clears all textures (i.e., calls |glDeleteTextures()| on all textures in
-  // the texture queue).
-  void ClearTextures();
-
-  // Callbacks:
-
-  // Callback for |GetIdNamespace()|:
-  void GetIdNamespaceCallback(uint32_t id_namespace);
-
-  // "Callback" for |MojoGLES2CreateContext()|:
-  static void OnContextLostThunk(void* self);
-  void OnContextLost();
-
-  // Callback for |SubmitFrame()|:
-  void SubmitFrameCallback(uint32_t frame_id);
-
-  Client* const client_;
-  const GLint texture_format_;
-  const bool flipped_;
-
-  mojo::GpuPtr gpu_;
-  mojo::SurfacePtr surface_;
-  mojo::Binding<mojo::ResourceReturner> returner_binding_;
-
-  // The size for the surface at the next |StartFrame()|.
-  mojo::Size next_surface_size_;
-
-  MGLContext mgl_context_;
-
-  std::deque<TextureInfo> textures_;
-
-  uint32_t next_frame_id_;
-  // The texture that'll be used to draw the current frame. Only valid (nonzero)
-  // between |StartFrame()| and |EndFrame()|.
-  GLuint frame_texture_;
-
-  uint32_t id_namespace_;
-  uint32_t local_id_;
-  // If |local_id_| is nonzero, there's currently a surface, in which case this
-  // is its size.
-  mojo::Size current_surface_size_;
-
-  uint32_t next_resource_id_;
-  std::vector<TextureInfo> textures_pending_return_;
-
-  base::WeakPtrFactory<GlHelper> weak_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(GlHelper);
-};
-
-#endif  // APPS_MOTERM_GL_HELPER_H_
diff --git a/apps/moterm/gl_helper_test_app.cc b/apps/moterm/gl_helper_test_app.cc
deleted file mode 100644
index 7147f6d..0000000
--- a/apps/moterm/gl_helper_test_app.cc
+++ /dev/null
@@ -1,279 +0,0 @@
-// 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.
-
-// This is a test application for gl_helper.*, which draws directly to a native
-// viewport (without using the view manager).
-
-#include <GLES2/gl2.h>
-#include <math.h>
-
-#include "apps/moterm/gl_helper.h"
-#include "base/bind.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "mojo/application/application_runner_chromium.h"
-#include "mojo/public/c/system/main.h"
-#include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/services/geometry/interfaces/geometry.mojom.h"
-#include "mojo/services/gpu/interfaces/context_provider.mojom.h"
-#include "mojo/services/native_viewport/interfaces/native_viewport.mojom.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"
-
-namespace {
-
-GLuint LoadShader(GLenum type, const char* shader_source) {
-  GLuint shader = glCreateShader(type);
-  CHECK(shader);
-  glShaderSource(shader, 1, &shader_source, nullptr);
-  glCompileShader(shader);
-  GLint compiled = 0;
-  glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
-  CHECK(compiled);
-  return shader;
-}
-
-class GlHelperExampleApp : public mojo::ApplicationDelegate,
-                           public GlHelper::Client {
- public:
-  GlHelperExampleApp()
-      : application_impl_(),
-        program_(0),
-        transform_location_(0),
-        scale_location_(0),
-        framebuffer_(0),
-        vertex_buffer_(0),
-        draw_number_(0) {}
-  ~GlHelperExampleApp() override {}
-
- private:
-  void OnViewportMetricsReceived(mojo::ViewportMetricsPtr metrics) {
-    DVLOG(1) << "Viewport metrics received: size = " << metrics->size->width
-             << "x" << metrics->size->height
-             << ", device_pixel_ratio = " << metrics->device_pixel_ratio;
-
-    if (metrics->size->width != viewport_size_.width ||
-        metrics->size->height != viewport_size_.height) {
-      viewport_size_ = *metrics->size;
-      gl_helper_->SetSurfaceSize(viewport_size_);
-    }
-
-    native_viewport_->RequestMetrics(
-        base::Bind(&GlHelperExampleApp::OnViewportMetricsReceived,
-                   base::Unretained(this)));
-  }
-
-  void InitGlState() {
-    static const char vertex_shader_source[] =
-        "attribute vec4 position;                        \n"
-        "uniform mat4 transform;                         \n"
-        "uniform vec4 scale;                             \n"
-        "void main() {                                   \n"
-        "  gl_Position = (position * transform) * scale; \n"
-        "}                                               \n";
-    static const char fragment_shader_source[] =
-        "precision mediump float;                   \n"
-        "void main() {                              \n"
-        "  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); \n"
-        "}                                          \n";
-    static const float vertices[] = {
-        0.f, 0.5f, -sqrtf(5.f) / 4.f, -0.25f, sqrtf(5.f) / 4.f, -0.25f};
-    GLuint vertex_shader = LoadShader(GL_VERTEX_SHADER, vertex_shader_source);
-    GLuint fragment_shader =
-        LoadShader(GL_FRAGMENT_SHADER, fragment_shader_source);
-    program_ = glCreateProgram();
-    CHECK(program_);
-    glAttachShader(program_, vertex_shader);
-    glAttachShader(program_, fragment_shader);
-    glBindAttribLocation(program_, kPositionLocation, "position");
-    glLinkProgram(program_);
-    glDeleteShader(vertex_shader);
-    glDeleteShader(fragment_shader);
-    GLint linked = 0;
-    glGetProgramiv(program_, GL_LINK_STATUS, &linked);
-    CHECK(linked);
-    transform_location_ = glGetUniformLocation(program_, "transform");
-    CHECK_NE(transform_location_, -1);
-    scale_location_ = glGetUniformLocation(program_, "scale");
-    CHECK_NE(scale_location_, -1);
-
-    glGenFramebuffers(1, &framebuffer_);
-    CHECK(framebuffer_);
-
-    glGenBuffers(1, &vertex_buffer_);
-    CHECK(vertex_buffer_);
-    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
-    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
-    glClearColor(0.f, 1.f, 0.f, 1.f);
-  }
-
-  void Draw() {
-    DCHECK(gl_helper_);
-
-    gl_helper_->StartFrame();
-
-    if (!program_)
-      InitGlState();
-
-    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_);
-    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                           gl_helper_->GetFrameTexture(), 0);
-    glViewport(0, 0, viewport_size_.width, viewport_size_.height);
-
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glUseProgram(program_);
-
-    float angle = static_cast<float>(draw_number_ % 360) * M_PI / 180;
-    float transform[4][4] = {{cosf(angle), sinf(angle), 0.f, 0.f},
-                             {-sinf(angle), cosf(angle), 0.f, 0.f},
-                             {0.f, 0.f, 1.f, 0.f},
-                             {0.f, 0.f, 0.f, 1.f}};
-    glUniformMatrix4fv(transform_location_, 1, GL_FALSE, &transform[0][0]);
-    if (viewport_size_.width >= viewport_size_.height) {
-      glUniform4f(scale_location_, static_cast<float>(viewport_size_.height) /
-                                       static_cast<float>(viewport_size_.width),
-                  1.f, 1.f, 1.f);
-    } else {
-      glUniform4f(scale_location_, 1.f,
-                  static_cast<float>(viewport_size_.width) /
-                      static_cast<float>(viewport_size_.height),
-                  1.f, 1.f);
-    }
-
-    glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_);
-    glVertexAttribPointer(kPositionLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
-    glEnableVertexAttribArray(kPositionLocation);
-    glDrawArrays(GL_TRIANGLES, 0, 3);
-
-    gl_helper_->EndFrame();
-
-    draw_number_++;
-  }
-
-  void SubmitFrame() {
-    DCHECK(surface_id_.local);
-    DCHECK(surface_id_.id_namespace);
-
-    mojo::FramePtr frame = mojo::Frame::New();
-    frame->resources = mojo::Array<mojo::TransferableResourcePtr>::New(0);
-    frame->passes = mojo::Array<mojo::PassPtr>::New(1);
-    mojo::PassPtr pass = mojo::Pass::New();
-    pass->id = 0;
-    mojo::Rect viewport_rect;
-    viewport_rect.width = viewport_size_.width;
-    viewport_rect.height = viewport_size_.height;
-    pass->output_rect = viewport_rect.Clone();
-    pass->damage_rect = viewport_rect.Clone();
-    mojo::Transform identity_transform;
-    identity_transform.matrix = mojo::Array<float>::New(16);
-    for (size_t i = 0; i < 4; i++) {
-      for (size_t j = 0; j < 4; j++)
-        identity_transform.matrix[4 * i + j] = (i == j);
-    }
-    pass->transform_to_root_target = identity_transform.Clone();
-    pass->has_transparent_background = false;
-    pass->quads = mojo::Array<mojo::QuadPtr>::New(1);
-    mojo::QuadPtr quad = mojo::Quad::New();
-    quad->material = mojo::Material::SURFACE_CONTENT;
-    quad->rect = viewport_rect.Clone();
-    quad->opaque_rect = viewport_rect.Clone();
-    quad->visible_rect = viewport_rect.Clone();
-    quad->needs_blending = false;
-    quad->shared_quad_state_index = 0;
-    mojo::SurfaceQuadStatePtr surface_quad_state =
-        mojo::SurfaceQuadState::New();
-    surface_quad_state->surface = surface_id_.Clone();
-    quad->surface_quad_state = surface_quad_state.Pass();
-
-    pass->quads[0] = quad.Pass();
-    pass->shared_quad_states = mojo::Array<mojo::SharedQuadStatePtr>::New(1);
-    mojo::SharedQuadStatePtr shared_quad_state = mojo::SharedQuadState::New();
-    shared_quad_state->content_to_target_transform = identity_transform.Clone();
-    shared_quad_state->content_bounds = viewport_size_.Clone();
-    shared_quad_state->visible_content_rect = viewport_rect.Clone();
-    shared_quad_state->clip_rect = viewport_rect.Clone();
-    shared_quad_state->is_clipped = false;
-    shared_quad_state->opacity = 1;
-    shared_quad_state->blend_mode = mojo::SkXfermode::kSrc_Mode;
-    shared_quad_state->sorting_context_id = 0;
-    pass->shared_quad_states[0] = shared_quad_state.Pass();
-    frame->passes[0] = pass.Pass();
-
-    DCHECK(display_);
-    display_->SubmitFrame(frame.Pass(), mojo::Display::SubmitFrameCallback());
-  }
-
-  // |mojo::ApplicationDelegate|:
-  void Initialize(mojo::ApplicationImpl* application_impl) override {
-    DCHECK(!application_impl_);
-    application_impl_ = application_impl;
-
-    application_impl->ConnectToService("mojo:native_viewport_service",
-                                       &native_viewport_);
-    viewport_size_.width = 800;
-    viewport_size_.height = 600;
-    native_viewport_->Create(
-        viewport_size_.Clone(), mojo::SurfaceConfiguration::New(),
-        base::Bind(&GlHelperExampleApp::OnViewportMetricsReceived,
-                   base::Unretained(this)));
-    native_viewport_->Show();
-
-    mojo::ContextProviderPtr context_provider;
-    native_viewport_->GetContextProvider(GetProxy(&context_provider));
-
-    mojo::DisplayFactoryPtr display_factory;
-    application_impl_->ConnectToService("mojo:surfaces_service",
-                                        &display_factory);
-    display_factory->Create(context_provider.Pass(), nullptr,
-                            GetProxy(&display_));
-
-    gl_helper_.reset(new GlHelper(this, application_impl_->shell(), GL_RGBA,
-                                  false, viewport_size_));
-    Draw();
-  }
-
-  // |GlHelper::Client|:
-  void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override {
-    surface_id_ = *surface_id;
-    SubmitFrame();
-  }
-
-  void OnContextLost() override { CHECK(false) << "Oops, lost context."; }
-
-  void OnFrameDisplayed(uint32_t frame_id) override { Draw(); }
-
-  static const GLuint kPositionLocation = 0;
-
-  mojo::ApplicationImpl* application_impl_;
-  mojo::NativeViewportPtr native_viewport_;
-  mojo::Size viewport_size_;
-  mojo::DisplayPtr display_;
-  scoped_ptr<GlHelper> gl_helper_;
-
-  mojo::SurfaceId surface_id_;
-
-  // GL-related state:
-  GLuint program_;
-  GLint transform_location_;
-  GLint scale_location_;
-  GLuint framebuffer_;
-  GLuint vertex_buffer_;
-
-  uint64_t draw_number_;
-
-  DISALLOW_COPY_AND_ASSIGN(GlHelperExampleApp);
-};
-
-const GLuint GlHelperExampleApp::kPositionLocation;
-
-}  // namespace
-
-MojoResult MojoMain(MojoHandle application_request) {
-  mojo::ApplicationRunnerChromium runner(new GlHelperExampleApp());
-  return runner.Run(application_request);
-}
diff --git a/apps/moterm/gl_helper_unittest.cc b/apps/moterm/gl_helper_unittest.cc
deleted file mode 100644
index 409aab6..0000000
--- a/apps/moterm/gl_helper_unittest.cc
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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.
-
-// Note: This test depends on |GlHelper|, which depends on an implementation of
-// mojo:native_viewport_service and mojo:surfaces_service.
-
-#include "apps/moterm/gl_helper.h"
-
-#include <GLES2/gl2.h>
-
-#include "base/logging.h"
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/application/application_test_base.h"
-#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace {
-
-class GlHelperTest : public mojo::test::ApplicationTestBase,
-                     public GlHelper::Client {
- public:
-  GlHelperTest()
-      : surface_id_changed_call_count_(0),
-        on_frame_displayed_call_count_(0),
-        last_frame_id_(0) {}
-  ~GlHelperTest() override {}
-
-  void SetUp() override {
-    mojo::test::ApplicationTestBase::SetUp();
-
-    initial_size_.width = 100;
-    initial_size_.height = 100;
-    gl_helper_.reset(new GlHelper(this, application_impl()->shell(), GL_RGBA,
-                                  false, initial_size_));
-  }
-
- protected:
-  const mojo::Size& initial_size() const { return initial_size_; }
-  GlHelper* gl_helper() { return gl_helper_.get(); }
-
-  unsigned surface_id_changed_call_count() const {
-    return surface_id_changed_call_count_;
-  }
-  const mojo::SurfaceId* last_surface_id() const {
-    return last_surface_id_.get();
-  }
-  unsigned on_frame_displayed_call_count() const {
-    return on_frame_displayed_call_count_;
-  }
-  uint32_t last_frame_id() const { return last_frame_id_; }
-
- private:
-  // |GlHelper::Client|:
-  void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override {
-    surface_id_changed_call_count_++;
-    last_surface_id_ = surface_id.Pass();
-    base::MessageLoop::current()->Quit();
-  }
-
-  void OnContextLost() override {
-    // We don't currently test this, but we'll get it on teardown.
-    // TODO(vtl): We probably should figure out how to test this explicitly.
-  }
-
-  void OnFrameDisplayed(uint32_t frame_id) override {
-    on_frame_displayed_call_count_++;
-    last_frame_id_ = frame_id;
-    base::MessageLoop::current()->Quit();
-  }
-
-  mojo::Size initial_size_;
-  scoped_ptr<GlHelper> gl_helper_;
-
-  unsigned surface_id_changed_call_count_;
-  mojo::SurfaceIdPtr last_surface_id_;
-  unsigned on_frame_displayed_call_count_;
-  uint32_t last_frame_id_;
-
-  DISALLOW_COPY_AND_ASSIGN(GlHelperTest);
-};
-
-TEST_F(GlHelperTest, Basic) {
-  gl_helper()->StartFrame();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  scoped_ptr<unsigned char[]> bitmap(
-      new unsigned char[initial_size().width * initial_size().height * 4]);
-  memset(bitmap.get(), 123, initial_size().width * initial_size().height * 4);
-  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, initial_size().width,
-                  initial_size().height, GL_RGBA, GL_UNSIGNED_BYTE,
-                  bitmap.get());
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  uint32_t frame_id = gl_helper()->EndFrame();
-
-  // We should get |OnSurfaceIdChanged()| and then |OnFrameDisplayed()|.
-  while (surface_id_changed_call_count() < 1u ||
-         on_frame_displayed_call_count() < 1u)
-    base::MessageLoop::current()->Run();
-
-  EXPECT_EQ(1u, surface_id_changed_call_count());
-  EXPECT_TRUE(last_surface_id());
-  EXPECT_NE(last_surface_id()->local, 0u);
-  EXPECT_NE(last_surface_id()->id_namespace, 0u);
-  EXPECT_EQ(1u, on_frame_displayed_call_count());
-  EXPECT_EQ(frame_id, last_frame_id());
-}
-
-TEST_F(GlHelperTest, SetSurfaceSizeAndMakeCurrent) {
-  // It only creates surfaces lazily, so we have to start/end a frame.
-  gl_helper()->StartFrame();
-  GLuint frame_id = gl_helper()->EndFrame();
-  while (surface_id_changed_call_count() < 1u ||
-         on_frame_displayed_call_count() < 1u)
-    base::MessageLoop::current()->Run();
-  EXPECT_EQ(1u, surface_id_changed_call_count());
-  mojo::SurfaceId surface_id = *last_surface_id();
-  EXPECT_EQ(1u, on_frame_displayed_call_count());
-  EXPECT_EQ(frame_id, last_frame_id());
-
-  // Set a new surface size.
-  mojo::Size new_size;
-  new_size.width = initial_size().width * 2;
-  new_size.height = initial_size().height + 50;
-  gl_helper()->SetSurfaceSize(new_size);
-
-  gl_helper()->StartFrame();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  GLuint frame_texture = gl_helper()->GetFrameTexture();
-  EXPECT_NE(frame_texture, 0u);
-
-  // Binding it should be OK.
-  glBindTexture(GL_TEXTURE_2D, frame_texture);
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  scoped_ptr<unsigned char[]> bitmap(
-      new unsigned char[new_size.width * new_size.height * 4]);
-  memset(bitmap.get(), 123, new_size.width * new_size.height * 4);
-  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, new_size.width, new_size.height,
-                  GL_RGBA, GL_UNSIGNED_BYTE, bitmap.get());
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-
-  frame_id = gl_helper()->EndFrame();
-  EXPECT_NE(frame_id, last_frame_id());
-
-  // We should get another |OnSurfaceIdChanged()| and another
-  // |OnFrameDisplayed()|.
-  while (surface_id_changed_call_count() < 2u ||
-         on_frame_displayed_call_count() < 2u)
-    base::MessageLoop::current()->Run();
-  EXPECT_EQ(2u, surface_id_changed_call_count());
-  EXPECT_TRUE(last_surface_id());
-  // Only the local part of the surface ID should have changed.
-  EXPECT_NE(last_surface_id()->local, surface_id.local);
-  EXPECT_EQ(surface_id.id_namespace, last_surface_id()->id_namespace);
-  EXPECT_EQ(2u, on_frame_displayed_call_count());
-  EXPECT_EQ(frame_id, last_frame_id());
-
-  // An out-of-the-blue |MakeCurrent()| should work.
-  gl_helper()->MakeCurrent();
-  EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
-}
-
-}  // namespace
diff --git a/apps/moterm/main.cc b/apps/moterm/main.cc
new file mode 100644
index 0000000..abeb499
--- /dev/null
+++ b/apps/moterm/main.cc
@@ -0,0 +1,16 @@
+// 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.
+
+// This is the "main" for the embeddable Moterm terminal view, which provides
+// services to the thing embedding it. (This is not very useful as a "top-level"
+// application.)
+
+#include "apps/moterm/moterm_app.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/public/c/system/main.h"
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new MotermApp());
+  return runner.Run(application_request);
+}
diff --git a/apps/moterm/moterm_app.cc b/apps/moterm/moterm_app.cc
new file mode 100644
index 0000000..efdb0fa
--- /dev/null
+++ b/apps/moterm/moterm_app.cc
@@ -0,0 +1,20 @@
+// 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 "apps/moterm/moterm_app.h"
+
+#include "apps/moterm/moterm_view.h"
+
+MotermApp::MotermApp() {}
+
+MotermApp::~MotermApp() {}
+
+bool MotermApp::CreateView(
+    const std::string& connection_url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services,
+    const mojo::ui::ViewProvider::CreateViewCallback& callback) {
+  new MotermView(app_impl(), services.Pass(), callback);
+  return true;
+}
diff --git a/apps/moterm/moterm_app.h b/apps/moterm/moterm_app.h
new file mode 100644
index 0000000..0b4aead
--- /dev/null
+++ b/apps/moterm/moterm_app.h
@@ -0,0 +1,26 @@
+// 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 APPS_MOTERM_MOTERM_APP_H_
+#define APPS_MOTERM_MOTERM_APP_H_
+
+#include "base/macros.h"
+#include "mojo/ui/view_provider_app.h"
+
+class MotermApp : public mojo::ui::ViewProviderApp {
+ public:
+  MotermApp();
+  ~MotermApp() override;
+
+  bool CreateView(
+      const std::string& connection_url,
+      mojo::InterfaceRequest<mojo::ServiceProvider> services,
+      mojo::ServiceProviderPtr exposed_services,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MotermApp);
+};
+
+#endif  // APPS_MOTERM_MOTERM_APP_H_
diff --git a/apps/moterm/moterm_main.cc b/apps/moterm/moterm_main.cc
deleted file mode 100644
index 299a3b4..0000000
--- a/apps/moterm/moterm_main.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// 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.
-
-// This is the "main" for the embeddable Moterm terminal view, which provides
-// services to the thing embedding it. (This is not very useful as a "top-level"
-// application.)
-
-#include "apps/moterm/moterm_view.h"
-#include "base/logging.h"
-#include "base/macros.h"
-#include "mojo/application/application_runner_chromium.h"
-#include "mojo/public/c/system/main.h"
-#include "mojo/public/cpp/application/application_connection.h"
-#include "mojo/public/cpp/application/application_delegate.h"
-#include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/services/view_manager/cpp/view_manager.h"
-#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
-#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
-
-namespace {
-
-class Moterm : public mojo::ApplicationDelegate,
-               public mojo::ViewManagerDelegate {
- public:
-  Moterm() : application_impl_() {}
-  ~Moterm() override {}
-
- private:
-  // |mojo::ApplicationDelegate|:
-  void Initialize(mojo::ApplicationImpl* application_impl) override {
-    DCHECK(!application_impl_);
-    application_impl_ = application_impl;
-    view_manager_client_factory_.reset(
-        new mojo::ViewManagerClientFactory(application_impl_->shell(), this));
-  }
-
-  bool ConfigureIncomingConnection(
-      mojo::ApplicationConnection* connection) override {
-    connection->AddService(view_manager_client_factory_.get());
-    return true;
-  }
-
-  // |mojo::ViewManagerDelegate|:
-  void OnEmbed(mojo::View* root,
-               mojo::InterfaceRequest<mojo::ServiceProvider> services,
-               mojo::ServiceProviderPtr exposed_services) override {
-    new MotermView(application_impl_->shell(), root, services.Pass());
-  }
-
-  void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {
-  }
-
-  mojo::ApplicationImpl* application_impl_;
-  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(Moterm);
-};
-
-}  // namespace
-
-MojoResult MojoMain(MojoHandle application_request) {
-  mojo::ApplicationRunnerChromium runner(new Moterm());
-  return runner.Run(application_request);
-}
diff --git a/apps/moterm/moterm_view.cc b/apps/moterm/moterm_view.cc
index 838b817..4f992cc 100644
--- a/apps/moterm/moterm_view.cc
+++ b/apps/moterm/moterm_view.cc
@@ -15,14 +15,14 @@
 #include <string>
 
 #include "apps/moterm/key_util.h"
+#include "base/bind.h"
 #include "base/logging.h"
+#include "mojo/public/cpp/application/connect.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
 #include "mojo/services/files/interfaces/file.mojom.h"
 #include "mojo/services/files/interfaces/types.mojom.h"
-#include "mojo/services/input_events/interfaces/input_event_constants.mojom.h"
-#include "mojo/services/input_events/interfaces/input_events.mojom.h"
 #include "mojo/services/terminal/interfaces/terminal_client.mojom.h"
-#include "skia/ext/refptr.h"
+#include "mojo/skia/ganesh_texture_surface.h"
 #include "third_party/dejavu-fonts-ttf-2.34/kDejaVuSansMonoRegular.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkCanvas.h"
@@ -33,32 +33,17 @@
 #include "third_party/skia/include/core/SkStream.h"
 #include "third_party/skia/include/core/SkXfermode.h"
 
-namespace {
-
-const GLint kTextureFormat =
-    (kN32_SkColorType == kRGBA_8888_SkColorType) ? GL_RGBA : GL_BGRA_EXT;
-
-mojo::Size RectToSize(const mojo::Rect& rect) {
-  mojo::Size size;
-  size.width = rect.width;
-  size.height = rect.height;
-  return size;
-}
-
-}  // namespace
+constexpr uint32_t kMotermImageResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
 
 MotermView::MotermView(
-    mojo::Shell* shell,
-    mojo::View* view,
-    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request)
-    : view_(view),
-      gl_helper_(this,
-                 shell,
-                 kTextureFormat,
-                 false,
-                 RectToSize(view->bounds())),
+    mojo::ApplicationImpl* app_impl,
+    mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : GaneshView(app_impl, "Moterm", create_view_callback),
+      choreographer_(scene(), this),
+      input_handler_(view_service_provider(), this),
       model_(MotermModel::Size(240, 160), MotermModel::Size(24, 80), this),
-      frame_pending_(false),
       force_next_draw_(false),
       ascent_(0),
       line_height_(0),
@@ -89,11 +74,6 @@
   // monospace.
   advance_width_ = static_cast<int>(ceilf(fg_paint.measureText("X", 1)));
   DCHECK_GT(advance_width_, 0);
-
-  view_->AddObserver(this);
-
-  // Force an initial draw.
-  Draw(true);
 }
 
 MotermView::~MotermView() {
@@ -101,39 +81,27 @@
     driver_->Detach();
 }
 
-void MotermView::OnViewDestroyed(mojo::View* view) {
-  DCHECK_EQ(view, view_);
-  view_->RemoveObserver(this);
-  delete this;
+void MotermView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                          mojo::Array<uint32_t> children_needing_layout,
+                          const OnLayoutCallback& callback) {
+  view_size_.width = layout_params->constraints->max_width;
+  view_size_.height = layout_params->constraints->max_height;
+
+  ScheduleDraw(true);
+
+  auto info = mojo::ui::ViewLayoutResult::New();
+  info->size = view_size_.Clone();
+  callback.Run(info.Pass());
 }
 
-void MotermView::OnViewBoundsChanged(mojo::View* view,
-                                     const mojo::Rect& old_bounds,
-                                     const mojo::Rect& new_bounds) {
-  DCHECK_EQ(view, view_);
-  gl_helper_.SetSurfaceSize(RectToSize(view_->bounds()));
-  bitmap_device_.clear();
-  Draw(true);
-}
-
-void MotermView::OnViewInputEvent(mojo::View* view,
-                                  const mojo::EventPtr& event) {
-  if (event->action == mojo::EventType::KEY_PRESSED)
-    OnKeyPressed(event);
-}
-
-void MotermView::OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) {
-  view_->SetSurfaceId(surface_id.Pass());
-}
-
-void MotermView::OnContextLost() {
-  // TODO(vtl): We'll need to force a draw when we regain a context.
-}
-
-void MotermView::OnFrameDisplayed(uint32_t frame_id) {
-  DCHECK(frame_pending_);
-  frame_pending_ = false;
-  Draw(false);
+void MotermView::OnEvent(mojo::EventPtr event,
+                         const OnEventCallback& callback) {
+  if (event->action == mojo::EventType::KEY_PRESSED) {
+    OnKeyPressed(event.Pass());
+    callback.Run(true);
+  } else {
+    callback.Run(false);
+  }
 }
 
 void MotermView::OnResponse(const void* buf, size_t size) {
@@ -147,7 +115,7 @@
 
 void MotermView::OnDataReceived(const void* bytes, size_t num_bytes) {
   model_.ProcessInput(bytes, num_bytes, &model_state_changes_);
-  Draw(false);
+  ScheduleDraw(false);
 }
 
 void MotermView::OnClosed() {
@@ -225,51 +193,65 @@
                          bool reset,
                          const SetSizeCallback& callback) {
   if (!rows) {
-    rows = std::max(1u, std::min(MotermModel::kMaxRows,
-                                 static_cast<uint32_t>(view_->bounds().height) /
-                                     line_height_));
+    rows = std::max(
+        1u, std::min(MotermModel::kMaxRows,
+                     static_cast<uint32_t>(view_size_.height) / line_height_));
   }
   if (!columns) {
-    columns =
-        std::max(1u, std::min(MotermModel::kMaxColumns,
-                              static_cast<uint32_t>(view_->bounds().width) /
-                                  advance_width_));
+    columns = std::max(
+        1u, std::min(MotermModel::kMaxColumns,
+                     static_cast<uint32_t>(view_size_.width) / advance_width_));
   }
 
   model_.SetSize(MotermModel::Size(rows, columns), reset);
   callback.Run(mojo::files::Error::OK, rows, columns);
 
-  Draw(false);
+  ScheduleDraw(false);
 }
 
-void MotermView::Draw(bool force) {
-  // TODO(vtl): See TODO above |frame_pending_| in the class declaration.
-  if (frame_pending_) {
+void MotermView::ScheduleDraw(bool force) {
+  if (view_size_.width == 0 || view_size_.height == 0 ||
+      (!model_state_changes_.IsDirty() && !force && !force_next_draw_)) {
     force_next_draw_ |= force;
     return;
   }
-
-  force |= force_next_draw_;
   force_next_draw_ = false;
+  choreographer_.ScheduleDraw();
+}
 
-  if (!force && !model_state_changes_.IsDirty())
-    return;
-
-  // TODO(vtl): If |!force|, draw only the dirty region(s)?
+void MotermView::OnDraw(const mojo::gfx::composition::FrameInfo& frame_info,
+                        const base::TimeDelta& time_delta) {
+  // TODO(vtl): Draw only the dirty region(s)?
   model_state_changes_.Reset();
 
-  int32_t width = view_->bounds().width;
-  int32_t height = view_->bounds().height;
-  DCHECK_GT(width, 0);
-  DCHECK_GT(height, 0);
+  mojo::Rect bounds;
+  bounds.width = view_size_.width;
+  bounds.height = view_size_.height;
 
-  if (!bitmap_device_) {
-    bitmap_device_ = skia::AdoptRef(SkBitmapDevice::Create(SkImageInfo::Make(
-        width, height, kN32_SkColorType, kOpaque_SkAlphaType)));
-  }
+  auto update = mojo::gfx::composition::SceneUpdate::New();
+  mojo::gfx::composition::ResourcePtr moterm_resource =
+      ganesh_renderer()->DrawCanvas(
+          view_size_,
+          base::Bind(&MotermView::DrawContent, base::Unretained(this)));
+  DCHECK(moterm_resource);
+  update->resources.insert(kMotermImageResourceId, moterm_resource.Pass());
 
-  SkCanvas canvas(bitmap_device_.get());
-  canvas.clear(SK_ColorBLACK);
+  auto root_node = mojo::gfx::composition::Node::New();
+  root_node->op = mojo::gfx::composition::NodeOp::New();
+  root_node->op->set_image(mojo::gfx::composition::ImageNodeOp::New());
+  root_node->op->get_image()->content_rect = bounds.Clone();
+  root_node->op->get_image()->image_resource_id = kMotermImageResourceId;
+  update->nodes.insert(kRootNodeId, root_node.Pass());
+
+  auto metadata = mojo::gfx::composition::SceneMetadata::New();
+  metadata->presentation_time = frame_info.presentation_time;
+
+  scene()->Update(update.Pass());
+  scene()->Publish(metadata.Pass());
+}
+
+void MotermView::DrawContent(SkCanvas* canvas) {
+  canvas->clear(SK_ColorBLACK);
 
   SkPaint bg_paint;
   bg_paint.setStyle(SkPaint::kFill_Style);
@@ -291,8 +273,8 @@
       bg_paint.setColor(SkColorSetRGB(ch.background_color.red,
                                       ch.background_color.green,
                                       ch.background_color.blue));
-      canvas.drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_),
-                      bg_paint);
+      canvas->drawRect(SkRect::MakeXYWH(x, y, advance_width_, line_height_),
+                       bg_paint);
 
       // Paint the foreground.
       if (ch.code_point) {
@@ -308,8 +290,8 @@
                                         ch.foreground_color.green,
                                         ch.foreground_color.blue));
 
-        canvas.drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_,
-                        fg_paint);
+        canvas->drawText(&ch.code_point, sizeof(ch.code_point), x, y + ascent_,
+                         fg_paint);
       }
     }
   }
@@ -324,27 +306,15 @@
     // focused and/or active.
     bg_paint.setColor(SK_ColorWHITE);
     bg_paint.setXfermodeMode(SkXfermode::kDifference_Mode);
-    canvas.drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_,
-                                     cursor_pos.row * line_height_,
-                                     advance_width_, line_height_),
-                    bg_paint);
+    canvas->drawRect(SkRect::MakeXYWH(cursor_pos.column * advance_width_,
+                                      cursor_pos.row * line_height_,
+                                      advance_width_, line_height_),
+                     bg_paint);
   }
-
-  canvas.flush();
-
-  const SkBitmap& bitmap(bitmap_device_->accessBitmap(false));
-  // TODO(vtl): Do we need really need to lock/unlock pixels?
-  SkAutoLockPixels pixel_locker(bitmap);
-
-  gl_helper_.StartFrame();
-  // (The texture is already bound.)
-  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, kTextureFormat,
-                  GL_UNSIGNED_BYTE, bitmap.getPixels());
-  gl_helper_.EndFrame();
-  frame_pending_ = true;
+  canvas->flush();
 }
 
-void MotermView::OnKeyPressed(const mojo::EventPtr& key_event) {
+void MotermView::OnKeyPressed(mojo::EventPtr key_event) {
   std::string input_sequence =
       GetInputSequenceForKeyPressedEvent(*key_event, keypad_application_mode_);
   if (input_sequence.empty())
diff --git a/apps/moterm/moterm_view.h b/apps/moterm/moterm_view.h
index 4929e4a..4569f3d 100644
--- a/apps/moterm/moterm_view.h
+++ b/apps/moterm/moterm_view.h
@@ -5,7 +5,8 @@
 #ifndef APPS_MOTERM_MOTERM_VIEW_H_
 #define APPS_MOTERM_MOTERM_VIEW_H_
 
-#include "apps/moterm/gl_helper.h"
+#include <memory>
+
 #include "apps/moterm/moterm_driver.h"
 #include "apps/moterm/moterm_model.h"
 #include "base/macros.h"
@@ -15,42 +16,41 @@
 #include "mojo/public/cpp/application/service_provider_impl.h"
 #include "mojo/public/cpp/bindings/callback.h"
 #include "mojo/public/cpp/bindings/interface_request.h"
-#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
+#include "mojo/services/input_events/interfaces/input_event_constants.mojom.h"
+#include "mojo/services/input_events/interfaces/input_events.mojom.h"
 #include "mojo/services/terminal/interfaces/terminal.mojom.h"
-#include "mojo/services/view_manager/cpp/view_observer.h"
-#include "skia/ext/refptr.h"
+#include "mojo/ui/choreographer.h"
+#include "mojo/ui/ganesh_view.h"
+#include "mojo/ui/input_handler.h"
 #include "third_party/skia/include/core/SkBitmapDevice.h"
 #include "third_party/skia/include/core/SkTypeface.h"
 
-namespace mojo {
-class Shell;
-}
-
-class MotermView : public mojo::ViewObserver,
-                   public GlHelper::Client,
+class MotermView : public mojo::ui::GaneshView,
+                   public mojo::ui::ChoreographerDelegate,
+                   public mojo::ui::InputListener,
                    public MotermModel::Delegate,
                    public MotermDriver::Client,
                    public mojo::InterfaceFactory<mojo::terminal::Terminal>,
                    public mojo::terminal::Terminal {
  public:
   MotermView(
-      mojo::Shell* shell,
-      mojo::View* view,
-      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request);
+      mojo::ApplicationImpl* app_impl,
+      mojo::InterfaceRequest<mojo::ServiceProvider> service_provider_request,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
   ~MotermView() override;
 
  private:
-  // |mojo::ViewObserver|:
-  void OnViewDestroyed(mojo::View* view) override;
-  void OnViewBoundsChanged(mojo::View* view,
-                           const mojo::Rect& old_bounds,
-                           const mojo::Rect& new_bounds) override;
-  void OnViewInputEvent(mojo::View* view, const mojo::EventPtr& event) override;
+  // |mojo::ui::GaneshView|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override;
 
-  // |GlHelper::Client|:
-  void OnSurfaceIdChanged(mojo::SurfaceIdPtr surface_id) override;
-  void OnContextLost() override;
-  void OnFrameDisplayed(uint32_t frame_id) override;
+  // |mojo::ui::ChoreographerDelegate|:
+  void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info,
+              const base::TimeDelta& time_delta) override;
+
+  // |mojo::ui::InputListener|:
+  void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override;
 
   // |MotermModel::Delegate|:
   void OnResponse(const void* buf, size_t size) override;
@@ -81,12 +81,14 @@
 
   // If |force| is true, it will draw everything. Otherwise it will draw only if
   // |model_state_changes_| is dirty.
-  void Draw(bool force);
+  void ScheduleDraw(bool force);
+  void DrawContent(SkCanvas* canvas);
 
-  void OnKeyPressed(const mojo::EventPtr& key_event);
+  void OnKeyPressed(mojo::EventPtr key_event);
 
-  mojo::View* const view_;
-  GlHelper gl_helper_;
+  mojo::Size view_size_;
+  mojo::ui::Choreographer choreographer_;
+  mojo::ui::InputHandler input_handler_;
 
   // TODO(vtl): Consider the structure of this app. Do we really want the "view"
   // owning the model?
@@ -102,12 +104,6 @@
   mojo::ServiceProviderImpl service_provider_impl_;
   mojo::BindingSet<mojo::terminal::Terminal> terminal_bindings_;
 
-  // TODO(vtl): For some reason, drawing while a frame is already pending (i.e.,
-  // we've submitted it but haven't gotten a callback) interacts badly with
-  // resizing -- sometimes this results in us losing all future
-  // |OnViewBoundsChanged()| messages. So, for now, don't submit frames in that
-  // case.
-  bool frame_pending_;
   // If we skip drawing despite being forced to, we should force the next draw.
   bool force_next_draw_;
 
@@ -117,8 +113,6 @@
   int line_height_;
   int advance_width_;
 
-  skia::RefPtr<SkBitmapDevice> bitmap_device_;
-
   // Keyboard state.
   bool keypad_application_mode_;
 
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index 05c5a7c..2eb75dc 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -36,6 +36,7 @@
 
   deps = [
     ":portable_examples",
+    "//examples/moterm_example_app",
     "//examples/ui/noodles",
     "//examples/ui/png_viewer",
     "//examples/ui/shapes",
diff --git a/examples/moterm_example_app/BUILD.gn b/examples/moterm_example_app/BUILD.gn
index 0737a99..dc3701d 100644
--- a/examples/moterm_example_app/BUILD.gn
+++ b/examples/moterm_example_app/BUILD.gn
@@ -12,12 +12,13 @@
   deps = [
     "//base",
     "//mojo/application",
+    "//mojo/common",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/utility",
     "//mojo/public/interfaces/application",
     "//mojo/services/files/interfaces",
     "//mojo/services/geometry/interfaces",
     "//mojo/services/terminal/interfaces",
-    "//mojo/services/view_manager/cpp",
+    "//mojo/services/ui/views/interfaces",
   ]
 }
diff --git a/examples/moterm_example_app/README.md b/examples/moterm_example_app/README.md
index b31d0d3..85716cc 100644
--- a/examples/moterm_example_app/README.md
+++ b/examples/moterm_example_app/README.md
@@ -9,7 +9,8 @@
 
 On Linux, run it in the usual way, e.g.:
 
-    $ mojo/devtools/common/mojo_run --enable-multiprocess "mojo:window_manager mojo:moterm_example_app"
+    $ mojo/devtools/common/mojo_run --enable-multiprocess \
+        "mojo:launcher mojo:moterm_example_app"
 
 You'll probably need to click on the window to give it keyboard focus. You may
 also want to resize the window (especially horizontally) to make it bigger.
diff --git a/examples/moterm_example_app/moterm_example_app.cc b/examples/moterm_example_app/moterm_example_app.cc
index 9fabc34..edab7ac 100644
--- a/examples/moterm_example_app/moterm_example_app.cc
+++ b/examples/moterm_example_app/moterm_example_app.cc
@@ -15,23 +15,23 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/binding_set.h"
 #include "mojo/public/c/system/main.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_delegate.h"
 #include "mojo/public/cpp/application/application_impl.h"
 #include "mojo/public/cpp/application/connect.h"
 #include "mojo/public/cpp/bindings/array.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
 #include "mojo/public/interfaces/application/service_provider.mojom.h"
 #include "mojo/public/interfaces/application/shell.mojom.h"
 #include "mojo/services/files/interfaces/file.mojom.h"
 #include "mojo/services/files/interfaces/types.mojom.h"
 #include "mojo/services/terminal/interfaces/terminal.mojom.h"
 #include "mojo/services/terminal/interfaces/terminal_client.mojom.h"
-#include "mojo/services/view_manager/cpp/view.h"
-#include "mojo/services/view_manager/cpp/view_manager.h"
-#include "mojo/services/view_manager/cpp/view_manager_client_factory.h"
-#include "mojo/services/view_manager/cpp/view_manager_delegate.h"
-#include "mojo/services/view_manager/cpp/view_observer.h"
+#include "mojo/services/ui/views/interfaces/view_manager.mojom.h"
+#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+#include "mojo/services/ui/views/interfaces/views.mojom.h"
 
 // Kind of like |fputs()| (doesn't wait for result).
 void Fputs(mojo::files::File* file, const char* s) {
@@ -43,25 +43,32 @@
               mojo::files::File::WriteCallback());
 }
 
-class MotermExampleAppView : public mojo::ViewObserver {
+class MotermExampleAppView {
  public:
-  explicit MotermExampleAppView(mojo::Shell* shell, mojo::View* view)
-      : shell_(shell), view_(view), moterm_view_(), weak_factory_(this) {
-    view_->AddObserver(this);
+  MotermExampleAppView(
+      mojo::Shell* shell,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback)
+      : shell_(shell), weak_factory_(this) {
+    // Connect to the moterm app.
+    LOG(INFO) << "Connecting to moterm";
+    mojo::ServiceProviderPtr moterm_app;
+    shell->ConnectToApplication("mojo:moterm", GetProxy(&moterm_app), nullptr);
 
-    moterm_view_ = view_->view_manager()->CreateView();
-    view_->AddChild(moterm_view_);
-    moterm_view_->SetBounds(view_->bounds());
-    moterm_view_->SetVisible(true);
-    mojo::ServiceProviderPtr moterm_sp;
-    moterm_view_->Embed("mojo:moterm", GetProxy(&moterm_sp), nullptr);
-    moterm_sp->ConnectToService(mojo::terminal::Terminal::Name_,
-                                GetProxy(&moterm_terminal_).PassMessagePipe());
-    Resize();
+    // Create the moterm view and pass it back to the client directly.
+    mojo::ConnectToService(moterm_app.get(), &moterm_view_provider_);
+    mojo::ServiceProviderPtr moterm_service_provider;
+    moterm_view_provider_->CreateView(GetProxy(&moterm_service_provider),
+                                      nullptr, callback);
+
+    // Connect to the moterm terminal service associated with the view
+    // we just created.
+    mojo::ConnectToService(moterm_service_provider.get(), &moterm_terminal_);
+
+    // Start running.
     StartPrompt(true);
   }
 
-  ~MotermExampleAppView() override {}
+  ~MotermExampleAppView() {}
 
  private:
   void Resize() {
@@ -135,24 +142,9 @@
     StartPrompt(false);
   }
 
-  // |ViewObserver|:
-  void OnViewDestroyed(mojo::View* view) override {
-    DCHECK(view == view_);
-    delete this;
-  }
-
-  void OnViewBoundsChanged(mojo::View* view,
-                           const mojo::Rect& old_bounds,
-                           const mojo::Rect& new_bounds) override {
-    DCHECK_EQ(view, view_);
-    moterm_view_->SetBounds(view_->bounds());
-    Resize();
-  }
-
   mojo::Shell* const shell_;
-  mojo::View* const view_;
+  mojo::ui::ViewProviderPtr moterm_view_provider_;
 
-  mojo::View* moterm_view_;
   mojo::terminal::TerminalPtr moterm_terminal_;
   // Valid while prompting.
   mojo::files::FilePtr moterm_file_;
@@ -163,7 +155,8 @@
 };
 
 class MotermExampleApp : public mojo::ApplicationDelegate,
-                         public mojo::ViewManagerDelegate {
+                         public mojo::InterfaceFactory<mojo::ui::ViewProvider>,
+                         public mojo::ui::ViewProvider {
  public:
   MotermExampleApp() : application_impl_() {}
   ~MotermExampleApp() override {}
@@ -173,27 +166,29 @@
   void Initialize(mojo::ApplicationImpl* application_impl) override {
     DCHECK(!application_impl_);
     application_impl_ = application_impl;
-    view_manager_client_factory_.reset(
-        new mojo::ViewManagerClientFactory(application_impl_->shell(), this));
   }
 
   bool ConfigureIncomingConnection(
       mojo::ApplicationConnection* connection) override {
-    connection->AddService(view_manager_client_factory_.get());
+    connection->AddService<mojo::ui::ViewProvider>(this);
     return true;
   }
 
-  // |mojo::ViewManagerDelegate|:
-  void OnEmbed(mojo::View* root,
-               mojo::InterfaceRequest<mojo::ServiceProvider> services,
-               mojo::ServiceProviderPtr exposed_services) override {
-    new MotermExampleAppView(application_impl_->shell(), root);
+  // |InterfaceFactory<mojo::ui::ViewProvider>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override {
+    bindings_.AddBinding(this, request.Pass());
   }
 
-  void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {}
+  // |ViewProvider|:
+  void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                  mojo::ServiceProviderPtr exposed_services,
+                  const CreateViewCallback& callback) override {
+    new MotermExampleAppView(application_impl_->shell(), callback);
+  }
 
   mojo::ApplicationImpl* application_impl_;
-  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+  mojo::BindingSet<mojo::ui::ViewProvider> bindings_;
 
   DISALLOW_COPY_AND_ASSIGN(MotermExampleApp);
 };