diff --git a/core/modules/layout/layouts/static/one-col/one-col.yml b/core/modules/layout/layouts/static/one-col/one-col.yml
index deaa7f8..ea550ca 100644
--- a/core/modules/layout/layouts/static/one-col/one-col.yml
+++ b/core/modules/layout/layouts/static/one-col/one-col.yml
@@ -2,4 +2,6 @@ title: One column
 category: Columns: 1
 template: one-col
 regions:
-  content: 'Content'
+  content:
+    label: Middle column
+    type: content
\ No newline at end of file
diff --git a/core/modules/layout/layouts/static/twocol/two-col.yml b/core/modules/layout/layouts/static/twocol/two-col.yml
index 9df31fb..e6c435e 100644
--- a/core/modules/layout/layouts/static/twocol/two-col.yml
+++ b/core/modules/layout/layouts/static/twocol/two-col.yml
@@ -4,5 +4,9 @@ template: two-col
 stylesheets:
   - two-col.css
 regions:
-  first: 'First column'
-  second: 'Second column'
+  first:
+    label: Left side
+    type: content
+  second:
+    label: Right side
+    type: aside
diff --git a/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php
new file mode 100644
index 0000000..125c34e
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/BoundDisplayInterface.php
@@ -0,0 +1,88 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\BoundDisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface for a Display object that is coupled to a specific layout.
+ *
+ * Bound displays contains references both to block instances and a specific
+ * layout, and the blocks are assigned to specific regions in that layout. Bound
+ * displays are used to serve real pages at request time.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+interface BoundDisplayInterface extends DisplayInterface {
+
+  /**
+   * Sets the layout to be used by this display.
+   *
+   * @param string $layout_id
+   *   The id of the desired layout.
+   */
+  public function setLayout($layout_id);
+
+  /**
+   * Returns the blocks in the requested region, ordered by weight.
+   *
+   * @param string $region
+   *   The region from which to return the set of blocks.
+   *
+   * @return array
+   *   The list of blocks, ordered by their weight within this display. Each
+   *   value in the list is the configuration object name of the block.
+   */
+  public function getSortedBlocksByRegion($region);
+
+  /**
+   * Returns this display's blocks, organized by region and ordered by weight.
+   *
+   * @return array
+   *   An array keyed by region name. For each region, the value is the same as
+   *   what is returned by getSortedBlocksByRegion().
+   *
+   * @see getSortedBlocksByRegion()
+   */
+  public function getAllSortedBlocks();
+
+  /**
+   * Returns the instantiated layout object to be used by this display.
+   *
+   * @return \Drupal\layout\Plugin\LayoutInterface
+   */
+  public function getLayoutInstance();
+
+  /**
+   * Adjusts this display's block placement to work with the provided layout.
+   *
+   * Essentially a shortcut that calls DisplayInterface::mapBlocksToLayout(),
+   * saves the result in the appropriate object property, and finally calls
+   * BoundDisplayInterface::setLayout().
+   *
+   * @param \Drupal\layout\Plugin\LayoutInterface $layout
+   *   The new layout to which blocks should be remapped.
+   *
+   * @see \Drupal\layout\Config\DisplayInterface::mapBlocksToLayout()
+   */
+  public function remapToLayout(LayoutInterface $layout);
+
+  /**
+   * Returns an entity with the non-layout-specific configuration of this one.
+   *
+   * @param string $id
+   *   The entity id to assign to the newly created entity.
+   *
+   * @param string $entity_type
+   *   The type of entity to create. The PHP class for this entity type must
+   *   implement \Drupal\layout\Config\UnboundDisplayInterface.
+   *
+   * @return \Drupal\layout\Config\UnboundDisplayInterface
+   *   The newly-created unbound display.
+   */
+  public function generateUnboundDisplay($id, $entity_type = 'unbound_display');
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php b/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php
new file mode 100644
index 0000000..6322e22
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/DisplayBase.php
@@ -0,0 +1,134 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Config\DisplayBase.
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Base class for 'display' and 'unbound_display' configuration entities.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+abstract class DisplayBase extends ConfigEntityBase implements DisplayInterface {
+
+  /**
+   * The ID (config name) identifying a specific display object.
+   *
+   * @var string
+   */
+  public $id;
+
+  /**
+   * The UUID identifying a specific display object.
+   *
+   * @var string
+   */
+  public $uuid;
+
+  /**
+   * Contains all block configuration.
+   *
+   * There are two levels to the configuration contained herein: display-level
+   * block configuration, and then block instance configuration.
+   *
+   * Block instance configuration is stored in a separate config object. This
+   * array is keyed by the config name that uniquely identifies each block
+   * instance. At runtime, various object methods will retrieve this additional
+   * config and return it to calling code.
+   *
+   * Display-level block configuration is data that determines the behavior of
+   * a block *in this display*. The most important examples of this are the
+   * region to which the block is assigned, and its weighting in that region.
+   *
+   * @code
+   *    array(
+   *      'block1-configkey' => array(
+   *        'region' => 'content',
+   *        // store the region type name here so that we can do type conversion w/out
+   *        // needing to have access to the original layout plugin
+   *        'region-type' => 'content',
+   *        // increment by 100 so there is ALWAYS plenty of space for manual insertion
+   *        'weight' => -100,
+   *      ),
+   *      'block2-configkey' => array(
+   *        'region' => 'sidebar_first',
+   *        'region-type' => 'aside',
+   *        'weight' => -100,
+   *      ),
+   *      'block2-configkey' => array(
+   *        'region' => 'sidebar_first',
+   *        'region-type' => 'aside',
+   *        'weight' => 0,
+   *      ),
+   *      'maincontent' => array(
+   *        'region' => 'content',
+   *        'region-type' => 'content',
+   *        'weight' => -200,
+   *      ),
+   *    );
+   * @endcode
+   *
+   * @var array
+   */
+  protected $blockInfo = array();
+
+  /**
+   * Implements DisplayInterface::getAllBlockInfo().
+   */
+  public function getAllBlockInfo() {
+    return $this->blockInfo;
+  }
+
+  /**
+   * Implements DisplayInterface::mapBlocksToLayout().
+   *
+   * @todo Decouple this implementation from this class, so that it could be
+   *   more easily customized.
+   */
+  public function mapBlocksToLayout(LayoutInterface $layout) {
+    $types = array();
+
+    $layout_regions = $layout->getRegions();
+    $layout_regions_indexed = array_keys($layout_regions);
+    foreach ($layout_regions as $name => $info) {
+      $types[$info['type']][] = $name;
+    }
+
+    $remapped_config = array();
+    foreach ($this->blockInfo as $name => $info) {
+      // First, if there's a direct region name match, use that.
+      if (!empty($info['region']) && isset($layout_regions[$info['region']])) {
+        // No need to do anything.
+      }
+      // Then, try to remap using region types.
+      else if (!empty($types[$info['region-type']])) {
+        $info['region'] = reset($types[$info['region-type']]);
+      }
+      // Finally, fall back to dumping everything in the layout's first region.
+      else {
+        $info['region'] = reset($layout_regions_indexed);
+      }
+
+      $remapped_config[$name] = $info;
+    }
+
+    return $remapped_config;
+  }
+
+  /**
+   * Implements DisplayInterface::getAllRegionTypes().
+   */
+  public function getAllRegionTypes() {
+    $types = array();
+    foreach ($this->blockInfo as $info) {
+      $types[$info['region-type']] = TRUE;
+    }
+    return array_keys($types);
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php
new file mode 100644
index 0000000..871aadc
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/DisplayInterface.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\DisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface describing a Display configuration object.
+ *
+ * Displays are configuration that describe the placement of block instances
+ * in regions. Drupal includes two types of Display objects:
+ * - Bound displays include a reference to a specific layout, and each block is
+ *   specified to display in a specific region of that layout. Bound displays
+ *   are used to serve real pages at request time.
+ * - Unbound displays do not include a reference to any layout, and each block
+ *   is assigned a region type, but not a specific region. Developers including
+ *   default displays with their modules or distributions are encouraged to use
+ *   unbound displays in order to minimize dependencies on specific layouts and
+ *   allow site-specific configuration to dictate the layout.
+ *
+ * This interface defines what is common to all displays, whether bound or
+ * unbound.
+ *
+ * @see \Drupal\layout\Config\BoundDisplayInterface
+ * @see \Drupal\layout\Config\UnboundDisplayInterface
+ */
+interface DisplayInterface {
+
+  /**
+   * Returns the display-specific configuration of all blocks in this display.
+   *
+   * For each block that exists in Drupal (e.g., the "Who's Online" block),
+   * multiple "configured instances" can be created (e.g., a "Who's been online
+   * in the last 5 minutes" instance and a "Who's been online in the last 60
+   * minutes" instance). Each configured instance can be referenced by multiple
+   * displays (e.g., by a "regular" page, by an administrative page, and within
+   * one or more dashboards). This function returns the block instances that
+   * have been added to this display. Each key of the returned array is the
+   * block instance's configuration object name, and config() may be called on
+   * it in order to retrieve the full configuration that is shared across all
+   * displays. For each key, the value is an array of display-specific
+   * configuration, primarily the 'region' and 'weight', and anything else that
+   * affects the placement of the block within the layout rather than only the
+   * contents of the block.
+   *
+   * @return array
+   *   An array keyed on each block's configuration object name. Each value is
+   *   an array of information that determines the placement of the block within
+   *   a layout, including:
+   *   - region: The region in which to display the block (for bound displays
+   *     only).
+   *   - region-type: The type of region that is most appropriate for the block.
+   *     Usually one of 'header', 'footer', 'nav', 'content', 'aside', or
+   *     'system', though custom region types are also allowed. This is
+   *     primarily specified by unbound displays, where specifying a specific
+   *     region name is impossible, because different layouts come with
+   *     different regions.
+   *   - weight: Within a region, blocks are rendered from low to high weight.
+   */
+  public function getAllBlockInfo();
+
+  /**
+   * Maps the contained block info to the provided layout.
+   *
+   * @param \Drupal\layout\Plugin\LayoutInterface $layout
+   *
+   * @return array
+   *   An array containing block configuration info, identical to that which
+   *   is returned by DisplayInterface::getAllBlockInfo().
+   */
+  public function mapBlocksToLayout(LayoutInterface $layout);
+
+  /**
+   * Returns the names of all region types to which blocks are assigned.
+   *
+   * @return array
+   *   An indexed array of unique region type names, or an empty array if no
+   *   region types were assigned.
+   */
+  public function getAllRegionTypes();
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php
new file mode 100644
index 0000000..34ba2f8
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Config/UnboundDisplayInterface.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * @file
+ * Definition of Drupal\layout\Config\UnboundDisplayInterface
+ */
+
+namespace Drupal\layout\Config;
+
+use Drupal\layout\Plugin\LayoutInterface;
+
+/**
+ * Interface for a Display that is not coupled with any layout.
+ *
+ * Unbound displays contain references to blocks, but not to any particular
+ * layout. Their primary use case is to express a set of relative block
+ * placements without necessitating any particular layout be present. This
+ * allows upstream (module and distribution) developers to express a visual
+ * composition of blocks without knowing anything about the layouts a
+ * particular site has available.
+ *
+ * @see \Drupal\layout\Config\DisplayInterface
+ */
+interface UnboundDisplayInterface extends DisplayInterface {
+
+  /**
+   * Returns a bound display entity by binding a layout to this unbound display.
+   *
+   * This will DisplayInterface::mapBlocksToLayout() using the provided layout,
+   * then create and return a new Display object with the output. This is just
+   * a factory - calling code is responsible for saving the returned object.
+   *
+   * @param \Drupal\layout\Plugin\LayoutInterface $layout
+   *   The desired layout.
+   *
+   * @param string $id
+   *   The entity id to assign to the newly created entity.
+   *
+   * @param string $entity_type
+   *   The type of entity to create. The PHP class for this entity type must
+   *   implement \Drupal\layout\Config\BoundDisplayInterface.
+   *
+   * @return \Drupal\layout\Config\BoundDisplayInterface
+   *   The newly created entity.
+   */
+  public function generateDisplay(LayoutInterface $layout, $id, $entity_type = 'display');
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
new file mode 100644
index 0000000..a4270e1
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
@@ -0,0 +1,186 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Core\Entity\Display.
+ */
+
+namespace Drupal\layout\Plugin\Core\Entity;
+
+use Drupal\layout\Config\DisplayBase;
+use Drupal\layout\Config\BoundDisplayInterface;
+use Drupal\layout\Config\UnboundDisplayInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the display entity.
+ *
+ * @Plugin(
+ *   id = "display",
+ *   label = @Translation("Display"),
+ *   module = "layout",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "display.bound",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class Display extends DisplayBase implements BoundDisplayInterface {
+
+  /**
+   * A two-level array expressing block ordering within regions.
+   *
+   * The outer array is associative, keyed on region name. Each inner array is
+   * indexed, with the config address of a block as values and sorted according
+   * to order in which those blocks should appear in that region.
+   *
+   * This property is not stored statically in config, but is derived at runtime
+   * by DisplayBase::sortBlocks(). It is not stored statically because that
+   * would make using weights for ordering more difficult, and weights make
+   * external mass manipulation of displays much easier.
+   *
+   * @var array
+   */
+  protected $blocksInRegions;
+
+  /**
+   * The layout instance being used to serve this page.
+   *
+   * @var \Drupal\layout\Plugin\LayoutInterface
+   */
+  protected $layoutInstance;
+
+  /**
+   * The name of the layout plugin to use.
+   *
+   * @var string
+   */
+  public $layout;
+
+  /**
+   * The settings with which to instantiate the layout plugin.
+   *
+   * @var array
+   */
+  public $layoutSettings = array();
+
+  /**
+   * Implements BoundDisplayInterface::getSortedBlocksByRegion().
+   *
+   * @throws \Exception
+   */
+  public function getSortedBlocksByRegion($region) {
+    if ($this->blocksInRegions === NULL) {
+      $this->sortBlocks();
+    }
+
+    if (!isset($this->blocksInRegions[$region])) {
+      throw new \Exception(sprintf("Region %region does not exist in layout %layout", array('%region' => $region, '%layout' => $this->getLayoutInstance()->name)), E_RECOVERABLE_ERROR);
+    }
+
+    return $this->blocksInRegions[$region];
+  }
+
+  /**
+   * Implements BoundDisplayInterface::getAllSortedBlocks().
+   */
+  public function getAllSortedBlocks() {
+    if ($this->blocksInRegions === NULL) {
+      $this->sortBlocks();
+    }
+
+    return $this->blocksInRegions;
+  }
+
+  /**
+   * Transform the stored blockConfig into a sorted, region-oriented array.
+   */
+  protected function sortBlocks() {
+    $layout_instance = $this->getLayoutInstance();
+    if ($this->layout !== $layout_instance->getPluginId()) {
+      $block_config = $this->mapBlocksToLayout($layout_instance);
+    }
+    else {
+      $block_config = $this->blockInfo;
+    }
+
+    $this->blocksInRegions = array();
+
+    $regions = array_fill_keys(array_keys($layout_instance->getRegions()), array());
+    foreach ($block_config as $config_name => $info) {
+      $regions[$info['region']][$config_name] = $info;
+    }
+
+    foreach ($regions as $region_name => &$blocks) {
+      uasort($blocks, 'drupal_sort_weight');
+      $this->blocksInRegions[$region_name] = array_keys($blocks);
+    }
+  }
+
+  /**
+   * Implements BoundDisplayInterface::remapToLayout().
+   */
+  public function remapToLayout(LayoutInterface $layout) {
+    $this->blockInfo = $this->mapBlocksToLayout($layout);
+    $this->setLayout($layout->getPluginId());
+  }
+
+  /**
+   * Set the contained layout plugin.
+   *
+   * @param string $plugin_id
+   *   The plugin id of the desired layout plugin.
+   */
+  public function setLayout($plugin_id) {
+    // @todo verification?
+    $this->layout = $plugin_id;
+    $this->layoutInstance = NULL;
+    $this->blocksInRegions = NULL;
+  }
+
+  /**
+   * Implements BoundDisplayInterface::generateUnboundDisplay().
+   *
+   * @throws \Exception
+   */
+  public function generateUnboundDisplay($id, $entity_type = 'unbound_display') {
+    $block_info = $this->getAllBlockInfo();
+    foreach ($block_info as &$info) {
+      unset ($info['region']);
+    }
+
+    $values = array(
+      'blockInfo' => $block_info,
+      'id' => $id,
+    );
+
+    $entity = entity_create($entity_type, $values);
+    if (!$entity instanceof UnboundDisplayInterface) {
+      throw new \Exception(sprintf('Attempted to create an unbound display using an invalid entity type.'), E_RECOVERABLE_ERROR);
+    }
+
+    return $entity;
+  }
+
+  /**
+   * Returns the instantiated layout object.
+   *
+   * @throws \Exception
+   */
+  public function getLayoutInstance() {
+    if ($this->layoutInstance === NULL) {
+      if (empty($this->layout)) {
+        throw new \Exception(sprintf('Display "%id" had no layout plugin attached.', array('%id' => $this->id())), E_RECOVERABLE_ERROR);
+      }
+
+      $this->layoutInstance = layout_manager()->createInstance($this->layout, $this->layoutSettings);
+      // @todo add handling for remapping if the layout could not be found
+    }
+
+    return $this->layoutInstance;
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
new file mode 100644
index 0000000..5450e2c
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\layout\Plugin\Core\Entity\Display.
+ */
+
+namespace Drupal\layout\Plugin\Core\Entity;
+
+use Drupal\layout\Config\DisplayBase;
+use Drupal\layout\Config\BoundDisplayInterface;
+use Drupal\layout\Config\UnboundDisplayInterface;
+use Drupal\layout\Plugin\LayoutInterface;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the unbound_display entity.
+ *
+ * Unbound displays contain blocks that are not 'bound' to a specific layout,
+ * and their contained blocks are mapped only to region types, not regions.
+ *
+ * @Plugin(
+ *   id = "unbound_display",
+ *   label = @Translation("Unbound Display"),
+ *   module = "layout",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "display.unbound",
+ *   entity_keys = {
+ *     "id" = "id",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class UnboundDisplay extends DisplayBase implements UnboundDisplayInterface {
+
+  /**
+   * Implements UnboundDisplayInterface::generateDisplay().
+   *
+   * @throws \Exception
+   */
+  public function generateDisplay(LayoutInterface $layout, $id, $entity_type = 'display') {
+    $values = array(
+      'layout' => $layout->getPluginId(),
+      'blockInfo' => $this->mapBlocksToLayout($layout),
+      'id' => $id,
+    );
+
+    $entity = entity_create($entity_type, $values);
+
+    if (!$entity instanceof BoundDisplayInterface) {
+      throw new \Exception(sprintf('Attempted to bind an unbound display but provided an invalid entity type.'), E_RECOVERABLE_ERROR);
+    }
+
+    return $entity;
+  }
+}
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
index 5b874f6..7521c57 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/LayoutInterface.php
@@ -7,10 +7,12 @@
 
 namespace Drupal\layout\Plugin;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
+
 /**
  * Defines the shared interface for all layout plugins.
  */
-interface LayoutInterface {
+interface LayoutInterface extends PluginInspectionInterface {
 
   /**
    * Returns a list of regions.
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
index 4819595..3ded379 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/layout/layout/StaticLayout.php
@@ -88,11 +88,11 @@ public function renderLayout($admin = FALSE) {
     );
 
     // Render all regions needed for this layout.
-    foreach ($this->getRegions() as $region => $title) {
+    foreach ($this->getRegions() as $region => $info) {
       // @todo This is just stub code to fill in regions with stuff for now.
       // When blocks are related to layouts and not themes, we can make this
       // really be filled in with blocks.
-      $build['#content'][$region] = '<h3>' . $title . '</h3>';
+      $build['#content'][$region] = '<h3>' . $info['label'] . '</h3>';
     }
 
     // Fill in attached CSS and JS files based on metadata.
diff --git a/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php b/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php
new file mode 100644
index 0000000..07a77bf
--- /dev/null
+++ b/core/modules/layout/lib/Drupal/layout/Tests/DisplayInternalLogicTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\layout\Tests\DisplayInternalLogicTest.
+ */
+
+namespace Drupal\layout\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\layout\Plugin\Core\Entity\Display;
+use Drupal\layout\Plugin\Core\Entity\UnboundDisplay;
+
+/**
+ * Tests the API and internal logic offered by Displays.
+ */
+class DisplayInternalLogicTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('layout', 'layout_test');
+
+  /**
+   * The twocol test display.
+   *
+   * @var \Drupal\layout\Plugin\Core\Entity\Display
+   */
+  public $twocol;
+
+  /**
+   * The onecol test display.
+   *
+   * @var \Drupal\layout\Plugin\Core\Entity\Display
+   */
+  public $onecol;
+
+  /**
+   * The unbound test display.
+   *
+   * @var \Drupal\layout\Plugin\Core\Entity\UnboundDisplay
+   */
+  public $unbound;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Display behaviors',
+      'description' => 'Tests internal behaviors of DisplayInterface implementations, such as layout remapping.',
+      'group' => 'Display',
+    );
+  }
+
+  public function setUp() {
+    parent::setUp();
+    $this->twocol = entity_load('display', 'test_twocol');
+    $this->onecol = entity_load('display', 'test_onecol');
+    $this->unbound = entity_load('unbound_display', 'test_unbound_display');
+  }
+
+  /**
+   * Tests block sorting within regions.
+   */
+  public function testBlockSorting() {
+    $expected = array(
+      'left' => array('block.test_block_3', 'block.test_block_1'),
+      'right' => array('block.test_block_2'),
+    );
+    $this->assertIdentical($this->twocol->getSortedBlocksByRegion('left'), $expected['left']);
+    $this->assertIdentical($this->twocol->getSortedBlocksByRegion('right'), $expected['right']);
+    $this->assertIdentical($this->twocol->getAllSortedBlocks(), $expected);
+  }
+
+  /**
+   * Test the various block remapping scenarios allowed for by the assorted
+   * Display types.
+   *
+   * This includes remapping a Display's blocks to a new layout, binding an
+   * UnboundDisplay with a layout to generate a new Display, and releasing a
+   * Display from its layout binding to generate an UnboundDisplay.
+   */
+  public function testBlockMapping() {
+    // Remap from twocol to onecol. All blocks are expected to move to the one
+    // and only region and be sorted by their original weights.
+    $expected = array(
+      'middle' => array('block.test_block_3', 'block.test_block_2', 'block.test_block_1'),
+    );
+    $two_to_one = clone($this->twocol);
+    $two_to_one->remapToLayout($this->onecol->getLayoutInstance());
+    $this->assertIdentical($two_to_one->getAllSortedBlocks(), $expected);
+
+    // Remap from onecol to twocol. Since the blocks are assigned the 'content'
+    // region type, and twocol's 'left' region has that type, the blocks are
+    // expected to move to there and be sorted by their original weights.
+    $expected = array(
+      'left' => array('block.test_block_2', 'block.test_block_1'),
+      'right' => array(),
+    );
+    $one_to_two = clone($this->onecol);
+    $one_to_two->remapToLayout($this->twocol->getLayoutInstance());
+    $this->assertIdentical($one_to_two->getAllSortedBlocks(), $expected);
+
+    // Bind the unbound display to the twocol layout:
+    // - Block 1 is assigned the 'content' region type, so is expected to be
+    //   mapped to the 'left' region, which has that type.
+    // - Block 2 is assigned the 'aside' region type, so is expected to be
+    //   mapped to the 'right' region, which has that type.
+    // - Block 3 is assigned the 'nav' region type, and there is no twocol
+    //   region with that type, so it is expected to be mapped to twocol's
+    //   first region, which is 'left'.
+    $expected = array(
+      'left' => array('block.test_block_1', 'block.test_block_3'),
+      'right' => array('block.test_block_2'),
+    );
+    $unbound_to_twocol = $this->unbound->generateDisplay($this->twocol->getLayoutInstance(), 'unbound_to_twocol');
+    $this->assertTrue($unbound_to_twocol instanceof Display, 'Binding the unbound display successfully created a Display object');
+    $this->assertIdentical($unbound_to_twocol->getAllSortedBlocks(), $expected);
+
+    // Generate an unbound display from the twocol display.
+    $expected = array(
+      'block.test_block_1' => array('region-type' => 'content', 'weight' => 100),
+      'block.test_block_2' => array('region-type' => 'aside', 'weight' => 0),
+      'block.test_block_3' => array('region-type' => 'content', 'weight' => -100),
+    );
+    $twocol_to_unbound = $this->twocol->generateUnboundDisplay('twocol_to_unbound');
+    $this->assertTrue($twocol_to_unbound instanceof UnboundDisplay, 'Unbinding the twocol display successfully created an UnboundDisplay object');
+    // We can use Equal instead of Identical, because for this, array order and
+    // integer vs. string data types do not matter.
+    $this->assertEqual($twocol_to_unbound->getAllBlockInfo(), $expected);
+  }
+}
diff --git a/core/modules/layout/tests/config/display.bound.test_onecol.yml b/core/modules/layout/tests/config/display.bound.test_onecol.yml
new file mode 100644
index 0000000..a156355
--- /dev/null
+++ b/core/modules/layout/tests/config/display.bound.test_onecol.yml
@@ -0,0 +1,13 @@
+id: test_onecol
+label: Onecol testing display
+layout: static_layout:layout_test__one-col
+layoutSettings: { }
+blockInfo:
+  block.test_block_1:
+    region: middle
+    region-type: content
+    weight: 100
+  block.test_block_2:
+    region: middle
+    region-type: content
+    weight: -100
diff --git a/core/modules/layout/tests/config/display.bound.test_twocol.yml b/core/modules/layout/tests/config/display.bound.test_twocol.yml
new file mode 100644
index 0000000..3c47e09
--- /dev/null
+++ b/core/modules/layout/tests/config/display.bound.test_twocol.yml
@@ -0,0 +1,17 @@
+id: test_twocol
+label: Twocol testing display
+layout: static_layout:layout_test_theme__two-col
+layoutSettings: { }
+blockInfo:
+  block.test_block_1:
+    region: left
+    region-type: content
+    weight: 100
+  block.test_block_2:
+    region: right
+    region-type: aside
+    weight: 0
+  block.test_block_3:
+    region: left
+    region-type: content
+    weight: -100
diff --git a/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml b/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml
new file mode 100644
index 0000000..8dfd4c8
--- /dev/null
+++ b/core/modules/layout/tests/config/display.unbound.test_unbound_display.yml
@@ -0,0 +1,13 @@
+id: test_unbound_display
+label: Unbound display test
+layoutSettings: { }
+blockInfo:
+  block.test_block_1:
+    region-type: content
+    weight: -100
+  block.test_block_2:
+    region-type: aside
+    weight: -100
+  block.test_block_3:
+    region-type: nav
+    weight: 0
diff --git a/core/modules/layout/tests/layout_test.module b/core/modules/layout/tests/layout_test.module
index 52ab4a3..b9eb63b 100644
--- a/core/modules/layout/tests/layout_test.module
+++ b/core/modules/layout/tests/layout_test.module
@@ -27,7 +27,10 @@ function layout_test_page() {
   global $theme;
   $theme = 'layout_test_theme';
   theme_enable(array($theme));
-  $layout = layout_manager()->createInstance('static_layout:layout_test_theme__two-col');
+  $display = entity_load('display', 'test_twocol');
+  $layout = $display->getLayoutInstance();
+  // @todo This tests that the layout can render its regions, but does not test
+  //   block rendering: http://drupal.org/node/1812720.
   return $layout->renderLayout();
 }
 
diff --git a/core/modules/layout/tests/layouts/static/one-col/one-col.yml b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
index 27d7d06..9048e14 100644
--- a/core/modules/layout/tests/layouts/static/one-col/one-col.yml
+++ b/core/modules/layout/tests/layouts/static/one-col/one-col.yml
@@ -2,4 +2,6 @@ title: Single column
 category: Columns: 1
 template: one-col
 regions:
-  middle: 'Middle column'
+  middle:
+    label: Middle column
+    type: content
diff --git a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
index 7ee126f..01b9e86 100644
--- a/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
+++ b/core/modules/layout/tests/themes/layout_test_theme/layouts/static/two-col/two-col.yml
@@ -4,5 +4,9 @@ template: two-col
 stylesheets:
   - two-col.css
 regions:
-  left: 'Left side'
-  right: 'Right side'
+  left:
+    label: Left side
+    type: content
+  right:
+    label: Right side
+    type: aside
\ No newline at end of file
