 core/misc/edit-active.png                          |    8 ++
 core/misc/edit.png                                 |    7 ++
 core/modules/contextual/contextual.js              |   50 +++++++--
 core/modules/contextual/contextual.module          |   52 +++++++++
 core/modules/contextual/contextual.theme.css       |   40 +++----
 core/modules/contextual/contextual.toolbar-rtl.css |   11 ++
 core/modules/contextual/contextual.toolbar.css     |   36 +++++++
 core/modules/contextual/contextual.toolbar.js      |  114 ++++++++++++++++++++
 core/modules/edit/css/edit.css                     |   50 ++-------
 core/modules/edit/edit.info                        |    1 +
 core/modules/edit/edit.module                      |   44 +++-----
 core/modules/edit/images/icon-edit-active.png      |    3 -
 core/modules/edit/images/icon-edit.png             |    5 -
 core/modules/edit/js/app.js                        |   54 ++++++----
 .../editingWidgets/drupalcontenteditablewidget.js  |    8 +-
 .../edit/js/createjs/editingWidgets/formwidget.js  |   14 +--
 core/modules/edit/js/edit.js                       |   28 ++---
 core/modules/edit/js/models/edit-app-model.js      |    3 +-
 core/modules/edit/js/routers/edit-router.js        |   59 ----------
 core/modules/edit/js/views/contextuallink-view.js  |   94 ++++++++++++++++
 core/modules/edit/js/views/menu-view.js            |   82 --------------
 core/modules/edit/js/views/modal-view.js           |   24 +----
 core/modules/edit/js/views/overlay-view.js         |   86 ---------------
 .../edit/js/views/propertyeditordecoration-view.js |   20 ++++
 core/modules/edit/js/views/toolbar-view.js         |   18 +++-
 .../Drupal/filter/Tests/FilterFormatAccessTest.php |    9 ++
 .../node/lib/Drupal/node/NodeRenderController.php  |    6 +-
 .../node/lib/Drupal/node/Tests/PageEditTest.php    |   16 +--
 core/modules/node/node.module                      |    3 +-
 core/modules/system/system.module                  |    4 +-
 .../lib/Drupal/taxonomy/Tests/TermTest.php         |    9 +-
 31 files changed, 521 insertions(+), 437 deletions(-)

diff --git a/core/misc/edit-active.png b/core/misc/edit-active.png
new file mode 100644
index 0000000..6dd158d
--- /dev/null
+++ b/core/misc/edit-active.png
@@ -0,0 +1,8 @@
+PNG
+
+   IHDR         Ĵl;   IDATxA0JL
+0	$ 	%l8q[)DD8U5'
+/rc^૵
+rthgzΨ~
+<vvvOߣ&5
+h ̇üyuA9A: *"IzX;2c@=P;vB    IENDB`
\ No newline at end of file
diff --git a/core/misc/edit.png b/core/misc/edit.png
new file mode 100644
index 0000000..eb93320
--- /dev/null
+++ b/core/misc/edit.png
@@ -0,0 +1,7 @@
+PNG
+
+   IHDR         Ĵl;  4IDATxڥщ@aKRQp	",){ %sav͜gAz$P؛xMp(
+{^(.v}ȡv(B:UU	P?ڣF t@a
+0ᜳ@ahkxZ	hKx\
+hGxX԰ G;\`HaPx4f	@`x:ސ}WÜ£pYC
+'L&$40hpJ!q@gx4ݐQޅwa]d!Ƶ    IENDB`
\ No newline at end of file
diff --git a/core/modules/contextual/contextual.js b/core/modules/contextual/contextual.js
index dee63e6..dc1f4a5 100644
--- a/core/modules/contextual/contextual.js
+++ b/core/modules/contextual/contextual.js
@@ -7,6 +7,8 @@
 
 "use strict";
 
+var contextuals = [];
+
 /**
  * Attaches outline behavior for regions associated with contextual links.
  */
@@ -14,7 +16,14 @@ Drupal.behaviors.contextual = {
   attach: function (context) {
     $('ul.contextual-links', context).once('contextual', function () {
       var $this = $(this);
-      $this.data('drupal-contextual', new Drupal.contextual($this, $this.closest('.contextual-region')));
+      var contextual = new Drupal.contextual($this, $this.closest('.contextual-region'));
+      contextuals.push(contextual);
+      $this.data('drupal-contextual', contextual);
+    });
+
+    // Bind to edit mode changes.
+    $('body').once('contextual', function () {
+      $(document).on('drupalEditModeChanged.contextual', toggleEditMode);
     });
   }
 };
@@ -54,15 +63,32 @@ Drupal.contextual.prototype.init = function() {
     .attr('aria-pressed', false)
     .prependTo(this.$wrapper);
 
+  // The trigger behaviors are never detached or mutated.
+  this.$region
+    .on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this))
+    .on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this));
+  // Attach highlight behaviors.
+  this.attachHighlightBehaviors();
+};
+
+/**
+ * Attaches highlight-on-mouseenter behaviors.
+ */
+Drupal.contextual.prototype.attachHighlightBehaviors = function () {
   // Bind behaviors through delegation.
   var highlightRegion = $.proxy(this.highlightRegion, this);
   this.$region
-    .on('click.contextual', '.contextual .trigger', $.proxy(this.triggerClickHandler, this))
-    .on('mouseenter.contextual', {highlight: true}, highlightRegion)
-    .on('mouseleave.contextual', {highlight: false}, highlightRegion)
-    .on('mouseleave.contextual', '.contextual', {show: false}, $.proxy(this.triggerLeaveHandler, this))
-    .on('focus.contextual', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion)
-    .on('blur.contextual', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion);
+    .on('mouseenter.contextual.highlight', {highlight: true}, highlightRegion)
+    .on('mouseleave.contextual.highlight', {highlight: false}, highlightRegion)
+    .on('focus.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: true}, highlightRegion)
+    .on('blur.contextual.highlight', '.contextual-links a, .contextual .trigger', {highlight: false}, highlightRegion);
+};
+
+/**
+ * Detaches unhighlight-on-mouseleave behaviors.
+ */
+Drupal.contextual.prototype.detachHighlightBehaviors = function () {
+  this.$region.off('.contextual.highlight');
 };
 
 /**
@@ -139,6 +165,16 @@ Drupal.contextual.prototype.showLinks = function(show) {
 };
 
 /**
+ * Shows or hides all pencil icons and corresponding contextual regions.
+ */
+function toggleEditMode (event, data) {
+  for (var i = contextuals.length - 1; i >= 0; i--) {
+    contextuals[i][(data.status) ? 'detachHighlightBehaviors' : 'attachHighlightBehaviors']();
+    contextuals[i].$region.toggleClass('contextual-region-active', data.status);
+  }
+}
+
+/**
  * Wraps contextual links.
  *
  * @return {String}
diff --git a/core/modules/contextual/contextual.module b/core/modules/contextual/contextual.module
index 30dcacb..7aca273 100644
--- a/core/modules/contextual/contextual.module
+++ b/core/modules/contextual/contextual.module
@@ -6,6 +6,41 @@
  */
 
 /**
+ * Implements hook_toolbar().
+ */
+function contextual_toolbar() {
+  if (!user_access('access contextual links')) {
+    return;
+  }
+
+  $tab['contextual'] = array(
+    '#type' => 'toolbar_item',
+    'tab' => array(
+      '#type' => 'html_tag',
+      '#tag' => 'button',
+      '#value' => t('Edit'),
+      '#attributes' => array(
+        'class' => array('icon', 'icon-edit'),
+        'role' => 'button',
+        'aria-pressed' => 'false',
+      ),
+      // TRICKY: toolbar_pre_render_item() expects this.
+      '#options' => array('attributes' => array()),
+    ),
+    '#wrapper_attributes' => array(
+      'class' => array('element-hidden', 'contextual-toolbar-tab'),
+    ),
+    '#attached' => array(
+      'library' => array(
+        array('contextual', 'drupal.contextual-toolbar'),
+      ),
+    ),
+  );
+
+  return $tab;
+}
+
+/**
  * Implements hook_help().
  */
 function contextual_help($path, $arg) {
@@ -57,6 +92,23 @@ function contextual_library_info() {
       array('system', 'jquery.once'),
     ),
   );
+  $libraries['drupal.contextual-toolbar'] = array(
+    'title' => 'Contextual Links Toolbar Tab',
+    'version' => VERSION,
+    'js' => array(
+      // Add the JavaScript, with a group and weight such that it will run
+      // before modules/overlay/overlay-parent.js.
+      $path . '/contextual.toolbar.js' => array('group' => JS_LIBRARY, 'weight' => -1),
+    ),
+    'css' => array(
+      $path . '/contextual.toolbar.css' => array(),
+    ),
+    'dependencies' => array(
+      array('system', 'jquery'),
+      array('system', 'jquery.once'),
+      array('system', 'backbone'),
+    ),
+  );
 
   return $libraries;
 }
diff --git a/core/modules/contextual/contextual.theme.css b/core/modules/contextual/contextual.theme.css
index aca019c..e84b232 100644
--- a/core/modules/contextual/contextual.theme.css
+++ b/core/modules/contextual/contextual.theme.css
@@ -10,39 +10,39 @@
   position: absolute;
   right: 0; /* LTR */
   top: 2px;
-  z-index: 999;
-}
-.contextual-region-active {
-  outline: 1px dashed #d6d6d6;
-  outline-offset: 1px;
+  z-index: 500;
 }
 
 /**
  * Contextual trigger.
  */
 .contextual .trigger {
-  background: transparent url("images/gear-select.png") no-repeat 2px 0;
-  border: 1px solid transparent;
-  border-radius: 4px 4px 0 0;
+  background-attachment: scroll;
+  background-color: #fff;
+  background-image: url("../../misc/edit.png");
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: 16px 16px;
+  border: 1px solid #ddd;
+  border-radius: 13px;
+  box-shadow: 1px 1px 2px rgba(0,0,0,0.3);
   /* Override the .element-focusable height: auto */
-  height: 18px !important;
+  height: 28px !important;
   float: right; /* LTR */
   margin: 0;
   overflow: hidden;
   padding: 0 2px;
   position: relative;
-  width: 34px;
+  right: 2px;
+  width: 28px;
   text-indent: -9999px;
   z-index: 2;
+  cursor: pointer;
 }
-.no-touch .contextual .trigger:hover,
 .contextual-links-active .trigger {
-  background-position: 2px -18px;
-}
-.contextual-links-active .trigger {
-  background-color: #fff;
-  border-bottom: none;
-  border-color: #d6d6d6;
+  border-bottom-color: transparent;
+  border-radius: 13px 13px 0 0;
+  box-shadow: none;
 }
 
 /**
@@ -52,7 +52,7 @@
  */
 .contextual-region .contextual .contextual-links {
   background-color: #fff;
-  border: 1px solid #d6d6d6;
+  border: 1px solid #ddd;
   border-radius: 4px 0 4px 4px; /* LTR */
   clear: both;
   float: right; /* LTR */
@@ -90,5 +90,7 @@
   text-decoration: none;
 }
 .no-touch .contextual-region .contextual .contextual-links li a:hover {
-  background-color: #bfdcee;
+  color: white;
+  background-image: -webkit-linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%);
+  background-image: linear-gradient(rgb(78,159,234) 0%,rgb(65,126,210) 100%);
 }
diff --git a/core/modules/contextual/contextual.toolbar-rtl.css b/core/modules/contextual/contextual.toolbar-rtl.css
new file mode 100644
index 0000000..226969f
--- /dev/null
+++ b/core/modules/contextual/contextual.toolbar-rtl.css
@@ -0,0 +1,11 @@
+/**
+ * @file
+ * RTL styling for contextual module's toolbar tab.
+ */
+
+.js .toolbar .bar .contextual-toolbar-tab.tab {
+  float: left;
+}
+.js .toolbar .bar .contextual-toolbar-tab button {
+  padding-right: 1.3333em;
+}
diff --git a/core/modules/contextual/contextual.toolbar.css b/core/modules/contextual/contextual.toolbar.css
new file mode 100644
index 0000000..1994bb9
--- /dev/null
+++ b/core/modules/contextual/contextual.toolbar.css
@@ -0,0 +1,36 @@
+/**
+ * @file
+ * Styling for contextual module's toolbar tab.
+ */
+
+/* Tab icon. */
+.icon-edit:before {
+  background-image: url("../../misc/edit.png");
+}
+.icon-edit:active:before,
+.active.icon-edit:before {
+  background-image: url("../../misc/edit-active.png");
+}
+
+/* Tab appearance. */
+.js .toolbar .bar .contextual-toolbar-tab.tab {
+  float: right;
+}
+.js .toolbar .bar .contextual-toolbar-tab button {
+  padding-bottom: 1em;
+  padding-top: 1em;
+  /* Hide tab text. */
+  padding-left: 1.3333em; /* LTR */
+  text-indent: -9999px;
+}
+.js .toolbar .bar .contextual-toolbar-tab button.active {
+  background-image:-moz-linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%);
+  background-image:-webkit-gradient(linear,color-stop(0, rgb(78,159,234)),color-stop(1, rgb(69,132,221)));
+  background-image: -webkit-linear-gradient(top,  rgb(78,159,234) 0%, rgb(69,132,221) 100%);
+  background-image:linear-gradient(rgb(78,159,234) 0%,rgb(69,132,221) 100%);
+}
+
+/* @todo get rid of this declaration by making toolbar.module's CSS less specific */
+.js .toolbar .bar .contextual-toolbar-tab.tab.element-hidden {
+  display: none;
+}
diff --git a/core/modules/contextual/contextual.toolbar.js b/core/modules/contextual/contextual.toolbar.js
new file mode 100644
index 0000000..df941b2
--- /dev/null
+++ b/core/modules/contextual/contextual.toolbar.js
@@ -0,0 +1,114 @@
+/**
+ * @file
+ * Attaches behaviors for the Contextual module's "Edit" toolbar tab.
+ */
+
+(function ($, Backbone, Drupal, document, localStorage) {
+
+"use strict";
+
+/**
+ * Attaches contextual's "Edit" toolbar tab behavior.
+ *
+ * Events
+ * Contextual triggers a number of events that can be used by other scripts.
+ * - drupalEditModeChanged: This event is triggered when the edit mode changes.
+ */
+Drupal.behaviors.contextualToolbar = {
+  attach: function (context) {
+    $('body').once('contextualToolbar-init', function () {
+      var model = new Drupal.contextualToolbar.models.EditToggleModel({
+        isViewing: (localStorage.getItem('Drupal.contextualToolbar.isViewing') === null) ? true : false
+      });
+      var view = new Drupal.contextualToolbar.views.EditToggleView({
+        el: $('.js .toolbar .bar .contextual-toolbar-tab'),
+        model: model
+      });
+
+      // Update the model based on overlay events.
+      $(document)
+        .on('drupalOverlayOpen.contextualToolbar', function () {
+          model.set('isVisible', false);
+        })
+        .on('drupalOverlayClose.contextualToolbar', function () {
+          model.set('isVisible', true);
+        });
+
+      // Update the model to show the "Edit" tab if there's >=1 contextual link.
+      if ($(context).find('.contextual-links').length > 0) {
+        model.set('isVisible', true);
+      }
+
+      // Allow other scripts to respond to edit mode changes.
+      model.on('change:isViewing', function (model, value) {
+        $(document).trigger('drupalEditModeChanged', { status: !value });
+      });
+    });
+  }
+};
+
+Drupal.contextualToolbar = Drupal.contextualToolbar || { models: {}, views: {}};
+
+/**
+ * Backbone Model for the "Edit" toggle.
+ */
+Drupal.contextualToolbar.models.EditToggleModel = Backbone.Model.extend({
+  defaults: {
+    /* Indicates whether the toggle is currently in "view" or "edit" mode. */
+    isViewing: true,
+    /* Indicates whether the toggle should be visible or hidden. */
+    isVisible: false
+  }
+});
+
+/**
+ * Backbone View to make the "Edit" toggle interactive.
+ */
+Drupal.contextualToolbar.views.EditToggleView = Backbone.View.extend({
+
+  events: { 'click': 'onClick' },
+
+  /**
+   * Implements Backbone Views' initialize() function.
+   */
+  initialize: function () {
+    this.model.on('change', this.render, this);
+    this.model.on('change:isViewing', this.persist, this);
+  },
+
+  /**
+   * Implements Backbone Views' render() function.
+   */
+  render: function () {
+    // Render the visibility.
+    this.$el.toggleClass('element-hidden', !this.model.get('isVisible'));
+
+    // Render the state.
+    var isViewing = this.model.get('isViewing');
+    this.$el.find('button')
+      .toggleClass('active', !isViewing)
+      .attr('aria-pressed', !isViewing);
+
+    return this;
+  },
+
+  persist: function (model, isViewing) {
+    // isViewing === true is the default, so only store in localStorage when
+    // it's not the default value.
+    if (!isViewing) {
+      localStorage.setItem('Drupal.contextualToolbar.isViewing', 'false');
+    }
+    else {
+      localStorage.removeItem('Drupal.contextualToolbar.isViewing');
+    }
+  },
+
+  onClick: function (event) {
+    this.model.set('isViewing', !this.model.get('isViewing'));
+    event.preventDefault();
+    event.stopPropagation();
+  }
+
+});
+
+})(jQuery, Backbone, Drupal, document, localStorage);
diff --git a/core/modules/edit/css/edit.css b/core/modules/edit/css/edit.css
index 37e10eb..7b758b7 100644
--- a/core/modules/edit/css/edit.css
+++ b/core/modules/edit/css/edit.css
@@ -71,47 +71,14 @@
 
 
 
-/**
- * Toolbar.
- */
-.icon-edit:before {
-  background-image: url("../images/icon-edit.png");
-}
-.icon-edit:active:before,
-.active .icon-edit:before {
-  background-image: url("../images/icon-edit-active.png");
-}
-.toolbar .tray.edit.active {
-  z-index: 340;
-}
-.toolbar .icon-edit.edit-nothing-editable-hidden {
-  display: none;
-}
-/* In-place editing doesn't work in the overlay, so always hide the tab. */
-.overlay-open .toolbar .icon-edit {
-  display: none;
-}
-
-
 
 /**
- * Edit mode: overlay + candidate editables + editables being edited.
+ * Candidate editables + editables being edited.
  *
  * Note: every class is prefixed with "edit-" to prevent collisions with modules
  * or themes. In IPE-specific DOM subtrees, this is not necessary.
  */
 
-#edit_overlay {
-  position: fixed;
-  z-index: 250;
-  width: 100%;
-  height: 100%;
-  background-color: #fff;
-  background-color: rgba(255,255,255,.5);
-  top: 0;
-  left: 0;
-}
-
 /* Editable. */
 .edit-editable {
   z-index: 300;
@@ -127,6 +94,7 @@
 
 /* Highlighted (hovered) editable. */
 .edit-editable.edit-highlighted {
+  z-index: 305;
   min-width: 200px;
 }
 .edit-field.edit-editable.edit-highlighted,
@@ -184,16 +152,6 @@
   background: #f5f5f5;
 }
 
-/* Modal active: prevent user from interacting with toolbar & editables. */
-.edit-form-container.edit-belowoverlay,
-.edit-toolbar-container.edit-belowoverlay,
-.edit-validation-errors.edit-belowoverlay {
-  z-index: 210;
-}
-.edit-editable.edit-belowoverlay {
-  z-index: 200;
-}
-
 
 
 
@@ -279,6 +237,10 @@
   bottom: 1px;
   box-shadow: 0 0 1px 1px #0199ff, 0 0 3px 3px rgba(153, 153, 153, .5);
   background: #fff;
+  display: none;
+}
+.edit-highlighted .edit-toolbar-heightfaker {
+  display: block;
 }
 
 /* The toolbar; these are not necessarily visible. */
diff --git a/core/modules/edit/edit.info b/core/modules/edit/edit.info
index 4074a7b..7c6d4b9 100644
--- a/core/modules/edit/edit.info
+++ b/core/modules/edit/edit.info
@@ -3,4 +3,5 @@ description = In-place content editing.
 package = Core
 core = 8.x
 version = VERSION
+dependencies[] = contextual
 dependencies[] = field
diff --git a/core/modules/edit/edit.module b/core/modules/edit/edit.module
index 584cace..fd4e096 100644
--- a/core/modules/edit/edit.module
+++ b/core/modules/edit/edit.module
@@ -39,39 +39,16 @@ function edit_permission() {
 }
 
 /**
- * Implements hook_toolbar().
+ * Implements hook_contextual_links_view_alter().
  */
-function edit_toolbar() {
+function edit_contextual_links_view_alter(&$element, $items) {
   if (!user_access('access in-place editing')) {
     return;
   }
 
-  $tab['edit'] = array(
-    '#type' => 'toolbar_item',
-    'tab' => array(
-      '#type' => 'link',
-      '#title' => t('Edit'),
-      '#href' => '',
-      '#options' => array(
-        'html' => FALSE,
-        'attributes' => array(
-          'id' => 'toolbar-tab-edit',
-          'class' => array('icon', 'icon-edit', 'edit-nothing-editable-hidden'),
-        ),
-      ),
-    ),
-    '#attached' => array(
-      'library' => array(
-        array('edit', 'edit'),
-      ),
-    ),
-  );
-
   // Include the attachments and settings for all available editors.
   $attachments = drupal_container()->get('edit.editor.selector')->getAllEditorAttachments();
-  $tab['edit']['#attached'] = NestedArray::mergeDeep($tab['edit']['#attached'], $attachments);
-
-  return $tab;
+  $element['#attached'] = NestedArray::mergeDeep($element['#attached'], $attachments);
 }
 
 /**
@@ -91,15 +68,12 @@ function edit_library_info() {
       // Core.
       $path . '/js/edit.js' => $options,
       $path . '/js/app.js' => $options,
-      // Routers.
-      $path . '/js/routers/edit-router.js' => $options,
       // Models.
       $path . '/js/models/edit-app-model.js' => $options,
       // Views.
       $path . '/js/views/propertyeditordecoration-view.js' => $options,
-      $path . '/js/views/menu-view.js' => $options,
+      $path . '/js/views/contextuallink-view.js' => $options,
       $path . '/js/views/modal-view.js' => $options,
-      $path . '/js/views/overlay-view.js' => $options,
       $path . '/js/views/toolbar-view.js' => $options,
       // Backbone.sync implementation on top of Drupal forms.
       $path . '/js/backbone.drupalform.js' => $options,
@@ -171,6 +145,16 @@ function edit_preprocess_field(&$variables) {
 }
 
 /**
+ * Implements hook_preprocess_HOOK() for node.tpl.php.
+ *
+ * @todo Move towards hook_preprocess_entity() once that's available.
+ */
+function edit_preprocess_node(&$variables) {
+  $node = $variables['elements']['#node'];
+  $variables['attributes']['data-edit-entity'] = 'node/' . $node->nid;
+}
+
+/**
  * Form constructor for the field editing form.
  *
  * @ingroup forms
diff --git a/core/modules/edit/images/icon-edit-active.png b/core/modules/edit/images/icon-edit-active.png
deleted file mode 100644
index ad84761..0000000
--- a/core/modules/edit/images/icon-edit-active.png
+++ /dev/null
@@ -1,3 +0,0 @@
-PNG
-
-   IHDR         j	   `PLTE   [   tRNS@ P00p`ϟ Dƙ   IDATxe DQ8Ϩ/BDU9xV+D\?x@qWcF8wicS B}?v;Vf.V$JgX=Kضp0XS"iRw\:LL\~;Z5wu 5E)L    IENDB`
\ No newline at end of file
diff --git a/core/modules/edit/images/icon-edit.png b/core/modules/edit/images/icon-edit.png
deleted file mode 100644
index 4f0dcc2..0000000
--- a/core/modules/edit/images/icon-edit.png
+++ /dev/null
@@ -1,5 +0,0 @@
-PNG
-
-   IHDR         j	   PLTE̻ʪ̡̜ˠ̣̽¼Ƿ   ʨªZ(e   +tRNSϟ `π@`0p0p`0Pϟϟ c   IDATxeW0 {W
-H"ʵ,y{Hpyo?mf,RBRxBvL;&LPJaRb\(Tbn(1wϔJ)ԈkS
-58äT^4 P6c}[i	<ާ'-+HP>K    IENDB`
\ No newline at end of file
diff --git a/core/modules/edit/js/app.js b/core/modules/edit/js/app.js
index 00bba20..8181039 100644
--- a/core/modules/edit/js/app.js
+++ b/core/modules/edit/js/app.js
@@ -47,20 +47,8 @@
         editableNs: 'createeditable'
       });
 
-      // Instantiate OverlayView.
-      var overlayView = new Drupal.edit.views.OverlayView({
-        el: (Drupal.theme('editOverlay', {})),
-        model: this.model
-      });
-
-      // Instantiate MenuView.
-      var editMenuView = new Drupal.edit.views.MenuView({
-        el: this.el,
-        model: this.model
-      });
-
       // When view/edit mode is toggled in the menu, update the editor widgets.
-      this.model.on('change:isViewing', this.appStateChange);
+      this.model.on('change:activeEntity', this.appStateChange);
     },
 
     /**
@@ -74,7 +62,7 @@
      */
     findEditableProperties: function($context) {
       var that = this;
-      var newState = (this.model.get('isViewing')) ? 'inactive' : 'candidate';
+      var activeEntity = this.model.get('activeEntity');
 
       this.domService.findSubjectElements($context).each(function() {
         var $element = $(this);
@@ -103,10 +91,17 @@
           .on('destroyedPropertyEditor.edit', function(event, editor) {
             that.undecorateEditor(editor);
             that.$entityElements = that.$entityElements.not($(this));
-
           })
-          // Transition the new PropertyEditor into the current state.
-          .createEditable('setState', newState);
+          // Transition the new PropertyEditor into the default state.
+          .createEditable('setState', 'inactive');
+
+        // If the new PropertyEditor is for the entity that's currently being
+        // edited, then transition it to the 'candidate' state.
+        // (This happens when a field was modified and is re-rendered.)
+        var entityOfProperty = $element.createEditable('option', 'model');
+        if (entityOfProperty.getSubjectUri() === activeEntity) {
+          $element.createEditable('setState', 'candidate');
+        }
 
         // Add this new EditableEntity widget element to the list.
         that.$entityElements = that.$entityElements.add($element);
@@ -116,18 +111,30 @@
     /**
      * Sets the state of PropertyEditor widgets when edit mode begins or ends.
      *
-     * Should be called whenever EditAppModel's "isViewing" changes.
+     * Should be called whenever EditAppModel's "activeEntity" changes.
      */
     appStateChange: function() {
       // @todo: BLOCKED_ON(Create.js, https://github.com/bergie/create/issues/133, https://github.com/bergie/create/issues/140)
       // We're currently setting the state on EditableEntity widgets instead of
       // PropertyEditor widgets, because of
       // https://github.com/bergie/create/issues/133.
-      var newState = (this.model.get('isViewing')) ? 'inactive' : 'candidate';
+
+      var activeEntity = this.model.get('activeEntity');
+      var $editableFieldsForEntity = $('[data-edit-id^="' + activeEntity + '/"]');
+
+      // First, change the status of all PropertyEditor widgets to 'inactive'.
       this.$entityElements.each(function() {
-        $(this).createEditable('setState', newState);
+        $(this).createEditable('setState', 'inactive', null, {reason: 'stop'});
+      });
+
+      // Then, change the status of PropertyEditor widgets of the currently
+      // active entity to 'candidate'.
+      $editableFieldsForEntity.each(function() {
+        $(this).createEditable('setState', 'candidate');
       });
+
       // Manage the page's tab indexes.
+      /*
       if (newState === 'candidate') {
         this._manageDocumentFocus();
         Drupal.edit.setMessage(Drupal.t('In place edit mode is active'), Drupal.t('Page navigation is limited to editable items.'), Drupal.t('Press escape to exit'));
@@ -136,6 +143,7 @@
         this._releaseDocumentFocusManagement();
         Drupal.edit.setMessage(Drupal.t('Edit mode is inactive.'), Drupal.t('Resume normal page navigation'));
       }
+      */
     },
 
     /**
@@ -159,9 +167,9 @@
 
       // If the app is in view mode, then reject all state changes except for
       // those to 'inactive'.
-      if (this.model.get('isViewing')) {
-        if (to !== 'inactive') {
-          accept = false;
+      if (context && context.reason === 'stop') {
+        if (from === 'candidate' && to === 'inactive') {
+          accept = true;
         }
       }
       // Handling of edit mode state changes is more granular.
diff --git a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
index 5671f39..caac604 100644
--- a/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
+++ b/core/modules/edit/js/createjs/editingWidgets/drupalcontenteditablewidget.js
@@ -29,13 +29,6 @@
     _initialize: function() {
       var that = this;
 
-      // Sets the state to 'activated' upon clicking the element.
-      this.element.on("click.edit", function(event) {
-        event.stopPropagation();
-        event.preventDefault();
-        that.options.activated();
-      });
-
       // Sets the state to 'changed' whenever the content has changed.
       var before = jQuery.trim(this.element.text());
       this.element.on('keyup paste', function (event) {
@@ -68,6 +61,7 @@
         case 'highlighted':
           break;
         case 'activating':
+          this.options.activated();
           break;
         case 'active':
           // Sets the "contenteditable" attribute to "true".
diff --git a/core/modules/edit/js/createjs/editingWidgets/formwidget.js b/core/modules/edit/js/createjs/editingWidgets/formwidget.js
index 3238566..83dad4f 100644
--- a/core/modules/edit/js/createjs/editingWidgets/formwidget.js
+++ b/core/modules/edit/js/createjs/editingWidgets/formwidget.js
@@ -29,15 +29,7 @@
     /**
      * Implements Create's _initialize() method.
      */
-    _initialize: function() {
-      // Sets the state to 'activating' upon clicking the element.
-      var that = this;
-      this.element.on("click.edit", function(event) {
-        event.stopPropagation();
-        event.preventDefault();
-        that.options.activating();
-      });
-    },
+    _initialize: function() {},
 
     /**
      * Makes this PropertyEditor widget react to state changes.
@@ -49,15 +41,11 @@
         case 'candidate':
           if (from !== 'inactive') {
             this.disable();
-            if (from !== 'highlighted') {
-              this.element.removeClass('edit-belowoverlay');
-            }
           }
           break;
         case 'highlighted':
           break;
         case 'activating':
-          this.element.addClass('edit-belowoverlay');
           this.enable();
           break;
         case 'active':
diff --git a/core/modules/edit/js/edit.js b/core/modules/edit/js/edit.js
index cfaf76b..639ff75 100644
--- a/core/modules/edit/js/edit.js
+++ b/core/modules/edit/js/edit.js
@@ -28,7 +28,7 @@ Drupal.behaviors.edit = {
     var $fields = $context.find('[data-edit-id]');
 
     // Initialize the Edit app.
-    $context.find('#toolbar-tab-edit').once('edit-init', Drupal.edit.init);
+    $('body').once('edit-init', Drupal.edit.init);
 
     var annotateField = function(field) {
       if (_.has(Drupal.edit.metadataCache, field.editID)) {
@@ -81,13 +81,6 @@ Drupal.behaviors.edit = {
             // Annotate the remaining fields based on the updated access cache.
             _.each(remainingFieldsToAnnotate, annotateField);
 
-            // As soon as there is at least one editable field, show the Edit
-            // tab in the toolbar.
-            if ($fields.filter('.edit-allowed').length) {
-              $('.toolbar .icon-edit.edit-nothing-editable-hidden')
-                .removeClass('edit-nothing-editable-hidden');
-            }
-
             // Find editable fields, make them editable.
             Drupal.edit.app.findEditableProperties($context);
           }
@@ -109,14 +102,21 @@ Drupal.edit.init = function() {
     model: appModel
   });
 
-  // Instantiate EditRouter.
-  var editRouter = new Drupal.edit.routers.EditRouter({
-    appModel: appModel
+  // Add "Quick edit" links to all contextual menus where editing the full
+  // node is possible.
+  // @todo Generalize this to work for all entities.
+  $('ul.contextual-links li.node-edit')
+  .before('<li class="quick-edit"></li>')
+  .each(function() {
+    // Instantiate ContextualLinkView.
+    var $editContextualLink = $(this).prev();
+    var editContextualLinkView = new Drupal.edit.views.ContextualLinkView({
+      el: $editContextualLink.get(0),
+      model: appModel,
+      entity: $editContextualLink.parents('[data-edit-entity]').attr('data-edit-entity')
+    });
   });
 
-  // Start Backbone's history/route handling.
-  Backbone.history.start();
-
   // For now, we work with a singleton app, because for Drupal.behaviors to be
   // able to discover new editable properties that get AJAXed in, it must know
   // with which app instance they should be associated.
diff --git a/core/modules/edit/js/models/edit-app-model.js b/core/modules/edit/js/models/edit-app-model.js
index b6ff36f..0c90fd0 100644
--- a/core/modules/edit/js/models/edit-app-model.js
+++ b/core/modules/edit/js/models/edit-app-model.js
@@ -10,8 +10,7 @@ Drupal.edit = Drupal.edit || {};
 Drupal.edit.models = Drupal.edit.models || {};
 Drupal.edit.models.EditAppModel = Backbone.Model.extend({
   defaults: {
-    // We always begin in view mode.
-    isViewing: true,
+    activeEntity: null,
     highlightedEditor: null,
     activeEditor: null,
     // Reference to a ModalView-instance if a transition requires confirmation.
diff --git a/core/modules/edit/js/routers/edit-router.js b/core/modules/edit/js/routers/edit-router.js
deleted file mode 100644
index d160ad4..0000000
--- a/core/modules/edit/js/routers/edit-router.js
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * @file
- * A Backbone Router enabling URLs to make the user enter edit mode directly.
- */
-(function(Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.routers = {};
-Drupal.edit.routers.EditRouter = Backbone.Router.extend({
-
-  appModel: null,
-
-  routes: {
-    "edit": "edit",
-    "view": "view",
-    "": "view"
-  },
-
-  initialize: function(options) {
-    this.appModel = options.appModel;
-
-    var that = this;
-    this.appModel.on('change:isViewing', function() {
-      that.navigate(that.appModel.get('isViewing') ? '#view' : '#edit');
-    });
-  },
-
-  edit: function() {
-    this.appModel.set('isViewing', false);
-  },
-
-  view: function(query, page) {
-    var that = this;
-
-    // If there's an active editor, attempt to set its state to 'candidate', and
-    // then act according to the user's choice.
-    var activeEditor = this.appModel.get('activeEditor');
-    if (activeEditor) {
-      var editableEntity = activeEditor.options.widget;
-      var predicate = activeEditor.options.property;
-      editableEntity.setState('candidate', predicate, { reason: 'menu' }, function(accepted) {
-        if (accepted) {
-          that.appModel.set('isViewing', true);
-        }
-        else {
-          that.appModel.set('isViewing', false);
-        }
-      });
-    }
-    // Otherwise, we can switch to view mode directly.
-    else {
-      that.appModel.set('isViewing', true);
-    }
-  }
-});
-
-})(Backbone, Drupal);
diff --git a/core/modules/edit/js/views/contextuallink-view.js b/core/modules/edit/js/views/contextuallink-view.js
new file mode 100644
index 0000000..cb6a2f6
--- /dev/null
+++ b/core/modules/edit/js/views/contextuallink-view.js
@@ -0,0 +1,94 @@
+/**
+ * @file
+ * A Backbone View that a dynamic contextual link.
+ */
+(function ($, _, Backbone, Drupal) {
+
+"use strict";
+
+Drupal.edit = Drupal.edit || {};
+Drupal.edit.views = Drupal.edit.views || {};
+Drupal.edit.views.ContextualLinkView = Backbone.View.extend({
+
+  entity: null,
+
+  events: {
+    'click': 'onClick'
+  },
+
+  /**
+   * Implements Backbone Views' initialize() function.
+   *
+   * @param options
+   *   An object with the following keys:
+   *   - entity: the entity ID (e.g. node/1) of the entity
+   */
+  initialize: function (options) {
+    this.entity = options.entity;
+
+    // Initial render.
+    this.render();
+
+    // Re-render whenever the app state's active entity changes.
+    this.model.on('change:activeEntity', this.render, this);
+  },
+
+  /**
+   * Equates clicks anywhere on the overlay to clicking the active editor's (if
+   * any) "close" button.
+   *
+   * @param {Object} event
+   */
+  onClick: function (event) {
+    event.preventDefault();
+
+    var that = this;
+    var updateActiveEntity = function() {
+      // The active entity is the current entity, i.e. stop editing the current
+      // entity.
+      if (that.model.get('activeEntity') === that.entity) {
+        that.model.set('activeEntity', null);
+      }
+      // The active entity is different from the current entity, i.e. start
+      // editing this entity instead of the previous one.
+      else {
+        that.model.set('activeEntity', that.entity);
+      }
+    };
+
+    // If there's an active editor, attempt to set its state to 'candidate', and
+    // only then do what the user asked.
+    // (Only when all PropertyEditor widgets of an entity are in the 'candidate'
+    // state, it is possible to stop editing it.)
+    var activeEditor = this.model.get('activeEditor');
+    if (activeEditor) {
+      var editableEntity = activeEditor.options.widget;
+      var predicate = activeEditor.options.property;
+      editableEntity.setState('candidate', predicate, { reason: 'stop or switch' }, function(accepted) {
+        if (accepted) {
+          updateActiveEntity();
+        }
+        else {
+          // No change.
+        }
+      });
+    }
+    // Otherwise, we can immediately do what the user asked.
+    else {
+      updateActiveEntity();
+    }
+  },
+
+  /**
+   * Render the "Quick edit" contextual link.
+   */
+  render: function () {
+    var activeEntity = this.model.get('activeEntity');
+    var string = (activeEntity !== this.entity) ? Drupal.t('Quick edit') : Drupal.t('Stop quick edit');
+    this.$el.html('<a href="">' + string + '</a>');
+    return this;
+  }
+
+});
+
+})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/menu-view.js b/core/modules/edit/js/views/menu-view.js
deleted file mode 100644
index ac7c4e4..0000000
--- a/core/modules/edit/js/views/menu-view.js
+++ /dev/null
@@ -1,82 +0,0 @@
-/**
- * @file
- * A Backbone View that provides the app-level interactive menu.
- */
-(function($, _, Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.MenuView = Backbone.View.extend({
-
-  events: {
-    'click #toolbar-tab-edit': 'editClickHandler'
-  },
-
-  /**
-   * Implements Backbone Views' initialize() function.
-   */
-  initialize: function() {
-    _.bindAll(this, 'stateChange');
-    this.model.on('change:isViewing', this.stateChange);
-    // @todo
-    // Re-implement hook_toolbar and the corresponding JavaScript behaviors
-    // once https://drupal.org/node/1847198 is resolved. The toolbar tray is
-    // necessary when the page request is processed because its render element
-    // has an #attached property with the Edit module library code assigned to
-    // it. Currently a toolbar tab is not passed as a renderable array, so
-    // #attached properties are not processed. The toolbar tray DOM element is
-    // unnecessary right now, so it is removed.
-    this.$el.find('#toolbar-tray-edit').remove();
-    // Respond to clicks on other toolbar tabs. This temporary pending
-    // improvements to the toolbar module.
-    $('#toolbar-administration').on('click.edit', '.bar a:not(#toolbar-tab-edit)', _.bind(function (event) {
-      this.model.set('isViewing', true);
-    }, this));
-    // We have to call stateChange() here because URL fragments are not passed
-    // to the server, thus the wrong anchor may be marked as active.
-    this.stateChange();
-  },
-
-  /**
-   * Listens to app state changes.
-   */
-  stateChange: function() {
-    var isViewing = this.model.get('isViewing');
-    // Toggle the state of the Toolbar Edit tab based on the isViewing state.
-    this.$el.find('#toolbar-tab-edit')
-      .toggleClass('active', !isViewing)
-      .attr('aria-pressed', !isViewing);
-    // Manage the toolbar state until
-    // https://drupal.org/node/1847198 is resolved
-    if (!isViewing) {
-      // Remove the 'toolbar-tray-open' class from the body element.
-      this.$el.removeClass('toolbar-tray-open');
-      // Deactivate any other active tabs and trays.
-      this.$el
-        .find('.bar a', '#toolbar-administration')
-        .not('#toolbar-tab-edit')
-        .add('.tray', '#toolbar-administration')
-        .removeClass('active');
-      // Set the height of the toolbar.
-      if ('toolbar' in Drupal) {
-        Drupal.toolbar.setHeight();
-      }
-    }
-  },
-  /**
-   * Handles clicks on the edit tab of the toolbar.
-   *
-   * @param {Object} event
-   */
-  editClickHandler: function (event) {
-    var isViewing = this.model.get('isViewing');
-    // Toggle the href of the Toolbar Edit tab based on the isViewing state. The
-    // href value should represent to state to be entered.
-    this.$el.find('#toolbar-tab-edit').attr('href', (isViewing) ? '#edit' : '#view');
-    this.model.set('isViewing', !isViewing);
-  }
-});
-
-})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/modal-view.js b/core/modules/edit/js/views/modal-view.js
index 2e3b49c..edd158a 100644
--- a/core/modules/edit/js/views/modal-view.js
+++ b/core/modules/edit/js/views/modal-view.js
@@ -43,17 +43,6 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
    * Implements Backbone Views' render() function.
    */
   render: function() {
-    // Step 1: move certain UI elements below the overlay.
-    var editor = this.model.get('activeEditor');
-    this.$elementsToHide = $([])
-      .add((editor.element.hasClass('edit-belowoverlay')) ? null : editor.element)
-      .add(editor.toolbarView.$el)
-      .add((editor.options.editorName === 'form') ? editor.$formContainer : editor.element.next('.edit-validation-errors'));
-    this.$elementsToHide.addClass('edit-belowoverlay');
-
-    // Step 2: the modal. When the user makes a choice, the UI elements that
-    // were moved below the overlay will be restored, and the callback will be
-    // called.
     this.setElement(Drupal.theme('editModal', {}));
     this.$el.appendTo('body');
     // Template.
@@ -61,7 +50,7 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
     var $actions = $(Drupal.theme('editButtons', { 'buttons' : this.buttons}));
     this.$('.actions').append($actions);
 
-    // Step 3; show the modal with an animation.
+    // Show the modal with an animation.
     var that = this;
     setTimeout(function() {
       that.$el.removeClass('edit-animate-invisible');
@@ -90,17 +79,6 @@ Drupal.edit.views.ModalView = Backbone.View.extend({
 
     var action = $(event.target).attr('data-edit-modal-action');
     return this.callback(action);
-  },
-
-  /**
-   * Overrides Backbone Views' remove() function.
-   */
-  remove: function() {
-    // Move the moved UI elements on top of the overlay again.
-    this.$elementsToHide.removeClass('edit-belowoverlay');
-
-    // Remove the modal itself.
-    this.$el.remove();
   }
 });
 
diff --git a/core/modules/edit/js/views/overlay-view.js b/core/modules/edit/js/views/overlay-view.js
deleted file mode 100644
index 2113ab8..0000000
--- a/core/modules/edit/js/views/overlay-view.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/**
- * @file
- * A Backbone View that provides the app-level overlay.
- *
- * The overlay sits on top of the existing content, the properties that are
- * candidates for editing sit on top of the overlay.
- */
-(function ($, _, Backbone, Drupal) {
-
-"use strict";
-
-Drupal.edit = Drupal.edit || {};
-Drupal.edit.views = Drupal.edit.views || {};
-Drupal.edit.views.OverlayView = Backbone.View.extend({
-
-  events: {
-    'click': 'onClick'
-  },
-
-  /**
-   * Implements Backbone Views' initialize() function.
-   */
-  initialize: function (options) {
-    _.bindAll(this, 'stateChange');
-    this.model.on('change:isViewing', this.stateChange);
-    // Add the overlay to the page.
-    this.$el
-      .addClass('edit-animate-slow edit-animate-invisible')
-      .hide()
-      .appendTo('body');
-  },
-
-  /**
-   * Listens to app state changes.
-   */
-  stateChange: function () {
-    if (this.model.get('isViewing')) {
-      this.remove();
-      return;
-    }
-    this.render();
-  },
-
-  /**
-   * Equates clicks anywhere on the overlay to clicking the active editor's (if
-   * any) "close" button.
-   *
-   * @param {Object} event
-   */
-  onClick: function (event) {
-    event.preventDefault();
-    var activeEditor = this.model.get('activeEditor');
-    if (activeEditor) {
-      var editableEntity = activeEditor.options.widget;
-      var predicate = activeEditor.options.property;
-      editableEntity.setState('candidate', predicate, { reason: 'overlay' });
-    }
-    else {
-      this.model.set('isViewing', true);
-    }
-  },
-
-  /**
-   * Reveal the overlay element.
-   */
-  render: function () {
-    this.$el
-      .show()
-      .css('top', $('#navbar').outerHeight())
-      .removeClass('edit-animate-invisible');
-  },
-
-  /**
-   * Hide the overlay element.
-   */
-  remove: function () {
-    var that = this;
-    this.$el
-      .addClass('edit-animate-invisible')
-      .on(Drupal.edit.util.constants.transitionEnd, function (event) {
-        that.$el.hide();
-      });
-  }
-});
-
-})(jQuery, _, Backbone, Drupal);
diff --git a/core/modules/edit/js/views/propertyeditordecoration-view.js b/core/modules/edit/js/views/propertyeditordecoration-view.js
index 0eb4e45..aad9832 100644
--- a/core/modules/edit/js/views/propertyeditordecoration-view.js
+++ b/core/modules/edit/js/views/propertyeditordecoration-view.js
@@ -19,6 +19,7 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
   events: {
     'mouseenter.edit' : 'onMouseEnter',
     'mouseleave.edit' : 'onMouseLeave',
+    'click': 'onClick',
     'tabIn.edit': 'onMouseEnter',
     'tabOut.edit': 'onMouseLeave'
   },
@@ -38,7 +39,12 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
     this.editor = options.editor;
     this.toolbarId = options.toolbarId;
 
+    this.predicate = this.editor.options.property;
+
     this.$el.css('background-color', this._getBgColor(this.$el));
+
+    // Only start listening to events as soon as we're no longer in the 'inactive' state.
+    this.undelegateEvents();
   },
 
   /**
@@ -113,13 +119,27 @@ Drupal.edit.views.PropertyEditorDecorationView = Backbone.View.extend({
     });
   },
 
+  /**
+   * Clicks: transition to 'activating' stage.
+   *
+   * @param event
+   */
+  onClick: function(event) {
+    var editableEntity = this.editor.options.widget;
+    editableEntity.setState('activating', this.predicate);
+    event.preventDefault();
+    event.stopPropagation();
+  },
+
   decorate: function () {
     this.$el.addClass('edit-animate-fast edit-candidate edit-editable');
+    this.delegateEvents();
   },
 
   undecorate: function () {
     this.$el
       .removeClass('edit-candidate edit-editable edit-highlighted edit-editing');
+    this.undelegateEvents();
   },
 
   startHighlight: function () {
diff --git a/core/modules/edit/js/views/toolbar-view.js b/core/modules/edit/js/views/toolbar-view.js
index 90f5db7..b052362 100644
--- a/core/modules/edit/js/views/toolbar-view.js
+++ b/core/modules/edit/js/views/toolbar-view.js
@@ -29,7 +29,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({
     'click.edit button.label': 'onClickInfoLabel',
     'mouseleave.edit': 'onMouseLeave',
     'click.edit button.field-save': 'onClickSave',
-    'click.edit button.field-close': 'onClickClose'
+    'click.edit button.field-close': 'onClickClose',
   },
 
   /**
@@ -66,19 +66,26 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({
   stateChange: function(from, to) {
     switch (to) {
       case 'inactive':
-        // Nothing happens in this stage.
+        if (from) {
+          this.remove();
+        }
         break;
       case 'candidate':
-        if (from !== 'inactive') {
+        if (from === 'inactive') {
+          this.render();
+        }
+        else {
+          // Remove all toolgroups; they're no longer necessary.
+          this.$el
+            .removeClass('edit-highlighted edit-editing')
+            .find('.edit-toolbar .edit-toolgroup').remove();
           if (from !== 'highlighted' && this.getEditUISetting('padding')) {
             this._unpad();
           }
-          this.remove();
         }
         break;
       case 'highlighted':
         // As soon as we highlight, make sure we have a toolbar in the DOM (with at least a title).
-        this.render();
         this.startHighlight();
         break;
       case 'activating':
@@ -275,6 +282,7 @@ Drupal.edit.views.ToolbarView = Backbone.View.extend({
     }
 
     this.$el
+      .addClass('edit-highlighted')
       .find('.edit-toolbar')
       // Append the "info" toolgroup into the toolbar.
       .append(Drupal.theme('editToolgroup', {
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
index 48faf24..65dd7af 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterFormatAccessTest.php
@@ -48,6 +48,13 @@ class FilterFormatAccessTest extends WebTestBase {
    */
   protected $disallowed_format;
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('contextual');
+
   public static function getInfo() {
     return array(
       'name' => 'Filter format access',
@@ -88,6 +95,7 @@ function setUp() {
     $this->web_user = $this->drupalCreateUser(array(
       'create page content',
       'edit any page content',
+      'access contextual links',
       filter_permission_name($this->allowed_format),
     ));
 
@@ -96,6 +104,7 @@ function setUp() {
       'administer filters',
       'create page content',
       'edit any page content',
+      'access contextual links',
       filter_permission_name($this->allowed_format),
       filter_permission_name($this->disallowed_format),
     ));
diff --git a/core/modules/node/lib/Drupal/node/NodeRenderController.php b/core/modules/node/lib/Drupal/node/NodeRenderController.php
index e829e40..6a6608a 100644
--- a/core/modules/node/lib/Drupal/node/NodeRenderController.php
+++ b/core/modules/node/lib/Drupal/node/NodeRenderController.php
@@ -88,11 +88,7 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
    */
   protected function alterBuild(array &$build, EntityInterface $entity, EntityDisplay $display, $view_mode, $langcode = NULL) {
     parent::alterBuild($build, $entity, $display, $view_mode, $langcode);
-    // Add contextual links for this node, except when the node is already being
-    // displayed on its own page. Modules may alter this behavior (for example,
-    // to restrict contextual links to certain view modes) by implementing
-    // hook_node_view_alter().
-    if (!empty($entity->nid) && !($view_mode == 'full' && node_is_page($entity))) {
+    if (!empty($entity->nid)) {
       $build['#contextual_links']['node'] = array('node', array($entity->nid));
     }
   }
diff --git a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php
index b461492..b650fea 100644
--- a/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/PageEditTest.php
@@ -14,6 +14,13 @@ class PageEditTest extends NodeTestBase {
   protected $web_user;
   protected $admin_user;
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('node', 'contextual');
+
   public static function getInfo() {
     return array(
       'name' => 'Node edit',
@@ -25,8 +32,8 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $this->web_user = $this->drupalCreateUser(array('edit own page content', 'create page content'));
-    $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes'));
+    $this->web_user = $this->drupalCreateUser(array('edit own page content', 'create page content', 'access contextual links'));
+    $this->admin_user = $this->drupalCreateUser(array('bypass node access', 'administer nodes', 'access contextual links'));
   }
 
   /**
@@ -50,14 +57,11 @@ function testPageEdit() {
 
     // Check that "edit" link points to correct page.
     $this->clickLink(t('Edit'));
-    $edit_url = url("node/$node->nid/edit", array('absolute' => TRUE));
+    $edit_url = url("node/$node->nid/edit", array('absolute' => TRUE, 'query' => array('destination' => 'node/1')));
     $actual_url = $this->getURL();
     $this->assertEqual($edit_url, $actual_url, 'On edit page.');
 
     // Check that the title and body fields are displayed with the correct values.
-    $active = '<span class="element-invisible">' . t('(active tab)') . '</span>';
-    $link_text = t('!local-task-title!active', array('!local-task-title' => t('Edit'), '!active' => $active));
-    $this->assertText(strip_tags($link_text), 0, 'Edit tab found and marked active.');
     $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
     $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
 
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index f2206a0..ff073ae 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1769,7 +1769,7 @@ function node_menu() {
     'access arguments' => array('update', 1),
     'weight' => 0,
     'type' => MENU_LOCAL_TASK,
-    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
+    'context' => MENU_CONTEXT_INLINE,
     'file' => 'node.pages.inc',
   );
   $items['node/%node/delete'] = array(
@@ -1791,6 +1791,7 @@ function node_menu() {
     'access arguments' => array(1),
     'weight' => 2,
     'type' => MENU_LOCAL_TASK,
+    'context' => MENU_CONTEXT_INLINE,
     'file' => 'node.pages.inc',
   );
   $items['node/%node/revisions/%node_revision/view'] = array(
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 322e416..b361134 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -1998,7 +1998,7 @@ function system_library_info() {
     'website' => 'http://underscorejs.org/',
     'version' => '1.4.0',
     'js' => array(
-      'core/misc/underscore/underscore.js' => array('group' => JS_LIBRARY),
+      'core/misc/underscore/underscore.js' => array('group' => JS_LIBRARY, 'weight' => -20),
     ),
   );
 
@@ -2008,7 +2008,7 @@ function system_library_info() {
     'website' => 'http://backbonejs.org/',
     'version' => '0.9.2',
     'js' => array(
-      'core/misc/backbone/backbone.js' => array('group' => JS_LIBRARY),
+      'core/misc/backbone/backbone.js' => array('group' => JS_LIBRARY, 'weight' => -19),
     ),
     'dependencies' => array(
       array('system', 'underscore'),
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
index 93cc8d4..9f65f1c 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
@@ -12,6 +12,13 @@
  */
 class TermTest extends TaxonomyTestBase {
 
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('taxonomy', 'contextual');
+
   public static function getInfo() {
     return array(
       'name' => 'Taxonomy term functions and forms',
@@ -22,7 +29,7 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access'));
+    $this->admin_user = $this->drupalCreateUser(array('administer taxonomy', 'bypass node access', 'access contextual links'));
     $this->drupalLogin($this->admin_user);
     $this->vocabulary = $this->createVocabulary();
 
