Update the UI examples.

Port the Spinning Cube, Tile, Ganesh (aka. Shapes), PNG, and PDF examples
to the new Mozart compositor and view manager.

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

Review URL: https://codereview.chromium.org/1559723002 .
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index c639c07..8b33176 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -36,8 +36,17 @@
 
   deps = [
     ":portable_examples",
+    "//examples/ui/png_viewer",
+    "//examples/ui/shapes",
+    "//examples/ui/spinning_cube",
+    "//examples/ui/tile",
   ]
 
+  # TODO(cstout) - javascript/v8 build support for fnl/musl
+  if (!is_android && !is_fnl) {
+    deps += [ "//examples/ui/pdf_viewer" ]
+  }
+
   if (is_android) {
     deps += [
       "//examples/device_name",
diff --git a/examples/ganesh_app/BUILD.gn b/examples/ganesh_app/BUILD.gn
deleted file mode 100644
index 915558c..0000000
--- a/examples/ganesh_app/BUILD.gn
+++ /dev/null
@@ -1,35 +0,0 @@
-# Copyright 2014 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")
-
-mojo_native_application("ganesh_app") {
-  sources = [
-    "ganesh_app.cc",
-    "ganesh_view.cc",
-    "ganesh_view.h",
-    "texture_uploader.cc",
-    "texture_uploader.h",
-  ]
-
-  deps = [
-    "//base",
-    "//mojo/application",
-    "//mojo/common:tracing_impl",
-    "//mojo/environment:chromium",
-    "//mojo/gpu",
-    "//mojo/public/c/gpu",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/system",
-    "//mojo/public/interfaces/application",
-    "//mojo/services/geometry/cpp",
-    "//mojo/services/geometry/interfaces",
-    "//mojo/services/surfaces/cpp",
-    "//mojo/services/surfaces/interfaces",
-    "//mojo/services/surfaces/interfaces:surface_id",
-    "//mojo/services/view_manager/cpp",
-    "//mojo/skia",
-    "//skia",
-  ]
-}
diff --git a/examples/ganesh_app/README.md b/examples/ganesh_app/README.md
deleted file mode 100644
index e4baf6b..0000000
--- a/examples/ganesh_app/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Running ganesh_app
-
-To run ganesh_app, use one of the following command from the root of your Mojo repo.
-
-## Android
-
-```
-mojo/devtools/common/mojo_run --android "mojo:kiosk_wm mojo:ganesh_app"
-```
-
-## Linux
-
-```
-mojo/devtools/common/mojo_run "mojo:kiosk_wm mojo:ganesh_app"
-```
diff --git a/examples/ganesh_app/ganesh_app.cc b/examples/ganesh_app/ganesh_app.cc
deleted file mode 100644
index c382765..0000000
--- a/examples/ganesh_app/ganesh_app.cc
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright 2014 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/macros.h"
-#include "base/trace_event/trace_event.h"
-#include "examples/ganesh_app/ganesh_view.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_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 examples {
-
-class GaneshApp : public mojo::ApplicationDelegate,
-                  public mojo::ViewManagerDelegate {
- public:
-  GaneshApp() {}
-  ~GaneshApp() override {}
-
-  void Initialize(mojo::ApplicationImpl* app) override {
-    tracing_.Initialize(app);
-    TRACE_EVENT0("ganesh_app", __func__);
-    shell_ = app->shell();
-    view_manager_client_factory_.reset(
-        new mojo::ViewManagerClientFactory(app->shell(), this));
-  }
-
-  bool ConfigureIncomingConnection(
-      mojo::ApplicationConnection* connection) override {
-    TRACE_EVENT0("ganesh_app", __func__);
-    connection->AddService(view_manager_client_factory_.get());
-    return true;
-  }
-
-  void OnEmbed(mojo::View* root,
-               mojo::InterfaceRequest<mojo::ServiceProvider> services,
-               mojo::ServiceProviderPtr exposed_services) override {
-    TRACE_EVENT0("ganesh_app", __func__);
-    new GaneshView(shell_, root);
-  }
-
-  void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {
-    base::MessageLoop::current()->Quit();
-  }
-
- private:
-  mojo::TracingImpl tracing_;
-  mojo::Shell* shell_;
-  scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(GaneshApp);
-};
-
-}  // namespace examples
-
-MojoResult MojoMain(MojoHandle application_request) {
-  mojo::ApplicationRunnerChromium runner(new examples::GaneshApp);
-  return runner.Run(application_request);
-}
diff --git a/examples/ganesh_app/ganesh_view.cc b/examples/ganesh_app/ganesh_view.cc
deleted file mode 100644
index 4807548..0000000
--- a/examples/ganesh_app/ganesh_view.cc
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2014 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 "examples/ganesh_app/ganesh_view.h"
-
-#include "base/logging.h"
-#include "base/trace_event/trace_event.h"
-#include "mojo/skia/ganesh_texture_surface.h"
-#include "third_party/skia/include/core/SkCanvas.h"
-#include "third_party/skia/include/core/SkColor.h"
-
-namespace examples {
-
-namespace {
-
-mojo::Size ToSize(const mojo::Rect& rect) {
-  mojo::Size size;
-  size.width = rect.width;
-  size.height = rect.height;
-  return size;
-}
-
-}  // namespace
-
-GaneshView::GaneshView(mojo::Shell* shell, mojo::View* view)
-    : view_(view),
-      gl_context_(mojo::GLContext::Create(shell)),
-      gr_context_(new mojo::GaneshContext(gl_context_)),
-      texture_uploader_(this, shell, gl_context_) {
-  view_->AddObserver(this);
-  Draw(ToSize(view_->bounds()));
-}
-
-GaneshView::~GaneshView() {
-  if (gl_context_) {
-    // GaneshContext needs to be destroyed before GLContext.
-    gr_context_.reset();
-    gl_context_->Destroy();
-  }
-}
-
-void GaneshView::OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) {
-  view_->SetSurfaceId(surface_id.Pass());
-}
-
-void GaneshView::OnViewDestroyed(mojo::View* view) {
-  DCHECK(view == view_);
-  view_->RemoveObserver(this);
-  delete this;
-}
-
-void GaneshView::OnViewInputEvent(mojo::View* view,
-                                  const mojo::EventPtr& event) {
-  Draw(ToSize(view_->bounds()));
-}
-
-void GaneshView::OnViewBoundsChanged(mojo::View* view,
-                                     const mojo::Rect& old_bounds,
-                                     const mojo::Rect& new_bounds) {
-  Draw(ToSize(new_bounds));
-}
-
-void GaneshView::Draw(const mojo::Size& size) {
-  TRACE_EVENT0("ganesh_app", __func__);
-  mojo::GaneshContext::Scope scope(gr_context_.get());
-  mojo::GaneshTextureSurface surface(
-      gr_context_.get(),
-      std::unique_ptr<mojo::GLTexture>(new mojo::GLTexture(gl_context_, size)));
-
-  SkCanvas* canvas = surface.canvas();
-  canvas->clear(SK_ColorCYAN);
-
-  SkPaint paint;
-  paint.setColor(SK_ColorGREEN);
-  SkRect rect = SkRect::MakeWH(size.width, size.height);
-  rect.inset(10, 10);
-  canvas->drawRect(rect, paint);
-
-  paint.setColor(SK_ColorRED);
-  paint.setFlags(SkPaint::kAntiAlias_Flag);
-  canvas->drawCircle(50, 100, 100, paint);
-
-  canvas->flush();
-
-  texture_uploader_.Upload(
-      scoped_ptr<mojo::GLTexture>(surface.TakeTexture().release()));
-}
-
-}  // namespace examples
diff --git a/examples/ganesh_app/ganesh_view.h b/examples/ganesh_app/ganesh_view.h
deleted file mode 100644
index e2c0c21..0000000
--- a/examples/ganesh_app/ganesh_view.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2014 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 EXAMPLES_GANESH_APP_GANESH_VIEW_H_
-#define EXAMPLES_GANESH_APP_GANESH_VIEW_H_
-
-#include "examples/ganesh_app/texture_uploader.h"
-#include "mojo/gpu/gl_context.h"
-#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
-#include "mojo/services/view_manager/cpp/view_observer.h"
-#include "mojo/skia/ganesh_context.h"
-
-namespace mojo {
-class Shell;
-}
-
-namespace examples {
-
-class GaneshView : public TextureUploader::Client, public mojo::ViewObserver {
- public:
-  GaneshView(mojo::Shell* shell, mojo::View* view);
-  ~GaneshView() override;
-
- private:
-  // mojo::ViewObserver implementation.
-  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;
-
-  // TextureUploader::Client implementation.
-  void OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) override;
-
-  void Draw(const mojo::Size& size);
-
-  mojo::View* view_;
-  base::WeakPtr<mojo::GLContext> gl_context_;
-  scoped_ptr<mojo::GaneshContext> gr_context_;
-  TextureUploader texture_uploader_;
-
-  DISALLOW_COPY_AND_ASSIGN(GaneshView);
-};
-
-}  // namespace examples
-
-#endif  // EXAMPLES_GANESH_APP_GANESH_VIEW_H_
diff --git a/examples/ganesh_app/texture_uploader.cc b/examples/ganesh_app/texture_uploader.cc
deleted file mode 100644
index 9639460..0000000
--- a/examples/ganesh_app/texture_uploader.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2014 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 "examples/ganesh_app/texture_uploader.h"
-
-#ifndef GL_GLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2extmojo.h>
-
-#include "base/bind.h"
-#include "base/trace_event/trace_event.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"
-
-namespace examples {
-
-TextureUploader::Client::~Client() {}
-
-TextureUploader::TextureUploader(Client* client,
-                                 mojo::Shell* shell,
-                                 base::WeakPtr<mojo::GLContext> context)
-    : client_(client),
-      context_(context),
-      next_resource_id_(0u),
-      id_namespace_(0u),
-      local_id_(0u),
-      returner_binding_(this) {
-  TRACE_EVENT0("ganesh_app", __func__);
-  context_->AddObserver(this);
-
-  mojo::ServiceProviderPtr surfaces_service_provider;
-  shell->ConnectToApplication("mojo:surfaces_service",
-                              mojo::GetProxy(&surfaces_service_provider),
-                              nullptr);
-  mojo::ConnectToService(surfaces_service_provider.get(), &surface_);
-  surface_->GetIdNamespace(
-      base::Bind(&TextureUploader::SetIdNamespace, base::Unretained(this)));
-  mojo::ResourceReturnerPtr returner_ptr;
-  returner_binding_.Bind(GetProxy(&returner_ptr));
-  surface_->SetResourceReturner(returner_ptr.Pass());
-}
-
-TextureUploader::~TextureUploader() {
-  if (context_.get())
-    context_->RemoveObserver(this);
-}
-
-void TextureUploader::Upload(scoped_ptr<mojo::GLTexture> texture) {
-  TRACE_EVENT0("ganesh_app", __func__);
-  mojo::Size size = texture->size();
-  EnsureSurfaceForSize(size);
-
-  mojo::FramePtr frame = mojo::Frame::New();
-  frame->resources.resize(0u);
-
-  mojo::Rect bounds;
-  bounds.width = size.width;
-  bounds.height = size.height;
-  mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
-  pass->quads.resize(0u);
-  pass->shared_quad_states.push_back(mojo::CreateDefaultSQS(size));
-
-  context_->MakeCurrent();
-  glBindTexture(GL_TEXTURE_2D, texture->texture_id());
-  GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
-  glGenMailboxCHROMIUM(mailbox);
-  glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox);
-  GLuint sync_point = glInsertSyncPointCHROMIUM();
-
-  mojo::TransferableResourcePtr resource = mojo::TransferableResource::New();
-  resource->id = next_resource_id_++;
-  resource_to_texture_map_[resource->id] = texture.release();
-  resource->format = mojo::ResourceFormat::RGBA_8888;
-  resource->filter = GL_LINEAR;
-  resource->size = 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;
-
-  mojo::QuadPtr quad = mojo::Quad::New();
-  quad->material = mojo::Material::TEXTURE_CONTENT;
-
-  mojo::RectPtr rect = mojo::Rect::New();
-  rect->width = size.width;
-  rect->height = size.height;
-  quad->rect = rect.Clone();
-  quad->opaque_rect = rect.Clone();
-  quad->visible_rect = rect.Clone();
-  quad->needs_blending = true;
-  quad->shared_quad_state_index = 0u;
-
-  mojo::TextureQuadStatePtr texture_state = mojo::TextureQuadState::New();
-  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 = false;
-
-  frame->resources.push_back(resource.Pass());
-  quad->texture_quad_state = texture_state.Pass();
-  pass->quads.push_back(quad.Pass());
-
-  frame->passes.push_back(pass.Pass());
-  surface_->SubmitFrame(local_id_, frame.Pass(), mojo::Closure());
-}
-
-void TextureUploader::OnContextLost() {
-  LOG(FATAL) << "Context lost.";
-}
-
-void TextureUploader::ReturnResources(
-    mojo::Array<mojo::ReturnedResourcePtr> resources) {
-  TRACE_EVENT0("ganesh_app", __func__);
-  context_->MakeCurrent();
-  for (size_t i = 0u; i < resources.size(); ++i) {
-    mojo::ReturnedResourcePtr resource = resources[i].Pass();
-    DCHECK_EQ(1, resource->count);
-    glWaitSyncPointCHROMIUM(resource->sync_point);
-    mojo::GLTexture* texture = resource_to_texture_map_[resource->id];
-    DCHECK_NE(0u, texture->texture_id());
-    resource_to_texture_map_.erase(resource->id);
-    delete texture;
-  }
-}
-
-void TextureUploader::EnsureSurfaceForSize(const mojo::Size& size) {
-  TRACE_EVENT0("ganesh_app", __func__);
-  if (local_id_ != 0u && size == surface_size_)
-    return;
-
-  if (local_id_ != 0u) {
-    surface_->DestroySurface(local_id_);
-  }
-
-  local_id_++;
-  surface_->CreateSurface(local_id_);
-  surface_size_ = size;
-  if (id_namespace_ != 0u)
-    SendFullyQualifiedID();
-}
-void TextureUploader::SendFullyQualifiedID() {
-  auto qualified_id = mojo::SurfaceId::New();
-  qualified_id->id_namespace = id_namespace_;
-  qualified_id->local = local_id_;
-  client_->OnSurfaceIdAvailable(qualified_id.Pass());
-}
-
-void TextureUploader::SetIdNamespace(uint32_t id_namespace) {
-  id_namespace_ = id_namespace;
-  if (local_id_ != 0u)
-    SendFullyQualifiedID();
-}
-
-}  // namespace examples
diff --git a/examples/ganesh_app/texture_uploader.h b/examples/ganesh_app/texture_uploader.h
deleted file mode 100644
index 2b449e5..0000000
--- a/examples/ganesh_app/texture_uploader.h
+++ /dev/null
@@ -1,69 +0,0 @@
-// Copyright 2014 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 EXAMPLES_GANESH_APP_TEXTURE_UPLOADER_H_
-#define EXAMPLES_GANESH_APP_TEXTURE_UPLOADER_H_
-
-#include "base/containers/hash_tables.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/memory/weak_ptr.h"
-#include "mojo/gpu/gl_context.h"
-#include "mojo/gpu/gl_texture.h"
-#include "mojo/public/cpp/bindings/binding.h"
-#include "mojo/services/geometry/interfaces/geometry.mojom.h"
-#include "mojo/services/surfaces/interfaces/surface_id.mojom.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.h"
-
-namespace mojo {
-class Shell;
-}
-
-namespace examples {
-
-class TextureUploader : public mojo::ResourceReturner,
-                        public mojo::GLContext::Observer {
- public:
-  class Client {
-   public:
-    virtual void OnSurfaceIdAvailable(mojo::SurfaceIdPtr surface_id) = 0;
-
-   protected:
-    virtual ~Client();
-  };
-
-  TextureUploader(Client* client,
-                  mojo::Shell* shell,
-                  base::WeakPtr<mojo::GLContext> context);
-  ~TextureUploader() override;
-
-  void Upload(scoped_ptr<mojo::GLTexture> texture);
-
- private:
-  // mojo::GLContext::Observer
-  void OnContextLost() override;
-
-  // mojo::ResourceReturner
-  void ReturnResources(
-      mojo::Array<mojo::ReturnedResourcePtr> resources) override;
-
-  void SetIdNamespace(uint32_t id_namespace);
-  void EnsureSurfaceForSize(const mojo::Size& size);
-  void SendFullyQualifiedID();
-
-  Client* client_;
-  base::WeakPtr<mojo::GLContext> context_;
-  mojo::SurfacePtr surface_;
-  mojo::Size surface_size_;
-  uint32_t next_resource_id_;
-  uint32_t id_namespace_;
-  uint32_t local_id_;
-  base::hash_map<uint32_t, mojo::GLTexture*> resource_to_texture_map_;
-  mojo::Binding<mojo::ResourceReturner> returner_binding_;
-
-  DISALLOW_COPY_AND_ASSIGN(TextureUploader);
-};
-
-}  // namespace examples
-
-#endif  // EXAMPLES_GANESH_APP_TEXTURE_UPLOADER_H_
diff --git a/examples/pdf_viewer/BUILD.gn b/examples/pdf_viewer/BUILD.gn
deleted file mode 100644
index 58bf26e..0000000
--- a/examples/pdf_viewer/BUILD.gn
+++ /dev/null
@@ -1,26 +0,0 @@
-# Copyright 2014 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")
-
-mojo_native_application("pdf_viewer") {
-  sources = [
-    "pdf_viewer.cc",
-  ]
-
-  deps = [
-    "//examples/bitmap_uploader",
-    "//mojo/application",
-    "//mojo/application:content_handler",
-    "//mojo/data_pipe_utils",
-    "//mojo/public/cpp/bindings",
-    "//mojo/public/cpp/utility",
-    "//mojo/services/content_handler/interfaces",
-    "//mojo/services/input_events/interfaces",
-    "//mojo/services/network/interfaces",
-    "//mojo/services/view_manager/cpp",
-    "//third_party/pdfium",
-    "//v8",
-  ]
-}
diff --git a/examples/pdf_viewer/pdf_viewer.cc b/examples/pdf_viewer/pdf_viewer.cc
deleted file mode 100644
index 57370d0..0000000
--- a/examples/pdf_viewer/pdf_viewer.cc
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2014 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 <string>
-
-#include "examples/bitmap_uploader/bitmap_uploader.h"
-#include "mojo/application/application_runner_chromium.h"
-#include "mojo/application/content_handler_factory.h"
-#include "mojo/data_pipe_utils/data_pipe_utils.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/interface_factory_impl.h"
-#include "mojo/public/cpp/application/service_provider_impl.h"
-#include "mojo/services/content_handler/interfaces/content_handler.mojom.h"
-#include "mojo/services/input_events/interfaces/input_events.mojom.h"
-#include "mojo/services/input_events/interfaces/input_key_codes.mojom.h"
-#include "mojo/services/view_manager/cpp/types.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 "third_party/pdfium/fpdfsdk/include/fpdf_ext.h"
-#include "third_party/pdfium/fpdfsdk/include/fpdfview.h"
-#include "v8/include/v8.h"
-
-#define BACKGROUND_COLOR 0xFF888888
-
-namespace mojo {
-namespace examples {
-
-namespace {
-
-class EmbedderData {
- public:
-  EmbedderData(Shell* shell, View* root) : bitmap_uploader_(root) {
-    bitmap_uploader_.Init(shell);
-    bitmap_uploader_.SetColor(BACKGROUND_COLOR);
-  }
-
-  BitmapUploader& bitmap_uploader() { return bitmap_uploader_; }
-
- private:
-  BitmapUploader bitmap_uploader_;
-
-  DISALLOW_COPY_AND_ASSIGN(EmbedderData);
-};
-
-}  // namespace
-
-class PDFView : public ApplicationDelegate,
-                public ViewManagerDelegate,
-                public ViewObserver {
- public:
-  PDFView(URLResponsePtr response)
-      : current_page_(0), page_count_(0), doc_(NULL), app_(nullptr) {
-    FetchPDF(response.Pass());
-  }
-
-  ~PDFView() override {
-    if (doc_)
-      FPDF_CloseDocument(doc_);
-    for (auto& roots : embedder_for_roots_) {
-      roots.first->RemoveObserver(this);
-      delete roots.second;
-    }
-  }
-
- private:
-  // Overridden from ApplicationDelegate:
-  void Initialize(ApplicationImpl* app) override {
-    app_ = app;
-    view_manager_client_factory_.reset(
-        new ViewManagerClientFactory(app->shell(), this));
-  }
-
-  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
-    connection->AddService(view_manager_client_factory_.get());
-    return true;
-  }
-
-  // Overridden from ViewManagerDelegate:
-  void OnEmbed(View* root,
-               InterfaceRequest<ServiceProvider> services,
-               ServiceProviderPtr exposed_services) override {
-    DCHECK(embedder_for_roots_.find(root) == embedder_for_roots_.end());
-    root->AddObserver(this);
-    EmbedderData* embedder_data = new EmbedderData(app_->shell(), root);
-    embedder_for_roots_[root] = embedder_data;
-    DrawBitmap(embedder_data);
-  }
-
-  void OnViewManagerDisconnected(ViewManager* view_manager) override {}
-
-  // Overridden from ViewObserver:
-  void OnViewBoundsChanged(View* view,
-                           const Rect& old_bounds,
-                           const Rect& new_bounds) override {
-    DCHECK(embedder_for_roots_.find(view) != embedder_for_roots_.end());
-    DrawBitmap(embedder_for_roots_[view]);
-  }
-
-  void OnViewInputEvent(View* view, const EventPtr& event) override {
-    DCHECK(embedder_for_roots_.find(view) != embedder_for_roots_.end());
-    if (event->key_data &&
-        (event->action != EventType::KEY_PRESSED || event->key_data->is_char)) {
-      return;
-    }
-    if ((event->key_data &&
-         event->key_data->windows_key_code == KeyboardCode::DOWN) ||
-        (event->pointer_data && event->pointer_data->vertical_wheel < 0)) {
-      if (current_page_ < (page_count_ - 1)) {
-        current_page_++;
-        DrawBitmap(embedder_for_roots_[view]);
-      }
-    } else if ((event->key_data &&
-                event->key_data->windows_key_code == KeyboardCode::UP) ||
-               (event->pointer_data &&
-                event->pointer_data->vertical_wheel > 0)) {
-      if (current_page_ > 0) {
-        current_page_--;
-        DrawBitmap(embedder_for_roots_[view]);
-      }
-    }
-  }
-
-  void OnViewDestroyed(View* view) override {
-    DCHECK(embedder_for_roots_.find(view) != embedder_for_roots_.end());
-    const auto& it = embedder_for_roots_.find(view);
-    DCHECK(it != embedder_for_roots_.end());
-    delete it->second;
-    embedder_for_roots_.erase(it);
-    if (embedder_for_roots_.size() == 0)
-      ApplicationImpl::Terminate();
-  }
-
-  void DrawBitmap(EmbedderData* embedder_data) {
-    if (!doc_)
-      return;
-
-    FPDF_PAGE page = FPDF_LoadPage(doc_, current_page_);
-    int width = static_cast<int>(FPDF_GetPageWidth(page));
-    int height = static_cast<int>(FPDF_GetPageHeight(page));
-
-    scoped_ptr<std::vector<unsigned char>> bitmap;
-    bitmap.reset(new std::vector<unsigned char>);
-    bitmap->resize(width * height * 4);
-
-    FPDF_BITMAP f_bitmap = FPDFBitmap_CreateEx(width, height, FPDFBitmap_BGRA,
-                                               &(*bitmap)[0], width * 4);
-    FPDFBitmap_FillRect(f_bitmap, 0, 0, width, height, 0xFFFFFFFF);
-    FPDF_RenderPageBitmap(f_bitmap, page, 0, 0, width, height, 0, 0);
-    FPDFBitmap_Destroy(f_bitmap);
-
-    FPDF_ClosePage(page);
-
-    embedder_data->bitmap_uploader().SetBitmap(width, height, bitmap.Pass(),
-                                               BitmapUploader::BGRA);
-  }
-
-  void FetchPDF(URLResponsePtr response) {
-    data_.clear();
-    mojo::common::BlockingCopyToString(response->body.Pass(), &data_);
-    if (data_.length() >= static_cast<size_t>(std::numeric_limits<int>::max()))
-      return;
-    doc_ = FPDF_LoadMemDocument(data_.data(), static_cast<int>(data_.length()),
-                                nullptr);
-    page_count_ = FPDF_GetPageCount(doc_);
-  }
-
-  std::string data_;
-  int current_page_;
-  int page_count_;
-  FPDF_DOCUMENT doc_;
-  ApplicationImpl* app_;
-  std::map<View*, EmbedderData*> embedder_for_roots_;
-  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PDFView);
-};
-
-class PDFViewer : public ApplicationDelegate,
-                  public ContentHandlerFactory::ManagedDelegate {
- public:
-  PDFViewer() : content_handler_factory_(this) {
-    v8::V8::InitializeICU();
-    FPDF_InitLibrary();
-  }
-
-  ~PDFViewer() override { FPDF_DestroyLibrary(); }
-
- private:
-  // Overridden from ApplicationDelegate:
-  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
-    connection->AddService(&content_handler_factory_);
-    return true;
-  }
-
-  // Overridden from ContentHandlerFactory::ManagedDelegate:
-  scoped_ptr<ContentHandlerFactory::HandledApplicationHolder>
-  CreateApplication(InterfaceRequest<Application> application_request,
-                    URLResponsePtr response) override {
-    return make_handled_factory_holder(new mojo::ApplicationImpl(
-        new PDFView(response.Pass()), application_request.Pass()));
-  }
-
-  ContentHandlerFactory content_handler_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PDFViewer);
-};
-
-}  // namespace examples
-}  // namespace mojo
-
-MojoResult MojoMain(MojoHandle application_request) {
-  mojo::ApplicationRunnerChromium runner(new mojo::examples::PDFViewer());
-  return runner.Run(application_request);
-}
diff --git a/examples/png_viewer/png_viewer.cc b/examples/png_viewer/png_viewer.cc
deleted file mode 100644
index 47b31a0..0000000
--- a/examples/png_viewer/png_viewer.cc
+++ /dev/null
@@ -1,198 +0,0 @@
-// Copyright 2014 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 <algorithm>
-#include <string>
-
-#include "base/macros.h"
-#include "base/memory/scoped_ptr.h"
-#include "base/message_loop/message_loop.h"
-#include "examples/bitmap_uploader/bitmap_uploader.h"
-#include "mojo/application/application_runner_chromium.h"
-#include "mojo/application/content_handler_factory.h"
-#include "mojo/data_pipe_utils/data_pipe_utils.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/interface_factory_impl.h"
-#include "mojo/public/cpp/application/service_provider_impl.h"
-#include "mojo/services/content_handler/interfaces/content_handler.mojom.h"
-#include "mojo/services/view_manager/cpp/types.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 "third_party/skia/include/core/SkColor.h"
-#include "ui/gfx/codec/png_codec.h"
-
-namespace mojo {
-namespace examples {
-
-namespace {
-
-class EmbedderData {
- public:
-  EmbedderData(Shell* shell, View* root) : bitmap_uploader_(root) {
-    bitmap_uploader_.Init(shell);
-    bitmap_uploader_.SetColor(SK_ColorGRAY);
-  }
-
-  BitmapUploader& bitmap_uploader() { return bitmap_uploader_; }
-
- private:
-  BitmapUploader bitmap_uploader_;
-
-  DISALLOW_COPY_AND_ASSIGN(EmbedderData);
-};
-
-}  // namespace
-
-// TODO(aa): Hook up ZoomableMedia interface again.
-class PNGView : public ApplicationDelegate,
-                public ViewManagerDelegate,
-                public ViewObserver {
- public:
-  PNGView(URLResponsePtr response)
-      : width_(0),
-        height_(0),
-        app_(nullptr),
-        zoom_percentage_(kDefaultZoomPercentage) {
-    DecodePNG(response.Pass());
-  }
-
-  ~PNGView() override {
-    for (auto& roots : embedder_for_roots_) {
-      roots.first->RemoveObserver(this);
-      delete roots.second;
-    }
-  }
-
- private:
-  static const uint16_t kMaxZoomPercentage = 400;
-  static const uint16_t kMinZoomPercentage = 20;
-  static const uint16_t kDefaultZoomPercentage = 100;
-  static const uint16_t kZoomStep = 20;
-
-  // Overridden from ApplicationDelegate:
-  void Initialize(ApplicationImpl* app) override {
-    app_ = app;
-    view_manager_client_factory_.reset(
-        new ViewManagerClientFactory(app->shell(), this));
-  }
-
-  // Overridden from ApplicationDelegate:
-  bool ConfigureIncomingConnection(
-      ApplicationConnection* connection) override {
-    connection->AddService(view_manager_client_factory_.get());
-    return true;
-  }
-
-  // Overridden from ViewManagerDelegate:
-  void OnEmbed(View* root,
-               InterfaceRequest<ServiceProvider> services,
-               ServiceProviderPtr exposed_services) override {
-    // TODO(qsr): The same view should be embeddable on multiple views.
-    DCHECK(embedder_for_roots_.find(root) == embedder_for_roots_.end());
-    root->AddObserver(this);
-    EmbedderData* embedder_data = new EmbedderData(app_->shell(), root);
-    embedder_for_roots_[root] = embedder_data;
-    embedder_data->bitmap_uploader().SetBitmap(
-        width_, height_,
-        make_scoped_ptr(new std::vector<unsigned char>(*bitmap_)),
-        BitmapUploader::BGRA);
-  }
-
-  void OnViewManagerDisconnected(ViewManager* view_manager) override {
-  }
-
-  // Overridden from ViewObserver:
-  void OnViewBoundsChanged(View* view,
-                           const Rect& old_bounds,
-                           const Rect& new_bounds) override {
-    DCHECK(embedder_for_roots_.find(view) != embedder_for_roots_.end());
-  }
-
-  void OnViewDestroyed(View* view) override {
-    // TODO(aa): Need to figure out how shutdown works.
-    const auto& it = embedder_for_roots_.find(view);
-    DCHECK(it != embedder_for_roots_.end());
-    delete it->second;
-    embedder_for_roots_.erase(it);
-    if (embedder_for_roots_.size() == 0)
-      ApplicationImpl::Terminate();
-  }
-
-  void ZoomIn() {
-    // TODO(qsr,aa) Zoom should be per embedder view.
-    if (zoom_percentage_ >= kMaxZoomPercentage)
-      return;
-    zoom_percentage_ += kZoomStep;
-  }
-
-  void ZoomOut() {
-    if (zoom_percentage_ <= kMinZoomPercentage)
-      return;
-    zoom_percentage_ -= kZoomStep;
-  }
-
-  void ZoomToActualSize() {
-    if (zoom_percentage_ == kDefaultZoomPercentage)
-      return;
-    zoom_percentage_ = kDefaultZoomPercentage;
-  }
-
-  void DecodePNG(URLResponsePtr response) {
-    std::string data;
-    mojo::common::BlockingCopyToString(response->body.Pass(), &data);
-    bitmap_.reset(new std::vector<unsigned char>);
-    gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(data.data()),
-                          data.length(), gfx::PNGCodec::FORMAT_BGRA,
-                          bitmap_.get(), &width_, &height_);
-  }
-
-  int width_;
-  int height_;
-  scoped_ptr<std::vector<unsigned char>> bitmap_;
-  ApplicationImpl* app_;
-  std::map<View*, EmbedderData*> embedder_for_roots_;
-  scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
-  uint16_t zoom_percentage_;
-
-  DISALLOW_COPY_AND_ASSIGN(PNGView);
-};
-
-class PNGViewer : public ApplicationDelegate,
-                  public ContentHandlerFactory::ManagedDelegate {
- public:
-  PNGViewer() : content_handler_factory_(this) {}
-
- private:
-  // Overridden from ApplicationDelegate:
-  bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
-    connection->AddService(&content_handler_factory_);
-    return true;
-  }
-
-  // Overridden from ContentHandlerFactory::ManagedDelegate:
-  scoped_ptr<ContentHandlerFactory::HandledApplicationHolder>
-  CreateApplication(InterfaceRequest<Application> application_request,
-                    URLResponsePtr response) override {
-    return make_handled_factory_holder(new mojo::ApplicationImpl(
-        new PNGView(response.Pass()), application_request.Pass()));
-  }
-
-  ContentHandlerFactory content_handler_factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(PNGViewer);
-};
-
-}  // namespace examples
-}  // namespace mojo
-
-MojoResult MojoMain(MojoHandle application_request) {
-  mojo::ApplicationRunnerChromium runner(new mojo::examples::PNGViewer());
-  return runner.Run(application_request);
-}
diff --git a/examples/ui/pdf_viewer/BUILD.gn b/examples/ui/pdf_viewer/BUILD.gn
new file mode 100644
index 0000000..7bb8927
--- /dev/null
+++ b/examples/ui/pdf_viewer/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2014 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")
+
+mojo_native_application("pdf_viewer") {
+  sources = [
+    "pdf_viewer.cc",
+  ]
+
+  deps = [
+    "//mojo/application",
+    "//mojo/data_pipe_utils",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/c/gpu",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/utility",
+    "//mojo/services/content_handler/interfaces",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/network/interfaces",
+    "//mojo/services/ui/input/interfaces",
+    "//mojo/services/ui/views/interfaces",
+    "//mojo/skia",
+    "//mojo/ui",
+    "//mojo/ui:content",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
+    "//third_party/pdfium",
+    "//ui/gfx",
+    "//v8",
+  ]
+}
diff --git a/examples/ui/pdf_viewer/README.md b/examples/ui/pdf_viewer/README.md
new file mode 100644
index 0000000..1be9a65
--- /dev/null
+++ b/examples/ui/pdf_viewer/README.md
@@ -0,0 +1,8 @@
+# PDF Content Handler Example
+
+This directory contains a simple application which decodes and views
+PDFs by implementing a Mojo content handler.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher http://.../my.pdf"
diff --git a/examples/ui/pdf_viewer/pdf_viewer.cc b/examples/ui/pdf_viewer/pdf_viewer.cc
new file mode 100644
index 0000000..ee760ab
--- /dev/null
+++ b/examples/ui/pdf_viewer/pdf_viewer.cc
@@ -0,0 +1,305 @@
+// Copyright 2014 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 <memory>
+#include <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/data_pipe_utils/data_pipe_utils.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/ui/choreographer.h"
+#include "mojo/ui/content_viewer_app.h"
+#include "mojo/ui/ganesh_view.h"
+#include "mojo/ui/input_handler.h"
+#include "third_party/pdfium/fpdfsdk/include/fpdf_ext.h"
+#include "third_party/pdfium/fpdfsdk/include/fpdfview.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkData.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "v8/include/v8.h"
+
+namespace examples {
+
+namespace {
+constexpr uint32_t kContentImageResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+}  // namespace
+
+class PDFLibrary;
+
+class PDFDocument {
+ public:
+  PDFDocument(const std::shared_ptr<PDFLibrary>& pdf_library,
+              FPDF_DOCUMENT doc,
+              const std::string& data)
+      : pdf_library_(pdf_library),
+        doc_(doc),
+        data_(data),
+        page_count_(FPDF_GetPageCount(doc_)) {}
+
+  ~PDFDocument() { FPDF_CloseDocument(doc_); }
+
+  uint32_t page_count() { return page_count_; }
+
+  skia::RefPtr<SkImage> DrawPage(int page_index, const mojo::Size& size) {
+    FPDF_PAGE page = FPDF_LoadPage(doc_, page_index);
+
+    double width_pts = FPDF_GetPageWidth(page);
+    double height_pts = FPDF_GetPageHeight(page);
+
+    int width, height;
+    if (size.width * height_pts < size.height * width_pts) {
+      width = size.width;
+      height = height_pts * size.width / width_pts;
+    } else {
+      width = width_pts * size.height / height_pts;
+      height = size.height;
+    }
+
+    int stride = width * 4;
+    skia::RefPtr<SkData> pixels =
+        skia::AdoptRef(SkData::NewUninitialized(stride * height));
+    DCHECK(pixels);
+
+    FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(width, height, FPDFBitmap_BGRA,
+                                             pixels->writable_data(), stride);
+    FPDFBitmap_FillRect(bitmap, 0, 0, width, height, 0xFFFFFFFF);
+    FPDF_RenderPageBitmap(bitmap, page, 0, 0, width, height, 0, 0);
+    FPDFBitmap_Destroy(bitmap);
+
+    FPDF_ClosePage(page);
+
+    SkImageInfo info = SkImageInfo::Make(width, height, kBGRA_8888_SkColorType,
+                                         kOpaque_SkAlphaType);
+    return skia::AdoptRef(SkImage::NewRasterData(info, pixels.get(), stride));
+  }
+
+ private:
+  std::shared_ptr<PDFLibrary> pdf_library_;
+  FPDF_DOCUMENT doc_;
+  std::string data_;
+  uint32_t page_count_;
+};
+
+class PDFLibrary : public std::enable_shared_from_this<PDFLibrary> {
+ public:
+  PDFLibrary() {
+    v8::V8::InitializeICU();
+    FPDF_InitLibrary();
+  }
+
+  ~PDFLibrary() { FPDF_DestroyLibrary(); }
+
+  std::shared_ptr<PDFDocument> Decode(mojo::URLResponsePtr response) {
+    std::string data;
+    mojo::common::BlockingCopyToString(response->body.Pass(), &data);
+    if (data.length() >= static_cast<size_t>(std::numeric_limits<int>::max()))
+      return nullptr;
+
+    FPDF_DOCUMENT doc = FPDF_LoadMemDocument(
+        data.data(), static_cast<int>(data.length()), nullptr);
+    if (!doc)
+      return nullptr;
+
+    return std::make_shared<PDFDocument>(shared_from_this(), doc, data);
+  }
+};
+
+class PDFDocumentView : public mojo::ui::GaneshView,
+                        public mojo::ui::ChoreographerDelegate,
+                        public mojo::ui::InputListener {
+ public:
+  PDFDocumentView(
+      mojo::ApplicationImpl* app_impl,
+      const std::shared_ptr<PDFDocument>& pdf_document,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+      : GaneshView(app_impl, "PDFDocumentViewer", create_view_callback),
+        pdf_document_(pdf_document),
+        choreographer_(scene(), this),
+        input_handler_(view_service_provider(), this) {
+    DCHECK(pdf_document_);
+  }
+
+  ~PDFDocumentView() override {}
+
+ private:
+  // |GaneshView|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override {
+    size_.width = layout_params->constraints->max_width;
+    size_.height = layout_params->constraints->max_height;
+
+    auto info = mojo::ui::ViewLayoutResult::New();
+    info->size = size_.Clone();
+    callback.Run(info.Pass());
+
+    Redraw();
+  }
+
+  // |InputListener|:
+  void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override {
+    if (event->key_data && (event->action != mojo::EventType::KEY_PRESSED ||
+                            event->key_data->is_char)) {
+      callback.Run(false);
+      return;
+    }
+
+    if ((event->key_data &&
+         event->key_data->windows_key_code == mojo::KeyboardCode::DOWN) ||
+        (event->pointer_data && event->pointer_data->vertical_wheel < 0)) {
+      GotoNextPage();
+      callback.Run(true);
+      return;
+    }
+
+    if ((event->key_data &&
+         event->key_data->windows_key_code == mojo::KeyboardCode::UP) ||
+        (event->pointer_data && event->pointer_data->vertical_wheel > 0)) {
+      GotoPreviousPage();
+      callback.Run(true);
+      return;
+    }
+
+    callback.Run(false);
+  }
+
+  // |ChoreographerDelegate|:
+  void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info,
+              const base::TimeDelta& time_delta) override {
+    mojo::Rect bounds;
+    bounds.width = size_.width;
+    bounds.height = size_.height;
+
+    auto update = mojo::gfx::composition::SceneUpdate::New();
+    mojo::gfx::composition::ResourcePtr content_resource =
+        ganesh_renderer()->DrawCanvas(
+            size_,
+            base::Bind(&PDFDocumentView::DrawContent, base::Unretained(this)));
+    DCHECK(content_resource);
+    update->resources.insert(kContentImageResourceId, content_resource.Pass());
+
+    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 = kContentImageResourceId;
+    update->nodes.insert(kRootNodeId, root_node.Pass());
+
+    scene()->Update(update.Pass());
+    scene()->Publish(nullptr);
+  }
+
+  void DrawContent(SkCanvas* canvas) {
+    if (!cached_image_) {
+      cached_image_ = pdf_document_->DrawPage(page_, size_);
+    }
+
+    if (cached_image_) {
+      canvas->clear(SK_ColorBLACK);
+      canvas->drawImageRect(
+          cached_image_.get(),
+          SkRect::MakeWH(cached_image_->width(), cached_image_->height()),
+          SkRect::MakeXYWH((size_.width - cached_image_->width()) / 2,
+                           (size_.height - cached_image_->height()) / 2,
+                           cached_image_->width(), cached_image_->height()),
+          nullptr);
+    } else {
+      canvas->clear(SK_ColorBLUE);
+    }
+  }
+
+  void GotoNextPage() {
+    if (page_ + 1 < pdf_document_->page_count()) {
+      page_++;
+      Redraw();
+    }
+  }
+
+  void GotoPreviousPage() {
+    if (page_ > 0) {
+      page_--;
+      Redraw();
+    }
+  }
+
+  void Redraw() {
+    cached_image_.clear();
+    choreographer_.ScheduleDraw();
+  }
+
+  std::shared_ptr<PDFDocument> pdf_document_;
+  uint32_t page_ = 0u;
+  skia::RefPtr<SkImage> cached_image_;
+
+  mojo::Size size_;
+  mojo::ui::Choreographer choreographer_;
+  mojo::ui::InputHandler input_handler_;
+
+  DISALLOW_COPY_AND_ASSIGN(PDFDocumentView);
+};
+
+class PDFContentViewProviderApp : public mojo::ui::ViewProviderApp {
+ public:
+  PDFContentViewProviderApp(const std::shared_ptr<PDFLibrary>& pdf_library,
+                            const std::shared_ptr<PDFDocument>& pdf_document)
+      : pdf_library_(pdf_library), pdf_document_(pdf_document) {
+    DCHECK(pdf_library_);
+    DCHECK(pdf_document_);
+  }
+
+  ~PDFContentViewProviderApp() override {}
+
+  bool CreateView(
+      const std::string& connection_url,
+      mojo::InterfaceRequest<mojo::ServiceProvider> services,
+      mojo::ServiceProviderPtr exposed_services,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback) override {
+    new PDFDocumentView(app_impl(), pdf_document_, callback);
+    return true;
+  }
+
+ private:
+  std::shared_ptr<PDFLibrary> pdf_library_;
+  std::shared_ptr<PDFDocument> pdf_document_;
+
+  DISALLOW_COPY_AND_ASSIGN(PDFContentViewProviderApp);
+};
+
+class PDFContentViewerApp : public mojo::ui::ContentViewerApp {
+ public:
+  PDFContentViewerApp() : pdf_library_(std::make_shared<PDFLibrary>()) {}
+
+  ~PDFContentViewerApp() override {}
+
+  mojo::ui::ViewProviderApp* LoadContent(
+      const std::string& content_handler_url,
+      mojo::URLResponsePtr response) override {
+    std::shared_ptr<PDFDocument> pdf_document =
+        pdf_library_->Decode(response.Pass());
+    if (!pdf_document) {
+      LOG(ERROR) << "Could not decode PDFDocument.";
+      return nullptr;
+    }
+
+    return new PDFContentViewProviderApp(pdf_library_, pdf_document);
+  }
+
+ private:
+  std::shared_ptr<PDFLibrary> pdf_library_;
+
+  DISALLOW_COPY_AND_ASSIGN(PDFContentViewerApp);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new examples::PDFContentViewerApp());
+  return runner.Run(application_request);
+}
diff --git a/examples/png_viewer/BUILD.gn b/examples/ui/png_viewer/BUILD.gn
similarity index 62%
rename from examples/png_viewer/BUILD.gn
rename to examples/ui/png_viewer/BUILD.gn
index 16061bc..6ff4b26 100644
--- a/examples/png_viewer/BUILD.gn
+++ b/examples/ui/png_viewer/BUILD.gn
@@ -10,15 +10,23 @@
   ]
 
   deps = [
-    "//examples/bitmap_uploader",
     "//mojo/application",
-    "//mojo/application:content_handler",
     "//mojo/data_pipe_utils",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/c/gpu",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/utility",
     "//mojo/services/content_handler/interfaces",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/network/interfaces",
-    "//mojo/services/view_manager/cpp",
+    "//mojo/services/ui/views/interfaces",
+    "//mojo/skia",
+    "//mojo/ui",
+    "//mojo/ui:content",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
     "//ui/gfx",
   ]
 }
diff --git a/examples/ui/png_viewer/README.md b/examples/ui/png_viewer/README.md
new file mode 100644
index 0000000..44f51cf
--- /dev/null
+++ b/examples/ui/png_viewer/README.md
@@ -0,0 +1,8 @@
+# PNG Content Handler Example
+
+This directory contains a simple application which decodes and views
+PNGs by implementing a Mojo content handler.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher http://.../my.png"
diff --git a/examples/ui/png_viewer/png_viewer.cc b/examples/ui/png_viewer/png_viewer.cc
new file mode 100644
index 0000000..abd5c48
--- /dev/null
+++ b/examples/ui/png_viewer/png_viewer.cc
@@ -0,0 +1,157 @@
+// Copyright 2014 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 <string>
+
+#include "base/bind.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/data_pipe_utils/data_pipe_utils.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/ui/content_viewer_app.h"
+#include "mojo/ui/ganesh_view.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkImage.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/codec/png_codec.h"
+
+namespace examples {
+
+namespace {
+constexpr uint32_t kContentImageResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+}  // namespace
+
+class PNGView : public mojo::ui::GaneshView {
+ public:
+  PNGView(
+      mojo::ApplicationImpl* app_impl,
+      const skia::RefPtr<SkImage>& image,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+      : GaneshView(app_impl, "PNGViewer", create_view_callback), image_(image) {
+    DCHECK(image_);
+  }
+
+  ~PNGView() override {}
+
+ private:
+  // |GaneshView|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override {
+    size_.width = layout_params->constraints->max_width;
+    size_.height = layout_params->constraints->max_height;
+
+    auto info = mojo::ui::ViewLayoutResult::New();
+    info->size = size_.Clone();
+    callback.Run(info.Pass());
+
+    UpdateScene();
+  }
+
+  void UpdateScene() {
+    mojo::Rect bounds;
+    bounds.width = size_.width;
+    bounds.height = size_.height;
+
+    auto update = mojo::gfx::composition::SceneUpdate::New();
+    mojo::gfx::composition::ResourcePtr content_resource =
+        ganesh_renderer()->DrawCanvas(
+            size_, base::Bind(&PNGView::DrawContent, base::Unretained(this)));
+    DCHECK(content_resource);
+    update->resources.insert(kContentImageResourceId, content_resource.Pass());
+
+    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 = kContentImageResourceId;
+    update->nodes.insert(kRootNodeId, root_node.Pass());
+
+    scene()->Update(update.Pass());
+    scene()->Publish(nullptr);
+  }
+
+  void DrawContent(SkCanvas* canvas) {
+    canvas->clear(SK_ColorBLACK);
+
+    int32_t w, h;
+    if (size_.width * image_->height() < size_.height * image_->width()) {
+      w = size_.width;
+      h = image_->height() * size_.width / image_->width();
+    } else {
+      w = image_->width() * size_.height / image_->height();
+      h = size_.height;
+    }
+    canvas->drawImageRect(
+        image_.get(), SkRect::MakeWH(image_->width(), image_->height()),
+        SkRect::MakeXYWH((size_.width - w) / 2, (size_.height - h) / 2, w, h),
+        nullptr);
+  }
+
+  skia::RefPtr<SkImage> image_;
+  mojo::Size size_;
+
+  DISALLOW_COPY_AND_ASSIGN(PNGView);
+};
+
+class PNGContentViewProviderApp : public mojo::ui::ViewProviderApp {
+ public:
+  PNGContentViewProviderApp(skia::RefPtr<SkImage> image) : image_(image) {
+    DCHECK(image_);
+  }
+
+  ~PNGContentViewProviderApp() override {}
+
+  bool CreateView(
+      const std::string& connection_url,
+      mojo::InterfaceRequest<mojo::ServiceProvider> services,
+      mojo::ServiceProviderPtr exposed_services,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback) override {
+    new PNGView(app_impl(), image_, callback);
+    return true;
+  }
+
+ private:
+  skia::RefPtr<SkImage> image_;
+
+  DISALLOW_COPY_AND_ASSIGN(PNGContentViewProviderApp);
+};
+
+class PNGContentViewerApp : public mojo::ui::ContentViewerApp {
+ public:
+  PNGContentViewerApp() {}
+
+  ~PNGContentViewerApp() override {}
+
+  mojo::ui::ViewProviderApp* LoadContent(
+      const std::string& content_handler_url,
+      mojo::URLResponsePtr response) override {
+    std::string data;
+    mojo::common::BlockingCopyToString(response->body.Pass(), &data);
+    SkBitmap bitmap;
+    if (!::gfx::PNGCodec::Decode(
+            reinterpret_cast<const unsigned char*>(data.data()), data.length(),
+            &bitmap) ||
+        bitmap.empty()) {
+      LOG(ERROR) << "Could not decode PNG.";
+      return nullptr;
+    }
+
+    return new PNGContentViewProviderApp(
+        skia::AdoptRef(SkImage::NewFromBitmap(bitmap)));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PNGContentViewerApp);
+};
+
+}  // namespace examples
+
+MojoResult MojoMain(MojoHandle application_request) {
+  mojo::ApplicationRunnerChromium runner(new examples::PNGContentViewerApp());
+  return runner.Run(application_request);
+}
diff --git a/examples/ui/shapes/BUILD.gn b/examples/ui/shapes/BUILD.gn
new file mode 100644
index 0000000..4462987
--- /dev/null
+++ b/examples/ui/shapes/BUILD.gn
@@ -0,0 +1,37 @@
+# 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")
+
+mojo_native_application("shapes") {
+  output_name = "shapes_view"
+
+  sources = [
+    "main.cc",
+    "shapes_app.cc",
+    "shapes_app.h",
+    "shapes_view.cc",
+    "shapes_view.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/environment:chromium",
+    "//mojo/gpu",
+    "//mojo/public/c/gpu",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/cpp/environment",
+    "//mojo/public/cpp/system",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/views/interfaces",
+    "//mojo/skia",
+    "//mojo/ui",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
+    "//skia",
+  ]
+}
diff --git a/examples/ui/shapes/README.md b/examples/ui/shapes/README.md
new file mode 100644
index 0000000..968dc4a
--- /dev/null
+++ b/examples/ui/shapes/README.md
@@ -0,0 +1,8 @@
+# Skia GPU Accelerated Drawing Example (Ganesh)
+
+This directory contains a simple application which uses Skia's
+Ganesh renderer to draw some simple shapes in a view.
+
+## USAGE
+
+  out/Debug/mojo_shell "mojo:launcher mojo:shapes_view"
diff --git a/examples/ui/shapes/main.cc b/examples/ui/shapes/main.cc
new file mode 100644
index 0000000..bf4925e
--- /dev/null
+++ b/examples/ui/shapes/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 "examples/ui/shapes/shapes_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 examples::ShapesApp);
+  return runner.Run(application_request);
+}
diff --git a/examples/ui/shapes/shapes_app.cc b/examples/ui/shapes/shapes_app.cc
new file mode 100644
index 0000000..86443a1
--- /dev/null
+++ b/examples/ui/shapes/shapes_app.cc
@@ -0,0 +1,24 @@
+// 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 "examples/ui/shapes/shapes_app.h"
+
+#include "examples/ui/shapes/shapes_view.h"
+
+namespace examples {
+
+ShapesApp::ShapesApp() {}
+
+ShapesApp::~ShapesApp() {}
+
+bool ShapesApp::CreateView(
+    const std::string& connection_url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services,
+    const mojo::ui::ViewProvider::CreateViewCallback& callback) {
+  new ShapesView(app_impl(), callback);
+  return true;
+}
+
+}  // namespace examples
diff --git a/examples/ui/shapes/shapes_app.h b/examples/ui/shapes/shapes_app.h
new file mode 100644
index 0000000..4aba71c
--- /dev/null
+++ b/examples/ui/shapes/shapes_app.h
@@ -0,0 +1,29 @@
+// 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 EXAMPLES_UI_SHAPES_SHAPES_APP_H_
+#define EXAMPLES_UI_SHAPES_SHAPES_APP_H_
+
+#include "mojo/ui/view_provider_app.h"
+
+namespace examples {
+
+class ShapesApp : public mojo::ui::ViewProviderApp {
+ public:
+  ShapesApp();
+  ~ShapesApp() 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(ShapesApp);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_SHAPES_SHAPES_APP_H_
diff --git a/examples/ui/shapes/shapes_view.cc b/examples/ui/shapes/shapes_view.cc
new file mode 100644
index 0000000..8bea9c3
--- /dev/null
+++ b/examples/ui/shapes/shapes_view.cc
@@ -0,0 +1,86 @@
+// 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 "examples/ui/shapes/shapes_view.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace examples {
+
+namespace {
+constexpr uint32_t kContentImageResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+}  // namespace
+
+ShapesView::ShapesView(
+    mojo::ApplicationImpl* app_impl,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : GaneshView(app_impl, "Shapes", create_view_callback) {}
+
+ShapesView::~ShapesView() {}
+
+void ShapesView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                          mojo::Array<uint32_t> children_needing_layout,
+                          const OnLayoutCallback& callback) {
+  size_.width = layout_params->constraints->max_width;
+  size_.height = layout_params->constraints->max_height;
+
+  // Submit the new layout information.
+  auto info = mojo::ui::ViewLayoutResult::New();
+  info->size = size_.Clone();
+  callback.Run(info.Pass());
+
+  // Draw!
+  UpdateScene();
+}
+
+void ShapesView::UpdateScene() {
+  mojo::Rect bounds;
+  bounds.width = size_.width;
+  bounds.height = size_.height;
+
+  auto update = mojo::gfx::composition::SceneUpdate::New();
+
+  // Draw the content of the view to a texture and include it as an
+  // image resource in the scene.
+  mojo::gfx::composition::ResourcePtr content_resource =
+      ganesh_renderer()->DrawCanvas(
+          size_, base::Bind(&ShapesView::DrawContent, base::Unretained(this)));
+  DCHECK(content_resource);
+  update->resources.insert(kContentImageResourceId, content_resource.Pass());
+
+  // Add a root node to the scene graph to draw the image resource to
+  // the screen such that it fills the entire view.
+  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 = kContentImageResourceId;
+  update->nodes.insert(kRootNodeId, root_node.Pass());
+
+  // Submit the scene update then publish it to cause the changes to be
+  // applied.
+  scene()->Update(update.Pass());
+  scene()->Publish(nullptr);
+}
+
+void ShapesView::DrawContent(SkCanvas* canvas) {
+  canvas->clear(SK_ColorCYAN);
+
+  SkPaint paint;
+  paint.setColor(SK_ColorGREEN);
+  SkRect rect = SkRect::MakeWH(size_.width, size_.height);
+  rect.inset(10, 10);
+  canvas->drawRect(rect, paint);
+
+  paint.setColor(SK_ColorRED);
+  paint.setFlags(SkPaint::kAntiAlias_Flag);
+  canvas->drawCircle(50, 100, 100, paint);
+}
+
+}  // namespace examples
diff --git a/examples/ui/shapes/shapes_view.h b/examples/ui/shapes/shapes_view.h
new file mode 100644
index 0000000..d041f7e
--- /dev/null
+++ b/examples/ui/shapes/shapes_view.h
@@ -0,0 +1,38 @@
+// 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 EXAMPLES_UI_SHAPES_SHAPES_VIEW_H_
+#define EXAMPLES_UI_SHAPES_SHAPES_VIEW_H_
+
+#include "mojo/ui/ganesh_view.h"
+
+class SkCanvas;
+
+namespace examples {
+
+class ShapesView : public mojo::ui::GaneshView {
+ public:
+  ShapesView(
+      mojo::ApplicationImpl* app_impl,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
+
+  ~ShapesView() override;
+
+ private:
+  // |GaneshView|:
+  void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
+                mojo::Array<uint32_t> children_needing_layout,
+                const OnLayoutCallback& callback) override;
+
+  void UpdateScene();
+  void DrawContent(SkCanvas* canvas);
+
+  mojo::Size size_;
+
+  DISALLOW_COPY_AND_ASSIGN(ShapesView);
+};
+
+}  // namespace examples
+
+#endif  // EXAMPLES_UI_SHAPES_SHAPES_VIEW_H_
diff --git a/examples/ui/spinning_cube/BUILD.gn b/examples/ui/spinning_cube/BUILD.gn
index db71418..ef0ee43 100644
--- a/examples/ui/spinning_cube/BUILD.gn
+++ b/examples/ui/spinning_cube/BUILD.gn
@@ -23,13 +23,17 @@
     "//mojo/environment:chromium",
     "//mojo/gpu",
     "//mojo/public/c/gpu",
-    "//mojo/public/c/gpu:gpu_onscreen",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/environment",
     "//mojo/public/cpp/system",
+    "//mojo/services/geometry/cpp",
     "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/input_events/interfaces",
-    "//mojo/services/surfaces/interfaces",
+    "//mojo/services/ui/input/interfaces",
     "//mojo/services/ui/views/interfaces",
+    "//mojo/ui",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
   ]
 }
diff --git a/examples/ui/spinning_cube/spinning_cube_app.cc b/examples/ui/spinning_cube/spinning_cube_app.cc
index 6bee8d4..79818ee 100644
--- a/examples/ui/spinning_cube/spinning_cube_app.cc
+++ b/examples/ui/spinning_cube/spinning_cube_app.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "examples/ui/spinning_cube/spinning_cube_app.h"
+
 #include "examples/ui/spinning_cube/spinning_cube_view.h"
 
 namespace examples {
@@ -11,27 +12,13 @@
 
 SpinningCubeApp::~SpinningCubeApp() {}
 
-void SpinningCubeApp::Initialize(mojo::ApplicationImpl* app_impl) {
-  app_impl_ = app_impl;
-}
-
-bool SpinningCubeApp::ConfigureIncomingConnection(
-    mojo::ApplicationConnection* connection) {
-  connection->AddService<mojo::ui::ViewProvider>(this);
-  return true;
-}
-
-void SpinningCubeApp::Create(
-    mojo::ApplicationConnection* connection,
-    mojo::InterfaceRequest<mojo::ui::ViewProvider> request) {
-  bindings_.AddBinding(this, request.Pass());
-}
-
-void SpinningCubeApp::CreateView(
+bool SpinningCubeApp::CreateView(
+    const std::string& connection_url,
     mojo::InterfaceRequest<mojo::ServiceProvider> services,
     mojo::ServiceProviderPtr exposed_services,
-    const CreateViewCallback& callback) {
-  new SpinningCubeView(app_impl_, callback);
+    const mojo::ui::ViewProvider::CreateViewCallback& callback) {
+  new SpinningCubeView(app_impl(), callback);
+  return true;
 }
 
 }  // namespace examples
diff --git a/examples/ui/spinning_cube/spinning_cube_app.h b/examples/ui/spinning_cube/spinning_cube_app.h
index d1e99e7..e658717 100644
--- a/examples/ui/spinning_cube/spinning_cube_app.h
+++ b/examples/ui/spinning_cube/spinning_cube_app.h
@@ -5,43 +5,22 @@
 #ifndef EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_APP_H_
 #define EXAMPLES_UI_SPINNING_CUBE_SPINNING_CUBE_APP_H_
 
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.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_impl.h"
-#include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/macros.h"
-#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+#include "mojo/ui/view_provider_app.h"
 
 namespace examples {
 
-class SpinningCubeApp : public mojo::ApplicationDelegate,
-                        public mojo::InterfaceFactory<mojo::ui::ViewProvider>,
-                        public mojo::ui::ViewProvider {
+class SpinningCubeApp : public mojo::ui::ViewProviderApp {
  public:
   SpinningCubeApp();
   ~SpinningCubeApp() 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:
-  // |ApplicationDelegate|:
-  void Initialize(mojo::ApplicationImpl* app) override;
-  bool ConfigureIncomingConnection(
-      mojo::ApplicationConnection* connection) override;
-
-  // |InterfaceFactory<mojo::ui::ViewProvider>|:
-  void Create(mojo::ApplicationConnection* connection,
-              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override;
-
-  // |ViewProvider|:
-  void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services,
-                  mojo::ServiceProviderPtr exposed_services,
-                  const CreateViewCallback& callback) override;
-
-  mojo::ApplicationImpl* app_impl_;
-  mojo::BindingSet<mojo::ui::ViewProvider> bindings_;
-
   DISALLOW_COPY_AND_ASSIGN(SpinningCubeApp);
 };
 
diff --git a/examples/ui/spinning_cube/spinning_cube_view.cc b/examples/ui/spinning_cube/spinning_cube_view.cc
index 8c3ce7a..68fb4f6 100644
--- a/examples/ui/spinning_cube/spinning_cube_view.cc
+++ b/examples/ui/spinning_cube/spinning_cube_view.cc
@@ -2,161 +2,191 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "examples/ui/spinning_cube/spinning_cube_view.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
 #include <GLES2/gl2.h>
 #include <GLES2/gl2extmojo.h>
-#include <MGL/mgl.h>
 
-#include "base/message_loop/message_loop.h"
-#include "examples/ui/spinning_cube/spinning_cube_view.h"
+#include <cmath>
+
+#include "base/bind.h"
+#include "mojo/services/geometry/cpp/geometry_util.h"
 
 namespace examples {
 
+namespace {
+constexpr uint32_t kCubeImageResourceId = 1;
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+
+// TODO(johngro) : investigate extending mojom with a formal flags type which it
+// generates good bindings for, so we don't need to resort to this.
+constexpr bool operator&(const mojo::EventFlags& f1,
+                         const mojo::EventFlags& f2) {
+  return ((static_cast<uint32_t>(f1) & static_cast<uint32_t>(f2)) != 0);
+}
+
+float CalculateDragDistance(const mojo::PointF& start,
+                            const mojo::PointF& end) {
+  return std::hypot(start.x - end.x, start.y - end.y);
+}
+
+float GetRandomColor() {
+  return static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+}
+
+// Return a direction multiplier to apply to drag distances:
+// 1 for natural (positive) motion, -1 for reverse (negative) motion
+int GetEventDirection(const mojo::PointF& current,
+                      const mojo::PointF& initial,
+                      const mojo::PointF& last) {
+  // Axis of motion is determined by coarse alignment of overall movement
+  bool use_x =
+      std::abs(current.y - initial.y) < std::abs(current.x - initial.x);
+  // Current direction is determined by comparison with previous point
+  float delta = use_x ? (current.x - last.x) : (current.y - last.y);
+  return delta > 0 ? -1 : 1;
+}
+}  // namespace
+
 SpinningCubeView::SpinningCubeView(
-    mojo::ApplicationImpl* app,
-    const mojo::ui::ViewProvider::CreateViewCallback& callback)
-    : callback_(callback),
-      binding_(this),
-      context_owner_(app->shell()),
-      texture_cache_(context_owner_.context(), &resource_returner_),
-      surface_id_namespace_(0),
-      draw_scheduled_(false),
+    mojo::ApplicationImpl* app_impl,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : GLView(app_impl, "SpinningCube", create_view_callback),
+      choreographer_(scene(), this),
+      input_handler_(view_service_provider(), this),
       weak_ptr_factory_(this) {
-  app->ConnectToService("mojo:surfaces_service", &surfaces_);
-  app->ConnectToService("mojo:view_manager_service", &view_manager_);
-
-  surfaces_->SetResourceReturner(resource_returner_.Pass());
-  surfaces_->GetIdNamespace(
-      base::Bind(&SpinningCubeView::OnSurfaceIdNamespaceAvailable,
-                 base::Unretained(this)));
-
-  InitCube();
+  gl_renderer()->gl_context()->MakeCurrent();
+  cube_.Init();
 }
 
 SpinningCubeView::~SpinningCubeView() {}
 
-void SpinningCubeView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) {
-  surface_id_namespace_ = id_namespace;
-  InitView();
-}
-
-void SpinningCubeView::InitView() {
-  mojo::ui::ViewPtr view;
-  binding_.Bind(mojo::GetProxy(&view));
-  view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_),
-                              callback_);
-
-  view_host_->GetServiceProvider(mojo::GetProxy(&view_service_provider_));
-}
-
 void SpinningCubeView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
                                 mojo::Array<uint32_t> children_needing_layout,
                                 const OnLayoutCallback& callback) {
-  // Create a new surface the first time or if the size has changed.
-  mojo::Size new_size;
-  new_size.width = layout_params->constraints->max_width;
-  new_size.height = layout_params->constraints->max_height;
-  if (!surface_id_ || !size_.Equals(new_size)) {
-    if (!surface_id_) {
-      surface_id_ = mojo::SurfaceId::New();
-      surface_id_->id_namespace = surface_id_namespace_;
-    } else {
-      surfaces_->DestroySurface(surface_id_->local);
-    }
-    surface_id_->local++;
-    size_ = new_size;
-    surfaces_->CreateSurface(surface_id_->local);
-  }
+  size_.width = layout_params->constraints->max_width;
+  size_.height = layout_params->constraints->max_height;
 
   // Submit the new layout information.
-  auto info = mojo::ui::ViewLayoutInfo::New();
+  auto info = mojo::ui::ViewLayoutResult::New();
   info->size = size_.Clone();
-  info->surface_id = surface_id_->Clone();
   callback.Run(info.Pass());
 
   // Draw!
-  ScheduleDraw();
+  choreographer_.ScheduleDraw();
 }
 
-void SpinningCubeView::OnChildUnavailable(
-    uint32_t child_key,
-    const OnChildUnavailableCallback& callback) {
-  callback.Run();
-}
-
-void SpinningCubeView::InitCube() {
-  context_owner_.context()->MakeCurrent();
-  cube_.Init();
-  last_draw_ = mojo::GetTimeTicksNow();
-}
-
-void SpinningCubeView::DrawCube() {
-  draw_scheduled_ = false;
-
-  scoped_ptr<mojo::TextureCache::TextureInfo> texture_info =
-      texture_cache_.GetTexture(size_);
-  if (!texture_info) {
-    LOG(ERROR) << "Could not allocate texture of size " << size_.width << "x"
-               << size_.height;
+void SpinningCubeView::OnEvent(mojo::EventPtr event,
+                               const OnEventCallback& callback) {
+  if (!event->pointer_data) {
+    callback.Run(false);
     return;
   }
 
-  context_owner_.context()->MakeCurrent();
-  scoped_ptr<mojo::GLTexture> texture = texture_info->TakeTexture();
+  switch (event->action) {
+    case mojo::EventType::POINTER_DOWN:
+      if (event->flags & mojo::EventFlags::RIGHT_MOUSE_BUTTON)
+        break;
+      capture_point_.x = event->pointer_data->x;
+      capture_point_.y = event->pointer_data->y;
+      last_drag_point_ = capture_point_;
+      drag_start_time_ = mojo::GetTimeTicksNow();
+      cube_.SetFlingMultiplier(0.0f, 1.0f);
+      break;
 
-  GLuint fbo = 0u;
-  glGenFramebuffers(1, &fbo);
-  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
-  GLuint depth_buffer = 0u;
-  glGenRenderbuffers(1, &depth_buffer);
-  glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
-  glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size_.width,
-                        size_.height);
-  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
-                         texture->texture_id(), 0);
-  glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
-                            GL_RENDERBUFFER, depth_buffer);
-  DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
-            glCheckFramebufferStatus(GL_FRAMEBUFFER));
-  glClearColor(1, 0, 0, 0.5);
+    case mojo::EventType::POINTER_MOVE: {
+      if (!(event->flags & mojo::EventFlags::LEFT_MOUSE_BUTTON) &&
+          event->pointer_data->kind == mojo::PointerKind::MOUSE) {
+        break;
+      }
+      mojo::PointF event_location;
+      event_location.x = event->pointer_data->x;
+      event_location.y = event->pointer_data->y;
+      int direction =
+          GetEventDirection(event_location, capture_point_, last_drag_point_);
+      cube_.UpdateForDragDistance(
+          direction * CalculateDragDistance(last_drag_point_, event_location));
+      last_drag_point_ = event_location;
+      break;
+    }
 
-  cube_.set_size(size_.width, size_.height);
+    case mojo::EventType::POINTER_UP: {
+      if (event->flags & mojo::EventFlags::RIGHT_MOUSE_BUTTON) {
+        cube_.set_color(GetRandomColor(), GetRandomColor(), GetRandomColor());
+        break;
+      }
+      mojo::PointF event_location;
+      event_location.x = event->pointer_data->x;
+      event_location.y = event->pointer_data->y;
+      MojoTimeTicks offset = mojo::GetTimeTicksNow() - drag_start_time_;
+      float delta = static_cast<float>(offset) / 1000000.f;
+      // Last drag point is the same as current point here; use initial capture
+      // point instead
+      int direction =
+          GetEventDirection(event_location, capture_point_, capture_point_);
+      cube_.SetFlingMultiplier(
+          direction * CalculateDragDistance(capture_point_, event_location),
+          delta);
+      capture_point_ = last_drag_point_ = mojo::PointF();
+      break;
+    }
 
-  MojoTimeTicks now = mojo::GetTimeTicksNow();
-  MojoTimeTicks offset = now - last_draw_;
-  cube_.UpdateForTimeDelta(offset * 0.000001f);
-  last_draw_ = now;
-
-  cube_.Draw();
-
-  glDeleteFramebuffers(1, &fbo);
-  glDeleteRenderbuffers(1, &depth_buffer);
-
-  mojo::FramePtr frame = mojo::TextureUploader::GetUploadFrame(
-      context_owner_.context(), texture_info->resource_id(), texture);
-  surfaces_->SubmitFrame(surface_id_->local, frame.Pass(),
-                         base::Bind(&SpinningCubeView::OnSurfaceSubmitted,
-                                    base::Unretained(this)));
-
-  texture_cache_.NotifyPendingResourceReturn(texture_info->resource_id(),
-                                             texture.Pass());
-}
-
-void SpinningCubeView::OnSurfaceSubmitted() {
-  ScheduleDraw();
-}
-
-void SpinningCubeView::ScheduleDraw() {
-  if (!draw_scheduled_) {
-    draw_scheduled_ = true;
-
-    // TODO(jeffbrown): For now, we need to throttle this down because
-    // drawing as fast as we can appears to cause starvation of the
-    // Mojo message loop and makes X11 unhappy.
-    base::MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        base::Bind(&SpinningCubeView::DrawCube, weak_ptr_factory_.GetWeakPtr()),
-        base::TimeDelta::FromMilliseconds(30));
+    default:
+      break;
   }
+
+  callback.Run(true);
+}
+
+void SpinningCubeView::OnDraw(
+    const mojo::gfx::composition::FrameInfo& frame_info,
+    const base::TimeDelta& time_delta) {
+  // Update the state of the cube.
+  cube_.UpdateForTimeDelta(time_delta.InSecondsF());
+
+  // Update the contents of the scene.
+  mojo::Rect bounds;
+  bounds.width = size_.width;
+  bounds.height = size_.height;
+
+  auto update = mojo::gfx::composition::SceneUpdate::New();
+  mojo::gfx::composition::ResourcePtr cube_resource = gl_renderer()->DrawGL(
+      size_, true,
+      base::Bind(&SpinningCubeView::DrawCubeWithGL, base::Unretained(this)));
+  DCHECK(cube_resource);
+  update->resources.insert(kCubeImageResourceId, cube_resource.Pass());
+
+  auto root_node = mojo::gfx::composition::Node::New();
+  root_node->content_transform = mojo::Transform::New();
+  mojo::SetIdentityTransform(root_node->content_transform.get());
+  // TODO(jeffbrown): Figure out why spinning cube is drawing upside down.
+  // Other GL based programs don't seem to have this problem.
+  root_node->content_transform->matrix[5] = -1;  // flip image vertically
+  root_node->content_transform->matrix[7] = size_.height;
+  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 = kCubeImageResourceId;
+  update->nodes.insert(kRootNodeId, root_node.Pass());
+
+  auto metadata = mojo::gfx::composition::SceneMetadata::New();
+  metadata->presentation_time = frame_info.presentation_time;
+
+  // Publish the scene.
+  scene()->Update(update.Pass());
+  scene()->Publish(metadata.Pass());
+
+  // Loop!
+  choreographer_.ScheduleDraw();
+}
+
+void SpinningCubeView::DrawCubeWithGL() {
+  cube_.set_size(size_.width, size_.height);
+  cube_.Draw();
 }
 
 }  // namespace examples
diff --git a/examples/ui/spinning_cube/spinning_cube_view.h b/examples/ui/spinning_cube/spinning_cube_view.h
index 1a5b7fe..e65220c 100644
--- a/examples/ui/spinning_cube/spinning_cube_view.h
+++ b/examples/ui/spinning_cube/spinning_cube_view.h
@@ -7,71 +7,48 @@
 
 #include <memory>
 
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
 #include "examples/spinning_cube/spinning_cube.h"
-#include "mojo/gpu/gl_context.h"
-#include "mojo/gpu/gl_context_owner.h"
-#include "mojo/gpu/gl_texture.h"
-#include "mojo/gpu/texture_cache.h"
-#include "mojo/gpu/texture_uploader.h"
-#include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/environment/environment.h"
-#include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/macros.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"
-#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
-#include "mojo/services/ui/views/interfaces/views.mojom.h"
+#include "mojo/ui/choreographer.h"
+#include "mojo/ui/gl_view.h"
+#include "mojo/ui/input_handler.h"
 
 namespace examples {
 
-class SpinningCubeView : public mojo::ui::View {
+class SpinningCubeView : public mojo::ui::GLView,
+                         public mojo::ui::ChoreographerDelegate,
+                         public mojo::ui::InputListener {
  public:
-  SpinningCubeView(mojo::ApplicationImpl* app,
-                   const mojo::ui::ViewProvider::CreateViewCallback& callback);
+  SpinningCubeView(
+      mojo::ApplicationImpl* app_impl,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
 
   ~SpinningCubeView() override;
 
  private:
-  // |View|:
+  // |GLView|:
   void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
                 mojo::Array<uint32_t> children_needing_layout,
                 const OnLayoutCallback& callback) override;
-  void OnChildUnavailable(uint32_t child_key,
-                          const OnChildUnavailableCallback& callback) override;
 
-  void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace);
+  // |ChoreographerDelegate|:
+  void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info,
+              const base::TimeDelta& time_delta) override;
 
-  void InitView();
+  // |InputListener|:
+  void OnEvent(mojo::EventPtr event, const OnEventCallback& callback) override;
 
-  void InitCube();
-  void DrawCube();
-  void OnSurfaceSubmitted();
-  void ScheduleDraw();
+  void DrawCubeWithGL();
 
-  mojo::ui::ViewProvider::CreateViewCallback callback_;
-  mojo::StrongBinding<mojo::ui::View> binding_;
-
-  mojo::GLContextOwner context_owner_;
-  mojo::ResourceReturnerPtr resource_returner_;  // must be before texture cache
-  mojo::TextureCache texture_cache_;
-
-  mojo::SurfacePtr surfaces_;
-  mojo::SurfaceIdPtr surface_id_;
-  uint32_t surface_id_namespace_;
-
-  mojo::ui::ViewManagerPtr view_manager_;
-  mojo::ui::ViewHostPtr view_host_;
-  mojo::ServiceProviderPtr view_service_provider_;
+  mojo::ui::Choreographer choreographer_;
+  mojo::ui::InputHandler input_handler_;
 
   mojo::Size size_;
 
   SpinningCube cube_;
-  MojoTimeTicks last_draw_;
-  bool draw_scheduled_;
+
+  mojo::PointF capture_point_;
+  mojo::PointF last_drag_point_;
+  MojoTimeTicks drag_start_time_ = 0u;
 
   base::WeakPtrFactory<SpinningCubeView> weak_ptr_factory_;
 
diff --git a/examples/ui/tile/BUILD.gn b/examples/ui/tile/BUILD.gn
index 1727e09..3393ba4 100644
--- a/examples/ui/tile/BUILD.gn
+++ b/examples/ui/tile/BUILD.gn
@@ -22,15 +22,17 @@
     "//mojo/environment:chromium",
     "//mojo/gpu",
     "//mojo/public/c/gpu",
-    "//mojo/public/c/gpu:gpu_onscreen",
     "//mojo/public/cpp/bindings",
     "//mojo/public/cpp/environment",
     "//mojo/public/cpp/system",
+    "//mojo/services/geometry/cpp",
     "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
     "//mojo/services/input_events/interfaces",
-    "//mojo/services/surfaces/cpp",
-    "//mojo/services/surfaces/interfaces",
     "//mojo/services/ui/views/interfaces",
+    "//mojo/ui",
+    "//mojo/ui:ganesh",
+    "//mojo/ui:gl",
     "//url",
   ]
 }
diff --git a/examples/ui/tile/tile_app.cc b/examples/ui/tile/tile_app.cc
index 3360e1c..2f5d4a4 100644
--- a/examples/ui/tile/tile_app.cc
+++ b/examples/ui/tile/tile_app.cc
@@ -11,55 +11,27 @@
 
 namespace examples {
 
-class TileViewProvider : public mojo::ui::ViewProvider {
- public:
-  TileViewProvider(mojo::ApplicationImpl* app_impl,
-                   const std::vector<std::string>& view_urls)
-      : app_impl_(app_impl), view_urls_(view_urls) {}
-  ~TileViewProvider() override {}
-
- private:
-  // |ViewProvider|:
-  void CreateView(mojo::InterfaceRequest<mojo::ServiceProvider> services,
-                  mojo::ServiceProviderPtr exposed_services,
-                  const CreateViewCallback& callback) override {
-    new TileView(app_impl_, view_urls_, callback);
-  }
-
-  mojo::ApplicationImpl* app_impl_;
-  std::vector<std::string> view_urls_;
-
-  DISALLOW_COPY_AND_ASSIGN(TileViewProvider);
-};
-
 TileApp::TileApp() {}
 
 TileApp::~TileApp() {}
 
-void TileApp::Initialize(mojo::ApplicationImpl* app_impl) {
-  app_impl_ = app_impl;
-}
-
-bool TileApp::ConfigureIncomingConnection(
-    mojo::ApplicationConnection* connection) {
-  connection->AddService<mojo::ui::ViewProvider>(this);
-  return true;
-}
-
-void TileApp::Create(mojo::ApplicationConnection* connection,
-                     mojo::InterfaceRequest<mojo::ui::ViewProvider> request) {
-  GURL url(connection->GetConnectionURL());
+bool TileApp::CreateView(
+    const std::string& connection_url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services,
+    const mojo::ui::ViewProvider::CreateViewCallback& callback) {
+  GURL url(connection_url);
   std::vector<std::string> view_urls;
   base::SplitString(url.query(), ',', &view_urls);
 
   if (view_urls.empty()) {
     LOG(ERROR) << "Must supply comma-delimited URLs of mojo views to tile as a "
                   "query parameter.";
-    return;
+    return false;
   }
 
-  bindings_.AddBinding(new TileViewProvider(app_impl_, view_urls),
-                       request.Pass());
+  new TileView(app_impl(), view_urls, callback);
+  return true;
 }
 
 }  // namespace examples
diff --git a/examples/ui/tile/tile_app.h b/examples/ui/tile/tile_app.h
index 35227db..1b4d619 100644
--- a/examples/ui/tile/tile_app.h
+++ b/examples/ui/tile/tile_app.h
@@ -5,37 +5,22 @@
 #ifndef EXAMPLES_UI_TILE_TILE_APP_H_
 #define EXAMPLES_UI_TILE_TILE_APP_H_
 
-#include "base/bind.h"
-#include "base/memory/scoped_ptr.h"
-#include "mojo/common/strong_binding_set.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/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/macros.h"
-#include "mojo/services/ui/views/interfaces/view_provider.mojom.h"
+#include "mojo/ui/view_provider_app.h"
 
 namespace examples {
 
-class TileApp : public mojo::ApplicationDelegate,
-                public mojo::InterfaceFactory<mojo::ui::ViewProvider> {
+class TileApp : public mojo::ui::ViewProviderApp {
  public:
   TileApp();
   ~TileApp() override;
 
-  // |ApplicationDelegate|:
-  void Initialize(mojo::ApplicationImpl* app) override;
-  bool ConfigureIncomingConnection(
-      mojo::ApplicationConnection* connection) override;
-
-  // |InterfaceFactory<mojo::ui::ViewProvider>|:
-  void Create(mojo::ApplicationConnection* connection,
-              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) 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:
-  mojo::ApplicationImpl* app_impl_;
-  mojo::StrongBindingSet<mojo::ui::ViewProvider> bindings_;
-
   DISALLOW_COPY_AND_ASSIGN(TileApp);
 };
 
diff --git a/examples/ui/tile/tile_view.cc b/examples/ui/tile/tile_view.cc
index 34c984e..744f9c4 100644
--- a/examples/ui/tile/tile_view.cc
+++ b/examples/ui/tile/tile_view.cc
@@ -2,50 +2,38 @@
 // 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 "examples/ui/tile/tile_view.h"
-#include "mojo/services/surfaces/cpp/surfaces_utils.h"
-#include "mojo/services/surfaces/interfaces/quads.mojom.h"
+#include "mojo/services/geometry/cpp/geometry_util.h"
 
 namespace examples {
 
+namespace {
+constexpr uint32_t kViewResourceIdBase = 100;
+constexpr uint32_t kViewResourceIdSpacing = 100;
+
+constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId;
+constexpr uint32_t kViewNodeIdBase = 100;
+constexpr uint32_t kViewNodeIdSpacing = 100;
+constexpr uint32_t kViewSceneNodeIdOffset = 1;
+constexpr uint32_t kViewFallbackNodeIdOffset = 2;
+}  // namespace
+
 TileView::TileView(mojo::ApplicationImpl* app_impl,
                    const std::vector<std::string>& view_urls,
                    const mojo::ui::ViewProvider::CreateViewCallback& callback)
-    : app_impl_(app_impl),
-      view_urls_(view_urls),
-      callback_(callback),
-      binding_(this),
-      surface_id_namespace_(0),
-      pending_child_layout_count_(0),
-      frame_pending_(false),
-      weak_ptr_factory_(this) {
-  app_impl_->ConnectToService("mojo:surfaces_service", &surfaces_);
-  app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_);
-
-  surfaces_->GetIdNamespace(base::Bind(&TileView::OnSurfaceIdNamespaceAvailable,
-                                       base::Unretained(this)));
+    : BaseView(app_impl, "Tile", callback), view_urls_(view_urls) {
+  ConnectViews();
 }
 
 TileView::~TileView() {}
 
-void TileView::OnSurfaceIdNamespaceAvailable(uint32_t id_namespace) {
-  surface_id_namespace_ = id_namespace;
-  InitView();
-}
-
-void TileView::InitView() {
-  // Register the view.
-  mojo::ui::ViewPtr view;
-  binding_.Bind(mojo::GetProxy(&view));
-  view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_),
-                              callback_);
-
-  // Connect to all child views.
+void TileView::ConnectViews() {
   uint32_t child_key = 0;
   for (const auto& url : view_urls_) {
     // Start connecting to the view provider.
     mojo::ui::ViewProviderPtr provider;
-    app_impl_->ConnectToService(url, &provider);
+    app_impl()->ConnectToService(url, &provider);
     provider.set_connection_error_handler(
         base::Bind(&TileView::OnChildConnectionError, base::Unretained(this),
                    child_key, url));
@@ -76,9 +64,9 @@
   DCHECK(views_.find(child_key) == views_.end());
   LOG(INFO) << "View created: child_key=" << child_key << ", url=" << url;
 
-  view_host_->AddChild(child_key, token.Pass());
-  views_.emplace(
-      std::make_pair(child_key, std::unique_ptr<ViewData>(new ViewData(url))));
+  view_host()->AddChild(child_key, token.Pass());
+  views_.emplace(std::make_pair(
+      child_key, std::unique_ptr<ViewData>(new ViewData(url, child_key))));
 
   // Note that the view provider will be destroyed once this function
   // returns which is fine now that we are done creating the view.
@@ -94,7 +82,7 @@
   std::unique_ptr<ViewData> view_data = std::move(it->second);
   views_.erase(it);
 
-  view_host_->RemoveChild(child_key);
+  view_host()->RemoveChild(child_key);
 
   if (view_data->layout_pending) {
     DCHECK(pending_child_layout_count_);
@@ -108,21 +96,8 @@
 void TileView::OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
                         mojo::Array<uint32_t> children_needing_layout,
                         const OnLayoutCallback& callback) {
-  // Create a new surface the first time or if the size has changed.
-  mojo::Size new_size;
-  new_size.width = layout_params->constraints->max_width;
-  new_size.height = layout_params->constraints->max_height;
-  if (!surface_id_ || !size_.Equals(new_size)) {
-    if (!surface_id_) {
-      surface_id_ = mojo::SurfaceId::New();
-      surface_id_->id_namespace = surface_id_namespace_;
-    } else {
-      surfaces_->DestroySurface(surface_id_->local);
-    }
-    surface_id_->local++;
-    size_ = new_size;
-    surfaces_->CreateSurface(surface_id_->local);
-  }
+  size_.width = layout_params->constraints->max_width;
+  size_.height = layout_params->constraints->max_height;
 
   // Wipe out cached layout information for children needing layout.
   for (uint32_t child_key : children_needing_layout) {
@@ -134,8 +109,8 @@
   // Layout all children in a row.
   if (!views_.empty()) {
     uint32_t index = 0;
-    uint32_t base_width = new_size.width / views_.size();
-    uint32_t excess_width = new_size.width % views_.size();
+    uint32_t base_width = size_.width / views_.size();
+    uint32_t excess_width = size_.width % views_.size();
     uint32_t x = 0;
     for (auto it = views_.begin(); it != views_.end(); ++it, ++index) {
       ViewData* view_data = it->second.get();
@@ -147,7 +122,7 @@
         child_width++;
         excess_width--;
       }
-      uint32_t child_height = new_size.height;
+      uint32_t child_height = size_.height;
       uint32_t child_x = x;
       x += child_width;
 
@@ -172,9 +147,9 @@
       view_data->layout_params = params.Clone();
       view_data->layout_info.reset();
 
-      view_host_->LayoutChild(it->first, params.Pass(),
-                              base::Bind(&TileView::OnChildLayoutFinished,
-                                         base::Unretained(this), it->first));
+      view_host()->LayoutChild(it->first, params.Pass(),
+                               base::Bind(&TileView::OnChildLayoutFinished,
+                                          base::Unretained(this), it->first));
     }
   }
 
@@ -199,7 +174,7 @@
 }
 
 void TileView::FinishLayout() {
-  if (frame_pending_ || pending_layout_callback_.is_null())
+  if (pending_layout_callback_.is_null())
     return;
 
   // Wait until all children have laid out.
@@ -207,76 +182,91 @@
   if (pending_child_layout_count_)
     return;
 
-  // Produce a new frame.
-  mojo::FramePtr frame = mojo::Frame::New();
-  frame->resources.resize(0u);
+  // Update the scene.
+  // TODO: only send the resources once, be more incremental
+  auto update = mojo::gfx::composition::SceneUpdate::New();
 
-  mojo::Rect bounds;
-  bounds.width = size_.width;
-  bounds.height = size_.height;
-  mojo::PassPtr pass = mojo::CreateDefaultPass(1, bounds);
-  pass->shared_quad_states.resize(0u);
-  pass->quads.resize(0u);
+  // Create the root node.
+  auto root_node = mojo::gfx::composition::Node::New();
 
+  // Add the children.
   for (auto it = views_.cbegin(); it != views_.cend(); it++) {
     const ViewData& view_data = *(it->second.get());
+    const uint32_t scene_resource_id =
+        kViewResourceIdBase + view_data.key * kViewResourceIdSpacing;
+    const uint32_t container_node_id =
+        kViewNodeIdBase + view_data.key * kViewNodeIdSpacing;
+    const uint32_t scene_node_id = container_node_id + kViewSceneNodeIdOffset;
+    const uint32_t fallback_node_id =
+        container_node_id + kViewFallbackNodeIdOffset;
 
-    mojo::QuadPtr quad = mojo::Quad::New();
-    quad->rect = view_data.layout_bounds.Clone();
-    quad->rect->x = 0;
-    quad->rect->y = 0;
-    quad->opaque_rect = quad->rect.Clone();
-    quad->visible_rect = quad->rect.Clone();
-    quad->shared_quad_state_index = pass->shared_quad_states.size();
+    mojo::Rect extent;
+    extent.width = view_data.layout_bounds.width;
+    extent.height = view_data.layout_bounds.height;
 
-    mojo::Size size;
-    size.width = view_data.layout_bounds.width;
-    size.height = view_data.layout_bounds.height;
+    // Create a container to represent the place where the child view
+    // will be presented.  The children of the container provide
+    // fallback behavior in case the view is not available.
+    auto container_node = mojo::gfx::composition::Node::New();
+    container_node->content_clip = extent.Clone();
+    container_node->content_transform = mojo::Transform::New();
+    SetTranslationTransform(container_node->content_transform.get(),
+                            view_data.layout_bounds.x,
+                            view_data.layout_bounds.y, 0.f);
+    container_node->combinator =
+        mojo::gfx::composition::Node::Combinator::FALLBACK;
 
-    mojo::SharedQuadStatePtr quad_state = mojo::CreateDefaultSQS(size);
-    quad_state->content_to_target_transform->matrix[3] =
-        view_data.layout_bounds.x;
-    pass->shared_quad_states.push_back(quad_state.Pass());
+    // If we have the view, add it to the scene.
+    if (view_data.layout_info) {
+      auto scene_resource = mojo::gfx::composition::Resource::New();
+      scene_resource->set_scene(mojo::gfx::composition::SceneResource::New());
+      scene_resource->get_scene()->scene_token =
+          view_data.layout_info->scene_token.Clone();
+      update->resources.insert(scene_resource_id, scene_resource.Pass());
 
-    if (it->second->layout_info) {
-      quad->material = mojo::Material::SURFACE_CONTENT;
-      quad->surface_quad_state = mojo::SurfaceQuadState::New();
-      quad->surface_quad_state->surface =
-          view_data.layout_info->surface_id.Clone();
+      auto scene_node = mojo::gfx::composition::Node::New();
+      scene_node->op = mojo::gfx::composition::NodeOp::New();
+      scene_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New());
+      scene_node->op->get_scene()->scene_resource_id = scene_resource_id;
+      update->nodes.insert(scene_node_id, scene_node.Pass());
+      container_node->child_node_ids.push_back(scene_node_id);
     } 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 = 0xffff00ff;
+      update->resources.insert(fallback_node_id, nullptr);
+      update->nodes.insert(scene_node_id, nullptr);
     }
 
-    pass->quads.push_back(quad.Pass());
+    // Add the fallback content.
+    auto fallback_node = mojo::gfx::composition::Node::New();
+    fallback_node->op = mojo::gfx::composition::NodeOp::New();
+    fallback_node->op->set_rect(mojo::gfx::composition::RectNodeOp::New());
+    fallback_node->op->get_rect()->content_rect = extent.Clone();
+    fallback_node->op->get_rect()->color = mojo::gfx::composition::Color::New();
+    fallback_node->op->get_rect()->color->red = 255;
+    fallback_node->op->get_rect()->color->alpha = 255;
+    update->nodes.insert(fallback_node_id, fallback_node.Pass());
+    container_node->child_node_ids.push_back(fallback_node_id);
+
+    // Add the container.
+    update->nodes.insert(container_node_id, container_node.Pass());
+    root_node->child_node_ids.push_back(container_node_id);
   }
 
-  frame->passes.push_back(pass.Pass());
+  // Add the root node.
+  update->nodes.insert(kRootNodeId, root_node.Pass());
 
-  frame_pending_ = true;
-  surfaces_->SubmitFrame(
-      surface_id_->local, frame.Pass(),
-      base::Bind(&TileView::OnFrameSubmitted, base::Unretained(this)));
+  // Publish the scene.
+  scene()->Update(update.Pass());
+  scene()->Publish(nullptr);
 
   // Submit the new layout information.
-  mojo::ui::ViewLayoutInfoPtr info = mojo::ui::ViewLayoutInfo::New();
+  auto info = mojo::ui::ViewLayoutResult::New();
   info->size = size_.Clone();
-  info->surface_id = surface_id_->Clone();
   pending_layout_callback_.Run(info.Pass());
   pending_layout_callback_.reset();
 }
 
-void TileView::OnFrameSubmitted() {
-  DCHECK(frame_pending_);
-
-  frame_pending_ = false;
-  FinishLayout();
-}
-
-TileView::ViewData::ViewData(const std::string& url)
-    : url(url), layout_pending(false) {}
+TileView::ViewData::ViewData(const std::string& url, uint32_t key)
+    : url(url), key(key), layout_pending(false) {}
 
 TileView::ViewData::~ViewData() {}
 
diff --git a/examples/ui/tile/tile_view.h b/examples/ui/tile/tile_view.h
index a0132cf..085e431 100644
--- a/examples/ui/tile/tile_view.h
+++ b/examples/ui/tile/tile_view.h
@@ -8,22 +8,11 @@
 #include <map>
 #include <memory>
 
-#include "base/bind.h"
-#include "base/memory/weak_ptr.h"
-#include "mojo/public/cpp/application/application_impl.h"
-#include "mojo/public/cpp/bindings/strong_binding.h"
-#include "mojo/public/cpp/environment/environment.h"
-#include "mojo/public/cpp/system/core.h"
-#include "mojo/public/cpp/system/macros.h"
-#include "mojo/services/geometry/interfaces/geometry.mojom.h"
-#include "mojo/services/surfaces/interfaces/surfaces.mojom.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"
+#include "mojo/ui/base_view.h"
 
 namespace examples {
 
-class TileView : public mojo::ui::View {
+class TileView : public mojo::ui::BaseView {
  public:
   TileView(mojo::ApplicationImpl* app_impl_,
            const std::vector<std::string>& view_urls,
@@ -33,10 +22,11 @@
 
  private:
   struct ViewData {
-    explicit ViewData(const std::string& url);
+    explicit ViewData(const std::string& url, uint32_t key);
     ~ViewData();
 
     const std::string url;
+    const uint32_t key;
 
     bool layout_pending;
     mojo::ui::ViewLayoutParamsPtr layout_params;
@@ -44,16 +34,14 @@
     mojo::Rect layout_bounds;
   };
 
-  // |View|:
+  // |BaseView|:
   void OnLayout(mojo::ui::ViewLayoutParamsPtr layout_params,
                 mojo::Array<uint32_t> children_needing_layout,
                 const OnLayoutCallback& callback) override;
   void OnChildUnavailable(uint32_t child_key,
                           const OnChildUnavailableCallback& callback) override;
 
-  void OnSurfaceIdNamespaceAvailable(uint32_t id_namespace);
-
-  void InitView();
+  void ConnectViews();
   void OnChildConnectionError(uint32_t child_key, const std::string& url);
   void OnChildCreated(uint32_t child_key,
                       const std::string& url,
@@ -65,26 +53,12 @@
 
   void OnFrameSubmitted();
 
-  mojo::ApplicationImpl* app_impl_;
   std::vector<std::string> view_urls_;
-  mojo::ui::ViewProvider::CreateViewCallback callback_;
-  mojo::StrongBinding<mojo::ui::View> binding_;
-
-  mojo::SurfacePtr surfaces_;
-  mojo::SurfaceIdPtr surface_id_;
-  uint32_t surface_id_namespace_;
-
-  mojo::ui::ViewManagerPtr view_manager_;
-  mojo::ui::ViewHostPtr view_host_;
-
   std::map<uint32_t, std::unique_ptr<ViewData>> views_;
 
   mojo::Size size_;
   OnLayoutCallback pending_layout_callback_;
-  uint32_t pending_child_layout_count_;
-  bool frame_pending_;
-
-  base::WeakPtrFactory<TileView> weak_ptr_factory_;
+  uint32_t pending_child_layout_count_ = 0u;
 
   DISALLOW_COPY_AND_ASSIGN(TileView);
 };