Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/ui/ozone/platform/README b/ui/ozone/platform/README
new file mode 100644
index 0000000..cc91d3b
--- /dev/null
+++ b/ui/ozone/platform/README
@@ -0,0 +1,2 @@
+This directory contains implementations of platforms. Each platform implements
+the interfaces from ui/ozone/public for the rest of chromium.
diff --git a/ui/ozone/platform/caca/BUILD.gn b/ui/ozone/platform/caca/BUILD.gn
new file mode 100644
index 0000000..085925d
--- /dev/null
+++ b/ui/ozone/platform/caca/BUILD.gn
@@ -0,0 +1,34 @@
+# 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("//build/config/linux/pkg_config.gni")
+
+source_set("caca") {
+  sources = [
+    "caca_event_source.cc",
+    "caca_event_source.h",
+    "caca_window.cc",
+    "caca_window.h",
+    "caca_window_manager.cc",
+    "caca_window_manager.h",
+    "ozone_platform_caca.cc",
+    "ozone_platform_caca.h",
+    "scoped_caca_types.cc",
+    "scoped_caca_types.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/gfx/geometry",
+  ]
+
+  configs += [
+    ":libcaca",
+  ]
+}
+
+pkg_config("libcaca") {
+  packages = [ "caca" ]
+}
diff --git a/ui/ozone/platform/caca/OWNERS b/ui/ozone/platform/caca/OWNERS
new file mode 100644
index 0000000..0d77c0e
--- /dev/null
+++ b/ui/ozone/platform/caca/OWNERS
@@ -0,0 +1 @@
+dnicoara@chromium.org
diff --git a/ui/ozone/platform/caca/caca.gypi b/ui/ozone/platform/caca/caca.gypi
new file mode 100644
index 0000000..34a5e69
--- /dev/null
+++ b/ui/ozone/platform/caca/caca.gypi
@@ -0,0 +1,47 @@
+# 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.
+
+{
+  'variables': {
+    'internal_ozone_platform_deps': [
+      'ozone_platform_caca',
+    ],
+    'internal_ozone_platforms': [
+      'caca'
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ozone_platform_caca',
+      'type': 'static_library',
+      'defines': [
+        'OZONE_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../skia/skia.gyp:skia',
+        '../events/events.gyp:events',
+        '../gfx/gfx.gyp:gfx',
+        '../gfx/gfx.gyp:gfx_geometry',
+      ],
+      'link_settings': {
+        'libraries': [
+          '-lcaca',
+        ],
+      },
+      'sources': [
+        'caca_event_source.cc',
+        'caca_event_source.h',
+        'caca_window.cc',
+        'caca_window.h',
+        'caca_window_manager.cc',
+        'caca_window_manager.h',
+        'ozone_platform_caca.cc',
+        'ozone_platform_caca.h',
+        'scoped_caca_types.cc',
+        'scoped_caca_types.h',
+      ],
+    },
+  ],
+}
diff --git a/ui/ozone/platform/caca/caca_event_source.cc b/ui/ozone/platform/caca/caca_event_source.cc
new file mode 100644
index 0000000..a8da8b4
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_event_source.cc
@@ -0,0 +1,229 @@
+// 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 "ui/ozone/platform/caca/caca_event_source.h"
+
+#include <caca.h>
+
+#include "base/bind.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/events/event.h"
+#include "ui/events/keycodes/keyboard_codes.h"
+#include "ui/ozone/platform/caca/caca_window.h"
+
+namespace ui {
+
+namespace {
+
+ui::KeyboardCode GetKeyboardCode(const caca_event_t& event) {
+  // List of special mappings the Caca provides.
+  static const ui::KeyboardCode kCacaKeyMap[] = {
+    ui::VKEY_UNKNOWN,
+    ui::VKEY_A,
+    ui::VKEY_B,
+    ui::VKEY_C,
+    ui::VKEY_D,
+    ui::VKEY_E,
+    ui::VKEY_F,
+    ui::VKEY_G,
+    ui::VKEY_BACK,
+    ui::VKEY_TAB,
+    ui::VKEY_J,
+    ui::VKEY_K,
+    ui::VKEY_L,
+    ui::VKEY_RETURN,
+    ui::VKEY_N,
+    ui::VKEY_O,
+    ui::VKEY_P,
+    ui::VKEY_Q,
+    ui::VKEY_R,
+    ui::VKEY_PAUSE,
+    ui::VKEY_T,
+    ui::VKEY_U,
+    ui::VKEY_V,
+    ui::VKEY_W,
+    ui::VKEY_X,
+    ui::VKEY_Y,
+    ui::VKEY_Z,
+    ui::VKEY_ESCAPE,
+    ui::VKEY_DELETE,
+    ui::VKEY_UP,
+    ui::VKEY_DOWN,
+    ui::VKEY_LEFT,
+    ui::VKEY_RIGHT,
+    ui::VKEY_INSERT,
+    ui::VKEY_HOME,
+    ui::VKEY_END,
+    ui::VKEY_PRIOR,
+    ui::VKEY_NEXT,
+    ui::VKEY_F1,
+    ui::VKEY_F2,
+    ui::VKEY_F3,
+    ui::VKEY_F4,
+    ui::VKEY_F5,
+    ui::VKEY_F6,
+    ui::VKEY_F7,
+    ui::VKEY_F8,
+    ui::VKEY_F9,
+    ui::VKEY_F10,
+    ui::VKEY_F11,
+    ui::VKEY_F12,
+  };
+
+  int key_code = caca_get_event_key_ch(&event);
+  if (key_code >= 'a' && key_code <= 'z')
+    return static_cast<ui::KeyboardCode>(key_code - ('a' - 'A'));
+  if (key_code >= '0' && key_code <= 'Z')
+    return static_cast<ui::KeyboardCode>(key_code);
+  if (static_cast<unsigned int>(key_code) < arraysize(kCacaKeyMap))
+    return kCacaKeyMap[key_code];
+
+  return ui::VKEY_UNKNOWN;
+}
+
+int ModifierFromKey(const caca_event_t& event) {
+  int key_code = caca_get_event_key_ch(&event);
+  if (key_code >= 'A' && key_code <= 'Z')
+    return ui::EF_SHIFT_DOWN;
+  if ((key_code >= CACA_KEY_CTRL_A && key_code <= CACA_KEY_CTRL_G) ||
+      (key_code >= CACA_KEY_CTRL_J && key_code <= CACA_KEY_CTRL_L) ||
+      (key_code >= CACA_KEY_CTRL_N && key_code <= CACA_KEY_CTRL_R) ||
+      (key_code >= CACA_KEY_CTRL_T && key_code <= CACA_KEY_CTRL_Z))
+    return ui::EF_CONTROL_DOWN;
+
+  return ui::EF_NONE;
+}
+
+int ModifierFromButton(const caca_event_t& event) {
+  switch (caca_get_event_mouse_button(&event)) {
+    case 1:
+      return ui::EF_LEFT_MOUSE_BUTTON;
+    case 2:
+      return ui::EF_RIGHT_MOUSE_BUTTON;
+    case 3:
+      return ui::EF_MIDDLE_MOUSE_BUTTON;
+  }
+  return 0;
+}
+
+// Translate coordinates to bitmap coordinates.
+gfx::PointF TranslateLocation(const gfx::PointF& location, CacaWindow* window) {
+  gfx::Size physical_size = window->physical_size();
+  gfx::Size bitmap_size = window->bitmap_size();
+  return gfx::PointF(
+      location.x() * bitmap_size.width() / physical_size.width(),
+      location.y() * bitmap_size.height() / physical_size.height());
+}
+
+ui::EventType GetEventTypeFromNative(const caca_event_t& event) {
+  switch (caca_get_event_type(&event)) {
+    case CACA_EVENT_KEY_PRESS:
+      return ui::ET_KEY_PRESSED;
+    case CACA_EVENT_KEY_RELEASE:
+      return ui::ET_KEY_RELEASED;
+    case CACA_EVENT_MOUSE_PRESS:
+      return ui::ET_MOUSE_PRESSED;
+    case CACA_EVENT_MOUSE_RELEASE:
+      return ui::ET_MOUSE_RELEASED;
+    case CACA_EVENT_MOUSE_MOTION:
+      return ui::ET_MOUSE_MOVED;
+    default:
+      return ui::ET_UNKNOWN;
+  }
+}
+
+}  // namespace
+
+CacaEventSource::CacaEventSource() : modifier_flags_(0) {
+}
+
+CacaEventSource::~CacaEventSource() {
+}
+
+void CacaEventSource::TryProcessingEvent(CacaWindow* window) {
+  if (!window->display())
+    return;
+
+  caca_event_t event;
+  int event_mask = CACA_EVENT_KEY_PRESS | CACA_EVENT_KEY_RELEASE |
+                   CACA_EVENT_MOUSE_PRESS | CACA_EVENT_MOUSE_RELEASE |
+                   CACA_EVENT_MOUSE_MOTION | CACA_EVENT_RESIZE |
+                   CACA_EVENT_QUIT;
+
+  if (!caca_get_event(window->display(), event_mask, &event, 0))
+    return;
+
+  switch (caca_get_event_type(&event)) {
+    case CACA_EVENT_KEY_PRESS:
+    case CACA_EVENT_KEY_RELEASE:
+    case CACA_EVENT_MOUSE_PRESS:
+    case CACA_EVENT_MOUSE_RELEASE:
+    case CACA_EVENT_MOUSE_MOTION:
+      OnInputEvent(&event, window);
+      break;
+    case CACA_EVENT_RESIZE:
+      window->OnCacaResize();
+      break;
+    case CACA_EVENT_QUIT:
+      window->OnCacaQuit();
+      break;
+    default:
+      NOTIMPLEMENTED();
+  }
+}
+
+void CacaEventSource::OnInputEvent(caca_event_t* event, CacaWindow* window) {
+  ui::EventType type = GetEventTypeFromNative(*event);
+  bool pressed = type == ui::ET_KEY_PRESSED || type == ui::ET_MOUSE_PRESSED;
+
+  switch (type) {
+    case ui::ET_KEY_PRESSED:
+    case ui::ET_KEY_RELEASED: {
+      if (pressed)
+        modifier_flags_ |= ModifierFromKey(*event);
+      else
+        modifier_flags_ &= ~ModifierFromKey(*event);
+
+      ui::KeyEvent key_event(
+          type, GetKeyboardCode(*event), modifier_flags_);
+      window->OnCacaEvent(&key_event);
+      break;
+    }
+    case ui::ET_MOUSE_MOVED:
+      last_cursor_location_.SetPoint(caca_get_event_mouse_x(event),
+                                     caca_get_event_mouse_y(event));
+      // Update cursor location.
+      caca_gotoxy(caca_get_canvas(window->display()),
+                  last_cursor_location_.x(),
+                  last_cursor_location_.y());
+
+    // fallthrough
+    case ui::ET_MOUSE_PRESSED:
+    case ui::ET_MOUSE_RELEASED: {
+      int flags = 0;
+      int changed_flags = 0;
+      if (type != ui::ET_MOUSE_MOVED) {
+        if (pressed) {
+          changed_flags = ModifierFromButton(*event);
+          modifier_flags_ |= changed_flags;
+        } else {
+          modifier_flags_ &= ~changed_flags;
+        }
+        // On release the button pressed is removed from |modifier_flags_|,
+        // but sending the event needs it set.
+        flags = modifier_flags_ | changed_flags;
+      }
+      gfx::PointF location = TranslateLocation(last_cursor_location_, window);
+      ui::MouseEvent mouse_event(
+          type, location, location, flags, changed_flags);
+      window->OnCacaEvent(&mouse_event);
+      break;
+    }
+    default:
+      NOTIMPLEMENTED();
+      break;
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/caca/caca_event_source.h b/ui/ozone/platform/caca/caca_event_source.h
new file mode 100644
index 0000000..daa7fc9
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_event_source.h
@@ -0,0 +1,44 @@
+// 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 UI_OZONE_PLATFORM_CACA_CACA_EVENT_SOURCE_H_
+#define UI_OZONE_PLATFORM_CACA_CACA_EVENT_SOURCE_H_
+
+#include <caca.h>
+
+#include "base/memory/ref_counted.h"
+#include "base/time/time.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/events/platform/scoped_event_dispatcher.h"
+#include "ui/gfx/geometry/point_f.h"
+
+namespace ui {
+
+class CacaWindow;
+
+class CacaEventSource : public PlatformEventSource {
+ public:
+  CacaEventSource();
+  virtual ~CacaEventSource();
+
+  // Poll for an event on a particular window. Input events will be
+  // dispatched on the given dispatcher.
+  void TryProcessingEvent(CacaWindow* window);
+
+  // Process an input event on a particular window.
+  void OnInputEvent(caca_event_t* event, CacaWindow* window);
+
+ private:
+  // Keep track of last cursor position to dispatch correct mouse push/release
+  // events.
+  gfx::PointF last_cursor_location_;
+
+  int modifier_flags_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacaEventSource);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CACA_CACA_EVENT_SOURCE_H_
diff --git a/ui/ozone/platform/caca/caca_window.cc b/ui/ozone/platform/caca/caca_window.cc
new file mode 100644
index 0000000..ff2f571
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_window.cc
@@ -0,0 +1,146 @@
+// 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 "ui/ozone/platform/caca/caca_window.h"
+
+#include "base/bind.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "ui/events/ozone/events_ozone.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/platform/caca/caca_event_source.h"
+#include "ui/ozone/platform/caca/caca_window_manager.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+namespace {
+
+// Size of initial cnavas (in characters).
+const int kDefaultCanvasWidth = 160;
+const int kDefaultCanvasHeight = 48;
+
+}  // namespace
+
+// TODO(dnicoara) As an optimization, |bitmap_size_| should be scaled based on
+// |physical_size_| and font size.
+CacaWindow::CacaWindow(PlatformWindowDelegate* delegate,
+                       CacaWindowManager* manager,
+                       CacaEventSource* event_source,
+                       const gfx::Rect& bounds)
+    : delegate_(delegate),
+      manager_(manager),
+      event_source_(event_source),
+      weak_ptr_factory_(this) {
+  widget_ = manager_->AddWindow(this);
+  ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+  delegate_->OnAcceleratedWidgetAvailable(widget_);
+}
+
+CacaWindow::~CacaWindow() {
+  ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+  manager_->RemoveWindow(widget_, this);
+}
+
+bool CacaWindow::Initialize() {
+  if (display_)
+    return true;
+
+  canvas_.reset(caca_create_canvas(kDefaultCanvasWidth, kDefaultCanvasHeight));
+  if (!canvas_) {
+    PLOG(ERROR) << "failed to create libcaca canvas";
+    return false;
+  }
+
+  display_.reset(caca_create_display(canvas_.get()));
+  if (!display_) {
+    PLOG(ERROR) << "failed to initialize libcaca display";
+    return false;
+  }
+
+  caca_set_cursor(display_.get(), 1);
+  caca_set_display_title(display_.get(), "Ozone Content Shell");
+
+  UpdateDisplaySize();
+
+  TryProcessingEvent();
+
+  return true;
+}
+
+void CacaWindow::TryProcessingEvent() {
+  event_source_->TryProcessingEvent(this);
+
+  // Caca uses a poll based event retrieval. Since we don't want to block we'd
+  // either need to spin up a new thread or just poll. For simplicity just poll
+  // for messages.
+  base::MessageLoop::current()->PostDelayedTask(
+      FROM_HERE,
+      base::Bind(&CacaWindow::TryProcessingEvent,
+                 weak_ptr_factory_.GetWeakPtr()),
+      base::TimeDelta::FromMilliseconds(10));
+}
+
+void CacaWindow::UpdateDisplaySize() {
+  physical_size_.SetSize(caca_get_canvas_width(canvas_.get()),
+                         caca_get_canvas_height(canvas_.get()));
+
+  bitmap_size_.SetSize(caca_get_display_width(display_.get()),
+                       caca_get_display_height(display_.get()));
+}
+
+void CacaWindow::OnCacaResize() {
+  UpdateDisplaySize();
+
+  delegate_->OnBoundsChanged(gfx::Rect(bitmap_size_));
+}
+
+void CacaWindow::OnCacaQuit() {
+  delegate_->OnCloseRequest();
+}
+
+
+void CacaWindow::OnCacaEvent(ui::Event* event) {
+  DispatchEventFromNativeUiEvent(
+      event,
+      base::Bind(&PlatformWindowDelegate::DispatchEvent,
+                 base::Unretained(delegate_)));
+}
+
+gfx::Rect CacaWindow::GetBounds() { return gfx::Rect(bitmap_size_); }
+
+void CacaWindow::SetBounds(const gfx::Rect& bounds) {}
+
+void CacaWindow::Show() {}
+
+void CacaWindow::Hide() {}
+
+void CacaWindow::Close() {}
+
+void CacaWindow::SetCapture() {}
+
+void CacaWindow::ReleaseCapture() {}
+
+void CacaWindow::ToggleFullscreen() {}
+
+void CacaWindow::Maximize() {}
+
+void CacaWindow::Minimize() {}
+
+void CacaWindow::Restore() {}
+
+void CacaWindow::SetCursor(PlatformCursor cursor) {}
+
+void CacaWindow::MoveCursorTo(const gfx::Point& location) {}
+
+bool CacaWindow::CanDispatchEvent(const PlatformEvent& event) { return true; }
+
+uint32_t CacaWindow::DispatchEvent(const PlatformEvent& ne) {
+  // We don't dispatch events via PlatformEventSource.
+  NOTREACHED();
+  return ui::POST_DISPATCH_STOP_PROPAGATION;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/caca/caca_window.h b/ui/ozone/platform/caca/caca_window.h
new file mode 100644
index 0000000..444f3d5
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_window.h
@@ -0,0 +1,97 @@
+// 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 UI_OZONE_PLATFORM_CACA_CACA_WINDOW_H_
+#define UI_OZONE_PLATFORM_CACA_CACA_WINDOW_H_
+
+#include <caca.h>
+
+#include "base/debug/stack_trace.h"
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/caca/scoped_caca_types.h"
+#include "ui/platform_window/platform_window.h"
+
+namespace ui {
+
+class CacaEventSource;
+class CacaWindowManager;
+class PlatformWindowDelegate;
+
+// Basic initialization of Libcaca. This needs to be shared between SFO and EFO
+// since both need the |display_| to draw and process events respectively.
+// Note, |canvas_| needs to be here since it is needed for creating a
+// |display_|.
+class CacaWindow : public PlatformWindow, public PlatformEventDispatcher {
+ public:
+  CacaWindow(PlatformWindowDelegate* delegate,
+             CacaWindowManager* manager,
+             CacaEventSource* event_source,
+             const gfx::Rect& bounds);
+  virtual ~CacaWindow();
+
+  bool Initialize();
+
+  // Handlers for events received from libcaca.
+  void OnCacaQuit();
+  void OnCacaResize();
+  void OnCacaEvent(ui::Event* event);
+
+  // This is the Caca canvas size.
+  gfx::Size physical_size() const { return physical_size_; }
+  gfx::Size bitmap_size() const { return bitmap_size_; }
+  caca_display_t* display() const { return display_.get(); }
+
+  // PlatformWindow:
+  virtual gfx::Rect GetBounds() OVERRIDE;
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+  virtual void Show() OVERRIDE;
+  virtual void Hide() OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual void SetCapture() OVERRIDE;
+  virtual void ReleaseCapture() OVERRIDE;
+  virtual void ToggleFullscreen() OVERRIDE;
+  virtual void Maximize() OVERRIDE;
+  virtual void Minimize() OVERRIDE;
+  virtual void Restore() OVERRIDE;
+  virtual void SetCursor(PlatformCursor cursor) OVERRIDE;
+  virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+
+  // PlatformEventDispatcher:
+  virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE;
+  virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE;
+
+ private:
+  // Event polling.
+  void TryProcessingEvent();
+
+  // Sync sizes with libcaca.
+  void UpdateDisplaySize();
+
+  PlatformWindowDelegate* delegate_;
+  CacaWindowManager* manager_;
+  CacaEventSource* event_source_;
+  gfx::AcceleratedWidget widget_;
+
+  ScopedCacaCanvas canvas_;
+  ScopedCacaDisplay display_;
+
+  // Size of the console in characters. This can be changed by setting
+  // CACA_GEOMETRY environment variable.
+  gfx::Size physical_size_;
+
+  // Size of the backing buffer we draw into. Size in pixels.
+  gfx::Size bitmap_size_;
+
+  base::WeakPtrFactory<CacaWindow> weak_ptr_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacaWindow);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CACA_CACA_WINDOW_H_
diff --git a/ui/ozone/platform/caca/caca_window_manager.cc b/ui/ozone/platform/caca/caca_window_manager.cc
new file mode 100644
index 0000000..9dbbc6b
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_window_manager.cc
@@ -0,0 +1,141 @@
+// 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 "ui/ozone/platform/caca/caca_window_manager.h"
+
+#include "base/debug/trace_event.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/platform/caca/caca_window.h"
+#include "ui/ozone/platform/caca/scoped_caca_types.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+
+namespace {
+
+class CacaSurface : public ui::SurfaceOzoneCanvas {
+ public:
+  CacaSurface(CacaWindow* window);
+  virtual ~CacaSurface();
+
+  bool Initialize();
+
+  // ui::SurfaceOzoneCanvas overrides:
+  virtual skia::RefPtr<SkCanvas> GetCanvas() OVERRIDE;
+  virtual void ResizeCanvas(const gfx::Size& viewport_size) OVERRIDE;
+  virtual void PresentCanvas(const gfx::Rect& damage) OVERRIDE;
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE;
+
+ private:
+  CacaWindow* window_;  // Not owned.
+
+  ScopedCacaDither dither_;
+
+  skia::RefPtr<SkSurface> surface_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacaSurface);
+};
+
+CacaSurface::CacaSurface(CacaWindow* window) : window_(window) {
+}
+
+CacaSurface::~CacaSurface() {
+}
+
+bool CacaSurface::Initialize() {
+  ResizeCanvas(window_->bitmap_size());
+  return true;
+}
+
+skia::RefPtr<SkCanvas> CacaSurface::GetCanvas() {
+  return skia::SharePtr<SkCanvas>(surface_->getCanvas());
+}
+
+void CacaSurface::ResizeCanvas(const gfx::Size& viewport_size) {
+  TRACE_EVENT0("ozone", "CacaSurface::ResizeCanvas");
+
+  VLOG(2) << "creating libcaca surface with from window " << (void*)window_;
+
+  SkImageInfo info = SkImageInfo::Make(window_->bitmap_size().width(),
+                                       window_->bitmap_size().height(),
+                                       kN32_SkColorType,
+                                       kPremul_SkAlphaType);
+
+  surface_ = skia::AdoptRef(SkSurface::NewRaster(info));
+  if (!surface_)
+    LOG(ERROR) << "Failed to create SkSurface";
+
+  dither_.reset(caca_create_dither(info.bytesPerPixel() * 8,
+                                   info.width(),
+                                   info.height(),
+                                   info.minRowBytes(),
+                                   0x00ff0000,
+                                   0x0000ff00,
+                                   0x000000ff,
+                                   0xff000000));
+  if (!dither_)
+    LOG(ERROR) << "failed to create dither";
+}
+
+void CacaSurface::PresentCanvas(const gfx::Rect& damage) {
+  TRACE_EVENT0("ozone", "CacaSurface::PresentCanvas");
+
+  SkImageInfo info;
+  size_t row_bytes;
+  const void* pixels = surface_->peekPixels(&info, &row_bytes);
+
+  caca_canvas_t* canvas = caca_get_canvas(window_->display());
+  caca_dither_bitmap(canvas,
+                     0,
+                     0,
+                     caca_get_canvas_width(canvas),
+                     caca_get_canvas_height(canvas),
+                     dither_.get(),
+                     static_cast<const uint8_t*>(pixels));
+  caca_refresh_display(window_->display());
+}
+
+scoped_ptr<gfx::VSyncProvider> CacaSurface::CreateVSyncProvider() {
+  return scoped_ptr<gfx::VSyncProvider>();
+}
+
+}  // namespace
+
+CacaWindowManager::CacaWindowManager() {
+}
+
+int32_t CacaWindowManager::AddWindow(CacaWindow* window) {
+  return windows_.Add(window);
+}
+
+void CacaWindowManager::RemoveWindow(int window_id, CacaWindow* window) {
+  DCHECK_EQ(window, windows_.Lookup(window_id));
+  windows_.Remove(window_id);
+}
+
+CacaWindowManager::~CacaWindowManager() {
+}
+
+bool CacaWindowManager::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  return false;
+}
+
+scoped_ptr<ui::SurfaceOzoneCanvas> CacaWindowManager::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  CacaWindow* window = windows_.Lookup(widget);
+  DCHECK(window);
+
+  scoped_ptr<CacaSurface> canvas(new CacaSurface(window));
+  bool initialized = canvas->Initialize();
+  DCHECK(initialized);
+  return canvas.PassAs<ui::SurfaceOzoneCanvas>();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/caca/caca_window_manager.h b/ui/ozone/platform/caca/caca_window_manager.h
new file mode 100644
index 0000000..e01cf55
--- /dev/null
+++ b/ui/ozone/platform/caca/caca_window_manager.h
@@ -0,0 +1,46 @@
+// 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 UI_OZONE_PLATFORM_CACA_CACA_WINDOW_MANAGER_H_
+#define UI_OZONE_PLATFORM_CACA_CACA_WINDOW_MANAGER_H_
+
+#include "base/id_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+
+class CacaWindow;
+
+class CacaWindowManager : public SurfaceFactoryOzone {
+ public:
+  CacaWindowManager();
+  virtual ~CacaWindowManager();
+
+  // Register a new libcaca window/instance. Returns the window id.
+  int32_t AddWindow(CacaWindow* window);
+
+  // Remove a libcaca window/instance.
+  void RemoveWindow(int32_t window_id, CacaWindow* window);
+
+  // ui::SurfaceFactoryOzone overrides:
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
+  virtual scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget widget) OVERRIDE;
+
+ private:
+  IDMap<CacaWindow> windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(CacaWindowManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CACA_CACA_WINDOW_MANAGER_H_
diff --git a/ui/ozone/platform/caca/ozone_platform_caca.cc b/ui/ozone/platform/caca/ozone_platform_caca.cc
new file mode 100644
index 0000000..d892a63
--- /dev/null
+++ b/ui/ozone/platform/caca/ozone_platform_caca.cc
@@ -0,0 +1,70 @@
+// 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 "ui/ozone/platform/caca/ozone_platform_caca.h"
+
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+#include "ui/ozone/platform/caca/caca_event_source.h"
+#include "ui/ozone/platform/caca/caca_window.h"
+#include "ui/ozone/platform/caca/caca_window_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+namespace {
+
+class OzonePlatformCaca : public OzonePlatform {
+ public:
+  OzonePlatformCaca() {}
+  virtual ~OzonePlatformCaca() {}
+
+  // OzonePlatform:
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
+    return window_manager_.get();
+  }
+  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
+    return cursor_factory_ozone_.get();
+  }
+  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
+    return NULL;  // no GPU support
+  }
+  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
+    return NULL;  // no GPU support
+  }
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) OVERRIDE {
+    scoped_ptr<CacaWindow> caca_window(new CacaWindow(
+        delegate, window_manager_.get(), event_source_.get(), bounds));
+    if (!caca_window->Initialize())
+      return scoped_ptr<PlatformWindow>();
+    return caca_window.PassAs<PlatformWindow>();
+  }
+  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
+      OVERRIDE {
+    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+  }
+
+  virtual void InitializeUI() OVERRIDE {
+    window_manager_.reset(new CacaWindowManager);
+    event_source_.reset(new CacaEventSource());
+    cursor_factory_ozone_.reset(new CursorFactoryOzone());
+  }
+
+  virtual void InitializeGPU() OVERRIDE {}
+
+ private:
+  scoped_ptr<CacaWindowManager> window_manager_;
+  scoped_ptr<CacaEventSource> event_source_;
+  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformCaca);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformCaca() { return new OzonePlatformCaca; }
+
+}  // namespace ui
diff --git a/ui/ozone/platform/caca/ozone_platform_caca.h b/ui/ozone/platform/caca/ozone_platform_caca.h
new file mode 100644
index 0000000..bf1f735
--- /dev/null
+++ b/ui/ozone/platform/caca/ozone_platform_caca.h
@@ -0,0 +1,17 @@
+// 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 UI_OZONE_PLATFORM_CACA_OZONE_PLATFORM_CACA_H_
+#define UI_OZONE_PLATFORM_CACA_OZONE_PLATFORM_CACA_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformCaca();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CACA_OZONE_PLATFORM_CACA_H_
diff --git a/ui/ozone/platform/caca/scoped_caca_types.cc b/ui/ozone/platform/caca/scoped_caca_types.cc
new file mode 100644
index 0000000..093b7c9
--- /dev/null
+++ b/ui/ozone/platform/caca/scoped_caca_types.cc
@@ -0,0 +1,23 @@
+// 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 "ui/ozone/platform/caca/scoped_caca_types.h"
+
+#include <caca.h>
+
+namespace ui {
+
+void CacaCanvasDeleter::operator()(caca_canvas_t* canvas) const {
+  caca_free_canvas(canvas);
+}
+
+void CacaDisplayDeleter::operator()(caca_display_t* display) const {
+  caca_free_display(display);
+}
+
+void CacaDitherDeleter::operator()(caca_dither_t* dither) const {
+  caca_free_dither(dither);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/caca/scoped_caca_types.h b/ui/ozone/platform/caca/scoped_caca_types.h
new file mode 100644
index 0000000..d789476
--- /dev/null
+++ b/ui/ozone/platform/caca/scoped_caca_types.h
@@ -0,0 +1,34 @@
+// 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 UI_OZONE_PLATFORM_CACA_SCOPED_CACA_TYPES_H_
+#define UI_OZONE_PLATFORM_CACA_SCOPED_CACA_TYPES_H_
+
+#include "base/memory/scoped_ptr.h"
+
+typedef struct caca_canvas caca_canvas_t;
+typedef struct caca_dither caca_dither_t;
+typedef struct caca_display caca_display_t;
+
+namespace ui {
+
+struct CacaCanvasDeleter {
+  void operator()(caca_canvas_t* canvas) const;
+};
+
+struct CacaDisplayDeleter {
+  void operator()(caca_display_t* display) const;
+};
+
+struct CacaDitherDeleter {
+  void operator()(caca_dither_t* dither) const;
+};
+
+typedef scoped_ptr<caca_canvas_t, CacaCanvasDeleter> ScopedCacaCanvas;
+typedef scoped_ptr<caca_display_t, CacaDisplayDeleter> ScopedCacaDisplay;
+typedef scoped_ptr<caca_dither_t, CacaDitherDeleter> ScopedCacaDither;
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_CACA_SCOPED_CACA_TYPES_H_
diff --git a/ui/ozone/platform/dri/BUILD.gn b/ui/ozone/platform/dri/BUILD.gn
new file mode 100644
index 0000000..61d2baf
--- /dev/null
+++ b/ui/ozone/platform/dri/BUILD.gn
@@ -0,0 +1,150 @@
+# 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("//build/config/linux/pkg_config.gni")
+import("//ui/ozone/ozone.gni")
+
+pkg_config("libdrm") {
+  packages = [ "libdrm" ]
+}
+
+source_set("dri_common") {
+  sources = [
+    "crtc_state.cc",
+    "crtc_state.h",
+    "display_mode_dri.cc",
+    "display_mode_dri.h",
+    "display_snapshot_dri.cc",
+    "display_snapshot_dri.h",
+    "dri_console_buffer.cc",
+    "dri_console_buffer.h",
+    "dri_cursor.cc",
+    "dri_cursor.h",
+    "dri_buffer.cc",
+    "dri_buffer.h",
+    "dri_surface.cc",
+    "dri_surface.h",
+    "dri_surface_factory.cc",
+    "dri_surface_factory.h",
+    "dri_util.cc",
+    "dri_util.h",
+    "dri_vsync_provider.cc",
+    "dri_vsync_provider.h",
+    "dri_window.cc",
+    "dri_window.h",
+    "dri_window_delegate.h",
+    "dri_window_delegate_impl.cc",
+    "dri_window_delegate_impl.h",
+    "dri_window_delegate_manager.cc",
+    "dri_window_delegate_manager.h",
+    "dri_window_manager.cc",
+    "dri_window_manager.h",
+    "dri_wrapper.cc",
+    "dri_wrapper.h",
+    "hardware_display_controller.cc",
+    "hardware_display_controller.h",
+    "native_display_delegate_dri.cc",
+    "native_display_delegate_dri.h",
+    "scoped_drm_types.cc",
+    "scoped_drm_types.h",
+    "screen_manager.cc",
+    "screen_manager.h",
+    "scanout_buffer.h",
+    "virtual_terminal_manager.cc",
+    "virtual_terminal_manager.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ipc",
+    "//ui/base",
+    "//ui/display/types",
+    "//ui/events",
+    "//ui/events/ozone:events_ozone_evdev",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  public_configs = [
+    ":libdrm",
+  ]
+}
+
+if (ozone_platform_dri) {
+  source_set("dri") {
+    sources = [
+      "ozone_platform_dri.cc",
+      "ozone_platform_dri.h",
+    ]
+
+    deps = [
+      ":dri_common",
+      "//base",
+      "//skia",
+      "//ui/events/ozone:events_ozone_evdev",
+    ]
+  }
+
+  source_set("dri_unittests") {
+    testonly = true
+    sources = [
+      "dri_surface_factory_unittest.cc",
+      "dri_surface_unittest.cc",
+      "hardware_display_controller_unittest.cc",
+      "screen_manager_unittest.cc",
+      "test/mock_dri_wrapper.cc",
+      "test/mock_dri_wrapper.h",
+    ]
+
+    deps = [
+      ":dri_common",
+      "//skia",
+      "//testing/gtest",
+    ]
+  }
+}
+
+if (ozone_platform_gbm) {
+  pkg_config("libgbm") {
+    packages = [ "gbm" ]
+  }
+
+  source_set("gbm") {
+    sources = [
+      "channel_observer.h",
+      "dri_window_delegate_proxy.cc",
+      "dri_window_delegate_proxy.h",
+      "gbm_buffer.cc",
+      "gbm_buffer.h",
+      "gbm_buffer_base.cc",
+      "gbm_buffer_base.h",
+      "gbm_surface.cc",
+      "gbm_surface.h",
+      "gbm_surfaceless.cc",
+      "gbm_surfaceless.h",
+      "gbm_surface_factory.cc",
+      "gbm_surface_factory.h",
+      "gpu_platform_support_gbm.cc",
+      "gpu_platform_support_gbm.h",
+      "gpu_platform_support_host_gbm.cc",
+      "gpu_platform_support_host_gbm.h",
+      "native_display_delegate_proxy.cc",
+      "native_display_delegate_proxy.h",
+      "ozone_platform_gbm.cc",
+      "ozone_platform_gbm.h",
+    ]
+
+    deps = [
+      ":dri_common",
+      "//base",
+      "//skia",
+      "//ui/events/ozone:events_ozone_evdev",
+    ]
+
+    public_configs = [
+      ":libgbm",
+    ]
+  }
+}
diff --git a/ui/ozone/platform/dri/DEPS b/ui/ozone/platform/dri/DEPS
new file mode 100644
index 0000000..0ab1471
--- /dev/null
+++ b/ui/ozone/platform/dri/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/display/util",
+]
diff --git a/ui/ozone/platform/dri/OWNERS b/ui/ozone/platform/dri/OWNERS
new file mode 100644
index 0000000..0d77c0e
--- /dev/null
+++ b/ui/ozone/platform/dri/OWNERS
@@ -0,0 +1 @@
+dnicoara@chromium.org
diff --git a/ui/ozone/platform/dri/channel_observer.h b/ui/ozone/platform/dri/channel_observer.h
new file mode 100644
index 0000000..356a79e
--- /dev/null
+++ b/ui/ozone/platform/dri/channel_observer.h
@@ -0,0 +1,21 @@
+// 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 UI_OZONE_PLATFORM_DRI_CHANNEL_OBSERVER_H_
+#define UI_OZONE_PLATFORM_DRI_CHANNEL_OBSERVER_H_
+
+namespace ui {
+
+// Observes the channel state.
+class ChannelObserver {
+ public:
+  virtual ~ChannelObserver() {}
+
+  virtual void OnChannelEstablished() = 0;
+  virtual void OnChannelDestroyed() = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_CHANNEL_OBSERVER_H_
diff --git a/ui/ozone/platform/dri/crtc_state.cc b/ui/ozone/platform/dri/crtc_state.cc
new file mode 100644
index 0000000..ff2e137
--- /dev/null
+++ b/ui/ozone/platform/dri/crtc_state.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 "ui/ozone/platform/dri/crtc_state.h"
+
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+CrtcState::CrtcState(DriWrapper* drm,
+                     uint32_t crtc,
+                     uint32_t connector)
+    : drm_(drm),
+      crtc_(crtc),
+      connector_(connector),
+      saved_crtc_(drm->GetCrtc(crtc)),
+      is_disabled_(true) {}
+
+CrtcState::~CrtcState() {
+  if (!is_disabled_) {
+    drm_->SetCrtc(saved_crtc_.get(), std::vector<uint32_t>(1, connector_));
+    drm_->SetCursor(crtc_, 0, gfx::Size());
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/crtc_state.h b/ui/ozone/platform/dri/crtc_state.h
new file mode 100644
index 0000000..e4ad391
--- /dev/null
+++ b/ui/ozone/platform/dri/crtc_state.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 UI_OZONE_PLATFORM_DRI_CRTC_STATE_H_
+#define UI_OZONE_PLATFORM_DRI_CRTC_STATE_H_
+
+#include <stdint.h>
+
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+namespace ui {
+
+class DriWrapper;
+
+// Represents the state of a CRTC.
+//
+// One CRTC can be paired up with one or more connectors. The simplest
+// configuration represents one CRTC driving one monitor, while pairing up a
+// CRTC with multiple connectors results in hardware mirroring.
+class CrtcState {
+ public:
+  CrtcState(DriWrapper* drm, uint32_t crtc, uint32_t connector);
+  ~CrtcState();
+
+  uint32_t crtc() const { return crtc_; }
+  uint32_t connector() const { return connector_; }
+  bool is_disabled() const { return is_disabled_; }
+
+  void set_is_disabled(bool state) { is_disabled_ = state; }
+
+ private:
+  DriWrapper* drm_;  // Not owned.
+
+  uint32_t crtc_;
+
+  // TODO(dnicoara) Add support for hardware mirroring (multiple connectors).
+  uint32_t connector_;
+
+  // Store the state of the CRTC before we took over. Used to restore the CRTC
+  // once we no longer need it.
+  ScopedDrmCrtcPtr saved_crtc_;
+
+  // Keeps track of the CRTC state. If a surface has been bound, then the value
+  // is set to false. Otherwise it is true.
+  bool is_disabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(CrtcState);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_CRTC_STATE_H_
diff --git a/ui/ozone/platform/dri/display_mode_dri.cc b/ui/ozone/platform/dri/display_mode_dri.cc
new file mode 100644
index 0000000..6dd660c
--- /dev/null
+++ b/ui/ozone/platform/dri/display_mode_dri.cc
@@ -0,0 +1,19 @@
+// 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 "ui/ozone/platform/dri/display_mode_dri.h"
+
+namespace ui {
+
+DisplayModeDri::DisplayModeDri(const drmModeModeInfo& mode)
+    : DisplayMode(gfx::Size(mode.hdisplay, mode.vdisplay),
+                  mode.flags & DRM_MODE_FLAG_INTERLACE,
+                  mode.vrefresh),
+      mode_info_(mode) {
+}
+
+DisplayModeDri::~DisplayModeDri() {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/display_mode_dri.h b/ui/ozone/platform/dri/display_mode_dri.h
new file mode 100644
index 0000000..c23c339
--- /dev/null
+++ b/ui/ozone/platform/dri/display_mode_dri.h
@@ -0,0 +1,33 @@
+// 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 UI_OZONE_PLATFORM_DRI_DISPLAY_MODE_DRI_H_
+#define UI_OZONE_PLATFORM_DRI_DISPLAY_MODE_DRI_H_
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <xf86drmMode.h>
+
+#include "ui/display/types/display_mode.h"
+
+namespace ui {
+
+class DisplayModeDri : public DisplayMode {
+ public:
+  DisplayModeDri(const drmModeModeInfo& mode);
+  virtual ~DisplayModeDri();
+
+  // Native details about this mode. Only used internally in the DRI
+  // implementation.
+  drmModeModeInfo mode_info() const { return mode_info_; }
+
+ private:
+  drmModeModeInfo mode_info_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayModeDri);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DISPLAY_MODE_DRI_H_
diff --git a/ui/ozone/platform/dri/display_snapshot_dri.cc b/ui/ozone/platform/dri/display_snapshot_dri.cc
new file mode 100644
index 0000000..fa7b6f9
--- /dev/null
+++ b/ui/ozone/platform/dri/display_snapshot_dri.cc
@@ -0,0 +1,127 @@
+// 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 "ui/ozone/platform/dri/display_snapshot_dri.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <xf86drmMode.h>
+
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "ui/display/util/edid_parser.h"
+#include "ui/ozone/platform/dri/display_mode_dri.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+namespace {
+
+DisplayConnectionType GetDisplayType(drmModeConnector* connector) {
+  switch (connector->connector_type) {
+    case DRM_MODE_CONNECTOR_VGA:
+      return DISPLAY_CONNECTION_TYPE_VGA;
+    case DRM_MODE_CONNECTOR_DVII:
+    case DRM_MODE_CONNECTOR_DVID:
+    case DRM_MODE_CONNECTOR_DVIA:
+      return DISPLAY_CONNECTION_TYPE_DVI;
+    case DRM_MODE_CONNECTOR_LVDS:
+    case DRM_MODE_CONNECTOR_eDP:
+      return DISPLAY_CONNECTION_TYPE_INTERNAL;
+    case DRM_MODE_CONNECTOR_DisplayPort:
+      return DISPLAY_CONNECTION_TYPE_DISPLAYPORT;
+    case DRM_MODE_CONNECTOR_HDMIA:
+    case DRM_MODE_CONNECTOR_HDMIB:
+      return DISPLAY_CONNECTION_TYPE_HDMI;
+    default:
+      return DISPLAY_CONNECTION_TYPE_UNKNOWN;
+  }
+}
+
+bool IsAspectPreserving(DriWrapper* drm, drmModeConnector* connector) {
+  ScopedDrmPropertyPtr property(drm->GetProperty(connector, "scaling mode"));
+  if (property) {
+    for (int j = 0; j < property->count_enums; ++j) {
+      if (property->enums[j].value ==
+              connector->prop_values[property->prop_id] &&
+          strcmp(property->enums[j].name, "Full aspect") == 0)
+        return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace
+
+DisplaySnapshotDri::DisplaySnapshotDri(DriWrapper* drm,
+                                       drmModeConnector* connector,
+                                       drmModeCrtc* crtc,
+                                       uint32_t index)
+    : DisplaySnapshot(index,
+                      false,
+                      gfx::Point(crtc->x, crtc->y),
+                      gfx::Size(connector->mmWidth, connector->mmHeight),
+                      GetDisplayType(connector),
+                      IsAspectPreserving(drm, connector),
+                      false,
+                      std::string(),
+                      std::vector<const DisplayMode*>(),
+                      NULL,
+                      NULL),
+      connector_(connector->connector_id),
+      crtc_(crtc->crtc_id),
+      dpms_property_(drm->GetProperty(connector, "DPMS")) {
+  if (!dpms_property_)
+    VLOG(1) << "Failed to find the DPMS property for connector "
+            << connector->connector_id;
+
+  ScopedDrmPropertyBlobPtr edid_blob(drm->GetPropertyBlob(connector, "EDID"));
+
+  if (edid_blob) {
+    std::vector<uint8_t> edid(
+        static_cast<uint8_t*>(edid_blob->data),
+        static_cast<uint8_t*>(edid_blob->data) + edid_blob->length);
+
+    has_proper_display_id_ = GetDisplayIdFromEDID(edid, index, &display_id_);
+    ParseOutputDeviceData(edid, NULL, &display_name_);
+    ParseOutputOverscanFlag(edid, &overscan_flag_);
+  } else {
+    VLOG(1) << "Failed to get EDID blob for connector "
+            << connector->connector_id;
+  }
+
+  for (int i = 0; i < connector->count_modes; ++i) {
+    drmModeModeInfo& mode = connector->modes[i];
+    modes_.push_back(new DisplayModeDri(mode));
+
+    if (crtc->mode_valid && SameMode(crtc->mode, mode))
+      current_mode_ = modes_.back();
+
+    if (mode.type & DRM_MODE_TYPE_PREFERRED)
+      native_mode_ = modes_.back();
+  }
+
+  // If no preferred mode is found then use the first one. Using the first one
+  // since it should be the best mode.
+  if (!native_mode_ && !modes_.empty())
+    native_mode_ = modes_.front();
+}
+
+DisplaySnapshotDri::~DisplaySnapshotDri() {
+}
+
+std::string DisplaySnapshotDri::ToString() const {
+  return base::StringPrintf(
+      "[type=%d, connector=%" PRIu32 ", crtc=%" PRIu32 ", mode=%s, dim=%s]",
+      type_,
+      connector_,
+      crtc_,
+      current_mode_ ? current_mode_->ToString().c_str() : "NULL",
+      physical_size_.ToString().c_str());
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/display_snapshot_dri.h b/ui/ozone/platform/dri/display_snapshot_dri.h
new file mode 100644
index 0000000..a509be4
--- /dev/null
+++ b/ui/ozone/platform/dri/display_snapshot_dri.h
@@ -0,0 +1,44 @@
+// 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 UI_OZONE_PLATFORM_DRI_DISPLAY_SNAPSHOT_DRI_H_
+#define UI_OZONE_PLATFORM_DRI_DISPLAY_SNAPSHOT_DRI_H_
+
+#include "ui/display/types/display_snapshot.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+namespace ui {
+
+class DriWrapper;
+
+class DisplaySnapshotDri : public DisplaySnapshot {
+ public:
+  DisplaySnapshotDri(DriWrapper* drm,
+                     drmModeConnector* connector,
+                     drmModeCrtc* crtc,
+                     uint32_t index);
+  virtual ~DisplaySnapshotDri();
+
+  // Native properties of a display used by the DRI implementation in
+  // configuring this display.
+  uint32_t connector() const { return connector_; }
+  uint32_t crtc() const { return crtc_; }
+  drmModePropertyRes* dpms_property() const { return dpms_property_.get(); }
+
+  // DisplaySnapshot overrides:
+  virtual std::string ToString() const OVERRIDE;
+
+ private:
+  uint32_t connector_;
+  uint32_t crtc_;
+  ScopedDrmPropertyPtr dpms_property_;
+  std::string name_;
+  bool overscan_flag_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotDri);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DISPLAY_SNAPSHOT_DRI_H_
diff --git a/ui/ozone/platform/dri/dri.gypi b/ui/ozone/platform/dri/dri.gypi
new file mode 100644
index 0000000..9c74b7f
--- /dev/null
+++ b/ui/ozone/platform/dri/dri.gypi
@@ -0,0 +1,120 @@
+# 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.
+
+{
+  'variables': {
+    'internal_ozone_platform_deps': [
+      'ozone_platform_dri',
+    ],
+    'internal_ozone_platform_unittest_deps': [
+      'ozone_platform_dri_unittests',
+    ],
+    'internal_ozone_platforms': [
+      'dri',
+    ],
+    'use_drm_atomic_flip%': 0,
+  },
+  'targets': [
+    {
+      'target_name': 'ozone_platform_dri',
+      'type': 'static_library',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../build/linux/system.gyp:libdrm',
+        '../../skia/skia.gyp:skia',
+        '../base/ui_base.gyp:ui_base',
+        '../display/display.gyp:display_types',
+        '../display/display.gyp:display_util',
+        '../events/events.gyp:events',
+        '../events/ozone/events_ozone.gyp:events_ozone_evdev',
+        '../gfx/gfx.gyp:gfx',
+        '../gfx/gfx.gyp:gfx_geometry',
+      ],
+      'defines': [
+        'OZONE_IMPLEMENTATION',
+      ],
+      'sources': [
+        'crtc_state.cc',
+        'crtc_state.h',
+        'display_mode_dri.cc',
+        'display_mode_dri.h',
+        'display_snapshot_dri.cc',
+        'display_snapshot_dri.h',
+        'dri_console_buffer.cc',
+        'dri_console_buffer.h',
+        'dri_buffer.cc',
+        'dri_buffer.h',
+        'dri_cursor.cc',
+        'dri_cursor.h',
+        'dri_surface.cc',
+        'dri_surface.h',
+        'dri_surface_factory.cc',
+        'dri_surface_factory.h',
+        'dri_util.cc',
+        'dri_util.h',
+        'dri_vsync_provider.cc',
+        'dri_vsync_provider.h',
+        'dri_window.cc',
+        'dri_window.h',
+        'dri_window_delegate.h',
+        'dri_window_delegate_impl.cc',
+        'dri_window_delegate_impl.h',
+        'dri_window_delegate_manager.cc',
+        'dri_window_delegate_manager.h',
+        'dri_window_manager.cc',
+        'dri_window_manager.h',
+        'dri_wrapper.cc',
+        'dri_wrapper.h',
+        'hardware_display_controller.cc',
+        'hardware_display_controller.h',
+        'native_display_delegate_dri.cc',
+        'native_display_delegate_dri.h',
+        'ozone_platform_dri.cc',
+        'ozone_platform_dri.h',
+        'scoped_drm_types.cc',
+        'scoped_drm_types.h',
+        'screen_manager.cc',
+        'screen_manager.h',
+        'scanout_buffer.h',
+        'virtual_terminal_manager.cc',
+        'virtual_terminal_manager.h',
+      ],
+      'conditions': [
+        ['use_drm_atomic_flip==1', {
+          'sources': [
+            'hardware_display_plane.cc',
+            'hardware_display_plane.h',
+            'hardware_display_plane_manager.cc',
+            'hardware_display_plane_manager.h',
+          ],
+        }],
+      ],
+    },
+    {
+      'target_name': 'ozone_platform_dri_unittests',
+      'type': 'none',
+      'dependencies': [
+        '../../build/linux/system.gyp:libdrm',
+        '../../skia/skia.gyp:skia',
+        '../gfx/gfx.gyp:gfx_geometry',
+        'ozone_platform_dri',
+      ],
+      'export_dependent_settings': [
+        '../../build/linux/system.gyp:libdrm',
+        '../../skia/skia.gyp:skia',
+        '../gfx/gfx.gyp:gfx_geometry',
+      ],
+      'direct_dependent_settings': {
+        'sources': [
+          'dri_surface_factory_unittest.cc',
+          'dri_surface_unittest.cc',
+          'hardware_display_controller_unittest.cc',
+          'screen_manager_unittest.cc',
+          'test/mock_dri_wrapper.cc',
+          'test/mock_dri_wrapper.h',
+        ],
+      },
+    },
+  ],
+}
diff --git a/ui/ozone/platform/dri/dri_buffer.cc b/ui/ozone/platform/dri/dri_buffer.cc
new file mode 100644
index 0000000..4a35395
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_buffer.cc
@@ -0,0 +1,111 @@
+// 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 "ui/ozone/platform/dri/dri_buffer.h"
+
+#include "base/logging.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+namespace {
+
+// Modesetting cannot happen from a buffer with transparencies. Return the size
+// of a pixel without alpha.
+uint8_t GetColorDepth(SkColorType type) {
+  switch (type) {
+    case kUnknown_SkColorType:
+    case kAlpha_8_SkColorType:
+      return 0;
+    case kIndex_8_SkColorType:
+      return 8;
+    case kRGB_565_SkColorType:
+      return 16;
+    case kARGB_4444_SkColorType:
+      return 12;
+    case kN32_SkColorType:
+      return 24;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+}  // namespace
+
+DriBuffer::DriBuffer(DriWrapper* dri)
+    : dri_(dri), handle_(0), framebuffer_(0) {}
+
+DriBuffer::~DriBuffer() {
+  if (!surface_)
+    return;
+
+  if (framebuffer_)
+    dri_->RemoveFramebuffer(framebuffer_);
+
+  SkImageInfo info;
+  void* pixels = const_cast<void*>(surface_->peekPixels(&info, NULL));
+  if (!pixels)
+    return;
+
+  dri_->DestroyDumbBuffer(info, handle_, stride_, pixels);
+}
+
+bool DriBuffer::Initialize(const SkImageInfo& info) {
+  void* pixels = NULL;
+  if (!dri_->CreateDumbBuffer(info, &handle_, &stride_, &pixels)) {
+    VLOG(2) << "Cannot create drm dumb buffer";
+    return false;
+  }
+
+  if (!dri_->AddFramebuffer(info.width(),
+                            info.height(),
+                            GetColorDepth(info.colorType()),
+                            info.bytesPerPixel() << 3,
+                            stride_,
+                            handle_,
+                            &framebuffer_)) {
+    VLOG(2) << "Failed to register framebuffer: " << strerror(errno);
+    return false;
+  }
+
+  surface_ = skia::AdoptRef(SkSurface::NewRasterDirect(info, pixels, stride_));
+  if (!surface_) {
+    VLOG(2) << "Cannot install Skia pixels for drm buffer";
+    return false;
+  }
+
+  return true;
+}
+
+SkCanvas* DriBuffer::GetCanvas() const {
+  return surface_->getCanvas();
+}
+
+uint32_t DriBuffer::GetFramebufferId() const {
+  return framebuffer_;
+}
+
+uint32_t DriBuffer::GetHandle() const {
+  return handle_;
+}
+
+gfx::Size DriBuffer::GetSize() const {
+  return gfx::Size(surface_->width(), surface_->height());
+}
+
+DriBufferGenerator::DriBufferGenerator(DriWrapper* dri) : dri_(dri) {}
+
+DriBufferGenerator::~DriBufferGenerator() {}
+
+scoped_refptr<ScanoutBuffer> DriBufferGenerator::Create(const gfx::Size& size) {
+  scoped_refptr<DriBuffer> buffer(new DriBuffer(dri_));
+  SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+  if (!buffer->Initialize(info))
+    return NULL;
+
+  return buffer;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_buffer.h b/ui/ozone/platform/dri/dri_buffer.h
new file mode 100644
index 0000000..9b645ae
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_buffer.h
@@ -0,0 +1,72 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_BUFFER_H_
+
+#include "base/macros.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/ozone/platform/dri/scanout_buffer.h"
+
+namespace ui {
+
+class DriWrapper;
+
+// Wrapper for a DRM allocated buffer. Keeps track of the native properties of
+// the buffer and wraps the pixel memory into a SkSurface which can be used to
+// draw into using Skia.
+class DriBuffer : public ScanoutBuffer {
+ public:
+  DriBuffer(DriWrapper* dri);
+
+  // Allocates the backing pixels and wraps them in |surface_|. |info| is used
+  // to describe the buffer characteristics (size, color format).
+  bool Initialize(const SkImageInfo& info);
+
+  SkCanvas* GetCanvas() const;
+
+  // ScanoutBuffer:
+  virtual uint32_t GetFramebufferId() const OVERRIDE;
+  virtual uint32_t GetHandle() const OVERRIDE;
+  virtual gfx::Size GetSize() const OVERRIDE;
+
+ protected:
+  virtual ~DriBuffer();
+
+  DriWrapper* dri_;  // Not owned.
+
+  // Wrapper around the native pixel memory.
+  skia::RefPtr<SkSurface> surface_;
+
+  // Length of a row of pixels.
+  uint32_t stride_;
+
+  // Buffer handle used by the DRM allocator.
+  uint32_t handle_;
+
+  // Buffer ID used by the DRM modesettings API. This is set when the buffer is
+  // registered with the CRTC.
+  uint32_t framebuffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriBuffer);
+};
+
+class DriBufferGenerator : public ScanoutBufferGenerator {
+ public:
+  DriBufferGenerator(DriWrapper* dri);
+  virtual ~DriBufferGenerator();
+
+  // ScanoutBufferGenerator:
+  virtual scoped_refptr<ScanoutBuffer> Create(const gfx::Size& size) OVERRIDE;
+
+ private:
+  DriWrapper* dri_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(DriBufferGenerator);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_BUFFER_H_
diff --git a/ui/ozone/platform/dri/dri_console_buffer.cc b/ui/ozone/platform/dri/dri_console_buffer.cc
new file mode 100644
index 0000000..6c1f7a9
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_console_buffer.cc
@@ -0,0 +1,56 @@
+// 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 "ui/ozone/platform/dri/dri_console_buffer.h"
+
+#include <sys/mman.h>
+#include <xf86drmMode.h>
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+namespace ui {
+
+DriConsoleBuffer::DriConsoleBuffer(DriWrapper* dri, uint32_t framebuffer)
+    : dri_(dri),
+      handle_(0),
+      framebuffer_(framebuffer),
+      mmap_base_(NULL),
+      mmap_size_(0) {
+}
+
+DriConsoleBuffer::~DriConsoleBuffer() {
+  if (mmap_base_)
+    if (munmap(mmap_base_, mmap_size_))
+      PLOG(ERROR) << "munmap";
+}
+
+bool DriConsoleBuffer::Initialize() {
+  ScopedDrmFramebufferPtr fb(dri_->GetFramebuffer(framebuffer_));
+
+  if (!fb)
+    return false;
+
+  handle_ = fb->handle;
+  stride_ = fb->pitch;
+  SkImageInfo info = SkImageInfo::MakeN32Premul(fb->width, fb->height);
+
+  mmap_size_ = info.getSafeSize(stride_);
+
+  if (!MapDumbBuffer(dri_->get_fd(), fb->handle, mmap_size_, &mmap_base_)) {
+    mmap_base_ = NULL;
+    return false;
+  }
+
+  surface_ =
+      skia::AdoptRef(SkSurface::NewRasterDirect(info, mmap_base_, stride_));
+  if (!surface_)
+    return false;
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_console_buffer.h b/ui/ozone/platform/dri/dri_console_buffer.h
new file mode 100644
index 0000000..51cb4ad
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_console_buffer.h
@@ -0,0 +1,58 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_CONSOLE_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_CONSOLE_BUFFER_H_
+
+#include "base/macros.h"
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+
+class SkCanvas;
+
+namespace ui {
+
+class DriWrapper;
+
+// Wrapper for the console buffer. This is the buffer that is allocated by
+// default by the system and is used when no application is controlling the
+// CRTC. Keeps track of the native properties of the buffer and wraps the pixel
+// memory into a SkSurface which can be used to draw into using Skia.
+class DriConsoleBuffer {
+ public:
+  DriConsoleBuffer(DriWrapper* dri, uint32_t framebuffer);
+  ~DriConsoleBuffer();
+
+  SkCanvas* canvas() { return surface_->getCanvas(); }
+
+  // Memory map the backing pixels and wrap them in |surface_|.
+  bool Initialize();
+
+ protected:
+  DriWrapper* dri_;  // Not owned.
+
+  // Wrapper around the native pixel memory.
+  skia::RefPtr<SkSurface> surface_;
+
+  // Length of a row of pixels.
+  uint32_t stride_;
+
+  // Buffer handle used by the DRM allocator.
+  uint32_t handle_;
+
+  // Buffer ID used by the DRM modesettings API.
+  uint32_t framebuffer_;
+
+  // Memory map base address.
+  void* mmap_base_;
+
+  // Memory map size.
+  size_t mmap_size_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriConsoleBuffer);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_CONSOLE_BUFFER_H_
diff --git a/ui/ozone/platform/dri/dri_cursor.cc b/ui/ozone/platform/dri/dri_cursor.cc
new file mode 100644
index 0000000..308541b
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_cursor.cc
@@ -0,0 +1,99 @@
+// 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 "ui/ozone/platform/dri/dri_cursor.h"
+
+#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/point_conversions.h"
+#include "ui/gfx/geometry/point_f.h"
+#include "ui/ozone/platform/dri/dri_surface_factory.h"
+#include "ui/ozone/platform/dri/dri_window.h"
+#include "ui/ozone/platform/dri/dri_window_manager.h"
+#include "ui/ozone/platform/dri/hardware_cursor_delegate.h"
+
+namespace ui {
+
+DriCursor::DriCursor(HardwareCursorDelegate* hardware,
+                     DriWindowManager* window_manager)
+    : hardware_(hardware),
+      window_manager_(window_manager),
+      cursor_window_(gfx::kNullAcceleratedWidget) {
+}
+
+DriCursor::~DriCursor() {
+}
+
+void DriCursor::SetCursor(gfx::AcceleratedWidget widget,
+                          PlatformCursor platform_cursor) {
+  DCHECK_NE(widget, gfx::kNullAcceleratedWidget);
+  scoped_refptr<BitmapCursorOzone> cursor =
+      BitmapCursorFactoryOzone::GetBitmapCursor(platform_cursor);
+  if (cursor_ == cursor || cursor_window_ != widget)
+    return;
+
+  cursor_ = cursor;
+  ShowCursor();
+}
+
+void DriCursor::ShowCursor() {
+  DCHECK_NE(cursor_window_, gfx::kNullAcceleratedWidget);
+  if (cursor_.get())
+    hardware_->SetHardwareCursor(cursor_window_,
+                                 cursor_->bitmaps(),
+                                 bitmap_location(),
+                                 cursor_->frame_delay_ms());
+  else
+    HideCursor();
+}
+
+void DriCursor::HideCursor() {
+  DCHECK_NE(cursor_window_, gfx::kNullAcceleratedWidget);
+  hardware_->SetHardwareCursor(
+      cursor_window_, std::vector<SkBitmap>(), gfx::Point(), 0);
+}
+
+void DriCursor::MoveCursorTo(gfx::AcceleratedWidget widget,
+                             const gfx::PointF& location) {
+  if (widget != cursor_window_ && cursor_window_ != gfx::kNullAcceleratedWidget)
+    HideCursor();
+
+  cursor_window_ = widget;
+  cursor_location_ = location;
+
+  if (cursor_window_ == gfx::kNullAcceleratedWidget)
+    return;
+
+  DriWindow* window = window_manager_->GetWindow(cursor_window_);
+  const gfx::Size& size = window->GetBounds().size();
+  cursor_location_.SetToMax(gfx::PointF(0, 0));
+  // Right and bottom edges are exclusive.
+  cursor_location_.SetToMin(gfx::PointF(size.width() - 1, size.height() - 1));
+
+  if (cursor_.get())
+    hardware_->MoveHardwareCursor(cursor_window_, bitmap_location());
+}
+
+void DriCursor::MoveCursor(const gfx::Vector2dF& delta) {
+  MoveCursorTo(cursor_window_, cursor_location_ + delta);
+}
+
+gfx::AcceleratedWidget DriCursor::GetCursorWindow() {
+  return cursor_window_;
+}
+
+bool DriCursor::IsCursorVisible() {
+  return cursor_.get();
+}
+
+gfx::PointF DriCursor::location() {
+  return cursor_location_;
+}
+
+gfx::Point DriCursor::bitmap_location() {
+  return gfx::ToFlooredPoint(cursor_location_) -
+         cursor_->hotspot().OffsetFromOrigin();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_cursor.h b/ui/ozone/platform/dri/dri_cursor.h
new file mode 100644
index 0000000..949584c
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_cursor.h
@@ -0,0 +1,64 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_CURSOR_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_CURSOR_H_
+
+#include "base/memory/ref_counted.h"
+#include "ui/base/cursor/cursor.h"
+#include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class PointF;
+class Vector2dF;
+}
+
+namespace ui {
+
+class BitmapCursorOzone;
+class BitmapCursorFactoryOzone;
+class DriWindowManager;
+class HardwareCursorDelegate;
+
+class DriCursor : public CursorDelegateEvdev {
+ public:
+  explicit DriCursor(HardwareCursorDelegate* hardware,
+                     DriWindowManager* window_manager);
+  virtual ~DriCursor();
+
+  void SetCursor(gfx::AcceleratedWidget widget, PlatformCursor platform_cursor);
+  void ShowCursor();
+  void HideCursor();
+  gfx::AcceleratedWidget GetCursorWindow();
+
+  // CursorDelegateEvdev:
+  virtual void MoveCursorTo(gfx::AcceleratedWidget widget,
+                            const gfx::PointF& location) OVERRIDE;
+  virtual void MoveCursor(const gfx::Vector2dF& delta) OVERRIDE;
+  virtual bool IsCursorVisible() OVERRIDE;
+  virtual gfx::PointF location() OVERRIDE;
+
+ private:
+  // The location of the bitmap (the cursor location is the hotspot location).
+  gfx::Point bitmap_location();
+
+  // The DRI implementation for setting the hardware cursor.
+  HardwareCursorDelegate* hardware_;
+
+  DriWindowManager* window_manager_;  // Not owned.
+
+  // The current cursor bitmap.
+  scoped_refptr<BitmapCursorOzone> cursor_;
+
+  // The window under the cursor.
+  gfx::AcceleratedWidget cursor_window_;
+
+  // The location of the cursor within the window.
+  gfx::PointF cursor_location_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_CURSOR_H_
diff --git a/ui/ozone/platform/dri/dri_surface.cc b/ui/ozone/platform/dri/dri_surface.cc
new file mode 100644
index 0000000..34f2ce2
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface.cc
@@ -0,0 +1,101 @@
+// 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 "ui/ozone/platform/dri/dri_surface.h"
+
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_vsync_provider.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+
+namespace ui {
+
+namespace {
+
+scoped_refptr<DriBuffer> AllocateBuffer(DriWrapper* dri,
+                                        const gfx::Size& size) {
+  scoped_refptr<DriBuffer> buffer(new DriBuffer(dri));
+  SkImageInfo info = SkImageInfo::MakeN32Premul(size.width(), size.height());
+
+  bool initialized = buffer->Initialize(info);
+  DCHECK(initialized) << "Failed to create drm buffer.";
+
+  return buffer;
+}
+
+}  // namespace
+
+DriSurface::DriSurface(DriWindowDelegate* window_delegate, DriWrapper* dri)
+    : window_delegate_(window_delegate),
+      dri_(dri),
+      buffers_(),
+      front_buffer_(0) {
+}
+
+DriSurface::~DriSurface() {
+}
+
+skia::RefPtr<SkCanvas> DriSurface::GetCanvas() {
+  return skia::SharePtr(surface_->getCanvas());
+}
+
+void DriSurface::ResizeCanvas(const gfx::Size& viewport_size) {
+  SkImageInfo info = SkImageInfo::MakeN32(
+      viewport_size.width(), viewport_size.height(), kOpaque_SkAlphaType);
+  surface_ = skia::AdoptRef(SkSurface::NewRaster(info));
+
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return;
+
+  // For the display buffers use the mode size since a |viewport_size| smaller
+  // than the display size will not scanout.
+  for (size_t i = 0; i < arraysize(buffers_); ++i)
+    buffers_[i] = AllocateBuffer(dri_, controller->GetModeSize());
+}
+
+void DriSurface::PresentCanvas(const gfx::Rect& damage) {
+  DCHECK(base::MessageLoopForUI::IsCurrent());
+  DCHECK(buffers_[front_buffer_ ^ 1].get());
+
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return;
+
+  controller->QueueOverlayPlane(OverlayPlane(buffers_[front_buffer_ ^ 1]));
+
+  UpdateNativeSurface(damage);
+  controller->SchedulePageFlip();
+  controller->WaitForPageFlipEvent();
+
+  // Update our front buffer pointer.
+  front_buffer_ ^= 1;
+}
+
+scoped_ptr<gfx::VSyncProvider> DriSurface::CreateVSyncProvider() {
+  return scoped_ptr<gfx::VSyncProvider>(new DriVSyncProvider(window_delegate_));
+}
+
+void DriSurface::UpdateNativeSurface(const gfx::Rect& damage) {
+  SkCanvas* canvas = buffers_[front_buffer_ ^ 1]->GetCanvas();
+
+  // The DriSurface is double buffered, so the current back buffer is
+  // missing the previous update. Expand damage region.
+  SkRect real_damage = RectToSkRect(UnionRects(damage, last_damage_));
+
+  // Copy damage region.
+  skia::RefPtr<SkImage> image = skia::AdoptRef(surface_->newImageSnapshot());
+  canvas->drawImageRect(image.get(), &real_damage, real_damage, NULL);
+
+  last_damage_ = damage;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_surface.h b/ui/ozone/platform/dri/dri_surface.h
new file mode 100644
index 0000000..740a310
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface.h
@@ -0,0 +1,58 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_SURFACE_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_SURFACE_H_
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+class SkCanvas;
+class SkSurface;
+
+namespace ui {
+
+class DriBuffer;
+class DriWindowDelegate;
+class DriWrapper;
+class HardwareDisplayController;
+
+class DriSurface : public SurfaceOzoneCanvas {
+ public:
+  DriSurface(DriWindowDelegate* window_delegate, DriWrapper* dri);
+  virtual ~DriSurface();
+
+  // SurfaceOzoneCanvas:
+  virtual skia::RefPtr<SkCanvas> GetCanvas() OVERRIDE;
+  virtual void ResizeCanvas(const gfx::Size& viewport_size) OVERRIDE;
+  virtual void PresentCanvas(const gfx::Rect& damage) OVERRIDE;
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE;
+
+ private:
+  void UpdateNativeSurface(const gfx::Rect& damage);
+
+  DriWindowDelegate* window_delegate_;
+
+  // Stores the connection to the graphics card. Pointer not owned by this
+  // class.
+  DriWrapper* dri_;
+
+  // The actual buffers used for painting.
+  scoped_refptr<DriBuffer> buffers_[2];
+
+  // Keeps track of which bitmap is |buffers_| is the frontbuffer.
+  int front_buffer_;
+
+  skia::RefPtr<SkSurface> surface_;
+  gfx::Rect last_damage_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriSurface);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_SURFACE_H_
diff --git a/ui/ozone/platform/dri/dri_surface_factory.cc b/ui/ozone/platform/dri/dri_surface_factory.cc
new file mode 100644
index 0000000..fb8ce75
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface_factory.cc
@@ -0,0 +1,184 @@
+// 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 "ui/ozone/platform/dri/dri_surface_factory.h"
+
+#include <errno.h>
+
+#include "base/debug/trace_event.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_surface.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+
+namespace {
+
+// TODO(dnicoara) Read the cursor plane size from the hardware.
+const gfx::Size kCursorSize(64, 64);
+
+void UpdateCursorImage(DriBuffer* cursor, const SkBitmap& image) {
+  SkRect damage;
+  image.getBounds(&damage);
+
+  // Clear to transparent in case |image| is smaller than the canvas.
+  SkCanvas* canvas = cursor->GetCanvas();
+  canvas->clear(SK_ColorTRANSPARENT);
+
+  SkRect clip;
+  clip.set(
+      0, 0, canvas->getDeviceSize().width(), canvas->getDeviceSize().height());
+  canvas->clipRect(clip, SkRegion::kReplace_Op);
+  canvas->drawBitmapRectToRect(image, &damage, damage);
+}
+
+}  // namespace
+
+// static
+const gfx::AcceleratedWidget DriSurfaceFactory::kDefaultWidgetHandle = 1;
+
+DriSurfaceFactory::DriSurfaceFactory(DriWrapper* drm,
+                                     ScreenManager* screen_manager,
+                                     DriWindowDelegateManager* window_manager)
+    : drm_(drm),
+      screen_manager_(screen_manager),
+      window_manager_(window_manager),
+      state_(UNINITIALIZED),
+      cursor_frontbuffer_(0),
+      cursor_widget_(0),
+      cursor_frame_(0),
+      cursor_frame_delay_ms_(0) {
+}
+
+DriSurfaceFactory::~DriSurfaceFactory() {
+  if (state_ == INITIALIZED)
+    ShutdownHardware();
+}
+
+DriSurfaceFactory::HardwareState DriSurfaceFactory::InitializeHardware() {
+  if (state_ != UNINITIALIZED)
+    return state_;
+
+  if (drm_->get_fd() < 0) {
+    LOG(ERROR) << "Failed to create DRI connection";
+    state_ = FAILED;
+    return state_;
+  }
+
+  SkImageInfo info = SkImageInfo::MakeN32Premul(kCursorSize.width(),
+                                                kCursorSize.height());
+  for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) {
+    cursor_buffers_[i] = new DriBuffer(drm_);
+    if (!cursor_buffers_[i]->Initialize(info)) {
+      LOG(ERROR) << "Failed to initialize cursor buffer";
+      state_ = FAILED;
+      return state_;
+    }
+  }
+
+  state_ = INITIALIZED;
+  return state_;
+}
+
+void DriSurfaceFactory::ShutdownHardware() {
+  DCHECK(state_ == INITIALIZED);
+  state_ = UNINITIALIZED;
+}
+
+scoped_ptr<ui::SurfaceOzoneCanvas> DriSurfaceFactory::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(state_ == INITIALIZED);
+
+  return scoped_ptr<ui::SurfaceOzoneCanvas>(
+      new DriSurface(window_manager_->GetWindowDelegate(widget), drm_));
+}
+
+bool DriSurfaceFactory::LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  return false;
+}
+
+void DriSurfaceFactory::SetHardwareCursor(gfx::AcceleratedWidget widget,
+                                          const std::vector<SkBitmap>& bitmaps,
+                                          const gfx::Point& location,
+                                          int frame_delay_ms) {
+  cursor_widget_ = widget;
+  cursor_bitmaps_ = bitmaps;
+  cursor_location_ = location;
+  cursor_frame_ = 0;
+  cursor_frame_delay_ms_ = frame_delay_ms;
+  cursor_timer_.Stop();
+
+  if (cursor_frame_delay_ms_)
+    cursor_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(cursor_frame_delay_ms_),
+        this,
+        &DriSurfaceFactory::OnCursorAnimationTimeout);
+
+  if (state_ != INITIALIZED)
+    return;
+
+  ResetCursor();
+}
+
+void DriSurfaceFactory::MoveHardwareCursor(gfx::AcceleratedWidget widget,
+                                           const gfx::Point& location) {
+  cursor_location_ = location;
+
+  if (state_ != INITIALIZED)
+    return;
+
+  HardwareDisplayController* controller =
+      window_manager_->GetWindowDelegate(widget)->GetController();
+  if (controller)
+    controller->MoveCursor(location);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DriSurfaceFactory private
+
+void DriSurfaceFactory::ResetCursor() {
+  if (!cursor_widget_)
+    return;
+
+  HardwareDisplayController* controller =
+      window_manager_->GetWindowDelegate(cursor_widget_)->GetController();
+  if (cursor_bitmaps_.size()) {
+    // Draw new cursor into backbuffer.
+    UpdateCursorImage(cursor_buffers_[cursor_frontbuffer_ ^ 1].get(),
+                      cursor_bitmaps_[cursor_frame_]);
+
+    // Reset location & buffer.
+    if (controller) {
+      controller->MoveCursor(cursor_location_);
+      controller->SetCursor(cursor_buffers_[cursor_frontbuffer_ ^ 1]);
+      cursor_frontbuffer_ ^= 1;
+    }
+  } else {
+    // No cursor set.
+    if (controller)
+      controller->UnsetCursor();
+  }
+}
+
+void DriSurfaceFactory::OnCursorAnimationTimeout() {
+  cursor_frame_++;
+  cursor_frame_ %= cursor_bitmaps_.size();
+
+  ResetCursor();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_surface_factory.h b/ui/ozone/platform/dri/dri_surface_factory.h
new file mode 100644
index 0000000..6800128
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface_factory.h
@@ -0,0 +1,92 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_SURFACE_FACTORY_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/timer/timer.h"
+#include "third_party/skia/include/core/SkBitmap.h"
+#include "ui/ozone/platform/dri/hardware_cursor_delegate.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+class DriBuffer;
+class DriWindowDelegateManager;
+class DriWrapper;
+class ScreenManager;
+class SurfaceOzoneCanvas;
+
+// SurfaceFactoryOzone implementation on top of DRM/KMS using dumb buffers.
+// This implementation is used in conjunction with the software rendering
+// path.
+class DriSurfaceFactory : public SurfaceFactoryOzone,
+                          public HardwareCursorDelegate {
+ public:
+  static const gfx::AcceleratedWidget kDefaultWidgetHandle;
+
+  DriSurfaceFactory(DriWrapper* drm,
+                    ScreenManager* screen_manager,
+                    DriWindowDelegateManager* window_manager);
+  virtual ~DriSurfaceFactory();
+
+  // Describes the state of the hardware after initialization.
+  enum HardwareState {
+    UNINITIALIZED,
+    INITIALIZED,
+    FAILED,
+  };
+
+  // Open the display device.
+  HardwareState InitializeHardware();
+
+  // Close the display device.
+  void ShutdownHardware();
+
+  // SurfaceFactoryOzone:
+  virtual scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget widget) OVERRIDE;
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
+
+  // HardwareCursorDelegate:
+  virtual void SetHardwareCursor(gfx::AcceleratedWidget widget,
+                                 const std::vector<SkBitmap>& bitmaps,
+                                 const gfx::Point& location,
+                                 int frame_delay_ms) OVERRIDE;
+  virtual void MoveHardwareCursor(gfx::AcceleratedWidget window,
+                                  const gfx::Point& location) OVERRIDE;
+
+ protected:
+  // Draw the last set cursor & update the cursor plane.
+  void ResetCursor();
+
+  // Draw next frame in an animated cursor.
+  void OnCursorAnimationTimeout();
+
+  DriWrapper* drm_;  // Not owned.
+  ScreenManager* screen_manager_;  // Not owned.
+  DriWindowDelegateManager* window_manager_;  // Not owned.
+  HardwareState state_;
+
+  scoped_refptr<DriBuffer> cursor_buffers_[2];
+  int cursor_frontbuffer_;
+
+  gfx::AcceleratedWidget cursor_widget_;
+  std::vector<SkBitmap> cursor_bitmaps_;
+  gfx::Point cursor_location_;
+  int cursor_frame_;
+  int cursor_frame_delay_ms_;
+  base::RepeatingTimer<DriSurfaceFactory> cursor_timer_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriSurfaceFactory);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_SURFACE_FACTORY_H_
diff --git a/ui/ozone/platform/dri/dri_surface_factory_unittest.cc b/ui/ozone/platform/dri/dri_surface_factory_unittest.cc
new file mode 100644
index 0000000..1fbaf31
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface_factory_unittest.cc
@@ -0,0 +1,155 @@
+// 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 <vector>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_surface.h"
+#include "ui/ozone/platform/dri/dri_surface_factory.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/platform/dri/test/mock_dri_wrapper.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace {
+
+// Mode of size 6x4.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kDefaultCrtc = 1;
+const uint32_t kDefaultConnector = 2;
+
+class MockScreenManager : public ui::ScreenManager {
+ public:
+  MockScreenManager(ui::DriWrapper* dri,
+                    ui::ScanoutBufferGenerator* buffer_generator)
+      : ScreenManager(dri, buffer_generator),
+        dri_(dri) {}
+  virtual ~MockScreenManager() {}
+
+  // Normally we'd use DRM to figure out the controller configuration. But we
+  // can't use DRM in unit tests, so we just create a fake configuration.
+  virtual void ForceInitializationOfPrimaryDisplay() OVERRIDE {
+    ConfigureDisplayController(
+        kDefaultCrtc, kDefaultConnector, gfx::Point(), kDefaultMode);
+  }
+
+ private:
+  ui::DriWrapper* dri_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(MockScreenManager);
+};
+
+}  // namespace
+
+class DriSurfaceFactoryTest : public testing::Test {
+ public:
+  DriSurfaceFactoryTest() {}
+
+  virtual void SetUp() OVERRIDE;
+  virtual void TearDown() OVERRIDE;
+ protected:
+  scoped_ptr<base::MessageLoop> message_loop_;
+  scoped_ptr<ui::MockDriWrapper> dri_;
+  scoped_ptr<ui::DriBufferGenerator> buffer_generator_;
+  scoped_ptr<MockScreenManager> screen_manager_;
+  scoped_ptr<ui::DriSurfaceFactory> factory_;
+  scoped_ptr<ui::DriWindowDelegateManager> window_delegate_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DriSurfaceFactoryTest);
+};
+
+void DriSurfaceFactoryTest::SetUp() {
+  message_loop_.reset(new base::MessageLoopForUI);
+  dri_.reset(new ui::MockDriWrapper(3));
+  buffer_generator_.reset(new ui::DriBufferGenerator(dri_.get()));
+  screen_manager_.reset(new MockScreenManager(dri_.get(),
+                                              buffer_generator_.get()));
+  window_delegate_manager_.reset(new ui::DriWindowDelegateManager());
+  factory_.reset(new ui::DriSurfaceFactory(
+      dri_.get(), screen_manager_.get(), window_delegate_manager_.get()));
+
+  scoped_ptr<ui::DriWindowDelegate> window_delegate(
+      new ui::DriWindowDelegateImpl(ui::DriSurfaceFactory::kDefaultWidgetHandle,
+                                    screen_manager_.get()));
+  window_delegate->Initialize();
+  window_delegate_manager_->AddWindowDelegate(
+      ui::DriSurfaceFactory::kDefaultWidgetHandle, window_delegate.Pass());
+}
+
+void DriSurfaceFactoryTest::TearDown() {
+  scoped_ptr<ui::DriWindowDelegate> delegate =
+      window_delegate_manager_->RemoveWindowDelegate(
+          ui::DriSurfaceFactory::kDefaultWidgetHandle);
+  delegate->Shutdown();
+  factory_.reset();
+  message_loop_.reset();
+}
+
+TEST_F(DriSurfaceFactoryTest, FailInitialization) {
+  dri_->fail_init();
+  EXPECT_EQ(ui::DriSurfaceFactory::FAILED, factory_->InitializeHardware());
+}
+
+TEST_F(DriSurfaceFactoryTest, SuccessfulInitialization) {
+  EXPECT_EQ(ui::DriSurfaceFactory::INITIALIZED,
+            factory_->InitializeHardware());
+}
+
+TEST_F(DriSurfaceFactoryTest, SuccessfulWidgetRealization) {
+  EXPECT_EQ(ui::DriSurfaceFactory::INITIALIZED,
+            factory_->InitializeHardware());
+
+  EXPECT_TRUE(factory_->CreateCanvasForWidget(
+      ui::DriSurfaceFactory::kDefaultWidgetHandle));
+}
+
+TEST_F(DriSurfaceFactoryTest, SetCursorImage) {
+  EXPECT_EQ(ui::DriSurfaceFactory::INITIALIZED,
+            factory_->InitializeHardware());
+
+  scoped_ptr<ui::SurfaceOzoneCanvas> surf = factory_->CreateCanvasForWidget(
+      ui::DriSurfaceFactory::kDefaultWidgetHandle);
+  EXPECT_TRUE(surf);
+
+  SkBitmap image;
+  SkImageInfo info = SkImageInfo::Make(
+      6, 4, kN32_SkColorType, kPremul_SkAlphaType);
+  image.allocPixels(info);
+  image.eraseColor(SK_ColorWHITE);
+
+  std::vector<SkBitmap> cursor_bitmaps;
+  cursor_bitmaps.push_back(image);
+  factory_->SetHardwareCursor(ui::DriSurfaceFactory::kDefaultWidgetHandle,
+                              cursor_bitmaps,
+                              gfx::Point(4, 2),
+                              0);
+
+  SkBitmap cursor;
+  // Buffers 0 and 1 are the cursor buffers.
+  cursor.setInfo(dri_->buffers()[1]->getCanvas()->imageInfo());
+  EXPECT_TRUE(dri_->buffers()[1]->getCanvas()->readPixels(&cursor, 0, 0));
+
+  // Check that the frontbuffer is displaying the right image as set above.
+  for (int i = 0; i < cursor.height(); ++i) {
+    for (int j = 0; j < cursor.width(); ++j) {
+      if (j < info.width() && i < info.height())
+        EXPECT_EQ(SK_ColorWHITE, cursor.getColor(j, i));
+      else
+        EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
+                  cursor.getColor(j, i));
+    }
+  }
+}
diff --git a/ui/ozone/platform/dri/dri_surface_unittest.cc b/ui/ozone/platform/dri/dri_surface_unittest.cc
new file mode 100644
index 0000000..0d64728
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_surface_unittest.cc
@@ -0,0 +1,128 @@
+// 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/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkColor.h"
+#include "third_party/skia/include/core/SkDevice.h"
+#include "ui/ozone/platform/dri/crtc_state.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_surface.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/test/mock_dri_wrapper.h"
+
+namespace {
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kDefaultCrtc = 1;
+const uint32_t kDefaultConnector = 2;
+
+class MockDriWindowDelegate : public ui::DriWindowDelegate {
+ public:
+  MockDriWindowDelegate(ui::DriWrapper* drm) {
+    controller_.reset(new ui::HardwareDisplayController(
+        drm,
+        make_scoped_ptr(
+            new ui::CrtcState(drm, kDefaultCrtc, kDefaultConnector))));
+    scoped_refptr<ui::DriBuffer> buffer(new ui::DriBuffer(drm));
+    SkImageInfo info = SkImageInfo::MakeN32Premul(kDefaultMode.hdisplay,
+                                                  kDefaultMode.vdisplay);
+    EXPECT_TRUE(buffer->Initialize(info));
+    EXPECT_TRUE(controller_->Modeset(ui::OverlayPlane(buffer), kDefaultMode));
+  }
+  virtual ~MockDriWindowDelegate() {}
+
+  // DriWindowDelegate:
+  virtual void Initialize() OVERRIDE {}
+  virtual void Shutdown() OVERRIDE {}
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE { return 1; }
+  virtual ui::HardwareDisplayController* GetController() OVERRIDE {
+    return controller_.get();
+  }
+  virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE {}
+
+ private:
+  scoped_ptr<ui::HardwareDisplayController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDriWindowDelegate);
+};
+
+}  // namespace
+
+class DriSurfaceTest : public testing::Test {
+ public:
+  DriSurfaceTest() {}
+
+  virtual void SetUp() OVERRIDE;
+  virtual void TearDown() OVERRIDE;
+
+ protected:
+  scoped_ptr<base::MessageLoop> message_loop_;
+  scoped_ptr<ui::MockDriWrapper> drm_;
+  scoped_ptr<MockDriWindowDelegate> window_delegate_;
+  scoped_ptr<ui::DriSurface> surface_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DriSurfaceTest);
+};
+
+void DriSurfaceTest::SetUp() {
+  message_loop_.reset(new base::MessageLoopForUI);
+  drm_.reset(new ui::MockDriWrapper(3));
+  window_delegate_.reset(new MockDriWindowDelegate(drm_.get()));
+  surface_.reset(new ui::DriSurface(window_delegate_.get(), drm_.get()));
+  surface_->ResizeCanvas(gfx::Size(kDefaultMode.hdisplay,
+                                   kDefaultMode.vdisplay));
+}
+
+void DriSurfaceTest::TearDown() {
+  surface_.reset();
+  window_delegate_.reset();
+  drm_.reset();
+  message_loop_.reset();
+}
+
+TEST_F(DriSurfaceTest, CheckFBIDOnSwap) {
+  surface_->PresentCanvas(gfx::Rect());
+  // Framebuffer ID 1 is allocated in SetUp for the buffer used to modeset.
+  EXPECT_EQ(3u, drm_->current_framebuffer());
+  surface_->PresentCanvas(gfx::Rect());
+  EXPECT_EQ(2u, drm_->current_framebuffer());
+}
+
+TEST_F(DriSurfaceTest, CheckSurfaceContents) {
+  SkPaint paint;
+  paint.setColor(SK_ColorWHITE);
+  SkRect rect = SkRect::MakeWH(kDefaultMode.hdisplay / 2,
+                               kDefaultMode.vdisplay / 2);
+  surface_->GetCanvas()->drawRect(rect, paint);
+  surface_->PresentCanvas(
+      gfx::Rect(0, 0, kDefaultMode.hdisplay / 2, kDefaultMode.vdisplay / 2));
+
+  SkBitmap image;
+  // Buffer 0 is the buffer used in SetUp for modesetting and buffer 1 is the
+  // frontbuffer.
+  // Buffer 2 is the backbuffer we just painted in, so we want to make sure its
+  // contents are correct.
+  image.setInfo(drm_->buffers()[2]->getCanvas()->imageInfo());
+  EXPECT_TRUE(drm_->buffers()[2]->getCanvas()->readPixels(&image, 0, 0));
+
+  EXPECT_EQ(kDefaultMode.hdisplay, image.width());
+  EXPECT_EQ(kDefaultMode.vdisplay, image.height());
+
+  // Make sure the updates are correctly propagated to the native surface.
+  for (int i = 0; i < image.height(); ++i) {
+    for (int j = 0; j < image.width(); ++j) {
+      if (j < kDefaultMode.hdisplay / 2 && i < kDefaultMode.vdisplay / 2)
+        EXPECT_EQ(SK_ColorWHITE, image.getColor(j, i));
+      else
+        EXPECT_EQ(SK_ColorBLACK, image.getColor(j, i));
+    }
+  }
+}
diff --git a/ui/ozone/platform/dri/dri_util.cc b/ui/ozone/platform/dri/dri_util.cc
new file mode 100644
index 0000000..5305f6d
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_util.cc
@@ -0,0 +1,138 @@
+// 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 "ui/ozone/platform/dri/dri_util.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+namespace ui {
+
+namespace {
+
+bool IsCrtcInUse(uint32_t crtc,
+                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+  for (size_t i = 0; i < displays.size(); ++i) {
+    if (crtc == displays[i]->crtc()->crtc_id)
+      return true;
+  }
+
+  return false;
+}
+
+uint32_t GetCrtc(int fd,
+                 drmModeConnector* connector,
+                 drmModeRes* resources,
+                 const ScopedVector<HardwareDisplayControllerInfo>& displays) {
+  // If the connector already has an encoder try to re-use.
+  if (connector->encoder_id) {
+    ScopedDrmEncoderPtr encoder(drmModeGetEncoder(fd, connector->encoder_id));
+    if (encoder && encoder->crtc_id && !IsCrtcInUse(encoder->crtc_id, displays))
+      return encoder->crtc_id;
+  }
+
+  // Try to find an encoder for the connector.
+  for (int i = 0; i < connector->count_encoders; ++i) {
+    ScopedDrmEncoderPtr encoder(drmModeGetEncoder(fd, connector->encoders[i]));
+    if (!encoder)
+      continue;
+
+    for (int j = 0; j < resources->count_crtcs; ++j) {
+      // Check if the encoder is compatible with this CRTC
+      if (!(encoder->possible_crtcs & (1 << j)) ||
+          IsCrtcInUse(resources->crtcs[j], displays))
+        continue;
+
+      return resources->crtcs[j];
+    }
+  }
+
+  return 0;
+}
+
+}  // namespace
+
+HardwareDisplayControllerInfo::HardwareDisplayControllerInfo(
+    ScopedDrmConnectorPtr connector,
+    ScopedDrmCrtcPtr crtc)
+    : connector_(connector.Pass()),
+      crtc_(crtc.Pass()) {}
+
+HardwareDisplayControllerInfo::~HardwareDisplayControllerInfo() {}
+
+ScopedVector<HardwareDisplayControllerInfo>
+GetAvailableDisplayControllerInfos(int fd) {
+  ScopedDrmResourcesPtr resources(drmModeGetResources(fd));
+  DCHECK(resources) << "Failed to get DRM resources";
+  ScopedVector<HardwareDisplayControllerInfo> displays;
+
+  for (int i = 0; i < resources->count_connectors; ++i) {
+    ScopedDrmConnectorPtr connector(drmModeGetConnector(
+        fd, resources->connectors[i]));
+
+    if (!connector || connector->connection != DRM_MODE_CONNECTED ||
+        connector->count_modes == 0)
+      continue;
+
+    uint32_t crtc_id = GetCrtc(fd, connector.get(), resources.get(), displays);
+    if (!crtc_id)
+      continue;
+
+    ScopedDrmCrtcPtr crtc(drmModeGetCrtc(fd, crtc_id));
+    displays.push_back(new HardwareDisplayControllerInfo(connector.Pass(),
+                                                         crtc.Pass()));
+  }
+
+  return displays.Pass();
+}
+
+bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs) {
+  return lhs.clock == rhs.clock &&
+         lhs.hdisplay == rhs.hdisplay &&
+         lhs.vdisplay == rhs.vdisplay &&
+         lhs.vrefresh == rhs.vrefresh &&
+         lhs.hsync_start == rhs.hsync_start &&
+         lhs.hsync_end == rhs.hsync_end &&
+         lhs.htotal == rhs.htotal &&
+         lhs.hskew == rhs.hskew &&
+         lhs.vsync_start == rhs.vsync_start &&
+         lhs.vsync_end == rhs.vsync_end &&
+         lhs.vtotal == rhs.vtotal &&
+         lhs.vscan == rhs.vscan &&
+         lhs.flags == rhs.flags &&
+         strcmp(lhs.name, rhs.name) == 0;
+}
+
+bool MapDumbBuffer(int fd,
+                   uint32_t handle,
+                   uint32_t size,
+                   void** pixels) {
+  struct drm_mode_map_dumb map_request;
+  memset(&map_request, 0, sizeof(map_request));
+  map_request.handle = handle;
+  if (drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_request)) {
+    VLOG(2) << "Cannot prepare dumb buffer for mapping (" << errno << ") "
+            << strerror(errno);
+    return false;
+  }
+
+  *pixels = mmap(0,
+                 size,
+                 PROT_READ | PROT_WRITE,
+                 MAP_SHARED,
+                 fd,
+                 map_request.offset);
+  if (*pixels == MAP_FAILED) {
+    VLOG(2) << "Cannot mmap dumb buffer (" << errno << ") " << strerror(errno);
+    return false;
+  }
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_util.h b/ui/ozone/platform/dri/dri_util.h
new file mode 100644
index 0000000..94f2b45
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_util.h
@@ -0,0 +1,49 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_UTIL_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_UTIL_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+namespace ui {
+
+// Representation of the information required to initialize and configure a
+// native display.
+class HardwareDisplayControllerInfo {
+ public:
+  HardwareDisplayControllerInfo(ScopedDrmConnectorPtr connector,
+                                ScopedDrmCrtcPtr crtc);
+  ~HardwareDisplayControllerInfo();
+
+  drmModeConnector* connector() const { return connector_.get(); }
+  drmModeCrtc* crtc() const { return crtc_.get(); }
+
+ private:
+  ScopedDrmConnectorPtr connector_;
+  ScopedDrmCrtcPtr crtc_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerInfo);
+};
+
+// Looks-up and parses the native display configurations returning all available
+// displays.
+ScopedVector<HardwareDisplayControllerInfo>
+GetAvailableDisplayControllerInfos(int fd);
+
+bool SameMode(const drmModeModeInfo& lhs, const drmModeModeInfo& rhs);
+
+// Memory maps a DRM buffer.
+bool MapDumbBuffer(int fd,
+                   uint32_t handle,
+                   uint32_t size,
+                   void** pixels);
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_UTIL_H_
diff --git a/ui/ozone/platform/dri/dri_vsync_provider.cc b/ui/ozone/platform/dri/dri_vsync_provider.cc
new file mode 100644
index 0000000..3888d40
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_vsync_provider.cc
@@ -0,0 +1,39 @@
+// 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 "ui/ozone/platform/dri/dri_vsync_provider.h"
+
+#include "base/time/time.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+
+namespace ui {
+
+DriVSyncProvider::DriVSyncProvider(DriWindowDelegate* window_delegate)
+    : window_delegate_(window_delegate) {
+}
+
+DriVSyncProvider::~DriVSyncProvider() {}
+
+void DriVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) {
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return;
+
+  // The value is invalid, so we can't update the parameters.
+  if (controller->get_time_of_last_flip() == 0 ||
+      controller->get_mode().vrefresh == 0)
+    return;
+
+  // Stores the time of the last refresh.
+  base::TimeTicks timebase =
+      base::TimeTicks::FromInternalValue(controller->get_time_of_last_flip());
+  // Stores the refresh rate.
+  base::TimeDelta interval =
+      base::TimeDelta::FromSeconds(1) / controller->get_mode().vrefresh;
+
+  callback.Run(timebase, interval);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_vsync_provider.h b/ui/ozone/platform/dri/dri_vsync_provider.h
new file mode 100644
index 0000000..c90aa80
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_vsync_provider.h
@@ -0,0 +1,29 @@
+// 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 UI_OZONE_PLATFORM_IMPL_DRI_VSYNC_PROVIDER_H_
+#define UI_OZONE_PLATFORM_IMPL_DRI_VSYNC_PROVIDER_H_
+
+#include "ui/gfx/vsync_provider.h"
+
+namespace ui {
+
+class DriWindowDelegate;
+
+class DriVSyncProvider : public gfx::VSyncProvider {
+ public:
+  DriVSyncProvider(DriWindowDelegate* window_delegate);
+  virtual ~DriVSyncProvider();
+
+  virtual void GetVSyncParameters(const UpdateVSyncCallback& callback) OVERRIDE;
+
+ private:
+  DriWindowDelegate* window_delegate_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(DriVSyncProvider);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_IMPL_DRI_VSYNC_PROVIDER_H_
diff --git a/ui/ozone/platform/dri/dri_window.cc b/ui/ozone/platform/dri/dri_window.cc
new file mode 100644
index 0000000..7e60bd7
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window.cc
@@ -0,0 +1,111 @@
+// 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 "ui/ozone/platform/dri/dri_window.h"
+
+#include "base/bind.h"
+#include "ui/events/event.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/events_ozone.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/platform/dri/dri_cursor.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/dri_window_manager.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+DriWindow::DriWindow(PlatformWindowDelegate* delegate,
+                     const gfx::Rect& bounds,
+                     scoped_ptr<DriWindowDelegate> dri_window_delegate,
+                     EventFactoryEvdev* event_factory,
+                     DriWindowDelegateManager* window_delegate_manager,
+                     DriWindowManager* window_manager)
+    : delegate_(delegate),
+      bounds_(bounds),
+      widget_(dri_window_delegate->GetAcceleratedWidget()),
+      dri_window_delegate_(dri_window_delegate.get()),
+      event_factory_(event_factory),
+      window_delegate_manager_(window_delegate_manager),
+      window_manager_(window_manager) {
+  window_delegate_manager_->AddWindowDelegate(widget_,
+                                              dri_window_delegate.Pass());
+  window_manager_->AddWindow(widget_, this);
+}
+
+DriWindow::~DriWindow() {
+  PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+  dri_window_delegate_->Shutdown();
+  window_manager_->RemoveWindow(widget_);
+  window_delegate_manager_->RemoveWindowDelegate(widget_);
+}
+
+void DriWindow::Initialize() {
+  dri_window_delegate_->Initialize();
+  dri_window_delegate_->OnBoundsChanged(bounds_);
+  delegate_->OnAcceleratedWidgetAvailable(widget_);
+  PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+}
+
+void DriWindow::Show() {}
+
+void DriWindow::Hide() {}
+
+void DriWindow::Close() {}
+
+void DriWindow::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+  if (window_manager_->cursor()->GetCursorWindow() == widget_)
+    window_manager_->cursor()->HideCursor();
+
+  dri_window_delegate_->OnBoundsChanged(bounds);
+
+  if (window_manager_->cursor()->GetCursorWindow() == widget_)
+    window_manager_->cursor()->ShowCursor();
+}
+
+gfx::Rect DriWindow::GetBounds() {
+  return bounds_;
+}
+
+void DriWindow::SetCapture() {}
+
+void DriWindow::ReleaseCapture() {}
+
+void DriWindow::ToggleFullscreen() {}
+
+void DriWindow::Maximize() {}
+
+void DriWindow::Minimize() {}
+
+void DriWindow::Restore() {}
+
+void DriWindow::SetCursor(PlatformCursor cursor) {
+  window_manager_->cursor()->SetCursor(widget_, cursor);
+}
+
+void DriWindow::MoveCursorTo(const gfx::Point& location) {
+  event_factory_->WarpCursorTo(widget_, location);
+}
+
+bool DriWindow::CanDispatchEvent(const PlatformEvent& ne) {
+  DCHECK(ne);
+  Event* event = static_cast<Event*>(ne);
+  if (event->IsMouseEvent() || event->IsScrollEvent())
+    return window_manager_->cursor()->GetCursorWindow() == widget_;
+
+  return true;
+}
+
+uint32_t DriWindow::DispatchEvent(const PlatformEvent& native_event) {
+  DispatchEventFromNativeUiEvent(
+      native_event,
+      base::Bind(&PlatformWindowDelegate::DispatchEvent,
+                 base::Unretained(delegate_)));
+  return POST_DISPATCH_STOP_PROPAGATION;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_window.h b/ui/ozone/platform/dri/dri_window.h
new file mode 100644
index 0000000..799b1ee
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window.h
@@ -0,0 +1,67 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window.h"
+
+namespace ui {
+
+class DriWindowDelegate;
+class DriWindowDelegateManager;
+class DriWindowManager;
+class EventFactoryEvdev;
+
+class DriWindow : public PlatformWindow,
+                  public PlatformEventDispatcher {
+ public:
+  DriWindow(PlatformWindowDelegate* delegate,
+            const gfx::Rect& bounds,
+            scoped_ptr<DriWindowDelegate> dri_window_delegate,
+            EventFactoryEvdev* event_factory,
+            DriWindowDelegateManager* window_delegate_manager,
+            DriWindowManager* window_manager);
+  virtual ~DriWindow();
+
+  void Initialize();
+
+  // PlatformWindow:
+  virtual void Show() OVERRIDE;
+  virtual void Hide() OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+  virtual gfx::Rect GetBounds() OVERRIDE;
+  virtual void SetCapture() OVERRIDE;
+  virtual void ReleaseCapture() OVERRIDE;
+  virtual void ToggleFullscreen() OVERRIDE;
+  virtual void Maximize() OVERRIDE;
+  virtual void Minimize() OVERRIDE;
+  virtual void Restore() OVERRIDE;
+  virtual void SetCursor(PlatformCursor cursor) OVERRIDE;
+  virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+
+  // PlatformEventDispatcher:
+  virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE;
+  virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE;
+
+ private:
+  PlatformWindowDelegate* delegate_;
+  gfx::Rect bounds_;
+  gfx::AcceleratedWidget widget_;
+  DriWindowDelegate* dri_window_delegate_;
+  EventFactoryEvdev* event_factory_;
+  DriWindowDelegateManager* window_delegate_manager_;
+  DriWindowManager* window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWindow);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_H_
diff --git a/ui/ozone/platform/dri/dri_window_delegate.h b/ui/ozone/platform/dri/dri_window_delegate.h
new file mode 100644
index 0000000..2787e2c
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate.h
@@ -0,0 +1,52 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+class HardwareDisplayController;
+
+// Interface for the display-server half of a DriWindow.
+//
+// The main implementation of this lives in the process that owns the display
+// connection (usually the GPU process) and associates a platform window
+// (DriWindow) with a display. A window is associated with the display whose
+// bounds contains the window bounds. If there's no suitable display, the window
+// is disconnected and its contents will not be visible.
+//
+// In software mode, this is owned directly on DriWindow because the display
+// controller object is in the same process.
+//
+// In accelerated mode, there's a proxy implementation and calls are forwarded
+// to the real object in the GPU process via IPC.
+class DriWindowDelegate {
+ public:
+  virtual ~DriWindowDelegate() {}
+
+  virtual void Initialize() = 0;
+
+  virtual void Shutdown() = 0;
+
+  // Returns the accelerated widget associated with the delegate.
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() = 0;
+
+  // Returns the current controller the window is displaying on. Callers should
+  // not cache the result as the controller may change as the window is moved.
+  virtual HardwareDisplayController* GetController() = 0;
+
+  // Called when the window is resized/moved.
+  virtual void OnBoundsChanged(const gfx::Rect& bounds) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_H_
diff --git a/ui/ozone/platform/dri/dri_window_delegate_impl.cc b/ui/ozone/platform/dri/dri_window_delegate_impl.cc
new file mode 100644
index 0000000..a95a8fc
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_impl.cc
@@ -0,0 +1,46 @@
+// 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 "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+
+#include "base/debug/trace_event.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+
+namespace ui {
+
+DriWindowDelegateImpl::DriWindowDelegateImpl(gfx::AcceleratedWidget widget,
+                                             ScreenManager* screen_manager)
+    : widget_(widget), screen_manager_(screen_manager) {
+}
+
+DriWindowDelegateImpl::~DriWindowDelegateImpl() {
+}
+
+void DriWindowDelegateImpl::Initialize() {
+  TRACE_EVENT1("dri", "DriWindowDelegateImpl::Initialize", "widget", widget_);
+}
+
+void DriWindowDelegateImpl::Shutdown() {
+  TRACE_EVENT1("dri", "DriWindowDelegateImpl::Shutdown", "widget", widget_);
+}
+
+gfx::AcceleratedWidget DriWindowDelegateImpl::GetAcceleratedWidget() {
+  return widget_;
+}
+
+HardwareDisplayController* DriWindowDelegateImpl::GetController() {
+  return controller_.get();
+}
+
+void DriWindowDelegateImpl::OnBoundsChanged(const gfx::Rect& bounds) {
+  TRACE_EVENT2("dri",
+               "DriWindowDelegateImpl::OnBoundsChanged",
+               "widget",
+               widget_,
+               "bounds",
+               bounds.ToString());
+  controller_ = screen_manager_->GetDisplayController(bounds);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_window_delegate_impl.h b/ui/ozone/platform/dri/dri_window_delegate_impl.h
new file mode 100644
index 0000000..336b05d
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_impl.h
@@ -0,0 +1,46 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_IMPL_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_IMPL_H_
+
+#include "base/memory/weak_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+class HardwareDisplayController;
+class ScreenManager;
+
+class DriWindowDelegateImpl : public DriWindowDelegate {
+ public:
+  DriWindowDelegateImpl(gfx::AcceleratedWidget widget,
+                        ScreenManager* screen_manager);
+  virtual ~DriWindowDelegateImpl();
+
+  // DriWindowDelegate:
+  virtual void Initialize() OVERRIDE;
+  virtual void Shutdown() OVERRIDE;
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+  virtual HardwareDisplayController* GetController() OVERRIDE;
+  virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE;
+
+ private:
+  gfx::AcceleratedWidget widget_;
+
+  ScreenManager* screen_manager_;  // Not owned.
+
+  base::WeakPtr<HardwareDisplayController> controller_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWindowDelegateImpl);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_IMPL_H_
diff --git a/ui/ozone/platform/dri/dri_window_delegate_manager.cc b/ui/ozone/platform/dri/dri_window_delegate_manager.cc
new file mode 100644
index 0000000..ffbc4ba
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_manager.cc
@@ -0,0 +1,49 @@
+// 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 "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+
+namespace ui {
+
+DriWindowDelegateManager::DriWindowDelegateManager() {
+}
+
+DriWindowDelegateManager::~DriWindowDelegateManager() {
+  DCHECK(delegate_map_.empty());
+}
+
+void DriWindowDelegateManager::AddWindowDelegate(
+    gfx::AcceleratedWidget widget,
+    scoped_ptr<DriWindowDelegate> delegate) {
+  std::pair<WidgetToDelegateMap::iterator, bool> result =
+      delegate_map_.add(widget, delegate.Pass());
+  DCHECK(result.second) << "Delegate already added.";
+}
+
+scoped_ptr<DriWindowDelegate> DriWindowDelegateManager::RemoveWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  scoped_ptr<DriWindowDelegate> delegate = delegate_map_.take_and_erase(widget);
+  DCHECK(delegate) << "Attempting to remove non-existing delegate for "
+                   << widget;
+  return delegate.Pass();
+}
+
+DriWindowDelegate* DriWindowDelegateManager::GetWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  WidgetToDelegateMap::iterator it = delegate_map_.find(widget);
+  if (it != delegate_map_.end())
+    return it->second;
+
+  NOTREACHED() << "Attempting to get non-existing delegate for " << widget;
+  return NULL;
+}
+
+bool DriWindowDelegateManager::HasWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  return delegate_map_.find(widget) != delegate_map_.end();
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_window_delegate_manager.h b/ui/ozone/platform/dri/dri_window_delegate_manager.h
new file mode 100644
index 0000000..5f2a572
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_manager.h
@@ -0,0 +1,48 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_MANAGER_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+
+class DriWindowDelegate;
+
+class DriWindowDelegateManager {
+ public:
+  DriWindowDelegateManager();
+  ~DriWindowDelegateManager();
+
+  // Adds a delegate for |widget|. Note: |widget| should not be associated with
+  // a delegate when calling this function.
+  void AddWindowDelegate(gfx::AcceleratedWidget widget,
+                         scoped_ptr<DriWindowDelegate> surface);
+
+  // Removes the delegate for |widget|. Note: |widget| must have a delegate
+  // associated with it when calling this function.
+  scoped_ptr<DriWindowDelegate> RemoveWindowDelegate(
+      gfx::AcceleratedWidget widget);
+
+  // Returns the delegate associated with |widget|. Note: This function should
+  // be called only if a valid delegate has been associated with |widget|.
+  DriWindowDelegate* GetWindowDelegate(gfx::AcceleratedWidget widget);
+
+  // Check if |widget| has a valid delegate associated with it.
+  bool HasWindowDelegate(gfx::AcceleratedWidget widget);
+
+ private:
+  typedef base::ScopedPtrHashMap<gfx::AcceleratedWidget, DriWindowDelegate>
+      WidgetToDelegateMap;
+
+  WidgetToDelegateMap delegate_map_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWindowDelegateManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_MANAGER_H_
diff --git a/ui/ozone/platform/dri/dri_window_delegate_proxy.cc b/ui/ozone/platform/dri/dri_window_delegate_proxy.cc
new file mode 100644
index 0000000..8d875f4
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_proxy.cc
@@ -0,0 +1,74 @@
+// 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 "ui/ozone/platform/dri/dri_window_delegate_proxy.h"
+
+#include "ui/ozone/common/gpu/ozone_gpu_messages.h"
+#include "ui/ozone/platform/dri/gpu_platform_support_host_gbm.h"
+
+namespace ui {
+
+DriWindowDelegateProxy::DriWindowDelegateProxy(
+    gfx::AcceleratedWidget widget,
+    GpuPlatformSupportHostGbm* sender)
+    : widget_(widget), sender_(sender) {
+}
+
+DriWindowDelegateProxy::~DriWindowDelegateProxy() {
+}
+
+void DriWindowDelegateProxy::Initialize() {
+  TRACE_EVENT1("dri", "DriWindowDelegateProxy::Initialize", "widget", widget_);
+  sender_->AddChannelObserver(this);
+}
+
+void DriWindowDelegateProxy::Shutdown() {
+  TRACE_EVENT1("dri", "DriWindowDelegateProxy::Shutdown", "widget", widget_);
+  sender_->RemoveChannelObserver(this);
+  if (!sender_->IsConnected())
+    return;
+
+  bool status = sender_->Send(new OzoneGpuMsg_DestroyWindowDelegate(widget_));
+  DCHECK(status);
+}
+
+gfx::AcceleratedWidget DriWindowDelegateProxy::GetAcceleratedWidget() {
+  return widget_;
+}
+
+HardwareDisplayController* DriWindowDelegateProxy::GetController() {
+  NOTREACHED();
+  return NULL;
+}
+
+void DriWindowDelegateProxy::OnBoundsChanged(const gfx::Rect& bounds) {
+  TRACE_EVENT2("dri",
+               "DriWindowDelegateProxy::OnBoundsChanged",
+               "widget",
+               widget_,
+               "bounds",
+               bounds.ToString());
+  bounds_ = bounds;
+  if (!sender_->IsConnected())
+    return;
+
+  bool status =
+      sender_->Send(new OzoneGpuMsg_WindowBoundsChanged(widget_, bounds));
+  DCHECK(status);
+}
+
+void DriWindowDelegateProxy::OnChannelEstablished() {
+  TRACE_EVENT1(
+      "dri", "DriWindowDelegateProxy::OnChannelEstablished", "widget", widget_);
+  bool status = sender_->Send(new OzoneGpuMsg_CreateWindowDelegate(widget_));
+  DCHECK(status);
+  OnBoundsChanged(bounds_);
+}
+
+void DriWindowDelegateProxy::OnChannelDestroyed() {
+  TRACE_EVENT1(
+      "dri", "DriWindowDelegateProxy::OnChannelDestroyed", "widget", widget_);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_window_delegate_proxy.h b/ui/ozone/platform/dri/dri_window_delegate_proxy.h
new file mode 100644
index 0000000..ef95c4c
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_delegate_proxy.h
@@ -0,0 +1,49 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_PROXY_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_PROXY_H_
+
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/dri/channel_observer.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+
+namespace ui {
+
+class GpuPlatformSupportHostGbm;
+
+// This is used when running with a GPU process (or with the in-process GPU) to
+// IPC the native window configuration from the browser to the GPU.
+class DriWindowDelegateProxy : public DriWindowDelegate,
+                               public ChannelObserver {
+ public:
+  DriWindowDelegateProxy(gfx::AcceleratedWidget widget,
+                         GpuPlatformSupportHostGbm* sender);
+  virtual ~DriWindowDelegateProxy();
+
+  // DriWindowDelegate:
+  virtual void Initialize() OVERRIDE;
+  virtual void Shutdown() OVERRIDE;
+  virtual gfx::AcceleratedWidget GetAcceleratedWidget() OVERRIDE;
+  virtual HardwareDisplayController* GetController() OVERRIDE;
+  virtual void OnBoundsChanged(const gfx::Rect& bounds) OVERRIDE;
+
+  // ChannelObserver:
+  virtual void OnChannelEstablished() OVERRIDE;
+  virtual void OnChannelDestroyed() OVERRIDE;
+
+ private:
+  gfx::AcceleratedWidget widget_;
+  GpuPlatformSupportHostGbm* sender_;  // Not owned.
+
+  // Cached state for the window. If the GPU process crashes, this state is used
+  // to update the GPU side when it comes back.
+  gfx::Rect bounds_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWindowDelegateProxy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_DELEGATE_PROXY_H_
diff --git a/ui/ozone/platform/dri/dri_window_manager.cc b/ui/ozone/platform/dri/dri_window_manager.cc
new file mode 100644
index 0000000..289d930
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_manager.cc
@@ -0,0 +1,77 @@
+// 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 "ui/ozone/platform/dri/dri_window_manager.h"
+
+#include "base/logging.h"
+#include "ui/ozone/platform/dri/dri_cursor.h"
+#include "ui/ozone/platform/dri/dri_window.h"
+
+namespace ui {
+
+namespace {
+
+gfx::PointF GetDefaultCursorLocation(DriWindow* window) {
+  return gfx::PointF(window->GetBounds().width() / 2,
+                     window->GetBounds().height() / 2);
+}
+
+}  // namespace
+
+DriWindowManager::DriWindowManager(HardwareCursorDelegate* cursor_delegate)
+    : last_allocated_widget_(0), cursor_(new DriCursor(cursor_delegate, this)) {
+}
+
+DriWindowManager::~DriWindowManager() {
+}
+
+gfx::AcceleratedWidget DriWindowManager::NextAcceleratedWidget() {
+  // We're not using 0 since other code assumes that a 0 AcceleratedWidget is an
+  // invalid widget.
+  return ++last_allocated_widget_;
+}
+
+void DriWindowManager::AddWindow(gfx::AcceleratedWidget widget,
+                                 DriWindow* window) {
+  std::pair<WidgetToWindowMap::iterator, bool> result = window_map_.insert(
+      std::pair<gfx::AcceleratedWidget, DriWindow*>(widget, window));
+  DCHECK(result.second) << "Window for " << widget << " already added.";
+
+  if (cursor_->GetCursorWindow() == gfx::kNullAcceleratedWidget)
+    ResetCursorLocation();
+}
+
+void DriWindowManager::RemoveWindow(gfx::AcceleratedWidget widget) {
+  WidgetToWindowMap::iterator it = window_map_.find(widget);
+  if (it != window_map_.end())
+    window_map_.erase(it);
+  else
+    NOTREACHED() << "Attempting to remove non-existing window " << widget;
+
+  if (cursor_->GetCursorWindow() == widget)
+    ResetCursorLocation();
+}
+
+DriWindow* DriWindowManager::GetWindow(gfx::AcceleratedWidget widget) {
+  WidgetToWindowMap::iterator it = window_map_.find(widget);
+  if (it != window_map_.end())
+    return it->second;
+
+  NOTREACHED() << "Attempting to get non-existing window " << widget;
+  return NULL;
+}
+
+void DriWindowManager::ResetCursorLocation() {
+  gfx::AcceleratedWidget cursor_widget = gfx::kNullAcceleratedWidget;
+  gfx::PointF location;
+  if (!window_map_.empty()) {
+    WidgetToWindowMap::iterator it = window_map_.begin();
+    cursor_widget = it->first;
+    location = GetDefaultCursorLocation(it->second);
+  }
+
+  cursor_->MoveCursorTo(cursor_widget, location);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_window_manager.h b/ui/ozone/platform/dri/dri_window_manager.h
new file mode 100644
index 0000000..5a03957
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_window_manager.h
@@ -0,0 +1,58 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WINDOW_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WINDOW_MANAGER_H_
+
+#include <map>
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+
+namespace ui {
+
+class DriCursor;
+class DriWindow;
+class HardwareCursorDelegate;
+
+// Responsible for keeping the mapping between the allocated widgets and
+// windows.
+class DriWindowManager {
+ public:
+  explicit DriWindowManager(HardwareCursorDelegate* cursor_delegate);
+  ~DriWindowManager();
+
+  gfx::AcceleratedWidget NextAcceleratedWidget();
+
+  // Adds a window for |widget|. Note: |widget| should not be associated when
+  // calling this function.
+  void AddWindow(gfx::AcceleratedWidget widget, DriWindow* window);
+
+  // Removes the window association for |widget|. Note: |widget| must be
+  // associated with a window when calling this function.
+  void RemoveWindow(gfx::AcceleratedWidget widget);
+
+  // Returns the window associated with |widget|. Note: This function should
+  // only be called if a valid window has been associated.
+  DriWindow* GetWindow(gfx::AcceleratedWidget widget);
+
+  DriCursor* cursor() const { return cursor_.get(); }
+
+ private:
+  // Reset the cursor location based on the list of active windows.
+  void ResetCursorLocation();
+
+  typedef std::map<gfx::AcceleratedWidget, DriWindow*> WidgetToWindowMap;
+
+  gfx::AcceleratedWidget last_allocated_widget_;
+  WidgetToWindowMap window_map_;
+
+  scoped_ptr<DriCursor> cursor_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWindowManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WINDOW_MANAGER_H_
diff --git a/ui/ozone/platform/dri/dri_wrapper.cc b/ui/ozone/platform/dri/dri_wrapper.cc
new file mode 100644
index 0000000..7e80c3a
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_wrapper.cc
@@ -0,0 +1,304 @@
+// 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 "ui/ozone/platform/dri/dri_wrapper.h"
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "third_party/skia/include/core/SkImageInfo.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+
+namespace ui {
+
+namespace {
+
+uint32_t ToFixedPoint(double v) {
+  // This returns a number in a 16-bit.16-bit fixed point.
+  return v * 65536.0;
+}
+
+bool DrmCreateDumbBuffer(int fd,
+                         const SkImageInfo& info,
+                         uint32_t* handle,
+                         uint32_t* stride) {
+  struct drm_mode_create_dumb request;
+  memset(&request, 0, sizeof(request));
+  request.width = info.width();
+  request.height = info.height();
+  request.bpp = info.bytesPerPixel() << 3;
+  request.flags = 0;
+
+  if (drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &request) < 0) {
+    VLOG(2) << "Cannot create dumb buffer (" << errno << ") "
+            << strerror(errno);
+    return false;
+  }
+
+  // The driver may choose to align the last row as well. We don't care about
+  // the last alignment bits since they aren't used for display purposes, so
+  // just check that the expected size is <= to what the driver allocated.
+  DCHECK_LE(info.getSafeSize(request.pitch), request.size);
+
+  *handle = request.handle;
+  *stride = request.pitch;
+  return true;
+}
+
+void DrmDestroyDumbBuffer(int fd, uint32_t handle) {
+  struct drm_mode_destroy_dumb destroy_request;
+  memset(&destroy_request, 0, sizeof(destroy_request));
+  destroy_request.handle = handle;
+  drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_request);
+}
+
+}  // namespace
+
+DriWrapper::DriWrapper(const char* device_path)
+    : fd_(-1), device_path_(device_path) {
+}
+
+DriWrapper::~DriWrapper() {
+  if (fd_ >= 0)
+    close(fd_);
+}
+
+void DriWrapper::Initialize() {
+  fd_ = open(device_path_, O_RDWR | O_CLOEXEC);
+  if (fd_ < 0)
+    PLOG(FATAL) << "open: " << device_path_;
+}
+
+ScopedDrmCrtcPtr DriWrapper::GetCrtc(uint32_t crtc_id) {
+  DCHECK(fd_ >= 0);
+  return ScopedDrmCrtcPtr(drmModeGetCrtc(fd_, crtc_id));
+}
+
+bool DriWrapper::SetCrtc(uint32_t crtc_id,
+                         uint32_t framebuffer,
+                         std::vector<uint32_t> connectors,
+                         drmModeModeInfo* mode) {
+  DCHECK(fd_ >= 0);
+  DCHECK(!connectors.empty());
+  DCHECK(mode);
+
+  TRACE_EVENT2("dri",
+               "DriWrapper::SetCrtc",
+               "crtc",
+               crtc_id,
+               "size",
+               gfx::Size(mode->hdisplay, mode->vdisplay).ToString());
+  return !drmModeSetCrtc(fd_,
+                         crtc_id,
+                         framebuffer,
+                         0,
+                         0,
+                         vector_as_array(&connectors),
+                         connectors.size(), mode);
+}
+
+bool DriWrapper::SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors) {
+  DCHECK(fd_ >= 0);
+  // If there's no buffer then the CRTC was disabled.
+  if (!crtc->buffer_id)
+    return DisableCrtc(crtc->crtc_id);
+
+  DCHECK(!connectors.empty());
+
+  TRACE_EVENT1("dri", "DriWrapper::RestoreCrtc",
+               "crtc", crtc->crtc_id);
+  return !drmModeSetCrtc(fd_,
+                         crtc->crtc_id,
+                         crtc->buffer_id,
+                         crtc->x,
+                         crtc->y,
+                         vector_as_array(&connectors),
+                         connectors.size(),
+                         &crtc->mode);
+}
+
+bool DriWrapper::DisableCrtc(uint32_t crtc_id) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::DisableCrtc",
+               "crtc", crtc_id);
+  return !drmModeSetCrtc(fd_, crtc_id, 0, 0, 0, NULL, 0, NULL);
+}
+
+ScopedDrmConnectorPtr DriWrapper::GetConnector(uint32_t connector_id) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::GetConnector", "connector", connector_id);
+  return ScopedDrmConnectorPtr(drmModeGetConnector(fd_, connector_id));
+}
+
+bool DriWrapper::AddFramebuffer(uint32_t width,
+                                uint32_t height,
+                                uint8_t depth,
+                                uint8_t bpp,
+                                uint32_t stride,
+                                uint32_t handle,
+                                uint32_t* framebuffer) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::AddFramebuffer",
+               "handle", handle);
+  return !drmModeAddFB(fd_,
+                       width,
+                       height,
+                       depth,
+                       bpp,
+                       stride,
+                       handle,
+                       framebuffer);
+}
+
+bool DriWrapper::RemoveFramebuffer(uint32_t framebuffer) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::RemoveFramebuffer",
+               "framebuffer", framebuffer);
+  return !drmModeRmFB(fd_, framebuffer);
+}
+
+bool DriWrapper::PageFlip(uint32_t crtc_id,
+                          uint32_t framebuffer,
+                          void* data) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT2("dri", "DriWrapper::PageFlip",
+               "crtc", crtc_id,
+               "framebuffer", framebuffer);
+  return !drmModePageFlip(fd_,
+                          crtc_id,
+                          framebuffer,
+                          DRM_MODE_PAGE_FLIP_EVENT,
+                          data);
+}
+
+bool DriWrapper::PageFlipOverlay(uint32_t crtc_id,
+                                 uint32_t framebuffer,
+                                 const gfx::Rect& location,
+                                 const gfx::RectF& source,
+                                 int overlay_plane) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT2("dri", "DriWrapper::PageFlipOverlay",
+               "crtc", crtc_id,
+               "framebuffer", framebuffer);
+  return !drmModeSetPlane(fd_,
+                          overlay_plane,
+                          crtc_id,
+                          framebuffer,
+                          0,
+                          location.x(),
+                          location.y(),
+                          location.width(),
+                          location.height(),
+                          ToFixedPoint(source.x()),
+                          ToFixedPoint(source.y()),
+                          ToFixedPoint(source.width()),
+                          ToFixedPoint(source.height()));
+}
+
+ScopedDrmFramebufferPtr DriWrapper::GetFramebuffer(uint32_t framebuffer) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::GetFramebuffer",
+               "framebuffer", framebuffer);
+  return ScopedDrmFramebufferPtr(drmModeGetFB(fd_, framebuffer));
+}
+
+ScopedDrmPropertyPtr DriWrapper::GetProperty(drmModeConnector* connector,
+                                             const char* name) {
+  TRACE_EVENT2("dri", "DriWrapper::GetProperty",
+               "connector", connector->connector_id,
+               "name", name);
+  for (int i = 0; i < connector->count_props; ++i) {
+    ScopedDrmPropertyPtr property(drmModeGetProperty(fd_, connector->props[i]));
+    if (!property)
+      continue;
+
+    if (strcmp(property->name, name) == 0)
+      return property.Pass();
+  }
+
+  return ScopedDrmPropertyPtr();
+}
+
+bool DriWrapper::SetProperty(uint32_t connector_id,
+                             uint32_t property_id,
+                             uint64_t value) {
+  DCHECK(fd_ >= 0);
+  return !drmModeConnectorSetProperty(fd_, connector_id, property_id, value);
+}
+
+ScopedDrmPropertyBlobPtr DriWrapper::GetPropertyBlob(
+    drmModeConnector* connector, const char* name) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT2("dri", "DriWrapper::GetPropertyBlob",
+               "connector", connector->connector_id,
+               "name", name);
+  for (int i = 0; i < connector->count_props; ++i) {
+    ScopedDrmPropertyPtr property(drmModeGetProperty(fd_, connector->props[i]));
+    if (!property)
+      continue;
+
+    if (strcmp(property->name, name) == 0 &&
+        property->flags & DRM_MODE_PROP_BLOB)
+      return ScopedDrmPropertyBlobPtr(
+          drmModeGetPropertyBlob(fd_, connector->prop_values[i]));
+  }
+
+  return ScopedDrmPropertyBlobPtr();
+}
+
+bool DriWrapper::SetCursor(uint32_t crtc_id,
+                           uint32_t handle,
+                           const gfx::Size& size) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::SetCursor", "handle", handle);
+  return !drmModeSetCursor(fd_, crtc_id, handle, size.width(), size.height());
+}
+
+bool DriWrapper::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
+  DCHECK(fd_ >= 0);
+  return !drmModeMoveCursor(fd_, crtc_id, point.x(), point.y());
+}
+
+void DriWrapper::HandleEvent(drmEventContext& event) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT0("dri", "DriWrapper::HandleEvent");
+  drmHandleEvent(fd_, &event);
+}
+
+bool DriWrapper::CreateDumbBuffer(const SkImageInfo& info,
+                                  uint32_t* handle,
+                                  uint32_t* stride,
+                                  void** pixels) {
+  DCHECK(fd_ >= 0);
+
+  TRACE_EVENT0("dri", "DriWrapper::CreateDumbBuffer");
+  if (!DrmCreateDumbBuffer(fd_, info, handle, stride))
+    return false;
+
+  if (!MapDumbBuffer(fd_, *handle, info.getSafeSize(*stride), pixels)) {
+    DrmDestroyDumbBuffer(fd_, *handle);
+    return false;
+  }
+
+  return true;
+}
+
+void DriWrapper::DestroyDumbBuffer(const SkImageInfo& info,
+                                   uint32_t handle,
+                                   uint32_t stride,
+                                   void* pixels) {
+  DCHECK(fd_ >= 0);
+  TRACE_EVENT1("dri", "DriWrapper::DestroyDumbBuffer", "handle", handle);
+  munmap(pixels, info.getSafeSize(stride));
+  DrmDestroyDumbBuffer(fd_, handle);
+}
+
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/dri_wrapper.h b/ui/ozone/platform/dri/dri_wrapper.h
new file mode 100644
index 0000000..a555814
--- /dev/null
+++ b/ui/ozone/platform/dri/dri_wrapper.h
@@ -0,0 +1,149 @@
+// 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 UI_OZONE_PLATFORM_DRI_DRI_WRAPPER_H_
+#define UI_OZONE_PLATFORM_DRI_DRI_WRAPPER_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "base/macros.h"
+#include "ui/gfx/overlay_transform.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/rect_f.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+typedef struct _drmEventContext drmEventContext;
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+struct SkImageInfo;
+
+namespace ui {
+
+// Wraps DRM calls into a nice interface. Used to provide different
+// implementations of the DRM calls. For the actual implementation the DRM API
+// would be called. In unit tests this interface would be stubbed.
+class DriWrapper {
+ public:
+  DriWrapper(const char* device_path);
+  virtual ~DriWrapper();
+
+  // Open device.
+  virtual void Initialize();
+
+  // Get the CRTC state. This is generally used to save state before using the
+  // CRTC. When the user finishes using the CRTC, the user should restore the
+  // CRTC to it's initial state. Use |SetCrtc| to restore the state.
+  virtual ScopedDrmCrtcPtr GetCrtc(uint32_t crtc_id);
+
+  // Used to configure CRTC with ID |crtc_id| to use the connector in
+  // |connectors|. The CRTC will be configured with mode |mode| and will display
+  // the framebuffer with ID |framebuffer|. Before being able to display the
+  // framebuffer, it should be registered with the CRTC using |AddFramebuffer|.
+  virtual bool SetCrtc(uint32_t crtc_id,
+                       uint32_t framebuffer,
+                       std::vector<uint32_t> connectors,
+                       drmModeModeInfo* mode);
+
+  // Used to set a specific configuration to the CRTC. Normally this function
+  // would be called with a CRTC saved state (from |GetCrtc|) to restore it to
+  // its original configuration.
+  virtual bool SetCrtc(drmModeCrtc* crtc, std::vector<uint32_t> connectors);
+
+  virtual bool DisableCrtc(uint32_t crtc_id);
+
+  // Returns the connector properties for |connector_id|.
+  virtual ScopedDrmConnectorPtr GetConnector(uint32_t connector_id);
+
+  // Register a buffer with the CRTC. On successful registration, the CRTC will
+  // assign a framebuffer ID to |framebuffer|.
+  virtual bool AddFramebuffer(uint32_t width,
+                              uint32_t height,
+                              uint8_t depth,
+                              uint8_t bpp,
+                              uint32_t stride,
+                              uint32_t handle,
+                              uint32_t* framebuffer);
+
+  // Deregister the given |framebuffer|.
+  virtual bool RemoveFramebuffer(uint32_t framebuffer);
+
+  // Get the DRM details associated with |framebuffer|.
+  virtual ScopedDrmFramebufferPtr GetFramebuffer(uint32_t framebuffer);
+
+  // Schedules a pageflip for CRTC |crtc_id|. This function will return
+  // immediately. Upon completion of the pageflip event, the CRTC will be
+  // displaying the buffer with ID |framebuffer| and will have a DRM event
+  // queued on |fd_|. |data| is a generic pointer to some information the user
+  // will receive when processing the pageflip event.
+  virtual bool PageFlip(uint32_t crtc_id, uint32_t framebuffer, void* data);
+
+  // Schedule an overlay to be show during the page flip for CRTC |crtc_id|.
+  // |source| location from |framebuffer| will be shown on overlay
+  // |overlay_plane|, in the bounds specified by |location| on the screen.
+  virtual bool PageFlipOverlay(uint32_t crtc_id,
+                               uint32_t framebuffer,
+                               const gfx::Rect& location,
+                               const gfx::RectF& source,
+                               int overlay_plane);
+
+  // Returns the property with name |name| associated with |connector|. Returns
+  // NULL if property not found. If the returned value is valid, it must be
+  // released using FreeProperty().
+  virtual ScopedDrmPropertyPtr GetProperty(drmModeConnector* connector,
+                                           const char* name);
+
+  // Sets the value of property with ID |property_id| to |value|. The property
+  // is applied to the connector with ID |connector_id|.
+  virtual bool SetProperty(uint32_t connector_id,
+                           uint32_t property_id,
+                           uint64_t value);
+
+  // Return a binary blob associated with |connector|. The binary blob is
+  // associated with the property with name |name|. Return NULL if the property
+  // could not be found or if the property does not have a binary blob. If valid
+  // the returned object must be freed using FreePropertyBlob().
+  virtual ScopedDrmPropertyBlobPtr GetPropertyBlob(drmModeConnector* connector,
+                                                   const char* name);
+
+  // Set the cursor to be displayed in CRTC |crtc_id|. (width, height) is the
+  // cursor size pointed by |handle|.
+  virtual bool SetCursor(uint32_t crtc_id,
+                         uint32_t handle,
+                         const gfx::Size& size);
+
+
+  // Move the cursor on CRTC |crtc_id| to (x, y);
+  virtual bool MoveCursor(uint32_t crtc_id, const gfx::Point& point);
+
+  virtual void HandleEvent(drmEventContext& event);
+
+  virtual bool CreateDumbBuffer(const SkImageInfo& info,
+                                uint32_t* handle,
+                                uint32_t* stride,
+                                void** pixels);
+
+  virtual void DestroyDumbBuffer(const SkImageInfo& info,
+                                 uint32_t handle,
+                                 uint32_t stride,
+                                 void* pixels);
+
+  int get_fd() const { return fd_; }
+
+ protected:
+  // The file descriptor associated with this wrapper. All DRM operations will
+  // be performed using this FD.
+  int fd_;
+
+ private:
+  // Path to DRM device.
+  const char* device_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(DriWrapper);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_DRI_WRAPPER_H_
diff --git a/ui/ozone/platform/dri/gbm.gypi b/ui/ozone/platform/dri/gbm.gypi
new file mode 100644
index 0000000..b5af8bd
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm.gypi
@@ -0,0 +1,57 @@
+# 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.
+
+{
+  'variables': {
+    'internal_ozone_platform_deps': [
+      'ozone_platform_gbm',
+    ],
+    'internal_ozone_platforms': [
+      'gbm',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ozone_platform_gbm',
+      'type': 'static_library',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../build/linux/system.gyp:libdrm',
+        '../../build/linux/system.gyp:gbm',
+        '../../skia/skia.gyp:skia',
+        '../../third_party/khronos/khronos.gyp:khronos_headers',
+        '../base/ui_base.gyp:ui_base',
+        '../events/events.gyp:events',
+        '../events/ozone/events_ozone.gyp:events_ozone',
+        '../gfx/gfx.gyp:gfx',
+      ],
+      'defines': [
+        'OZONE_IMPLEMENTATION',
+      ],
+      'sources': [
+        'channel_observer.h',
+        'dri_window_delegate_proxy.cc',
+        'dri_window_delegate_proxy.h',
+        'gbm_buffer.cc',
+        'gbm_buffer.h',
+        'gbm_buffer_base.cc',
+        'gbm_buffer_base.h',
+        'gbm_surface.cc',
+        'gbm_surface.h',
+        'gbm_surfaceless.cc',
+        'gbm_surfaceless.h',
+        'gbm_surface_factory.cc',
+        'gbm_surface_factory.h',
+        'gpu_platform_support_gbm.cc',
+        'gpu_platform_support_gbm.h',
+        'gpu_platform_support_host_gbm.cc',
+        'gpu_platform_support_host_gbm.h',
+        'native_display_delegate_proxy.cc',
+        'native_display_delegate_proxy.h',
+        'ozone_platform_gbm.cc',
+        'ozone_platform_gbm.h',
+      ],
+    },
+  ],
+}
diff --git a/ui/ozone/platform/dri/gbm_buffer.cc b/ui/ozone/platform/dri/gbm_buffer.cc
new file mode 100644
index 0000000..398064d
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_buffer.cc
@@ -0,0 +1,78 @@
+// 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 "ui/ozone/platform/dri/gbm_buffer.h"
+
+#include <gbm.h>
+
+#include "base/logging.h"
+
+namespace ui {
+
+namespace {
+
+int GetGbmFormatFromBufferFormat(SurfaceFactoryOzone::BufferFormat fmt) {
+  switch (fmt) {
+    case SurfaceFactoryOzone::RGBA_8888:
+      return GBM_BO_FORMAT_ARGB8888;
+    case SurfaceFactoryOzone::RGBX_8888:
+      return GBM_BO_FORMAT_XRGB8888;
+    default:
+      NOTREACHED();
+      return 0;
+  }
+}
+
+}  // namespace
+
+GbmBuffer::GbmBuffer(DriWrapper* dri, gbm_bo* bo, bool scanout)
+    : GbmBufferBase(dri, bo, scanout) {
+}
+
+GbmBuffer::~GbmBuffer() {
+  if (bo())
+    gbm_bo_destroy(bo());
+}
+
+// static
+scoped_refptr<GbmBuffer> GbmBuffer::CreateBuffer(
+    DriWrapper* dri,
+    gbm_device* device,
+    SurfaceFactoryOzone::BufferFormat format,
+    const gfx::Size& size,
+    bool scanout) {
+  unsigned flags = GBM_BO_USE_RENDERING;
+  if (scanout)
+    flags |= GBM_BO_USE_SCANOUT;
+  gbm_bo* bo = gbm_bo_create(device,
+                             size.width(),
+                             size.height(),
+                             GetGbmFormatFromBufferFormat(format),
+                             flags);
+  if (!bo)
+    return NULL;
+
+  scoped_refptr<GbmBuffer> buffer(new GbmBuffer(dri, bo, scanout));
+  if (scanout && !buffer->GetFramebufferId())
+    return NULL;
+
+  return buffer;
+}
+
+GbmPixmap::GbmPixmap(scoped_refptr<GbmBuffer> buffer) : buffer_(buffer) {
+}
+
+GbmPixmap::~GbmPixmap() {
+}
+
+void* GbmPixmap::GetEGLClientBuffer() {
+  return buffer_->bo();
+}
+
+int GbmPixmap::GetDmaBufFd() {
+  NOTIMPLEMENTED();
+  return -1;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_buffer.h b/ui/ozone/platform/dri/gbm_buffer.h
new file mode 100644
index 0000000..b455db3
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_buffer.h
@@ -0,0 +1,58 @@
+// 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 UI_OZONE_PLATFORM_DRI_GBM_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRI_GBM_BUFFER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/dri/gbm_buffer_base.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+struct gbm_bo;
+struct gbm_device;
+
+namespace ui {
+
+class DriWrapper;
+
+class GbmBuffer : public GbmBufferBase {
+ public:
+  static scoped_refptr<GbmBuffer> CreateBuffer(
+      DriWrapper* dri,
+      gbm_device* device,
+      SurfaceFactoryOzone::BufferFormat format,
+      const gfx::Size& size,
+      bool scanout);
+
+ private:
+  GbmBuffer(DriWrapper* dri, gbm_bo* bo, bool scanout);
+  virtual ~GbmBuffer();
+
+  DISALLOW_COPY_AND_ASSIGN(GbmBuffer);
+};
+
+class GbmPixmap : public NativePixmap {
+ public:
+  GbmPixmap(scoped_refptr<GbmBuffer> buffer);
+
+  // NativePixmap:
+  virtual void* GetEGLClientBuffer() OVERRIDE;
+  virtual int GetDmaBufFd() OVERRIDE;
+
+  scoped_refptr<GbmBuffer> buffer() { return buffer_; }
+
+ private:
+  virtual ~GbmPixmap();
+
+  scoped_refptr<GbmBuffer> buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmPixmap);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GBM_BUFFER_H_
diff --git a/ui/ozone/platform/dri/gbm_buffer_base.cc b/ui/ozone/platform/dri/gbm_buffer_base.cc
new file mode 100644
index 0000000..f5c057a
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_buffer_base.cc
@@ -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.
+
+#include "ui/ozone/platform/dri/gbm_buffer_base.h"
+
+#include <gbm.h>
+
+#include "base/logging.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+namespace {
+
+// Pixel configuration for the current buffer format.
+// TODO(dnicoara) These will need to change once we query the hardware for
+// supported configurations.
+const uint8_t kColorDepth = 24;
+const uint8_t kPixelDepth = 32;
+
+}  // namespace
+
+GbmBufferBase::GbmBufferBase(DriWrapper* dri, gbm_bo* bo, bool scanout)
+    : dri_(dri), bo_(bo), framebuffer_(0) {
+  if (scanout && !dri_->AddFramebuffer(gbm_bo_get_width(bo),
+                                       gbm_bo_get_height(bo),
+                                       kColorDepth,
+                                       kPixelDepth,
+                                       gbm_bo_get_stride(bo),
+                                       gbm_bo_get_handle(bo).u32,
+                                       &framebuffer_))
+    LOG(ERROR) << "Failed to register buffer";
+}
+
+GbmBufferBase::~GbmBufferBase() {
+  if (framebuffer_)
+    dri_->RemoveFramebuffer(framebuffer_);
+}
+
+uint32_t GbmBufferBase::GetFramebufferId() const {
+  return framebuffer_;
+}
+
+uint32_t GbmBufferBase::GetHandle() const {
+  return gbm_bo_get_handle(bo_).u32;
+}
+
+gfx::Size GbmBufferBase::GetSize() const {
+  return gfx::Size(gbm_bo_get_width(bo_), gbm_bo_get_height(bo_));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_buffer_base.h b/ui/ozone/platform/dri/gbm_buffer_base.h
new file mode 100644
index 0000000..b536694
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_buffer_base.h
@@ -0,0 +1,43 @@
+// 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 UI_OZONE_PLATFORM_DRI_GBM_BUFFER_BASE_H_
+#define UI_OZONE_PLATFORM_DRI_GBM_BUFFER_BASE_H_
+
+#include "ui/ozone/platform/dri/scanout_buffer.h"
+
+struct gbm_bo;
+
+namespace ui {
+
+class DriWrapper;
+
+// Wrapper for a GBM buffer. The base class provides common functionality
+// required to prepare the buffer for scanout. It does not provide any ownership
+// of the buffer. Implementations of this base class should deal with buffer
+// ownership.
+class GbmBufferBase : public ScanoutBuffer {
+ public:
+  gbm_bo* bo() const { return bo_; }
+
+  // ScanoutBuffer:
+  virtual uint32_t GetFramebufferId() const OVERRIDE;
+  virtual uint32_t GetHandle() const OVERRIDE;
+  virtual gfx::Size GetSize() const OVERRIDE;
+
+ protected:
+  GbmBufferBase(DriWrapper* dri, gbm_bo* bo, bool scanout);
+  virtual ~GbmBufferBase();
+
+ private:
+  DriWrapper* dri_;
+  gbm_bo* bo_;
+  uint32_t framebuffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmBufferBase);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GBM_BUFFER_BASE_H_
diff --git a/ui/ozone/platform/dri/gbm_surface.cc b/ui/ozone/platform/dri/gbm_surface.cc
new file mode 100644
index 0000000..18d00e6
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surface.cc
@@ -0,0 +1,161 @@
+// 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 "ui/ozone/platform/dri/gbm_surface.h"
+
+#include <gbm.h>
+
+#include "base/logging.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/gbm_buffer_base.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/scanout_buffer.h"
+
+namespace ui {
+
+namespace {
+
+class GbmSurfaceBuffer : public GbmBufferBase {
+ public:
+  static scoped_refptr<GbmSurfaceBuffer> CreateBuffer(DriWrapper* dri,
+                                                      gbm_bo* buffer);
+  static scoped_refptr<GbmSurfaceBuffer> GetBuffer(gbm_bo* buffer);
+
+ private:
+  GbmSurfaceBuffer(DriWrapper* dri, gbm_bo* bo);
+  virtual ~GbmSurfaceBuffer();
+
+  static void Destroy(gbm_bo* buffer, void* data);
+
+  // This buffer is special and is released by GBM at any point in time (as
+  // long as it isn't being used). Since GBM should be the only one to
+  // release this buffer, keep a self-reference in order to keep this alive.
+  // When GBM calls Destroy(..) the self-reference will dissapear and this will
+  // be destroyed.
+  scoped_refptr<GbmSurfaceBuffer> self_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceBuffer);
+};
+
+GbmSurfaceBuffer::GbmSurfaceBuffer(DriWrapper* dri, gbm_bo* bo)
+  : GbmBufferBase(dri, bo, true) {
+  if (GetFramebufferId()) {
+    self_ = this;
+    gbm_bo_set_user_data(bo, this, GbmSurfaceBuffer::Destroy);
+  }
+}
+
+GbmSurfaceBuffer::~GbmSurfaceBuffer() {}
+
+// static
+scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::CreateBuffer(
+    DriWrapper* dri, gbm_bo* buffer) {
+  scoped_refptr<GbmSurfaceBuffer> scoped_buffer(new GbmSurfaceBuffer(dri,
+                                                                     buffer));
+  if (!scoped_buffer->GetFramebufferId())
+    return NULL;
+
+  return scoped_buffer;
+}
+
+// static
+scoped_refptr<GbmSurfaceBuffer> GbmSurfaceBuffer::GetBuffer(gbm_bo* buffer) {
+  return scoped_refptr<GbmSurfaceBuffer>(
+      static_cast<GbmSurfaceBuffer*>(gbm_bo_get_user_data(buffer)));
+}
+
+// static
+void GbmSurfaceBuffer::Destroy(gbm_bo* buffer, void* data) {
+  GbmSurfaceBuffer* scoped_buffer = static_cast<GbmSurfaceBuffer*>(data);
+  scoped_buffer->self_ = NULL;
+}
+
+}  // namespace
+
+GbmSurface::GbmSurface(DriWindowDelegate* window_delegate,
+                       gbm_device* device,
+                       DriWrapper* dri)
+    : GbmSurfaceless(window_delegate),
+      gbm_device_(device),
+      dri_(dri),
+      native_surface_(NULL),
+      current_buffer_(NULL) {
+}
+
+GbmSurface::~GbmSurface() {
+  if (current_buffer_)
+    gbm_surface_release_buffer(native_surface_, current_buffer_);
+
+  if (native_surface_)
+    gbm_surface_destroy(native_surface_);
+}
+
+bool GbmSurface::Initialize() {
+  // If we're initializing the surface without a controller (possible on startup
+  // where the surface creation can happen before the native window delegate
+  // IPCs arrive), initialize the size to a valid value such that surface
+  // creation doesn't fail.
+  gfx::Size size(1, 1);
+  if (window_delegate_->GetController()) {
+    size = window_delegate_->GetController()->GetModeSize();
+  }
+  // TODO(dnicoara) Check underlying system support for pixel format.
+  native_surface_ =
+      gbm_surface_create(gbm_device_,
+                         size.width(),
+                         size.height(),
+                         GBM_BO_FORMAT_XRGB8888,
+                         GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);
+
+  if (!native_surface_)
+    return false;
+
+  size_ = size;
+  return true;
+}
+
+intptr_t GbmSurface::GetNativeWindow() {
+  DCHECK(native_surface_);
+  return reinterpret_cast<intptr_t>(native_surface_);
+}
+
+bool GbmSurface::ResizeNativeWindow(const gfx::Size& viewport_size) {
+  if (size_ == viewport_size)
+    return true;
+
+  return false;
+}
+
+bool GbmSurface::OnSwapBuffers() {
+  DCHECK(native_surface_);
+
+  gbm_bo* pending_buffer = gbm_surface_lock_front_buffer(native_surface_);
+  scoped_refptr<GbmSurfaceBuffer> primary =
+      GbmSurfaceBuffer::GetBuffer(pending_buffer);
+  if (!primary.get()) {
+    primary = GbmSurfaceBuffer::CreateBuffer(dri_, pending_buffer);
+    if (!primary.get()) {
+      LOG(ERROR) << "Failed to associate the buffer with the controller";
+      return false;
+    }
+  }
+
+  // The primary buffer is a special case.
+  if (window_delegate_->GetController())
+    window_delegate_->GetController()->QueueOverlayPlane(OverlayPlane(primary));
+
+  if (!GbmSurfaceless::OnSwapBuffers())
+    return false;
+
+  // If there was a frontbuffer, it is no longer active. Release it back to GBM.
+  if (current_buffer_)
+    gbm_surface_release_buffer(native_surface_, current_buffer_);
+
+  current_buffer_ = pending_buffer;
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_surface.h b/ui/ozone/platform/dri/gbm_surface.h
new file mode 100644
index 0000000..be52c9c
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surface.h
@@ -0,0 +1,59 @@
+// 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 UI_OZONE_PLATFORM_DRI_GBM_SURFACE_H_
+#define UI_OZONE_PLATFORM_DRI_GBM_SURFACE_H_
+
+#include "base/macros.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/dri/gbm_surfaceless.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+struct gbm_bo;
+struct gbm_device;
+struct gbm_surface;
+
+namespace ui {
+
+class DriBuffer;
+class DriWrapper;
+class DriWindowDelegate;
+
+// Extends the GBM surfaceless functionality and adds an implicit surface for
+// the primary plane. Arbitrary buffers can still be allocated and displayed as
+// overlay planes, however the primary plane is associated with the native
+// surface and is updated via an EGLSurface.
+class GbmSurface : public GbmSurfaceless {
+ public:
+  GbmSurface(DriWindowDelegate* window_delegate,
+             gbm_device* device,
+             DriWrapper* dri);
+  virtual ~GbmSurface();
+
+  bool Initialize();
+
+  // GbmSurfaceless:
+  virtual intptr_t GetNativeWindow() OVERRIDE;
+  virtual bool ResizeNativeWindow(const gfx::Size& viewport_size) OVERRIDE;
+  virtual bool OnSwapBuffers() OVERRIDE;
+
+ private:
+  gbm_device* gbm_device_;
+
+  DriWrapper* dri_;
+
+  // The native GBM surface. In EGL this represents the EGLNativeWindowType.
+  gbm_surface* native_surface_;
+
+  // Buffer currently used for scanout.
+  gbm_bo* current_buffer_;
+
+  gfx::Size size_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurface);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GBM_SURFACE_H_
diff --git a/ui/ozone/platform/dri/gbm_surface_factory.cc b/ui/ozone/platform/dri/gbm_surface_factory.cc
new file mode 100644
index 0000000..a31fcee
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surface_factory.cc
@@ -0,0 +1,231 @@
+// 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 "ui/ozone/platform/dri/gbm_surface_factory.h"
+
+#include <gbm.h>
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/gbm_buffer.h"
+#include "ui/ozone/platform/dri/gbm_surface.h"
+#include "ui/ozone/platform/dri/gbm_surfaceless.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/public/native_pixmap.h"
+#include "ui/ozone/public/overlay_candidates_ozone.h"
+#include "ui/ozone/public/ozone_switches.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace ui {
+namespace {
+
+class SingleOverlay : public OverlayCandidatesOzone {
+ public:
+  SingleOverlay() {}
+  virtual ~SingleOverlay() {}
+
+  virtual void CheckOverlaySupport(
+      OverlaySurfaceCandidateList* candidates) OVERRIDE {
+    if (candidates->size() == 2) {
+      OverlayCandidatesOzone::OverlaySurfaceCandidate* first =
+          &(*candidates)[0];
+      OverlayCandidatesOzone::OverlaySurfaceCandidate* second =
+          &(*candidates)[1];
+      OverlayCandidatesOzone::OverlaySurfaceCandidate* overlay;
+      if (first->plane_z_order == 0) {
+        overlay = second;
+      } else if (second->plane_z_order == 0) {
+        overlay = first;
+      } else {
+        NOTREACHED();
+        return;
+      }
+      if (overlay->plane_z_order > 0 &&
+          IsTransformSupported(overlay->transform)) {
+        overlay->overlay_handled = true;
+      }
+    }
+  }
+
+ private:
+  bool IsTransformSupported(gfx::OverlayTransform transform) {
+    switch (transform) {
+      case gfx::OVERLAY_TRANSFORM_NONE:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  DISALLOW_COPY_AND_ASSIGN(SingleOverlay);
+};
+
+}  // namespace
+
+GbmSurfaceFactory::GbmSurfaceFactory(bool allow_surfaceless)
+    : DriSurfaceFactory(NULL, NULL, NULL),
+      device_(NULL),
+      allow_surfaceless_(allow_surfaceless) {
+}
+
+GbmSurfaceFactory::~GbmSurfaceFactory() {}
+
+void GbmSurfaceFactory::InitializeGpu(
+    DriWrapper* dri,
+    gbm_device* device,
+    ScreenManager* screen_manager,
+    DriWindowDelegateManager* window_manager) {
+  drm_ = dri;
+  device_ = device;
+  screen_manager_ = screen_manager;
+  window_manager_ = window_manager;
+}
+
+intptr_t GbmSurfaceFactory::GetNativeDisplay() {
+  DCHECK(state_ == INITIALIZED);
+  return reinterpret_cast<intptr_t>(device_);
+}
+
+const int32* GbmSurfaceFactory::GetEGLSurfaceProperties(
+    const int32* desired_list) {
+  static const int32 kConfigAttribs[] = {
+    EGL_BUFFER_SIZE, 32,
+    EGL_ALPHA_SIZE, 8,
+    EGL_BLUE_SIZE, 8,
+    EGL_GREEN_SIZE, 8,
+    EGL_RED_SIZE, 8,
+    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
+    EGL_NONE
+  };
+
+  return kConfigAttribs;
+}
+
+bool GbmSurfaceFactory::LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  base::NativeLibraryLoadError error;
+  base::NativeLibrary gles_library = base::LoadNativeLibrary(
+      base::FilePath("libGLESv2.so.2"),
+      &error);
+  if (!gles_library) {
+    LOG(WARNING) << "Failed to load GLES library: " << error.ToString();
+    return false;
+  }
+
+  base::NativeLibrary egl_library = base::LoadNativeLibrary(
+      base::FilePath("libEGL.so.1"),
+      &error);
+  if (!egl_library) {
+    LOG(WARNING) << "Failed to load EGL library: " << error.ToString();
+    base::UnloadNativeLibrary(gles_library);
+    return false;
+  }
+
+  GLGetProcAddressProc get_proc_address =
+      reinterpret_cast<GLGetProcAddressProc>(
+          base::GetFunctionPointerFromNativeLibrary(
+              egl_library, "eglGetProcAddress"));
+  if (!get_proc_address) {
+    LOG(ERROR) << "eglGetProcAddress not found.";
+    base::UnloadNativeLibrary(egl_library);
+    base::UnloadNativeLibrary(gles_library);
+    return false;
+  }
+
+  set_gl_get_proc_address.Run(get_proc_address);
+  add_gl_library.Run(egl_library);
+  add_gl_library.Run(gles_library);
+
+  return true;
+}
+
+scoped_ptr<SurfaceOzoneEGL> GbmSurfaceFactory::CreateEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  DCHECK(state_ == INITIALIZED);
+
+  DriWindowDelegate* delegate = GetOrCreateWindowDelegate(widget);
+
+  scoped_ptr<GbmSurface> surface(new GbmSurface(delegate, device_, drm_));
+  if (!surface->Initialize())
+    return scoped_ptr<SurfaceOzoneEGL>();
+
+  return surface.PassAs<SurfaceOzoneEGL>();
+}
+
+scoped_ptr<SurfaceOzoneEGL>
+GbmSurfaceFactory::CreateSurfacelessEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  if (!allow_surfaceless_)
+    return scoped_ptr<SurfaceOzoneEGL>();
+
+  DriWindowDelegate* delegate = GetOrCreateWindowDelegate(widget);
+  return scoped_ptr<SurfaceOzoneEGL>(new GbmSurfaceless(delegate));
+}
+
+scoped_refptr<ui::NativePixmap> GbmSurfaceFactory::CreateNativePixmap(
+    gfx::Size size,
+    BufferFormat format) {
+  scoped_refptr<GbmBuffer> buffer = GbmBuffer::CreateBuffer(
+      drm_, device_, format, size, true);
+  if (!buffer.get())
+    return NULL;
+
+  return scoped_refptr<GbmPixmap>(new GbmPixmap(buffer));
+}
+
+OverlayCandidatesOzone* GbmSurfaceFactory::GetOverlayCandidates(
+    gfx::AcceleratedWidget w) {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kOzoneTestSingleOverlaySupport))
+    return new SingleOverlay();
+  return NULL;
+}
+
+bool GbmSurfaceFactory::ScheduleOverlayPlane(
+    gfx::AcceleratedWidget widget,
+    int plane_z_order,
+    gfx::OverlayTransform plane_transform,
+    scoped_refptr<NativePixmap> buffer,
+    const gfx::Rect& display_bounds,
+    const gfx::RectF& crop_rect) {
+  scoped_refptr<GbmPixmap> pixmap = static_cast<GbmPixmap*>(buffer.get());
+  if (!pixmap.get()) {
+    LOG(ERROR) << "ScheduleOverlayPlane passed NULL buffer.";
+    return false;
+  }
+  HardwareDisplayController* hdc =
+      window_manager_->GetWindowDelegate(widget)->GetController();
+  if (!hdc)
+    return true;
+
+  hdc->QueueOverlayPlane(OverlayPlane(pixmap->buffer(),
+                                      plane_z_order,
+                                      plane_transform,
+                                      display_bounds,
+                                      crop_rect));
+  return true;
+}
+
+bool GbmSurfaceFactory::CanShowPrimaryPlaneAsOverlay() {
+  return allow_surfaceless_;
+}
+
+DriWindowDelegate* GbmSurfaceFactory::GetOrCreateWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  if (!window_manager_->HasWindowDelegate(widget)) {
+    scoped_ptr<DriWindowDelegate> delegate(
+        new DriWindowDelegateImpl(widget, screen_manager_));
+    delegate->Initialize();
+    window_manager_->AddWindowDelegate(widget, delegate.Pass());
+  }
+
+  return window_manager_->GetWindowDelegate(widget);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_surface_factory.h b/ui/ozone/platform/dri/gbm_surface_factory.h
new file mode 100644
index 0000000..2118db2
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surface_factory.h
@@ -0,0 +1,62 @@
+// 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 UI_OZONE_PLATFORM_DRI_GBM_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_DRI_GBM_SURFACE_FACTORY_H_
+
+#include "ui/ozone/platform/dri/dri_surface_factory.h"
+
+struct gbm_device;
+
+namespace ui {
+
+class DriWindowDelegate;
+class DriWindowDelegateManager;
+
+class GbmSurfaceFactory : public DriSurfaceFactory {
+ public:
+  GbmSurfaceFactory(bool allow_surfaceless);
+  virtual ~GbmSurfaceFactory();
+
+  void InitializeGpu(DriWrapper* dri,
+                     gbm_device* device,
+                     ScreenManager* screen_manager,
+                     DriWindowDelegateManager* window_manager);
+
+  // DriSurfaceFactory:
+  virtual intptr_t GetNativeDisplay() OVERRIDE;
+  virtual const int32_t* GetEGLSurfaceProperties(
+      const int32_t* desired_list) OVERRIDE;
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
+  virtual scoped_ptr<ui::SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+      gfx::AcceleratedWidget w) OVERRIDE;
+  virtual scoped_ptr<SurfaceOzoneEGL> CreateSurfacelessEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget) OVERRIDE;
+  virtual scoped_refptr<ui::NativePixmap> CreateNativePixmap(
+      gfx::Size size,
+      BufferFormat format) OVERRIDE;
+  virtual OverlayCandidatesOzone* GetOverlayCandidates(
+      gfx::AcceleratedWidget w) OVERRIDE;
+  virtual bool ScheduleOverlayPlane(gfx::AcceleratedWidget widget,
+                                    int plane_z_order,
+                                    gfx::OverlayTransform plane_transform,
+                                    scoped_refptr<NativePixmap> buffer,
+                                    const gfx::Rect& display_bounds,
+                                    const gfx::RectF& crop_rect) OVERRIDE;
+  virtual bool CanShowPrimaryPlaneAsOverlay() OVERRIDE;
+
+ private:
+  DriWindowDelegate* GetOrCreateWindowDelegate(gfx::AcceleratedWidget widget);
+
+  gbm_device* device_;  // Not owned.
+  bool allow_surfaceless_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceFactory);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GBM_SURFACE_FACTORY_H_
diff --git a/ui/ozone/platform/dri/gbm_surfaceless.cc b/ui/ozone/platform/dri/gbm_surfaceless.cc
new file mode 100644
index 0000000..b374844
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surfaceless.cc
@@ -0,0 +1,45 @@
+// 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 "ui/ozone/platform/dri/gbm_surfaceless.h"
+
+#include "ui/ozone/platform/dri/dri_vsync_provider.h"
+#include "ui/ozone/platform/dri/dri_window_delegate.h"
+#include "ui/ozone/platform/dri/gbm_buffer.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+
+namespace ui {
+
+GbmSurfaceless::GbmSurfaceless(DriWindowDelegate* window_delegate)
+    : window_delegate_(window_delegate) {
+}
+
+GbmSurfaceless::~GbmSurfaceless() {}
+
+intptr_t GbmSurfaceless::GetNativeWindow() {
+  NOTREACHED();
+  return 0;
+}
+
+bool GbmSurfaceless::ResizeNativeWindow(const gfx::Size& viewport_size) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool GbmSurfaceless::OnSwapBuffers() {
+  HardwareDisplayController* controller = window_delegate_->GetController();
+  if (!controller)
+    return true;
+
+  bool success = controller->SchedulePageFlip();
+  controller->WaitForPageFlipEvent();
+
+  return success;
+}
+
+scoped_ptr<gfx::VSyncProvider> GbmSurfaceless::CreateVSyncProvider() {
+  return scoped_ptr<gfx::VSyncProvider>(new DriVSyncProvider(window_delegate_));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gbm_surfaceless.h b/ui/ozone/platform/dri/gbm_surfaceless.h
new file mode 100644
index 0000000..4e3b92b
--- /dev/null
+++ b/ui/ozone/platform/dri/gbm_surfaceless.h
@@ -0,0 +1,41 @@
+// 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 UI_OZONE_PLATFORM_DRI_GBM_SURFACELESS_H_
+#define UI_OZONE_PLATFORM_DRI_GBM_SURFACELESS_H_
+
+#include "ui/ozone/public/surface_ozone_egl.h"
+
+namespace gfx {
+class Size;
+}  // namespace gfx
+
+namespace ui {
+
+class DriWindowDelegate;
+
+// In surfaceless mode drawing and displaying happens directly through
+// NativePixmap buffers. CC would call into SurfaceFactoryOzone to allocate the
+// buffers and then call ScheduleOverlayPlane(..) to schedule the buffer for
+// presentation.
+class GbmSurfaceless : public SurfaceOzoneEGL {
+ public:
+  GbmSurfaceless(DriWindowDelegate* window_delegate);
+  virtual ~GbmSurfaceless();
+
+  // SurfaceOzoneEGL:
+  virtual intptr_t GetNativeWindow() OVERRIDE;
+  virtual bool ResizeNativeWindow(const gfx::Size& viewport_size) OVERRIDE;
+  virtual bool OnSwapBuffers() OVERRIDE;
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE;
+
+ protected:
+  DriWindowDelegate* window_delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmSurfaceless);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GBM_SURFACELESS_H_
diff --git a/ui/ozone/platform/dri/gpu_platform_support_gbm.cc b/ui/ozone/platform/dri/gpu_platform_support_gbm.cc
new file mode 100644
index 0000000..9bcd094
--- /dev/null
+++ b/ui/ozone/platform/dri/gpu_platform_support_gbm.cc
@@ -0,0 +1,206 @@
+// 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 "ui/ozone/platform/dri/gpu_platform_support_gbm.h"
+
+#include "ipc/ipc_message_macros.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/ozone/common/display_util.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/common/gpu/ozone_gpu_messages.h"
+#include "ui/ozone/platform/dri/dri_surface_factory.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/native_display_delegate_dri.h"
+
+namespace ui {
+
+namespace {
+
+class FindDisplayById {
+ public:
+  FindDisplayById(int64_t display_id) : display_id_(display_id) {}
+
+  bool operator()(const DisplaySnapshot_Params& display) const {
+    return display.display_id == display_id_;
+  }
+
+ private:
+  int64_t display_id_;
+};
+
+}  // namespace
+
+GpuPlatformSupportGbm::GpuPlatformSupportGbm(
+    DriSurfaceFactory* dri,
+    DriWindowDelegateManager* window_manager,
+    ScreenManager* screen_manager,
+    scoped_ptr<NativeDisplayDelegateDri> ndd)
+    : sender_(NULL),
+      dri_(dri),
+      window_manager_(window_manager),
+      screen_manager_(screen_manager),
+      ndd_(ndd.Pass()) {
+}
+
+GpuPlatformSupportGbm::~GpuPlatformSupportGbm() {}
+
+void GpuPlatformSupportGbm::AddHandler(scoped_ptr<GpuPlatformSupport> handler) {
+  handlers_.push_back(handler.release());
+}
+
+void GpuPlatformSupportGbm::OnChannelEstablished(IPC::Sender* sender) {
+  sender_ = sender;
+
+  for (size_t i = 0; i < handlers_.size(); ++i)
+    handlers_[i]->OnChannelEstablished(sender);
+}
+
+bool GpuPlatformSupportGbm::OnMessageReceived(const IPC::Message& message) {
+  bool handled = true;
+
+  IPC_BEGIN_MESSAGE_MAP(GpuPlatformSupportGbm, message)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_CreateWindowDelegate, OnCreateWindowDelegate)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_DestroyWindowDelegate,
+                      OnDestroyWindowDelegate)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_WindowBoundsChanged, OnWindowBoundsChanged)
+
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_CursorSet, OnCursorSet)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_CursorMove, OnCursorMove)
+
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_ForceDPMSOn, OnForceDPMSOn)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_RefreshNativeDisplays,
+                      OnRefreshNativeDisplays)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_ConfigureNativeDisplay,
+                      OnConfigureNativeDisplay)
+  IPC_MESSAGE_HANDLER(OzoneGpuMsg_DisableNativeDisplay, OnDisableNativeDisplay)
+  IPC_MESSAGE_UNHANDLED(handled = false);
+  IPC_END_MESSAGE_MAP()
+
+  if (!handled)
+    for (size_t i = 0; i < handlers_.size(); ++i)
+      if (handlers_[i]->OnMessageReceived(message))
+        return true;
+
+  return false;
+}
+
+void GpuPlatformSupportGbm::OnCreateWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  // Due to how the GPU process starts up this IPC call may happen after the IPC
+  // to create a surface. Since a surface wants to know the window associated
+  // with it, we create it ahead of time. So when this call happens we do not
+  // create a delegate if it already exists.
+  if (!window_manager_->HasWindowDelegate(widget)) {
+    scoped_ptr<DriWindowDelegate> delegate(
+        new DriWindowDelegateImpl(widget, screen_manager_));
+    delegate->Initialize();
+    window_manager_->AddWindowDelegate(widget, delegate.Pass());
+  }
+}
+
+void GpuPlatformSupportGbm::OnDestroyWindowDelegate(
+    gfx::AcceleratedWidget widget) {
+  scoped_ptr<DriWindowDelegate> delegate =
+      window_manager_->RemoveWindowDelegate(widget);
+  delegate->Shutdown();
+}
+
+void GpuPlatformSupportGbm::OnWindowBoundsChanged(gfx::AcceleratedWidget widget,
+                                                  const gfx::Rect& bounds) {
+  window_manager_->GetWindowDelegate(widget)->OnBoundsChanged(bounds);
+}
+
+void GpuPlatformSupportGbm::OnCursorSet(gfx::AcceleratedWidget widget,
+                                        const std::vector<SkBitmap>& bitmaps,
+                                        const gfx::Point& location,
+                                        int frame_delay_ms) {
+  dri_->SetHardwareCursor(widget, bitmaps, location, frame_delay_ms);
+}
+
+void GpuPlatformSupportGbm::OnCursorMove(gfx::AcceleratedWidget widget,
+                                         const gfx::Point& location) {
+  dri_->MoveHardwareCursor(widget, location);
+}
+
+void GpuPlatformSupportGbm::OnForceDPMSOn() {
+  ndd_->ForceDPMSOn();
+}
+
+void GpuPlatformSupportGbm::OnRefreshNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& cached_displays) {
+  std::vector<DisplaySnapshot_Params> displays;
+  std::vector<DisplaySnapshot*> native_displays = ndd_->GetDisplays();
+
+  // If any of the cached displays are in the list of new displays then apply
+  // their configuration immediately.
+  for (size_t i = 0; i < native_displays.size(); ++i) {
+    std::vector<DisplaySnapshot_Params>::const_iterator it =
+        std::find_if(cached_displays.begin(),
+                     cached_displays.end(),
+                     FindDisplayById(native_displays[i]->display_id()));
+
+    if (it == cached_displays.end())
+      continue;
+
+    if (it->has_current_mode)
+      OnConfigureNativeDisplay(it->display_id, it->current_mode, it->origin);
+    else
+      OnDisableNativeDisplay(it->display_id);
+  }
+
+  for (size_t i = 0; i < native_displays.size(); ++i)
+    displays.push_back(GetDisplaySnapshotParams(*native_displays[i]));
+
+  sender_->Send(new OzoneHostMsg_UpdateNativeDisplays(displays));
+}
+
+void GpuPlatformSupportGbm::OnConfigureNativeDisplay(
+    int64_t id,
+    const DisplayMode_Params& mode_param,
+    const gfx::Point& origin) {
+  DisplaySnapshot* display = ndd_->FindDisplaySnapshot(id);
+  if (!display) {
+    LOG(ERROR) << "There is no display with ID " << id;
+    return;
+  }
+
+  const DisplayMode* mode = NULL;
+  for (size_t i = 0; i < display->modes().size(); ++i) {
+    if (mode_param.size == display->modes()[i]->size() &&
+        mode_param.is_interlaced == display->modes()[i]->is_interlaced() &&
+        mode_param.refresh_rate == display->modes()[i]->refresh_rate()) {
+      mode = display->modes()[i];
+      break;
+    }
+  }
+
+  // If the display doesn't have the mode natively, then lookup the mode from
+  // other displays and try using it on the current display (some displays
+  // support panel fitting and they can use different modes even if the mode
+  // isn't explicitly declared).
+  if (!mode)
+    mode = ndd_->FindDisplayMode(
+        mode_param.size, mode_param.is_interlaced, mode_param.refresh_rate);
+
+  if (!mode) {
+    LOG(ERROR) << "Failed to find mode: size=" << mode_param.size.ToString()
+               << " is_interlaced=" << mode_param.is_interlaced
+               << " refresh_rate=" << mode_param.refresh_rate;
+    return;
+  }
+
+  ndd_->Configure(*display, mode, origin);
+}
+
+void GpuPlatformSupportGbm::OnDisableNativeDisplay(int64_t id) {
+  DisplaySnapshot* display = ndd_->FindDisplaySnapshot(id);
+  if (display)
+    ndd_->Configure(*display, NULL, gfx::Point());
+  else
+    LOG(ERROR) << "There is no display with ID " << id;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gpu_platform_support_gbm.h b/ui/ozone/platform/dri/gpu_platform_support_gbm.h
new file mode 100644
index 0000000..8fa4cc3
--- /dev/null
+++ b/ui/ozone/platform/dri/gpu_platform_support_gbm.h
@@ -0,0 +1,79 @@
+// 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 UI_OZONE_PLATFORM_DRI_GPU_PLATFORM_SUPPORT_GBM_H_
+#define UI_OZONE_PLATFORM_DRI_GPU_PLATFORM_SUPPORT_GBM_H_
+
+#include "base/containers/scoped_ptr_hash_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Point;
+class Rect;
+}
+
+namespace ui {
+
+class DriSurfaceFactory;
+class DriWindowDelegate;
+class DriWindowDelegateManager;
+class NativeDisplayDelegateDri;
+class ScreenManager;
+
+struct DisplayMode_Params;
+struct DisplaySnapshot_Params;
+
+class GpuPlatformSupportGbm : public GpuPlatformSupport {
+ public:
+  GpuPlatformSupportGbm(DriSurfaceFactory* dri,
+                        DriWindowDelegateManager* window_manager,
+                        ScreenManager* screen_manager,
+                        scoped_ptr<NativeDisplayDelegateDri> ndd);
+  virtual ~GpuPlatformSupportGbm();
+
+  void AddHandler(scoped_ptr<GpuPlatformSupport> handler);
+
+  // GpuPlatformSupport:
+  virtual void OnChannelEstablished(IPC::Sender* sender) OVERRIDE;
+
+  // IPC::Listener:
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+  void OnCreateWindowDelegate(gfx::AcceleratedWidget widget);
+  void OnDestroyWindowDelegate(gfx::AcceleratedWidget widget);
+  void OnWindowBoundsChanged(gfx::AcceleratedWidget widget,
+                             const gfx::Rect& bounds);
+  void OnCursorSet(gfx::AcceleratedWidget widget,
+                   const std::vector<SkBitmap>& bitmaps,
+                   const gfx::Point& location,
+                   int frame_delay_ms);
+  void OnCursorMove(gfx::AcceleratedWidget widget, const gfx::Point& location);
+
+  // Display related IPC handlers.
+  void OnForceDPMSOn();
+  void OnRefreshNativeDisplays(
+      const std::vector<DisplaySnapshot_Params>& cached_displays);
+  void OnConfigureNativeDisplay(int64_t id,
+                                const DisplayMode_Params& mode,
+                                const gfx::Point& origin);
+  void OnDisableNativeDisplay(int64_t id);
+
+  IPC::Sender* sender_;                       // Not owned.
+  DriSurfaceFactory* dri_;                    // Not owned.
+  DriWindowDelegateManager* window_manager_;  // Not owned.
+  ScreenManager* screen_manager_;             // Not owned.
+
+  scoped_ptr<NativeDisplayDelegateDri> ndd_;
+  ScopedVector<GpuPlatformSupport> handlers_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_GPU_PLATFORM_SUPPORT_GBM_H_
diff --git a/ui/ozone/platform/dri/gpu_platform_support_host_gbm.cc b/ui/ozone/platform/dri/gpu_platform_support_host_gbm.cc
new file mode 100644
index 0000000..f5a369a
--- /dev/null
+++ b/ui/ozone/platform/dri/gpu_platform_support_host_gbm.cc
@@ -0,0 +1,117 @@
+// 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 "ui/ozone/platform/dri/gpu_platform_support_host_gbm.h"
+
+#include "base/debug/trace_event.h"
+#include "ui/ozone/common/gpu/ozone_gpu_message_params.h"
+#include "ui/ozone/common/gpu/ozone_gpu_messages.h"
+#include "ui/ozone/platform/dri/channel_observer.h"
+
+namespace ui {
+
+GpuPlatformSupportHostGbm::GpuPlatformSupportHostGbm()
+    : host_id_(-1), sender_(NULL) {
+}
+
+GpuPlatformSupportHostGbm::~GpuPlatformSupportHostGbm() {}
+
+bool GpuPlatformSupportHostGbm::IsConnected() const {
+  return sender_ != NULL;
+}
+
+void GpuPlatformSupportHostGbm::RegisterHandler(
+    GpuPlatformSupportHost* handler) {
+  handlers_.push_back(handler);
+}
+
+void GpuPlatformSupportHostGbm::UnregisterHandler(
+    GpuPlatformSupportHost* handler) {
+  std::vector<GpuPlatformSupportHost*>::iterator it =
+      std::find(handlers_.begin(), handlers_.end(), handler);
+  if (it != handlers_.end())
+    handlers_.erase(it);
+}
+
+void GpuPlatformSupportHostGbm::AddChannelObserver(ChannelObserver* observer) {
+  channel_observers_.AddObserver(observer);
+
+  if (sender_)
+    observer->OnChannelEstablished();
+}
+
+void GpuPlatformSupportHostGbm::RemoveChannelObserver(
+    ChannelObserver* observer) {
+  channel_observers_.RemoveObserver(observer);
+}
+
+void GpuPlatformSupportHostGbm::OnChannelEstablished(int host_id,
+                                                     IPC::Sender* sender) {
+  TRACE_EVENT1("dri",
+               "GpuPlatformSupportHostGbm::OnChannelEstablished",
+               "host_id",
+               host_id);
+  host_id_ = host_id;
+  sender_ = sender;
+
+  while (!queued_messages_.empty()) {
+    Send(queued_messages_.front());
+    queued_messages_.pop();
+  }
+
+  for (size_t i = 0; i < handlers_.size(); ++i)
+    handlers_[i]->OnChannelEstablished(host_id, sender);
+
+  FOR_EACH_OBSERVER(
+      ChannelObserver, channel_observers_, OnChannelEstablished());
+}
+
+void GpuPlatformSupportHostGbm::OnChannelDestroyed(int host_id) {
+  TRACE_EVENT1("dri",
+               "GpuPlatformSupportHostGbm::OnChannelDestroyed",
+               "host_id",
+               host_id);
+  if (host_id_ == host_id) {
+    host_id_ = -1;
+    sender_ = NULL;
+
+    FOR_EACH_OBSERVER(
+        ChannelObserver, channel_observers_, OnChannelDestroyed());
+  }
+
+  for (size_t i = 0; i < handlers_.size(); ++i)
+    handlers_[i]->OnChannelDestroyed(host_id);
+}
+
+bool GpuPlatformSupportHostGbm::OnMessageReceived(const IPC::Message& message) {
+  for (size_t i = 0; i < handlers_.size(); ++i)
+    if (handlers_[i]->OnMessageReceived(message))
+      return true;
+
+  return false;
+}
+
+bool GpuPlatformSupportHostGbm::Send(IPC::Message* message) {
+  if (sender_)
+    return sender_->Send(message);
+
+  queued_messages_.push(message);
+  return true;
+}
+
+void GpuPlatformSupportHostGbm::SetHardwareCursor(
+    gfx::AcceleratedWidget widget,
+    const std::vector<SkBitmap>& bitmaps,
+    const gfx::Point& location,
+    int frame_delay_ms) {
+  Send(new OzoneGpuMsg_CursorSet(widget, bitmaps, location, frame_delay_ms));
+}
+
+void GpuPlatformSupportHostGbm::MoveHardwareCursor(
+    gfx::AcceleratedWidget widget,
+    const gfx::Point& location) {
+  Send(new OzoneGpuMsg_CursorMove(widget, location));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/gpu_platform_support_host_gbm.h b/ui/ozone/platform/dri/gpu_platform_support_host_gbm.h
new file mode 100644
index 0000000..a0e06f4
--- /dev/null
+++ b/ui/ozone/platform/dri/gpu_platform_support_host_gbm.h
@@ -0,0 +1,72 @@
+// 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 UI_OZONE_PLATFORM_DRI_GPU_PLATFORM_SUPPORT_HOST_GBM_H_
+#define UI_OZONE_PLATFORM_DRI_GPU_PLATFORM_SUPPORT_HOST_GBM_H_
+
+#include <queue>
+#include <vector>
+
+#include "base/observer_list.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/dri/hardware_cursor_delegate.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class ChannelObserver;
+
+class GpuPlatformSupportHostGbm : public GpuPlatformSupportHost,
+                                  public HardwareCursorDelegate,
+                                  public IPC::Sender {
+ public:
+  GpuPlatformSupportHostGbm();
+  virtual ~GpuPlatformSupportHostGbm();
+
+  bool IsConnected() const;
+
+  void RegisterHandler(GpuPlatformSupportHost* handler);
+  void UnregisterHandler(GpuPlatformSupportHost* handler);
+
+  void AddChannelObserver(ChannelObserver* observer);
+  void RemoveChannelObserver(ChannelObserver* observer);
+
+  // GpuPlatformSupportHost:
+  virtual void OnChannelEstablished(int host_id, IPC::Sender* sender) OVERRIDE;
+  virtual void OnChannelDestroyed(int host_id) OVERRIDE;
+
+  // IPC::Listener:
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+  // IPC::Sender:
+  virtual bool Send(IPC::Message* message) OVERRIDE;
+
+  // HardwareCursorDelegate:
+  virtual void SetHardwareCursor(gfx::AcceleratedWidget widget,
+                                 const std::vector<SkBitmap>& bitmaps,
+                                 const gfx::Point& location,
+                                 int frame_delay_ms) OVERRIDE;
+  virtual void MoveHardwareCursor(gfx::AcceleratedWidget widget,
+                                  const gfx::Point& location) OVERRIDE;
+
+ private:
+  int host_id_;
+  IPC::Sender* sender_;
+  std::vector<GpuPlatformSupportHost*> handlers_;
+  // If messages are sent before the channel is created, store the messages and
+  // delay sending them until the channel is created. These messages are stored
+  // in |queued_messaged_|.
+  std::queue<IPC::Message*> queued_messages_;
+  ObserverList<ChannelObserver> channel_observers_;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_GPU_GPU_PLATFORM_SUPPORT_HOST_GBM_H_
diff --git a/ui/ozone/platform/dri/hardware_cursor_delegate.h b/ui/ozone/platform/dri/hardware_cursor_delegate.h
new file mode 100644
index 0000000..17cea44
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_cursor_delegate.h
@@ -0,0 +1,37 @@
+// 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 UI_OZONE_PLATFORM_DRI_HARDWARE_CURSOR_DELEGATE_H_
+#define UI_OZONE_PLATFORM_DRI_HARDWARE_CURSOR_DELEGATE_H_
+
+#include "ui/gfx/native_widget_types.h"
+
+class SkBitmap;
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class HardwareCursorDelegate {
+ public:
+  // Update the HW cursor bitmap & move to specified location. If
+  // the bitmap is empty, the cursor is hidden.
+  virtual void SetHardwareCursor(gfx::AcceleratedWidget widget,
+                                 const std::vector<SkBitmap>& bitmaps,
+                                 const gfx::Point& location,
+                                 int frame_delay_ms) = 0;
+
+  // Move the HW cursor to the specified location.
+  virtual void MoveHardwareCursor(gfx::AcceleratedWidget widget,
+                                  const gfx::Point& location) = 0;
+
+ protected:
+  virtual ~HardwareCursorDelegate() {}
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_HARDWARE_CURSOR_DELEGATE_H_
diff --git a/ui/ozone/platform/dri/hardware_display_controller.cc b/ui/ozone/platform/dri/hardware_display_controller.cc
new file mode 100644
index 0000000..7d41188
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_controller.cc
@@ -0,0 +1,333 @@
+// 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 "ui/ozone/platform/dri/hardware_display_controller.h"
+
+#include <drm.h>
+#include <errno.h>
+#include <string.h>
+#include <xf86drm.h>
+
+#include "base/basictypes.h"
+#include "base/debug/trace_event.h"
+#include "base/logging.h"
+#include "base/time/time.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/dri/crtc_state.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace ui {
+
+namespace {
+
+// DRM callback on page flip events. This callback is triggered after the
+// page flip has happened and the backbuffer is now the new frontbuffer
+// The old frontbuffer is no longer used by the hardware and can be used for
+// future draw operations.
+//
+// |device| will contain a reference to the |ScanoutSurface| object which
+// the event belongs to.
+//
+// TODO(dnicoara) When we have a FD handler for the DRM calls in the message
+// loop, we can move this function in the handler.
+void HandlePageFlipEvent(int fd,
+                         unsigned int frame,
+                         unsigned int seconds,
+                         unsigned int useconds,
+                         void* controller) {
+  static_cast<HardwareDisplayController*>(controller)
+      ->OnPageFlipEvent(frame, seconds, useconds);
+}
+
+const OverlayPlane& GetPrimaryPlane(const OverlayPlaneList& overlays) {
+  for (size_t i = 0; i < overlays.size(); ++i) {
+    if (overlays[i].z_order == 0)
+      return overlays[i];
+  }
+
+  NOTREACHED();
+  return overlays[0];
+}
+
+}  // namespace
+
+OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer)
+    : buffer(buffer),
+      z_order(0),
+      display_bounds(gfx::Point(), buffer->GetSize()),
+      crop_rect(0, 0, 1, 1),
+      overlay_plane(0) {}
+
+OverlayPlane::OverlayPlane(scoped_refptr<ScanoutBuffer> buffer,
+                           int z_order,
+                           gfx::OverlayTransform plane_transform,
+                           const gfx::Rect& display_bounds,
+                           const gfx::RectF& crop_rect)
+    : buffer(buffer),
+      z_order(z_order),
+      plane_transform(plane_transform),
+      display_bounds(display_bounds),
+      crop_rect(crop_rect),
+      overlay_plane(0) {
+}
+
+OverlayPlane::~OverlayPlane() {}
+
+HardwareDisplayController::HardwareDisplayController(
+    DriWrapper* drm,
+    scoped_ptr<CrtcState> state)
+    : drm_(drm),
+      is_disabled_(true),
+      time_of_last_flip_(0),
+      pending_page_flips_(0) {
+  crtc_states_.push_back(state.release());
+}
+
+HardwareDisplayController::~HardwareDisplayController() {
+  // Reset the cursor.
+  UnsetCursor();
+}
+
+bool HardwareDisplayController::Modeset(const OverlayPlane& primary,
+                                        drmModeModeInfo mode) {
+  TRACE_EVENT0("dri", "HDC::Modeset");
+  DCHECK(primary.buffer.get());
+  pending_page_flips_ = 0;
+  bool status = true;
+  for (size_t i = 0; i < crtc_states_.size(); ++i) {
+    status &= ModesetCrtc(primary.buffer, mode, crtc_states_[i]);
+    crtc_states_[i]->set_is_disabled(false);
+  }
+
+  // Since a subset of controllers may be actively using |primary|, just keep
+  // track of it.
+  current_planes_ = std::vector<OverlayPlane>(1, primary);
+  pending_planes_.clear();
+  is_disabled_ = false;
+  mode_ = mode;
+  return status;
+}
+
+bool HardwareDisplayController::Enable() {
+  TRACE_EVENT0("dri", "HDC::Enable");
+  DCHECK(!current_planes_.empty());
+  OverlayPlane primary = GetPrimaryPlane(current_planes_);
+  DCHECK(primary.buffer.get());
+  pending_page_flips_ = 0;
+  bool status = true;
+  for (size_t i = 0; i < crtc_states_.size(); ++i)
+    status &= ModesetCrtc(primary.buffer, mode_, crtc_states_[i]);
+
+  return status;
+}
+
+void HardwareDisplayController::Disable() {
+  TRACE_EVENT0("dri", "HDC::Disable");
+  pending_page_flips_ = 0;
+  for (size_t i = 0; i < crtc_states_.size(); ++i) {
+    drm_->DisableCrtc(crtc_states_[i]->crtc());
+    crtc_states_[i]->set_is_disabled(true);
+  }
+
+  is_disabled_ = true;
+}
+
+void HardwareDisplayController::QueueOverlayPlane(const OverlayPlane& plane) {
+  pending_planes_.push_back(plane);
+}
+
+bool HardwareDisplayController::SchedulePageFlip() {
+  DCHECK(!pending_planes_.empty());
+  DCHECK_EQ(0u, pending_page_flips_);
+
+  if (is_disabled_)
+    return true;
+
+  bool status = true;
+  for (size_t i = 0; i < crtc_states_.size(); ++i)
+    status &= SchedulePageFlipOnCrtc(pending_planes_, crtc_states_[i]);
+
+  return status;
+}
+
+void HardwareDisplayController::WaitForPageFlipEvent() {
+  TRACE_EVENT1("dri", "HDC::WaitForPageFlipEvent",
+               "pending_pageflips", pending_page_flips_);
+
+  bool has_pending_page_flips = pending_page_flips_ != 0;
+  drmEventContext drm_event;
+  drm_event.version = DRM_EVENT_CONTEXT_VERSION;
+  drm_event.page_flip_handler = HandlePageFlipEvent;
+  drm_event.vblank_handler = NULL;
+
+  // Wait for the page-flips to complete.
+  while (pending_page_flips_ > 0)
+    drm_->HandleEvent(drm_event);
+
+  // In case there are no pending pageflips do not replace the current planes
+  // since they are still being used.
+  if (has_pending_page_flips)
+    current_planes_.swap(pending_planes_);
+
+  pending_planes_.clear();
+}
+
+void HardwareDisplayController::OnPageFlipEvent(unsigned int frame,
+                                                unsigned int seconds,
+                                                unsigned int useconds) {
+  TRACE_EVENT0("dri", "HDC::OnPageFlipEvent");
+
+  --pending_page_flips_;
+  time_of_last_flip_ =
+      static_cast<uint64_t>(seconds) * base::Time::kMicrosecondsPerSecond +
+      useconds;
+}
+
+bool HardwareDisplayController::SetCursor(scoped_refptr<ScanoutBuffer> buffer) {
+  bool status = true;
+  cursor_buffer_ = buffer;
+
+  if (is_disabled_)
+    return true;
+
+  for (size_t i = 0; i < crtc_states_.size(); ++i) {
+    status &= drm_->SetCursor(
+        crtc_states_[i]->crtc(), buffer->GetHandle(), buffer->GetSize());
+  }
+
+  return status;
+}
+
+bool HardwareDisplayController::UnsetCursor() {
+  bool status = true;
+  cursor_buffer_ = NULL;
+  for (size_t i = 0; i < crtc_states_.size(); ++i)
+    status &= drm_->SetCursor(crtc_states_[i]->crtc(), 0, gfx::Size());
+
+  return status;
+}
+
+bool HardwareDisplayController::MoveCursor(const gfx::Point& location) {
+  if (is_disabled_)
+    return true;
+
+  bool status = true;
+  for (size_t i = 0; i < crtc_states_.size(); ++i)
+    status &= drm_->MoveCursor(crtc_states_[i]->crtc(), location);
+
+  return status;
+}
+
+void HardwareDisplayController::AddCrtc(
+    scoped_ptr<CrtcState> state) {
+  crtc_states_.push_back(state.release());
+}
+
+scoped_ptr<CrtcState> HardwareDisplayController::RemoveCrtc(uint32_t crtc) {
+  for (ScopedVector<CrtcState>::iterator it = crtc_states_.begin();
+       it != crtc_states_.end();
+       ++it) {
+    if ((*it)->crtc() == crtc) {
+      scoped_ptr<CrtcState> controller(*it);
+      crtc_states_.weak_erase(it);
+      return controller.Pass();
+    }
+  }
+
+  return scoped_ptr<CrtcState>();
+}
+
+bool HardwareDisplayController::HasCrtc(uint32_t crtc) const {
+  for (size_t i = 0; i < crtc_states_.size(); ++i)
+    if (crtc_states_[i]->crtc() == crtc)
+      return true;
+
+  return false;
+}
+
+bool HardwareDisplayController::IsMirrored() const {
+  return crtc_states_.size() > 1;
+}
+
+bool HardwareDisplayController::IsDisabled() const {
+  return is_disabled_;
+}
+
+gfx::Size HardwareDisplayController::GetModeSize() const {
+  return gfx::Size(mode_.hdisplay, mode_.vdisplay);
+}
+
+bool HardwareDisplayController::ModesetCrtc(
+    const scoped_refptr<ScanoutBuffer>& buffer,
+    drmModeModeInfo mode,
+    CrtcState* state) {
+  if (!drm_->SetCrtc(state->crtc(),
+                     buffer->GetFramebufferId(),
+                     std::vector<uint32_t>(1, state->connector()),
+                     &mode)) {
+    LOG(ERROR) << "Failed to modeset: error='" << strerror(errno)
+               << "' crtc=" << state->crtc()
+               << " connector=" << state->connector()
+               << " framebuffer_id=" << buffer->GetFramebufferId()
+               << " mode=" << mode.hdisplay << "x" << mode.vdisplay << "@"
+               << mode.vrefresh;
+    return false;
+  }
+
+  return true;
+}
+
+bool HardwareDisplayController::SchedulePageFlipOnCrtc(
+    const OverlayPlaneList& overlays,
+    CrtcState* state) {
+  const OverlayPlane& primary = GetPrimaryPlane(overlays);
+  DCHECK(primary.buffer.get());
+
+  if (primary.buffer->GetSize() != gfx::Size(mode_.hdisplay, mode_.vdisplay)) {
+    LOG(WARNING) << "Trying to pageflip a buffer with the wrong size. Expected "
+                 << mode_.hdisplay << "x" << mode_.vdisplay
+                 << " got " << primary.buffer->GetSize().ToString() << " for"
+                 << " crtc=" << state->crtc()
+                 << " connector=" << state->connector();
+    return true;
+  }
+
+  if (!drm_->PageFlip(state->crtc(),
+                      primary.buffer->GetFramebufferId(),
+                      this)) {
+    LOG(ERROR) << "Cannot page flip: error='" << strerror(errno) << "'"
+               << " crtc=" << state->crtc()
+               << " framebuffer=" << primary.buffer->GetFramebufferId()
+               << " size=" << primary.buffer->GetSize().ToString();
+    return false;
+  }
+
+  ++pending_page_flips_;
+
+  for (size_t i = 0; i < overlays.size(); i++) {
+    const OverlayPlane& plane = overlays[i];
+    if (!plane.overlay_plane)
+      continue;
+
+    const gfx::Size& size = plane.buffer->GetSize();
+    gfx::RectF crop_rect = plane.crop_rect;
+    crop_rect.Scale(size.width(), size.height());
+    if (!drm_->PageFlipOverlay(state->crtc(),
+                               plane.buffer->GetFramebufferId(),
+                               plane.display_bounds,
+                               crop_rect,
+                               plane.overlay_plane)) {
+      LOG(ERROR) << "Cannot display on overlay: " << strerror(errno);
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/hardware_display_controller.h b/ui/ozone/platform/dri/hardware_display_controller.h
new file mode 100644
index 0000000..b26654d
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_controller.h
@@ -0,0 +1,219 @@
+// 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 UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_CONTROLLER_H_
+#define UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_CONTROLLER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+
+class CrtcState;
+class ScanoutBuffer;
+
+struct OverlayPlane {
+  // Simpler constructor for the primary plane.
+  explicit OverlayPlane(scoped_refptr<ScanoutBuffer> buffer);
+
+  OverlayPlane(scoped_refptr<ScanoutBuffer> buffer,
+               int z_order,
+               gfx::OverlayTransform plane_transform,
+               const gfx::Rect& display_bounds,
+               const gfx::RectF& crop_rect);
+
+  ~OverlayPlane();
+
+  scoped_refptr<ScanoutBuffer> buffer;
+  int z_order;
+  gfx::OverlayTransform plane_transform;
+  gfx::Rect display_bounds;
+  gfx::RectF crop_rect;
+  int overlay_plane;
+};
+
+typedef std::vector<OverlayPlane> OverlayPlaneList;
+
+// The HDCOz will handle modesettings and scannout operations for hardware
+// devices.
+//
+// In the DRM world there are 3 components that need to be paired up to be able
+// to display an image to the monitor: CRTC (cathode ray tube controller),
+// encoder and connector. The CRTC determines which framebuffer to read, when
+// to scanout and where to scanout. Encoders converts the stream from the CRTC
+// to the appropriate format for the connector. The connector is the physical
+// connection that monitors connect to.
+//
+// There is no 1:1:1 pairing for these components. It is possible for an encoder
+// to be compatible to multiple CRTCs and each connector can be used with
+// multiple encoders. In addition, it is possible to use one CRTC with multiple
+// connectors such that we can display the same image on multiple monitors.
+//
+// For example, the following configuration shows 2 different screens being
+// initialized separately.
+// -------------      -------------
+// | Connector |      | Connector |
+// |   HDMI    |      |    VGA    |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |  Encoder1  |     |  Encoder2 |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |   CRTC1   |      |   CRTC2   |
+// -------------      -------------
+//
+// In the following configuration 2 different screens are associated with the
+// same CRTC, so on scanout the same framebuffer will be displayed on both
+// monitors.
+// -------------      -------------
+// | Connector |      | Connector |
+// |   HDMI    |      |    VGA    |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+// -------------      -------------
+// |  Encoder1  |     |  Encoder2 |
+// -------------      -------------
+//       ^                  ^
+//       |                  |
+//      ----------------------
+//      |        CRTC1       |
+//      ----------------------
+//
+// Note that it is possible to have more connectors than CRTCs which means that
+// only a subset of connectors can be active independently, showing different
+// framebuffers. Though, in this case, it would be possible to have all
+// connectors active if some use the same CRTC to mirror the display.
+class HardwareDisplayController
+    : public base::SupportsWeakPtr<HardwareDisplayController> {
+ public:
+  HardwareDisplayController(DriWrapper* drm,
+                            scoped_ptr<CrtcState> state);
+
+  ~HardwareDisplayController();
+
+  // Performs the initial CRTC configuration. If successful, it will display the
+  // framebuffer for |primary| with |mode|.
+  bool Modeset(const OverlayPlane& primary,
+               drmModeModeInfo mode);
+
+  // Reconfigures the CRTC with the current surface and mode.
+  bool Enable();
+
+  // Disables the CRTC.
+  void Disable();
+
+  void QueueOverlayPlane(const OverlayPlane& plane);
+
+  // Schedules the |overlays|' framebuffers to be displayed on the next vsync
+  // event. The event will be posted on the graphics card file descriptor |fd_|
+  // and it can be read and processed by |drmHandleEvent|. That function can
+  // define the callback for the page flip event. A generic data argument will
+  // be presented to the callback. We use that argument to pass in the HDCO
+  // object the event belongs to.
+  //
+  // Between this call and the callback, the framebuffers used in this call
+  // should not be modified in any way as it would cause screen tearing if the
+  // hardware performed the flip. Note that the frontbuffer should also not
+  // be modified as it could still be displayed.
+  //
+  // Note that this function does not block. Also, this function should not be
+  // called again before the page flip occurrs.
+  //
+  // Returns true if the page flip was successfully registered, false otherwise.
+  bool SchedulePageFlip();
+
+  // TODO(dnicoara) This should be on the MessageLoop when Ozone can have
+  // BeginFrame can be triggered explicitly by Ozone.
+  void WaitForPageFlipEvent();
+
+  // Called when the page flip event occurred. The event is provided by the
+  // kernel when a VBlank event finished. This allows the controller to
+  // update internal state and propagate the update to the surface.
+  // The tuple (seconds, useconds) represents the event timestamp. |seconds|
+  // represents the number of seconds while |useconds| represents the
+  // microseconds (< 1 second) in the timestamp.
+  void OnPageFlipEvent(unsigned int frame,
+                       unsigned int seconds,
+                       unsigned int useconds);
+
+  // Set the hardware cursor to show the contents of |surface|.
+  bool SetCursor(scoped_refptr<ScanoutBuffer> buffer);
+
+  bool UnsetCursor();
+
+  // Moves the hardware cursor to |location|.
+  bool MoveCursor(const gfx::Point& location);
+
+  void AddCrtc(scoped_ptr<CrtcState> state);
+  scoped_ptr<CrtcState> RemoveCrtc(uint32_t crtc);
+  bool HasCrtc(uint32_t crtc) const;
+  bool IsMirrored() const;
+  bool IsDisabled() const;
+  gfx::Size GetModeSize() const;
+
+  gfx::Point origin() const { return origin_; }
+  void set_origin(const gfx::Point& origin) { origin_ = origin; }
+
+  const drmModeModeInfo& get_mode() const { return mode_; };
+  uint64_t get_time_of_last_flip() const {
+    return time_of_last_flip_;
+  };
+
+ private:
+  bool ModesetCrtc(const scoped_refptr<ScanoutBuffer>& buffer,
+                   drmModeModeInfo mode,
+                   CrtcState* state);
+
+  bool SchedulePageFlipOnCrtc(const OverlayPlaneList& overlays,
+                              CrtcState* state);
+
+  // Buffers need to be declared first so that they are destroyed last. Needed
+  // since the controllers may reference the buffers.
+  OverlayPlaneList current_planes_;
+  OverlayPlaneList pending_planes_;
+  scoped_refptr<ScanoutBuffer> cursor_buffer_;
+
+  // Object containing the connection to the graphics device and wraps the API
+  // calls to control it.
+  DriWrapper* drm_;
+
+  // Stores the CRTC configuration. This is used to identify monitors and
+  // configure them.
+  ScopedVector<CrtcState> crtc_states_;
+  gfx::Point origin_;
+  drmModeModeInfo mode_;
+  bool is_disabled_;
+  uint64_t time_of_last_flip_;
+
+  // Keeps track of the number of page flips scheduled but not yet serviced (in
+  // mirror mode each CRTC schedules its own page flip event). This value is
+  // changed as follows:
+  //  1) incremented when a successful SchedulePageFlipOnController() occurrs,
+  //  2) decremented when the page flip callback is triggered,
+  //  3) reset to 0 when a drmModeSetCrtc is called (via the DriWrapper).
+  uint32_t pending_page_flips_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayController);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_CONTROLLER_H_
diff --git a/ui/ozone/platform/dri/hardware_display_controller_unittest.cc b/ui/ozone/platform/dri/hardware_display_controller_unittest.cc
new file mode 100644
index 0000000..998ccca
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_controller_unittest.cc
@@ -0,0 +1,187 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/dri/crtc_state.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/test/mock_dri_wrapper.h"
+#include "ui/ozone/public/native_pixmap.h"
+
+namespace {
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kPrimaryCrtc = 1;
+const uint32_t kPrimaryConnector = 2;
+const uint32_t kSecondaryCrtc = 3;
+const uint32_t kSecondaryConnector = 4;
+
+const gfx::Size kDefaultModeSize(kDefaultMode.hdisplay, kDefaultMode.vdisplay);
+const gfx::SizeF kDefaultModeSizeF(1.0, 1.0);
+
+class MockScanoutBuffer : public ui::ScanoutBuffer {
+ public:
+  MockScanoutBuffer(const gfx::Size& size) : size_(size) {}
+
+  // ScanoutBuffer:
+  virtual uint32_t GetFramebufferId() const OVERRIDE {return 0; }
+  virtual uint32_t GetHandle() const OVERRIDE { return 0; }
+  virtual gfx::Size GetSize() const OVERRIDE { return size_; }
+
+ private:
+  virtual ~MockScanoutBuffer() {}
+
+  gfx::Size size_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockScanoutBuffer);
+};
+
+}  // namespace
+
+class HardwareDisplayControllerTest : public testing::Test {
+ public:
+  HardwareDisplayControllerTest() {}
+  virtual ~HardwareDisplayControllerTest() {}
+
+  virtual void SetUp() OVERRIDE;
+  virtual void TearDown() OVERRIDE;
+ protected:
+  scoped_ptr<ui::HardwareDisplayController> controller_;
+  scoped_ptr<ui::MockDriWrapper> drm_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayControllerTest);
+};
+
+void HardwareDisplayControllerTest::SetUp() {
+  drm_.reset(new ui::MockDriWrapper(3));
+  controller_.reset(new ui::HardwareDisplayController(
+      drm_.get(),
+      scoped_ptr<ui::CrtcState>(
+          new ui::CrtcState(drm_.get(), kPrimaryCrtc, kPrimaryConnector))));
+}
+
+void HardwareDisplayControllerTest::TearDown() {
+  controller_.reset();
+  drm_.reset();
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckModesettingResult) {
+  ui::OverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane, kDefaultMode));
+  EXPECT_FALSE(plane.buffer->HasOneRef());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateAfterPageFlip) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  controller_->QueueOverlayPlane(plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip());
+  controller_->WaitForPageFlipEvent();
+  EXPECT_TRUE(plane1.buffer->HasOneRef());
+  EXPECT_FALSE(plane2.buffer->HasOneRef());
+
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(0, drm_->get_overlay_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateIfModesetFails) {
+  drm_->set_set_crtc_expectation(false);
+
+  ui::OverlayPlane plane(scoped_refptr<ui::ScanoutBuffer>(new MockScanoutBuffer(
+      kDefaultModeSize)));
+
+  EXPECT_FALSE(controller_->Modeset(plane, kDefaultMode));
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckStateIfPageFlipFails) {
+  drm_->set_page_flip_expectation(false);
+
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  controller_->QueueOverlayPlane(plane2);
+  EXPECT_FALSE(controller_->SchedulePageFlip());
+  EXPECT_FALSE(plane1.buffer->HasOneRef());
+  EXPECT_FALSE(plane2.buffer->HasOneRef());
+
+  controller_->WaitForPageFlipEvent();
+  EXPECT_FALSE(plane1.buffer->HasOneRef());
+  EXPECT_TRUE(plane2.buffer->HasOneRef());
+}
+
+TEST_F(HardwareDisplayControllerTest, VerifyNoDRMCallsWhenDisabled) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  controller_->Disable();
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  controller_->QueueOverlayPlane(plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip());
+  controller_->WaitForPageFlipEvent();
+  EXPECT_EQ(0, drm_->get_page_flip_call_count());
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  controller_->QueueOverlayPlane(plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip());
+  controller_->WaitForPageFlipEvent();
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, CheckOverlayPresent) {
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  ui::OverlayPlane plane2(
+      scoped_refptr<ui::ScanoutBuffer>(new MockScanoutBuffer(kDefaultModeSize)),
+      1,
+      gfx::OVERLAY_TRANSFORM_NONE,
+      gfx::Rect(kDefaultModeSize),
+      gfx::RectF(kDefaultModeSizeF));
+  plane2.overlay_plane = 1;  // Force association with a plane.
+
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+
+  controller_->QueueOverlayPlane(plane1);
+  controller_->QueueOverlayPlane(plane2);
+
+  EXPECT_TRUE(controller_->SchedulePageFlip());
+  controller_->WaitForPageFlipEvent();
+  EXPECT_EQ(1, drm_->get_page_flip_call_count());
+  EXPECT_EQ(1, drm_->get_overlay_flip_call_count());
+}
+
+TEST_F(HardwareDisplayControllerTest, PageflipMirroredControllers) {
+  controller_->AddCrtc(
+      scoped_ptr<ui::CrtcState>(
+          new ui::CrtcState(drm_.get(), kSecondaryCrtc, kSecondaryConnector)));
+
+  ui::OverlayPlane plane1(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  EXPECT_TRUE(controller_->Modeset(plane1, kDefaultMode));
+  EXPECT_EQ(2, drm_->get_set_crtc_call_count());
+
+  ui::OverlayPlane plane2(scoped_refptr<ui::ScanoutBuffer>(
+      new MockScanoutBuffer(kDefaultModeSize)));
+  controller_->QueueOverlayPlane(plane2);
+  EXPECT_TRUE(controller_->SchedulePageFlip());
+  EXPECT_EQ(2, drm_->get_page_flip_call_count());
+}
diff --git a/ui/ozone/platform/dri/hardware_display_plane.cc b/ui/ozone/platform/dri/hardware_display_plane.cc
new file mode 100644
index 0000000..61743dc
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_plane.cc
@@ -0,0 +1,130 @@
+// 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 "ui/ozone/platform/dri/hardware_display_plane.h"
+
+#include <drm.h>
+#include <errno.h>
+#include <xf86drm.h>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+namespace {
+const char* kCrtcPropName = "CRTC_ID";
+const char* kFbPropName = "FB_ID";
+const char* kCrtcXPropName = "CRTC_X";
+const char* kCrtcYPropName = "CRTC_Y";
+const char* kCrtcWPropName = "CRTC_W";
+const char* kCrtcHPropName = "CRTC_H";
+const char* kSrcXPropName = "SRC_X";
+const char* kSrcYPropName = "SRC_Y";
+const char* kSrcWPropName = "SRC_W";
+const char* kSrcHPropName = "SRC_H";
+}
+
+HardwareDisplayPlane::Property::Property() : id_(0) {
+}
+
+bool HardwareDisplayPlane::Property::Initialize(
+    DriWrapper* drm,
+    const char* name,
+    const ScopedDrmObjectPropertyPtr& plane_props) {
+  for (uint32_t i = 0; i < plane_props->count_props; i++) {
+    ScopedDrmPropertyPtr property(
+        drmModeGetProperty(drm->get_fd(), plane_props->props[i]));
+    if (!strcmp(property->name, name)) {
+      id_ = property->prop_id;
+      break;
+    }
+  }
+  if (!id_) {
+    LOG(ERROR) << "Could not find property " << name;
+    return false;
+  }
+  return true;
+}
+
+HardwareDisplayPlane::HardwareDisplayPlane(
+    DriWrapper* drm,
+    drmModePropertySetPtr atomic_property_set,
+    ScopedDrmPlanePtr plane)
+    : drm_(drm),
+      property_set_(atomic_property_set),
+      plane_(plane.Pass()),
+      plane_id_(plane_->plane_id) {
+}
+
+HardwareDisplayPlane::~HardwareDisplayPlane() {
+}
+
+bool HardwareDisplayPlane::CanUseForCrtc(uint32_t crtc_id) {
+  return plane_->possible_crtcs & (1 << crtc_id);
+}
+
+bool HardwareDisplayPlane::SetPlaneData(uint32_t crtc_id,
+                                        uint32_t framebuffer,
+                                        const gfx::Rect& crtc_rect,
+                                        const gfx::Rect& src_rect) {
+  int plane_set_error =
+      drmModePropertySetAdd(
+          property_set_, plane_id_, crtc_prop_.id_, crtc_id) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, fb_prop_.id_, framebuffer) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, crtc_x_prop_.id_, crtc_rect.x()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, crtc_y_prop_.id_, crtc_rect.y()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, crtc_w_prop_.id_, crtc_rect.width()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, crtc_h_prop_.id_, crtc_rect.height()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, src_x_prop_.id_, src_rect.x()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, src_y_prop_.id_, src_rect.x()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, src_w_prop_.id_, src_rect.width()) ||
+      drmModePropertySetAdd(
+          property_set_, plane_id_, src_h_prop_.id_, src_rect.height());
+
+  if (plane_set_error) {
+    LOG(ERROR) << "Failed to set plane data";
+    return false;
+  }
+  return true;
+}
+
+bool HardwareDisplayPlane::Initialize() {
+  ScopedDrmObjectPropertyPtr plane_props(drmModeObjectGetProperties(
+      drm_->get_fd(), plane_id_, DRM_MODE_OBJECT_PLANE));
+
+  if (!plane_props) {
+    LOG(ERROR) << "Unable to get plane properties.";
+    return false;
+  }
+
+  bool props_init =
+      crtc_prop_.Initialize(drm_, kCrtcPropName, plane_props) &&
+      fb_prop_.Initialize(drm_, kFbPropName, plane_props) &&
+      crtc_x_prop_.Initialize(drm_, kCrtcXPropName, plane_props) &&
+      crtc_y_prop_.Initialize(drm_, kCrtcYPropName, plane_props) &&
+      crtc_w_prop_.Initialize(drm_, kCrtcWPropName, plane_props) &&
+      crtc_h_prop_.Initialize(drm_, kCrtcHPropName, plane_props) &&
+      src_x_prop_.Initialize(drm_, kSrcXPropName, plane_props) &&
+      src_y_prop_.Initialize(drm_, kSrcYPropName, plane_props) &&
+      src_w_prop_.Initialize(drm_, kSrcWPropName, plane_props) &&
+      src_h_prop_.Initialize(drm_, kSrcHPropName, plane_props);
+
+  if (!props_init) {
+    LOG(ERROR) << "Unable to get plane properties.";
+    return false;
+  }
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/hardware_display_plane.h b/ui/ozone/platform/dri/hardware_display_plane.h
new file mode 100644
index 0000000..dd79f76
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_plane.h
@@ -0,0 +1,77 @@
+// 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 UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_H_
+#define UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+
+#include "base/basictypes.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace ui {
+
+class DriWrapper;
+
+class HardwareDisplayPlane {
+ public:
+  HardwareDisplayPlane(DriWrapper* drm,
+                       drmModePropertySetPtr atomic_property_set,
+                       ScopedDrmPlanePtr plane);
+
+  ~HardwareDisplayPlane();
+
+  bool Initialize();
+
+  bool CanUseForCrtc(uint32_t crtc_id);
+  bool SetPlaneData(uint32_t crtc_id,
+                    uint32_t framebuffer,
+                    const gfx::Rect& crtc_rect,
+                    const gfx::Rect& src_rect);
+
+  bool in_use() const { return in_use_; }
+  void set_in_use(bool in_use) { in_use_ = in_use; }
+
+ private:
+  struct Property {
+    Property();
+    bool Initialize(DriWrapper* drm,
+                    const char* name,
+                    const ScopedDrmObjectPropertyPtr& plane_properties);
+    uint32_t id_;
+  };
+  // Object containing the connection to the graphics device and wraps the API
+  // calls to control it.
+  DriWrapper* drm_;
+
+  // Not owned.
+  drmModePropertySetPtr property_set_;
+
+  ScopedDrmPlanePtr plane_;
+  uint32_t plane_id_;
+  bool in_use_;
+
+  Property crtc_prop_;
+  Property fb_prop_;
+  Property crtc_x_prop_;
+  Property crtc_y_prop_;
+  Property crtc_w_prop_;
+  Property crtc_h_prop_;
+  Property src_x_prop_;
+  Property src_y_prop_;
+  Property src_w_prop_;
+  Property src_h_prop_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlane);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_H_
diff --git a/ui/ozone/platform/dri/hardware_display_plane_manager.cc b/ui/ozone/platform/dri/hardware_display_plane_manager.cc
new file mode 100644
index 0000000..0e9bc99
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_plane_manager.cc
@@ -0,0 +1,57 @@
+// 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 "ui/ozone/platform/dri/hardware_display_plane_manager.h"
+
+#include <drm.h>
+#include <errno.h>
+#include <xf86drm.h>
+
+#include "base/logging.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+HardwareDisplayPlaneManager::HardwareDisplayPlaneManager(DriWrapper* drm)
+    : drm_(drm), property_set_(NULL) {
+}
+
+HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() {
+  if (property_set_)
+    drmModePropertySetFree(property_set_);
+}
+
+bool HardwareDisplayPlaneManager::Initialize() {
+  ScopedDrmPlaneResPtr plane_resources(
+      drmModeGetPlaneResources(drm_->get_fd()));
+  if (!plane_resources) {
+    LOG(ERROR) << "Failed to get plane resources.";
+    return false;
+  }
+
+  property_set_ = drmModePropertySetAlloc();
+  if (!property_set_) {
+    LOG(ERROR) << "Failed to allocate property set.";
+    return false;
+  }
+
+  uint32_t num_planes = plane_resources->count_planes;
+  for (uint32_t i = 0; i < num_planes; ++i) {
+    ScopedDrmPlanePtr drm_plane(
+        drmModeGetPlane(drm_->get_fd(), plane_resources->planes[i]));
+    if (!drm_plane) {
+      LOG(ERROR) << "Failed to get plane " << i << ".";
+      return false;
+    }
+    scoped_ptr<HardwareDisplayPlane> plane(
+        new HardwareDisplayPlane(drm_, property_set_, drm_plane.Pass()));
+    if (plane->Initialize())
+      planes_.push_back(plane.release());
+  }
+
+  return true;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/hardware_display_plane_manager.h b/ui/ozone/platform/dri/hardware_display_plane_manager.h
new file mode 100644
index 0000000..b6862e3
--- /dev/null
+++ b/ui/ozone/platform/dri/hardware_display_plane_manager.h
@@ -0,0 +1,49 @@
+// 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 UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_MANAGER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+#include <xf86drmMode.h>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_vector.h"
+#include "ui/ozone/platform/dri/hardware_display_plane.h"
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+namespace gfx {
+class Rect;
+}  // namespace gfx
+
+namespace ui {
+
+class DriWrapper;
+
+class HardwareDisplayPlaneManager {
+ public:
+  HardwareDisplayPlaneManager(DriWrapper* drm);
+
+  ~HardwareDisplayPlaneManager();
+
+  bool Initialize();
+
+  const ScopedVector<HardwareDisplayPlane>& get_planes() { return planes_; }
+
+ private:
+  // Object containing the connection to the graphics device and wraps the API
+  // calls to control it.
+  DriWrapper* drm_;
+
+  drmModePropertySetPtr property_set_;
+  ScopedVector<HardwareDisplayPlane> planes_;
+
+  DISALLOW_COPY_AND_ASSIGN(HardwareDisplayPlaneManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_HARDWARE_DISPLAY_PLANE_MANAGER_H_
diff --git a/ui/ozone/platform/dri/native_display_delegate_dri.cc b/ui/ozone/platform/dri/native_display_delegate_dri.cc
new file mode 100644
index 0000000..6e06d2b
--- /dev/null
+++ b/ui/ozone/platform/dri/native_display_delegate_dri.cc
@@ -0,0 +1,336 @@
+// 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 "ui/ozone/platform/dri/native_display_delegate_dri.h"
+
+#include "base/bind.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/events/ozone/device/device_event.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/ozone/platform/dri/display_mode_dri.h"
+#include "ui/ozone/platform/dri/display_snapshot_dri.h"
+#include "ui/ozone/platform/dri/dri_console_buffer.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+
+namespace ui {
+
+namespace {
+
+const size_t kMaxDisplayCount = 2;
+
+const char kContentProtection[] = "Content Protection";
+
+struct ContentProtectionMapping {
+  const char* name;
+  HDCPState state;
+};
+
+const ContentProtectionMapping kContentProtectionStates[] = {
+    {"Undesired", HDCP_STATE_UNDESIRED},
+    {"Desired", HDCP_STATE_DESIRED},
+    {"Enabled", HDCP_STATE_ENABLED}};
+
+uint32_t GetContentProtectionValue(drmModePropertyRes* property,
+                                   HDCPState state) {
+  std::string name;
+  for (size_t i = 0; i < arraysize(kContentProtectionStates); ++i) {
+    if (kContentProtectionStates[i].state == state) {
+      name = kContentProtectionStates[i].name;
+      break;
+    }
+  }
+
+  for (int i = 0; i < property->count_enums; ++i)
+    if (name == property->enums[i].name)
+      return i;
+
+  NOTREACHED();
+  return 0;
+}
+
+class DisplaySnapshotComparator {
+ public:
+  DisplaySnapshotComparator(const DisplaySnapshotDri* snapshot)
+      : snapshot_(snapshot) {}
+
+  bool operator()(const DisplaySnapshotDri* other) const {
+    if (snapshot_->connector() == other->connector() &&
+        snapshot_->crtc() == other->crtc())
+      return true;
+
+    return false;
+  }
+
+ private:
+  const DisplaySnapshotDri* snapshot_;
+};
+
+}  // namespace
+
+NativeDisplayDelegateDri::NativeDisplayDelegateDri(
+    DriWrapper* dri,
+    ScreenManager* screen_manager,
+    DeviceManager* device_manager)
+    : dri_(dri),
+      screen_manager_(screen_manager),
+      device_manager_(device_manager) {
+  // TODO(dnicoara): Remove when async display configuration is supported.
+  screen_manager_->ForceInitializationOfPrimaryDisplay();
+}
+
+NativeDisplayDelegateDri::~NativeDisplayDelegateDri() {
+  if (device_manager_)
+    device_manager_->RemoveObserver(this);
+}
+
+DisplaySnapshot* NativeDisplayDelegateDri::FindDisplaySnapshot(int64_t id) {
+  for (size_t i = 0; i < cached_displays_.size(); ++i)
+    if (cached_displays_[i]->display_id() == id)
+      return cached_displays_[i];
+
+  return NULL;
+}
+
+const DisplayMode* NativeDisplayDelegateDri::FindDisplayMode(
+    const gfx::Size& size,
+    bool is_interlaced,
+    float refresh_rate) {
+  for (size_t i = 0; i < cached_modes_.size(); ++i)
+    if (cached_modes_[i]->size() == size &&
+        cached_modes_[i]->is_interlaced() == is_interlaced &&
+        cached_modes_[i]->refresh_rate() == refresh_rate)
+      return cached_modes_[i];
+
+  return NULL;
+}
+
+void NativeDisplayDelegateDri::Initialize() {
+  if (device_manager_)
+    device_manager_->AddObserver(this);
+
+  ScopedVector<HardwareDisplayControllerInfo> displays =
+      GetAvailableDisplayControllerInfos(dri_->get_fd());
+
+  // By default all displays show the same console buffer.
+  console_buffer_.reset(
+      new DriConsoleBuffer(dri_, displays[0]->crtc()->buffer_id));
+  if (!console_buffer_->Initialize()) {
+    VLOG(1) << "Failed to initialize console buffer";
+    console_buffer_.reset();
+  } else {
+    // Clear the console buffer such that restarting Chrome will show a
+    // predetermined background.
+    //
+    // Black was chosen since Chrome's first buffer paints start with a black
+    // background.
+    console_buffer_->canvas()->clear(SK_ColorBLACK);
+  }
+}
+
+void NativeDisplayDelegateDri::GrabServer() {
+}
+
+void NativeDisplayDelegateDri::UngrabServer() {
+}
+
+void NativeDisplayDelegateDri::SyncWithServer() {
+}
+
+void NativeDisplayDelegateDri::SetBackgroundColor(uint32_t color_argb) {
+  if (console_buffer_)
+    console_buffer_->canvas()->clear(color_argb);
+}
+
+void NativeDisplayDelegateDri::ForceDPMSOn() {
+  for (size_t i = 0; i < cached_displays_.size(); ++i) {
+    DisplaySnapshotDri* dri_output = cached_displays_[i];
+    if (dri_output->dpms_property())
+      dri_->SetProperty(dri_output->connector(),
+                        dri_output->dpms_property()->prop_id,
+                        DRM_MODE_DPMS_ON);
+  }
+}
+
+std::vector<DisplaySnapshot*> NativeDisplayDelegateDri::GetDisplays() {
+  ScopedVector<DisplaySnapshotDri> old_displays(cached_displays_.Pass());
+  cached_modes_.clear();
+
+  ScopedVector<HardwareDisplayControllerInfo> displays =
+      GetAvailableDisplayControllerInfos(dri_->get_fd());
+  for (size_t i = 0;
+       i < displays.size() && cached_displays_.size() < kMaxDisplayCount;
+       ++i) {
+    DisplaySnapshotDri* display = new DisplaySnapshotDri(
+        dri_, displays[i]->connector(), displays[i]->crtc(), i);
+    cached_displays_.push_back(display);
+    cached_modes_.insert(
+        cached_modes_.end(), display->modes().begin(), display->modes().end());
+  }
+
+  NotifyScreenManager(cached_displays_.get(), old_displays.get());
+
+  std::vector<DisplaySnapshot*> generic_displays(cached_displays_.begin(),
+                                                 cached_displays_.end());
+  return generic_displays;
+}
+
+void NativeDisplayDelegateDri::AddMode(const DisplaySnapshot& output,
+                                       const DisplayMode* mode) {
+}
+
+bool NativeDisplayDelegateDri::Configure(const DisplaySnapshot& output,
+                                         const DisplayMode* mode,
+                                         const gfx::Point& origin) {
+  const DisplaySnapshotDri& dri_output =
+      static_cast<const DisplaySnapshotDri&>(output);
+
+  VLOG(1) << "DRM configuring: crtc=" << dri_output.crtc()
+          << " connector=" << dri_output.connector()
+          << " origin=" << origin.ToString()
+          << " size=" << (mode ? mode->size().ToString() : "0x0");
+
+  if (mode) {
+    if (!screen_manager_->ConfigureDisplayController(
+            dri_output.crtc(),
+            dri_output.connector(),
+            origin,
+            static_cast<const DisplayModeDri*>(mode)->mode_info())) {
+      VLOG(1) << "Failed to configure: crtc=" << dri_output.crtc()
+              << " connector=" << dri_output.connector();
+      return false;
+    }
+  } else {
+    if (!screen_manager_->DisableDisplayController(dri_output.crtc())) {
+      VLOG(1) << "Failed to disable crtc=" << dri_output.crtc();
+      return false;
+    }
+  }
+
+  return true;
+}
+
+void NativeDisplayDelegateDri::CreateFrameBuffer(const gfx::Size& size) {
+}
+
+bool NativeDisplayDelegateDri::GetHDCPState(const DisplaySnapshot& output,
+                                            HDCPState* state) {
+  const DisplaySnapshotDri& dri_output =
+      static_cast<const DisplaySnapshotDri&>(output);
+
+  ScopedDrmConnectorPtr connector(dri_->GetConnector(dri_output.connector()));
+  if (!connector) {
+    LOG(ERROR) << "Failed to get connector " << dri_output.connector();
+    return false;
+  }
+
+  ScopedDrmPropertyPtr hdcp_property(
+      dri_->GetProperty(connector.get(), kContentProtection));
+  if (!hdcp_property) {
+    LOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
+    return false;
+  }
+
+  DCHECK_LT(static_cast<int>(hdcp_property->prop_id), connector->count_props);
+  int hdcp_state_idx = connector->prop_values[hdcp_property->prop_id];
+  DCHECK_LT(hdcp_state_idx, hdcp_property->count_enums);
+
+  std::string name(hdcp_property->enums[hdcp_state_idx].name);
+  for (size_t i = 0; i < arraysize(kContentProtectionStates); ++i) {
+    if (name == kContentProtectionStates[i].name) {
+      *state = kContentProtectionStates[i].state;
+      VLOG(3) << "HDCP state: " << *state << " (" << name << ")";
+      return true;
+    }
+  }
+
+  LOG(ERROR) << "Unknown content protection value '" << name << "'";
+  return false;
+}
+
+bool NativeDisplayDelegateDri::SetHDCPState(const DisplaySnapshot& output,
+                                            HDCPState state) {
+  const DisplaySnapshotDri& dri_output =
+      static_cast<const DisplaySnapshotDri&>(output);
+
+  ScopedDrmConnectorPtr connector(dri_->GetConnector(dri_output.connector()));
+  if (!connector) {
+    LOG(ERROR) << "Failed to get connector " << dri_output.connector();
+    return false;
+  }
+
+  ScopedDrmPropertyPtr hdcp_property(
+      dri_->GetProperty(connector.get(), kContentProtection));
+  if (!hdcp_property) {
+    LOG(ERROR) << "'" << kContentProtection << "' property doesn't exist.";
+    return false;
+  }
+
+  return dri_->SetProperty(
+      dri_output.connector(),
+      hdcp_property->prop_id,
+      GetContentProtectionValue(hdcp_property.get(), state));
+}
+
+std::vector<ui::ColorCalibrationProfile>
+NativeDisplayDelegateDri::GetAvailableColorCalibrationProfiles(
+    const ui::DisplaySnapshot& output) {
+  NOTIMPLEMENTED();
+  return std::vector<ui::ColorCalibrationProfile>();
+}
+
+bool NativeDisplayDelegateDri::SetColorCalibrationProfile(
+    const ui::DisplaySnapshot& output,
+    ui::ColorCalibrationProfile new_profile) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void NativeDisplayDelegateDri::AddObserver(NativeDisplayObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void NativeDisplayDelegateDri::RemoveObserver(NativeDisplayObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void NativeDisplayDelegateDri::OnDeviceEvent(const DeviceEvent& event) {
+  if (event.device_type() != DeviceEvent::DISPLAY)
+    return;
+
+  if (event.action_type() == DeviceEvent::CHANGE) {
+    VLOG(1) << "Got display changed event";
+    FOR_EACH_OBSERVER(
+        NativeDisplayObserver, observers_, OnConfigurationChanged());
+  }
+}
+
+void NativeDisplayDelegateDri::NotifyScreenManager(
+    const std::vector<DisplaySnapshotDri*>& new_displays,
+    const std::vector<DisplaySnapshotDri*>& old_displays) const {
+  for (size_t i = 0; i < old_displays.size(); ++i) {
+    const std::vector<DisplaySnapshotDri*>::const_iterator it =
+        std::find_if(new_displays.begin(),
+                     new_displays.end(),
+                     DisplaySnapshotComparator(old_displays[i]));
+
+    if (it == new_displays.end())
+      screen_manager_->RemoveDisplayController(old_displays[i]->crtc());
+  }
+
+  for (size_t i = 0; i < new_displays.size(); ++i) {
+    const std::vector<DisplaySnapshotDri*>::const_iterator it =
+        std::find_if(old_displays.begin(),
+                     old_displays.end(),
+                     DisplaySnapshotComparator(new_displays[i]));
+
+    if (it == old_displays.end())
+      screen_manager_->AddDisplayController(new_displays[i]->crtc(),
+                                            new_displays[i]->connector());
+  }
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/native_display_delegate_dri.h b/ui/ozone/platform/dri/native_display_delegate_dri.h
new file mode 100644
index 0000000..70abb7b
--- /dev/null
+++ b/ui/ozone/platform/dri/native_display_delegate_dri.h
@@ -0,0 +1,87 @@
+// 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 UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_DRI_H_
+#define UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_DRI_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/events/ozone/device/device_event_observer.h"
+
+namespace ui {
+
+class DeviceManager;
+class DisplaySnapshotDri;
+class DriConsoleBuffer;
+class DriWrapper;
+class ScreenManager;
+
+class NativeDisplayDelegateDri : public NativeDisplayDelegate,
+                                 DeviceEventObserver {
+ public:
+  NativeDisplayDelegateDri(DriWrapper* dri,
+                           ScreenManager* screen_manager,
+                           DeviceManager* device_manager);
+  virtual ~NativeDisplayDelegateDri();
+
+  DisplaySnapshot* FindDisplaySnapshot(int64_t id);
+  const DisplayMode* FindDisplayMode(const gfx::Size& size,
+                                     bool is_interlaced,
+                                     float refresh_rate);
+
+  // NativeDisplayDelegate overrides:
+  virtual void Initialize() OVERRIDE;
+  virtual void GrabServer() OVERRIDE;
+  virtual void UngrabServer() OVERRIDE;
+  virtual void SyncWithServer() OVERRIDE;
+  virtual void SetBackgroundColor(uint32_t color_argb) OVERRIDE;
+  virtual void ForceDPMSOn() OVERRIDE;
+  virtual std::vector<DisplaySnapshot*> GetDisplays() OVERRIDE;
+  virtual void AddMode(const DisplaySnapshot& output,
+                       const DisplayMode* mode) OVERRIDE;
+  virtual bool Configure(const DisplaySnapshot& output,
+                         const DisplayMode* mode,
+                         const gfx::Point& origin) OVERRIDE;
+  virtual void CreateFrameBuffer(const gfx::Size& size) OVERRIDE;
+  virtual bool GetHDCPState(const DisplaySnapshot& output,
+                            HDCPState* state) OVERRIDE;
+  virtual bool SetHDCPState(const DisplaySnapshot& output,
+                            HDCPState state) OVERRIDE;
+  virtual std::vector<ui::ColorCalibrationProfile>
+      GetAvailableColorCalibrationProfiles(
+          const ui::DisplaySnapshot& output) OVERRIDE;
+  virtual bool SetColorCalibrationProfile(
+      const ui::DisplaySnapshot& output,
+      ui::ColorCalibrationProfile new_profile) OVERRIDE;
+  virtual void AddObserver(NativeDisplayObserver* observer) OVERRIDE;
+  virtual void RemoveObserver(NativeDisplayObserver* observer) OVERRIDE;
+
+  // DeviceEventObserver overrides:
+  virtual void OnDeviceEvent(const DeviceEvent& event) OVERRIDE;
+
+ private:
+  // Notify ScreenManager of all the displays that were present before the
+  // update but are gone after the update.
+  void NotifyScreenManager(
+      const std::vector<DisplaySnapshotDri*>& new_displays,
+      const std::vector<DisplaySnapshotDri*>& old_displays) const;
+
+  DriWrapper* dri_;                // Not owned.
+  ScreenManager* screen_manager_;  // Not owned.
+  DeviceManager* device_manager_;  // Not owned.
+  scoped_ptr<DriConsoleBuffer> console_buffer_;
+  // Modes can be shared between different displays, so we need to keep track
+  // of them independently for cleanup.
+  ScopedVector<const DisplayMode> cached_modes_;
+  ScopedVector<DisplaySnapshotDri> cached_displays_;
+  ObserverList<NativeDisplayObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateDri);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_DRI_H_
diff --git a/ui/ozone/platform/dri/native_display_delegate_proxy.cc b/ui/ozone/platform/dri/native_display_delegate_proxy.cc
new file mode 100644
index 0000000..f6cbba3
--- /dev/null
+++ b/ui/ozone/platform/dri/native_display_delegate_proxy.cc
@@ -0,0 +1,162 @@
+// 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 "ui/ozone/platform/dri/native_display_delegate_proxy.h"
+
+#include "base/logging.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/events/ozone/device/device_event.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/ozone/common/display_snapshot_proxy.h"
+#include "ui/ozone/common/display_util.h"
+#include "ui/ozone/common/gpu/ozone_gpu_messages.h"
+#include "ui/ozone/platform/dri/gpu_platform_support_host_gbm.h"
+
+namespace ui {
+
+NativeDisplayDelegateProxy::NativeDisplayDelegateProxy(
+    GpuPlatformSupportHostGbm* proxy,
+    DeviceManager* device_manager)
+    : proxy_(proxy), device_manager_(device_manager) {
+  proxy_->RegisterHandler(this);
+}
+
+NativeDisplayDelegateProxy::~NativeDisplayDelegateProxy() {
+  if (device_manager_)
+    device_manager_->RemoveObserver(this);
+
+  proxy_->UnregisterHandler(this);
+}
+
+void NativeDisplayDelegateProxy::Initialize() {
+  if (device_manager_)
+    device_manager_->AddObserver(this);
+}
+
+void NativeDisplayDelegateProxy::GrabServer() {
+}
+
+void NativeDisplayDelegateProxy::UngrabServer() {
+}
+
+void NativeDisplayDelegateProxy::SyncWithServer() {
+}
+
+void NativeDisplayDelegateProxy::SetBackgroundColor(uint32_t color_argb) {
+  NOTIMPLEMENTED();
+}
+
+void NativeDisplayDelegateProxy::ForceDPMSOn() {
+  proxy_->Send(new OzoneGpuMsg_ForceDPMSOn());
+}
+
+std::vector<DisplaySnapshot*> NativeDisplayDelegateProxy::GetDisplays() {
+  return displays_.get();
+}
+
+void NativeDisplayDelegateProxy::AddMode(const DisplaySnapshot& output,
+                                         const DisplayMode* mode) {
+}
+
+bool NativeDisplayDelegateProxy::Configure(const DisplaySnapshot& output,
+                                           const DisplayMode* mode,
+                                           const gfx::Point& origin) {
+  // TODO(dnicoara) Should handle an asynchronous response.
+  if (mode)
+    proxy_->Send(new OzoneGpuMsg_ConfigureNativeDisplay(
+        output.display_id(), GetDisplayModeParams(*mode), origin));
+  else
+    proxy_->Send(new OzoneGpuMsg_DisableNativeDisplay(output.display_id()));
+
+  return true;
+}
+
+void NativeDisplayDelegateProxy::CreateFrameBuffer(const gfx::Size& size) {
+}
+
+bool NativeDisplayDelegateProxy::GetHDCPState(const DisplaySnapshot& output,
+                                              HDCPState* state) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+bool NativeDisplayDelegateProxy::SetHDCPState(const DisplaySnapshot& output,
+                                              HDCPState state) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+std::vector<ColorCalibrationProfile>
+NativeDisplayDelegateProxy::GetAvailableColorCalibrationProfiles(
+    const DisplaySnapshot& output) {
+  NOTIMPLEMENTED();
+  return std::vector<ColorCalibrationProfile>();
+}
+
+bool NativeDisplayDelegateProxy::SetColorCalibrationProfile(
+    const DisplaySnapshot& output,
+    ColorCalibrationProfile new_profile) {
+  NOTIMPLEMENTED();
+  return false;
+}
+
+void NativeDisplayDelegateProxy::AddObserver(NativeDisplayObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void NativeDisplayDelegateProxy::RemoveObserver(
+    NativeDisplayObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void NativeDisplayDelegateProxy::OnDeviceEvent(const DeviceEvent& event) {
+  if (event.device_type() != DeviceEvent::DISPLAY)
+    return;
+
+  if (event.action_type() == DeviceEvent::CHANGE) {
+    VLOG(1) << "Got display changed event";
+    proxy_->Send(new OzoneGpuMsg_RefreshNativeDisplays(
+        std::vector<DisplaySnapshot_Params>()));
+  }
+}
+
+void NativeDisplayDelegateProxy::OnChannelEstablished(int host_id,
+                                                      IPC::Sender* sender) {
+  std::vector<DisplaySnapshot_Params> display_params;
+  for (size_t i = 0; i < displays_.size(); ++i)
+    display_params.push_back(GetDisplaySnapshotParams(*displays_[i]));
+
+  // Force an initial configure such that the browser process can get the actual
+  // state. Pass in the current display state since the GPU process may have
+  // crashed and we want to re-synchronize the state between processes.
+  proxy_->Send(new OzoneGpuMsg_RefreshNativeDisplays(display_params));
+}
+
+void NativeDisplayDelegateProxy::OnChannelDestroyed(int host_id) {
+}
+
+bool NativeDisplayDelegateProxy::OnMessageReceived(
+    const IPC::Message& message) {
+  bool handled = true;
+
+  IPC_BEGIN_MESSAGE_MAP(NativeDisplayDelegateProxy, message)
+  IPC_MESSAGE_HANDLER(OzoneHostMsg_UpdateNativeDisplays, OnUpdateNativeDisplays)
+  IPC_MESSAGE_UNHANDLED(handled = false)
+  IPC_END_MESSAGE_MAP()
+
+  return handled;
+}
+
+void NativeDisplayDelegateProxy::OnUpdateNativeDisplays(
+    const std::vector<DisplaySnapshot_Params>& displays) {
+  displays_.clear();
+  for (size_t i = 0; i < displays.size(); ++i)
+    displays_.push_back(new DisplaySnapshotProxy(displays[i]));
+
+  FOR_EACH_OBSERVER(
+      NativeDisplayObserver, observers_, OnConfigurationChanged());
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/native_display_delegate_proxy.h b/ui/ozone/platform/dri/native_display_delegate_proxy.h
new file mode 100644
index 0000000..ae80f4e
--- /dev/null
+++ b/ui/ozone/platform/dri/native_display_delegate_proxy.h
@@ -0,0 +1,81 @@
+// 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 UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_PROXY_H_
+#define UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_PROXY_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/events/ozone/device/device_event_observer.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+
+namespace ui {
+
+class DeviceManager;
+class GpuPlatformSupportHostGbm;
+
+struct DisplaySnapshot_Params;
+
+class NativeDisplayDelegateProxy : public NativeDisplayDelegate,
+                                   public DeviceEventObserver,
+                                   public GpuPlatformSupportHost {
+ public:
+  NativeDisplayDelegateProxy(GpuPlatformSupportHostGbm* proxy,
+                             DeviceManager* device_manager);
+  virtual ~NativeDisplayDelegateProxy();
+
+  // NativeDisplayDelegate overrides:
+  virtual void Initialize() OVERRIDE;
+  virtual void GrabServer() OVERRIDE;
+  virtual void UngrabServer() OVERRIDE;
+  virtual void SyncWithServer() OVERRIDE;
+  virtual void SetBackgroundColor(uint32_t color_argb) OVERRIDE;
+  virtual void ForceDPMSOn() OVERRIDE;
+  virtual std::vector<DisplaySnapshot*> GetDisplays() OVERRIDE;
+  virtual void AddMode(const DisplaySnapshot& output,
+                       const DisplayMode* mode) OVERRIDE;
+  virtual bool Configure(const DisplaySnapshot& output,
+                         const DisplayMode* mode,
+                         const gfx::Point& origin) OVERRIDE;
+  virtual void CreateFrameBuffer(const gfx::Size& size) OVERRIDE;
+  virtual bool GetHDCPState(const DisplaySnapshot& output,
+                            HDCPState* state) OVERRIDE;
+  virtual bool SetHDCPState(const DisplaySnapshot& output,
+                            HDCPState state) OVERRIDE;
+  virtual std::vector<ColorCalibrationProfile>
+      GetAvailableColorCalibrationProfiles(
+          const DisplaySnapshot& output) OVERRIDE;
+  virtual bool SetColorCalibrationProfile(
+      const DisplaySnapshot& output,
+      ColorCalibrationProfile new_profile) OVERRIDE;
+  virtual void AddObserver(NativeDisplayObserver* observer) OVERRIDE;
+  virtual void RemoveObserver(NativeDisplayObserver* observer) OVERRIDE;
+
+  // DeviceEventObserver overrides:
+  virtual void OnDeviceEvent(const DeviceEvent& event) OVERRIDE;
+
+  // GpuPlatformSupportHost:
+  virtual void OnChannelEstablished(int host_id, IPC::Sender* sender) OVERRIDE;
+  virtual void OnChannelDestroyed(int host_id) OVERRIDE;
+
+  // IPC::Listener overrides:
+  virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+  void OnUpdateNativeDisplays(
+      const std::vector<DisplaySnapshot_Params>& displays);
+
+  GpuPlatformSupportHostGbm* proxy_;  // Not owned.
+  DeviceManager* device_manager_;     // Not owned.
+  ScopedVector<DisplaySnapshot> displays_;
+  ObserverList<NativeDisplayObserver> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateProxy);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_NATIVE_DISPLAY_DELEGATE_PROXY_H_
diff --git a/ui/ozone/platform/dri/ozone_platform_dri.cc b/ui/ozone/platform/dri/ozone_platform_dri.cc
new file mode 100644
index 0000000..e4a1324
--- /dev/null
+++ b/ui/ozone/platform/dri/ozone_platform_dri.cc
@@ -0,0 +1,118 @@
+// Copyright 2013 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 "ui/ozone/platform/dri/ozone_platform_dri.h"
+
+#include "base/at_exit.h"
+#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/cursor_delegate_evdev.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/dri_cursor.h"
+#include "ui/ozone/platform/dri/dri_surface_factory.h"
+#include "ui/ozone/platform/dri/dri_window.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_impl.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/dri_window_manager.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/native_display_delegate_dri.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/platform/dri/virtual_terminal_manager.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+namespace {
+
+const char kDefaultGraphicsCardPath[] = "/dev/dri/card0";
+
+// OzonePlatform for Linux DRI (Direct Rendering Infrastructure)
+//
+// This platform is Linux without any display server (no X, wayland, or
+// anything). This means chrome alone owns the display and input devices.
+class OzonePlatformDri : public OzonePlatform {
+ public:
+  OzonePlatformDri()
+      : vt_manager_(new VirtualTerminalManager()),
+        dri_(new DriWrapper(kDefaultGraphicsCardPath)),
+        buffer_generator_(new DriBufferGenerator(dri_.get())),
+        screen_manager_(new ScreenManager(dri_.get(),
+                                          buffer_generator_.get())),
+        device_manager_(CreateDeviceManager()) {
+    base::AtExitManager::RegisterTask(
+        base::Bind(&base::DeletePointer<OzonePlatformDri>, this));
+  }
+  virtual ~OzonePlatformDri() {}
+
+  // OzonePlatform:
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
+    return surface_factory_ozone_.get();
+  }
+  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
+    return cursor_factory_ozone_.get();
+  }
+  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
+    return NULL;  // no GPU support
+  }
+  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
+    return NULL;  // no GPU support
+  }
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) OVERRIDE {
+    scoped_ptr<DriWindow> platform_window(new DriWindow(
+        delegate,
+        bounds,
+        scoped_ptr<DriWindowDelegate>(new DriWindowDelegateImpl(
+            window_manager_->NextAcceleratedWidget(), screen_manager_.get())),
+        event_factory_ozone_.get(),
+        &window_delegate_manager_,
+        window_manager_.get()));
+    platform_window->Initialize();
+    return platform_window.PassAs<PlatformWindow>();
+  }
+  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
+      OVERRIDE {
+    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateDri(
+        dri_.get(), screen_manager_.get(), device_manager_.get()));
+  }
+  virtual void InitializeUI() OVERRIDE {
+    dri_->Initialize();
+    surface_factory_ozone_.reset(new DriSurfaceFactory(
+        dri_.get(), screen_manager_.get(), &window_delegate_manager_));
+    cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
+    window_manager_.reset(new DriWindowManager(surface_factory_ozone_.get()));
+    event_factory_ozone_.reset(new EventFactoryEvdev(window_manager_->cursor(),
+                                                     device_manager_.get()));
+    if (surface_factory_ozone_->InitializeHardware() !=
+        DriSurfaceFactory::INITIALIZED)
+      LOG(FATAL) << "failed to initialize display hardware";
+
+  }
+
+  virtual void InitializeGPU() OVERRIDE {}
+
+ private:
+  scoped_ptr<VirtualTerminalManager> vt_manager_;
+  scoped_ptr<DriWrapper> dri_;
+  scoped_ptr<DriBufferGenerator> buffer_generator_;
+  scoped_ptr<ScreenManager> screen_manager_;
+  scoped_ptr<DeviceManager> device_manager_;
+
+  scoped_ptr<DriSurfaceFactory> surface_factory_ozone_;
+  scoped_ptr<BitmapCursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+
+  scoped_ptr<DriWindowManager> window_manager_;
+  DriWindowDelegateManager window_delegate_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformDri);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformDri() { return new OzonePlatformDri; }
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/ozone_platform_dri.h b/ui/ozone/platform/dri/ozone_platform_dri.h
new file mode 100644
index 0000000..a63a054
--- /dev/null
+++ b/ui/ozone/platform/dri/ozone_platform_dri.h
@@ -0,0 +1,17 @@
+// Copyright 2013 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 UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_DRI_H_
+#define UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_DRI_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformDri();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_DRI_H_
diff --git a/ui/ozone/platform/dri/ozone_platform_gbm.cc b/ui/ozone/platform/dri/ozone_platform_gbm.cc
new file mode 100644
index 0000000..8c467a4
--- /dev/null
+++ b/ui/ozone/platform/dri/ozone_platform_gbm.cc
@@ -0,0 +1,191 @@
+// 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 "ui/ozone/platform/dri/ozone_platform_gbm.h"
+
+#include <dlfcn.h>
+#include <gbm.h>
+#include <stdlib.h>
+
+#include "base/at_exit.h"
+#include "base/command_line.h"
+#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/ozone/platform/dri/dri_cursor.h"
+#include "ui/ozone/platform/dri/dri_window.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_manager.h"
+#include "ui/ozone/platform/dri/dri_window_delegate_proxy.h"
+#include "ui/ozone/platform/dri/dri_window_manager.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+#include "ui/ozone/platform/dri/gbm_buffer.h"
+#include "ui/ozone/platform/dri/gbm_surface.h"
+#include "ui/ozone/platform/dri/gbm_surface_factory.h"
+#include "ui/ozone/platform/dri/gpu_platform_support_gbm.h"
+#include "ui/ozone/platform/dri/gpu_platform_support_host_gbm.h"
+#include "ui/ozone/platform/dri/native_display_delegate_dri.h"
+#include "ui/ozone/platform/dri/native_display_delegate_proxy.h"
+#include "ui/ozone/platform/dri/scanout_buffer.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/platform/dri/virtual_terminal_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+namespace {
+
+const char kDefaultGraphicsCardPath[] = "/dev/dri/card0";
+
+class GbmBufferGenerator : public ScanoutBufferGenerator {
+ public:
+  GbmBufferGenerator(DriWrapper* dri)
+      : dri_(dri),
+        glapi_lib_(dlopen("libglapi.so.0", RTLD_LAZY | RTLD_GLOBAL)),
+        device_(gbm_create_device(dri_->get_fd())) {
+    if (!device_)
+      LOG(FATAL) << "Unable to initialize gbm for " << kDefaultGraphicsCardPath;
+  }
+  virtual ~GbmBufferGenerator() {
+    gbm_device_destroy(device_);
+    if (glapi_lib_)
+      dlclose(glapi_lib_);
+  }
+
+  gbm_device* device() const { return device_; }
+
+  virtual scoped_refptr<ScanoutBuffer> Create(const gfx::Size& size) OVERRIDE {
+    return GbmBuffer::CreateBuffer(
+        dri_, device_, SurfaceFactoryOzone::RGBA_8888, size, true);
+  }
+
+ protected:
+  DriWrapper* dri_;  // Not owned.
+
+  // HACK: gbm drivers have broken linkage
+  void *glapi_lib_;
+
+  gbm_device* device_;
+
+  DISALLOW_COPY_AND_ASSIGN(GbmBufferGenerator);
+};
+
+class OzonePlatformGbm : public OzonePlatform {
+ public:
+  OzonePlatformGbm(bool use_surfaceless) : use_surfaceless_(use_surfaceless) {
+    base::AtExitManager::RegisterTask(
+        base::Bind(&base::DeletePointer<OzonePlatformGbm>, this));
+  }
+  virtual ~OzonePlatformGbm() {}
+
+  // OzonePlatform:
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
+    return surface_factory_ozone_.get();
+  }
+  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
+    return cursor_factory_ozone_.get();
+  }
+  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
+    return gpu_platform_support_.get();
+  }
+  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
+    return gpu_platform_support_host_.get();
+  }
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) OVERRIDE {
+    scoped_ptr<DriWindow> platform_window(
+        new DriWindow(delegate,
+                      bounds,
+                      scoped_ptr<DriWindowDelegate>(new DriWindowDelegateProxy(
+                          window_manager_->NextAcceleratedWidget(),
+                          gpu_platform_support_host_.get())),
+                      event_factory_ozone_.get(),
+                      ui_window_delegate_manager_.get(),
+                      window_manager_.get()));
+    platform_window->Initialize();
+    return platform_window.PassAs<PlatformWindow>();
+  }
+  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
+      OVERRIDE {
+    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateProxy(
+        gpu_platform_support_host_.get(), device_manager_.get()));
+  }
+  virtual void InitializeUI() OVERRIDE {
+    vt_manager_.reset(new VirtualTerminalManager());
+    ui_window_delegate_manager_.reset(new DriWindowDelegateManager());
+    // Needed since the browser process creates the accelerated widgets and that
+    // happens through SFO.
+    surface_factory_ozone_.reset(new GbmSurfaceFactory(use_surfaceless_));
+    device_manager_ = CreateDeviceManager();
+    gpu_platform_support_host_.reset(new GpuPlatformSupportHostGbm());
+    cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
+    window_manager_.reset(
+        new DriWindowManager(gpu_platform_support_host_.get()));
+    event_factory_ozone_.reset(new EventFactoryEvdev(window_manager_->cursor(),
+                                                     device_manager_.get()));
+  }
+
+  virtual void InitializeGPU() OVERRIDE {
+    dri_.reset(new DriWrapper(kDefaultGraphicsCardPath));
+    dri_->Initialize();
+    buffer_generator_.reset(new GbmBufferGenerator(dri_.get()));
+    screen_manager_.reset(new ScreenManager(dri_.get(),
+                                            buffer_generator_.get()));
+    gpu_window_delegate_manager_.reset(new DriWindowDelegateManager());
+    if (!surface_factory_ozone_)
+      surface_factory_ozone_.reset(new GbmSurfaceFactory(use_surfaceless_));
+
+    surface_factory_ozone_->InitializeGpu(dri_.get(),
+                                          buffer_generator_->device(),
+                                          screen_manager_.get(),
+                                          gpu_window_delegate_manager_.get());
+    gpu_platform_support_.reset(new GpuPlatformSupportGbm(
+        surface_factory_ozone_.get(),
+        gpu_window_delegate_manager_.get(),
+        screen_manager_.get(),
+        scoped_ptr<NativeDisplayDelegateDri>(new NativeDisplayDelegateDri(
+            dri_.get(), screen_manager_.get(), NULL))));
+    if (surface_factory_ozone_->InitializeHardware() !=
+        DriSurfaceFactory::INITIALIZED)
+      LOG(FATAL) << "failed to initialize display hardware";
+  }
+
+ private:
+  bool use_surfaceless_;
+  scoped_ptr<VirtualTerminalManager> vt_manager_;
+  scoped_ptr<DriWrapper> dri_;
+  scoped_ptr<GbmBufferGenerator> buffer_generator_;
+  scoped_ptr<ScreenManager> screen_manager_;
+  scoped_ptr<DeviceManager> device_manager_;
+
+  scoped_ptr<GbmSurfaceFactory> surface_factory_ozone_;
+  scoped_ptr<BitmapCursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+
+  scoped_ptr<GpuPlatformSupportGbm> gpu_platform_support_;
+  scoped_ptr<GpuPlatformSupportHostGbm> gpu_platform_support_host_;
+
+  scoped_ptr<DriWindowDelegateManager> gpu_window_delegate_manager_;
+  // TODO(dnicoara) Once we have a mock channel for the software path the window
+  // can own the delegates on the browser side. Remove this then.
+  scoped_ptr<DriWindowDelegateManager> ui_window_delegate_manager_;
+
+  // Browser side object only.
+  scoped_ptr<DriWindowManager> window_manager_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformGbm);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformGbm() {
+  CommandLine* cmd = CommandLine::ForCurrentProcess();
+  return new OzonePlatformGbm(cmd->HasSwitch(switches::kOzoneUseSurfaceless));
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/ozone_platform_gbm.h b/ui/ozone/platform/dri/ozone_platform_gbm.h
new file mode 100644
index 0000000..47f5ca7
--- /dev/null
+++ b/ui/ozone/platform/dri/ozone_platform_gbm.h
@@ -0,0 +1,17 @@
+// 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 UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_GBM_H_
+#define UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_GBM_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformGbm();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_OZONE_PLATFORM_GBM_H_
diff --git a/ui/ozone/platform/dri/scanout_buffer.h b/ui/ozone/platform/dri/scanout_buffer.h
new file mode 100644
index 0000000..4475c8d
--- /dev/null
+++ b/ui/ozone/platform/dri/scanout_buffer.h
@@ -0,0 +1,42 @@
+// 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 UI_OZONE_PLATFORM_DRI_SCANOUT_BUFFER_H_
+#define UI_OZONE_PLATFORM_DRI_SCANOUT_BUFFER_H_
+
+#include <stdint.h>
+
+#include "base/memory/ref_counted.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+// Abstraction for a DRM buffer that can be scanned-out of.
+class ScanoutBuffer : public base::RefCounted<ScanoutBuffer> {
+ public:
+  // ID allocated by the KMS API when the buffer is registered (via the handle).
+  virtual uint32_t GetFramebufferId() const = 0;
+
+  // Handle for the buffer. This is received when allocating the buffer.
+  virtual uint32_t GetHandle() const = 0;
+
+  // Size of the buffer.
+  virtual gfx::Size GetSize() const = 0;
+
+ protected:
+  virtual ~ScanoutBuffer() {}
+
+  friend class base::RefCounted<ScanoutBuffer>;
+};
+
+class ScanoutBufferGenerator {
+ public:
+  virtual ~ScanoutBufferGenerator() {}
+
+  virtual scoped_refptr<ScanoutBuffer> Create(const gfx::Size& size) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_SCANOUT_BUFFER_H_
diff --git a/ui/ozone/platform/dri/scoped_drm_types.cc b/ui/ozone/platform/dri/scoped_drm_types.cc
new file mode 100644
index 0000000..0f1c6b3
--- /dev/null
+++ b/ui/ozone/platform/dri/scoped_drm_types.cc
@@ -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.
+
+#include "ui/ozone/platform/dri/scoped_drm_types.h"
+
+#include <xf86drmMode.h>
+
+namespace ui {
+
+void DrmResourcesDeleter::operator()(drmModeRes* resources) const {
+  drmModeFreeResources(resources);
+}
+
+void DrmConnectorDeleter::operator()(drmModeConnector* connector) const {
+  drmModeFreeConnector(connector);
+}
+
+void DrmCrtcDeleter::operator()(drmModeCrtc* crtc) const {
+  drmModeFreeCrtc(crtc);
+}
+
+void DrmEncoderDeleter::operator()(drmModeEncoder* encoder) const {
+  drmModeFreeEncoder(encoder);
+}
+
+void DrmObjectPropertiesDeleter::operator()(
+    drmModeObjectProperties* properties) const {
+  drmModeFreeObjectProperties(properties);
+}
+
+void DrmPlaneDeleter::operator()(drmModePlane* plane) const {
+  drmModeFreePlane(plane);
+}
+
+void DrmPlaneResDeleter::operator()(drmModePlaneRes* plane) const {
+  drmModeFreePlaneResources(plane);
+}
+
+void DrmPropertyDeleter::operator()(drmModePropertyRes* property) const {
+  drmModeFreeProperty(property);
+}
+
+void DrmPropertyBlobDeleter::operator()(
+    drmModePropertyBlobRes* property) const {
+  drmModeFreePropertyBlob(property);
+}
+
+void DrmFramebufferDeleter::operator()(drmModeFB* framebuffer) const {
+  drmModeFreeFB(framebuffer);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/scoped_drm_types.h b/ui/ozone/platform/dri/scoped_drm_types.h
new file mode 100644
index 0000000..e651232
--- /dev/null
+++ b/ui/ozone/platform/dri/scoped_drm_types.h
@@ -0,0 +1,69 @@
+// 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 UI_OZONE_PLATFORM_DRI_SCOPED_DRM_TYPES_H_
+#define UI_OZONE_PLATFORM_DRI_SCOPED_DRM_TYPES_H_
+
+#include "base/memory/scoped_ptr.h"
+
+typedef struct _drmModeConnector drmModeConnector;
+typedef struct _drmModeCrtc drmModeCrtc;
+typedef struct _drmModeEncoder drmModeEncoder;
+typedef struct _drmModeFB drmModeFB;
+typedef struct _drmModeObjectProperties drmModeObjectProperties;
+typedef struct _drmModePlane drmModePlane;
+typedef struct _drmModePlaneRes drmModePlaneRes;
+typedef struct _drmModeProperty drmModePropertyRes;
+typedef struct _drmModePropertyBlob drmModePropertyBlobRes;
+typedef struct _drmModeRes drmModeRes;
+
+namespace ui {
+
+struct DrmResourcesDeleter {
+  void operator()(drmModeRes* resources) const;
+};
+struct DrmConnectorDeleter {
+  void operator()(drmModeConnector* connector) const;
+};
+struct DrmCrtcDeleter {
+  void operator()(drmModeCrtc* crtc) const;
+};
+struct DrmEncoderDeleter {
+  void operator()(drmModeEncoder* encoder) const;
+};
+struct DrmObjectPropertiesDeleter {
+  void operator()(drmModeObjectProperties* properties) const;
+};
+struct DrmPlaneDeleter {
+  void operator()(drmModePlane* plane) const;
+};
+struct DrmPlaneResDeleter {
+  void operator()(drmModePlaneRes* plane_res) const;
+};
+struct DrmPropertyDeleter {
+  void operator()(drmModePropertyRes* property) const;
+};
+struct DrmPropertyBlobDeleter {
+  void operator()(drmModePropertyBlobRes* property) const;
+};
+struct DrmFramebufferDeleter {
+  void operator()(drmModeFB* framebuffer) const;
+};
+
+typedef scoped_ptr<drmModeRes, DrmResourcesDeleter> ScopedDrmResourcesPtr;
+typedef scoped_ptr<drmModeConnector, DrmConnectorDeleter> ScopedDrmConnectorPtr;
+typedef scoped_ptr<drmModeCrtc, DrmCrtcDeleter> ScopedDrmCrtcPtr;
+typedef scoped_ptr<drmModeEncoder, DrmEncoderDeleter> ScopedDrmEncoderPtr;
+typedef scoped_ptr<drmModeObjectProperties, DrmObjectPropertiesDeleter>
+    ScopedDrmObjectPropertyPtr;
+typedef scoped_ptr<drmModePlane, DrmPlaneDeleter> ScopedDrmPlanePtr;
+typedef scoped_ptr<drmModePlaneRes, DrmPlaneResDeleter> ScopedDrmPlaneResPtr;
+typedef scoped_ptr<drmModePropertyRes, DrmPropertyDeleter> ScopedDrmPropertyPtr;
+typedef scoped_ptr<drmModePropertyBlobRes, DrmPropertyBlobDeleter>
+ScopedDrmPropertyBlobPtr;
+typedef scoped_ptr<drmModeFB, DrmFramebufferDeleter> ScopedDrmFramebufferPtr;
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_SCOPED_DRM_TYPES_H_
diff --git a/ui/ozone/platform/dri/screen_manager.cc b/ui/ozone/platform/dri/screen_manager.cc
new file mode 100644
index 0000000..6330387
--- /dev/null
+++ b/ui/ozone/platform/dri/screen_manager.cc
@@ -0,0 +1,215 @@
+// 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 "ui/ozone/platform/dri/screen_manager.h"
+
+#include <xf86drmMode.h>
+
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/ozone/platform/dri/crtc_state.h"
+#include "ui/ozone/platform/dri/dri_util.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/scanout_buffer.h"
+
+namespace ui {
+
+ScreenManager::ScreenManager(DriWrapper* dri,
+                             ScanoutBufferGenerator* buffer_generator)
+    : dri_(dri), buffer_generator_(buffer_generator) {
+}
+
+ScreenManager::~ScreenManager() {
+}
+
+void ScreenManager::AddDisplayController(uint32_t crtc, uint32_t connector) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
+  // TODO(dnicoara): Turn this into a DCHECK when async display configuration is
+  // properly supported. (When there can't be a race between forcing initial
+  // display configuration in ScreenManager and NativeDisplayDelegate creating
+  // the display controllers.)
+  if (it != controllers_.end()) {
+    LOG(WARNING) << "Display controller (crtc=" << crtc << ") already present.";
+    return;
+  }
+
+  controllers_.push_back(new HardwareDisplayController(
+      dri_, scoped_ptr<CrtcState>(new CrtcState(dri_, crtc, connector))));
+}
+
+void ScreenManager::RemoveDisplayController(uint32_t crtc) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
+  if (it != controllers_.end()) {
+    bool is_mirrored = (*it)->IsMirrored();
+    (*it)->RemoveCrtc(crtc);
+    if (!is_mirrored)
+      controllers_.erase(it);
+  }
+}
+
+bool ScreenManager::ConfigureDisplayController(uint32_t crtc,
+                                               uint32_t connector,
+                                               const gfx::Point& origin,
+                                               const drmModeModeInfo& mode) {
+  gfx::Rect modeset_bounds(
+      origin.x(), origin.y(), mode.hdisplay, mode.vdisplay);
+  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
+  DCHECK(controllers_.end() != it) << "Display controller (crtc=" << crtc
+                                   << ") doesn't exist.";
+
+  HardwareDisplayController* controller = *it;
+  controller = *it;
+  // If nothing changed just enable the controller. Note, we perform an exact
+  // comparison on the mode since the refresh rate may have changed.
+  if (SameMode(mode, controller->get_mode()) &&
+      origin == controller->origin() && !controller->IsDisabled())
+    return controller->Enable();
+
+  // Either the mode or the location of the display changed, so exit mirror
+  // mode and configure the display independently. If the caller still wants
+  // mirror mode, subsequent calls configuring the other controllers will
+  // restore mirror mode.
+  if (controller->IsMirrored()) {
+    controller =
+        new HardwareDisplayController(dri_, controller->RemoveCrtc(crtc));
+    controllers_.push_back(controller);
+    it = controllers_.end() - 1;
+  }
+
+  HardwareDisplayControllers::iterator mirror =
+      FindActiveDisplayControllerByLocation(modeset_bounds);
+  // Handle mirror mode.
+  if (mirror != controllers_.end() && it != mirror)
+    return HandleMirrorMode(it, mirror, crtc, connector);
+
+  return ModesetDisplayController(controller, origin, mode);
+}
+
+bool ScreenManager::DisableDisplayController(uint32_t crtc) {
+  HardwareDisplayControllers::iterator it = FindDisplayController(crtc);
+  if (it != controllers_.end()) {
+    if ((*it)->IsMirrored()) {
+      HardwareDisplayController* controller =
+          new HardwareDisplayController(dri_, (*it)->RemoveCrtc(crtc));
+      controllers_.push_back(controller);
+    }
+
+    (*it)->Disable();
+    return true;
+  }
+
+  LOG(ERROR) << "Failed to find display controller crtc=" << crtc;
+  return false;
+}
+
+base::WeakPtr<HardwareDisplayController> ScreenManager::GetDisplayController(
+    const gfx::Rect& bounds) {
+  // TODO(dnicoara): Remove hack once TestScreen uses a simple Ozone display
+  // configuration reader and ScreenManager is called from there to create the
+  // one display needed by the content_shell target.
+  if (controllers_.empty())
+    ForceInitializationOfPrimaryDisplay();
+
+  HardwareDisplayControllers::iterator it =
+      FindActiveDisplayControllerByLocation(bounds);
+  if (it != controllers_.end())
+    return (*it)->AsWeakPtr();
+
+  return base::WeakPtr<HardwareDisplayController>();
+}
+
+ScreenManager::HardwareDisplayControllers::iterator
+ScreenManager::FindDisplayController(uint32_t crtc) {
+  for (HardwareDisplayControllers::iterator it = controllers_.begin();
+       it != controllers_.end();
+       ++it) {
+    if ((*it)->HasCrtc(crtc))
+      return it;
+  }
+
+  return controllers_.end();
+}
+
+ScreenManager::HardwareDisplayControllers::iterator
+ScreenManager::FindActiveDisplayControllerByLocation(const gfx::Rect& bounds) {
+  for (HardwareDisplayControllers::iterator it = controllers_.begin();
+       it != controllers_.end();
+       ++it) {
+    gfx::Rect controller_bounds((*it)->origin(), (*it)->GetModeSize());
+    // We don't perform a strict check since content_shell will have windows
+    // smaller than the display size.
+    if (controller_bounds.Contains(bounds) && !(*it)->IsDisabled())
+      return it;
+  }
+
+  return controllers_.end();
+}
+
+void ScreenManager::ForceInitializationOfPrimaryDisplay() {
+  LOG(WARNING) << "Forcing initialization of primary display.";
+  ScopedVector<HardwareDisplayControllerInfo> displays =
+      GetAvailableDisplayControllerInfos(dri_->get_fd());
+
+  DCHECK_NE(0u, displays.size());
+
+  ScopedDrmPropertyPtr dpms(
+      dri_->GetProperty(displays[0]->connector(), "DPMS"));
+  if (dpms)
+    dri_->SetProperty(displays[0]->connector()->connector_id,
+                      dpms->prop_id,
+                      DRM_MODE_DPMS_ON);
+
+  AddDisplayController(displays[0]->crtc()->crtc_id,
+                       displays[0]->connector()->connector_id);
+  ConfigureDisplayController(displays[0]->crtc()->crtc_id,
+                             displays[0]->connector()->connector_id,
+                             gfx::Point(),
+                             displays[0]->connector()->modes[0]);
+}
+
+bool ScreenManager::ModesetDisplayController(
+    HardwareDisplayController* controller,
+    const gfx::Point& origin,
+    const drmModeModeInfo& mode) {
+  controller->set_origin(origin);
+  // Create a surface suitable for the current controller.
+  scoped_refptr<ScanoutBuffer> buffer =
+      buffer_generator_->Create(gfx::Size(mode.hdisplay, mode.vdisplay));
+
+  if (!buffer.get()) {
+    LOG(ERROR) << "Failed to create scanout buffer";
+    return false;
+  }
+
+  if (!controller->Modeset(OverlayPlane(buffer), mode)) {
+    LOG(ERROR) << "Failed to modeset controller";
+    return false;
+  }
+
+  return true;
+}
+
+bool ScreenManager::HandleMirrorMode(
+    HardwareDisplayControllers::iterator original,
+    HardwareDisplayControllers::iterator mirror,
+    uint32_t crtc,
+    uint32_t connector) {
+  (*mirror)->AddCrtc((*original)->RemoveCrtc(crtc));
+  if ((*mirror)->Enable()) {
+    controllers_.erase(original);
+    return true;
+  }
+
+  LOG(ERROR) << "Failed to switch to mirror mode";
+
+  // When things go wrong revert back to the previous configuration since
+  // it is expected that the configuration would not have changed if
+  // things fail.
+  (*original)->AddCrtc((*mirror)->RemoveCrtc(crtc));
+  (*original)->Enable();
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/screen_manager.h b/ui/ozone/platform/dri/screen_manager.h
new file mode 100644
index 0000000..72278db
--- /dev/null
+++ b/ui/ozone/platform/dri/screen_manager.h
@@ -0,0 +1,100 @@
+// 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 UI_OZONE_PLATFORM_DRI_SCREEN_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRI_SCREEN_MANAGER_H_
+
+#include "base/macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+
+typedef struct _drmModeModeInfo drmModeModeInfo;
+
+namespace gfx {
+class Point;
+class Rect;
+class Size;
+}  // namespace gfx
+
+namespace ui {
+
+class DriWrapper;
+class ScanoutBufferGenerator;
+
+// Responsible for keeping track of active displays and configuring them.
+class ScreenManager {
+ public:
+  ScreenManager(DriWrapper* dri, ScanoutBufferGenerator* surface_generator);
+  virtual ~ScreenManager();
+
+  // Register a display controller. This must be called before trying to
+  // configure it.
+  void AddDisplayController(uint32_t crtc, uint32_t connector);
+
+  // Remove a display controller from the list of active controllers. The
+  // controller is removed since it was disconnected.
+  void RemoveDisplayController(uint32_t crtc);
+
+  // Configure a display controller. The display controller is identified by
+  // (|crtc|, |connector|) and the controller is modeset using |mode|.
+  bool ConfigureDisplayController(uint32_t crtc,
+                                  uint32_t connector,
+                                  const gfx::Point& origin,
+                                  const drmModeModeInfo& mode);
+
+  // Disable the display controller identified by |crtc|. Note, the controller
+  // may still be connected, so this does not remove the controller.
+  bool DisableDisplayController(uint32_t crtc);
+
+  // Returns a reference to the display controller configured to display within
+  // |bounds|.
+  // This returns a weak reference since the display controller may be destroyed
+  // at any point in time, but the changes are propagated to the compositor much
+  // later (Compositor owns SurfaceOzone*, which is responsible for updating the
+  // display surface).
+  base::WeakPtr<HardwareDisplayController> GetDisplayController(
+      const gfx::Rect& bounds);
+
+  // On non CrOS builds there is no display configurator to look-up available
+  // displays and initialize the HDCs. In such cases this is called internally
+  // to initialize a display.
+  virtual void ForceInitializationOfPrimaryDisplay();
+
+ private:
+  typedef ScopedVector<HardwareDisplayController> HardwareDisplayControllers;
+
+  // Returns an iterator into |controllers_| for the controller identified by
+  // (|crtc|, |connector|).
+  HardwareDisplayControllers::iterator FindDisplayController(uint32_t crtc);
+
+  // Returns an iterator into |controllers_| for the controller located at
+  // |origin|.
+  HardwareDisplayControllers::iterator FindActiveDisplayControllerByLocation(
+      const gfx::Rect& bounds);
+
+  // Perform modesetting in |controller| using |origin| and |mode|.
+  bool ModesetDisplayController(HardwareDisplayController* controller,
+                                const gfx::Point& origin,
+                                const drmModeModeInfo& mode);
+
+  // Tries to set the controller identified by (|crtc|, |connector|) to mirror
+  // those in |mirror|. |original| is an iterator to the HDC where the
+  // controller is currently present.
+  bool HandleMirrorMode(HardwareDisplayControllers::iterator original,
+                        HardwareDisplayControllers::iterator mirror,
+                        uint32_t crtc,
+                        uint32_t connector);
+
+  DriWrapper* dri_;  // Not owned.
+  ScanoutBufferGenerator* buffer_generator_;  // Not owned.
+  // List of display controllers (active and disabled).
+  HardwareDisplayControllers controllers_;
+
+  DISALLOW_COPY_AND_ASSIGN(ScreenManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_SCREEN_MANAGER_H_
diff --git a/ui/ozone/platform/dri/screen_manager_unittest.cc b/ui/ozone/platform/dri/screen_manager_unittest.cc
new file mode 100644
index 0000000..8eda243
--- /dev/null
+++ b/ui/ozone/platform/dri/screen_manager_unittest.cc
@@ -0,0 +1,240 @@
+// 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 "testing/gtest/include/gtest/gtest.h"
+#include "ui/ozone/platform/dri/dri_buffer.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+#include "ui/ozone/platform/dri/screen_manager.h"
+#include "ui/ozone/platform/dri/test/mock_dri_wrapper.h"
+
+namespace {
+
+// Create a basic mode for a 6x4 screen.
+const drmModeModeInfo kDefaultMode =
+    {0, 6, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, {'\0'}};
+
+const uint32_t kPrimaryCrtc = 1;
+const uint32_t kPrimaryConnector = 2;
+const uint32_t kSecondaryCrtc = 3;
+const uint32_t kSecondaryConnector = 4;
+
+class MockScreenManager : public ui::ScreenManager {
+ public:
+  MockScreenManager(ui::DriWrapper* dri,
+                    ui::ScanoutBufferGenerator* buffer_generator)
+      : ScreenManager(dri, buffer_generator), dri_(dri) {}
+
+  virtual void ForceInitializationOfPrimaryDisplay() OVERRIDE {}
+
+ private:
+  ui::DriWrapper* dri_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockScreenManager);
+};
+
+}  // namespace
+
+class ScreenManagerTest : public testing::Test {
+ public:
+  ScreenManagerTest() {}
+  virtual ~ScreenManagerTest() {}
+
+  gfx::Rect GetPrimaryBounds() const {
+    return gfx::Rect(0, 0, kDefaultMode.hdisplay, kDefaultMode.vdisplay);
+  }
+
+  // Secondary is in extended mode, right-of primary.
+  gfx::Rect GetSecondaryBounds() const {
+    return gfx::Rect(
+        kDefaultMode.hdisplay, 0, kDefaultMode.hdisplay, kDefaultMode.vdisplay);
+  }
+
+  virtual void SetUp() OVERRIDE {
+    dri_.reset(new ui::MockDriWrapper(3));
+    buffer_generator_.reset(new ui::DriBufferGenerator(dri_.get()));
+    screen_manager_.reset(new MockScreenManager(
+        dri_.get(), buffer_generator_.get()));
+  }
+  virtual void TearDown() OVERRIDE {
+    screen_manager_.reset();
+    dri_.reset();
+  }
+
+ protected:
+  scoped_ptr<ui::MockDriWrapper> dri_;
+  scoped_ptr<ui::DriBufferGenerator> buffer_generator_;
+  scoped_ptr<MockScreenManager> screen_manager_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScreenManagerTest);
+};
+
+TEST_F(ScreenManagerTest, CheckWithNoControllers) {
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckWithValidController) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  base::WeakPtr<ui::HardwareDisplayController> controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+
+  EXPECT_TRUE(controller);
+  EXPECT_TRUE(controller->HasCrtc(kPrimaryCrtc));
+}
+
+TEST_F(ScreenManagerTest, CheckWithInvalidBounds) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckForSecondValidController) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->AddDisplayController(kSecondaryCrtc, kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetSecondaryBounds().origin(),
+                                              kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckControllerAfterItIsRemoved) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  base::WeakPtr<ui::HardwareDisplayController> controller =
+      screen_manager_->GetDisplayController(GetPrimaryBounds());
+
+  EXPECT_TRUE(controller);
+  screen_manager_->RemoveDisplayController(kPrimaryCrtc);
+  EXPECT_FALSE(controller);
+}
+
+TEST_F(ScreenManagerTest, CheckDuplicateConfiguration) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckChangingMode) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  drmModeModeInfo new_mode = kDefaultMode;
+  new_mode.vdisplay = 10;
+  screen_manager_->ConfigureDisplayController(
+      kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), new_mode);
+
+  gfx::Rect new_bounds(0, 0, new_mode.hdisplay, new_mode.vdisplay);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(new_bounds));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+  drmModeModeInfo mode =
+      screen_manager_->GetDisplayController(new_bounds)->get_mode();
+  EXPECT_EQ(new_mode.vdisplay, mode.vdisplay);
+  EXPECT_EQ(new_mode.hdisplay, mode.hdisplay);
+}
+
+TEST_F(ScreenManagerTest, CheckForControllersInMirroredMode) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->AddDisplayController(kSecondaryCrtc, kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, CheckMirrorModeTransitions) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->AddDisplayController(kSecondaryCrtc, kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetSecondaryBounds().origin(),
+                                              kDefaultMode);
+
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetSecondaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
+
+TEST_F(ScreenManagerTest, MonitorGoneInMirrorMode) {
+  screen_manager_->AddDisplayController(kPrimaryCrtc, kPrimaryConnector);
+  screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                              kPrimaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+  screen_manager_->AddDisplayController(kSecondaryCrtc, kSecondaryConnector);
+  screen_manager_->ConfigureDisplayController(kSecondaryCrtc,
+                                              kSecondaryConnector,
+                                              GetPrimaryBounds().origin(),
+                                              kDefaultMode);
+
+  screen_manager_->RemoveDisplayController(kSecondaryCrtc);
+  EXPECT_TRUE(
+      screen_manager_->ConfigureDisplayController(kPrimaryCrtc,
+                                                  kPrimaryConnector,
+                                                  GetPrimaryBounds().origin(),
+                                                  kDefaultMode));
+  EXPECT_TRUE(screen_manager_->GetDisplayController(GetPrimaryBounds()));
+  EXPECT_FALSE(screen_manager_->GetDisplayController(GetSecondaryBounds()));
+}
diff --git a/ui/ozone/platform/dri/test/mock_dri_wrapper.cc b/ui/ozone/platform/dri/test/mock_dri_wrapper.cc
new file mode 100644
index 0000000..1ac5215
--- /dev/null
+++ b/ui/ozone/platform/dri/test/mock_dri_wrapper.cc
@@ -0,0 +1,160 @@
+// 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 "ui/ozone/platform/dri/test/mock_dri_wrapper.h"
+
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "ui/ozone/platform/dri/hardware_display_controller.h"
+
+namespace ui {
+
+namespace {
+
+template<class Object> Object* DrmAllocator() {
+  return static_cast<Object*>(drmMalloc(sizeof(Object)));
+}
+
+}  // namespace
+
+MockDriWrapper::MockDriWrapper(int fd)
+    : DriWrapper(""),
+      get_crtc_call_count_(0),
+      set_crtc_call_count_(0),
+      restore_crtc_call_count_(0),
+      add_framebuffer_call_count_(0),
+      remove_framebuffer_call_count_(0),
+      page_flip_call_count_(0),
+      overlay_flip_call_count_(0),
+      set_crtc_expectation_(true),
+      add_framebuffer_expectation_(true),
+      page_flip_expectation_(true),
+      create_dumb_buffer_expectation_(true),
+      current_framebuffer_(0) {
+  fd_ = fd;
+}
+
+MockDriWrapper::~MockDriWrapper() {
+  fd_ = -1;
+}
+
+ScopedDrmCrtcPtr MockDriWrapper::GetCrtc(uint32_t crtc_id) {
+  get_crtc_call_count_++;
+  return ScopedDrmCrtcPtr(DrmAllocator<drmModeCrtc>());
+}
+
+bool MockDriWrapper::SetCrtc(uint32_t crtc_id,
+                             uint32_t framebuffer,
+                             std::vector<uint32_t> connectors,
+                             drmModeModeInfo* mode) {
+  current_framebuffer_ = framebuffer;
+  set_crtc_call_count_++;
+  return set_crtc_expectation_;
+}
+
+bool MockDriWrapper::SetCrtc(drmModeCrtc* crtc,
+                             std::vector<uint32_t> connectors) {
+  restore_crtc_call_count_++;
+  return true;
+}
+
+ScopedDrmConnectorPtr MockDriWrapper::GetConnector(uint32_t connector_id) {
+  return ScopedDrmConnectorPtr(DrmAllocator<drmModeConnector>());
+}
+
+bool MockDriWrapper::AddFramebuffer(uint32_t width,
+                                    uint32_t height,
+                                    uint8_t depth,
+                                    uint8_t bpp,
+                                    uint32_t stride,
+                                    uint32_t handle,
+                                    uint32_t* framebuffer) {
+  add_framebuffer_call_count_++;
+  *framebuffer = add_framebuffer_call_count_;
+  return add_framebuffer_expectation_;
+}
+
+bool MockDriWrapper::RemoveFramebuffer(uint32_t framebuffer) {
+  remove_framebuffer_call_count_++;
+  return true;
+}
+
+bool MockDriWrapper::PageFlip(uint32_t crtc_id,
+                              uint32_t framebuffer,
+                              void* data) {
+  page_flip_call_count_++;
+  current_framebuffer_ = framebuffer;
+  controllers_.push(static_cast<ui::HardwareDisplayController*>(data));
+  return page_flip_expectation_;
+}
+
+bool MockDriWrapper::PageFlipOverlay(uint32_t crtc_id,
+                                     uint32_t framebuffer,
+                                     const gfx::Rect& location,
+                                     const gfx::RectF& source,
+                                     int overlay_plane) {
+  overlay_flip_call_count_++;
+  return true;
+}
+
+ScopedDrmPropertyPtr MockDriWrapper::GetProperty(drmModeConnector* connector,
+                                                 const char* name) {
+  return ScopedDrmPropertyPtr(DrmAllocator<drmModePropertyRes>());
+}
+
+bool MockDriWrapper::SetProperty(uint32_t connector_id,
+                                 uint32_t property_id,
+                                 uint64_t value) {
+  return true;
+}
+
+ScopedDrmPropertyBlobPtr MockDriWrapper::GetPropertyBlob(
+    drmModeConnector* connector,
+    const char* name) {
+  return ScopedDrmPropertyBlobPtr(DrmAllocator<drmModePropertyBlobRes>());
+}
+
+bool MockDriWrapper::SetCursor(uint32_t crtc_id,
+                               uint32_t handle,
+                               const gfx::Size& size) {
+  return true;
+}
+
+bool MockDriWrapper::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
+  return true;
+}
+
+void MockDriWrapper::HandleEvent(drmEventContext& event) {
+  CHECK(!controllers_.empty());
+  controllers_.front()->OnPageFlipEvent(0, 0, 0);
+  controllers_.pop();
+}
+
+bool MockDriWrapper::CreateDumbBuffer(const SkImageInfo& info,
+                                      uint32_t* handle,
+                                      uint32_t* stride,
+                                      void** pixels) {
+  if (!create_dumb_buffer_expectation_)
+    return false;
+
+  *handle = 0;
+  *stride = info.minRowBytes();
+  *pixels = new char[info.getSafeSize(*stride)];
+  buffers_.push_back(
+      skia::AdoptRef(SkSurface::NewRasterDirect(info, *pixels, *stride)));
+  buffers_.back()->getCanvas()->clear(SK_ColorBLACK);
+
+  return true;
+}
+
+void MockDriWrapper::DestroyDumbBuffer(const SkImageInfo& info,
+                                       uint32_t handle,
+                                       uint32_t stride,
+                                       void* pixels) {
+  delete[] static_cast<char*>(pixels);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/test/mock_dri_wrapper.h b/ui/ozone/platform/dri/test/mock_dri_wrapper.h
new file mode 100644
index 0000000..9ffb868
--- /dev/null
+++ b/ui/ozone/platform/dri/test/mock_dri_wrapper.h
@@ -0,0 +1,123 @@
+// 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 UI_OZONE_PLATFORM_DRI_TEST_MOCK_DRI_WRAPPER_H_
+#define UI_OZONE_PLATFORM_DRI_TEST_MOCK_DRI_WRAPPER_H_
+
+#include <queue>
+#include <vector>
+
+#include "skia/ext/refptr.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/ozone/platform/dri/dri_wrapper.h"
+
+namespace ui {
+
+class HardwareDisplayController;
+
+// The real DriWrapper makes actual DRM calls which we can't use in unit tests.
+class MockDriWrapper : public ui::DriWrapper {
+ public:
+  MockDriWrapper(int fd);
+  virtual ~MockDriWrapper();
+
+  int get_get_crtc_call_count() const { return get_crtc_call_count_; }
+  int get_set_crtc_call_count() const { return set_crtc_call_count_; }
+  int get_restore_crtc_call_count() const { return restore_crtc_call_count_; }
+  int get_add_framebuffer_call_count() const {
+    return add_framebuffer_call_count_;
+  }
+  int get_remove_framebuffer_call_count() const {
+    return remove_framebuffer_call_count_;
+  }
+  int get_page_flip_call_count() const { return page_flip_call_count_; }
+  int get_overlay_flip_call_count() const { return overlay_flip_call_count_; }
+  void fail_init() { fd_ = -1; }
+  void set_set_crtc_expectation(bool state) { set_crtc_expectation_ = state; }
+  void set_page_flip_expectation(bool state) { page_flip_expectation_ = state; }
+  void set_add_framebuffer_expectation(bool state) {
+    add_framebuffer_expectation_ = state;
+  }
+  void set_create_dumb_buffer_expectation(bool state) {
+    create_dumb_buffer_expectation_ = state;
+  }
+
+  uint32_t current_framebuffer() const { return current_framebuffer_; }
+
+  const std::vector<skia::RefPtr<SkSurface> > buffers() const {
+    return buffers_;
+  }
+
+  // DriWrapper:
+  virtual ScopedDrmCrtcPtr GetCrtc(uint32_t crtc_id) OVERRIDE;
+  virtual bool SetCrtc(uint32_t crtc_id,
+                       uint32_t framebuffer,
+                       std::vector<uint32_t> connectors,
+                       drmModeModeInfo* mode) OVERRIDE;
+  virtual bool SetCrtc(drmModeCrtc* crtc,
+                       std::vector<uint32_t> connectors) OVERRIDE;
+  virtual ScopedDrmConnectorPtr GetConnector(uint32_t connector_id) OVERRIDE;
+  virtual bool AddFramebuffer(uint32_t width,
+                              uint32_t height,
+                              uint8_t depth,
+                              uint8_t bpp,
+                              uint32_t stride,
+                              uint32_t handle,
+                              uint32_t* framebuffer) OVERRIDE;
+  virtual bool RemoveFramebuffer(uint32_t framebuffer) OVERRIDE;
+  virtual bool PageFlip(uint32_t crtc_id,
+                        uint32_t framebuffer,
+                        void* data) OVERRIDE;
+  virtual bool PageFlipOverlay(uint32_t crtc_id,
+                               uint32_t framebuffer,
+                               const gfx::Rect& location,
+                               const gfx::RectF& source,
+                               int overlay_plane) OVERRIDE;
+  virtual ScopedDrmPropertyPtr GetProperty(drmModeConnector* connector,
+                                           const char* name) OVERRIDE;
+  virtual bool SetProperty(uint32_t connector_id,
+                           uint32_t property_id,
+                           uint64_t value) OVERRIDE;
+  virtual ScopedDrmPropertyBlobPtr GetPropertyBlob(drmModeConnector* connector,
+                                                   const char* name) OVERRIDE;
+  virtual bool SetCursor(uint32_t crtc_id,
+                         uint32_t handle,
+                         const gfx::Size& size) OVERRIDE;
+  virtual bool MoveCursor(uint32_t crtc_id, const gfx::Point& point) OVERRIDE;
+  virtual void HandleEvent(drmEventContext& event) OVERRIDE;
+  virtual bool CreateDumbBuffer(const SkImageInfo& info,
+                                uint32_t* handle,
+                                uint32_t* stride,
+                                void** pixels) OVERRIDE;
+  virtual void DestroyDumbBuffer(const SkImageInfo& info,
+                                 uint32_t handle,
+                                 uint32_t stride,
+                                 void* pixels) OVERRIDE;
+
+ private:
+  int get_crtc_call_count_;
+  int set_crtc_call_count_;
+  int restore_crtc_call_count_;
+  int add_framebuffer_call_count_;
+  int remove_framebuffer_call_count_;
+  int page_flip_call_count_;
+  int overlay_flip_call_count_;
+
+  bool set_crtc_expectation_;
+  bool add_framebuffer_expectation_;
+  bool page_flip_expectation_;
+  bool create_dumb_buffer_expectation_;
+
+  uint32_t current_framebuffer_;
+
+  std::vector<skia::RefPtr<SkSurface> > buffers_;
+
+  std::queue<HardwareDisplayController*> controllers_;
+
+  DISALLOW_COPY_AND_ASSIGN(MockDriWrapper);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_TEST_MOCK_DRI_WRAPPER_H_
diff --git a/ui/ozone/platform/dri/virtual_terminal_manager.cc b/ui/ozone/platform/dri/virtual_terminal_manager.cc
new file mode 100644
index 0000000..efd8350
--- /dev/null
+++ b/ui/ozone/platform/dri/virtual_terminal_manager.cc
@@ -0,0 +1,75 @@
+// 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 "ui/ozone/platform/dri/virtual_terminal_manager.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/kd.h>
+#include <linux/vt.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+
+namespace ui {
+
+namespace {
+
+const char kTTYDevice[] = "/dev/tty1";
+
+const int kVT = 1;
+
+}  // namespace
+
+VirtualTerminalManager::VirtualTerminalManager() {
+  // Use the current console.
+  fd_ = open(kTTYDevice, O_RDWR | O_CLOEXEC, 0);
+  if (fd_ < 0)
+    LOG(ERROR) << "Failed to open '" << kTTYDevice << "' " << strerror(errno);
+
+  if (ioctl(fd_, VT_ACTIVATE, kVT) || ioctl(fd_, VT_WAITACTIVE, kVT))
+    LOG(ERROR) << "Failed to switch to VT: " << kVT
+               << " error: " << strerror(errno);;
+
+  if (ioctl(fd_, KDGETMODE, &vt_mode_))
+    LOG(ERROR) << "Failed to get VT mode: " << strerror(errno);
+
+  if (ioctl(fd_, KDSETMODE, KD_GRAPHICS))
+    LOG(ERROR) << "Failed to set graphics mode: " << strerror(errno);
+
+  if (tcgetattr(fd_, &terminal_attributes_))
+    LOG(ERROR) << "Failed to get terminal attributes";
+
+  // Stop the TTY from processing keys and echo-ing them to the terminal.
+  struct termios raw_attributes = terminal_attributes_;
+  cfmakeraw(&raw_attributes);
+  raw_attributes.c_oflag |= OPOST;
+  if (tcsetattr(fd_, TCSANOW, &raw_attributes))
+    LOG(ERROR) << "Failed to set raw attributes";
+
+  if (ioctl(fd_, KDGKBMODE, &previous_keyboard_mode_))
+    LOG(ERROR) << "Failed to get keyboard mode";
+
+  if (ioctl(fd_, KDSKBMODE, K_OFF) && ioctl(fd_, KDSKBMODE, K_RAW))
+    LOG(ERROR) << "Failed to set keyboard mode";
+}
+
+VirtualTerminalManager::~VirtualTerminalManager() {
+  if (fd_ < 0)
+    return;
+
+  if (ioctl(fd_, KDSETMODE, &vt_mode_))
+    LOG(ERROR) << "Failed to restore VT mode";
+
+  if (ioctl(fd_, KDSKBMODE, previous_keyboard_mode_))
+    LOG(ERROR) << "Failed to restore keyboard mode";
+
+  if (tcsetattr(fd_, TCSANOW, &terminal_attributes_))
+    LOG(ERROR) << "Failed to restore terminal attributes";
+
+  close(fd_);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/dri/virtual_terminal_manager.h b/ui/ozone/platform/dri/virtual_terminal_manager.h
new file mode 100644
index 0000000..11af0b0
--- /dev/null
+++ b/ui/ozone/platform/dri/virtual_terminal_manager.h
@@ -0,0 +1,31 @@
+// 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 UI_OZONE_PLATFORM_DRI_VIRTUAL_TERMINAL_MANAGER_H_
+#define UI_OZONE_PLATFORM_DRI_VIRTUAL_TERMINAL_MANAGER_H_
+
+#include <termios.h>
+
+#include "base/basictypes.h"
+
+namespace ui {
+
+class VirtualTerminalManager {
+ public:
+  VirtualTerminalManager();
+  ~VirtualTerminalManager();
+
+ private:
+
+  int fd_;
+  int vt_mode_;
+  int previous_keyboard_mode_;
+  struct termios terminal_attributes_;
+
+  DISALLOW_COPY_AND_ASSIGN(VirtualTerminalManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_DRI_VIRTUAL_TERMINAL_MANAGER_H_
diff --git a/ui/ozone/platform/egltest/BUILD.gn b/ui/ozone/platform/egltest/BUILD.gn
new file mode 100644
index 0000000..04cae36
--- /dev/null
+++ b/ui/ozone/platform/egltest/BUILD.gn
@@ -0,0 +1,40 @@
+# 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("//tools/generate_library_loader/generate_library_loader.gni")
+
+source_set("egltest") {
+  sources = [
+    "ozone_platform_egltest.cc",
+    "ozone_platform_egltest.h",
+  ]
+
+  deps = [
+    ":eglplatform_shim",
+    "//base",
+    "//ui/events/ozone:events_ozone_evdev",
+    "//ui/gfx",
+  ]
+}
+
+generate_library_loader("eglplatform_shim") {
+  name = "LibeglplatformShimLoader"
+  output_h = "libeglplatform_shim.h"
+  output_cc = "libeglplatform_shim_loader.cc"
+  header = "\"ui/ozone/platform/egltest/eglplatform_shim.h\""
+
+  functions = [
+    "ShimQueryString",
+    "ShimInitialize",
+    "ShimTerminate",
+    "ShimCreateWindow",
+    "ShimQueryWindow",
+    "ShimDestroyWindow",
+    "ShimGetNativeDisplay",
+    "ShimGetNativeWindow",
+    "ShimReleaseNativeWindow",
+  ]
+}
+
+# TODO(spang): eglplatform_shim_x11 once support lands: http://crbug.com/380327
diff --git a/ui/ozone/platform/egltest/eglplatform_shim.h b/ui/ozone/platform/egltest/eglplatform_shim.h
new file mode 100644
index 0000000..ce7cce9
--- /dev/null
+++ b/ui/ozone/platform/egltest/eglplatform_shim.h
@@ -0,0 +1,58 @@
+// 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 __eglplatform_shim_h_
+#define __eglplatform_shim_h_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SHIM_EXPORT __attribute__((visibility("default")))
+
+// Simple integral native window identifier.
+// NB: Unlike EGLNativeWindowType, this will be shipped between processes.
+typedef int ShimNativeWindowId;
+#define SHIM_NO_WINDOW_ID ((ShimNativeWindowId)0)
+
+// Opaque versions of EGL types (as used by ozone).
+typedef intptr_t ShimEGLNativeDisplayType;
+typedef intptr_t ShimEGLNativeWindowType;
+
+// QueryString targets
+#define SHIM_EGL_LIBRARY 0x1001
+#define SHIM_GLES_LIBRARY 0x1002
+
+// CreateWindow / QueryWindow attributes
+#define SHIM_WINDOW_WIDTH 0x0001
+#define SHIM_WINDOW_HEIGHT 0x0002
+
+// Query global implementation information.
+SHIM_EXPORT const char* ShimQueryString(int name);
+
+// Init/terminate library.
+SHIM_EXPORT bool ShimInitialize(void);
+SHIM_EXPORT bool ShimTerminate(void);
+
+// Create window handle & query window properties (called from browser process).
+SHIM_EXPORT ShimNativeWindowId ShimCreateWindow(void);
+SHIM_EXPORT bool ShimQueryWindow(ShimNativeWindowId window_id,
+                                 int attribute,
+                                 int* value);
+SHIM_EXPORT bool ShimDestroyWindow(ShimNativeWindowId window_id);
+
+// Manage actual EGL platform objects (called from GPU process).
+SHIM_EXPORT ShimEGLNativeDisplayType ShimGetNativeDisplay(void);
+SHIM_EXPORT ShimEGLNativeWindowType
+    ShimGetNativeWindow(ShimNativeWindowId native_window_id);
+SHIM_EXPORT bool ShimReleaseNativeWindow(ShimEGLNativeWindowType native_window);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __eglplatform_shim_h */
diff --git a/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc b/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc
new file mode 100644
index 0000000..af1228d
--- /dev/null
+++ b/ui/ozone/platform/egltest/eglplatform_shim_xeleven.cc
@@ -0,0 +1,106 @@
+// 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 "ui/ozone/platform/egltest/eglplatform_shim.h"
+
+#include <string.h>
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/Xutil.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Display* g_display;
+
+const int kDefaultX = 0;
+const int kDefaultY = 0;
+const int kDefaultWidth = 800;
+const int kDefaultHeight = 600;
+const int kDefaultBorderWidth = 0;
+
+const char* ShimQueryString(int name) {
+  switch (name) {
+    case SHIM_EGL_LIBRARY:
+      return "libEGL.so.1";
+    case SHIM_GLES_LIBRARY:
+      return "libGLESv2.so.2";
+    default:
+      return NULL;
+  }
+}
+
+bool ShimInitialize(void) {
+  g_display = XOpenDisplay(NULL);
+  return g_display != NULL;
+}
+
+bool ShimTerminate(void) {
+  XCloseDisplay(g_display);
+  return true;
+}
+
+ShimNativeWindowId ShimCreateWindow(void) {
+  XSetWindowAttributes swa;
+  memset(&swa, 0, sizeof(swa));
+  swa.event_mask = 0;
+
+  Window window = XCreateWindow(g_display,
+                                DefaultRootWindow(g_display),
+                                kDefaultX,
+                                kDefaultY,
+                                kDefaultWidth,
+                                kDefaultHeight,
+                                kDefaultBorderWidth,
+                                CopyFromParent,
+                                InputOutput,
+                                CopyFromParent,
+                                CWEventMask,
+                                &swa);
+
+  XMapWindow(g_display, window);
+  XStoreName(g_display, window, "EGL test");
+  XFlush(g_display);
+
+  return window;
+}
+
+bool ShimQueryWindow(ShimNativeWindowId window_id, int attribute, int* value) {
+  XWindowAttributes window_attributes;
+  switch (attribute) {
+    case SHIM_WINDOW_WIDTH:
+      XGetWindowAttributes(g_display, window_id, &window_attributes);
+      *value = window_attributes.width;
+      return true;
+    case SHIM_WINDOW_HEIGHT:
+      XGetWindowAttributes(g_display, window_id, &window_attributes);
+      *value = window_attributes.height;
+      return true;
+    default:
+      return false;
+  }
+}
+
+bool ShimDestroyWindow(ShimNativeWindowId window_id) {
+  XDestroyWindow(g_display, window_id);
+  return true;
+}
+
+ShimEGLNativeDisplayType ShimGetNativeDisplay(void) {
+  return reinterpret_cast<ShimEGLNativeDisplayType>(g_display);
+}
+
+ShimEGLNativeWindowType ShimGetNativeWindow(
+    ShimNativeWindowId native_window_id) {
+  return native_window_id;
+}
+
+bool ShimReleaseNativeWindow(ShimEGLNativeWindowType native_window) {
+  return true;
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/ui/ozone/platform/egltest/egltest.gypi b/ui/ozone/platform/egltest/egltest.gypi
new file mode 100644
index 0000000..8191af5
--- /dev/null
+++ b/ui/ozone/platform/egltest/egltest.gypi
@@ -0,0 +1,108 @@
+# 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.
+
+{
+  'variables': {
+    'internal_ozone_platform_deps': [
+      'ozone_platform_egltest',
+    ],
+    'internal_ozone_platforms': [
+      'egltest'
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ozone_platform_egltest',
+      'type': 'static_library',
+      'defines': [
+        'OZONE_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../third_party/khronos/khronos.gyp:khronos_headers',
+        '../events/events.gyp:events',
+        '../events/ozone/events_ozone.gyp:events_ozone_evdev',
+        '../gfx/gfx.gyp:gfx',
+        'eglplatform_shim',
+      ],
+      'sources': [
+        'ozone_platform_egltest.cc',
+        'ozone_platform_egltest.h',
+      ],
+    },
+    {
+      'target_name': 'eglplatform_shim',
+      'type': 'static_library',
+      'dependencies': [
+        '../../third_party/khronos/khronos.gyp:khronos_headers',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(SHARED_INTERMEDIATE_DIR)',
+        ],
+      },
+      'include_dirs': [
+        '../../../..',
+      ],
+      'hard_dependency': 1,
+      'actions': [
+        {
+          'variables': {
+            'output_h': '<(SHARED_INTERMEDIATE_DIR)/library_loaders/libeglplatform_shim.h',
+            'output_cc': '<(INTERMEDIATE_DIR)/libeglplatform_shim_loader.cc',
+            'generator': '../../tools/generate_library_loader/generate_library_loader.py',
+          },
+          'action_name': 'generate_libeglplatform_shim_loader',
+          'inputs': [
+            '<(generator)',
+          ],
+          'outputs': [
+            '<(output_h)',
+            '<(output_cc)',
+          ],
+          'action': ['python',
+                     '<(generator)',
+                     '--name', 'LibeglplatformShimLoader',
+                     '--output-h', '<(output_h)',
+                     '--output-cc', '<(output_cc)',
+                     '--header', '"ui/ozone/platform/egltest/eglplatform_shim.h"',
+                     'ShimQueryString',
+                     'ShimInitialize',
+                     'ShimTerminate',
+                     'ShimCreateWindow',
+                     'ShimQueryWindow',
+                     'ShimDestroyWindow',
+                     'ShimGetNativeDisplay',
+                     'ShimGetNativeWindow',
+                     'ShimReleaseNativeWindow',
+          ],
+          'message': 'Generating libeglplatform_shim library loader',
+          'process_outputs_as_sources': 1,
+        },
+      ],
+    },
+  ],
+  'conditions': [
+    ['ozone_platform_ozonex == 1', {
+      'targets': [
+        {
+          'target_name': 'eglplatform_shim_x11',
+          'type': 'loadable_module',
+          'product_name': 'eglplatform_shim',
+          'product_extension': 'so.1',
+          'include_dirs': [
+            '../../../..',
+          ],
+          'dependencies': [
+            '../../build/linux/system.gyp:x11',
+          ],
+          'sources': [
+            'eglplatform_shim.h',
+            'eglplatform_shim_xeleven.cc',
+          ],
+        },
+      ],
+    }],
+  ],
+}
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.cc b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
new file mode 100644
index 0000000..46ee4ef
--- /dev/null
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.cc
@@ -0,0 +1,386 @@
+// 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 "ui/ozone/platform/egltest/ozone_platform_egltest.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "library_loaders/libeglplatform_shim.h"
+#include "third_party/khronos/EGL/egl.h"
+#include "ui/events/ozone/device/device_manager.h"
+#include "ui/events/ozone/evdev/event_factory_evdev.h"
+#include "ui/events/ozone/events_ozone.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+#include "ui/ozone/public/surface_ozone_egl.h"
+#include "ui/platform_window/platform_window.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+namespace {
+
+const char kEglplatformShim[] = "EGLPLATFORM_SHIM";
+const char kEglplatformShimDefault[] = "libeglplatform_shim.so.1";
+const char kDefaultEglSoname[] = "libEGL.so.1";
+const char kDefaultGlesSoname[] = "libGLESv2.so.2";
+
+// Get the library soname to load.
+std::string GetShimLibraryName() {
+  std::string library;
+  scoped_ptr<base::Environment> env(base::Environment::Create());
+  if (env->GetVar(kEglplatformShim, &library))
+    return library;
+  return kEglplatformShimDefault;
+}
+
+class EgltestWindow : public PlatformWindow, public PlatformEventDispatcher {
+ public:
+  EgltestWindow(PlatformWindowDelegate* delegate,
+                LibeglplatformShimLoader* eglplatform_shim,
+                EventFactoryEvdev* event_factory,
+                const gfx::Rect& bounds);
+  virtual ~EgltestWindow();
+
+  // PlatformWindow:
+  virtual gfx::Rect GetBounds() OVERRIDE;
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+  virtual void Show() OVERRIDE;
+  virtual void Hide() OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual void SetCapture() OVERRIDE;
+  virtual void ReleaseCapture() OVERRIDE;
+  virtual void ToggleFullscreen() OVERRIDE;
+  virtual void Maximize() OVERRIDE;
+  virtual void Minimize() OVERRIDE;
+  virtual void Restore() OVERRIDE;
+  virtual void SetCursor(PlatformCursor cursor) OVERRIDE;
+  virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+
+  // PlatformEventDispatcher:
+  virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE;
+  virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE;
+
+ private:
+  PlatformWindowDelegate* delegate_;
+  LibeglplatformShimLoader* eglplatform_shim_;
+  EventFactoryEvdev* event_factory_;
+  gfx::Rect bounds_;
+  ShimNativeWindowId window_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(EgltestWindow);
+};
+
+EgltestWindow::EgltestWindow(PlatformWindowDelegate* delegate,
+                             LibeglplatformShimLoader* eglplatform_shim,
+                             EventFactoryEvdev* event_factory,
+                             const gfx::Rect& bounds)
+    : delegate_(delegate),
+      eglplatform_shim_(eglplatform_shim),
+      event_factory_(event_factory),
+      bounds_(bounds),
+      window_id_(SHIM_NO_WINDOW_ID) {
+  window_id_ = eglplatform_shim_->ShimCreateWindow();
+  delegate_->OnAcceleratedWidgetAvailable(window_id_);
+  ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(this);
+}
+
+EgltestWindow::~EgltestWindow() {
+  ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this);
+  if (window_id_ != SHIM_NO_WINDOW_ID)
+    eglplatform_shim_->ShimDestroyWindow(window_id_);
+}
+
+gfx::Rect EgltestWindow::GetBounds() {
+  return bounds_;
+}
+
+void EgltestWindow::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+}
+
+void EgltestWindow::Show() {
+}
+
+void EgltestWindow::Hide() {
+}
+
+void EgltestWindow::Close() {
+}
+
+void EgltestWindow::SetCapture() {
+}
+
+void EgltestWindow::ReleaseCapture() {
+}
+
+void EgltestWindow::ToggleFullscreen() {
+}
+
+void EgltestWindow::Maximize() {
+}
+
+void EgltestWindow::Minimize() {
+}
+
+void EgltestWindow::Restore() {
+}
+
+void EgltestWindow::SetCursor(PlatformCursor cursor) {
+}
+
+void EgltestWindow::MoveCursorTo(const gfx::Point& location) {
+  event_factory_->WarpCursorTo(window_id_, location);
+}
+
+bool EgltestWindow::CanDispatchEvent(const ui::PlatformEvent& ne) {
+  return true;
+}
+
+uint32_t EgltestWindow::DispatchEvent(const ui::PlatformEvent& native_event) {
+  DispatchEventFromNativeUiEvent(
+      native_event,
+      base::Bind(&PlatformWindowDelegate::DispatchEvent,
+                 base::Unretained(delegate_)));
+
+  return ui::POST_DISPATCH_STOP_PROPAGATION;
+}
+
+// EGL surface wrapper for libeglplatform_shim.
+//
+// This just manages the native window lifetime using
+// ShimGetNativeWindow & ShimReleaseNativeWindow.
+class SurfaceOzoneEgltest : public SurfaceOzoneEGL {
+ public:
+  SurfaceOzoneEgltest(ShimNativeWindowId window_id,
+                      LibeglplatformShimLoader* eglplatform_shim)
+      : eglplatform_shim_(eglplatform_shim) {
+    native_window_ = eglplatform_shim_->ShimGetNativeWindow(window_id);
+  }
+  virtual ~SurfaceOzoneEgltest() {
+    bool ret = eglplatform_shim_->ShimReleaseNativeWindow(native_window_);
+    DCHECK(ret);
+  }
+
+  virtual intptr_t GetNativeWindow() OVERRIDE { return native_window_; }
+
+  virtual bool OnSwapBuffers() OVERRIDE { return true; }
+
+  virtual bool ResizeNativeWindow(const gfx::Size& viewport_size) OVERRIDE {
+    return true;
+  }
+
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE {
+    return scoped_ptr<gfx::VSyncProvider>();
+  }
+
+ private:
+  LibeglplatformShimLoader* eglplatform_shim_;
+  intptr_t native_window_;
+};
+
+// EGL surface factory for libeglplatform_shim.
+//
+// This finds the right EGL/GLES2 libraries for loading, and creates
+// a single native window via ShimCreateWindow for drawing
+// into.
+class SurfaceFactoryEgltest : public ui::SurfaceFactoryOzone {
+ public:
+  SurfaceFactoryEgltest(LibeglplatformShimLoader* eglplatform_shim)
+      : eglplatform_shim_(eglplatform_shim) {}
+  virtual ~SurfaceFactoryEgltest() {}
+
+  // SurfaceFactoryOzone:
+  virtual intptr_t GetNativeDisplay() OVERRIDE;
+  virtual scoped_ptr<SurfaceOzoneEGL> CreateEGLSurfaceForWidget(
+      gfx::AcceleratedWidget widget) OVERRIDE;
+  virtual const int32* GetEGLSurfaceProperties(
+      const int32* desired_list) OVERRIDE;
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
+
+ private:
+  LibeglplatformShimLoader* eglplatform_shim_;
+};
+
+intptr_t SurfaceFactoryEgltest::GetNativeDisplay() {
+  return eglplatform_shim_->ShimGetNativeDisplay();
+}
+
+scoped_ptr<SurfaceOzoneEGL> SurfaceFactoryEgltest::CreateEGLSurfaceForWidget(
+    gfx::AcceleratedWidget widget) {
+  return make_scoped_ptr<SurfaceOzoneEGL>(
+      new SurfaceOzoneEgltest(widget, eglplatform_shim_));
+}
+
+bool SurfaceFactoryEgltest::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  const char* egl_soname = eglplatform_shim_->ShimQueryString(SHIM_EGL_LIBRARY);
+  const char* gles_soname =
+      eglplatform_shim_->ShimQueryString(SHIM_GLES_LIBRARY);
+  if (!egl_soname)
+    egl_soname = kDefaultEglSoname;
+  if (!gles_soname)
+    gles_soname = kDefaultGlesSoname;
+
+  base::NativeLibraryLoadError error;
+  base::NativeLibrary egl_library =
+      base::LoadNativeLibrary(base::FilePath(egl_soname), &error);
+  if (!egl_library) {
+    LOG(WARNING) << "Failed to load EGL library: " << error.ToString();
+    return false;
+  }
+
+  base::NativeLibrary gles_library =
+      base::LoadNativeLibrary(base::FilePath(gles_soname), &error);
+  if (!gles_library) {
+    LOG(WARNING) << "Failed to load GLES library: " << error.ToString();
+    base::UnloadNativeLibrary(egl_library);
+    return false;
+  }
+
+  GLGetProcAddressProc get_proc_address =
+      reinterpret_cast<GLGetProcAddressProc>(
+          base::GetFunctionPointerFromNativeLibrary(egl_library,
+                                                    "eglGetProcAddress"));
+  if (!get_proc_address) {
+    LOG(ERROR) << "eglGetProcAddress not found.";
+    base::UnloadNativeLibrary(egl_library);
+    base::UnloadNativeLibrary(gles_library);
+    return false;
+  }
+
+  set_gl_get_proc_address.Run(get_proc_address);
+  add_gl_library.Run(egl_library);
+  add_gl_library.Run(gles_library);
+  return true;
+}
+
+const int32* SurfaceFactoryEgltest::GetEGLSurfaceProperties(
+    const int32* desired_list) {
+  static const int32 broken_props[] = {
+      EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+      EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
+      EGL_NONE,
+  };
+  return broken_props;
+}
+
+// Test platform for EGL.
+//
+// This is a tiny EGL-based platform. Creation of the native window is
+// handled by a separate library called eglplatform_shim.so.1 because
+// this itself is platform specific and we want to test out multiple
+// hardware platforms.
+class OzonePlatformEgltest : public OzonePlatform {
+ public:
+  OzonePlatformEgltest() : shim_initialized_(false) {}
+  virtual ~OzonePlatformEgltest() {
+    if (shim_initialized_)
+      eglplatform_shim_.ShimTerminate();
+  }
+
+  void LoadShim() {
+    std::string library = GetShimLibraryName();
+
+    if (eglplatform_shim_.Load(library))
+      return;
+
+    base::FilePath module_path;
+    if (!PathService::Get(base::DIR_MODULE, &module_path))
+      LOG(ERROR) << "failed to get DIR_MODULE from PathService";
+    base::FilePath library_path = module_path.Append(library);
+
+    if (eglplatform_shim_.Load(library_path.value()))
+      return;
+
+    LOG(FATAL) << "failed to load " << library;
+  }
+
+  void Initialize() {
+    LoadShim();
+    shim_initialized_ = eglplatform_shim_.ShimInitialize();
+  }
+
+  // OzonePlatform:
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
+    return surface_factory_ozone_.get();
+  }
+  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
+    return cursor_factory_ozone_.get();
+  }
+  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
+    return gpu_platform_support_.get();
+  }
+  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
+    return gpu_platform_support_host_.get();
+  }
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) OVERRIDE {
+    return make_scoped_ptr<PlatformWindow>(
+        new EgltestWindow(delegate,
+                          &eglplatform_shim_,
+                          event_factory_ozone_.get(),
+                          bounds));
+  }
+  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
+      OVERRIDE {
+    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+  }
+
+  virtual void InitializeUI() OVERRIDE {
+    device_manager_ = CreateDeviceManager();
+    if (!surface_factory_ozone_)
+      surface_factory_ozone_.reset(
+          new SurfaceFactoryEgltest(&eglplatform_shim_));
+    event_factory_ozone_.reset(
+        new EventFactoryEvdev(NULL, device_manager_.get()));
+    cursor_factory_ozone_.reset(new CursorFactoryOzone());
+    gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
+  }
+
+  virtual void InitializeGPU() OVERRIDE {
+    if (!surface_factory_ozone_)
+      surface_factory_ozone_.reset(
+          new SurfaceFactoryEgltest(&eglplatform_shim_));
+    gpu_platform_support_.reset(CreateStubGpuPlatformSupport());
+  }
+
+ private:
+  LibeglplatformShimLoader eglplatform_shim_;
+  scoped_ptr<DeviceManager> device_manager_;
+  scoped_ptr<SurfaceFactoryEgltest> surface_factory_ozone_;
+  scoped_ptr<EventFactoryEvdev> event_factory_ozone_;
+  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<GpuPlatformSupport> gpu_platform_support_;
+  scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
+
+  bool shim_initialized_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformEgltest);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformEgltest() {
+  OzonePlatformEgltest* platform = new OzonePlatformEgltest;
+  platform->Initialize();
+  return platform;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/egltest/ozone_platform_egltest.h b/ui/ozone/platform/egltest/ozone_platform_egltest.h
new file mode 100644
index 0000000..fb49be5
--- /dev/null
+++ b/ui/ozone/platform/egltest/ozone_platform_egltest.h
@@ -0,0 +1,17 @@
+// 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 UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
+#define UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformEgltest();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_EGLTEST_H_
diff --git a/ui/ozone/platform/test/BUILD.gn b/ui/ozone/platform/test/BUILD.gn
new file mode 100644
index 0000000..d1ff717
--- /dev/null
+++ b/ui/ozone/platform/test/BUILD.gn
@@ -0,0 +1,21 @@
+# 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.
+
+source_set("test") {
+  sources = [
+    "ozone_platform_test.cc",
+    "ozone_platform_test.h",
+    "test_window.cc",
+    "test_window.h",
+    "test_window_manager.cc",
+    "test_window_manager.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+    "//ui/base",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/ui/ozone/platform/test/DEPS b/ui/ozone/platform/test/DEPS
new file mode 100644
index 0000000..41cd997
--- /dev/null
+++ b/ui/ozone/platform/test/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+third_party/skia/include",
+]
diff --git a/ui/ozone/platform/test/ozone_platform_test.cc b/ui/ozone/platform/test/ozone_platform_test.cc
new file mode 100644
index 0000000..1057291
--- /dev/null
+++ b/ui/ozone/platform/test/ozone_platform_test.cc
@@ -0,0 +1,92 @@
+// Copyright 2013 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 "ui/ozone/platform/test/ozone_platform_test.h"
+
+#include "base/command_line.h"
+#include "base/files/file_path.h"
+#include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/common/native_display_delegate_ozone.h"
+#include "ui/ozone/platform/test/test_window.h"
+#include "ui/ozone/platform/test/test_window_manager.h"
+#include "ui/ozone/public/cursor_factory_ozone.h"
+#include "ui/ozone/public/gpu_platform_support.h"
+#include "ui/ozone/public/gpu_platform_support_host.h"
+#include "ui/ozone/public/ozone_platform.h"
+#include "ui/ozone/public/ozone_switches.h"
+
+namespace ui {
+
+namespace {
+
+// OzonePlatform for testing
+//
+// This platform dumps images to a file for testing purposes.
+class OzonePlatformTest : public OzonePlatform {
+ public:
+  OzonePlatformTest(const base::FilePath& dump_file) : file_path_(dump_file) {}
+  virtual ~OzonePlatformTest() {}
+
+  // OzonePlatform:
+  virtual ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() OVERRIDE {
+    return window_manager_.get();
+  }
+  virtual CursorFactoryOzone* GetCursorFactoryOzone() OVERRIDE {
+    return cursor_factory_ozone_.get();
+  }
+  virtual GpuPlatformSupport* GetGpuPlatformSupport() OVERRIDE {
+    return gpu_platform_support_.get();
+  }
+  virtual GpuPlatformSupportHost* GetGpuPlatformSupportHost() OVERRIDE {
+    return gpu_platform_support_host_.get();
+  }
+  virtual scoped_ptr<PlatformWindow> CreatePlatformWindow(
+      PlatformWindowDelegate* delegate,
+      const gfx::Rect& bounds) OVERRIDE {
+    return make_scoped_ptr<PlatformWindow>(
+        new TestWindow(delegate, window_manager_.get(), bounds));
+  }
+  virtual scoped_ptr<NativeDisplayDelegate> CreateNativeDisplayDelegate()
+      OVERRIDE {
+    return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateOzone());
+  }
+
+  virtual void InitializeUI() OVERRIDE {
+    window_manager_.reset(new TestWindowManager(file_path_));
+    window_manager_->Initialize();
+    // This unbreaks tests that create their own.
+    if (!PlatformEventSource::GetInstance())
+      platform_event_source_ = PlatformEventSource::CreateDefault();
+
+    cursor_factory_ozone_.reset(new BitmapCursorFactoryOzone);
+    gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost());
+  }
+
+  virtual void InitializeGPU() OVERRIDE {
+    gpu_platform_support_.reset(CreateStubGpuPlatformSupport());
+  }
+
+ private:
+  scoped_ptr<TestWindowManager> window_manager_;
+  scoped_ptr<PlatformEventSource> platform_event_source_;
+  scoped_ptr<CursorFactoryOzone> cursor_factory_ozone_;
+  scoped_ptr<GpuPlatformSupport> gpu_platform_support_;
+  scoped_ptr<GpuPlatformSupportHost> gpu_platform_support_host_;
+  base::FilePath file_path_;
+
+  DISALLOW_COPY_AND_ASSIGN(OzonePlatformTest);
+};
+
+}  // namespace
+
+OzonePlatform* CreateOzonePlatformTest() {
+  CommandLine* cmd = CommandLine::ForCurrentProcess();
+  base::FilePath location;
+  if (cmd->HasSwitch(switches::kOzoneDumpFile))
+    location = cmd->GetSwitchValuePath(switches::kOzoneDumpFile);
+  return new OzonePlatformTest(location);
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/ozone_platform_test.h b/ui/ozone/platform/test/ozone_platform_test.h
new file mode 100644
index 0000000..fb25fd6
--- /dev/null
+++ b/ui/ozone/platform/test/ozone_platform_test.h
@@ -0,0 +1,17 @@
+// Copyright 2013 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 UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
+#define UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
+
+namespace ui {
+
+class OzonePlatform;
+
+// Constructor hook for use in ozone_platform_list.cc
+OzonePlatform* CreateOzonePlatformTest();
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_OZONE_PLATFORM_TEST_H_
diff --git a/ui/ozone/platform/test/test.gypi b/ui/ozone/platform/test/test.gypi
new file mode 100644
index 0000000..d1a35c8
--- /dev/null
+++ b/ui/ozone/platform/test/test.gypi
@@ -0,0 +1,36 @@
+# 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.
+
+{
+  'variables': {
+    'internal_ozone_platform_deps': [
+      'ozone_platform_test',
+    ],
+    'internal_ozone_platforms': [
+      'test'
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'ozone_platform_test',
+      'type': 'static_library',
+      'defines': [
+        'OZONE_IMPLEMENTATION',
+      ],
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../events/events.gyp:events',
+        '../gfx/gfx.gyp:gfx',
+      ],
+      'sources': [
+        'ozone_platform_test.cc',
+        'ozone_platform_test.h',
+        'test_window.cc',
+        'test_window.h',
+        'test_window_manager.cc',
+        'test_window_manager.h',
+      ],
+    },
+  ],
+}
diff --git a/ui/ozone/platform/test/test_window.cc b/ui/ozone/platform/test/test_window.cc
new file mode 100644
index 0000000..2cfa26c
--- /dev/null
+++ b/ui/ozone/platform/test/test_window.cc
@@ -0,0 +1,80 @@
+// 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 "ui/ozone/platform/test/test_window.h"
+
+#include <string>
+
+#include "base/files/file_path.h"
+#include "base/strings/string_number_conversions.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/ozone/platform/test/test_window_manager.h"
+#include "ui/platform_window/platform_window_delegate.h"
+
+namespace ui {
+
+TestWindow::TestWindow(PlatformWindowDelegate* delegate,
+                       TestWindowManager* manager,
+                       const gfx::Rect& bounds)
+    : delegate_(delegate), manager_(manager), bounds_(bounds) {
+  widget_ = manager_->AddWindow(this);
+  delegate_->OnAcceleratedWidgetAvailable(widget_);
+}
+
+TestWindow::~TestWindow() {
+  manager_->RemoveWindow(widget_, this);
+}
+
+base::FilePath TestWindow::path() {
+  base::FilePath base_path = manager_->base_path();
+  if (base_path.empty() || base_path == base::FilePath("/dev/null"))
+    return base_path;
+
+  // Disambiguate multiple window output files with the window id.
+  return base_path.Append(base::IntToString(widget_));
+}
+
+gfx::Rect TestWindow::GetBounds() {
+  return bounds_;
+}
+
+void TestWindow::SetBounds(const gfx::Rect& bounds) {
+  bounds_ = bounds;
+  delegate_->OnBoundsChanged(bounds);
+}
+
+void TestWindow::Show() {
+}
+
+void TestWindow::Hide() {
+}
+
+void TestWindow::Close() {
+}
+
+void TestWindow::SetCapture() {
+}
+
+void TestWindow::ReleaseCapture() {
+}
+
+void TestWindow::ToggleFullscreen() {
+}
+
+void TestWindow::Maximize() {
+}
+
+void TestWindow::Minimize() {
+}
+
+void TestWindow::Restore() {
+}
+
+void TestWindow::SetCursor(PlatformCursor cursor) {
+}
+
+void TestWindow::MoveCursorTo(const gfx::Point& location) {
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/test_window.h b/ui/ozone/platform/test/test_window.h
new file mode 100644
index 0000000..13a23a6
--- /dev/null
+++ b/ui/ozone/platform/test/test_window.h
@@ -0,0 +1,54 @@
+// 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 UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
+#define UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
+
+#include "base/files/file_path.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/platform_window/platform_window.h"
+
+namespace ui {
+
+class PlatformWindowDelegate;
+class TestWindowManager;
+
+class TestWindow : public PlatformWindow {
+ public:
+  TestWindow(PlatformWindowDelegate* delegate,
+             TestWindowManager* manager,
+             const gfx::Rect& bounds);
+  virtual ~TestWindow();
+
+  // Path for image file for this window.
+  base::FilePath path();
+
+  // PlatformWindow:
+  virtual gfx::Rect GetBounds() OVERRIDE;
+  virtual void SetBounds(const gfx::Rect& bounds) OVERRIDE;
+  virtual void Show() OVERRIDE;
+  virtual void Hide() OVERRIDE;
+  virtual void Close() OVERRIDE;
+  virtual void SetCapture() OVERRIDE;
+  virtual void ReleaseCapture() OVERRIDE;
+  virtual void ToggleFullscreen() OVERRIDE;
+  virtual void Maximize() OVERRIDE;
+  virtual void Minimize() OVERRIDE;
+  virtual void Restore() OVERRIDE;
+  virtual void SetCursor(PlatformCursor cursor) OVERRIDE;
+  virtual void MoveCursorTo(const gfx::Point& location) OVERRIDE;
+
+ private:
+  PlatformWindowDelegate* delegate_;
+  TestWindowManager* manager_;
+  gfx::Rect bounds_;
+  gfx::AcceleratedWidget widget_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindow);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_TEST_WINDOW_H_
diff --git a/ui/ozone/platform/test/test_window_manager.cc b/ui/ozone/platform/test/test_window_manager.cc
new file mode 100644
index 0000000..6eb1aef
--- /dev/null
+++ b/ui/ozone/platform/test/test_window_manager.cc
@@ -0,0 +1,112 @@
+// 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 "ui/ozone/platform/test/test_window_manager.h"
+
+#include "base/bind.h"
+#include "base/files/file_util.h"
+#include "base/location.h"
+#include "base/stl_util.h"
+#include "base/threading/worker_pool.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkSurface.h"
+#include "ui/gfx/codec/png_codec.h"
+#include "ui/gfx/skia_util.h"
+#include "ui/gfx/vsync_provider.h"
+#include "ui/ozone/public/surface_ozone_canvas.h"
+
+namespace ui {
+
+namespace {
+
+void WriteDataToFile(const base::FilePath& location, const SkBitmap& bitmap) {
+  DCHECK(!location.empty());
+  std::vector<unsigned char> png_data;
+  gfx::PNGCodec::FastEncodeBGRASkBitmap(bitmap, true, &png_data);
+  base::WriteFile(location,
+                  reinterpret_cast<const char*>(vector_as_array(&png_data)),
+                  png_data.size());
+}
+
+class FileSurface : public SurfaceOzoneCanvas {
+ public:
+  FileSurface(const base::FilePath& location) : location_(location) {}
+  virtual ~FileSurface() {}
+
+  // SurfaceOzoneCanvas overrides:
+  virtual void ResizeCanvas(const gfx::Size& viewport_size) OVERRIDE {
+    surface_ = skia::AdoptRef(SkSurface::NewRaster(SkImageInfo::MakeN32Premul(
+        viewport_size.width(), viewport_size.height())));
+  }
+  virtual skia::RefPtr<SkCanvas> GetCanvas() OVERRIDE {
+    return skia::SharePtr(surface_->getCanvas());
+  }
+  virtual void PresentCanvas(const gfx::Rect& damage) OVERRIDE {
+    if (location_.empty())
+      return;
+    SkBitmap bitmap;
+    bitmap.setInfo(surface_->getCanvas()->imageInfo());
+
+    // TODO(dnicoara) Use SkImage instead to potentially avoid a copy.
+    // See crbug.com/361605 for details.
+    if (surface_->getCanvas()->readPixels(&bitmap, 0, 0)) {
+      base::WorkerPool::PostTask(
+          FROM_HERE, base::Bind(&WriteDataToFile, location_, bitmap), true);
+    }
+  }
+  virtual scoped_ptr<gfx::VSyncProvider> CreateVSyncProvider() OVERRIDE {
+    return scoped_ptr<gfx::VSyncProvider>();
+  }
+
+ private:
+  base::FilePath location_;
+  skia::RefPtr<SkSurface> surface_;
+};
+
+}  // namespace
+
+TestWindowManager::TestWindowManager(const base::FilePath& dump_location)
+    : location_(dump_location) {
+}
+
+TestWindowManager::~TestWindowManager() {
+}
+
+void TestWindowManager::Initialize() {
+  if (location_.empty())
+    return;
+  if (!DirectoryExists(location_) && !base::CreateDirectory(location_) &&
+      location_ != base::FilePath("/dev/null"))
+    PLOG(FATAL) << "unable to create output directory";
+  if (!base::PathIsWritable(location_))
+    PLOG(FATAL) << "unable to write to output location";
+}
+
+int32_t TestWindowManager::AddWindow(TestWindow* window) {
+  return windows_.Add(window);
+}
+
+void TestWindowManager::RemoveWindow(int32_t window_id, TestWindow* window) {
+  DCHECK_EQ(window, windows_.Lookup(window_id));
+  windows_.Remove(window_id);
+}
+
+base::FilePath TestWindowManager::base_path() const {
+  return location_;
+}
+
+scoped_ptr<SurfaceOzoneCanvas> TestWindowManager::CreateCanvasForWidget(
+    gfx::AcceleratedWidget widget) {
+  TestWindow* window = windows_.Lookup(widget);
+  DCHECK(window);
+  return make_scoped_ptr<SurfaceOzoneCanvas>(new FileSurface(window->path()));
+}
+
+bool TestWindowManager::LoadEGLGLES2Bindings(
+    AddGLLibraryCallback add_gl_library,
+    SetGLGetProcAddressProcCallback set_gl_get_proc_address) {
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/ozone/platform/test/test_window_manager.h b/ui/ozone/platform/test/test_window_manager.h
new file mode 100644
index 0000000..8efbb69
--- /dev/null
+++ b/ui/ozone/platform/test/test_window_manager.h
@@ -0,0 +1,51 @@
+// 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 UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_
+#define UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_
+
+#include "base/files/file_path.h"
+#include "base/id_map.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/gfx/native_widget_types.h"
+#include "ui/ozone/platform/test/test_window.h"
+#include "ui/ozone/public/surface_factory_ozone.h"
+
+namespace ui {
+
+class TestWindowManager : public SurfaceFactoryOzone {
+ public:
+  explicit TestWindowManager(const base::FilePath& dump_location);
+  virtual ~TestWindowManager();
+
+  // Initialize (mainly check that we have a place to write output to).
+  void Initialize();
+
+  // Register a new window. Returns the window id.
+  int32_t AddWindow(TestWindow* window);
+
+  // Remove a window.
+  void RemoveWindow(int32_t window_id, TestWindow* window);
+
+  // User-supplied path for images.
+  base::FilePath base_path() const;
+
+  // SurfaceFactoryOzone:
+  virtual scoped_ptr<SurfaceOzoneCanvas> CreateCanvasForWidget(
+      gfx::AcceleratedWidget w) OVERRIDE;
+  virtual bool LoadEGLGLES2Bindings(
+      AddGLLibraryCallback add_gl_library,
+      SetGLGetProcAddressProcCallback set_gl_get_proc_address) OVERRIDE;
+
+ private:
+  base::FilePath location_;
+
+  IDMap<TestWindow> windows_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestWindowManager);
+};
+
+}  // namespace ui
+
+#endif  // UI_OZONE_PLATFORM_TEST_FILE_SURFACE_FACTORY_H_