Clone of chromium aad1ce808763f59c7a3753e08f1500a104ecc6fd refs/remotes/origin/HEAD
diff --git a/ui/display/BUILD.gn b/ui/display/BUILD.gn
new file mode 100644
index 0000000..8550c23
--- /dev/null
+++ b/ui/display/BUILD.gn
@@ -0,0 +1,105 @@
+# 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/ui.gni")
+
+component("display") {
+  sources = [
+    "chromeos/display_configurator.cc",
+    "chromeos/display_configurator.h",
+    "chromeos/ozone/display_configurator_ozone.cc",
+    "chromeos/x11/display_configurator_x11.cc",
+    "chromeos/x11/display_mode_x11.cc",
+    "chromeos/x11/display_mode_x11.h",
+    "chromeos/x11/display_snapshot_x11.cc",
+    "chromeos/x11/display_snapshot_x11.h",
+    "chromeos/x11/display_util_x11.cc",
+    "chromeos/x11/display_util_x11.h",
+    "chromeos/x11/native_display_delegate_x11.cc",
+    "chromeos/x11/native_display_delegate_x11.h",
+    "chromeos/x11/native_display_event_dispatcher_x11.cc",
+    "chromeos/x11/native_display_event_dispatcher_x11.h",
+    "display_export.h",
+    "display_switches.cc",
+    "display_switches.h",
+  ]
+
+  defines = [ "DISPLAY_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//ui/display/util",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  if (use_x11) {
+    configs += [
+      "//build/config/linux:x11",
+      "//build/config/linux:xext",
+      "//build/config/linux:xi",
+      "//build/config/linux:xrandr",
+    ]
+    deps += [
+      "//ui/events/platform",
+    ]
+  }
+
+  if (is_chromeos) {
+    deps += [ "//ui/display/types" ]
+  }
+
+  if (use_ozone) {
+    deps += [ "//ui/ozone" ]
+  }
+}
+
+component("test_util") {
+  output_name = "display_test_util"
+  sources = [
+    "chromeos/test/test_display_snapshot.cc",
+    "chromeos/test/test_display_snapshot.h",
+  ]
+
+  defines = [ "DISPLAY_IMPLEMENTATION" ]
+
+  public_deps = [
+    ":display",
+  ]
+  deps = [
+    "//base",
+    "//ui/gfx",
+    "//ui/gfx/geometry",
+  ]
+
+  if (is_chromeos) {
+    deps += [ "//ui/display/types" ]
+  }
+}
+
+test("display_unittests") {
+  sources = [
+    "chromeos/display_configurator_unittest.cc",
+    "chromeos/x11/display_util_x11_unittest.cc",
+    "chromeos/x11/native_display_event_dispatcher_x11_unittest.cc",
+    "util/display_util_unittest.cc",
+    "util/edid_parser_unittest.cc",
+  ]
+
+  deps = [
+    ":test_util",
+    "//base",
+    "//base/test:run_all_unittests",
+    "//testing/gtest",
+    "//ui/display/util",
+    "//ui/gfx/geometry",
+  ]
+
+  if (is_chromeos) {
+    deps += [
+      ":display",
+      "//ui/display/types",
+    ]
+  }
+}
diff --git a/ui/display/DEPS b/ui/display/DEPS
new file mode 100644
index 0000000..51399a6
--- /dev/null
+++ b/ui/display/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+third_party/cros_system_api",
+  "+ui/gfx/geometry",
+]
diff --git a/ui/display/OWNERS b/ui/display/OWNERS
new file mode 100644
index 0000000..69f028b
--- /dev/null
+++ b/ui/display/OWNERS
@@ -0,0 +1,3 @@
+derat@chromium.org
+marcheu@chromium.org
+oshima@chromium.org
diff --git a/ui/display/chromeos/display_configurator.cc b/ui/display/chromeos/display_configurator.cc
new file mode 100644
index 0000000..a32fd0b
--- /dev/null
+++ b/ui/display/chromeos/display_configurator.cc
@@ -0,0 +1,975 @@
+// 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/display/chromeos/display_configurator.h"
+
+#include "base/bind.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
+#include "base/sys_info.h"
+#include "base/time/time.h"
+#include "ui/display/display_switches.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/display_snapshot.h"
+#include "ui/display/types/native_display_delegate.h"
+
+namespace ui {
+
+namespace {
+
+typedef std::vector<const DisplayMode*> DisplayModeList;
+
+// The delay to perform configuration after RRNotify. See the comment for
+// |configure_timer_|.
+const int kConfigureDelayMs = 500;
+
+// The delay spent before reading the display configuration after coming out of
+// suspend. While coming out of suspend the display state may be updating. This
+// is used to wait until the hardware had a chance to update the display state
+// such that we read an up to date state.
+const int kResumeDelayMs = 500;
+
+// Returns a string describing |state|.
+std::string DisplayPowerStateToString(chromeos::DisplayPowerState state) {
+  switch (state) {
+    case chromeos::DISPLAY_POWER_ALL_ON:
+      return "ALL_ON";
+    case chromeos::DISPLAY_POWER_ALL_OFF:
+      return "ALL_OFF";
+    case chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON:
+      return "INTERNAL_OFF_EXTERNAL_ON";
+    case chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF:
+      return "INTERNAL_ON_EXTERNAL_OFF";
+    default:
+      return "unknown (" + base::IntToString(state) + ")";
+  }
+}
+
+// Returns a string describing |state|.
+std::string DisplayStateToString(MultipleDisplayState state) {
+  switch (state) {
+    case MULTIPLE_DISPLAY_STATE_INVALID:
+      return "INVALID";
+    case MULTIPLE_DISPLAY_STATE_HEADLESS:
+      return "HEADLESS";
+    case MULTIPLE_DISPLAY_STATE_SINGLE:
+      return "SINGLE";
+    case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR:
+      return "DUAL_MIRROR";
+    case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED:
+      return "DUAL_EXTENDED";
+  }
+  NOTREACHED() << "Unknown state " << state;
+  return "INVALID";
+}
+
+// Returns the number of displays in |displays| that should be turned on, per
+// |state|.  If |display_power| is non-NULL, it is updated to contain the
+// on/off state of each corresponding entry in |displays|.
+int GetDisplayPower(
+    const std::vector<DisplayConfigurator::DisplayState>& display_states,
+    chromeos::DisplayPowerState state,
+    std::vector<bool>* display_power) {
+  int num_on_displays = 0;
+  if (display_power)
+    display_power->resize(display_states.size());
+
+  for (size_t i = 0; i < display_states.size(); ++i) {
+    bool internal =
+        display_states[i].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+    bool on =
+        state == chromeos::DISPLAY_POWER_ALL_ON ||
+        (state == chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON &&
+         !internal) ||
+        (state == chromeos::DISPLAY_POWER_INTERNAL_ON_EXTERNAL_OFF && internal);
+    if (display_power)
+      (*display_power)[i] = on;
+    if (on)
+      num_on_displays++;
+  }
+  return num_on_displays;
+}
+
+}  // namespace
+
+
+const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0;
+const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0;
+const int
+DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1;
+
+DisplayConfigurator::DisplayState::DisplayState()
+    : display(NULL),
+      selected_mode(NULL),
+      mirror_mode(NULL) {}
+
+bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() {
+  if (configurator_->configure_timer_.IsRunning()) {
+    configurator_->configure_timer_.user_task().Run();
+    configurator_->configure_timer_.Stop();
+    return true;
+  } else {
+    return false;
+  }
+}
+
+// static
+const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize(
+    const DisplaySnapshot& display,
+    const gfx::Size& size) {
+  const DisplayMode* best_mode = NULL;
+  for (DisplayModeList::const_iterator it = display.modes().begin();
+       it != display.modes().end();
+       ++it) {
+    const DisplayMode* mode = *it;
+
+    if (mode->size() != size)
+      continue;
+
+    if (!best_mode) {
+      best_mode = mode;
+      continue;
+    }
+
+    if (mode->is_interlaced()) {
+      if (!best_mode->is_interlaced())
+        continue;
+    } else {
+      // Reset the best rate if the non interlaced is
+      // found the first time.
+      if (best_mode->is_interlaced()) {
+        best_mode = mode;
+        continue;
+      }
+    }
+    if (mode->refresh_rate() < best_mode->refresh_rate())
+      continue;
+
+    best_mode = mode;
+  }
+
+  return best_mode;
+}
+
+DisplayConfigurator::DisplayConfigurator()
+    : state_controller_(NULL),
+      mirroring_controller_(NULL),
+      is_panel_fitting_enabled_(false),
+      configure_display_(base::SysInfo::IsRunningOnChromeOS()),
+      display_state_(MULTIPLE_DISPLAY_STATE_INVALID),
+      requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
+      current_power_state_(chromeos::DISPLAY_POWER_ALL_ON),
+      next_display_protection_client_id_(1) {}
+
+DisplayConfigurator::~DisplayConfigurator() {
+  if (native_display_delegate_)
+    native_display_delegate_->RemoveObserver(this);
+}
+
+void DisplayConfigurator::SetDelegateForTesting(
+    scoped_ptr<NativeDisplayDelegate> display_delegate) {
+  DCHECK(!native_display_delegate_);
+
+  native_display_delegate_ = display_delegate.Pass();
+  configure_display_ = true;
+}
+
+void DisplayConfigurator::SetInitialDisplayPower(
+    chromeos::DisplayPowerState power_state) {
+  DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID);
+  requested_power_state_ = current_power_state_ = power_state;
+}
+
+void DisplayConfigurator::Init(bool is_panel_fitting_enabled) {
+  is_panel_fitting_enabled_ = is_panel_fitting_enabled;
+  if (!configure_display_)
+    return;
+
+  // If the delegate is already initialized don't update it (For example, tests
+  // set their own delegates).
+  if (!native_display_delegate_) {
+    native_display_delegate_ = CreatePlatformNativeDisplayDelegate();
+    native_display_delegate_->AddObserver(this);
+  }
+}
+
+void DisplayConfigurator::ForceInitialConfigure(
+    uint32_t background_color_argb) {
+  if (!configure_display_)
+    return;
+
+  native_display_delegate_->GrabServer();
+  native_display_delegate_->Initialize();
+
+  UpdateCachedDisplays();
+  if (cached_displays_.size() > 1 && background_color_argb)
+    native_display_delegate_->SetBackgroundColor(background_color_argb);
+  const MultipleDisplayState new_state = ChooseDisplayState(
+      requested_power_state_);
+  const bool success = EnterStateOrFallBackToSoftwareMirroring(
+      new_state, requested_power_state_);
+
+  // Force the DPMS on chrome startup as the driver doesn't always detect
+  // that all displays are on when signing out.
+  native_display_delegate_->ForceDPMSOn();
+  native_display_delegate_->UngrabServer();
+  NotifyObservers(success, new_state);
+}
+
+bool DisplayConfigurator::IsMirroring() const {
+  return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR ||
+      (mirroring_controller_ &&
+       mirroring_controller_->SoftwareMirroringEnabled());
+}
+
+bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) {
+  for (DisplayStateList::const_iterator it = cached_displays_.begin();
+       it != cached_displays_.end();
+       ++it) {
+    uint32_t all_desired = 0;
+
+    // In mirror mode, protection request of all displays need to be fulfilled.
+    // In non-mirror mode, only request of client's display needs to be
+    // fulfilled.
+    ContentProtections::const_iterator request_it;
+    if (IsMirroring()) {
+      for (request_it = requests.begin();
+           request_it != requests.end();
+           ++request_it)
+        all_desired |= request_it->second;
+    } else {
+      request_it = requests.find(it->display->display_id());
+      if (request_it != requests.end())
+        all_desired = request_it->second;
+    }
+
+    switch (it->display->type()) {
+      case DISPLAY_CONNECTION_TYPE_UNKNOWN:
+        return false;
+      // DisplayPort, DVI, and HDMI all support HDCP.
+      case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
+      case DISPLAY_CONNECTION_TYPE_DVI:
+      case DISPLAY_CONNECTION_TYPE_HDMI: {
+        HDCPState current_state;
+        // Need to poll the driver for updates since other applications may
+        // have updated the state.
+        if (!native_display_delegate_->GetHDCPState(*it->display,
+                                                    &current_state))
+          return false;
+        bool current_desired = (current_state != HDCP_STATE_UNDESIRED);
+        bool new_desired = (all_desired & CONTENT_PROTECTION_METHOD_HDCP);
+        // Don't enable again if HDCP is already active. Some buggy drivers
+        // may disable and enable if setting "desired" in active state.
+        if (current_desired != new_desired) {
+          HDCPState new_state =
+              new_desired ? HDCP_STATE_DESIRED : HDCP_STATE_UNDESIRED;
+          if (!native_display_delegate_->SetHDCPState(*it->display, new_state))
+            return false;
+        }
+        break;
+      }
+      case DISPLAY_CONNECTION_TYPE_INTERNAL:
+      case DISPLAY_CONNECTION_TYPE_VGA:
+      case DISPLAY_CONNECTION_TYPE_NETWORK:
+        // No protections for these types. Do nothing.
+        break;
+      case DISPLAY_CONNECTION_TYPE_NONE:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  return true;
+}
+
+DisplayConfigurator::ContentProtectionClientId
+DisplayConfigurator::RegisterContentProtectionClient() {
+  if (!configure_display_)
+    return kInvalidClientId;
+
+  return next_display_protection_client_id_++;
+}
+
+void DisplayConfigurator::UnregisterContentProtectionClient(
+    ContentProtectionClientId client_id) {
+  client_protection_requests_.erase(client_id);
+
+  ContentProtections protections;
+  for (ProtectionRequests::const_iterator it =
+           client_protection_requests_.begin();
+       it != client_protection_requests_.end();
+       ++it) {
+    for (ContentProtections::const_iterator it2 = it->second.begin();
+         it2 != it->second.end();
+         ++it2) {
+      protections[it2->first] |= it2->second;
+    }
+  }
+
+  ApplyProtections(protections);
+}
+
+bool DisplayConfigurator::QueryContentProtectionStatus(
+    ContentProtectionClientId client_id,
+    int64_t display_id,
+    uint32_t* link_mask,
+    uint32_t* protection_mask) {
+  if (!configure_display_)
+    return false;
+
+  uint32_t enabled = 0;
+  uint32_t unfulfilled = 0;
+  *link_mask = 0;
+  for (DisplayStateList::const_iterator it = cached_displays_.begin();
+       it != cached_displays_.end();
+       ++it) {
+    // Query display if it is in mirror mode or client on the same display.
+    if (!IsMirroring() && it->display->display_id() != display_id)
+      continue;
+
+    *link_mask |= it->display->type();
+    switch (it->display->type()) {
+      case DISPLAY_CONNECTION_TYPE_UNKNOWN:
+        return false;
+      // DisplayPort, DVI, and HDMI all support HDCP.
+      case DISPLAY_CONNECTION_TYPE_DISPLAYPORT:
+      case DISPLAY_CONNECTION_TYPE_DVI:
+      case DISPLAY_CONNECTION_TYPE_HDMI: {
+        HDCPState state;
+        if (!native_display_delegate_->GetHDCPState(*it->display, &state))
+          return false;
+        if (state == HDCP_STATE_ENABLED)
+          enabled |= CONTENT_PROTECTION_METHOD_HDCP;
+        else
+          unfulfilled |= CONTENT_PROTECTION_METHOD_HDCP;
+        break;
+      }
+      case DISPLAY_CONNECTION_TYPE_INTERNAL:
+      case DISPLAY_CONNECTION_TYPE_VGA:
+      case DISPLAY_CONNECTION_TYPE_NETWORK:
+        // No protections for these types. Do nothing.
+        break;
+      case DISPLAY_CONNECTION_TYPE_NONE:
+        NOTREACHED();
+        break;
+    }
+  }
+
+  // Don't reveal protections requested by other clients.
+  ProtectionRequests::iterator it = client_protection_requests_.find(client_id);
+  if (it != client_protection_requests_.end()) {
+    uint32_t requested_mask = 0;
+    if (it->second.find(display_id) != it->second.end())
+      requested_mask = it->second[display_id];
+    *protection_mask = enabled & ~unfulfilled & requested_mask;
+  } else {
+    *protection_mask = 0;
+  }
+  return true;
+}
+
+bool DisplayConfigurator::EnableContentProtection(
+    ContentProtectionClientId client_id,
+    int64_t display_id,
+    uint32_t desired_method_mask) {
+  if (!configure_display_)
+    return false;
+
+  ContentProtections protections;
+  for (ProtectionRequests::const_iterator it =
+           client_protection_requests_.begin();
+       it != client_protection_requests_.end();
+       ++it) {
+    for (ContentProtections::const_iterator it2 = it->second.begin();
+         it2 != it->second.end();
+         ++it2) {
+      if (it->first == client_id && it2->first == display_id)
+        continue;
+      protections[it2->first] |= it2->second;
+    }
+  }
+  protections[display_id] |= desired_method_mask;
+
+  if (!ApplyProtections(protections))
+    return false;
+
+  if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) {
+    if (client_protection_requests_.find(client_id) !=
+        client_protection_requests_.end()) {
+      client_protection_requests_[client_id].erase(display_id);
+      if (client_protection_requests_[client_id].size() == 0)
+        client_protection_requests_.erase(client_id);
+    }
+  } else {
+    client_protection_requests_[client_id][display_id] = desired_method_mask;
+  }
+
+  return true;
+}
+
+std::vector<ui::ColorCalibrationProfile>
+DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) {
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableDisplayColorCalibration)) {
+    for (size_t i = 0; i < cached_displays_.size(); ++i) {
+      if (cached_displays_[i].display &&
+          cached_displays_[i].display->display_id() == display_id) {
+        return native_display_delegate_->GetAvailableColorCalibrationProfiles(
+            *cached_displays_[i].display);
+      }
+    }
+  }
+
+  return std::vector<ui::ColorCalibrationProfile>();
+}
+
+bool DisplayConfigurator::SetColorCalibrationProfile(
+    int64_t display_id,
+    ui::ColorCalibrationProfile new_profile) {
+  for (size_t i = 0; i < cached_displays_.size(); ++i) {
+    if (cached_displays_[i].display &&
+        cached_displays_[i].display->display_id() == display_id) {
+      return native_display_delegate_->SetColorCalibrationProfile(
+          *cached_displays_[i].display, new_profile);
+    }
+  }
+
+  return false;
+}
+
+void DisplayConfigurator::PrepareForExit() {
+  configure_display_ = false;
+}
+
+bool DisplayConfigurator::SetDisplayPower(
+    chromeos::DisplayPowerState power_state,
+    int flags) {
+  if (!configure_display_)
+    return false;
+
+  VLOG(1) << "SetDisplayPower: power_state="
+          << DisplayPowerStateToString(power_state) << " flags=" << flags
+          << ", configure timer="
+          << (configure_timer_.IsRunning() ? "Running" : "Stopped");
+  if (power_state == current_power_state_ &&
+      !(flags & kSetDisplayPowerForceProbe))
+    return true;
+
+  native_display_delegate_->GrabServer();
+  UpdateCachedDisplays();
+
+  const MultipleDisplayState new_state = ChooseDisplayState(power_state);
+  bool attempted_change = false;
+  bool success = false;
+
+  bool only_if_single_internal_display =
+      flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
+  bool single_internal_display =
+      cached_displays_.size() == 1 &&
+      cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+  if (single_internal_display || !only_if_single_internal_display) {
+    success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state);
+    attempted_change = true;
+
+    // Force the DPMS on since the driver doesn't always detect that it
+    // should turn on. This is needed when coming back from idle suspend.
+    if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF)
+      native_display_delegate_->ForceDPMSOn();
+  }
+
+  native_display_delegate_->UngrabServer();
+  if (attempted_change)
+    NotifyObservers(success, new_state);
+  return success;
+}
+
+bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) {
+  if (!configure_display_)
+    return false;
+
+  VLOG(1) << "SetDisplayMode: state=" << DisplayStateToString(new_state);
+  if (display_state_ == new_state) {
+    // Cancel software mirroring if the state is moving from
+    // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to
+    // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED.
+    if (mirroring_controller_ &&
+        new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED)
+      mirroring_controller_->SetSoftwareMirroring(false);
+    NotifyObservers(true, new_state);
+    return true;
+  }
+
+  native_display_delegate_->GrabServer();
+  UpdateCachedDisplays();
+  const bool success = EnterStateOrFallBackToSoftwareMirroring(
+      new_state, requested_power_state_);
+  native_display_delegate_->UngrabServer();
+
+  NotifyObservers(success, new_state);
+  return success;
+}
+
+void DisplayConfigurator::OnConfigurationChanged() {
+  // Configure displays with |kConfigureDelayMs| delay,
+  // so that time-consuming ConfigureDisplays() won't be called multiple times.
+  if (configure_timer_.IsRunning()) {
+    // Note: when the timer is running it is possible that a different task
+    // (SetDisplayPower()) is scheduled. In these cases, prefer the already
+    // scheduled task to ConfigureDisplays() since ConfigureDisplays() performs
+    // only basic configuration while SetDisplayPower() will perform additional
+    // operations.
+    configure_timer_.Reset();
+  } else {
+    configure_timer_.Start(
+        FROM_HERE,
+        base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
+        this,
+        &DisplayConfigurator::ConfigureDisplays);
+  }
+}
+
+void DisplayConfigurator::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DisplayConfigurator::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void DisplayConfigurator::SuspendDisplays() {
+  // If the display is off due to user inactivity and there's only a single
+  // internal display connected, switch to the all-on state before
+  // suspending.  This shouldn't be very noticeable to the user since the
+  // backlight is off at this point, and doing this lets us resume directly
+  // into the "on" state, which greatly reduces resume times.
+  if (requested_power_state_ == chromeos::DISPLAY_POWER_ALL_OFF) {
+    SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
+                    kSetDisplayPowerOnlyIfSingleInternalDisplay);
+
+    // We need to make sure that the monitor configuration we just did actually
+    // completes before we return, because otherwise the X message could be
+    // racing with the HandleSuspendReadiness message.
+    native_display_delegate_->SyncWithServer();
+  }
+}
+
+void DisplayConfigurator::ResumeDisplays() {
+  // Force probing to ensure that we pick up any changes that were made
+  // while the system was suspended.
+  configure_timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(kResumeDelayMs),
+      base::Bind(base::IgnoreResult(&DisplayConfigurator::SetDisplayPower),
+                 base::Unretained(this),
+                 requested_power_state_,
+                 kSetDisplayPowerForceProbe));
+}
+
+void DisplayConfigurator::UpdateCachedDisplays() {
+  std::vector<DisplaySnapshot*> snapshots =
+      native_display_delegate_->GetDisplays();
+
+  cached_displays_.clear();
+  for (size_t i = 0; i < snapshots.size(); ++i) {
+    DisplayState display_state;
+    display_state.display = snapshots[i];
+    cached_displays_.push_back(display_state);
+  }
+
+  // Set |selected_mode| fields.
+  for (size_t i = 0; i < cached_displays_.size(); ++i) {
+    DisplayState* display_state = &cached_displays_[i];
+    if (display_state->display->has_proper_display_id()) {
+      gfx::Size size;
+      if (state_controller_ &&
+          state_controller_->GetResolutionForDisplayId(
+              display_state->display->display_id(), &size)) {
+        display_state->selected_mode =
+            FindDisplayModeMatchingSize(*display_state->display, size);
+      }
+    }
+    // Fall back to native mode.
+    if (!display_state->selected_mode)
+      display_state->selected_mode = display_state->display->native_mode();
+  }
+
+  // Set |mirror_mode| fields.
+  if (cached_displays_.size() == 2) {
+    bool one_is_internal =
+        cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+    bool two_is_internal =
+        cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL;
+    int internal_displays =
+        (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0);
+    DCHECK_LT(internal_displays, 2);
+    LOG_IF(WARNING, internal_displays == 2)
+        << "Two internal displays detected.";
+
+    bool can_mirror = false;
+    for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
+      // Try preserving external display's aspect ratio on the first attempt.
+      // If that fails, fall back to the highest matching resolution.
+      bool preserve_aspect = attempt == 0;
+
+      if (internal_displays == 1) {
+        if (one_is_internal) {
+          can_mirror = FindMirrorMode(&cached_displays_[0],
+                                      &cached_displays_[1],
+                                      is_panel_fitting_enabled_,
+                                      preserve_aspect);
+        } else {
+          DCHECK(two_is_internal);
+          can_mirror = FindMirrorMode(&cached_displays_[1],
+                                      &cached_displays_[0],
+                                      is_panel_fitting_enabled_,
+                                      preserve_aspect);
+        }
+      } else {  // if (internal_displays == 0)
+        // No panel fitting for external displays, so fall back to exact match.
+        can_mirror = FindMirrorMode(
+            &cached_displays_[0], &cached_displays_[1], false, preserve_aspect);
+        if (!can_mirror && preserve_aspect) {
+          // FindMirrorMode() will try to preserve aspect ratio of what it
+          // thinks is external display, so if it didn't succeed with one, maybe
+          // it will succeed with the other.  This way we will have the correct
+          // aspect ratio on at least one of them.
+          can_mirror = FindMirrorMode(&cached_displays_[1],
+                                      &cached_displays_[0],
+                                      false,
+                                      preserve_aspect);
+        }
+      }
+    }
+  }
+}
+
+bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display,
+                                         DisplayState* external_display,
+                                         bool try_panel_fitting,
+                                         bool preserve_aspect) {
+  const DisplayMode* internal_native_info =
+      internal_display->display->native_mode();
+  const DisplayMode* external_native_info =
+      external_display->display->native_mode();
+  if (!internal_native_info || !external_native_info)
+    return false;
+
+  // Check if some external display resolution can be mirrored on internal.
+  // Prefer the modes in the order they're present in DisplaySnapshot, assuming
+  // this is the order in which they look better on the monitor.
+  for (DisplayModeList::const_iterator external_it =
+           external_display->display->modes().begin();
+       external_it != external_display->display->modes().end();
+       ++external_it) {
+    const DisplayMode& external_info = **external_it;
+    bool is_native_aspect_ratio =
+        external_native_info->size().width() * external_info.size().height() ==
+        external_native_info->size().height() * external_info.size().width();
+    if (preserve_aspect && !is_native_aspect_ratio)
+      continue;  // Allow only aspect ratio preserving modes for mirroring.
+
+    // Try finding an exact match.
+    for (DisplayModeList::const_iterator internal_it =
+             internal_display->display->modes().begin();
+         internal_it != internal_display->display->modes().end();
+         ++internal_it) {
+      const DisplayMode& internal_info = **internal_it;
+      if (internal_info.size().width() == external_info.size().width() &&
+          internal_info.size().height() == external_info.size().height() &&
+          internal_info.is_interlaced() == external_info.is_interlaced()) {
+        internal_display->mirror_mode = *internal_it;
+        external_display->mirror_mode = *external_it;
+        return true;  // Mirror mode found.
+      }
+    }
+
+    // Try to create a matching internal display mode by panel fitting.
+    if (try_panel_fitting) {
+      // We can downscale by 1.125, and upscale indefinitely. Downscaling looks
+      // ugly, so, can fit == can upscale. Also, internal panels don't support
+      // fitting interlaced modes.
+      bool can_fit = internal_native_info->size().width() >=
+                         external_info.size().width() &&
+                     internal_native_info->size().height() >=
+                         external_info.size().height() &&
+                     !external_info.is_interlaced();
+      if (can_fit) {
+        native_display_delegate_->AddMode(*internal_display->display,
+                                          *external_it);
+        internal_display->display->add_mode(*external_it);
+        internal_display->mirror_mode = *external_it;
+        external_display->mirror_mode = *external_it;
+        return true;  // Mirror mode created.
+      }
+    }
+  }
+
+  return false;
+}
+
+void DisplayConfigurator::ConfigureDisplays() {
+  if (!configure_display_)
+    return;
+
+  native_display_delegate_->GrabServer();
+  UpdateCachedDisplays();
+  const MultipleDisplayState new_state = ChooseDisplayState(
+      requested_power_state_);
+  const bool success = EnterStateOrFallBackToSoftwareMirroring(
+      new_state, requested_power_state_);
+  native_display_delegate_->UngrabServer();
+
+  NotifyObservers(success, new_state);
+}
+
+void DisplayConfigurator::NotifyObservers(
+    bool success,
+    MultipleDisplayState attempted_state) {
+  if (success) {
+    FOR_EACH_OBSERVER(
+        Observer, observers_, OnDisplayModeChanged(cached_displays_));
+  } else {
+    FOR_EACH_OBSERVER(
+        Observer, observers_, OnDisplayModeChangeFailed(attempted_state));
+  }
+}
+
+bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring(
+    MultipleDisplayState display_state,
+    chromeos::DisplayPowerState power_state) {
+  bool success = EnterState(display_state, power_state);
+  if (mirroring_controller_) {
+    bool enable_software_mirroring = false;
+    if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) {
+      if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED ||
+          current_power_state_ != power_state)
+        EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state);
+      enable_software_mirroring = success =
+          display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
+    }
+    mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring);
+  }
+  return success;
+}
+
+bool DisplayConfigurator::EnterState(MultipleDisplayState display_state,
+                                     chromeos::DisplayPowerState power_state) {
+  std::vector<bool> display_power;
+  int num_on_displays =
+      GetDisplayPower(cached_displays_, power_state, &display_power);
+  VLOG(1) << "EnterState: display=" << DisplayStateToString(display_state)
+          << " power=" << DisplayPowerStateToString(power_state);
+
+  // Save the requested state so we'll try to use it next time even if we fail.
+  requested_power_state_ = power_state;
+
+  // Framebuffer dimensions.
+  gfx::Size size;
+
+  std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point());
+  std::vector<const DisplayMode*> new_mode;
+  for (size_t i = 0; i < cached_displays_.size(); ++i)
+    new_mode.push_back(cached_displays_[i].display->current_mode());
+
+  switch (display_state) {
+    case MULTIPLE_DISPLAY_STATE_INVALID:
+      NOTREACHED() << "Ignoring request to enter invalid state with "
+                   << cached_displays_.size() << " connected display(s)";
+      return false;
+    case MULTIPLE_DISPLAY_STATE_HEADLESS:
+      if (cached_displays_.size() != 0) {
+        LOG(WARNING) << "Ignoring request to enter headless mode with "
+                     << cached_displays_.size() << " connected display(s)";
+        return false;
+      }
+      break;
+    case MULTIPLE_DISPLAY_STATE_SINGLE: {
+      // If there are multiple displays connected, only one should be turned on.
+      if (cached_displays_.size() != 1 && num_on_displays != 1) {
+        LOG(WARNING) << "Ignoring request to enter single mode with "
+                     << cached_displays_.size() << " connected displays and "
+                     << num_on_displays << " turned on";
+        return false;
+      }
+
+      for (size_t i = 0; i < cached_displays_.size(); ++i) {
+        DisplayState* state = &cached_displays_[i];
+        new_mode[i] = display_power[i] ? state->selected_mode : NULL;
+
+        if (display_power[i] || cached_displays_.size() == 1) {
+          const DisplayMode* mode_info = state->selected_mode;
+          if (!mode_info) {
+            LOG(WARNING) << "No selected mode when configuring display: "
+                         << state->display->ToString();
+            return false;
+          }
+          if (mode_info->size() == gfx::Size(1024, 768)) {
+            VLOG(1) << "Potentially misdetecting display(1024x768):"
+                    << " displays size=" << cached_displays_.size()
+                    << ", num_on_displays=" << num_on_displays
+                    << ", current size:" << size.width() << "x" << size.height()
+                    << ", i=" << i << ", display=" << state->display->ToString()
+                    << ", display_mode=" << mode_info->ToString();
+          }
+          size = mode_info->size();
+        }
+      }
+      break;
+    }
+    case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: {
+      if (cached_displays_.size() != 2 ||
+          (num_on_displays != 0 && num_on_displays != 2)) {
+        LOG(WARNING) << "Ignoring request to enter mirrored mode with "
+                     << cached_displays_.size() << " connected display(s) and "
+                     << num_on_displays << " turned on";
+        return false;
+      }
+
+      const DisplayMode* mode_info = cached_displays_[0].mirror_mode;
+      if (!mode_info) {
+        LOG(WARNING) << "No mirror mode when configuring display: "
+                     << cached_displays_[0].display->ToString();
+        return false;
+      }
+      size = mode_info->size();
+
+      for (size_t i = 0; i < cached_displays_.size(); ++i) {
+        DisplayState* state = &cached_displays_[i];
+        new_mode[i] = display_power[i] ? state->mirror_mode : NULL;
+      }
+      break;
+    }
+    case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: {
+      if (cached_displays_.size() != 2 ||
+          (num_on_displays != 0 && num_on_displays != 2)) {
+        LOG(WARNING) << "Ignoring request to enter extended mode with "
+                     << cached_displays_.size() << " connected display(s) and "
+                     << num_on_displays << " turned on";
+        return false;
+      }
+
+      for (size_t i = 0; i < cached_displays_.size(); ++i) {
+        DisplayState* state = &cached_displays_[i];
+        new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0);
+        new_mode[i] = display_power[i] ? state->selected_mode : NULL;
+
+        // Retain the full screen size even if all displays are off so the
+        // same desktop configuration can be restored when the displays are
+        // turned back on.
+        const DisplayMode* mode_info = cached_displays_[i].selected_mode;
+        if (!mode_info) {
+          LOG(WARNING) << "No selected mode when configuring display: "
+                       << state->display->ToString();
+          return false;
+        }
+
+        size.set_width(std::max<int>(size.width(), mode_info->size().width()));
+        size.set_height(size.height() + (size.height() ? kVerticalGap : 0) +
+                        mode_info->size().height());
+      }
+      break;
+    }
+  }
+
+  // Finally, apply the desired changes.
+  bool all_succeeded = true;
+  if (!cached_displays_.empty()) {
+    native_display_delegate_->CreateFrameBuffer(size);
+    for (size_t i = 0; i < cached_displays_.size(); ++i) {
+      const DisplayState& state = cached_displays_[i];
+      bool configure_succeeded = false;
+
+      while (true) {
+        if (native_display_delegate_->Configure(
+                *state.display, new_mode[i], new_origins[i])) {
+          state.display->set_current_mode(new_mode[i]);
+          state.display->set_origin(new_origins[i]);
+
+          configure_succeeded = true;
+          break;
+        }
+
+        const DisplayMode* mode_info = new_mode[i];
+        if (!mode_info)
+          break;
+
+        // Find the mode with the next-best resolution and see if that can
+        // be set.
+        int best_mode_pixels = 0;
+
+        int current_mode_pixels = mode_info->size().GetArea();
+        for (DisplayModeList::const_iterator it =
+                 state.display->modes().begin();
+             it != state.display->modes().end();
+             it++) {
+          int pixel_count = (*it)->size().GetArea();
+          if ((pixel_count < current_mode_pixels) &&
+              (pixel_count > best_mode_pixels)) {
+            new_mode[i] = *it;
+            best_mode_pixels = pixel_count;
+          }
+        }
+
+        if (best_mode_pixels == 0)
+          break;
+      }
+
+      if (!configure_succeeded)
+        all_succeeded = false;
+
+      // If we are trying to set mirror mode and one of the modesets fails,
+      // then the two monitors will be mis-matched.  In this case, return
+      // false to let the observers be aware.
+      if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR &&
+          display_power[i] &&
+          state.display->current_mode() != state.mirror_mode)
+        all_succeeded = false;
+    }
+  }
+
+  if (all_succeeded) {
+    display_state_ = display_state;
+    current_power_state_ = power_state;
+    framebuffer_size_ = size;
+  }
+  return all_succeeded;
+}
+
+MultipleDisplayState DisplayConfigurator::ChooseDisplayState(
+    chromeos::DisplayPowerState power_state) const {
+  int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL);
+  switch (cached_displays_.size()) {
+    case 0:
+      return MULTIPLE_DISPLAY_STATE_HEADLESS;
+    case 1:
+      return MULTIPLE_DISPLAY_STATE_SINGLE;
+    case 2: {
+      if (num_on_displays == 1) {
+        // If only one display is currently turned on, return the "single"
+        // state so that its native mode will be used.
+        return MULTIPLE_DISPLAY_STATE_SINGLE;
+      } else {
+        if (!state_controller_)
+          return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
+        // With either both displays on or both displays off, use one of the
+        // dual modes.
+        std::vector<int64_t> display_ids;
+        for (size_t i = 0; i < cached_displays_.size(); ++i) {
+          // If display id isn't available, switch to extended mode.
+          if (!cached_displays_[i].display->has_proper_display_id())
+            return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED;
+          display_ids.push_back(cached_displays_[i].display->display_id());
+        }
+        return state_controller_->GetStateForDisplayIds(display_ids);
+      }
+    }
+    default:
+      NOTREACHED();
+  }
+  return MULTIPLE_DISPLAY_STATE_INVALID;
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/display_configurator.h b/ui/display/chromeos/display_configurator.h
new file mode 100644
index 0000000..bd0576c
--- /dev/null
+++ b/ui/display/chromeos/display_configurator.h
@@ -0,0 +1,354 @@
+// 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_DISPLAY_CHROMEOS_DISPLAY_CONFIGURATOR_H_
+#define UI_DISPLAY_CHROMEOS_DISPLAY_CONFIGURATOR_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/event_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/timer/timer.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+#include "ui/display/display_export.h"
+#include "ui/display/types/display_constants.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace gfx {
+class Point;
+class Size;
+}
+
+namespace ui {
+class DisplayMode;
+class DisplaySnapshot;
+class NativeDisplayDelegate;
+
+// This class interacts directly with the system display configurator.
+class DISPLAY_EXPORT DisplayConfigurator : public NativeDisplayObserver {
+ public:
+  typedef uint64_t ContentProtectionClientId;
+  static const ContentProtectionClientId kInvalidClientId = 0;
+
+  struct DisplayState {
+    DisplayState();
+
+    DisplaySnapshot* display;  // Not owned.
+
+    // User-selected mode for the display.
+    const DisplayMode* selected_mode;
+
+    // Mode used when displaying the same desktop on multiple displays.
+    const DisplayMode* mirror_mode;
+  };
+
+  typedef std::vector<DisplayState> DisplayStateList;
+
+  class Observer {
+   public:
+    virtual ~Observer() {}
+
+    // Called after the display mode has been changed. |display| contains the
+    // just-applied configuration. Note that the X server is no longer grabbed
+    // when this method is called, so the actual configuration could've changed
+    // already.
+    virtual void OnDisplayModeChanged(
+        const std::vector<DisplayState>& displays) {}
+
+    // Called after a display mode change attempt failed. |failed_new_state| is
+    // the new state which the system failed to enter.
+    virtual void OnDisplayModeChangeFailed(
+        MultipleDisplayState failed_new_state) {}
+  };
+
+  // Interface for classes that make decisions about which display state
+  // should be used.
+  class StateController {
+   public:
+    virtual ~StateController() {}
+
+    // Called when displays are detected.
+    virtual MultipleDisplayState GetStateForDisplayIds(
+        const std::vector<int64_t>& display_ids) const = 0;
+
+    // Queries the resolution (|size|) in pixels to select display mode for the
+    // given display id.
+    virtual bool GetResolutionForDisplayId(int64_t display_id,
+                                           gfx::Size* size) const = 0;
+  };
+
+  // Interface for classes that implement software based mirroring.
+  class SoftwareMirroringController {
+   public:
+    virtual ~SoftwareMirroringController() {}
+
+    // Called when the hardware mirroring failed.
+    virtual void SetSoftwareMirroring(bool enabled) = 0;
+    virtual bool SoftwareMirroringEnabled() const = 0;
+  };
+
+  // Helper class used by tests.
+  class TestApi {
+   public:
+    TestApi(DisplayConfigurator* configurator) : configurator_(configurator) {}
+    ~TestApi() {}
+
+    // If |configure_timer_| is started, stops the timer, runs
+    // ConfigureDisplays(), and returns true; returns false otherwise.
+    bool TriggerConfigureTimeout() WARN_UNUSED_RESULT;
+
+   private:
+    DisplayConfigurator* configurator_;  // not owned
+
+    DISALLOW_COPY_AND_ASSIGN(TestApi);
+  };
+
+  // Flags that can be passed to SetDisplayPower().
+  static const int kSetDisplayPowerNoFlags;
+  // Configure displays even if the passed-in state matches |power_state_|.
+  static const int kSetDisplayPowerForceProbe;
+  // Do not change the state if multiple displays are connected or if the
+  // only connected display is external.
+  static const int kSetDisplayPowerOnlyIfSingleInternalDisplay;
+
+  // Gap between screens so cursor at bottom of active display doesn't
+  // partially appear on top of inactive display. Higher numbers guard
+  // against larger cursors, but also waste more memory.
+  // For simplicity, this is hard-coded to avoid the complexity of always
+  // determining the DPI of the screen and rationalizing which screen we
+  // need to use for the DPI calculation.
+  // See crbug.com/130188 for initial discussion.
+  static const int kVerticalGap = 60;
+
+  // Returns the mode within |display| that matches the given size with highest
+  // refresh rate. Returns None if no matching display was found.
+  static const DisplayMode* FindDisplayModeMatchingSize(
+      const DisplaySnapshot& display,
+      const gfx::Size& size);
+
+  DisplayConfigurator();
+  virtual ~DisplayConfigurator();
+
+  MultipleDisplayState display_state() const { return display_state_; }
+  chromeos::DisplayPowerState requested_power_state() const {
+    return requested_power_state_;
+  }
+  const gfx::Size framebuffer_size() const { return framebuffer_size_; }
+  const std::vector<DisplayState>& cached_displays() const {
+    return cached_displays_;
+  }
+
+  void set_state_controller(StateController* controller) {
+    state_controller_ = controller;
+  }
+  void set_mirroring_controller(SoftwareMirroringController* controller) {
+    mirroring_controller_ = controller;
+  }
+
+  // Replaces |native_display_delegate_| with the delegate passed in and sets
+  // |configure_display_| to true. Should be called before Init().
+  void SetDelegateForTesting(
+      scoped_ptr<NativeDisplayDelegate> display_delegate);
+
+  // Sets the initial value of |power_state_|.  Must be called before Start().
+  void SetInitialDisplayPower(chromeos::DisplayPowerState power_state);
+
+  // Initialization, must be called right after constructor.
+  // |is_panel_fitting_enabled| indicates hardware panel fitting support.
+  void Init(bool is_panel_fitting_enabled);
+
+  // Does initial configuration of displays during startup.
+  // If |background_color_argb| is non zero and there are multiple displays,
+  // DisplayConfigurator sets the background color of X's RootWindow to this
+  // color.
+  void ForceInitialConfigure(uint32_t background_color_argb);
+
+  // Stop handling display configuration events/requests.
+  void PrepareForExit();
+
+  // Called when powerd notifies us that some set of displays should be turned
+  // on or off.  This requires enabling or disabling the CRTC associated with
+  // the display(s) in question so that the low power state is engaged.
+  // |flags| contains bitwise-or-ed kSetDisplayPower* values. Returns true if
+  // the system successfully enters (or was already in) |power_state|.
+  bool SetDisplayPower(chromeos::DisplayPowerState power_state, int flags);
+
+  // Force switching the display mode to |new_state|. Returns false if
+  // switching failed (possibly because |new_state| is invalid for the
+  // current set of connected displays).
+  bool SetDisplayMode(MultipleDisplayState new_state);
+
+  // NativeDisplayDelegate::Observer overrides:
+  virtual void OnConfigurationChanged() OVERRIDE;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // Sets all the displays into pre-suspend mode; usually this means
+  // configure them for their resume state. This allows faster resume on
+  // machines where display configuration is slow.
+  void SuspendDisplays();
+
+  // Reprobes displays to handle changes made while the system was
+  // suspended.
+  void ResumeDisplays();
+
+  // Registers a client for display protection and requests a client id. Returns
+  // 0 if requesting failed.
+  ContentProtectionClientId RegisterContentProtectionClient();
+
+  // Unregisters the client.
+  void UnregisterContentProtectionClient(ContentProtectionClientId client_id);
+
+  // Queries link status and protection status.
+  // |link_mask| is the type of connected display links, which is a bitmask of
+  // DisplayConnectionType values. |protection_mask| is the desired protection
+  // methods, which is a bitmask of the ContentProtectionMethod values.
+  // Returns true on success.
+  bool QueryContentProtectionStatus(ContentProtectionClientId client_id,
+                                    int64_t display_id,
+                                    uint32_t* link_mask,
+                                    uint32_t* protection_mask);
+
+  // Requests the desired protection methods.
+  // |protection_mask| is the desired protection methods, which is a bitmask
+  // of the ContentProtectionMethod values.
+  // Returns true when the protection request has been made.
+  bool EnableContentProtection(ContentProtectionClientId client_id,
+                               int64_t display_id,
+                               uint32_t desired_protection_mask);
+
+  // Checks the available color profiles for |display_id| and fills the result
+  // into |profiles|.
+  std::vector<ui::ColorCalibrationProfile> GetAvailableColorCalibrationProfiles(
+      int64_t display_id);
+
+  // Updates the color calibration to |new_profile|.
+  bool SetColorCalibrationProfile(int64_t display_id,
+                                  ui::ColorCalibrationProfile new_profile);
+
+ private:
+  // Mapping a display_id to a protection request bitmask.
+  typedef std::map<int64_t, uint32_t> ContentProtections;
+  // Mapping a client to its protection request.
+  typedef std::map<ContentProtectionClientId, ContentProtections>
+      ProtectionRequests;
+
+  // Performs platform specific delegate initialization.
+  scoped_ptr<NativeDisplayDelegate> CreatePlatformNativeDisplayDelegate();
+
+  // Updates |cached_displays_| to contain currently-connected displays. Calls
+  // |delegate_->GetDisplays()| and then does additional work, like finding the
+  // mirror mode and setting user-preferred modes. Note that the server must be
+  // grabbed via |delegate_->GrabServer()| first.
+  void UpdateCachedDisplays();
+
+  // Helper method for UpdateCachedDisplays() that initializes the passed-in
+  // displays' |mirror_mode| fields by looking for a mode in |internal_display|
+  // and |external_display| having the same resolution. Returns false if a
+  // shared
+  // mode wasn't found or created.
+  //
+  // |try_panel_fitting| allows creating a panel-fitting mode for
+  // |internal_display| instead of only searching for a matching mode (note that
+  // it may lead to a crash if |internal_info| is not capable of panel fitting).
+  //
+  // |preserve_aspect| limits the search/creation only to the modes having the
+  // native aspect ratio of |external_display|.
+  bool FindMirrorMode(DisplayState* internal_display,
+                      DisplayState* external_display,
+                      bool try_panel_fitting,
+                      bool preserve_aspect);
+
+  // Configures displays.
+  void ConfigureDisplays();
+
+  // Notifies observers about an attempted state change.
+  void NotifyObservers(bool success, MultipleDisplayState attempted_state);
+
+  // Switches to the state specified in |display_state| and |power_state|.
+  // If the hardware mirroring failed and |mirroring_controller_| is set,
+  // it switches to |STATE_DUAL_EXTENDED| and calls |SetSoftwareMirroring()|
+  // to enable software based mirroring.
+  // On success, updates |display_state_|, |power_state_|, and
+  // |cached_displays_| and returns true.
+  bool EnterStateOrFallBackToSoftwareMirroring(
+      MultipleDisplayState display_state,
+      chromeos::DisplayPowerState power_state);
+
+  // Switches to the state specified in |display_state| and |power_state|.
+  // On success, updates |display_state_|, |power_state_|, and
+  // |cached_displays_| and returns true.
+  bool EnterState(MultipleDisplayState display_state,
+                  chromeos::DisplayPowerState power_state);
+
+  // Returns the display state that should be used with |cached_displays_| while
+  // in |power_state|.
+  MultipleDisplayState ChooseDisplayState(
+      chromeos::DisplayPowerState power_state) const;
+
+  // Returns the ratio between mirrored mode area and native mode area:
+  // (mirror_mode_width * mirrow_mode_height) / (native_width * native_height)
+  float GetMirroredDisplayAreaRatio(const DisplayState& display);
+
+  // Returns true if in either hardware or software mirroring mode.
+  bool IsMirroring() const;
+
+  // Applies display protections according to requests.
+  bool ApplyProtections(const ContentProtections& requests);
+
+  StateController* state_controller_;
+  SoftwareMirroringController* mirroring_controller_;
+  scoped_ptr<NativeDisplayDelegate> native_display_delegate_;
+
+  // Used to enable modes which rely on panel fitting.
+  bool is_panel_fitting_enabled_;
+
+  // This is detected by the constructor to determine whether or not we should
+  // be enabled.  If we aren't running on ChromeOS, we can't assume that the
+  // Xrandr X11 extension is supported.
+  // If this flag is set to false, any attempts to change the display
+  // configuration to immediately fail without changing the state.
+  bool configure_display_;
+
+  // The current display state.
+  MultipleDisplayState display_state_;
+
+  gfx::Size framebuffer_size_;
+
+  // The last-requested and current power state. These may differ if
+  // configuration fails: SetDisplayMode() needs the last-requested state while
+  // SetDisplayPower() needs the current state.
+  chromeos::DisplayPowerState requested_power_state_;
+  chromeos::DisplayPowerState current_power_state_;
+
+  // Most-recently-used display configuration. Note that the actual
+  // configuration changes asynchronously.
+  DisplayStateList cached_displays_;
+
+  ObserverList<Observer> observers_;
+
+  // The timer to delay configuring displays. This is used to aggregate multiple
+  // display configuration events when they are reported in short time spans.
+  // See comment for NativeDisplayEventDispatcherX11 for more details.
+  base::OneShotTimer<DisplayConfigurator> configure_timer_;
+
+  // Id for next display protection client.
+  ContentProtectionClientId next_display_protection_client_id_;
+
+  // Display protection requests of each client.
+  ProtectionRequests client_protection_requests_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayConfigurator);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_DISPLAY_CONFIGURATOR_H_
diff --git a/ui/display/chromeos/display_configurator_unittest.cc b/ui/display/chromeos/display_configurator_unittest.cc
new file mode 100644
index 0000000..79cfd7b
--- /dev/null
+++ b/ui/display/chromeos/display_configurator_unittest.cc
@@ -0,0 +1,1341 @@
+// 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/display/chromeos/display_configurator.h"
+
+#include <stdint.h>
+
+#include <cmath>
+#include <cstdarg>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/format_macros.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/chromeos/test/test_display_snapshot.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/display/types/native_display_delegate.h"
+
+namespace ui {
+
+namespace {
+
+// Strings returned by TestNativeDisplayDelegate::GetActionsAndClear() to
+// describe various actions that were performed.
+const char kInitXRandR[] = "init";
+const char kGrab[] = "grab";
+const char kUngrab[] = "ungrab";
+const char kSync[] = "sync";
+const char kForceDPMS[] = "dpms";
+
+// String returned by TestNativeDisplayDelegate::GetActionsAndClear() if no
+// actions were requested.
+const char kNoActions[] = "";
+
+std::string DisplaySnapshotToString(const DisplaySnapshot& output) {
+  return base::StringPrintf("id=%" PRId64, output.display_id());
+}
+
+// Returns a string describing a TestNativeDisplayDelegate::SetBackgroundColor()
+// call.
+std::string GetBackgroundAction(uint32_t color_argb) {
+  return base::StringPrintf("background(0x%x)", color_argb);
+}
+
+// Returns a string describing a TestNativeDisplayDelegate::AddOutputMode()
+// call.
+std::string GetAddOutputModeAction(const DisplaySnapshot& output,
+                                   const DisplayMode* mode) {
+  return base::StringPrintf("add_mode(output=%" PRId64 ",mode=%s)",
+                            output.display_id(),
+                            mode->ToString().c_str());
+}
+
+// Returns a string describing a TestNativeDisplayDelegate::Configure()
+// call.
+std::string GetCrtcAction(const DisplaySnapshot& output,
+                          const DisplayMode* mode,
+                          const gfx::Point& origin) {
+  return base::StringPrintf("crtc(display=[%s],x=%d,y=%d,mode=[%s])",
+                            DisplaySnapshotToString(output).c_str(),
+                            origin.x(),
+                            origin.y(),
+                            mode ? mode->ToString().c_str() : "NULL");
+}
+
+// Returns a string describing a TestNativeDisplayDelegate::CreateFramebuffer()
+// call.
+std::string GetFramebufferAction(const gfx::Size& size,
+                                 const DisplaySnapshot* out1,
+                                 const DisplaySnapshot* out2) {
+  return base::StringPrintf(
+      "framebuffer(width=%d,height=%d,display1=%s,display2=%s)",
+      size.width(),
+      size.height(),
+      out1 ? DisplaySnapshotToString(*out1).c_str() : "NULL",
+      out2 ? DisplaySnapshotToString(*out2).c_str() : "NULL");
+}
+
+// Returns a string describing a TestNativeDisplayDelegate::SetHDCPState() call.
+std::string GetSetHDCPStateAction(const DisplaySnapshot& output,
+                                  HDCPState state) {
+  return base::StringPrintf(
+      "set_hdcp(id=%" PRId64 ",state=%d)", output.display_id(), state);
+}
+
+// Joins a sequence of strings describing actions (e.g. kScreenDim) such
+// that they can be compared against a string returned by
+// ActionLogger::GetActionsAndClear().  The list of actions must be
+// terminated by a NULL pointer.
+std::string JoinActions(const char* action, ...) {
+  std::string actions;
+
+  va_list arg_list;
+  va_start(arg_list, action);
+  while (action) {
+    if (!actions.empty())
+      actions += ",";
+    actions += action;
+    action = va_arg(arg_list, const char*);
+  }
+  va_end(arg_list);
+  return actions;
+}
+
+class ActionLogger {
+ public:
+  ActionLogger() {}
+
+  void AppendAction(const std::string& action) {
+    if (!actions_.empty())
+      actions_ += ",";
+    actions_ += action;
+  }
+
+  // Returns a comma-separated string describing the actions that were
+  // requested since the previous call to GetActionsAndClear() (i.e.
+  // results are non-repeatable).
+  std::string GetActionsAndClear() {
+    std::string actions = actions_;
+    actions_.clear();
+    return actions;
+  }
+
+ private:
+  std::string actions_;
+
+  DISALLOW_COPY_AND_ASSIGN(ActionLogger);
+};
+
+class TestNativeDisplayDelegate : public NativeDisplayDelegate {
+ public:
+  // Ownership of |log| remains with the caller.
+  explicit TestNativeDisplayDelegate(ActionLogger* log)
+      : max_configurable_pixels_(0),
+        hdcp_state_(HDCP_STATE_UNDESIRED),
+        log_(log) {}
+  virtual ~TestNativeDisplayDelegate() {}
+
+  const std::vector<DisplaySnapshot*>& outputs() const { return outputs_; }
+  void set_outputs(const std::vector<DisplaySnapshot*>& outputs) {
+    outputs_ = outputs;
+  }
+
+  void set_max_configurable_pixels(int pixels) {
+    max_configurable_pixels_ = pixels;
+  }
+
+  void set_hdcp_state(HDCPState state) { hdcp_state_ = state; }
+
+  // DisplayConfigurator::Delegate overrides:
+  virtual void Initialize() OVERRIDE { log_->AppendAction(kInitXRandR); }
+  virtual void GrabServer() OVERRIDE { log_->AppendAction(kGrab); }
+  virtual void UngrabServer() OVERRIDE { log_->AppendAction(kUngrab); }
+  virtual void SyncWithServer() OVERRIDE { log_->AppendAction(kSync); }
+  virtual void SetBackgroundColor(uint32_t color_argb) OVERRIDE {
+    log_->AppendAction(GetBackgroundAction(color_argb));
+  }
+  virtual void ForceDPMSOn() OVERRIDE { log_->AppendAction(kForceDPMS); }
+  virtual std::vector<DisplaySnapshot*> GetDisplays() OVERRIDE {
+    return outputs_;
+  }
+  virtual void AddMode(const DisplaySnapshot& output,
+                       const DisplayMode* mode) OVERRIDE {
+    log_->AppendAction(GetAddOutputModeAction(output, mode));
+  }
+  virtual bool Configure(const DisplaySnapshot& output,
+                         const DisplayMode* mode,
+                         const gfx::Point& origin) OVERRIDE {
+    log_->AppendAction(GetCrtcAction(output, mode, origin));
+
+    if (max_configurable_pixels_ == 0)
+      return true;
+
+    if (!mode)
+      return false;
+
+    return mode->size().GetArea() <= max_configurable_pixels_;
+  }
+  virtual void CreateFrameBuffer(const gfx::Size& size) OVERRIDE {
+    log_->AppendAction(
+        GetFramebufferAction(size,
+                             outputs_.size() >= 1 ? outputs_[0] : NULL,
+                             outputs_.size() >= 2 ? outputs_[1] : NULL));
+  }
+  virtual bool GetHDCPState(const DisplaySnapshot& output,
+                            HDCPState* state) OVERRIDE {
+    *state = hdcp_state_;
+    return true;
+  }
+
+  virtual bool SetHDCPState(const DisplaySnapshot& output,
+                            HDCPState state) OVERRIDE {
+    log_->AppendAction(GetSetHDCPStateAction(output, state));
+    return true;
+  }
+
+  virtual std::vector<ui::ColorCalibrationProfile>
+  GetAvailableColorCalibrationProfiles(const DisplaySnapshot& output) OVERRIDE {
+    return std::vector<ui::ColorCalibrationProfile>();
+  }
+
+  virtual bool SetColorCalibrationProfile(
+      const DisplaySnapshot& output,
+      ui::ColorCalibrationProfile new_profile) OVERRIDE {
+    return false;
+  }
+
+  virtual void AddObserver(NativeDisplayObserver* observer) OVERRIDE {}
+
+  virtual void RemoveObserver(NativeDisplayObserver* observer) OVERRIDE {}
+
+ private:
+  // Outputs to be returned by GetDisplays().
+  std::vector<DisplaySnapshot*> outputs_;
+
+  // |max_configurable_pixels_| represents the maximum number of pixels that
+  // Configure will support.  Tests can use this to force Configure
+  // to fail if attempting to set a resolution that is higher than what
+  // a device might support under a given circumstance.
+  // A value of 0 means that no limit is enforced and Configure will
+  // return success regardless of the resolution.
+  int max_configurable_pixels_;
+
+  // Result value of GetHDCPState().
+  HDCPState hdcp_state_;
+
+  ActionLogger* log_;  // Not owned.
+
+  DISALLOW_COPY_AND_ASSIGN(TestNativeDisplayDelegate);
+};
+
+class TestObserver : public DisplayConfigurator::Observer {
+ public:
+  explicit TestObserver(DisplayConfigurator* configurator)
+      : configurator_(configurator) {
+    Reset();
+    configurator_->AddObserver(this);
+  }
+  virtual ~TestObserver() { configurator_->RemoveObserver(this); }
+
+  int num_changes() const { return num_changes_; }
+  int num_failures() const { return num_failures_; }
+  const DisplayConfigurator::DisplayStateList& latest_outputs() const {
+    return latest_outputs_;
+  }
+  MultipleDisplayState latest_failed_state() const {
+    return latest_failed_state_;
+  }
+
+  void Reset() {
+    num_changes_ = 0;
+    num_failures_ = 0;
+    latest_outputs_.clear();
+    latest_failed_state_ = MULTIPLE_DISPLAY_STATE_INVALID;
+  }
+
+  // DisplayConfigurator::Observer overrides:
+  virtual void OnDisplayModeChanged(
+      const DisplayConfigurator::DisplayStateList& outputs) OVERRIDE {
+    num_changes_++;
+    latest_outputs_ = outputs;
+  }
+
+  virtual void OnDisplayModeChangeFailed(MultipleDisplayState failed_new_state)
+      OVERRIDE {
+    num_failures_++;
+    latest_failed_state_ = failed_new_state;
+  }
+
+ private:
+  DisplayConfigurator* configurator_;  // Not owned.
+
+  // Number of times that OnDisplayMode*() has been called.
+  int num_changes_;
+  int num_failures_;
+
+  // Parameters most recently passed to OnDisplayMode*().
+  DisplayConfigurator::DisplayStateList latest_outputs_;
+  MultipleDisplayState latest_failed_state_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestObserver);
+};
+
+class TestStateController : public DisplayConfigurator::StateController {
+ public:
+  TestStateController() : state_(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) {}
+  virtual ~TestStateController() {}
+
+  void set_state(MultipleDisplayState state) { state_ = state; }
+
+  // DisplayConfigurator::StateController overrides:
+  virtual MultipleDisplayState GetStateForDisplayIds(
+      const std::vector<int64_t>& outputs) const OVERRIDE {
+    return state_;
+  }
+  virtual bool GetResolutionForDisplayId(int64_t display_id,
+                                         gfx::Size* size) const OVERRIDE {
+    return false;
+  }
+
+ private:
+  MultipleDisplayState state_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestStateController);
+};
+
+class TestMirroringController
+    : public DisplayConfigurator::SoftwareMirroringController {
+ public:
+  TestMirroringController() : software_mirroring_enabled_(false) {}
+  virtual ~TestMirroringController() {}
+
+  virtual void SetSoftwareMirroring(bool enabled) OVERRIDE {
+    software_mirroring_enabled_ = enabled;
+  }
+
+  virtual bool SoftwareMirroringEnabled() const OVERRIDE {
+    return software_mirroring_enabled_;
+  }
+
+ private:
+  bool software_mirroring_enabled_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestMirroringController);
+};
+
+class DisplayConfiguratorTest : public testing::Test {
+ public:
+  DisplayConfiguratorTest()
+      : small_mode_(gfx::Size(1366, 768), false, 60.0f),
+        big_mode_(gfx::Size(2560, 1600), false, 60.0f),
+        observer_(&configurator_),
+        test_api_(&configurator_) {}
+  virtual ~DisplayConfiguratorTest() {}
+
+  virtual void SetUp() OVERRIDE {
+    log_.reset(new ActionLogger());
+
+    native_display_delegate_ = new TestNativeDisplayDelegate(log_.get());
+    configurator_.SetDelegateForTesting(
+        scoped_ptr<NativeDisplayDelegate>(native_display_delegate_));
+
+    configurator_.set_state_controller(&state_controller_);
+    configurator_.set_mirroring_controller(&mirroring_controller_);
+
+    std::vector<const DisplayMode*> modes;
+    modes.push_back(&small_mode_);
+
+    TestDisplaySnapshot* o = &outputs_[0];
+    o->set_current_mode(&small_mode_);
+    o->set_native_mode(&small_mode_);
+    o->set_modes(modes);
+    o->set_type(DISPLAY_CONNECTION_TYPE_INTERNAL);
+    o->set_is_aspect_preserving_scaling(true);
+    o->set_display_id(123);
+    o->set_has_proper_display_id(true);
+
+    o = &outputs_[1];
+    o->set_current_mode(&big_mode_);
+    o->set_native_mode(&big_mode_);
+    modes.push_back(&big_mode_);
+    o->set_modes(modes);
+    o->set_type(DISPLAY_CONNECTION_TYPE_HDMI);
+    o->set_is_aspect_preserving_scaling(true);
+    o->set_display_id(456);
+    o->set_has_proper_display_id(true);
+
+    UpdateOutputs(2, false);
+  }
+
+  // Predefined modes that can be used by outputs.
+  const DisplayMode small_mode_;
+  const DisplayMode big_mode_;
+
+ protected:
+  // Configures |native_display_delegate_| to return the first |num_outputs|
+  // entries from
+  // |outputs_|. If |send_events| is true, also sends screen-change and
+  // output-change events to |configurator_| and triggers the configure
+  // timeout if one was scheduled.
+  void UpdateOutputs(size_t num_outputs, bool send_events) {
+    ASSERT_LE(num_outputs, arraysize(outputs_));
+    std::vector<DisplaySnapshot*> outputs;
+    for (size_t i = 0; i < num_outputs; ++i)
+      outputs.push_back(&outputs_[i]);
+    native_display_delegate_->set_outputs(outputs);
+
+    if (send_events) {
+      configurator_.OnConfigurationChanged();
+      EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+    }
+  }
+
+  // Initializes |configurator_| with a single internal display.
+  void InitWithSingleOutput() {
+    UpdateOutputs(1, false);
+    EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+    configurator_.Init(false);
+    EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+    configurator_.ForceInitialConfigure(0);
+    EXPECT_EQ(
+        JoinActions(
+            kGrab,
+            kInitXRandR,
+            GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL)
+                .c_str(),
+            GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+            kForceDPMS,
+            kUngrab,
+            NULL),
+        log_->GetActionsAndClear());
+  }
+
+  base::MessageLoop message_loop_;
+  TestStateController state_controller_;
+  TestMirroringController mirroring_controller_;
+  DisplayConfigurator configurator_;
+  TestObserver observer_;
+  scoped_ptr<ActionLogger> log_;
+  TestNativeDisplayDelegate* native_display_delegate_;  // not owned
+  DisplayConfigurator::TestApi test_api_;
+
+  TestDisplaySnapshot outputs_[2];
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(DisplayConfiguratorTest);
+};
+
+}  // namespace
+
+TEST_F(DisplayConfiguratorTest, FindDisplayModeMatchingSize) {
+  ScopedVector<const DisplayMode> modes;
+
+  // Fields are width, height, interlaced, refresh rate.
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1200), false, 60.0));
+  // Different rates.
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 30.0));
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 50.0));
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 40.0));
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 0.0));
+  // Interlaced vs non-interlaced.
+  modes.push_back(new DisplayMode(gfx::Size(1280, 720), true, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1280, 720), false, 40.0));
+  // Interlaced only.
+  modes.push_back(new DisplayMode(gfx::Size(1024, 768), true, 0.0));
+  modes.push_back(new DisplayMode(gfx::Size(1024, 768), true, 40.0));
+  modes.push_back(new DisplayMode(gfx::Size(1024, 768), true, 60.0));
+  // Mixed.
+  modes.push_back(new DisplayMode(gfx::Size(1024, 600), true, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1024, 600), false, 40.0));
+  modes.push_back(new DisplayMode(gfx::Size(1024, 600), false, 50.0));
+  // Just one interlaced mode.
+  modes.push_back(new DisplayMode(gfx::Size(640, 480), true, 60.0));
+  // Refresh rate not available.
+  modes.push_back(new DisplayMode(gfx::Size(320, 200), false, 0.0));
+
+  TestDisplaySnapshot output;
+  output.set_modes(modes.get());
+
+  EXPECT_EQ(modes[0],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1920, 1200)));
+
+  // Should pick highest refresh rate.
+  EXPECT_EQ(modes[2],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1920, 1080)));
+
+  // Should pick non-interlaced mode.
+  EXPECT_EQ(modes[6],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1280, 720)));
+
+  // Interlaced only. Should pick one with the highest refresh rate in
+  // interlaced mode.
+  EXPECT_EQ(modes[9],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1024, 768)));
+
+  // Mixed: Should pick one with the highest refresh rate in
+  // interlaced mode.
+  EXPECT_EQ(modes[12],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1024, 600)));
+
+  // Just one interlaced mode.
+  EXPECT_EQ(modes[13],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(640, 480)));
+
+  // Refresh rate not available.
+  EXPECT_EQ(modes[14],
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(320, 200)));
+
+  // No mode found.
+  EXPECT_EQ(NULL,
+            DisplayConfigurator::FindDisplayModeMatchingSize(
+                output, gfx::Size(1440, 900)));
+}
+
+TEST_F(DisplayConfiguratorTest, ConnectSecondOutput) {
+  InitWithSingleOutput();
+
+  // Connect a second output and check that the configurator enters
+  // extended mode.
+  observer_.Reset();
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED);
+  UpdateOutputs(2, true);
+  const int kDualHeight = small_mode_.size().height() +
+                          DisplayConfigurator::kVerticalGap +
+                          big_mode_.size().height();
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight),
+                               &outputs_[0],
+                               &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        &big_mode_,
+                        gfx::Point(0,
+                                   small_mode_.size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  observer_.Reset();
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Disconnect the second output.
+  observer_.Reset();
+  UpdateOutputs(1, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Get rid of shared modes to force software mirroring.
+  outputs_[1].set_modes(std::vector<const DisplayMode*>(1, &big_mode_));
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED);
+  UpdateOutputs(2, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight),
+                               &outputs_[0],
+                               &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        &big_mode_,
+                        gfx::Point(0,
+                                   small_mode_.size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+
+  observer_.Reset();
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+  EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Setting MULTIPLE_DISPLAY_STATE_DUAL_MIRROR should try to reconfigure.
+  observer_.Reset();
+  EXPECT_TRUE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED));
+  EXPECT_EQ(JoinActions(NULL), log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Set back to software mirror mode.
+  observer_.Reset();
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+  EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Disconnect the second output.
+  observer_.Reset();
+  UpdateOutputs(1, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+}
+
+TEST_F(DisplayConfiguratorTest, SetDisplayPower) {
+  InitWithSingleOutput();
+
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  observer_.Reset();
+  UpdateOutputs(2, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Turning off the internal display should switch the external display to
+  // its native mode.
+  observer_.Reset();
+  configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
+      DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(big_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // When all displays are turned off, the framebuffer should switch back
+  // to the mirrored size.
+  observer_.Reset();
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(kGrab,
+                  GetFramebufferAction(
+                      small_mode_.size(), &outputs_[0], &outputs_[1]).c_str(),
+                  GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+                  GetCrtcAction(outputs_[1], NULL, gfx::Point(0, 0)).c_str(),
+                  kUngrab,
+                  NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Turn all displays on and check that mirroring is still used.
+  observer_.Reset();
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Get rid of shared modes to force software mirroring.
+  outputs_[1].set_modes(std::vector<const DisplayMode*>(1, &big_mode_));
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  observer_.Reset();
+  UpdateOutputs(2, true);
+  const int kDualHeight = small_mode_.size().height() +
+                          DisplayConfigurator::kVerticalGap +
+                          big_mode_.size().height();
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight),
+                               &outputs_[0],
+                               &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        &big_mode_,
+                        gfx::Point(0,
+                                   small_mode_.size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+  EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Turning off the internal display should switch the external display to
+  // its native mode.
+  observer_.Reset();
+  configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
+      DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(big_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &big_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_SINGLE, configurator_.display_state());
+  EXPECT_FALSE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // When all displays are turned off, the framebuffer should switch back
+  // to the extended + software mirroring.
+  observer_.Reset();
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight),
+                               &outputs_[0],
+                               &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        NULL,
+                        gfx::Point(0,
+                                   small_mode_.size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+  EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+
+  // Turn all displays on and check that mirroring is still used.
+  observer_.Reset();
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(gfx::Size(big_mode_.size().width(), kDualHeight),
+                               &outputs_[0],
+                               &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        &big_mode_,
+                        gfx::Point(0,
+                                   small_mode_.size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+  EXPECT_TRUE(mirroring_controller_.SoftwareMirroringEnabled());
+  EXPECT_EQ(1, observer_.num_changes());
+}
+
+TEST_F(DisplayConfiguratorTest, SuspendAndResume) {
+  InitWithSingleOutput();
+
+  // No preparation is needed before suspending when the display is already
+  // on.  The configurator should still reprobe on resume in case a display
+  // was connected while suspended.
+  configurator_.SuspendDisplays();
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  configurator_.ResumeDisplays();
+  EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  // Now turn the display off before suspending and check that the
+  // configurator turns it back on and syncs with the server.
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  configurator_.SuspendDisplays();
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          kSync,
+          NULL),
+      log_->GetActionsAndClear());
+
+  configurator_.ResumeDisplays();
+  EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  // If a second, external display is connected, the displays shouldn't be
+  // powered back on before suspending.
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  UpdateOutputs(2, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(
+      JoinActions(kGrab,
+                  GetFramebufferAction(
+                      small_mode_.size(), &outputs_[0], &outputs_[1]).c_str(),
+                  GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+                  GetCrtcAction(outputs_[1], NULL, gfx::Point(0, 0)).c_str(),
+                  kUngrab,
+                  NULL),
+      log_->GetActionsAndClear());
+
+  configurator_.SuspendDisplays();
+  EXPECT_EQ(JoinActions(kGrab, kUngrab, kSync, NULL),
+            log_->GetActionsAndClear());
+
+  // If a display is disconnected while suspended, the configurator should
+  // pick up the change.
+  UpdateOutputs(1, false);
+  configurator_.ResumeDisplays();
+  EXPECT_TRUE(test_api_.TriggerConfigureTimeout());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], NULL, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, Headless) {
+  UpdateOutputs(0, false);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  configurator_.Init(false);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_EQ(JoinActions(kGrab, kInitXRandR, kForceDPMS, kUngrab, NULL),
+            log_->GetActionsAndClear());
+
+  // Not much should happen when the display power state is changed while
+  // no displays are connected.
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_OFF,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), log_->GetActionsAndClear());
+  configurator_.SetDisplayPower(chromeos::DISPLAY_POWER_ALL_ON,
+                                DisplayConfigurator::kSetDisplayPowerNoFlags);
+  EXPECT_EQ(JoinActions(kGrab, kForceDPMS, kUngrab, NULL),
+            log_->GetActionsAndClear());
+
+  // Connect an external display and check that it's configured correctly.
+  outputs_[0].set_current_mode(outputs_[1].current_mode());
+  outputs_[0].set_native_mode(outputs_[1].native_mode());
+  outputs_[0].set_modes(outputs_[1].modes());
+  outputs_[0].set_type(outputs_[1].type());
+
+  UpdateOutputs(1, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(big_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &big_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, StartWithTwoOutputs) {
+  UpdateOutputs(2, false);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  configurator_.Init(false);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          kInitXRandR,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, InvalidMultipleDisplayStates) {
+  UpdateOutputs(0, false);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  configurator_.Init(false);
+  configurator_.ForceInitialConfigure(0);
+  observer_.Reset();
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS));
+  EXPECT_FALSE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE));
+  EXPECT_FALSE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_FALSE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED));
+  EXPECT_EQ(1, observer_.num_changes());
+  EXPECT_EQ(3, observer_.num_failures());
+
+  UpdateOutputs(1, true);
+  observer_.Reset();
+  EXPECT_FALSE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS));
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE));
+  EXPECT_FALSE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_FALSE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED));
+  EXPECT_EQ(1, observer_.num_changes());
+  EXPECT_EQ(3, observer_.num_failures());
+
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED);
+  UpdateOutputs(2, true);
+  observer_.Reset();
+  EXPECT_FALSE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_HEADLESS));
+  EXPECT_FALSE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_SINGLE));
+  EXPECT_TRUE(configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR));
+  EXPECT_TRUE(
+      configurator_.SetDisplayMode(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED));
+  EXPECT_EQ(2, observer_.num_changes());
+  EXPECT_EQ(2, observer_.num_failures());
+}
+
+TEST_F(DisplayConfiguratorTest, GetMultipleDisplayStateForDisplaysWithoutId) {
+  outputs_[0].set_has_proper_display_id(false);
+  UpdateOutputs(2, false);
+  configurator_.Init(false);
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+            configurator_.display_state());
+}
+
+TEST_F(DisplayConfiguratorTest, GetMultipleDisplayStateForDisplaysWithId) {
+  outputs_[0].set_has_proper_display_id(true);
+  UpdateOutputs(2, false);
+  configurator_.Init(false);
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state());
+}
+
+TEST_F(DisplayConfiguratorTest, UpdateCachedOutputsEvenAfterFailure) {
+  InitWithSingleOutput();
+  const DisplayConfigurator::DisplayStateList* cached =
+      &configurator_.cached_displays();
+  ASSERT_EQ(static_cast<size_t>(1), cached->size());
+  EXPECT_EQ(outputs_[0].current_mode(), (*cached)[0].display->current_mode());
+
+  // After connecting a second output, check that it shows up in
+  // |cached_displays_| even if an invalid state is requested.
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
+  UpdateOutputs(2, true);
+  cached = &configurator_.cached_displays();
+  ASSERT_EQ(static_cast<size_t>(2), cached->size());
+  EXPECT_EQ(outputs_[0].current_mode(), (*cached)[0].display->current_mode());
+  EXPECT_EQ(outputs_[1].current_mode(), (*cached)[1].display->current_mode());
+}
+
+TEST_F(DisplayConfiguratorTest, PanelFitting) {
+  // Configure the internal display to support only the big mode and the
+  // external display to support only the small mode.
+  outputs_[0].set_current_mode(&big_mode_);
+  outputs_[0].set_native_mode(&big_mode_);
+  outputs_[0].set_modes(std::vector<const DisplayMode*>(1, &big_mode_));
+
+  outputs_[1].set_current_mode(&small_mode_);
+  outputs_[1].set_native_mode(&small_mode_);
+  outputs_[1].set_modes(std::vector<const DisplayMode*>(1, &small_mode_));
+
+  // The small mode should be added to the internal output when requesting
+  // mirrored mode.
+  UpdateOutputs(2, false);
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  configurator_.Init(true /* is_panel_fitting_enabled */);
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_EQ(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR, configurator_.display_state());
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          kInitXRandR,
+          GetAddOutputModeAction(outputs_[0], &small_mode_).c_str(),
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kForceDPMS,
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  // Both outputs should be using the small mode.
+  ASSERT_EQ(1, observer_.num_changes());
+  ASSERT_EQ(static_cast<size_t>(2), observer_.latest_outputs().size());
+  EXPECT_EQ(&small_mode_, observer_.latest_outputs()[0].mirror_mode);
+  EXPECT_EQ(&small_mode_,
+            observer_.latest_outputs()[0].display->current_mode());
+  EXPECT_EQ(&small_mode_, observer_.latest_outputs()[1].mirror_mode);
+  EXPECT_EQ(&small_mode_,
+            observer_.latest_outputs()[1].display->current_mode());
+
+  // Also check that the newly-added small mode is present in the internal
+  // snapshot that was passed to the observer (http://crbug.com/289159).
+  const DisplayConfigurator::DisplayState& state =
+      observer_.latest_outputs()[0];
+  ASSERT_NE(state.display->modes().end(),
+            std::find(state.display->modes().begin(),
+                      state.display->modes().end(),
+                      &small_mode_));
+}
+
+TEST_F(DisplayConfiguratorTest, ContentProtection) {
+  configurator_.Init(false);
+  configurator_.ForceInitialConfigure(0);
+  EXPECT_NE(kNoActions, log_->GetActionsAndClear());
+
+  DisplayConfigurator::ContentProtectionClientId id =
+      configurator_.RegisterContentProtectionClient();
+  EXPECT_NE(0u, id);
+
+  // One output.
+  UpdateOutputs(1, true);
+  EXPECT_NE(kNoActions, log_->GetActionsAndClear());
+  uint32_t link_mask = 0;
+  uint32_t protection_mask = 0;
+  EXPECT_TRUE(configurator_.QueryContentProtectionStatus(
+      id, outputs_[0].display_id(), &link_mask, &protection_mask));
+  EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_INTERNAL), link_mask);
+  EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE),
+            protection_mask);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+
+  // Two outputs.
+  UpdateOutputs(2, true);
+  EXPECT_NE(kNoActions, log_->GetActionsAndClear());
+  EXPECT_TRUE(configurator_.QueryContentProtectionStatus(
+      id, outputs_[1].display_id(), &link_mask, &protection_mask));
+  EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask);
+  EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_NONE),
+            protection_mask);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      id, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED),
+            log_->GetActionsAndClear());
+
+  // Enable protection.
+  native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED);
+  EXPECT_TRUE(configurator_.QueryContentProtectionStatus(
+      id, outputs_[1].display_id(), &link_mask, &protection_mask));
+  EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask);
+  EXPECT_EQ(static_cast<uint32_t>(CONTENT_PROTECTION_METHOD_HDCP),
+            protection_mask);
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+
+  // Protections should be disabled after unregister.
+  configurator_.UnregisterContentProtectionClient(id);
+  EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_UNDESIRED),
+            log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClients) {
+  DisplayConfigurator::ContentProtectionClientId client1 =
+      configurator_.RegisterContentProtectionClient();
+  DisplayConfigurator::ContentProtectionClientId client2 =
+      configurator_.RegisterContentProtectionClient();
+  EXPECT_NE(client1, client2);
+
+  configurator_.Init(false);
+  configurator_.ForceInitialConfigure(0);
+  UpdateOutputs(2, true);
+  EXPECT_NE(kNoActions, log_->GetActionsAndClear());
+
+  // Clients never know state enableness for methods that they didn't request.
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED).c_str(),
+            log_->GetActionsAndClear());
+  native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED);
+
+  uint32_t link_mask = 0;
+  uint32_t protection_mask = 0;
+  EXPECT_TRUE(configurator_.QueryContentProtectionStatus(
+      client1, outputs_[1].display_id(), &link_mask, &protection_mask));
+  EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask);
+  EXPECT_EQ(CONTENT_PROTECTION_METHOD_HDCP, protection_mask);
+
+  EXPECT_TRUE(configurator_.QueryContentProtectionStatus(
+      client2, outputs_[1].display_id(), &link_mask, &protection_mask));
+  EXPECT_EQ(static_cast<uint32_t>(DISPLAY_CONNECTION_TYPE_HDMI), link_mask);
+  EXPECT_EQ(CONTENT_PROTECTION_METHOD_NONE, protection_mask);
+
+  // Protections will be disabled only if no more clients request them.
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE));
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_NONE));
+  EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_UNDESIRED).c_str(),
+            log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, ContentProtectionTwoClientsEnable) {
+  DisplayConfigurator::ContentProtectionClientId client1 =
+      configurator_.RegisterContentProtectionClient();
+  DisplayConfigurator::ContentProtectionClientId client2 =
+      configurator_.RegisterContentProtectionClient();
+  EXPECT_NE(client1, client2);
+
+  configurator_.Init(false);
+  configurator_.ForceInitialConfigure(0);
+  UpdateOutputs(2, true);
+  log_->GetActionsAndClear();
+
+  // Only enable once if HDCP is enabling.
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  native_display_delegate_->set_hdcp_state(HDCP_STATE_DESIRED);
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  EXPECT_EQ(GetSetHDCPStateAction(outputs_[1], HDCP_STATE_DESIRED).c_str(),
+            log_->GetActionsAndClear());
+  native_display_delegate_->set_hdcp_state(HDCP_STATE_ENABLED);
+
+  // Don't enable again if HDCP is already active.
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client1, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  EXPECT_TRUE(configurator_.EnableContentProtection(
+      client2, outputs_[1].display_id(), CONTENT_PROTECTION_METHOD_HDCP));
+  EXPECT_EQ(kNoActions, log_->GetActionsAndClear());
+}
+
+TEST_F(DisplayConfiguratorTest, HandleConfigureCrtcFailure) {
+  InitWithSingleOutput();
+
+  ScopedVector<const DisplayMode> modes;
+  // The first mode is the mode we are requesting DisplayConfigurator to choose.
+  // The test will be setup so that this mode will fail and it will have to
+  // choose the next best option.
+  modes.push_back(new DisplayMode(gfx::Size(2560, 1600), false, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1024, 768), false, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1280, 720), false, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 60.0));
+  modes.push_back(new DisplayMode(gfx::Size(1920, 1080), false, 40.0));
+
+  for (unsigned int i = 0; i < arraysize(outputs_); i++) {
+    outputs_[i].set_modes(modes.get());
+    outputs_[i].set_current_mode(modes[0]);
+    outputs_[i].set_native_mode(modes[0]);
+  }
+
+  // First test simply fails in MULTIPLE_DISPLAY_STATE_SINGLE mode. This is
+  // probably unrealistic but we want to make sure any assumptions don't creep
+  // in.
+  native_display_delegate_->set_max_configurable_pixels(
+      modes[2]->size().GetArea());
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_SINGLE);
+  UpdateOutputs(1, true);
+
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(big_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], modes[0], gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[0], modes[3], gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[0], modes[2], gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+
+  // This test should attempt to configure a mirror mode that will not succeed
+  // and should end up in extended mode.
+  native_display_delegate_->set_max_configurable_pixels(
+      modes[3]->size().GetArea());
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_MIRROR);
+  UpdateOutputs(2, true);
+
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(modes[0]->size(), &outputs_[0], &outputs_[1])
+              .c_str(),
+          GetCrtcAction(outputs_[0], modes[0], gfx::Point(0, 0)).c_str(),
+          // First mode tried is expected to fail and it will
+          // retry wil the 4th mode in the list.
+          GetCrtcAction(outputs_[0], modes[3], gfx::Point(0, 0)).c_str(),
+          // Then attempt to configure crtc1 with the first mode.
+          GetCrtcAction(outputs_[1], modes[0], gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1], modes[3], gfx::Point(0, 0)).c_str(),
+          // Since it was requested to go into mirror mode
+          // and the configured modes were different, it
+          // should now try and setup a valid configurable
+          // extended mode.
+          GetFramebufferAction(
+              gfx::Size(modes[0]->size().width(),
+                        modes[0]->size().height() + modes[0]->size().height() +
+                            DisplayConfigurator::kVerticalGap),
+              &outputs_[0],
+              &outputs_[1]).c_str(),
+          GetCrtcAction(outputs_[0], modes[0], gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[0], modes[3], gfx::Point(0, 0)).c_str(),
+          GetCrtcAction(outputs_[1],
+                        modes[0],
+                        gfx::Point(0,
+                                   modes[0]->size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          GetCrtcAction(outputs_[1],
+                        modes[3],
+                        gfx::Point(0,
+                                   modes[0]->size().height() +
+                                       DisplayConfigurator::kVerticalGap))
+              .c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+}
+
+// Tests that power state requests are saved after failed configuration attempts
+// so they can be reused later: http://crosbug.com/p/31571
+TEST_F(DisplayConfiguratorTest, SaveDisplayPowerStateOnConfigFailure) {
+  // Start out with two displays in extended mode.
+  state_controller_.set_state(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED);
+  configurator_.Init(false);
+  configurator_.ForceInitialConfigure(0);
+  log_->GetActionsAndClear();
+
+  // Turn off the internal display, simulating docked mode.
+  EXPECT_TRUE(configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON,
+      DisplayConfigurator::kSetDisplayPowerNoFlags));
+  log_->GetActionsAndClear();
+
+  // Make all subsequent configuration requests fail and try to turn the
+  // internal display back on.
+  native_display_delegate_->set_max_configurable_pixels(1);
+  EXPECT_FALSE(configurator_.SetDisplayPower(
+      chromeos::DISPLAY_POWER_ALL_ON,
+      DisplayConfigurator::kSetDisplayPowerNoFlags));
+  log_->GetActionsAndClear();
+
+  // Simulate the external display getting disconnected and check that the
+  // internal display is turned on (i.e. DISPLAY_POWER_ALL_ON is used) rather
+  // than the earlier DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON state.
+  native_display_delegate_->set_max_configurable_pixels(0);
+  UpdateOutputs(1, true);
+  EXPECT_EQ(
+      JoinActions(
+          kGrab,
+          GetFramebufferAction(small_mode_.size(), &outputs_[0], NULL).c_str(),
+          GetCrtcAction(outputs_[0], &small_mode_, gfx::Point(0, 0)).c_str(),
+          kUngrab,
+          NULL),
+      log_->GetActionsAndClear());
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/ozone/DEPS b/ui/display/chromeos/ozone/DEPS
new file mode 100644
index 0000000..de08167
--- /dev/null
+++ b/ui/display/chromeos/ozone/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/ozone/public",
+]
diff --git a/ui/display/chromeos/ozone/display_configurator_ozone.cc b/ui/display/chromeos/ozone/display_configurator_ozone.cc
new file mode 100644
index 0000000..858d21f
--- /dev/null
+++ b/ui/display/chromeos/ozone/display_configurator_ozone.cc
@@ -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.
+
+#include "ui/display/chromeos/display_configurator.h"
+
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/ozone/public/ozone_platform.h"
+
+namespace ui {
+
+scoped_ptr<NativeDisplayDelegate>
+DisplayConfigurator::CreatePlatformNativeDisplayDelegate() {
+  return ui::OzonePlatform::GetInstance()->CreateNativeDisplayDelegate();
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/test/test_display_snapshot.cc b/ui/display/chromeos/test/test_display_snapshot.cc
new file mode 100644
index 0000000..b7eba9a
--- /dev/null
+++ b/ui/display/chromeos/test/test_display_snapshot.cc
@@ -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.
+
+#include "ui/display/chromeos/test/test_display_snapshot.h"
+
+namespace ui {
+TestDisplaySnapshot::TestDisplaySnapshot()
+    : DisplaySnapshot(0,
+                      false,
+                      gfx::Point(0, 0),
+                      gfx::Size(0, 0),
+                      DISPLAY_CONNECTION_TYPE_UNKNOWN,
+                      false,
+                      false,
+                      std::string(),
+                      std::vector<const DisplayMode*>(),
+                      NULL,
+                      NULL) {}
+
+TestDisplaySnapshot::TestDisplaySnapshot(
+    int64_t display_id,
+    bool has_proper_display_id,
+    const gfx::Point& origin,
+    const gfx::Size& physical_size,
+    DisplayConnectionType type,
+    bool is_aspect_preserving_scaling,
+    const std::vector<const DisplayMode*>& modes,
+    const DisplayMode* current_mode,
+    const DisplayMode* native_mode)
+    : DisplaySnapshot(display_id,
+                      has_proper_display_id,
+                      origin,
+                      physical_size,
+                      type,
+                      is_aspect_preserving_scaling,
+                      false,
+                      std::string(),
+                      modes,
+                      current_mode,
+                      native_mode) {}
+
+TestDisplaySnapshot::~TestDisplaySnapshot() {}
+
+std::string TestDisplaySnapshot::ToString() const { return ""; }
+
+}  // namespace ui
diff --git a/ui/display/chromeos/test/test_display_snapshot.h b/ui/display/chromeos/test/test_display_snapshot.h
new file mode 100644
index 0000000..3d52f79
--- /dev/null
+++ b/ui/display/chromeos/test/test_display_snapshot.h
@@ -0,0 +1,50 @@
+// 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_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_SNAPSHOT_H_
+#define UI_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_SNAPSHOT_H_
+
+#include "ui/display/display_export.h"
+#include "ui/display/types/display_snapshot.h"
+
+namespace ui {
+
+class DISPLAY_EXPORT TestDisplaySnapshot : public DisplaySnapshot {
+ public:
+  TestDisplaySnapshot();
+  TestDisplaySnapshot(int64_t display_id,
+                      bool has_proper_display_id,
+                      const gfx::Point& origin,
+                      const gfx::Size& physical_size,
+                      DisplayConnectionType type,
+                      bool is_aspect_preserving_scaling,
+                      const std::vector<const DisplayMode*>& modes,
+                      const DisplayMode* current_mode,
+                      const DisplayMode* native_mode);
+  virtual ~TestDisplaySnapshot();
+
+  void set_type(DisplayConnectionType type) { type_ = type; }
+  void set_modes(const std::vector<const DisplayMode*>& modes) {
+    modes_ = modes;
+  }
+  void set_current_mode(const ui::DisplayMode* mode) { current_mode_ = mode; }
+  void set_native_mode(const ui::DisplayMode* mode) { native_mode_ = mode; }
+  void set_is_aspect_preserving_scaling(bool state) {
+    is_aspect_preserving_scaling_ = state;
+  }
+  void set_display_id(int64_t id) { display_id_ = id; }
+  void set_has_proper_display_id(bool has_display_id) {
+    has_proper_display_id_ = has_display_id;
+  }
+
+  // DisplaySnapshot overrides:
+  virtual std::string ToString() const OVERRIDE;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestDisplaySnapshot);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_TEST_TEST_DISPLAY_SNAPSHOT_H_
diff --git a/ui/display/chromeos/x11/DEPS b/ui/display/chromeos/x11/DEPS
new file mode 100644
index 0000000..4c156b2
--- /dev/null
+++ b/ui/display/chromeos/x11/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+ui/events/platform",
+  "+ui/gfx/x",
+]
diff --git a/ui/display/chromeos/x11/display_configurator_x11.cc b/ui/display/chromeos/x11/display_configurator_x11.cc
new file mode 100644
index 0000000..93c056c
--- /dev/null
+++ b/ui/display/chromeos/x11/display_configurator_x11.cc
@@ -0,0 +1,16 @@
+// 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/display/chromeos/display_configurator.h"
+
+#include "ui/display/chromeos/x11/native_display_delegate_x11.h"
+
+namespace ui {
+
+scoped_ptr<NativeDisplayDelegate>
+DisplayConfigurator::CreatePlatformNativeDisplayDelegate() {
+  return scoped_ptr<NativeDisplayDelegate>(new NativeDisplayDelegateX11());
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/display_mode_x11.cc b/ui/display/chromeos/x11/display_mode_x11.cc
new file mode 100644
index 0000000..7f04123
--- /dev/null
+++ b/ui/display/chromeos/x11/display_mode_x11.cc
@@ -0,0 +1,18 @@
+// 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/display/chromeos/x11/display_mode_x11.h"
+
+namespace ui {
+
+DisplayModeX11::DisplayModeX11(const gfx::Size& size,
+                               bool interlaced,
+                               float refresh_rate,
+                               RRMode mode_id)
+    : DisplayMode(size, interlaced, refresh_rate),
+      mode_id_(mode_id) {}
+
+DisplayModeX11::~DisplayModeX11() {}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/display_mode_x11.h b/ui/display/chromeos/x11/display_mode_x11.h
new file mode 100644
index 0000000..012e6da
--- /dev/null
+++ b/ui/display/chromeos/x11/display_mode_x11.h
@@ -0,0 +1,35 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UI_DISPLAY_CHROMEOS_X11_DISPLAY_MODE_X11_H_
+#define UI_DISPLAY_CHROMEOS_X11_DISPLAY_MODE_X11_H_
+
+#include "ui/display/display_export.h"
+#include "ui/display/types/display_mode.h"
+
+// Forward declare from Xlib and Xrandr.
+typedef unsigned long XID;
+typedef XID RRMode;
+
+namespace ui {
+
+class DISPLAY_EXPORT DisplayModeX11 : public DisplayMode {
+ public:
+  DisplayModeX11(const gfx::Size& size,
+                 bool interlaced,
+                 float refresh_rate,
+                 RRMode mode_id);
+  virtual ~DisplayModeX11();
+
+  RRMode mode_id() const { return mode_id_; }
+
+ private:
+  RRMode mode_id_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayModeX11);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_X11_DISPLAY_MODE_X11_H_
diff --git a/ui/display/chromeos/x11/display_snapshot_x11.cc b/ui/display/chromeos/x11/display_snapshot_x11.cc
new file mode 100644
index 0000000..52c4ba7
--- /dev/null
+++ b/ui/display/chromeos/x11/display_snapshot_x11.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/display/chromeos/x11/display_snapshot_x11.h"
+
+#include "base/strings/stringprintf.h"
+#include "ui/display/chromeos/x11/display_mode_x11.h"
+
+namespace ui {
+
+DisplaySnapshotX11::DisplaySnapshotX11(
+    int64_t display_id,
+    bool has_proper_display_id,
+    const gfx::Point& origin,
+    const gfx::Size& physical_size,
+    DisplayConnectionType type,
+    bool is_aspect_preserving_scaling,
+    bool has_overscan,
+    std::string display_name,
+    const std::vector<const DisplayMode*>& modes,
+    const DisplayMode* current_mode,
+    const DisplayMode* native_mode,
+    RROutput output,
+    RRCrtc crtc,
+    int index)
+    : DisplaySnapshot(display_id,
+                      has_proper_display_id,
+                      origin,
+                      physical_size,
+                      type,
+                      is_aspect_preserving_scaling,
+                      has_overscan,
+                      display_name,
+                      modes,
+                      current_mode,
+                      native_mode),
+      output_(output),
+      crtc_(crtc),
+      index_(index) {}
+
+DisplaySnapshotX11::~DisplaySnapshotX11() {}
+
+std::string DisplaySnapshotX11::ToString() const {
+  return base::StringPrintf(
+      "[type=%d, output=%ld, crtc=%ld, mode=%ld, dim=%dx%d]",
+      type_,
+      output_,
+      crtc_,
+      current_mode_
+          ? static_cast<const DisplayModeX11*>(current_mode_)->mode_id()
+          : 0,
+      physical_size_.width(),
+      physical_size_.height());
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/display_snapshot_x11.h b/ui/display/chromeos/x11/display_snapshot_x11.h
new file mode 100644
index 0000000..01a44c6
--- /dev/null
+++ b/ui/display/chromeos/x11/display_snapshot_x11.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_DISPLAY_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_
+#define UI_DISPLAY_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_
+
+#include "ui/display/display_export.h"
+#include "ui/display/types/display_snapshot.h"
+
+// Forward declare from Xlib and Xrandr.
+typedef unsigned long XID;
+typedef XID RROutput;
+typedef XID RRCrtc;
+
+namespace ui {
+
+class DISPLAY_EXPORT DisplaySnapshotX11 : public DisplaySnapshot {
+ public:
+  DisplaySnapshotX11(int64_t display_id,
+                     bool has_proper_display_id,
+                     const gfx::Point& origin,
+                     const gfx::Size& physical_size,
+                     DisplayConnectionType type,
+                     bool is_aspect_preserving_scaling,
+                     bool has_overscan,
+                     std::string display_name,
+                     const std::vector<const DisplayMode*>& modes,
+                     const DisplayMode* current_mode,
+                     const DisplayMode* native_mode,
+                     RROutput output,
+                     RRCrtc crtc,
+                     int index);
+  virtual ~DisplaySnapshotX11();
+
+  RROutput output() const { return output_; }
+  RRCrtc crtc() const { return crtc_; }
+  int index() const { return index_; }
+
+  // DisplaySnapshot overrides:
+  virtual std::string ToString() const OVERRIDE;
+
+ private:
+  RROutput output_;
+
+  // CRTC that should be used for this output. Not necessarily the CRTC
+  // that XRandR reports is currently being used.
+  RRCrtc crtc_;
+
+  // This output's index in the array returned by XRandR. Stable even as
+  // outputs are connected or disconnected.
+  int index_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySnapshotX11);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_X11_DISPLAY_SNAPSHOT_X11_H_
diff --git a/ui/display/chromeos/x11/display_util_x11.cc b/ui/display/chromeos/x11/display_util_x11.cc
new file mode 100644
index 0000000..560edd0
--- /dev/null
+++ b/ui/display/chromeos/x11/display_util_x11.cc
@@ -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.
+
+#include "ui/display/chromeos/x11/display_util_x11.h"
+
+#include "base/macros.h"
+
+namespace ui {
+
+namespace {
+
+struct DisplayConnectionTypeMapping {
+  // Prefix of output name.
+  const char* name;
+  DisplayConnectionType type;
+};
+
+const DisplayConnectionTypeMapping kDisplayConnectionTypeMapping[] = {
+    {"LVDS", DISPLAY_CONNECTION_TYPE_INTERNAL},
+    {"eDP", DISPLAY_CONNECTION_TYPE_INTERNAL},
+    {"DSI", DISPLAY_CONNECTION_TYPE_INTERNAL},
+    {"VGA", DISPLAY_CONNECTION_TYPE_VGA},
+    {"HDMI", DISPLAY_CONNECTION_TYPE_HDMI},
+    {"DVI", DISPLAY_CONNECTION_TYPE_DVI},
+    {"DP", DISPLAY_CONNECTION_TYPE_DISPLAYPORT}};
+
+}  // namespace
+
+DisplayConnectionType GetDisplayConnectionTypeFromName(
+    const std::string& name) {
+  for (unsigned int i = 0; i < arraysize(kDisplayConnectionTypeMapping); ++i) {
+    if (name.find(kDisplayConnectionTypeMapping[i].name) == 0) {
+      return kDisplayConnectionTypeMapping[i].type;
+    }
+  }
+
+  return DISPLAY_CONNECTION_TYPE_UNKNOWN;
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/display_util_x11.h b/ui/display/chromeos/x11/display_util_x11.h
new file mode 100644
index 0000000..1bc2369
--- /dev/null
+++ b/ui/display/chromeos/x11/display_util_x11.h
@@ -0,0 +1,25 @@
+// 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_DISPLAY_CHROMEOS_DISPLAY_UTIL_X11_H_
+#define UI_DISPLAY_CHROMEOS_DISPLAY_UTIL_X11_H_
+
+#include <string>
+
+#include "ui/display/display_export.h"
+#include "ui/display/types/display_constants.h"
+
+typedef unsigned long XID;
+typedef XID RROutput;
+
+namespace ui {
+
+// Returns the DisplayConnectionType by matching known type prefixes to |name|.
+// Returns DISPLAY_TYPE_UNKNOWN if no valid match.
+DISPLAY_EXPORT DisplayConnectionType
+    GetDisplayConnectionTypeFromName(const std::string& name);
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_DISPLAY_UTIL_X11_H_
diff --git a/ui/display/chromeos/x11/display_util_x11_unittest.cc b/ui/display/chromeos/x11/display_util_x11_unittest.cc
new file mode 100644
index 0000000..5c13024
--- /dev/null
+++ b/ui/display/chromeos/x11/display_util_x11_unittest.cc
@@ -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.
+
+#include "ui/display/chromeos/x11/display_util_x11.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+TEST(DisplayUtilX11Test, GetDisplayConnectionTypeFromName) {
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("LVDS"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("eDP"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("DSI"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("LVDSxx"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("eDPzz"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_INTERNAL,
+            GetDisplayConnectionTypeFromName("DSIyy"));
+
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_VGA,
+            GetDisplayConnectionTypeFromName("VGA"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_VGA,
+            GetDisplayConnectionTypeFromName("VGAxx"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI,
+            GetDisplayConnectionTypeFromName("HDMI"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_HDMI,
+            GetDisplayConnectionTypeFromName("HDMIyy"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DVI,
+            GetDisplayConnectionTypeFromName("DVI"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DVI,
+            GetDisplayConnectionTypeFromName("DVIzz"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DISPLAYPORT,
+            GetDisplayConnectionTypeFromName("DP"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_DISPLAYPORT,
+            GetDisplayConnectionTypeFromName("DPww"));
+
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("xyz"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("abcLVDS"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("cdeeDP"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("abcDSI"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("LVD"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("eD"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("DS"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("VG"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("HDM"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("DV"));
+  EXPECT_EQ(DISPLAY_CONNECTION_TYPE_UNKNOWN,
+            GetDisplayConnectionTypeFromName("D"));
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/native_display_delegate_x11.cc b/ui/display/chromeos/x11/native_display_delegate_x11.cc
new file mode 100644
index 0000000..6959f46
--- /dev/null
+++ b/ui/display/chromeos/x11/native_display_delegate_x11.cc
@@ -0,0 +1,651 @@
+// 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/display/chromeos/x11/native_display_delegate_x11.h"
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/extensions/XInput2.h>
+
+#include <utility>
+
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "ui/display/chromeos/x11/display_mode_x11.h"
+#include "ui/display/chromeos/x11/display_snapshot_x11.h"
+#include "ui/display/chromeos/x11/display_util_x11.h"
+#include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
+#include "ui/display/types/native_display_observer.h"
+#include "ui/display/util/x11/edid_parser_x11.h"
+#include "ui/events/platform/platform_event_source.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/x/x11_error_tracker.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace ui {
+
+namespace {
+
+// DPI measurements.
+const float kMmInInch = 25.4;
+const float kDpi96 = 96.0;
+const float kPixelsToMmScale = kMmInInch / kDpi96;
+
+const char kContentProtectionAtomName[] = "Content Protection";
+const char kProtectionUndesiredAtomName[] = "Undesired";
+const char kProtectionDesiredAtomName[] = "Desired";
+const char kProtectionEnabledAtomName[] = "Enabled";
+
+RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
+  return output_info->nmode > 0 ? output_info->modes[0] : None;
+}
+
+XRRCrtcGamma* ResampleGammaRamp(XRRCrtcGamma* gamma_ramp, int gamma_ramp_size) {
+  if (gamma_ramp->size == gamma_ramp_size)
+    return gamma_ramp;
+
+#define RESAMPLE(array, i, r) \
+  array[i] + (array[i + 1] - array[i]) * r / gamma_ramp_size
+
+  XRRCrtcGamma* resampled = XRRAllocGamma(gamma_ramp_size);
+  for (int i = 0; i < gamma_ramp_size; ++i) {
+    int base_index = gamma_ramp->size * i / gamma_ramp_size;
+    int remaining = gamma_ramp->size * i % gamma_ramp_size;
+    if (base_index < gamma_ramp->size - 1) {
+      resampled->red[i] = RESAMPLE(gamma_ramp->red, base_index, remaining);
+      resampled->green[i] = RESAMPLE(gamma_ramp->green, base_index, remaining);
+      resampled->blue[i] = RESAMPLE(gamma_ramp->blue, base_index, remaining);
+    } else {
+      resampled->red[i] = gamma_ramp->red[gamma_ramp->size - 1];
+      resampled->green[i] = gamma_ramp->green[gamma_ramp->size - 1];
+      resampled->blue[i] = gamma_ramp->blue[gamma_ramp->size - 1];
+    }
+  }
+
+#undef RESAMPLE
+  XRRFreeGamma(gamma_ramp);
+  return resampled;
+}
+
+}  // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeDisplayDelegateX11::HelperDelegateX11
+
+class NativeDisplayDelegateX11::HelperDelegateX11
+    : public NativeDisplayDelegateX11::HelperDelegate {
+ public:
+  HelperDelegateX11(NativeDisplayDelegateX11* delegate) : delegate_(delegate) {}
+  virtual ~HelperDelegateX11() {}
+
+  // NativeDisplayDelegateX11::HelperDelegate overrides:
+  virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
+      OVERRIDE {
+    XRRUpdateConfiguration(event);
+  }
+  virtual const std::vector<DisplaySnapshot*>& GetCachedDisplays() const
+      OVERRIDE {
+    return delegate_->cached_outputs_.get();
+  }
+  virtual void NotifyDisplayObservers() OVERRIDE {
+    FOR_EACH_OBSERVER(
+        NativeDisplayObserver, delegate_->observers_, OnConfigurationChanged());
+  }
+
+ private:
+  NativeDisplayDelegateX11* delegate_;
+
+  DISALLOW_COPY_AND_ASSIGN(HelperDelegateX11);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeDisplayDelegateX11 implementation:
+
+NativeDisplayDelegateX11::NativeDisplayDelegateX11()
+    : display_(gfx::GetXDisplay()),
+      window_(DefaultRootWindow(display_)),
+      screen_(NULL),
+      background_color_argb_(0) {}
+
+NativeDisplayDelegateX11::~NativeDisplayDelegateX11() {
+  if (ui::PlatformEventSource::GetInstance()) {
+    ui::PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(
+        platform_event_dispatcher_.get());
+  }
+
+  STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
+}
+
+void NativeDisplayDelegateX11::Initialize() {
+  int error_base_ignored = 0;
+  int xrandr_event_base = 0;
+  XRRQueryExtension(display_, &xrandr_event_base, &error_base_ignored);
+
+  helper_delegate_.reset(new HelperDelegateX11(this));
+  platform_event_dispatcher_.reset(new NativeDisplayEventDispatcherX11(
+      helper_delegate_.get(), xrandr_event_base));
+
+  if (ui::PlatformEventSource::GetInstance()) {
+    ui::PlatformEventSource::GetInstance()->AddPlatformEventDispatcher(
+        platform_event_dispatcher_.get());
+  }
+}
+
+void NativeDisplayDelegateX11::GrabServer() {
+  CHECK(!screen_) << "Server already grabbed";
+  XGrabServer(display_);
+  screen_ = XRRGetScreenResources(display_, window_);
+  CHECK(screen_);
+}
+
+void NativeDisplayDelegateX11::UngrabServer() {
+  CHECK(screen_) << "Server not grabbed";
+  XRRFreeScreenResources(screen_);
+  screen_ = NULL;
+  XUngrabServer(display_);
+  // crbug.com/366125
+  XFlush(display_);
+}
+
+void NativeDisplayDelegateX11::SyncWithServer() { XSync(display_, 0); }
+
+void NativeDisplayDelegateX11::SetBackgroundColor(uint32_t color_argb) {
+  background_color_argb_ = color_argb;
+}
+
+void NativeDisplayDelegateX11::ForceDPMSOn() {
+  CHECK(DPMSEnable(display_));
+  CHECK(DPMSForceLevel(display_, DPMSModeOn));
+}
+
+std::vector<DisplaySnapshot*> NativeDisplayDelegateX11::GetDisplays() {
+  CHECK(screen_) << "Server not grabbed";
+
+  cached_outputs_.clear();
+  RRCrtc last_used_crtc = None;
+
+  InitModes();
+  for (int i = 0; i < screen_->noutput && cached_outputs_.size() < 2; ++i) {
+    RROutput output_id = screen_->outputs[i];
+    XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, output_id);
+    if (output_info->connection == RR_Connected) {
+      DisplaySnapshotX11* output =
+          InitDisplaySnapshot(output_id, output_info, &last_used_crtc, i);
+      cached_outputs_.push_back(output);
+    }
+    XRRFreeOutputInfo(output_info);
+  }
+
+  return cached_outputs_.get();
+}
+
+void NativeDisplayDelegateX11::AddMode(const DisplaySnapshot& output,
+                                       const DisplayMode* mode) {
+  CHECK(screen_) << "Server not grabbed";
+  CHECK(mode) << "Must add valid mode";
+
+  const DisplaySnapshotX11& x11_output =
+      static_cast<const DisplaySnapshotX11&>(output);
+  RRMode mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
+
+  VLOG(1) << "AddDisplayMode: output=" << x11_output.output()
+          << " mode=" << mode_id;
+  XRRAddOutputMode(display_, x11_output.output(), mode_id);
+}
+
+bool NativeDisplayDelegateX11::Configure(const DisplaySnapshot& output,
+                                         const DisplayMode* mode,
+                                         const gfx::Point& origin) {
+  const DisplaySnapshotX11& x11_output =
+      static_cast<const DisplaySnapshotX11&>(output);
+  RRMode mode_id = None;
+  if (mode)
+    mode_id = static_cast<const DisplayModeX11*>(mode)->mode_id();
+
+  return ConfigureCrtc(
+      x11_output.crtc(), mode_id, x11_output.output(), origin.x(), origin.y());
+}
+
+bool NativeDisplayDelegateX11::ConfigureCrtc(RRCrtc crtc,
+                                             RRMode mode,
+                                             RROutput output,
+                                             int x,
+                                             int y) {
+  CHECK(screen_) << "Server not grabbed";
+  VLOG(1) << "ConfigureCrtc: crtc=" << crtc << " mode=" << mode
+          << " output=" << output << " x=" << x << " y=" << y;
+  // Xrandr.h is full of lies. XRRSetCrtcConfig() is defined as returning a
+  // Status, which is typically 0 for failure and 1 for success. In
+  // actuality it returns a RRCONFIGSTATUS, which uses 0 for success.
+  if (XRRSetCrtcConfig(display_,
+                       screen_,
+                       crtc,
+                       CurrentTime,
+                       x,
+                       y,
+                       mode,
+                       RR_Rotate_0,
+                       (output && mode) ? &output : NULL,
+                       (output && mode) ? 1 : 0) != RRSetConfigSuccess) {
+    LOG(WARNING) << "Unable to configure CRTC " << crtc << ":"
+                 << " mode=" << mode << " output=" << output << " x=" << x
+                 << " y=" << y;
+    return false;
+  }
+
+  return true;
+}
+
+void NativeDisplayDelegateX11::CreateFrameBuffer(const gfx::Size& size) {
+  CHECK(screen_) << "Server not grabbed";
+  gfx::Size current_screen_size(
+      DisplayWidth(display_, DefaultScreen(display_)),
+      DisplayHeight(display_, DefaultScreen(display_)));
+
+  VLOG(1) << "CreateFrameBuffer: new=" << size.ToString()
+          << " current=" << current_screen_size.ToString();
+
+  DestroyUnusedCrtcs();
+
+  if (size == current_screen_size)
+    return;
+
+  gfx::Size min_screen_size(current_screen_size);
+  min_screen_size.SetToMin(size);
+  UpdateCrtcsForNewFramebuffer(min_screen_size);
+
+  int mm_width = size.width() * kPixelsToMmScale;
+  int mm_height = size.height() * kPixelsToMmScale;
+  XRRSetScreenSize(
+      display_, window_, size.width(), size.height(), mm_width, mm_height);
+  // We don't wait for root window resize, therefore this end up with drawing
+  // in the old window size, which we care during the boot.
+  DrawBackground();
+
+  // Don't redraw the background upon framebuffer change again. This should
+  // happen only once after boot.
+  background_color_argb_ = 0;
+}
+
+void NativeDisplayDelegateX11::AddObserver(NativeDisplayObserver* observer) {
+  observers_.AddObserver(observer);
+}
+
+void NativeDisplayDelegateX11::RemoveObserver(NativeDisplayObserver* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void NativeDisplayDelegateX11::InitModes() {
+  CHECK(screen_) << "Server not grabbed";
+
+  STLDeleteContainerPairSecondPointers(modes_.begin(), modes_.end());
+  modes_.clear();
+
+  for (int i = 0; i < screen_->nmode; ++i) {
+    const XRRModeInfo& info = screen_->modes[i];
+    float refresh_rate = 0.0f;
+    if (info.hTotal && info.vTotal) {
+      refresh_rate =
+          static_cast<float>(info.dotClock) /
+          (static_cast<float>(info.hTotal) * static_cast<float>(info.vTotal));
+    }
+
+    modes_.insert(
+        std::make_pair(info.id,
+                       new DisplayModeX11(gfx::Size(info.width, info.height),
+                                          info.modeFlags & RR_Interlace,
+                                          refresh_rate,
+                                          info.id)));
+  }
+}
+
+DisplaySnapshotX11* NativeDisplayDelegateX11::InitDisplaySnapshot(
+    RROutput output,
+    XRROutputInfo* info,
+    RRCrtc* last_used_crtc,
+    int index) {
+  int64_t display_id = 0;
+  bool has_display_id = GetDisplayId(
+      output, static_cast<uint8_t>(index), &display_id);
+
+  bool has_overscan = false;
+  GetOutputOverscanFlag(output, &has_overscan);
+
+  DisplayConnectionType type = GetDisplayConnectionTypeFromName(info->name);
+  if (type == DISPLAY_CONNECTION_TYPE_UNKNOWN)
+    LOG(ERROR) << "Unknown link type: " << info->name;
+
+  // Use the index as a valid display ID even if the internal
+  // display doesn't have valid EDID because the index
+  // will never change.
+  if (!has_display_id) {
+    if (type == DISPLAY_CONNECTION_TYPE_INTERNAL)
+      has_display_id = true;
+
+    // Fallback to output index.
+    display_id = index;
+  }
+
+  RRMode native_mode_id = GetOutputNativeMode(info);
+  RRMode current_mode_id = None;
+  gfx::Point origin;
+  if (info->crtc) {
+    XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display_, screen_, info->crtc);
+    current_mode_id = crtc_info->mode;
+    origin.SetPoint(crtc_info->x, crtc_info->y);
+    XRRFreeCrtcInfo(crtc_info);
+  }
+
+  RRCrtc crtc = None;
+  // Assign a CRTC that isn't already in use.
+  for (int i = 0; i < info->ncrtc; ++i) {
+    if (info->crtcs[i] != *last_used_crtc) {
+      crtc = info->crtcs[i];
+      *last_used_crtc = crtc;
+      break;
+    }
+  }
+
+  const DisplayMode* current_mode = NULL;
+  const DisplayMode* native_mode = NULL;
+  std::vector<const DisplayMode*> display_modes;
+
+  for (int i = 0; i < info->nmode; ++i) {
+    const RRMode mode = info->modes[i];
+    if (modes_.find(mode) != modes_.end()) {
+      display_modes.push_back(modes_.at(mode));
+
+      if (mode == current_mode_id)
+        current_mode = display_modes.back();
+      if (mode == native_mode_id)
+        native_mode = display_modes.back();
+    } else {
+      LOG(WARNING) << "Unable to find XRRModeInfo for mode " << mode;
+    }
+  }
+
+  DisplaySnapshotX11* display_snapshot =
+      new DisplaySnapshotX11(display_id,
+                             has_display_id,
+                             origin,
+                             gfx::Size(info->mm_width, info->mm_height),
+                             type,
+                             IsOutputAspectPreservingScaling(output),
+                             has_overscan,
+                             GetDisplayName(output),
+                             display_modes,
+                             current_mode,
+                             native_mode,
+                             output,
+                             crtc,
+                             index);
+
+  VLOG(1) << "Found display " << cached_outputs_.size() << ":"
+          << " output=" << output << " crtc=" << crtc
+          << " current_mode=" << current_mode_id;
+
+  return display_snapshot;
+}
+
+bool NativeDisplayDelegateX11::GetHDCPState(const DisplaySnapshot& output,
+                                            HDCPState* state) {
+  unsigned char* values = NULL;
+  int actual_format = 0;
+  unsigned long nitems = 0;
+  unsigned long bytes_after = 0;
+  Atom actual_type = None;
+  int success = 0;
+  RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
+  // TODO(kcwu): Use X11AtomCache to save round trip time of XInternAtom.
+  Atom prop = XInternAtom(display_, kContentProtectionAtomName, False);
+
+  bool ok = true;
+  // TODO(kcwu): Move this to x11_util (similar method calls in this file and
+  // output_util.cc)
+  success = XRRGetOutputProperty(display_,
+                                 output_id,
+                                 prop,
+                                 0,
+                                 100,
+                                 False,
+                                 False,
+                                 AnyPropertyType,
+                                 &actual_type,
+                                 &actual_format,
+                                 &nitems,
+                                 &bytes_after,
+                                 &values);
+  if (actual_type == None) {
+    LOG(ERROR) << "Property '" << kContentProtectionAtomName
+               << "' does not exist";
+    ok = false;
+  } else if (success == Success && actual_type == XA_ATOM &&
+             actual_format == 32 && nitems == 1) {
+    Atom value = reinterpret_cast<Atom*>(values)[0];
+    if (value == XInternAtom(display_, kProtectionUndesiredAtomName, False)) {
+      *state = HDCP_STATE_UNDESIRED;
+    } else if (value ==
+               XInternAtom(display_, kProtectionDesiredAtomName, False)) {
+      *state = HDCP_STATE_DESIRED;
+    } else if (value ==
+               XInternAtom(display_, kProtectionEnabledAtomName, False)) {
+      *state = HDCP_STATE_ENABLED;
+    } else {
+      LOG(ERROR) << "Unknown " << kContentProtectionAtomName
+                 << " value: " << value;
+      ok = false;
+    }
+  } else {
+    LOG(ERROR) << "XRRGetOutputProperty failed";
+    ok = false;
+  }
+  if (values)
+    XFree(values);
+
+  VLOG(3) << "HDCP state: " << ok << "," << *state;
+  return ok;
+}
+
+bool NativeDisplayDelegateX11::SetHDCPState(const DisplaySnapshot& output,
+                                            HDCPState state) {
+  Atom name = XInternAtom(display_, kContentProtectionAtomName, False);
+  Atom value = None;
+  switch (state) {
+    case HDCP_STATE_UNDESIRED:
+      value = XInternAtom(display_, kProtectionUndesiredAtomName, False);
+      break;
+    case HDCP_STATE_DESIRED:
+      value = XInternAtom(display_, kProtectionDesiredAtomName, False);
+      break;
+    default:
+      NOTREACHED() << "Invalid HDCP state: " << state;
+      return false;
+  }
+  gfx::X11ErrorTracker err_tracker;
+  unsigned char* data = reinterpret_cast<unsigned char*>(&value);
+  RROutput output_id = static_cast<const DisplaySnapshotX11&>(output).output();
+  XRRChangeOutputProperty(
+      display_, output_id, name, XA_ATOM, 32, PropModeReplace, data, 1);
+  if (err_tracker.FoundNewError()) {
+    LOG(ERROR) << "XRRChangeOutputProperty failed";
+    return false;
+  } else {
+    return true;
+  }
+}
+
+void NativeDisplayDelegateX11::DestroyUnusedCrtcs() {
+  CHECK(screen_) << "Server not grabbed";
+
+  for (int i = 0; i < screen_->ncrtc; ++i) {
+    bool in_use = false;
+    for (ScopedVector<DisplaySnapshot>::const_iterator it =
+             cached_outputs_.begin();
+         it != cached_outputs_.end();
+         ++it) {
+      DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
+      if (screen_->crtcs[i] == x11_output->crtc()) {
+        in_use = true;
+        break;
+      }
+    }
+
+    if (!in_use)
+      ConfigureCrtc(screen_->crtcs[i], None, None, 0, 0);
+  }
+}
+
+void NativeDisplayDelegateX11::UpdateCrtcsForNewFramebuffer(
+    const gfx::Size& min_screen_size) {
+  CHECK(screen_) << "Server not grabbed";
+  // Setting the screen size will fail if any CRTC doesn't fit afterwards.
+  // At the same time, turning CRTCs off and back on uses up a lot of time.
+  // This function tries to be smart to avoid too many off/on cycles:
+  // - We set the new modes on CRTCs, if they fit in both the old and new
+  //   FBs, and park them at (0,0)
+  // - We disable the CRTCs we will need but don't fit in the old FB. Those
+  //   will be reenabled after the resize.
+  // We don't worry about the cached state of the outputs here since we are
+  // not interested in the state we are setting - we just try to get the CRTCs
+  // out of the way so we can rebuild the frame buffer.
+  gfx::Rect fb_rect(min_screen_size);
+  for (ScopedVector<DisplaySnapshot>::const_iterator it =
+           cached_outputs_.begin();
+       it != cached_outputs_.end();
+       ++it) {
+    DisplaySnapshotX11* x11_output = static_cast<DisplaySnapshotX11*>(*it);
+    const DisplayMode* mode_info = x11_output->current_mode();
+    RROutput output = x11_output->output();
+    RRMode mode = None;
+
+    if (mode_info) {
+      mode = static_cast<const DisplayModeX11*>(mode_info)->mode_id();
+
+      if (!fb_rect.Contains(gfx::Rect(mode_info->size()))) {
+        // In case our CRTC doesn't fit in common area of our current and about
+        // to be resized framebuffer, disable it.
+        // It'll get reenabled after we resize the framebuffer.
+        mode = None;
+        output = None;
+        mode_info = NULL;
+      }
+    }
+
+    ConfigureCrtc(x11_output->crtc(), mode, output, 0, 0);
+  }
+}
+
+bool NativeDisplayDelegateX11::IsOutputAspectPreservingScaling(RROutput id) {
+  bool ret = false;
+
+  Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
+  Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
+  if (scaling_prop == None || full_aspect_atom == None)
+    return false;
+
+  int nprop = 0;
+  Atom* props = XRRListOutputProperties(display_, id, &nprop);
+  for (int j = 0; j < nprop && !ret; j++) {
+    Atom prop = props[j];
+    if (scaling_prop == prop) {
+      unsigned char* values = NULL;
+      int actual_format;
+      unsigned long nitems;
+      unsigned long bytes_after;
+      Atom actual_type;
+      int success;
+
+      success = XRRGetOutputProperty(display_,
+                                     id,
+                                     prop,
+                                     0,
+                                     100,
+                                     False,
+                                     False,
+                                     AnyPropertyType,
+                                     &actual_type,
+                                     &actual_format,
+                                     &nitems,
+                                     &bytes_after,
+                                     &values);
+      if (success == Success && actual_type == XA_ATOM && actual_format == 32 &&
+          nitems == 1) {
+        Atom value = reinterpret_cast<Atom*>(values)[0];
+        if (full_aspect_atom == value)
+          ret = true;
+      }
+      if (values)
+        XFree(values);
+    }
+  }
+  if (props)
+    XFree(props);
+
+  return ret;
+}
+
+
+std::vector<ColorCalibrationProfile>
+NativeDisplayDelegateX11::GetAvailableColorCalibrationProfiles(
+    const DisplaySnapshot& output) {
+  // TODO(mukai|marcheu): Checks the system data and fills the result.
+  // Note that the order would be Dynamic -> Standard -> Movie -> Reading.
+  return std::vector<ColorCalibrationProfile>();
+}
+
+bool NativeDisplayDelegateX11::SetColorCalibrationProfile(
+    const DisplaySnapshot& output,
+    ColorCalibrationProfile new_profile) {
+  const DisplaySnapshotX11& x11_output =
+      static_cast<const DisplaySnapshotX11&>(output);
+
+  XRRCrtcGamma* gamma_ramp = CreateGammaRampForProfile(x11_output, new_profile);
+
+  if (!gamma_ramp)
+    return false;
+
+  int gamma_ramp_size = XRRGetCrtcGammaSize(display_, x11_output.crtc());
+  XRRSetCrtcGamma(display_,
+                  x11_output.crtc(),
+                  ResampleGammaRamp(gamma_ramp, gamma_ramp_size));
+  XRRFreeGamma(gamma_ramp);
+  return true;
+}
+
+XRRCrtcGamma* NativeDisplayDelegateX11::CreateGammaRampForProfile(
+    const DisplaySnapshotX11& x11_output,
+    ColorCalibrationProfile new_profile) {
+  // TODO(mukai|marcheu): Creates the appropriate gamma ramp data from the
+  // profile enum. It would be served by the vendor.
+  return NULL;
+}
+
+void NativeDisplayDelegateX11::DrawBackground() {
+  if (!background_color_argb_)
+    return;
+  // Configuring CRTCs/Framebuffer clears the boot screen image.  Paint the
+  // same background color after updating framebuffer to minimize the
+  // duration of black screen at boot time.
+  XColor color;
+  Colormap colormap = DefaultColormap(display_, 0);
+  // XColor uses 16 bits per color.
+  color.red = (background_color_argb_ & 0x00FF0000) >> 8;
+  color.green = (background_color_argb_ & 0x0000FF00);
+  color.blue = (background_color_argb_ & 0x000000FF) << 8;
+  color.flags = DoRed | DoGreen | DoBlue;
+  XAllocColor(display_, colormap, &color);
+
+  GC gc = XCreateGC(display_, window_, 0, 0);
+  XSetForeground(display_, gc, color.pixel);
+  XSetFillStyle(display_, gc, FillSolid);
+  int width = DisplayWidth(display_, DefaultScreen(display_));
+  int height = DisplayHeight(display_, DefaultScreen(display_));
+  XFillRectangle(display_, window_, gc, 0, 0, width, height);
+  XFreeGC(display_, gc);
+  XFreeColors(display_, colormap, &color.pixel, 1, 0);
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/native_display_delegate_x11.h b/ui/display/chromeos/x11/native_display_delegate_x11.h
new file mode 100644
index 0000000..d349843
--- /dev/null
+++ b/ui/display/chromeos/x11/native_display_delegate_x11.h
@@ -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.
+
+#ifndef UI_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_
+#define UI_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "base/event_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/observer_list.h"
+#include "ui/display/display_export.h"
+#include "ui/display/types/native_display_delegate.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+
+// Forward declarations for Xlib and Xrandr.
+// This is so unused X definitions don't pollute the namespace.
+typedef unsigned long XID;
+typedef XID RROutput;
+typedef XID RRCrtc;
+typedef XID RRMode;
+typedef XID Window;
+
+struct _XDisplay;
+typedef struct _XDisplay Display;
+struct _XRROutputInfo;
+typedef _XRROutputInfo XRROutputInfo;
+struct _XRRScreenResources;
+typedef _XRRScreenResources XRRScreenResources;
+struct _XRRCrtcGamma;
+typedef _XRRCrtcGamma XRRCrtcGamma;
+
+namespace ui {
+
+class DisplayModeX11;
+class DisplaySnapshotX11;
+class NativeDisplayEventDispatcherX11;
+
+class DISPLAY_EXPORT NativeDisplayDelegateX11 : public NativeDisplayDelegate {
+ public:
+  // Helper class that allows NativeDisplayEventDispatcherX11 and
+  // NativeDisplayDelegateX11::PlatformEventObserverX11 to interact with this
+  // class or with mocks in tests.
+  class HelperDelegate {
+   public:
+    virtual ~HelperDelegate() {}
+
+    // Tells XRandR to update its configuration in response to |event|, an
+    // RRScreenChangeNotify event.
+    virtual void UpdateXRandRConfiguration(const base::NativeEvent& event) = 0;
+
+    // Returns the list of current outputs. This is used to discard duplicate
+    // events.
+    virtual const std::vector<DisplaySnapshot*>& GetCachedDisplays() const = 0;
+
+    // Notify |observers_| that a change in configuration has occurred.
+    virtual void NotifyDisplayObservers() = 0;
+  };
+
+  NativeDisplayDelegateX11();
+  virtual ~NativeDisplayDelegateX11();
+
+  // 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;
+
+ private:
+  class HelperDelegateX11;
+
+  // Parses all the modes made available by |screen_|.
+  void InitModes();
+
+  // Helper method for GetOutputs() that returns an OutputSnapshot struct based
+  // on the passed-in information.
+  DisplaySnapshotX11* InitDisplaySnapshot(RROutput id,
+                                          XRROutputInfo* info,
+                                          RRCrtc* last_used_crtc,
+                                          int index);
+
+  // Destroys unused CRTCs.
+  void DestroyUnusedCrtcs();
+
+  // Parks used CRTCs in a way which allows a framebuffer resize. This is faster
+  // than turning them off, resizing, then turning them back on.
+  // |min_screen_size| represent the smallest size between the current
+  // framebuffer size and the requested framebuffer size.
+  void UpdateCrtcsForNewFramebuffer(const gfx::Size& min_screen_size);
+
+  bool ConfigureCrtc(RRCrtc crtc, RRMode mode, RROutput output, int x, int y);
+
+  // Returns whether |id| is configured to preserve aspect when scaling.
+  bool IsOutputAspectPreservingScaling(RROutput id);
+
+  // Creates the gamma ramp for |new_profile|, or NULL if it doesn't exist.
+  // The caller should take the ownership.
+  XRRCrtcGamma* CreateGammaRampForProfile(
+      const DisplaySnapshotX11& x11_output,
+      ColorCalibrationProfile new_profile);
+
+  void DrawBackground();
+
+  Display* display_;
+  Window window_;
+
+  // Initialized when the server is grabbed and freed when it's ungrabbed.
+  XRRScreenResources* screen_;
+
+  std::map<RRMode, DisplayModeX11*> modes_;
+
+  // Every time GetOutputs() is called we cache the updated list of outputs in
+  // |cached_outputs_| so that we can check for duplicate events rather than
+  // propagate them.
+  ScopedVector<DisplaySnapshot> cached_outputs_;
+
+  scoped_ptr<HelperDelegate> helper_delegate_;
+
+  // Processes X11 display events associated with the root window and notifies
+  // |observers_| when a display change has occurred.
+  scoped_ptr<NativeDisplayEventDispatcherX11> platform_event_dispatcher_;
+
+  // List of observers waiting for display configuration change events.
+  ObserverList<NativeDisplayObserver> observers_;
+
+  // A background color used during boot time + multi displays.
+  uint32_t background_color_argb_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayDelegateX11);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_DELEGATE_X11_H_
diff --git a/ui/display/chromeos/x11/native_display_event_dispatcher_x11.cc b/ui/display/chromeos/x11/native_display_event_dispatcher_x11.cc
new file mode 100644
index 0000000..f5c2e2e
--- /dev/null
+++ b/ui/display/chromeos/x11/native_display_event_dispatcher_x11.cc
@@ -0,0 +1,107 @@
+// 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/time/default_tick_clock.h"
+#include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
+#include "ui/display/chromeos/x11/display_mode_x11.h"
+#include "ui/display/chromeos/x11/display_snapshot_x11.h"
+#include "ui/events/platform/platform_event_source.h"
+
+#include <X11/extensions/Xrandr.h>
+
+namespace ui {
+
+// static
+const int NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs = 7000;
+
+NativeDisplayEventDispatcherX11::NativeDisplayEventDispatcherX11(
+    NativeDisplayDelegateX11::HelperDelegate* delegate,
+    int xrandr_event_base)
+    : delegate_(delegate),
+      xrandr_event_base_(xrandr_event_base),
+      tick_clock_(new base::DefaultTickClock) {
+  startup_time_ = tick_clock_->NowTicks();
+}
+
+NativeDisplayEventDispatcherX11::~NativeDisplayEventDispatcherX11() {}
+
+bool NativeDisplayEventDispatcherX11::CanDispatchEvent(
+    const PlatformEvent& event) {
+  return (event->type - xrandr_event_base_ == RRScreenChangeNotify) ||
+         (event->type - xrandr_event_base_ == RRNotify);
+}
+
+uint32_t NativeDisplayEventDispatcherX11::DispatchEvent(
+    const PlatformEvent& event) {
+  if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
+    VLOG(1) << "Received RRScreenChangeNotify event";
+    delegate_->UpdateXRandRConfiguration(event);
+    return ui::POST_DISPATCH_PERFORM_DEFAULT;
+  }
+
+  // Bail out early for everything except RRNotify_OutputChange events
+  // about an output getting connected or disconnected.
+  if (event->type - xrandr_event_base_ != RRNotify)
+    return ui::POST_DISPATCH_PERFORM_DEFAULT;
+  const XRRNotifyEvent* notify_event = reinterpret_cast<XRRNotifyEvent*>(event);
+  if (notify_event->subtype != RRNotify_OutputChange)
+    return ui::POST_DISPATCH_PERFORM_DEFAULT;
+  const XRROutputChangeNotifyEvent* output_change_event =
+      reinterpret_cast<XRROutputChangeNotifyEvent*>(event);
+  const int action = output_change_event->connection;
+  if (action != RR_Connected && action != RR_Disconnected)
+    return ui::POST_DISPATCH_PERFORM_DEFAULT;
+
+  const bool connected = (action == RR_Connected);
+  VLOG(1) << "Received RRNotify_OutputChange event:"
+          << " output=" << output_change_event->output
+          << " crtc=" << output_change_event->crtc
+          << " mode=" << output_change_event->mode
+          << " action=" << (connected ? "connected" : "disconnected");
+
+  bool check_cache = (tick_clock_->NowTicks() - startup_time_)
+                         .InMilliseconds() <= kUseCacheAfterStartupMs;
+
+  if (check_cache) {
+    bool found_changed_output = false;
+    const std::vector<DisplaySnapshot*>& cached_outputs =
+        delegate_->GetCachedDisplays();
+    for (std::vector<DisplaySnapshot*>::const_iterator it =
+             cached_outputs.begin();
+         it != cached_outputs.end();
+         ++it) {
+      const DisplaySnapshotX11* x11_output =
+          static_cast<const DisplaySnapshotX11*>(*it);
+      const DisplayModeX11* x11_mode =
+          static_cast<const DisplayModeX11*>(x11_output->current_mode());
+
+      if (x11_output->output() == output_change_event->output) {
+        if (connected && x11_output->crtc() == output_change_event->crtc &&
+            x11_mode->mode_id() == output_change_event->mode) {
+          VLOG(1) << "Ignoring event describing already-cached state";
+          return POST_DISPATCH_PERFORM_DEFAULT;
+        }
+        found_changed_output = true;
+        break;
+      }
+    }
+
+    if (!connected && !found_changed_output) {
+      VLOG(1) << "Ignoring event describing already-disconnected output";
+      return ui::POST_DISPATCH_PERFORM_DEFAULT;
+    }
+  }
+
+  delegate_->NotifyDisplayObservers();
+
+  return ui::POST_DISPATCH_PERFORM_DEFAULT;
+}
+
+void NativeDisplayEventDispatcherX11::SetTickClockForTest(
+    scoped_ptr<base::TickClock> tick_clock) {
+  tick_clock_ = tick_clock.Pass();
+  startup_time_ = tick_clock_->NowTicks();
+}
+
+}  // namespace ui
diff --git a/ui/display/chromeos/x11/native_display_event_dispatcher_x11.h b/ui/display/chromeos/x11/native_display_event_dispatcher_x11.h
new file mode 100644
index 0000000..e490074
--- /dev/null
+++ b/ui/display/chromeos/x11/native_display_event_dispatcher_x11.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_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_
+#define UI_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_
+
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "ui/display/chromeos/x11/native_display_delegate_x11.h"
+#include "ui/events/platform/platform_event_dispatcher.h"
+
+namespace ui {
+
+// The implementation is interested in the cases of RRNotify events which
+// correspond to output add/remove events. Note that Output add/remove events
+// are sent in response to our own reconfiguration operations so spurious events
+// are common. Spurious events will have no effect.
+class DISPLAY_EXPORT NativeDisplayEventDispatcherX11
+    : public ui::PlatformEventDispatcher {
+ public:
+  NativeDisplayEventDispatcherX11(
+      NativeDisplayDelegateX11::HelperDelegate* delegate,
+      int xrandr_event_base);
+  virtual ~NativeDisplayEventDispatcherX11();
+
+  // ui::PlatformEventDispatcher:
+  virtual bool CanDispatchEvent(const PlatformEvent& event) OVERRIDE;
+  virtual uint32_t DispatchEvent(const PlatformEvent& event) OVERRIDE;
+
+  void SetTickClockForTest(scoped_ptr<base::TickClock> tick_clock);
+
+  // How long the cached output is valid after startup.
+  static const int kUseCacheAfterStartupMs;
+
+ private:
+  NativeDisplayDelegateX11::HelperDelegate* delegate_;  // Not owned.
+
+  // The base of the event numbers used to represent XRandr events used in
+  // decoding events regarding output add/remove.
+  int xrandr_event_base_;
+
+  base::TimeTicks startup_time_;
+
+  scoped_ptr<base::TickClock> tick_clock_;
+
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_CHROMEOS_X11_NATIVE_DISPLAY_EVENT_DISPATCHER_X11_H_
diff --git a/ui/display/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc b/ui/display/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc
new file mode 100644
index 0000000..d7cf589
--- /dev/null
+++ b/ui/display/chromeos/x11/native_display_event_dispatcher_x11_unittest.cc
@@ -0,0 +1,292 @@
+// 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 <X11/extensions/Xrandr.h>
+
+#undef Bool
+#undef None
+
+#include "base/test/simple_test_tick_clock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/display/chromeos/x11/display_mode_x11.h"
+#include "ui/display/chromeos/x11/display_snapshot_x11.h"
+#include "ui/display/chromeos/x11/native_display_delegate_x11.h"
+#include "ui/display/chromeos/x11/native_display_event_dispatcher_x11.h"
+
+namespace ui {
+
+namespace {
+
+DisplaySnapshotX11* CreateOutput(RROutput output, RRCrtc crtc) {
+  static const DisplayModeX11* kDefaultDisplayMode =
+      new DisplayModeX11(gfx::Size(1, 1), false, 60.0f, 20);
+
+  DisplaySnapshotX11* snapshot = new DisplaySnapshotX11(
+      0,
+      false,
+      gfx::Point(0, 0),
+      gfx::Size(0, 0),
+      DISPLAY_CONNECTION_TYPE_UNKNOWN,
+      false,
+      false,
+      std::string(),
+      std::vector<const DisplayMode*>(1, kDefaultDisplayMode),
+      kDefaultDisplayMode,
+      NULL,
+      output,
+      crtc,
+      0);
+
+  return snapshot;
+}
+
+class TestHelperDelegate : public NativeDisplayDelegateX11::HelperDelegate {
+ public:
+  TestHelperDelegate();
+  virtual ~TestHelperDelegate();
+
+  int num_calls_update_xrandr_config() const {
+    return num_calls_update_xrandr_config_;
+  }
+
+  int num_calls_notify_observers() const { return num_calls_notify_observers_; }
+
+  void set_cached_outputs(const std::vector<DisplaySnapshot*>& outputs) {
+    cached_outputs_ = outputs;
+  }
+
+  // NativeDisplayDelegateX11::HelperDelegate overrides:
+  virtual void UpdateXRandRConfiguration(const base::NativeEvent& event)
+      OVERRIDE;
+  virtual const std::vector<DisplaySnapshot*>& GetCachedDisplays() const
+      OVERRIDE;
+  virtual void NotifyDisplayObservers() OVERRIDE;
+
+ private:
+  int num_calls_update_xrandr_config_;
+  int num_calls_notify_observers_;
+
+  std::vector<DisplaySnapshot*> cached_outputs_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestHelperDelegate);
+};
+
+TestHelperDelegate::TestHelperDelegate()
+    : num_calls_update_xrandr_config_(0), num_calls_notify_observers_(0) {}
+
+TestHelperDelegate::~TestHelperDelegate() {}
+
+void TestHelperDelegate::UpdateXRandRConfiguration(
+    const base::NativeEvent& event) {
+  ++num_calls_update_xrandr_config_;
+}
+
+const std::vector<DisplaySnapshot*>& TestHelperDelegate::GetCachedDisplays()
+    const {
+  return cached_outputs_;
+}
+
+void TestHelperDelegate::NotifyDisplayObservers() {
+  ++num_calls_notify_observers_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// NativeDisplayEventDispatcherX11Test
+
+class NativeDisplayEventDispatcherX11Test : public testing::Test {
+ public:
+  NativeDisplayEventDispatcherX11Test();
+  virtual ~NativeDisplayEventDispatcherX11Test();
+
+ protected:
+  void DispatchScreenChangeEvent();
+  void DispatchOutputChangeEvent(RROutput output,
+                                 RRCrtc crtc,
+                                 RRMode mode,
+                                 bool connected);
+
+  int xrandr_event_base_;
+  scoped_ptr<TestHelperDelegate> helper_delegate_;
+  scoped_ptr<NativeDisplayEventDispatcherX11> dispatcher_;
+  base::SimpleTestTickClock* test_tick_clock_;  // Owned by |dispatcher_|.
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(NativeDisplayEventDispatcherX11Test);
+};
+
+NativeDisplayEventDispatcherX11Test::NativeDisplayEventDispatcherX11Test()
+    : xrandr_event_base_(10),
+      helper_delegate_(new TestHelperDelegate()),
+      dispatcher_(new NativeDisplayEventDispatcherX11(helper_delegate_.get(),
+                                                      xrandr_event_base_)),
+      test_tick_clock_(new base::SimpleTestTickClock) {
+  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(1));
+  dispatcher_->SetTickClockForTest(
+      scoped_ptr<base::TickClock>(test_tick_clock_));
+}
+
+NativeDisplayEventDispatcherX11Test::~NativeDisplayEventDispatcherX11Test() {}
+
+void NativeDisplayEventDispatcherX11Test::DispatchScreenChangeEvent() {
+  XRRScreenChangeNotifyEvent event = {0};
+  event.type = xrandr_event_base_ + RRScreenChangeNotify;
+
+  dispatcher_->DispatchEvent(reinterpret_cast<const PlatformEvent>(&event));
+}
+
+void NativeDisplayEventDispatcherX11Test::DispatchOutputChangeEvent(
+    RROutput output,
+    RRCrtc crtc,
+    RRMode mode,
+    bool connected) {
+  XRROutputChangeNotifyEvent event = {0};
+  event.type = xrandr_event_base_ + RRNotify;
+  event.subtype = RRNotify_OutputChange;
+  event.output = output;
+  event.crtc = crtc;
+  event.mode = mode;
+  event.connection = connected ? RR_Connected : RR_Disconnected;
+
+  dispatcher_->DispatchEvent(reinterpret_cast<const PlatformEvent>(&event));
+}
+
+}  // namespace
+
+TEST_F(NativeDisplayEventDispatcherX11Test, OnScreenChangedEvent) {
+  DispatchScreenChangeEvent();
+  EXPECT_EQ(1, helper_delegate_->num_calls_update_xrandr_config());
+  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnFirstEvent) {
+  DispatchOutputChangeEvent(1, 10, 20, true);
+  EXPECT_EQ(0, helper_delegate_->num_calls_update_xrandr_config());
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationAfterSecondEvent) {
+  DispatchOutputChangeEvent(1, 10, 20, true);
+
+  // Simulate addition of the first output to the cached output list.
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDisconnect) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(1, 10, 20, false);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnModeChange) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(1, 10, 21, true);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnSecondOutput) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test, CheckNotificationOnDifferentCrtc) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(1, 11, 20, true);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test,
+       CheckNotificationOnSecondOutputDisconnect) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  outputs.push_back(CreateOutput(2, 11));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(2, 11, 20, false);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test,
+       AvoidDuplicateNotificationOnSecondOutputDisconnect) {
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  outputs.push_back(CreateOutput(2, 11));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(2, 11, 20, false);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+
+  // Simulate removal of second output from cached output list.
+  outputs.erase(outputs.begin() + 1);
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  DispatchOutputChangeEvent(2, 11, 20, false);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+}
+
+TEST_F(NativeDisplayEventDispatcherX11Test,
+       ForceUpdateAfterCacheExpiration) {
+  // +1 to compenstate a possible rounding error.
+  const int kHalfOfExpirationMs =
+      NativeDisplayEventDispatcherX11::kUseCacheAfterStartupMs / 2 + 1;
+
+  ScopedVector<DisplaySnapshot> outputs;
+  outputs.push_back(CreateOutput(1, 10));
+  outputs.push_back(CreateOutput(2, 11));
+  helper_delegate_->set_cached_outputs(outputs.get());
+
+  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());
+
+  // Duplicated event will be ignored during the startup.
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());
+
+  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(
+      kHalfOfExpirationMs));
+
+  // Duplicated event will still be ignored.
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(0, helper_delegate_->num_calls_notify_observers());
+
+  // The startup timeout has been elapsed. Duplicated event
+  // should not be ignored.
+  test_tick_clock_->Advance(
+      base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs));
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(1, helper_delegate_->num_calls_notify_observers());
+
+  // Sending the same event immediately shoudldn't be ignored.
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(2, helper_delegate_->num_calls_notify_observers());
+
+  // Advancing time further should not change the behavior.
+  test_tick_clock_->Advance(base::TimeDelta::FromMilliseconds(
+      kHalfOfExpirationMs));
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(3, helper_delegate_->num_calls_notify_observers());
+
+  test_tick_clock_->Advance(
+      base::TimeDelta::FromMilliseconds(kHalfOfExpirationMs));
+  DispatchOutputChangeEvent(2, 11, 20, true);
+  EXPECT_EQ(4, helper_delegate_->num_calls_notify_observers());
+}
+
+}  // namespace ui
diff --git a/ui/display/display.gyp b/ui/display/display.gyp
new file mode 100644
index 0000000..ef1d6af
--- /dev/null
+++ b/ui/display/display.gyp
@@ -0,0 +1,179 @@
+# 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': {
+    'chromium_code': 1,
+  },
+  'targets': [
+    {
+      # GN version: //ui/display/types
+      'target_name': 'display_types',
+      'type': '<(component)',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../ui/gfx/gfx.gyp:gfx_geometry',
+      ],
+      'defines': [
+        'DISPLAY_TYPES_IMPLEMENTATION',
+      ],
+      'sources': [
+        # Note: file list duplicated in GN build.
+        'types/display_constants.h',
+        'types/display_mode.cc',
+        'types/display_mode.h',
+        'types/display_snapshot.cc',
+        'types/display_snapshot.h',
+        'types/display_types_export.h',
+        'types/native_display_delegate.h',
+        'types/native_display_observer.h',
+      ],
+    },
+    {
+      # GN version: //ui/display
+      'target_name': 'display',
+      'type': '<(component)',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../ui/gfx/gfx.gyp:gfx',
+        '../../ui/gfx/gfx.gyp:gfx_geometry',
+        'display_util',
+      ],
+      'defines': [
+        'DISPLAY_IMPLEMENTATION',
+      ],
+      'sources': [
+        # Note: file list duplicated in GN build.
+        'chromeos/display_configurator.cc',
+        'chromeos/display_configurator.h',
+        'chromeos/ozone/display_configurator_ozone.cc',
+        'chromeos/x11/display_configurator_x11.cc',
+        'chromeos/x11/display_mode_x11.cc',
+        'chromeos/x11/display_mode_x11.h',
+        'chromeos/x11/display_snapshot_x11.cc',
+        'chromeos/x11/display_snapshot_x11.h',
+        'chromeos/x11/display_util_x11.cc',
+        'chromeos/x11/display_util_x11.h',
+        'chromeos/x11/native_display_delegate_x11.cc',
+        'chromeos/x11/native_display_delegate_x11.h',
+        'chromeos/x11/native_display_event_dispatcher_x11.cc',
+        'chromeos/x11/native_display_event_dispatcher_x11.h',
+        'display_export.h',
+        'display_switches.cc',
+        'display_switches.h',
+      ],
+      'conditions': [
+        ['use_x11 == 1', {
+          'dependencies': [
+            '../../build/linux/system.gyp:x11',
+            '../../build/linux/system.gyp:xext',
+            '../../build/linux/system.gyp:xi',
+            '../../build/linux/system.gyp:xrandr',
+            '../../ui/events/platform/events_platform.gyp:events_platform',
+          ],
+        }],
+        ['chromeos == 1', {
+          'dependencies': [
+            'display_types',
+          ],
+        }],
+        ['use_ozone == 1', {
+          'dependencies': [
+            '../../ui/ozone/ozone.gyp:ozone',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //ui/display/util
+      'target_name': 'display_util',
+      'type': '<(component)',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../ui/gfx/gfx.gyp:gfx_geometry',
+      ],
+      'defines': [
+        'DISPLAY_UTIL_IMPLEMENTATION',
+      ],
+      'sources': [
+        # Note: file list shared with GN build.
+        'util/display_util.cc',
+        'util/display_util.h',
+        'util/display_util_export.h',
+        'util/edid_parser.cc',
+        'util/edid_parser.h',
+        'util/x11/edid_parser_x11.cc',
+        'util/x11/edid_parser_x11.h',
+      ],
+      'conditions': [
+        ['use_x11 == 1', {
+          'dependencies': [
+            '../../build/linux/system.gyp:xrandr',
+            '../../ui/gfx/x/gfx_x11.gyp:gfx_x11',
+          ],
+        }],
+        ['chromeos == 1', {
+          'dependencies': [
+            'display_types',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //ui/display:test_util
+      'target_name': 'display_test_util',
+      'type': '<(component)',
+      'dependencies': [
+        '../../base/base.gyp:base',
+        '../../ui/gfx/gfx.gyp:gfx',
+        '../../ui/gfx/gfx.gyp:gfx_geometry',
+      ],
+      'defines': [
+        'DISPLAY_IMPLEMENTATION',
+      ],
+      'sources': [
+        # Note: file list duplicated in GN build.
+        'chromeos/test/test_display_snapshot.cc',
+        'chromeos/test/test_display_snapshot.h',
+      ],
+      'conditions': [
+        ['chromeos == 1', {
+          'dependencies': [
+            'display_types',
+          ],
+        }],
+      ],
+    },
+    {
+      # GN version: //ui/display:display_unittests
+      'target_name': 'display_unittests',
+      'type': 'executable',
+      'dependencies': [
+        '../../base/base.gyp:run_all_unittests',
+        '../../testing/gtest.gyp:gtest',
+        '../../ui/gfx/gfx.gyp:gfx_geometry',
+        'display_util',
+      ],
+      'include_dirs': [
+        '../..',
+      ],
+      'sources': [
+        'chromeos/display_configurator_unittest.cc',
+        'chromeos/x11/display_util_x11_unittest.cc',
+        'chromeos/x11/native_display_event_dispatcher_x11_unittest.cc',
+        'util/display_util_unittest.cc',
+        'util/edid_parser_unittest.cc',
+      ],
+      'conditions': [
+        ['chromeos == 1', {
+          'dependencies': [
+            'display',
+            'display_test_util',
+            'display_types',
+          ],
+        }],
+      ],
+    },
+  ],
+}
diff --git a/ui/display/display_export.h b/ui/display/display_export.h
new file mode 100644
index 0000000..5dc19e0
--- /dev/null
+++ b/ui/display/display_export.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_DISPLAY_DISPLAY_EXPORT_H_
+#define UI_DISPLAY_DISPLAY_EXPORT_H_
+
+// Defines DISPLAY_EXPORT so that functionality implemented by the UI module
+// can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(DISPLAY_IMPLEMENTATION)
+#define DISPLAY_EXPORT __declspec(dllexport)
+#else
+#define DISPLAY_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(DISPLAY_IMPLEMENTATION)
+#define DISPLAY_EXPORT __attribute__((visibility("default")))
+#else
+#define DISPLAY_EXPORT
+#endif
+
+#endif
+
+#else  // !defined(COMPONENT_BUILD)
+
+#define DISPLAY_EXPORT
+
+#endif
+
+#endif  // UI_DISPLAY_DISPLAY_EXPORT_H_
diff --git a/ui/display/display_switches.cc b/ui/display/display_switches.cc
new file mode 100644
index 0000000..7d32b5b
--- /dev/null
+++ b/ui/display/display_switches.cc
@@ -0,0 +1,16 @@
+// 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/display/display_switches.h"
+
+namespace ui {
+namespace switches {
+
+#if defined(OS_CHROMEOS)
+const char kDisableDisplayColorCalibration[] =
+    "disable-display-color-calibration";
+#endif
+
+}  // namespace switches
+}  // namespace ui
diff --git a/ui/display/display_switches.h b/ui/display/display_switches.h
new file mode 100644
index 0000000..362853b
--- /dev/null
+++ b/ui/display/display_switches.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_DISPLAY_DISPLAY_SWITCHES_H_
+#define UI_DISPLAY_DISPLAY_SWITCHES_H_
+
+#include "base/compiler_specific.h"
+#include "ui/display/display_export.h"
+
+namespace ui {
+namespace switches {
+
+#if defined(OS_CHROMEOS)
+DISPLAY_EXPORT extern const char kDisableDisplayColorCalibration[];
+#endif
+
+}  // namespace switches
+}  // namespace ui
+
+#endif  // UI_BASE_UI_BASE_SWITCHES_H_
diff --git a/ui/display/types/BUILD.gn b/ui/display/types/BUILD.gn
new file mode 100644
index 0000000..4613119
--- /dev/null
+++ b/ui/display/types/BUILD.gn
@@ -0,0 +1,24 @@
+# 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.
+
+component("types") {
+  output_name = "display_types"
+  sources = [
+    "display_constants.h",
+    "display_mode.cc",
+    "display_mode.h",
+    "display_snapshot.cc",
+    "display_snapshot.h",
+    "display_types_export.h",
+    "native_display_delegate.h",
+    "native_display_observer.h",
+  ]
+
+  defines = [ "DISPLAY_TYPES_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//ui/gfx/geometry",
+  ]
+}
diff --git a/ui/display/types/DEPS b/ui/display/types/DEPS
new file mode 100644
index 0000000..f2fc77d
--- /dev/null
+++ b/ui/display/types/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+  "-ui",
+  "+ui/display/types",
+  "+ui/gfx/geometry",
+]
diff --git a/ui/display/types/display_constants.h b/ui/display/types/display_constants.h
new file mode 100644
index 0000000..3bb4e95
--- /dev/null
+++ b/ui/display/types/display_constants.h
@@ -0,0 +1,55 @@
+// 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_DISPLAY_TYPES_DISPLAY_CONSTANTS_H_
+#define UI_DISPLAY_TYPES_DISPLAY_CONSTANTS_H_
+
+namespace ui {
+
+// Used to describe the state of a multi-display configuration.
+enum MultipleDisplayState {
+  MULTIPLE_DISPLAY_STATE_INVALID,
+  MULTIPLE_DISPLAY_STATE_HEADLESS,
+  MULTIPLE_DISPLAY_STATE_SINGLE,
+  MULTIPLE_DISPLAY_STATE_DUAL_MIRROR,
+  MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED,
+};
+
+// Video output types.
+enum DisplayConnectionType {
+  DISPLAY_CONNECTION_TYPE_NONE = 0,
+  DISPLAY_CONNECTION_TYPE_UNKNOWN = 1 << 0,
+  DISPLAY_CONNECTION_TYPE_INTERNAL = 1 << 1,
+  DISPLAY_CONNECTION_TYPE_VGA = 1 << 2,
+  DISPLAY_CONNECTION_TYPE_HDMI = 1 << 3,
+  DISPLAY_CONNECTION_TYPE_DVI = 1 << 4,
+  DISPLAY_CONNECTION_TYPE_DISPLAYPORT = 1 << 5,
+  DISPLAY_CONNECTION_TYPE_NETWORK = 1 << 6,
+
+  // Update this when adding a new type.
+  DISPLAY_CONNECTION_TYPE_LAST = DISPLAY_CONNECTION_TYPE_NETWORK
+};
+
+// Content protection methods applied on video output.
+enum ContentProtectionMethod {
+  CONTENT_PROTECTION_METHOD_NONE = 0,
+  CONTENT_PROTECTION_METHOD_HDCP = 1 << 0,
+};
+
+// HDCP protection state.
+enum HDCPState { HDCP_STATE_UNDESIRED, HDCP_STATE_DESIRED, HDCP_STATE_ENABLED };
+
+// Color calibration profiles. Don't change the order, and edit
+// tools/metrics/histograms/histograms.xml when a new item is added.
+enum ColorCalibrationProfile {
+  COLOR_PROFILE_STANDARD,
+  COLOR_PROFILE_DYNAMIC,
+  COLOR_PROFILE_MOVIE,
+  COLOR_PROFILE_READING,
+  NUM_COLOR_PROFILES,
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_TYPES_DISPLAY_CONSTANTS_H_
diff --git a/ui/display/types/display_mode.cc b/ui/display/types/display_mode.cc
new file mode 100644
index 0000000..1e4454d
--- /dev/null
+++ b/ui/display/types/display_mode.cc
@@ -0,0 +1,28 @@
+// 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/display/types/display_mode.h"
+
+#include "base/strings/stringprintf.h"
+
+namespace ui {
+
+DisplayMode::DisplayMode(const gfx::Size& size,
+                         bool interlaced,
+                         float refresh_rate)
+    : size_(size),
+      is_interlaced_(interlaced),
+      refresh_rate_(refresh_rate) {}
+
+DisplayMode::~DisplayMode() {}
+
+std::string DisplayMode::ToString() const {
+  return base::StringPrintf("[%dx%d %srate=%f]",
+                            size_.width(),
+                            size_.height(),
+                            is_interlaced_ ? "interlaced " : "",
+                            refresh_rate_);
+}
+
+}  // namespace ui
diff --git a/ui/display/types/display_mode.h b/ui/display/types/display_mode.h
new file mode 100644
index 0000000..cebe5fc
--- /dev/null
+++ b/ui/display/types/display_mode.h
@@ -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.
+
+#ifndef UI_DISPLAY_TYPES_DISPLAY_MODE_H_
+#define UI_DISPLAY_TYPES_DISPLAY_MODE_H_
+
+#include <string>
+
+#include "base/macros.h"
+#include "ui/display/types/display_types_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+// This class represents the basic information for a native mode. Platforms will
+// extend this class to add platform specific information about the mode.
+class DISPLAY_TYPES_EXPORT DisplayMode {
+ public:
+  DisplayMode(const gfx::Size& size, bool interlaced, float refresh_rate);
+  virtual ~DisplayMode();
+
+  const gfx::Size& size() const { return size_; }
+  bool is_interlaced() const { return is_interlaced_; }
+  float refresh_rate() const { return refresh_rate_; }
+
+  virtual std::string ToString() const;
+
+ private:
+  gfx::Size size_;
+  bool is_interlaced_;
+  float refresh_rate_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplayMode);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_TYPES_DISPLAY_MODE_H_
diff --git a/ui/display/types/display_snapshot.cc b/ui/display/types/display_snapshot.cc
new file mode 100644
index 0000000..efe401b
--- /dev/null
+++ b/ui/display/types/display_snapshot.cc
@@ -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.
+
+#include "ui/display/types/display_snapshot.h"
+
+namespace ui {
+
+DisplaySnapshot::DisplaySnapshot(int64_t display_id,
+                                 bool has_proper_display_id,
+                                 const gfx::Point& origin,
+                                 const gfx::Size& physical_size,
+                                 DisplayConnectionType type,
+                                 bool is_aspect_preserving_scaling,
+                                 bool has_overscan,
+                                 std::string display_name,
+                                 const std::vector<const DisplayMode*>& modes,
+                                 const DisplayMode* current_mode,
+                                 const DisplayMode* native_mode)
+    : display_id_(display_id),
+      has_proper_display_id_(has_proper_display_id),
+      origin_(origin),
+      physical_size_(physical_size),
+      type_(type),
+      is_aspect_preserving_scaling_(is_aspect_preserving_scaling),
+      has_overscan_(has_overscan),
+      display_name_(display_name),
+      modes_(modes),
+      current_mode_(current_mode),
+      native_mode_(native_mode) {}
+
+DisplaySnapshot::~DisplaySnapshot() {}
+
+}  // namespace ui
diff --git a/ui/display/types/display_snapshot.h b/ui/display/types/display_snapshot.h
new file mode 100644
index 0000000..7019208
--- /dev/null
+++ b/ui/display/types/display_snapshot.h
@@ -0,0 +1,90 @@
+// 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_DISPLAY_TYPES_DISPLAY_SNAPSHOT_H_
+#define UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_H_
+
+#include <vector>
+
+#include "ui/display/types/display_constants.h"
+#include "ui/display/types/display_mode.h"
+#include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+// This class represents the state of a display at one point in time. Platforms
+// will extend this class in order to add platform specific configuration and
+// identifiers required to configure this display.
+class DISPLAY_TYPES_EXPORT DisplaySnapshot {
+ public:
+  DisplaySnapshot(int64_t display_id,
+                  bool has_proper_display_id,
+                  const gfx::Point& origin,
+                  const gfx::Size& physical_size,
+                  DisplayConnectionType type,
+                  bool is_aspect_preserving_scaling,
+                  bool has_overscan,
+                  std::string display_name,
+                  const std::vector<const DisplayMode*>& modes,
+                  const DisplayMode* current_mode,
+                  const DisplayMode* native_mode);
+  virtual ~DisplaySnapshot();
+
+  const gfx::Point& origin() const { return origin_; }
+  const gfx::Size& physical_size() const { return physical_size_; }
+  ui::DisplayConnectionType type() const { return type_; }
+  bool is_aspect_preserving_scaling() const {
+    return is_aspect_preserving_scaling_;
+  }
+  bool has_overscan() const { return has_overscan_; }
+  std::string display_name() const { return display_name_; }
+
+  int64_t display_id() const { return display_id_; }
+  bool has_proper_display_id() const { return has_proper_display_id_; }
+
+  const DisplayMode* current_mode() const { return current_mode_; }
+  const DisplayMode* native_mode() const { return native_mode_; }
+
+  const std::vector<const DisplayMode*>& modes() const { return modes_; }
+
+  void set_current_mode(const DisplayMode* mode) { current_mode_ = mode; }
+  void set_origin(const gfx::Point& origin) { origin_ = origin; }
+  void add_mode(const DisplayMode* mode) { modes_.push_back(mode); }
+
+  // Returns a textual representation of this display state.
+  virtual std::string ToString() const = 0;
+
+ protected:
+  // Display id for this output.
+  int64_t display_id_;
+  bool has_proper_display_id_;
+
+  // Display's origin on the framebuffer.
+  gfx::Point origin_;
+
+  gfx::Size physical_size_;
+
+  DisplayConnectionType type_;
+
+  bool is_aspect_preserving_scaling_;
+
+  bool has_overscan_;
+
+  std::string display_name_;
+
+  std::vector<const DisplayMode*> modes_;  // Not owned.
+
+  // Mode currently being used by the output.
+  const DisplayMode* current_mode_;
+
+  // "Best" mode supported by the output.
+  const DisplayMode* native_mode_;
+
+  DISALLOW_COPY_AND_ASSIGN(DisplaySnapshot);
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_TYPES_DISPLAY_SNAPSHOT_H_
diff --git a/ui/display/types/display_types_export.h b/ui/display/types/display_types_export.h
new file mode 100644
index 0000000..b2ddbd1
--- /dev/null
+++ b/ui/display/types/display_types_export.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_DISPLAY_DISPLAY_TYPES_EXPORT_H_
+#define UI_DISPLAY_DISPLAY_TYPES_EXPORT_H_
+
+// Defines DISPLAY_TYPES_EXPORT so that functionality implemented by the
+// DISPLAY_TYPES module can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(DISPLAY_TYPES_IMPLEMENTATION)
+#define DISPLAY_TYPES_EXPORT __declspec(dllexport)
+#else
+#define DISPLAY_TYPES_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(DISPLAY_TYPES_IMPLEMENTATION)
+#define DISPLAY_TYPES_EXPORT __attribute__((visibility("default")))
+#else
+#define DISPLAY_TYPES_EXPORT
+#endif
+
+#endif
+
+#else  // !defined(COMPONENT_BUILD)
+
+#define DISPLAY_TYPES_EXPORT
+
+#endif
+
+#endif  // UI_DISPLAY_DISPLAY_TYPES_EXPORT_H_
diff --git a/ui/display/types/native_display_delegate.h b/ui/display/types/native_display_delegate.h
new file mode 100644
index 0000000..28c70e0
--- /dev/null
+++ b/ui/display/types/native_display_delegate.h
@@ -0,0 +1,95 @@
+// 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_DISPLAY_TYPES_NATIVE_DISPLAY_DELEGATE_H_
+#define UI_DISPLAY_TYPES_NATIVE_DISPLAY_DELEGATE_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "ui/display/types/display_constants.h"
+#include "ui/display/types/display_types_export.h"
+
+namespace gfx {
+class Point;
+class Size;
+}
+
+namespace ui {
+class DisplayMode;
+class DisplaySnapshot;
+
+class NativeDisplayObserver;
+
+// Interface for classes that perform display configuration actions on behalf
+// of DisplayConfigurator.
+class DISPLAY_TYPES_EXPORT NativeDisplayDelegate {
+ public:
+  virtual ~NativeDisplayDelegate() {}
+
+  virtual void Initialize() = 0;
+
+  // Grabs and refreshes any display server related resources. Must be balanced
+  // by a call to UngrabServer().
+  virtual void GrabServer() = 0;
+
+  // Released the display server and any resources allocated by GrabServer().
+  virtual void UngrabServer() = 0;
+
+  // Flushes all pending requests and waits for replies.
+  virtual void SyncWithServer() = 0;
+
+  // Sets the window's background color to |color_argb|.
+  virtual void SetBackgroundColor(uint32_t color_argb) = 0;
+
+  // Enables DPMS and forces it to the "on" state.
+  virtual void ForceDPMSOn() = 0;
+
+  // Returns information about the current outputs. This method may block for
+  // 60 milliseconds or more.
+  // NativeDisplayDelegate maintains ownership of the ui::DisplaySnapshot
+  // pointers.
+  virtual std::vector<ui::DisplaySnapshot*> GetDisplays() = 0;
+
+  // Adds |mode| to |output|. |mode| must be a valid display mode pointer.
+  virtual void AddMode(const ui::DisplaySnapshot& output,
+                       const ui::DisplayMode* mode) = 0;
+
+  // Configures the display represented by |output| to use |mode| and positions
+  // the display to |origin| in the framebuffer. |mode| can be NULL, which
+  // represents disabling the display. Returns true on success.
+  virtual bool Configure(const ui::DisplaySnapshot& output,
+                         const ui::DisplayMode* mode,
+                         const gfx::Point& origin) = 0;
+
+  // Called to set the frame buffer (underlying XRR "screen") size.
+  virtual void CreateFrameBuffer(const gfx::Size& size) = 0;
+
+  // Gets HDCP state of output.
+  virtual bool GetHDCPState(const ui::DisplaySnapshot& output,
+                            ui::HDCPState* state) = 0;
+
+  // Sets HDCP state of output.
+  virtual bool SetHDCPState(const ui::DisplaySnapshot& output,
+                            ui::HDCPState state) = 0;
+
+  // Gets the available list of color calibrations.
+  virtual std::vector<ui::ColorCalibrationProfile>
+      GetAvailableColorCalibrationProfiles(
+          const ui::DisplaySnapshot& output) = 0;
+
+  // Sets the color calibration of |output| to |new_profile|.
+  virtual bool SetColorCalibrationProfile(
+      const ui::DisplaySnapshot& output,
+      ui::ColorCalibrationProfile new_profile) = 0;
+
+  virtual void AddObserver(NativeDisplayObserver* observer) = 0;
+
+  virtual void RemoveObserver(NativeDisplayObserver* observer) = 0;
+};
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_TYPES_NATIVE_DISPLAY_DELEGATE_H_
diff --git a/ui/display/types/native_display_observer.h b/ui/display/types/native_display_observer.h
new file mode 100644
index 0000000..6091036
--- /dev/null
+++ b/ui/display/types/native_display_observer.h
@@ -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.
+
+#ifndef UI_DISPLAY_TYPES_NATIVE_DISPLAY_OBSERVER_H_
+#define UI_DISPLAY_TYPES_NATIVE_DISPLAY_OBSERVER_H_
+
+#include "ui/display/types/display_types_export.h"
+
+namespace ui {
+
+// Observer class used by NativeDisplayDelegate to announce when the display
+// configuration changes.
+class DISPLAY_TYPES_EXPORT NativeDisplayObserver {
+ public:
+  virtual ~NativeDisplayObserver() {}
+
+  virtual void OnConfigurationChanged() = 0;
+};
+
+}  //  namespace ui
+
+#endif  // UI_DISPLAY_TYPES_NATIVE_DISPLAY_OBSERVER_H_
diff --git a/ui/display/util/BUILD.gn b/ui/display/util/BUILD.gn
new file mode 100644
index 0000000..83f456d
--- /dev/null
+++ b/ui/display/util/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2014 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+component("util") {
+  output_name = "display_util"
+  sources = [
+    "display_util.cc",
+    "display_util.h",
+    "display_util_export.h",
+    "edid_parser.cc",
+    "edid_parser.h",
+  ]
+
+  defines = [ "DISPLAY_UTIL_IMPLEMENTATION" ]
+
+  deps = [
+    "//base",
+    "//ui/gfx/geometry",
+  ]
+
+  if (use_x11) {
+    sources += [
+      "x11/edid_parser_x11.cc",
+      "x11/edid_parser_x11.h",
+    ]
+    configs += [ "//build/config/linux:xrandr" ]
+    deps += [ "//ui/gfx/x" ]
+  }
+  if (is_chromeos) {
+    deps += [ "//ui/display/types" ]
+  }
+}
diff --git a/ui/display/util/display_util.cc b/ui/display/util/display_util.cc
new file mode 100644
index 0000000..aa23ec6
--- /dev/null
+++ b/ui/display/util/display_util.cc
@@ -0,0 +1,71 @@
+// 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/display/util/display_util.h"
+
+#include "base/logging.h"
+
+namespace ui {
+
+namespace {
+
+// A list of bogus sizes in mm that should be ignored.
+// See crbug.com/136533. The first element maintains the minimum
+// size required to be valid size.
+const int kInvalidDisplaySizeList[][2] = {
+  {40, 30},
+  {50, 40},
+  {160, 90},
+  {160, 100},
+};
+
+// The DPI threshold to detect high density screen.
+// Higher DPI than this will use device_scale_factor=2.
+const unsigned int kHighDensityDPIThresholdSmall = 170;
+
+// The HiDPI threshold for large (usually external) monitors. Lower threshold
+// makes sense for large monitors, because such monitors should be located
+// farther from the user's face usually. See http://crbug.com/348279
+const unsigned int kHighDensityDPIThresholdLarge = 150;
+
+// The width threshold in mm for "large" monitors.
+const int kLargeDisplayWidthThresholdMM = 500;
+
+// 1 inch in mm.
+const float kInchInMm = 25.4f;
+
+}  // namespace
+
+bool IsDisplaySizeBlackListed(const gfx::Size& physical_size) {
+  // Ignore if the reported display is smaller than minimum size.
+  if (physical_size.width() <= kInvalidDisplaySizeList[0][0] ||
+      physical_size.height() <= kInvalidDisplaySizeList[0][1]) {
+    VLOG(1) << "Smaller than minimum display size";
+    return true;
+  }
+  for (size_t i = 1; i < arraysize(kInvalidDisplaySizeList); ++i) {
+    const gfx::Size size(kInvalidDisplaySizeList[i][0],
+                         kInvalidDisplaySizeList[i][1]);
+    if (physical_size == size) {
+      VLOG(1) << "Black listed display size detected:" << size.ToString();
+      return true;
+    }
+  }
+  return false;
+}
+
+float GetScaleFactor(const gfx::Size& physical_size_in_mm,
+                     const gfx::Size& screen_size_in_pixels) {
+  if (IsDisplaySizeBlackListed(physical_size_in_mm))
+    return 1.0f;
+
+  const unsigned int dpi = (kInchInMm * screen_size_in_pixels.width() /
+                            physical_size_in_mm.width());
+  const unsigned int threshold =
+      (physical_size_in_mm.width() >= kLargeDisplayWidthThresholdMM) ?
+      kHighDensityDPIThresholdLarge : kHighDensityDPIThresholdSmall;
+  return (dpi > threshold) ? 2.0f : 1.0f;
+}
+
+}  // namespace ui
diff --git a/ui/display/util/display_util.h b/ui/display/util/display_util.h
new file mode 100644
index 0000000..012269d
--- /dev/null
+++ b/ui/display/util/display_util.h
@@ -0,0 +1,26 @@
+// 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_DISPLAY_UTIL_DISPLAY_UTIL_H_
+#define UI_DISPLAY_UTIL_DISPLAY_UTIL_H_
+
+#include "ui/display/util/display_util_export.h"
+#include "ui/gfx/geometry/size.h"
+
+namespace ui {
+
+// Returns true if a given size is in the list of bogus sizes in mm that should
+// be ignored.
+DISPLAY_UTIL_EXPORT bool IsDisplaySizeBlackListed(
+    const gfx::Size& physical_size);
+
+// Returns the desired device scale factor for the display with the given
+// physical_size and resoultion.
+DISPLAY_UTIL_EXPORT float GetScaleFactor(
+    const gfx::Size& physical_size_in_mm,
+    const gfx::Size& screen_size_in_pixels);
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_UTIL_DISPLAY_UTIL_H_
diff --git a/ui/display/util/display_util_export.h b/ui/display/util/display_util_export.h
new file mode 100644
index 0000000..8eeb30d
--- /dev/null
+++ b/ui/display/util/display_util_export.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_DISPLAY_UTIL_DISPLAY_UTIL_EXPORT_H_
+#define UI_DISPLAY_UTIL_DISPLAY_UTIL_EXPORT_H_
+
+// Defines DISPLAY_UTIL_EXPORT so that functionality implemented by the
+// display_util module can be exported to consumers.
+
+#if defined(COMPONENT_BUILD)
+
+#if defined(WIN32)
+
+#if defined(DISPLAY_UTIL_IMPLEMENTATION)
+#define DISPLAY_UTIL_EXPORT __declspec(dllexport)
+#else
+#define DISPLAY_UTIL_EXPORT __declspec(dllimport)
+#endif
+
+#else  // !defined(WIN32)
+
+#if defined(DISPLAY_UTIL_IMPLEMENTATION)
+#define DISPLAY_UTIL_EXPORT __attribute__((visibility("default")))
+#else
+#define DISPLAY_UTIL_EXPORT
+#endif
+
+#endif
+
+#else  // !defined(COMPONENT_BUILD)
+
+#define DISPLAY_UTIL_EXPORT
+
+#endif
+
+#endif  // UI_DISPLAY_UTIL_DISPLAY_UTIL_EXPORT_H_
diff --git a/ui/display/util/display_util_unittest.cc b/ui/display/util/display_util_unittest.cc
new file mode 100644
index 0000000..ddb4a1c
--- /dev/null
+++ b/ui/display/util/display_util_unittest.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/display/util/display_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+TEST(DisplayUtilTest, TestBlackListedDisplay) {
+  EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(10, 10)));
+  EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(40, 30)));
+  EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(50, 40)));
+  EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(160, 90)));
+  EXPECT_TRUE(IsDisplaySizeBlackListed(gfx::Size(160, 100)));
+
+  EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(50, 60)));
+  EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(100, 70)));
+  EXPECT_FALSE(IsDisplaySizeBlackListed(gfx::Size(272, 181)));
+}
+
+TEST(DisplayUtilTest, GetScaleFactor) {
+  // Normal chromebook spec. DPI ~= 130
+  EXPECT_EQ(1.0f, GetScaleFactor(
+      gfx::Size(256, 144), gfx::Size(1366, 768)));
+
+  // HiDPI like Pixel. DPI ~= 240
+  EXPECT_EQ(2.0f, GetScaleFactor(
+      gfx::Size(272, 181), gfx::Size(2560, 1700)));
+
+  // A large external display but normal pixel density. DPI ~= 100
+  EXPECT_EQ(1.0f, GetScaleFactor(
+      gfx::Size(641, 400), gfx::Size(2560, 1600)));
+
+  // A large external display with high pixel density. DPI ~= 157
+  EXPECT_EQ(2.0f, GetScaleFactor(
+      gfx::Size(621, 341), gfx::Size(3840, 2160)));
+
+  // 4K resolution but the display is physically even larger. DPI ~= 114
+  EXPECT_EQ(1.0f, GetScaleFactor(
+      gfx::Size(854, 481), gfx::Size(3840, 2160)));
+
+  // 21.5 inch, 1080p. DPI ~= 102
+  EXPECT_EQ(1.0f, GetScaleFactor(
+      gfx::Size(476, 267), gfx::Size(1920, 1080)));
+
+  // Corner case; slightly higher density but smaller screens. DPI ~= 165
+  EXPECT_EQ(1.0f, GetScaleFactor(
+      gfx::Size(293, 165), gfx::Size(1920, 1080)));
+}
+
+}  // namespace ui
diff --git a/ui/display/util/edid_parser.cc b/ui/display/util/edid_parser.cc
new file mode 100644
index 0000000..b95eef4
--- /dev/null
+++ b/ui/display/util/edid_parser.cc
@@ -0,0 +1,197 @@
+// 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/display/util/edid_parser.h"
+
+#include <algorithm>
+
+#include "base/hash.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+
+namespace ui {
+
+namespace {
+
+// Returns 64-bit persistent ID for the specified manufacturer's ID and
+// product_code_hash, and the index of the output it is connected to.
+// |output_index| is used to distinguish the displays of the same type. For
+// example, swapping two identical display between two outputs will not be
+// treated as swap. The 'serial number' field in EDID isn't used here because
+// it is not guaranteed to have unique number and it may have the same fixed
+// value (like 0).
+int64_t GetID(uint16_t manufacturer_id,
+              uint32_t product_code_hash,
+              uint8_t output_index) {
+  return ((static_cast<int64_t>(manufacturer_id) << 40) |
+          (static_cast<int64_t>(product_code_hash) << 8) | output_index);
+}
+
+}  // namespace
+
+bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid,
+                          uint8_t output_index,
+                          int64_t* display_id_out) {
+  uint16_t manufacturer_id = 0;
+  std::string product_name;
+
+  // ParseOutputDeviceData fails if it doesn't have product_name.
+  ParseOutputDeviceData(edid, &manufacturer_id, &product_name);
+
+  // Generates product specific value from product_name instead of product code.
+  // See crbug.com/240341
+  uint32_t product_code_hash = product_name.empty() ?
+      0 : base::Hash(product_name);
+  if (manufacturer_id != 0) {
+    // An ID based on display's index will be assigned later if this call
+    // fails.
+    *display_id_out = GetID(
+        manufacturer_id, product_code_hash, output_index);
+    return true;
+  }
+  return false;
+}
+
+bool ParseOutputDeviceData(const std::vector<uint8_t>& edid,
+                           uint16_t* manufacturer_id,
+                           std::string* human_readable_name) {
+  // See http://en.wikipedia.org/wiki/Extended_display_identification_data
+  // for the details of EDID data format.  We use the following data:
+  //   bytes 8-9: manufacturer EISA ID, in big-endian
+  //   bytes 54-125: four descriptors (18-bytes each) which may contain
+  //     the display name.
+  const unsigned int kManufacturerOffset = 8;
+  const unsigned int kManufacturerLength = 2;
+  const unsigned int kDescriptorOffset = 54;
+  const unsigned int kNumDescriptors = 4;
+  const unsigned int kDescriptorLength = 18;
+  // The specifier types.
+  const unsigned char kMonitorNameDescriptor = 0xfc;
+
+  if (manufacturer_id) {
+    if (edid.size() < kManufacturerOffset + kManufacturerLength) {
+      LOG(ERROR) << "too short EDID data: manifacturer id";
+      return false;
+    }
+
+    *manufacturer_id =
+        *reinterpret_cast<const uint16_t*>(&edid[kManufacturerOffset]);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+    *manufacturer_id = base::ByteSwap(*manufacturer_id);
+#endif
+  }
+
+  if (!human_readable_name)
+    return true;
+
+  human_readable_name->clear();
+  for (unsigned int i = 0; i < kNumDescriptors; ++i) {
+    if (edid.size() < kDescriptorOffset + (i + 1) * kDescriptorLength)
+      break;
+
+    size_t offset = kDescriptorOffset + i * kDescriptorLength;
+    // If the descriptor contains the display name, it has the following
+    // structure:
+    //   bytes 0-2, 4: \0
+    //   byte 3: descriptor type, defined above.
+    //   bytes 5-17: text data, ending with \r, padding with spaces
+    // we should check bytes 0-2 and 4, since it may have other values in
+    // case that the descriptor contains other type of data.
+    if (edid[offset] == 0 && edid[offset + 1] == 0 && edid[offset + 2] == 0 &&
+        edid[offset + 3] == kMonitorNameDescriptor && edid[offset + 4] == 0) {
+      std::string found_name(reinterpret_cast<const char*>(&edid[offset + 5]),
+                             kDescriptorLength - 5);
+      base::TrimWhitespaceASCII(
+          found_name, base::TRIM_TRAILING, human_readable_name);
+      break;
+    }
+  }
+
+  // Verify if the |human_readable_name| consists of printable characters only.
+  for (size_t i = 0; i < human_readable_name->size(); ++i) {
+    char c = (*human_readable_name)[i];
+    if (!isascii(c) || !isprint(c)) {
+      human_readable_name->clear();
+      LOG(ERROR) << "invalid EDID: human unreadable char in name";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool ParseOutputOverscanFlag(const std::vector<uint8_t>& edid,
+                             bool* flag) {
+  // See http://en.wikipedia.org/wiki/Extended_display_identification_data
+  // for the extension format of EDID.  Also see EIA/CEA-861 spec for
+  // the format of the extensions and how video capability is encoded.
+  //  - byte 0: tag.  should be 02h.
+  //  - byte 1: revision.  only cares revision 3 (03h).
+  //  - byte 4-: data block.
+  const unsigned int kExtensionBase = 128;
+  const unsigned int kExtensionSize = 128;
+  const unsigned int kNumExtensionsOffset = 126;
+  const unsigned int kDataBlockOffset = 4;
+  const unsigned char kCEAExtensionTag = '\x02';
+  const unsigned char kExpectedExtensionRevision = '\x03';
+  const unsigned char kExtendedTag = 7;
+  const unsigned char kExtendedVideoCapabilityTag = 0;
+  const unsigned int kPTOverscan = 4;
+  const unsigned int kITOverscan = 2;
+  const unsigned int kCEOverscan = 0;
+
+  if (edid.size() <= kNumExtensionsOffset)
+    return false;
+
+  unsigned char num_extensions = edid[kNumExtensionsOffset];
+
+  for (size_t i = 0; i < num_extensions; ++i) {
+    // Skip parsing the whole extension if size is not enough.
+    if (edid.size() < kExtensionBase + (i + 1) * kExtensionSize)
+      break;
+
+    size_t extension_offset = kExtensionBase + i * kExtensionSize;
+    unsigned char tag = edid[extension_offset];
+    unsigned char revision = edid[extension_offset + 1];
+    if (tag != kCEAExtensionTag || revision != kExpectedExtensionRevision)
+      continue;
+
+    unsigned char timing_descriptors_start = std::min(
+        edid[extension_offset + 2], static_cast<unsigned char>(kExtensionSize));
+
+    for (size_t data_offset = extension_offset + kDataBlockOffset;
+         data_offset < extension_offset + timing_descriptors_start;) {
+      // A data block is encoded as:
+      // - byte 1 high 3 bits: tag. '07' for extended tags.
+      // - byte 1 remaining bits: the length of data block.
+      // - byte 2: the extended tag.  '0' for video capability.
+      // - byte 3: the capability.
+      unsigned char tag = edid[data_offset] >> 5;
+      unsigned char payload_length = edid[data_offset] & 0x1f;
+      if (data_offset + payload_length > edid.size())
+        break;
+
+      if (tag != kExtendedTag || payload_length < 2 ||
+          edid[data_offset + 1] != kExtendedVideoCapabilityTag) {
+        data_offset += payload_length + 1;
+        continue;
+      }
+
+      // The difference between preferred, IT, and CE video formats
+      // doesn't matter. Sets |flag| to true if any of these flags are true.
+      if ((edid[data_offset + 2] & (1 << kPTOverscan)) ||
+          (edid[data_offset + 2] & (1 << kITOverscan)) ||
+          (edid[data_offset + 2] & (1 << kCEOverscan))) {
+        *flag = true;
+      } else {
+        *flag = false;
+      }
+      return true;
+    }
+  }
+
+  return false;
+}
+
+}  // namespace ui
diff --git a/ui/display/util/edid_parser.h b/ui/display/util/edid_parser.h
new file mode 100644
index 0000000..4f12d40
--- /dev/null
+++ b/ui/display/util/edid_parser.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_DISPLAY_UTIL_EDID_PARSER_H_
+#define UI_DISPLAY_UTIL_EDID_PARSER_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "ui/display/util/display_util_export.h"
+
+// EDID (Extended Display Identification Data) is a format for monitor
+// metadata. This provides a parser for the data.
+
+namespace ui {
+
+// Generates the display id for the pair of |edid| and |index|, and store in
+// |display_id_out|. Returns true if the display id is successfully generated,
+// or false otherwise.
+DISPLAY_UTIL_EXPORT bool GetDisplayIdFromEDID(const std::vector<uint8_t>& edid,
+                                              uint8_t index,
+                                              int64_t* display_id_out);
+
+// Parses |edid| as EDID data and stores extracted data into |manufacturer_id|
+// and |human_readable_name| and returns true. NULL can be passed for unwanted
+// output parameters. Some devices (especially internal displays) may not have
+// the field for |human_readable_name|, and it will return true in that case.
+DISPLAY_UTIL_EXPORT bool ParseOutputDeviceData(
+    const std::vector<uint8_t>& edid,
+    uint16_t* manufacturer_id,
+    std::string* human_readable_name);
+
+DISPLAY_UTIL_EXPORT bool ParseOutputOverscanFlag(
+    const std::vector<uint8_t>& edid,
+    bool* flag);
+
+}  // namespace ui
+
+#endif // UI_DISPLAY_UTIL_EDID_PARSER_H_
diff --git a/ui/display/util/edid_parser_unittest.cc b/ui/display/util/edid_parser_unittest.cc
new file mode 100644
index 0000000..ef55bd6
--- /dev/null
+++ b/ui/display/util/edid_parser_unittest.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/display/util/edid_parser.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ui {
+
+namespace {
+
+// Returns the number of characters in the string literal but doesn't count its
+// terminator NULL byte.
+#define charsize(str) (arraysize(str) - 1)
+
+// Sample EDID data extracted from real devices.
+const unsigned char kNormalDisplay[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+    "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+    "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+    "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+    "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+    "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+    "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+    "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+
+const unsigned char kInternalDisplay[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+    "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+    "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+    "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+    "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+    "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+    "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+    "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+
+const unsigned char kOverscanDisplay[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+    "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+    "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+    "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+    "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+    "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+    "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+    "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+    "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+    "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+    "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+    "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+    "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+    "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+    "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+
+// The EDID info misdetecting overscan once. see crbug.com/226318
+const unsigned char kMisdetectedDisplay[] =
+    "\x00\xff\xff\xff\xff\xff\xff\x00\x10\xac\x64\x40\x4c\x30\x30\x32"
+    "\x0c\x15\x01\x03\x80\x40\x28\x78\xea\x8d\x85\xad\x4f\x35\xb1\x25"
+    "\x0e\x50\x54\xa5\x4b\x00\x71\x4f\x81\x00\x81\x80\xd1\x00\xa9\x40"
+    "\x01\x01\x01\x01\x01\x01\x28\x3c\x80\xa0\x70\xb0\x23\x40\x30\x20"
+    "\x36\x00\x81\x91\x21\x00\x00\x1a\x00\x00\x00\xff\x00\x50\x48\x35"
+    "\x4e\x59\x31\x33\x4e\x32\x30\x30\x4c\x0a\x00\x00\x00\xfc\x00\x44"
+    "\x45\x4c\x4c\x20\x55\x33\x30\x31\x31\x0a\x20\x20\x00\x00\x00\xfd"
+    "\x00\x31\x56\x1d\x5e\x12\x00\x0a\x20\x20\x20\x20\x20\x20\x01\x38"
+    "\x02\x03\x29\xf1\x50\x90\x05\x04\x03\x02\x07\x16\x01\x06\x11\x12"
+    "\x15\x13\x14\x1f\x20\x23\x0d\x7f\x07\x83\x0f\x00\x00\x67\x03\x0c"
+    "\x00\x10\x00\x38\x2d\xe3\x05\x03\x01\x02\x3a\x80\x18\x71\x38\x2d"
+    "\x40\x58\x2c\x45\x00\x81\x91\x21\x00\x00\x1e\x01\x1d\x80\x18\x71"
+    "\x1c\x16\x20\x58\x2c\x25\x00\x81\x91\x21\x00\x00\x9e\x01\x1d\x00"
+    "\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\x81\x91\x21\x00\x00\x1e\x8c"
+    "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x81\x91\x21\x00\x00"
+    "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94";
+
+const unsigned char kLP2565A[] =
+    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
+    "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
+    "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00"
+    "\xA9\x40\xB3\x00\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
+    "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
+    "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
+    "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
+    "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4";
+
+const unsigned char kLP2565B[] =
+    "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01"
+    "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
+    "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F"
+    "\xB3\x00\xD1\xC0\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
+    "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
+    "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
+    "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
+    "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45";
+
+}  // namespace
+
+TEST(EDIDParserTest, ParseOverscanFlag) {
+  bool flag = false;
+  std::vector<uint8_t> edid(
+      kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay));
+  EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag));
+
+  flag = false;
+  edid.assign(kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay));
+  EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag));
+
+  flag = false;
+  edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay));
+  EXPECT_TRUE(ParseOutputOverscanFlag(edid, &flag));
+  EXPECT_TRUE(flag);
+
+  flag = false;
+  edid.assign(
+      kMisdetectedDisplay, kMisdetectedDisplay + charsize(kMisdetectedDisplay));
+  EXPECT_FALSE(ParseOutputOverscanFlag(edid, &flag));
+
+  flag = false;
+  // Copy |kOverscanDisplay| and set flags to false in it. The overscan flags
+  // are embedded at byte 150 in this specific example. Fix here too when the
+  // contents of kOverscanDisplay is altered.
+  edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay));
+  edid[150] = '\0';
+  EXPECT_TRUE(ParseOutputOverscanFlag(edid, &flag));
+  EXPECT_FALSE(flag);
+}
+
+TEST(EDIDParserTest, ParseBrokenOverscanData) {
+  // Do not fill valid data here because it anyway fails to parse the data.
+  std::vector<uint8_t> data;
+  bool flag = false;
+  EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag));
+  data.assign(126, '\0');
+  EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag));
+
+  // extending data because ParseOutputOverscanFlag() will access the data.
+  data.assign(128, '\0');
+  // The number of CEA extensions is stored at byte 126.
+  data[126] = '\x01';
+  EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag));
+
+  data.assign(150, '\0');
+  data[126] = '\x01';
+  EXPECT_FALSE(ParseOutputOverscanFlag(data, &flag));
+}
+
+TEST(EDIDParserTest, ParseEDID) {
+  uint16_t manufacturer_id = 0;
+  std::string human_readable_name;
+  std::vector<uint8_t> edid(
+      kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay));
+  EXPECT_TRUE(ParseOutputDeviceData(
+      edid, &manufacturer_id, &human_readable_name));
+  EXPECT_EQ(0x22f0u, manufacturer_id);
+  EXPECT_EQ("HP ZR30w", human_readable_name);
+
+  manufacturer_id = 0;
+  human_readable_name.clear();
+  edid.assign(kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay));
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, NULL));
+  EXPECT_EQ(0x4ca3u, manufacturer_id);
+  EXPECT_EQ("", human_readable_name);
+
+  // Internal display doesn't have name.
+  EXPECT_TRUE(ParseOutputDeviceData(edid, NULL, &human_readable_name));
+  EXPECT_TRUE(human_readable_name.empty());
+
+  manufacturer_id = 0;
+  human_readable_name.clear();
+  edid.assign(kOverscanDisplay, kOverscanDisplay + charsize(kOverscanDisplay));
+  EXPECT_TRUE(ParseOutputDeviceData(
+      edid, &manufacturer_id, &human_readable_name));
+  EXPECT_EQ(0x4c2du, manufacturer_id);
+  EXPECT_EQ("SAMSUNG", human_readable_name);
+}
+
+TEST(EDIDParserTest, ParseBrokenEDID) {
+  uint16_t manufacturer_id = 0;
+  std::string human_readable_name;
+  std::vector<uint8_t> edid;
+
+  // length == 0
+  EXPECT_FALSE(ParseOutputDeviceData(
+      edid, &manufacturer_id, &human_readable_name));
+
+  // name is broken. Copying kNormalDisplay and substitute its name data by
+  // some control code.
+  edid.assign(kNormalDisplay, kNormalDisplay + charsize(kNormalDisplay));
+
+  // display's name data is embedded in byte 95-107 in this specific example.
+  // Fix here too when the contents of kNormalDisplay is altered.
+  edid[97] = '\x1b';
+  EXPECT_FALSE(ParseOutputDeviceData(
+      edid, &manufacturer_id, &human_readable_name));
+
+  // If |human_readable_name| isn't specified, it skips parsing the name.
+  manufacturer_id = 0;
+  EXPECT_TRUE(ParseOutputDeviceData(edid, &manufacturer_id, NULL));
+  EXPECT_EQ(0x22f0u, manufacturer_id);
+}
+
+TEST(EDIDParserTest, GetDisplayId) {
+  // EDID of kLP2565A and B are slightly different but actually the same device.
+  int64_t id1 = -1;
+  int64_t id2 = -1;
+  std::vector<uint8_t> edid(kLP2565A, kLP2565A + charsize(kLP2565A));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id1));
+  edid.assign(kLP2565B, kLP2565B + charsize(kLP2565B));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id2));
+  EXPECT_EQ(id1, id2);
+  EXPECT_NE(-1, id1);
+}
+
+TEST(EDIDParserTest, GetDisplayIdFromInternal) {
+  int64_t id = -1;
+  std::vector<uint8_t> edid(
+      kInternalDisplay, kInternalDisplay + charsize(kInternalDisplay));
+  EXPECT_TRUE(GetDisplayIdFromEDID(edid, 0, &id));
+  EXPECT_NE(-1, id);
+}
+
+TEST(EDIDParserTest, GetDisplayIdFailure) {
+  int64_t id = -1;
+  std::vector<uint8_t> edid;
+  EXPECT_FALSE(GetDisplayIdFromEDID(edid, 0, &id));
+  EXPECT_EQ(-1, id);
+}
+
+}   // namespace ui
diff --git a/ui/display/util/x11/DEPS b/ui/display/util/x11/DEPS
new file mode 100644
index 0000000..25c2d71
--- /dev/null
+++ b/ui/display/util/x11/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/gfx/x",
+]
diff --git a/ui/display/util/x11/edid_parser_x11.cc b/ui/display/util/x11/edid_parser_x11.cc
new file mode 100644
index 0000000..407639b
--- /dev/null
+++ b/ui/display/util/x11/edid_parser_x11.cc
@@ -0,0 +1,122 @@
+// 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/display/util/x11/edid_parser_x11.h"
+
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+
+#include "base/strings/string_util.h"
+#include "ui/display/util/edid_parser.h"
+#include "ui/gfx/x/x11_types.h"
+
+namespace ui {
+
+namespace {
+
+bool IsRandRAvailable() {
+  int randr_version_major = 0;
+  int randr_version_minor = 0;
+  static bool is_randr_available = XRRQueryVersion(
+      gfx::GetXDisplay(), &randr_version_major, &randr_version_minor);
+  return is_randr_available;
+}
+
+// Get the EDID data from the |output| and stores to |edid|.
+// Returns true if EDID property is successfully obtained. Otherwise returns
+// false and does not touch |edid|.
+bool GetEDIDProperty(XID output, std::vector<uint8_t>* edid) {
+  if (!IsRandRAvailable())
+    return false;
+
+  Display* display = gfx::GetXDisplay();
+
+  static Atom edid_property = XInternAtom(
+      gfx::GetXDisplay(),
+      RR_PROPERTY_RANDR_EDID, false);
+
+  bool has_edid_property = false;
+  int num_properties = 0;
+  Atom* properties = XRRListOutputProperties(display, output, &num_properties);
+  for (int i = 0; i < num_properties; ++i) {
+    if (properties[i] == edid_property) {
+      has_edid_property = true;
+      break;
+    }
+  }
+  XFree(properties);
+  if (!has_edid_property)
+    return false;
+
+  Atom actual_type;
+  int actual_format;
+  unsigned long bytes_after;
+  unsigned long nitems = 0;
+  unsigned char* prop = NULL;
+  XRRGetOutputProperty(display,
+                       output,
+                       edid_property,
+                       0,                // offset
+                       128,              // length
+                       false,            // _delete
+                       false,            // pending
+                       AnyPropertyType,  // req_type
+                       &actual_type,
+                       &actual_format,
+                       &nitems,
+                       &bytes_after,
+                       &prop);
+  DCHECK_EQ(XA_INTEGER, actual_type);
+  DCHECK_EQ(8, actual_format);
+  edid->assign(prop, prop + nitems);
+  XFree(prop);
+  return true;
+}
+
+// Gets some useful data from the specified output device, such like
+// manufacturer's ID, product code, and human readable name. Returns false if it
+// fails to get those data and doesn't touch manufacturer ID/product code/name.
+// NULL can be passed for unwanted output parameters.
+bool GetOutputDeviceData(XID output,
+                         uint16_t* manufacturer_id,
+                         std::string* human_readable_name) {
+  std::vector<uint8_t> edid;
+  if (!GetEDIDProperty(output, &edid))
+    return false;
+
+  bool result = ParseOutputDeviceData(
+      edid, manufacturer_id, human_readable_name);
+  return result;
+}
+
+}  // namespace
+
+bool GetDisplayId(XID output_id,
+                  uint8_t output_index,
+                  int64_t* display_id_out) {
+  std::vector<uint8_t> edid;
+  if (!GetEDIDProperty(output_id, &edid))
+    return false;
+
+  bool result = GetDisplayIdFromEDID(edid, output_index, display_id_out);
+  return result;
+}
+
+std::string GetDisplayName(RROutput output) {
+  std::string display_name;
+  GetOutputDeviceData(output, NULL, &display_name);
+  return display_name;
+}
+
+bool GetOutputOverscanFlag(RROutput output, bool* flag) {
+  std::vector<uint8_t> edid;
+  if (!GetEDIDProperty(output, &edid))
+    return false;
+
+  bool found = ParseOutputOverscanFlag(edid, flag);
+  return found;
+}
+
+}  // namespace ui
diff --git a/ui/display/util/x11/edid_parser_x11.h b/ui/display/util/x11/edid_parser_x11.h
new file mode 100644
index 0000000..55048e6
--- /dev/null
+++ b/ui/display/util/x11/edid_parser_x11.h
@@ -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.
+
+#ifndef UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_
+#define UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "ui/display/types/display_constants.h"
+#include "ui/display/util/display_util_export.h"
+
+typedef unsigned long XID;
+typedef XID RROutput;
+
+// Xrandr utility functions to help get EDID information.
+
+namespace ui {
+
+// Gets the EDID data from |output| and generates the display id through
+// |GetDisplayIdFromEDID|.
+DISPLAY_UTIL_EXPORT bool GetDisplayId(XID output,
+                                      uint8_t index,
+                                      int64_t* display_id_out);
+
+// Generate the human readable string from EDID obtained from |output|.
+DISPLAY_UTIL_EXPORT std::string GetDisplayName(RROutput output);
+
+// Gets the overscan flag from |output| and stores to |flag|. Returns true if
+// the flag is found. Otherwise returns false and doesn't touch |flag|. The
+// output will produce overscan if |flag| is set to true, but the output may
+// still produce overscan even though it returns true and |flag| is set to
+// false.
+DISPLAY_UTIL_EXPORT bool GetOutputOverscanFlag(RROutput output, bool* flag);
+
+}  // namespace ui
+
+#endif  // UI_DISPLAY_UTIL_X11_EDID_PARSER_X11_H_