Add helpers for creating UI components.

These helpers make it easier to use Mozart in C++ and greatly
reduce the amount of boilerplate involved in writing simple
applications.

View implementations:

- BaseView: A base implementation of the View interface.
- GLView: A View with an associated GLRenderer which takes care
  of allocating, binding, and recycling textures.
- GaneshView: A View with an associated GaneshRenderer which takes
  care of setting up a GaneshContext and drawing to canvas.

View providers:

- ViewProviderApp: Skeleton of a simple app which offers the
  ViewProvider interface and vends Views on demand.
- ContextViewerApp: Skeleton of a simple app which offers the
  ContentHandler interface and vends ViewProviders on demand.

Helpers:

- Choreographer: Coordinates the scheduling of drawing operations
  on behalf of a View and compensates for lag.
- InputHandler: Binds an InputListener on behalf of a View.

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

Review URL: https://codereview.chromium.org/1556803002 .
diff --git a/mojo/BUILD.gn b/mojo/BUILD.gn
index 915c5b4..a5ef8c7 100644
--- a/mojo/BUILD.gn
+++ b/mojo/BUILD.gn
@@ -71,6 +71,7 @@
     "//mojo/services/files/cpp:files_impl_apptests",
     "//mojo/services/log/cpp:log_client_apptests",
     "//mojo/tools:message_generator",
+    "//mojo/ui:unittests",
   ]
 
   if (mojo_use_prebuilt_network_service) {
diff --git a/mojo/common/strong_binding_set.h b/mojo/common/strong_binding_set.h
index 95467d8..99b25ea 100644
--- a/mojo/common/strong_binding_set.h
+++ b/mojo/common/strong_binding_set.h
@@ -44,6 +44,16 @@
     });
   }
 
+  // Removes all bindings for the specified interface implementation.
+  // The implementation object is not destroyed.
+  void RemoveBindings(Interface* impl) {
+    bindings_.erase(
+        std::remove_if(bindings_.begin(), bindings_.end(),
+                       [impl](const std::unique_ptr<Binding<Interface>>& b) {
+                         return (b->impl() == impl);
+                       }));
+  }
+
   // Closes all bindings and deletes their associated interfaces.
   void CloseAllBindings() {
     for (auto it = bindings_.begin(); it != bindings_.end(); ++it) {
diff --git a/mojo/ui/BUILD.gn b/mojo/ui/BUILD.gn
new file mode 100644
index 0000000..a6c7b5a
--- /dev/null
+++ b/mojo/ui/BUILD.gn
@@ -0,0 +1,112 @@
+# 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")
+
+source_set("ui") {
+  sources = [
+    "base_view.cc",
+    "base_view.h",
+    "choreographer.cc",
+    "choreographer.h",
+    "input_handler.cc",
+    "input_handler.h",
+    "view_provider_app.cc",
+    "view_provider_app.h",
+  ]
+
+  deps = [
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/public/cpp/bindings",
+    "//mojo/public/interfaces/application",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/input/interfaces",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
+
+source_set("content") {
+  sources = [
+    "content_viewer_app.cc",
+    "content_viewer_app.h",
+  ]
+
+  deps = [
+    ":ui",
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/content_handler/interfaces",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
+
+source_set("gl") {
+  sources = [
+    "gl_renderer.cc",
+    "gl_renderer.h",
+    "gl_view.cc",
+    "gl_view.h",
+  ]
+
+  deps = [
+    ":ui",
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/gpu",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/views/interfaces",
+  ]
+}
+
+source_set("ganesh") {
+  sources = [
+    "ganesh_renderer.cc",
+    "ganesh_renderer.h",
+    "ganesh_view.cc",
+    "ganesh_view.h",
+  ]
+
+  deps = [
+    ":gl",
+    ":ui",
+    "//base",
+    "//mojo/application",
+    "//mojo/common",
+    "//mojo/gpu",
+    "//mojo/public/cpp/bindings",
+    "//mojo/services/gfx/composition/interfaces",
+    "//mojo/services/ui/views/interfaces",
+    "//mojo/skia",
+    "//skia",
+  ]
+}
+
+mojo_native_application("unittests") {
+  output_name = "ui_unittests"
+
+  testonly = true
+
+  sources = [
+    "gl_renderer_unittest.cc",
+  ]
+
+  deps = [
+    ":gl",
+    ":ui",
+    "//base",
+    "//mojo/application",
+    "//mojo/application:test_support",
+    "//mojo/gpu",
+    "//mojo/public/cpp/bindings:callback",
+    "//mojo/services/geometry/interfaces",
+    "//mojo/services/gfx/composition/interfaces",
+    "//testing/gtest",
+  ]
+}
diff --git a/mojo/ui/base_view.cc b/mojo/ui/base_view.cc
new file mode 100644
index 0000000..b58faf4
--- /dev/null
+++ b/mojo/ui/base_view.cc
@@ -0,0 +1,36 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/base_view.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace ui {
+
+BaseView::BaseView(
+    mojo::ApplicationImpl* app_impl,
+    const std::string& label,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : app_impl_(app_impl), view_binding_(this) {
+  DCHECK(app_impl_);
+  app_impl_->ConnectToService("mojo:view_manager_service", &view_manager_);
+
+  mojo::ui::ViewPtr view;
+  view_binding_.Bind(mojo::GetProxy(&view));
+  view_manager_->RegisterView(view.Pass(), mojo::GetProxy(&view_host_), label,
+                              create_view_callback);
+  view_host_->CreateScene(mojo::GetProxy(&scene_));
+  view_host_->GetServiceProvider(mojo::GetProxy(&view_service_provider_));
+}
+
+BaseView::~BaseView() {}
+
+void BaseView::OnChildUnavailable(uint32_t child_key,
+                                  const OnChildUnavailableCallback& callback) {
+  callback.Run();
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/base_view.h b/mojo/ui/base_view.h
new file mode 100644
index 0000000..757dcb9
--- /dev/null
+++ b/mojo/ui/base_view.h
@@ -0,0 +1,80 @@
+// 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 MOJO_UI_BASE_VIEW_H_
+#define MOJO_UI_BASE_VIEW_H_
+
+#include <string>
+
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/gfx/composition/interfaces/scenes.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"
+
+namespace mojo {
+namespace ui {
+
+// Abstract base implementation of the View interface for simple applications.
+// Subclasses must handle layout and provide content for the scene by
+// implementing the methods of the |View| mojom interface.
+//
+// It is not necessary to use this class to implement all Views.
+// This class is merely intended to make the simple apps easier to write.
+class BaseView : public mojo::ui::View {
+ public:
+  // TODO(jeffbrown): Consider switching this over to an ApplicationConnector
+  // but having ApplicationImpl is handy for simple examples.
+  BaseView(
+      mojo::ApplicationImpl* app_impl,
+      const std::string& label,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
+
+  ~BaseView() override;
+
+  // Gets the application implementation object provided at creation time.
+  mojo::ApplicationImpl* app_impl() { return app_impl_; }
+
+  // Gets the view manager.
+  mojo::ui::ViewManager* view_manager() { return view_manager_.get(); }
+
+  // Gets the view host for the view.
+  mojo::ui::ViewHost* view_host() { return view_host_.get(); }
+
+  // Gets the service provider for the view.
+  mojo::ServiceProvider* view_service_provider() {
+    return view_service_provider_.get();
+  }
+
+  // Gets the scene for the view.
+  // Returns nullptr if the |TakeScene| was called.
+  mojo::gfx::composition::Scene* scene() { return scene_.get(); }
+
+  // Takes the scene from the view.
+  // This is useful if the scene will be rendered by a separate component.
+  mojo::gfx::composition::ScenePtr TakeScene() { return scene_.Pass(); }
+
+  // |View|:
+  void OnChildUnavailable(uint32_t child_key,
+                          const OnChildUnavailableCallback& callback) override;
+
+ private:
+  mojo::ApplicationImpl* app_impl_;
+
+  mojo::StrongBinding<mojo::ui::View> view_binding_;
+  mojo::ui::ViewManagerPtr view_manager_;
+  mojo::ui::ViewHostPtr view_host_;
+  mojo::ServiceProviderPtr view_service_provider_;
+  mojo::gfx::composition::ScenePtr scene_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(BaseView);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_BASE_VIEW_H_
diff --git a/mojo/ui/choreographer.cc b/mojo/ui/choreographer.cc
new file mode 100644
index 0000000..7ea295b
--- /dev/null
+++ b/mojo/ui/choreographer.cc
@@ -0,0 +1,119 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/choreographer.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/system/functions.h"
+
+namespace mojo {
+namespace ui {
+
+Choreographer::Choreographer(mojo::gfx::composition::Scene* scene,
+                             ChoreographerDelegate* delegate)
+    : delegate_(delegate) {
+  DCHECK(delegate_);
+  scene->GetScheduler(mojo::GetProxy(&scene_scheduler_));
+}
+
+Choreographer::Choreographer(
+    mojo::gfx::composition::SceneSchedulerPtr scene_scheduler,
+    ChoreographerDelegate* delegate)
+    : scene_scheduler_(scene_scheduler.Pass()), delegate_(delegate) {
+  DCHECK(scene_scheduler_);
+  DCHECK(delegate_);
+}
+
+Choreographer::~Choreographer() {}
+
+void Choreographer::ScheduleDraw() {
+  if (!draw_scheduled_) {
+    draw_scheduled_ = true;
+    ScheduleFrame();
+  }
+}
+
+void Choreographer::ScheduleFrame() {
+  if (!frame_scheduled_) {
+    frame_scheduled_ = true;
+    scene_scheduler_->ScheduleFrame(
+        base::Bind(&Choreographer::DoFrame, base::Unretained(this)));
+  }
+}
+
+void Choreographer::DoFrame(mojo::gfx::composition::FrameInfoPtr frame_info) {
+  DCHECK(frame_info);
+  DCHECK(frame_scheduled_);
+  frame_scheduled_ = false;
+
+  if (draw_scheduled_) {
+    draw_scheduled_ = false;
+
+    // To reduce latency and jank, anticipate the next frame to be drawn by
+    // scheduling it early.
+    //
+    // TODO(jeffbrown): Reenable this once issue #604 is fixed.  Unfortunately
+    // this exacerbates starvation issues in the Mojo message pump.
+    // ScheduleFrame();
+
+    // Ensure frame info is sane since it comes from another service.
+    // TODO(jeffbrown): Would be better to report an error to the client
+    // who can shut things down if needed.
+    MojoTimeTicks now = MojoGetTimeTicksNow();
+    if (frame_info->frame_time > now) {
+      LOG(WARNING) << "Frame time is in the future: frame_time="
+                   << frame_info->frame_time << ", now=" << now;
+      frame_info->frame_time = now;
+    }
+    if (frame_info->frame_deadline < frame_info->frame_time) {
+      LOG(WARNING)
+          << "Frame deadline is earlier than frame time: frame_deadline="
+          << frame_info->frame_deadline
+          << ", frame_time=" << frame_info->frame_time << ", now=" << now;
+      frame_info->frame_deadline = frame_info->frame_time;
+    }
+    if (frame_info->presentation_time < frame_info->frame_deadline) {
+      LOG(WARNING) << "Presentation time is earlier than frame deadline: "
+                      "presentation_time="
+                   << frame_info->presentation_time
+                   << ", frame_deadline=" << frame_info->frame_deadline
+                   << ", now=" << now;
+      frame_info->presentation_time = frame_info->frame_deadline;
+    }
+
+    // Compensate for significant lag by adjusting the frame time if needed
+    // to step past skipped frames.
+    uint64_t lag = now - frame_info->frame_time;
+    if (frame_info->frame_interval > 0u && lag > frame_info->frame_interval) {
+      uint64_t offset = lag % frame_info->frame_interval;
+      uint64_t adjustment = now - offset - frame_info->frame_time;
+      frame_info->frame_time = now - offset;
+      frame_info->frame_deadline += adjustment;
+      frame_info->presentation_time += adjustment;
+
+      // Jank warning.
+      // TODO(jeffbrown): Suppress this once we're happy with things.
+      LOG(WARNING) << "Missed " << frame_info->frame_interval
+                   << " us frame deadline by " << lag << " us, skipping "
+                   << (lag / frame_info->frame_interval) << " frames";
+    }
+
+    // Ensure frame time isn't going backwards, just in case the compositor's
+    // timing is seriously broken.
+    base::TimeDelta time_delta;
+    if (last_frame_info_) {
+      DCHECK(frame_info->frame_time >= last_frame_info_->frame_time);
+      time_delta = base::TimeDelta::FromMicroseconds(
+          frame_info->frame_time - last_frame_info_->frame_time);
+    }
+
+    // Invoke the callback.
+    last_frame_info_ = frame_info.Pass();
+    delegate_->OnDraw(*last_frame_info_, time_delta);
+  }
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/choreographer.h b/mojo/ui/choreographer.h
new file mode 100644
index 0000000..3708cc2
--- /dev/null
+++ b/mojo/ui/choreographer.h
@@ -0,0 +1,98 @@
+// 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 MOJO_UI_CHOREOGRAPHER_H_
+#define MOJO_UI_CHOREOGRAPHER_H_
+
+#include "base/time/time.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/services/gfx/composition/interfaces/scenes.mojom.h"
+#include "mojo/services/gfx/composition/interfaces/scheduling.mojom.h"
+
+namespace mojo {
+namespace ui {
+
+class ChoreographerDelegate;
+
+// Coordinates drawing a frame of a scene.
+//
+// This class is intended to be included as a member of a View that wants to
+// schedule drawing using the following pattern.
+//
+// class MyView : public mojo::ui::BaseView,
+//                public mojo::ui::ChoreographerDelegate {
+//  public:
+//   MyView(mojo::ApplicationImpl* app_impl,
+//          const mojo::ui::ViewProvider::CreateViewCallback&
+//              create_view_callback)
+//          : BaseView(app_impl, "MyView", create_view_callback),
+//            choreographer_(scene_scheduler(), this) {}
+//   ~MyView() override {}
+//
+//  private:
+//   // |ChoreographerDelegate|:
+//   void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info) override;
+//
+//   mojo::ui::Choreographer choreographer_;
+//
+//   MOJO_DISALLOW_COPY_AND_ASSIGN(MyView);
+// };
+class Choreographer {
+ public:
+  Choreographer(mojo::gfx::composition::Scene* scene,
+                ChoreographerDelegate* delegate);
+  Choreographer(mojo::gfx::composition::SceneSchedulerPtr scene_scheduler,
+                ChoreographerDelegate* delegate);
+  ~Choreographer();
+
+  // Gets the scene scheduler.
+  mojo::gfx::composition::SceneScheduler* scene_scheduler() {
+    return scene_scheduler_.get();
+  }
+
+  // Gets the most recent frame info, or null if none.
+  mojo::gfx::composition::FrameInfo* last_frame_info() {
+    return last_frame_info_.get();
+  }
+
+  // Schedules a call to the delegate's |OnDraw| using the scene scheduler.
+  void ScheduleDraw();
+
+ private:
+  mojo::gfx::composition::SceneSchedulerPtr scene_scheduler_;
+  ChoreographerDelegate* delegate_;
+  mojo::gfx::composition::FrameInfoPtr last_frame_info_;
+
+  void ScheduleFrame();
+  void DoFrame(mojo::gfx::composition::FrameInfoPtr frame_info);
+
+  bool draw_scheduled_ = false;
+  bool frame_scheduled_ = false;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(Choreographer);
+};
+
+// An abstract class that the view may subclass to handle choreographer events.
+class ChoreographerDelegate {
+ public:
+  ChoreographerDelegate() = default;
+  virtual ~ChoreographerDelegate() = default;
+
+  // Called when it is time to draw the next frame and provides timing
+  // information for the frame.
+  //
+  // The |frame_info| provides information about the frame timing.
+  // The |time_delta| is the time interval since the last draw occurred,
+  // guaranteed to be non-negative.  Always zero for the first draw.
+  virtual void OnDraw(const mojo::gfx::composition::FrameInfo& frame_info,
+                      const base::TimeDelta& time_delta) = 0;
+
+ private:
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ChoreographerDelegate);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_CHOREOGRAPHER_H_
diff --git a/mojo/ui/content_viewer_app.cc b/mojo/ui/content_viewer_app.cc
new file mode 100644
index 0000000..6993082
--- /dev/null
+++ b/mojo/ui/content_viewer_app.cc
@@ -0,0 +1,74 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/content_viewer_app.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+
+namespace mojo {
+namespace ui {
+
+class ContentViewerApp::DelegatingContentHandler : public mojo::ContentHandler {
+ public:
+  DelegatingContentHandler(ContentViewerApp* app,
+                           const std::string& content_handler_url)
+      : app_(app), content_handler_url_(content_handler_url) {}
+
+  ~DelegatingContentHandler() override {}
+
+ private:
+  // |ContentHandler|:
+  void StartApplication(
+      mojo::InterfaceRequest<mojo::Application> application_request,
+      mojo::URLResponsePtr response) override {
+    app_->StartViewer(content_handler_url_, application_request.Pass(),
+                      response.Pass());
+  }
+
+  ContentViewerApp* app_;
+  std::string content_handler_url_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DelegatingContentHandler);
+};
+
+ContentViewerApp::ContentViewerApp() {}
+
+ContentViewerApp::~ContentViewerApp() {}
+
+void ContentViewerApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(app_impl_->args());
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+}
+
+bool ContentViewerApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::ContentHandler>(this);
+  return true;
+}
+
+void ContentViewerApp::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::ContentHandler> request) {
+  bindings_.AddBinding(
+      new DelegatingContentHandler(this, connection->GetConnectionURL()),
+      request.Pass());
+}
+
+void ContentViewerApp::StartViewer(
+    const std::string& content_handler_url,
+    mojo::InterfaceRequest<mojo::Application> application_request,
+    mojo::URLResponsePtr response) {
+  ViewProviderApp* app = LoadContent(content_handler_url, response.Pass());
+  if (app)
+    new mojo::ApplicationImpl(app, application_request.Pass());
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/content_viewer_app.h b/mojo/ui/content_viewer_app.h
new file mode 100644
index 0000000..a45ebf9
--- /dev/null
+++ b/mojo/ui/content_viewer_app.h
@@ -0,0 +1,72 @@
+// 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 MOJO_UI_CONTENT_VIEWER_APP_H_
+#define MOJO_UI_CONTENT_VIEWER_APP_H_
+
+#include "mojo/common/strong_binding_set.h"
+#include "mojo/services/content_handler/interfaces/content_handler.mojom.h"
+#include "mojo/ui/view_provider_app.h"
+
+namespace mojo {
+namespace ui {
+
+// A simple ContentHandler application implementation for rendering
+// content as Views.  Subclasses must provide a function to create
+// the view provider application on demand.
+//
+// TODO(jeffbrown): Support creating the view provider application in a
+// separate thread if desired (often not the case).  This is one reason
+// we are not using the ContentHandlerFactory here.
+class ContentViewerApp : public mojo::ApplicationDelegate,
+                         public mojo::InterfaceFactory<mojo::ContentHandler> {
+ public:
+  ContentViewerApp();
+  ~ContentViewerApp() override;
+
+  mojo::ApplicationImpl* app_impl() { return app_impl_; }
+
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // Called to create the view provider application to view the content.
+  //
+  // This method may be called multiple times to load different content
+  // into separate view providers.  The view provider implementation should
+  // cache the loaded content in case it is asked to create multiple instances
+  // of the view since the response can only be consumed once.
+  //
+  // The |content_handler_url| is the connection URL of the content handler
+  // request.
+  // The |response| carries the data retrieved by the content handler.
+  //
+  // Returns the view provider application delegate to view the content,
+  // or nullptr if the content could not be loaded.
+  virtual ViewProviderApp* LoadContent(const std::string& content_handler_url,
+                                       mojo::URLResponsePtr response) = 0;
+
+ private:
+  class DelegatingContentHandler;
+
+  // |InterfaceFactory<ContentHandler>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ContentHandler> request) override;
+
+  void StartViewer(
+      const std::string& content_handler_url,
+      mojo::InterfaceRequest<mojo::Application> application_request,
+      mojo::URLResponsePtr response);
+
+  mojo::ApplicationImpl* app_impl_ = nullptr;
+  mojo::StrongBindingSet<mojo::ContentHandler> bindings_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ContentViewerApp);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_CONTENT_VIEWER_APP_H_
diff --git a/mojo/ui/ganesh_renderer.cc b/mojo/ui/ganesh_renderer.cc
new file mode 100644
index 0000000..a1f47b3
--- /dev/null
+++ b/mojo/ui/ganesh_renderer.cc
@@ -0,0 +1,57 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/ganesh_renderer.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "mojo/gpu/gl_texture.h"
+#include "mojo/skia/ganesh_context.h"
+#include "mojo/skia/ganesh_texture_surface.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+namespace mojo {
+namespace ui {
+
+GaneshRenderer::GaneshRenderer(mojo::skia::GaneshContext* ganesh_context)
+    : ganesh_context_(ganesh_context),
+      gl_renderer_(ganesh_context_->gl_context()) {
+  DCHECK(ganesh_context_);
+}
+
+GaneshRenderer::~GaneshRenderer() {}
+
+mojo::gfx::composition::ResourcePtr GaneshRenderer::DrawSurface(
+    const mojo::Size& size,
+    const DrawSurfaceCallback& callback) {
+  std::unique_ptr<mojo::GLTexture> texture = gl_renderer_.GetTexture(size);
+  DCHECK(texture);
+
+  {
+    mojo::skia::GaneshContext::Scope scope(ganesh_context_);
+    mojo::skia::GaneshTextureSurface texture_surface(scope, std::move(texture));
+
+    callback.Run(texture_surface.surface());
+
+    texture = texture_surface.TakeTexture();
+  }
+
+  return gl_renderer_.BindTextureResource(std::move(texture));
+}
+
+static void RunCanvasCallback(
+    const mojo::ui::GaneshRenderer::DrawCanvasCallback& callback,
+    SkSurface* surface) {
+  callback.Run(surface->getCanvas());
+}
+
+mojo::gfx::composition::ResourcePtr GaneshRenderer::DrawCanvas(
+    const mojo::Size& size,
+    const DrawCanvasCallback& callback) {
+  return DrawSurface(size, base::Bind(&RunCanvasCallback, callback));
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/ganesh_renderer.h b/mojo/ui/ganesh_renderer.h
new file mode 100644
index 0000000..5190ecd
--- /dev/null
+++ b/mojo/ui/ganesh_renderer.h
@@ -0,0 +1,59 @@
+// 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 MOJO_UI_GANESH_RENDERER_H_
+#define MOJO_UI_GANESH_RENDERER_H_
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "mojo/services/gfx/composition/interfaces/resources.mojom.h"
+#include "mojo/ui/gl_renderer.h"
+
+class SkCanvas;
+class SkSurface;
+
+namespace mojo {
+namespace skia {
+class GaneshContext;
+}  // namespace skia
+
+namespace ui {
+
+// Provides support for rendering Skia commands into surfaces backed by
+// a pool of textures and producing scene resources for them.
+class GaneshRenderer {
+ public:
+  // Called with an active GaneshContext and a Skia surface or canvas
+  // to draw into the framebuffer.
+  using DrawSurfaceCallback = base::Callback<void(SkSurface* surface)>;
+  using DrawCanvasCallback = base::Callback<void(SkCanvas* canvas)>;
+
+  // Creates a Ganesh backed renderer.
+  // Does not take ownership of |ganesh_context|; it must outlive the renderer.
+  explicit GaneshRenderer(mojo::skia::GaneshContext* ganesh_context);
+  ~GaneshRenderer();
+
+  // Gets the Ganesh context.
+  mojo::skia::GaneshContext* ganesh_context() { return ganesh_context_; }
+
+  // Allocates a GL texture, binds it to a Ganesh surface or canvas,
+  // invokes the provided function, then returns the resulting resource.
+  mojo::gfx::composition::ResourcePtr DrawSurface(
+      const mojo::Size& size,
+      const DrawSurfaceCallback& callback);
+  mojo::gfx::composition::ResourcePtr DrawCanvas(
+      const mojo::Size& size,
+      const DrawCanvasCallback& callback);
+
+ private:
+  mojo::skia::GaneshContext* ganesh_context_;
+  mojo::ui::GLRenderer gl_renderer_;
+
+  DISALLOW_COPY_AND_ASSIGN(GaneshRenderer);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_GANESH_RENDERER_H_
diff --git a/mojo/ui/ganesh_view.cc b/mojo/ui/ganesh_view.cc
new file mode 100644
index 0000000..c700380
--- /dev/null
+++ b/mojo/ui/ganesh_view.cc
@@ -0,0 +1,27 @@
+// 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 "mojo/ui/ganesh_view.h"
+
+#include "base/logging.h"
+#include "mojo/skia/ganesh_texture_surface.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+
+namespace mojo {
+namespace ui {
+
+GaneshView::GaneshView(
+    mojo::ApplicationImpl* app_impl,
+    const std::string& label,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : BaseView(app_impl, label, create_view_callback),
+      gl_context_owner_(mojo::MakeProxy(app_impl->CreateApplicationConnector())
+                            .get()),
+      ganesh_context_(gl_context()),
+      ganesh_renderer_(&ganesh_context_) {}
+
+GaneshView::~GaneshView() {}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/ganesh_view.h b/mojo/ui/ganesh_view.h
new file mode 100644
index 0000000..7cf29e9
--- /dev/null
+++ b/mojo/ui/ganesh_view.h
@@ -0,0 +1,53 @@
+// 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 MOJO_UI_GANESH_VIEW_H_
+#define MOJO_UI_GANESH_VIEW_H_
+
+#include "mojo/gpu/gl_context.h"
+#include "mojo/gpu/gl_context_owner.h"
+#include "mojo/skia/ganesh_context.h"
+#include "mojo/ui/base_view.h"
+#include "mojo/ui/ganesh_renderer.h"
+
+class SkSurface;
+
+namespace mojo {
+namespace ui {
+
+// Abstract base implementation of the View interface for simple applications
+// which use Ganesh for rendering.  Subclasses must handle layout and provide
+// content for the scene.
+class GaneshView : public BaseView {
+ public:
+  GaneshView(
+      mojo::ApplicationImpl* app_impl,
+      const std::string& label,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
+
+  ~GaneshView() override;
+
+  // Gets the GL context, or null if none.
+  const base::WeakPtr<mojo::GLContext>& gl_context() const {
+    return gl_context_owner_.context();
+  }
+
+  // Gets the Ganesh context.
+  mojo::skia::GaneshContext* ganesh_context() { return &ganesh_context_; }
+
+  // Gets the Ganesh renderer.
+  mojo::ui::GaneshRenderer* ganesh_renderer() { return &ganesh_renderer_; }
+
+ private:
+  mojo::GLContextOwner gl_context_owner_;
+  mojo::skia::GaneshContext ganesh_context_;
+  mojo::ui::GaneshRenderer ganesh_renderer_;
+
+  DISALLOW_COPY_AND_ASSIGN(GaneshView);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_GANESH_VIEW_H_
diff --git a/mojo/ui/gl_renderer.cc b/mojo/ui/gl_renderer.cc
new file mode 100644
index 0000000..3adf507
--- /dev/null
+++ b/mojo/ui/gl_renderer.cc
@@ -0,0 +1,161 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/gl_renderer.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+#include <GLES2/gl2.h>
+#include <GLES2/gl2extmojo.h>
+
+#include "mojo/gpu/gl_context.h"
+#include "mojo/gpu/gl_texture.h"
+
+namespace mojo {
+namespace ui {
+
+GLRenderer::GLRenderer(base::WeakPtr<mojo::GLContext> gl_context,
+                       uint32_t max_recycled_textures)
+    : gl_context_(gl_context),
+      max_recycled_textures_(max_recycled_textures),
+      weak_factory_(this) {}
+
+GLRenderer::~GLRenderer() {}
+
+std::unique_ptr<mojo::GLTexture> GLRenderer::GetTexture(
+    const mojo::Size& requested_size) {
+  if (!gl_context_) {
+    recycled_textures_.clear();
+    return nullptr;
+  }
+
+  while (!recycled_textures_.empty()) {
+    GLRecycledTextureInfo texture_info(std::move(recycled_textures_.front()));
+    recycled_textures_.pop_front();
+    if (texture_info.first->size().Equals(requested_size)) {
+      gl_context_->MakeCurrent();
+      glWaitSyncPointCHROMIUM(texture_info.second);
+      return std::move(texture_info.first);
+    }
+  }
+
+  return std::unique_ptr<GLTexture>(new GLTexture(gl_context_, requested_size));
+}
+
+mojo::gfx::composition::ResourcePtr GLRenderer::BindTextureResource(
+    std::unique_ptr<GLTexture> texture) {
+  if (!gl_context_)
+    return nullptr;
+
+  // Produce the texture.
+  gl_context_->MakeCurrent();
+  glBindTexture(GL_TEXTURE_2D, texture->texture_id());
+  GLbyte mailbox[GL_MAILBOX_SIZE_CHROMIUM];
+  glGenMailboxCHROMIUM(mailbox);
+  glProduceTextureCHROMIUM(GL_TEXTURE_2D, mailbox);
+  glBindTexture(GL_TEXTURE_2D, 0);
+  GLuint sync_point = glInsertSyncPointCHROMIUM();
+
+  // Populate the resource description.
+  auto resource = mojo::gfx::composition::Resource::New();
+  resource->set_mailbox_texture(
+      mojo::gfx::composition::MailboxTextureResource::New());
+  resource->get_mailbox_texture()->mailbox_name.resize(sizeof(mailbox));
+  memcpy(resource->get_mailbox_texture()->mailbox_name.data(), mailbox,
+         sizeof(mailbox));
+  resource->get_mailbox_texture()->sync_point = sync_point;
+  resource->get_mailbox_texture()->size = texture->size().Clone();
+  resource->get_mailbox_texture()->callback =
+      (new GLTextureReleaser(
+           weak_factory_.GetWeakPtr(),
+           GLRecycledTextureInfo(std::move(texture), sync_point)))
+          ->StrongBind()
+          .Pass();
+
+  bound_textures_++;
+  DVLOG(2) << "bind: bound_textures=" << bound_textures_;
+  return resource;
+}
+
+mojo::gfx::composition::ResourcePtr GLRenderer::DrawGL(
+    const mojo::Size& size,
+    bool with_depth,
+    const DrawGLCallback& callback) {
+  std::unique_ptr<mojo::GLTexture> texture = GetTexture(size);
+  DCHECK(texture);
+
+  GLuint fbo = 0u;
+  glGenFramebuffers(1, &fbo);
+  glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+  glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+                         texture->texture_id(), 0);
+
+  GLuint depth_buffer = 0u;
+  if (with_depth) {
+    glGenRenderbuffers(1, &depth_buffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, depth_buffer);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size.width,
+                          size.height);
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
+                              GL_RENDERBUFFER, depth_buffer);
+  }
+
+  DCHECK_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
+            glCheckFramebufferStatus(GL_FRAMEBUFFER));
+
+  glViewport(0, 0, size.width, size.height);
+  callback.Run();
+
+  if (with_depth)
+    glDeleteRenderbuffers(1, &depth_buffer);
+  glDeleteFramebuffers(1, &fbo);
+
+  return BindTextureResource(std::move(texture));
+}
+
+void GLRenderer::ReleaseTexture(GLRecycledTextureInfo texture_info,
+                                bool recyclable) {
+  DCHECK(bound_textures_);
+  bound_textures_--;
+  if (recyclable && recycled_textures_.size() < max_recycled_textures_) {
+    recycled_textures_.emplace_back(std::move(texture_info));
+  }
+  DVLOG(2) << "release: bound_textures=" << bound_textures_
+           << ", recycled_textures=" << recycled_textures_.size();
+}
+
+GLRenderer::GLTextureReleaser::GLTextureReleaser(
+    const base::WeakPtr<GLRenderer>& provider,
+    GLRecycledTextureInfo info)
+    : provider_(provider), texture_info_(std::move(info)), binding_(this) {}
+
+GLRenderer::GLTextureReleaser::~GLTextureReleaser() {
+  // It's possible for the object to be destroyed due to a connection
+  // error on the callback pipe.  When this happens we don't want to
+  // recycle the texture since we have too little knowledge about its
+  // state to confirm that it will be safe to do so.
+  Release(false /*recyclable*/);
+}
+
+mojo::gfx::composition::MailboxTextureCallbackPtr
+GLRenderer::GLTextureReleaser::StrongBind() {
+  mojo::gfx::composition::MailboxTextureCallbackPtr callback;
+  binding_.Bind(mojo::GetProxy(&callback));
+  return callback;
+}
+
+void GLRenderer::GLTextureReleaser::OnMailboxTextureReleased() {
+  Release(true /*recyclable*/);
+}
+
+void GLRenderer::GLTextureReleaser::Release(bool recyclable) {
+  if (provider_) {
+    provider_->ReleaseTexture(std::move(texture_info_), recyclable);
+    provider_.reset();
+  }
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/gl_renderer.h b/mojo/ui/gl_renderer.h
new file mode 100644
index 0000000..a21c799
--- /dev/null
+++ b/mojo/ui/gl_renderer.h
@@ -0,0 +1,99 @@
+// 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 MOJO_UI_GL_RENDERER_H_
+#define MOJO_UI_GL_RENDERER_H_
+
+#include <deque>
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "mojo/services/gfx/composition/interfaces/resources.mojom.h"
+
+namespace mojo {
+
+class GLContext;
+class GLTexture;
+class Size;
+
+namespace ui {
+
+// Provides support for rendering GL commands into a pool of textures
+// and producing scene resources for them.
+class GLRenderer {
+ public:
+  // Called with an active GL context to draw into the framebuffer.
+  using DrawGLCallback = base::Closure;
+
+  GLRenderer(base::WeakPtr<mojo::GLContext> gl_context,
+             uint32_t max_recycled_textures = 3u);
+  ~GLRenderer();
+
+  // Gets the GL context, or null if none.
+  const base::WeakPtr<mojo::GLContext>& gl_context() const {
+    return gl_context_;
+  }
+
+  // Obtains a texture of the specified size.
+  // Returns a nullptr if the GLContext was destroyed.
+  std::unique_ptr<mojo::GLTexture> GetTexture(const mojo::Size& requested_size);
+
+  // Takes ownership of the specified texture, issues GL commands to
+  // produce a mailbox texture, and returns its resource pointer.
+  // The caller should add the resource to its scene.
+  // Returns a nullptr if the GLContext was destroyed.
+  mojo::gfx::composition::ResourcePtr BindTextureResource(
+      std::unique_ptr<GLTexture> texture);
+
+  // Allocates a GL texture, binds it to a framebuffer, invokes the
+  // provided function, then returns the resulting resource.
+  // If |with_depth| is true, provides a depth buffer attachment.
+  mojo::gfx::composition::ResourcePtr DrawGL(const mojo::Size& size,
+                                             bool with_depth,
+                                             const DrawGLCallback& callback);
+
+ private:
+  using GLRecycledTextureInfo =
+      std::pair<std::unique_ptr<mojo::GLTexture>, uint32_t>;
+
+  // TODO(jeffbrown): Avoid creating new callbacks each time, perhaps by
+  // migrating to image pipes.
+  class GLTextureReleaser : mojo::gfx::composition::MailboxTextureCallback {
+   public:
+    GLTextureReleaser(const base::WeakPtr<GLRenderer>& provider,
+                      GLRecycledTextureInfo info);
+    ~GLTextureReleaser() override;
+
+    mojo::gfx::composition::MailboxTextureCallbackPtr StrongBind();
+
+   private:
+    void OnMailboxTextureReleased() override;
+    void Release(bool recyclable);
+
+    base::WeakPtr<GLRenderer> provider_;
+    GLRecycledTextureInfo texture_info_;
+    mojo::StrongBinding<mojo::gfx::composition::MailboxTextureCallback>
+        binding_;
+  };
+
+  void ReleaseTexture(GLRecycledTextureInfo texture_info, bool recyclable);
+
+  const base::WeakPtr<mojo::GLContext> gl_context_;
+  const uint32_t max_recycled_textures_;
+
+  std::deque<GLRecycledTextureInfo> recycled_textures_;
+  uint32_t bound_textures_ = 0u;
+
+  base::WeakPtrFactory<mojo::ui::GLRenderer> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(GLRenderer);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_GL_RENDERER_H_
diff --git a/mojo/ui/gl_renderer_unittest.cc b/mojo/ui/gl_renderer_unittest.cc
new file mode 100644
index 0000000..c9ffabd
--- /dev/null
+++ b/mojo/ui/gl_renderer_unittest.cc
@@ -0,0 +1,209 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "mojo/gpu/gl_context.h"
+#include "mojo/gpu/gl_texture.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/services/geometry/interfaces/geometry.mojom.h"
+#include "mojo/services/gfx/composition/interfaces/resources.mojom.h"
+#include "mojo/ui/gl_renderer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+static const base::TimeDelta kDefaultMessageDelay =
+    base::TimeDelta::FromMilliseconds(20);
+
+class GLRendererTest : public mojo::test::ApplicationTestBase {
+ public:
+  GLRendererTest() : weak_factory_(this) {}
+  ~GLRendererTest() override {}
+
+  void SetUp() override {
+    mojo::test::ApplicationTestBase::SetUp();
+    gl_context_ = mojo::GLContext::CreateOffscreen(
+        mojo::MakeProxy(application_impl()->CreateApplicationConnector())
+            .get());
+    quit_message_loop_callback_ = base::Bind(
+        &GLRendererTest::QuitMessageLoopCallback, weak_factory_.GetWeakPtr());
+  }
+
+  void QuitMessageLoopCallback() { base::MessageLoop::current()->Quit(); }
+
+  void KickMessageLoop() {
+    base::MessageLoop::current()->PostDelayedTask(
+        FROM_HERE, quit_message_loop_callback_, kDefaultMessageDelay);
+    base::MessageLoop::current()->Run();
+  }
+
+ protected:
+  base::WeakPtr<mojo::GLContext> gl_context_;
+  base::Closure quit_message_loop_callback_;
+  base::WeakPtrFactory<GLRendererTest> weak_factory_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(GLRendererTest);
+};
+
+TEST_F(GLRendererTest, GetTextureOnce) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture = renderer.GetTexture(size);
+  EXPECT_NE(texture.get(), nullptr);
+
+  mojo::gfx::composition::ResourcePtr resource =
+      renderer.BindTextureResource(std::move(texture));
+  EXPECT_NE(resource.get(), nullptr);
+  EXPECT_NE(resource->get_mailbox_texture().get(), nullptr);
+  EXPECT_FALSE(resource->get_mailbox_texture()->mailbox_name.is_null());
+  EXPECT_TRUE(resource->get_mailbox_texture()->size->Equals(size));
+  EXPECT_NE(resource->get_mailbox_texture()->sync_point, 0u);
+  EXPECT_NE(resource->get_mailbox_texture()->callback.get(), nullptr);
+}
+
+TEST_F(GLRendererTest, GetTextureTwiceSameSize) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture1 = renderer.GetTexture(size);
+  EXPECT_NE(texture1.get(), nullptr);
+
+  std::unique_ptr<mojo::GLTexture> texture2 = renderer.GetTexture(size);
+  EXPECT_NE(texture2.get(), nullptr);
+
+  EXPECT_NE(texture2.get(), texture1.get());
+  EXPECT_NE(texture2->texture_id(), texture1->texture_id());
+
+  mojo::gfx::composition::ResourcePtr resource1 =
+      renderer.BindTextureResource(std::move(texture1));
+  EXPECT_NE(resource1.get(), nullptr);
+  EXPECT_NE(resource1->get_mailbox_texture().get(), nullptr);
+  EXPECT_FALSE(resource1->get_mailbox_texture()->mailbox_name.is_null());
+  EXPECT_TRUE(resource1->get_mailbox_texture()->size->Equals(size));
+  EXPECT_NE(resource1->get_mailbox_texture()->sync_point, 0u);
+  EXPECT_NE(resource1->get_mailbox_texture()->callback.get(), nullptr);
+
+  mojo::gfx::composition::ResourcePtr resource2 =
+      renderer.BindTextureResource(std::move(texture2));
+  EXPECT_NE(resource2.get(), nullptr);
+  EXPECT_NE(resource2->get_mailbox_texture().get(), nullptr);
+  EXPECT_FALSE(resource2->get_mailbox_texture()->mailbox_name.is_null());
+  EXPECT_TRUE(resource2->get_mailbox_texture()->size->Equals(size));
+  EXPECT_NE(resource2->get_mailbox_texture()->sync_point, 0u);
+  EXPECT_NE(resource2->get_mailbox_texture()->callback.get(), nullptr);
+
+  EXPECT_NE(resource2->get_mailbox_texture()->sync_point,
+            resource1->get_mailbox_texture()->sync_point);
+}
+
+TEST_F(GLRendererTest, GetTextureAfterRecycleSameSize) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture1 = renderer.GetTexture(size);
+  EXPECT_NE(texture1.get(), nullptr);
+  mojo::GLTexture* original_texture = texture1.get();
+
+  // Return the texture.
+  mojo::gfx::composition::ResourcePtr resource1 =
+      renderer.BindTextureResource(std::move(texture1));
+  EXPECT_NE(resource1.get(), nullptr);
+  resource1->get_mailbox_texture()->callback->OnMailboxTextureReleased();
+
+  KickMessageLoop();
+
+  // Get a texture of the same size, it should be the same one as before.
+  std::unique_ptr<mojo::GLTexture> texture2 = renderer.GetTexture(size);
+  EXPECT_EQ(texture2.get(), original_texture);
+}
+
+TEST_F(GLRendererTest, GetTextureAfterRecycleDifferentSize) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size1;
+  size1.width = 100;
+  size1.height = 100;
+  std::unique_ptr<mojo::GLTexture> texture1 = renderer.GetTexture(size1);
+  EXPECT_NE(texture1.get(), nullptr);
+  EXPECT_TRUE(texture1->size().Equals(size1));
+
+  // Return the texture.
+  mojo::gfx::composition::ResourcePtr resource1 =
+      renderer.BindTextureResource(std::move(texture1));
+  EXPECT_NE(resource1.get(), nullptr);
+  resource1->get_mailbox_texture()->callback->OnMailboxTextureReleased();
+
+  KickMessageLoop();
+
+  // Get a texture of the a different size, it should be a new one
+  // with the new size.
+  mojo::Size size2;
+  size2.width = size1.width - 1;
+  size2.height = size1.height - 1;
+  std::unique_ptr<mojo::GLTexture> texture2 = renderer.GetTexture(size2);
+  EXPECT_NE(texture2.get(), nullptr);
+  EXPECT_TRUE(texture2->size().Equals(size2));
+}
+
+TEST_F(GLRendererTest, GetTextureReleasedGlContext) {
+  gl_context_->Destroy();
+
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture = renderer.GetTexture(size);
+  EXPECT_EQ(texture.get(), nullptr);
+}
+
+TEST_F(GLRendererTest, BindTextureResourceReleasedGlContext) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture = renderer.GetTexture(size);
+  EXPECT_NE(texture.get(), nullptr);
+
+  gl_context_->Destroy();
+
+  mojo::gfx::composition::ResourcePtr resource =
+      renderer.BindTextureResource(std::move(texture));
+  EXPECT_EQ(resource.get(), nullptr);
+}
+
+TEST_F(GLRendererTest, RecycledAfterReleasedGlContext) {
+  mojo::ui::GLRenderer renderer(gl_context_);
+  mojo::Size size;
+  size.width = 100;
+  size.height = 100;
+
+  std::unique_ptr<mojo::GLTexture> texture1 = renderer.GetTexture(size);
+  EXPECT_NE(texture1.get(), nullptr);
+
+  mojo::gfx::composition::ResourcePtr resource1 =
+      renderer.BindTextureResource(std::move(texture1));
+  EXPECT_NE(resource1.get(), nullptr);
+
+  gl_context_->Destroy();
+  resource1->get_mailbox_texture()->callback->OnMailboxTextureReleased();
+
+  KickMessageLoop();
+
+  // Get a texture of the same size, should be null due to the released context.
+  std::unique_ptr<mojo::GLTexture> texture2 = renderer.GetTexture(size);
+  EXPECT_EQ(texture2.get(), nullptr);
+}
+
+}  // namespace
diff --git a/mojo/ui/gl_view.cc b/mojo/ui/gl_view.cc
new file mode 100644
index 0000000..3e7fea3
--- /dev/null
+++ b/mojo/ui/gl_view.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 "mojo/ui/gl_view.h"
+
+#include "base/logging.h"
+
+namespace mojo {
+namespace ui {
+
+GLView::GLView(
+    mojo::ApplicationImpl* app_impl,
+    const std::string& label,
+    const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback)
+    : BaseView(app_impl, label, create_view_callback),
+      gl_context_owner_(mojo::MakeProxy(app_impl->CreateApplicationConnector())
+                            .get()),
+      gl_renderer_(gl_context_owner_.context()) {}
+
+GLView::~GLView() {}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/gl_view.h b/mojo/ui/gl_view.h
new file mode 100644
index 0000000..1f9796c
--- /dev/null
+++ b/mojo/ui/gl_view.h
@@ -0,0 +1,46 @@
+// 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 MOJO_UI_GL_VIEW_H_
+#define MOJO_UI_GL_VIEW_H_
+
+#include "mojo/gpu/gl_context.h"
+#include "mojo/gpu/gl_context_owner.h"
+#include "mojo/ui/base_view.h"
+#include "mojo/ui/gl_renderer.h"
+
+namespace mojo {
+namespace ui {
+
+// Abstract base implementation of the View interface for simple applications
+// which use GL for rendering.  Subclasses must handle layout and provide
+// content for the scene.
+class GLView : public BaseView {
+ public:
+  GLView(
+      mojo::ApplicationImpl* app_impl,
+      const std::string& label,
+      const mojo::ui::ViewProvider::CreateViewCallback& create_view_callback);
+
+  ~GLView() override;
+
+  // Gets the GL context, or null if none.
+  const base::WeakPtr<mojo::GLContext>& gl_context() const {
+    return gl_context_owner_.context();
+  }
+
+  // Gets the GL renderer.
+  mojo::ui::GLRenderer* gl_renderer() { return &gl_renderer_; }
+
+ private:
+  mojo::GLContextOwner gl_context_owner_;
+  mojo::ui::GLRenderer gl_renderer_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(GLView);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_GL_VIEW_H_
diff --git a/mojo/ui/input_handler.cc b/mojo/ui/input_handler.cc
new file mode 100644
index 0000000..7eb06f8
--- /dev/null
+++ b/mojo/ui/input_handler.cc
@@ -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.
+
+#include "mojo/ui/input_handler.h"
+
+#include "base/logging.h"
+#include "mojo/public/cpp/application/connect.h"
+
+namespace mojo {
+namespace ui {
+
+InputHandler::InputHandler(mojo::ServiceProvider* service_provider,
+                           mojo::ui::InputListener* listener)
+    : listener_binding_(listener) {
+  DCHECK(service_provider);
+  DCHECK(listener);
+
+  mojo::ConnectToService(service_provider, &connection_);
+
+  mojo::ui::InputListenerPtr listener_ptr;
+  listener_binding_.Bind(mojo::GetProxy(&listener_ptr));
+  connection_->SetListener(listener_ptr.Pass());
+}
+
+InputHandler::~InputHandler() {}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/input_handler.h b/mojo/ui/input_handler.h
new file mode 100644
index 0000000..5073584
--- /dev/null
+++ b/mojo/ui/input_handler.h
@@ -0,0 +1,59 @@
+// 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 MOJO_UI_INPUT_HANDLER_H_
+#define MOJO_UI_INPUT_HANDLER_H_
+
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/services/ui/input/interfaces/input_connection.mojom.h"
+
+namespace mojo {
+namespace ui {
+
+// Holds an |InputConnection| and sets its |InputListener|.
+//
+// This class is intended to be included as a member of a View that wants to
+// receive input using the following pattern.
+//
+// class MyView : public mojo::ui::BaseView, public mojo::ui::InputListener {
+//  public:
+//   MyView(mojo::ApplicationImpl* app_impl,
+//          const mojo::ui::ViewProvider::CreateViewCallback&
+//              create_view_callback)
+//          : BaseView(app_impl, "MyView", create_view_callback),
+//            input_handler_(view_service_provider(), this) {}
+//   ~MyView() override {}
+//
+//  private:
+//   // |InputListener|:
+//   void OnEvent(mojo::EventPtr event,
+//                const OnEventCallback& callback) override;
+//
+//   mojo::ui::InputHandler input_handler_;
+//
+//   MOJO_DISALLOW_COPY_AND_ASSIGN(MyView);
+// };
+class InputHandler {
+ public:
+  // Creates an input connection associated with the specified view host.
+  InputHandler(mojo::ServiceProvider* service_provider,
+               mojo::ui::InputListener* listener);
+  ~InputHandler();
+
+  // Gets the input connection.
+  mojo::ui::InputConnection* connection() { return connection_.get(); }
+
+ private:
+  mojo::Binding<mojo::ui::InputListener> listener_binding_;
+  mojo::ui::InputConnectionPtr connection_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(InputHandler);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_INPUT_HANDLER_H_
diff --git a/mojo/ui/view_provider_app.cc b/mojo/ui/view_provider_app.cc
new file mode 100644
index 0000000..40d5a5b
--- /dev/null
+++ b/mojo/ui/view_provider_app.cc
@@ -0,0 +1,79 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/ui/view_provider_app.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+
+namespace mojo {
+namespace ui {
+
+class ViewProviderApp::DelegatingViewProvider : public mojo::ui::ViewProvider {
+ public:
+  DelegatingViewProvider(ViewProviderApp* app,
+                         const std::string& view_provider_url)
+      : app_(app), view_provider_url_(view_provider_url) {}
+
+  ~DelegatingViewProvider() override {}
+
+ private:
+  // |ViewProvider|:
+  void CreateView(
+      mojo::InterfaceRequest<mojo::ServiceProvider> services,
+      mojo::ServiceProviderPtr exposed_services,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback) override {
+    app_->CreateView(this, view_provider_url_, services.Pass(),
+                     exposed_services.Pass(), callback);
+  }
+
+  ViewProviderApp* app_;
+  std::string view_provider_url_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(DelegatingViewProvider);
+};
+
+ViewProviderApp::ViewProviderApp() {}
+
+ViewProviderApp::~ViewProviderApp() {}
+
+void ViewProviderApp::Initialize(mojo::ApplicationImpl* app_impl) {
+  app_impl_ = app_impl;
+
+  auto command_line = base::CommandLine::ForCurrentProcess();
+  command_line->InitFromArgv(app_impl_->args());
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG;
+  logging::InitLogging(settings);
+}
+
+bool ViewProviderApp::ConfigureIncomingConnection(
+    mojo::ApplicationConnection* connection) {
+  connection->AddService<mojo::ui::ViewProvider>(this);
+  return true;
+}
+
+void ViewProviderApp::Create(
+    mojo::ApplicationConnection* connection,
+    mojo::InterfaceRequest<mojo::ui::ViewProvider> request) {
+  bindings_.AddBinding(
+      new DelegatingViewProvider(this, connection->GetConnectionURL()),
+      request.Pass());
+}
+
+void ViewProviderApp::CreateView(
+    DelegatingViewProvider* provider,
+    const std::string& view_provider_url,
+    mojo::InterfaceRequest<mojo::ServiceProvider> services,
+    mojo::ServiceProviderPtr exposed_services,
+    const mojo::ui::ViewProvider::CreateViewCallback& callback) {
+  if (!CreateView(view_provider_url, services.Pass(), exposed_services.Pass(),
+                  callback)) {
+    bindings_.RemoveBindings(provider);
+    delete provider;
+  }
+}
+
+}  // namespace ui
+}  // namespace mojo
diff --git a/mojo/ui/view_provider_app.h b/mojo/ui/view_provider_app.h
new file mode 100644
index 0000000..07668c6
--- /dev/null
+++ b/mojo/ui/view_provider_app.h
@@ -0,0 +1,74 @@
+// 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 MOJO_UI_VIEW_PROVIDER_APP_H_
+#define MOJO_UI_VIEW_PROVIDER_APP_H_
+
+#include <string>
+
+#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"
+
+namespace mojo {
+namespace ui {
+
+// Abstract implementation of a simple application that offers a ViewProvider.
+// Subclasses must provide a function to create the necessary Views.
+//
+// It is not necessary to use this class to implement all ViewProviders.
+// This class is merely intended to make the simple apps easier to write.
+class ViewProviderApp : public mojo::ApplicationDelegate,
+                        public mojo::InterfaceFactory<mojo::ui::ViewProvider> {
+ public:
+  ViewProviderApp();
+  ~ViewProviderApp() override;
+
+  mojo::ApplicationImpl* app_impl() { return app_impl_; }
+
+  // |ApplicationDelegate|:
+  void Initialize(mojo::ApplicationImpl* app) override;
+  bool ConfigureIncomingConnection(
+      mojo::ApplicationConnection* connection) override;
+
+  // Called by the ViewProvider to create a view.
+  // This method may be called multiple times in the case where the
+  // view provider is asked to create multiple view instances.
+  //
+  // The |view_provider_url| is the connection URL of the view provider request.
+  //
+  // Returns true if successful, false if the view could not be created.
+  virtual bool CreateView(
+      const std::string& view_provider_url,
+      mojo::InterfaceRequest<mojo::ServiceProvider> services,
+      mojo::ServiceProviderPtr exposed_services,
+      const mojo::ui::ViewProvider::CreateViewCallback& callback) = 0;
+
+ private:
+  class DelegatingViewProvider;
+
+  // |InterfaceFactory<mojo::ui::ViewProvider>|:
+  void Create(mojo::ApplicationConnection* connection,
+              mojo::InterfaceRequest<mojo::ui::ViewProvider> request) override;
+
+  void CreateView(DelegatingViewProvider* provider,
+                  const std::string& view_provider_url,
+                  mojo::InterfaceRequest<mojo::ServiceProvider> services,
+                  mojo::ServiceProviderPtr exposed_services,
+                  const mojo::ui::ViewProvider::CreateViewCallback& callback);
+
+  mojo::ApplicationImpl* app_impl_ = nullptr;
+  mojo::StrongBindingSet<mojo::ui::ViewProvider> bindings_;
+
+  MOJO_DISALLOW_COPY_AND_ASSIGN(ViewProviderApp);
+};
+
+}  // namespace ui
+}  // namespace mojo
+
+#endif  // MOJO_UI_VIEW_PROVIDER_APP_H_