diff --git a/DEPS b/DEPS
index 3fac46da..9e6da72 100644
--- a/DEPS
+++ b/DEPS
@@ -36,7 +36,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e930459a18ea099859f7d0076802458be00a6b4c',
+  'skia_revision': '443023975e335e3630191227dbc21fa72c436af3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -88,7 +88,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': '243e3dfbc911dba7d7ad2397aa5d975d6b85a854',
+  'catapult_revision': '6cad5da3c39eca66c6dcd2db0b4cb1885fc00556',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
diff --git a/ash/common/wm/overview/scoped_transform_overview_window.cc b/ash/common/wm/overview/scoped_transform_overview_window.cc
index ca19743..0816a10c 100644
--- a/ash/common/wm/overview/scoped_transform_overview_window.cc
+++ b/ash/common/wm/overview/scoped_transform_overview_window.cc
@@ -180,7 +180,7 @@
 class ScopedTransformOverviewWindow::OverviewContentMask
     : public ui::LayerDelegate {
  public:
-  OverviewContentMask(int inset, int radius);
+  explicit OverviewContentMask(float radius);
   ~OverviewContentMask() override;
 
   ui::Layer* layer() { return &layer_; }
@@ -193,16 +193,14 @@
 
  private:
   ui::Layer layer_;
-  int inset_;
-  int radius_;
+  float radius_;
 
   DISALLOW_COPY_AND_ASSIGN(OverviewContentMask);
 };
 
 ScopedTransformOverviewWindow::OverviewContentMask::OverviewContentMask(
-    int inset,
-    int radius)
-    : layer_(ui::LAYER_TEXTURED), inset_(inset), radius_(radius) {
+    float radius)
+    : layer_(ui::LAYER_TEXTURED), radius_(radius) {
   layer_.set_delegate(this);
 }
 
@@ -214,7 +212,6 @@
     const ui::PaintContext& context) {
   ui::PaintRecorder recorder(context, layer()->size());
   gfx::Rect bounds(layer()->bounds().size());
-  bounds.Inset(0, inset_, 0, 0);
 
   // Tile a window into an area, rounding the bottom corners.
   const SkRect rect = gfx::RectToSkRect(bounds);
@@ -260,11 +257,6 @@
 ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {}
 
 void ScopedTransformOverviewWindow::RestoreWindow() {
-  if (ash::MaterialDesignController::IsOverviewMaterial()) {
-    window()->GetLayer()->SetMaskLayer(nullptr);
-    mask_.reset();
-  }
-
   ScopedAnimationSettings animation_settings_list;
   BeginScopedAnimation(OverviewAnimationType::OVERVIEW_ANIMATION_RESTORE_WINDOW,
                        &animation_settings_list);
@@ -288,6 +280,20 @@
   }
   window_->GetWindowState()->set_ignored_by_shelf(ignored_by_shelf_);
   SetOpacity(original_opacity_);
+
+  if (ash::MaterialDesignController::IsOverviewMaterial()) {
+    ui::Layer* layer = window()->GetLayer();
+    layer->SetMaskLayer(nullptr);
+    mask_.reset();
+
+    if (original_window_shape_) {
+      layer->SetAlphaShape(
+          base::WrapUnique(new SkRegion(*original_window_shape_.get())));
+    } else {
+      layer->SetAlphaShape(nullptr);
+    }
+    window()->SetMasksToBounds(false);
+  }
 }
 
 void ScopedTransformOverviewWindow::BeginScopedAnimation(
@@ -394,14 +400,31 @@
 void ScopedTransformOverviewWindow::SetTransform(
     WmWindow* root_window,
     const gfx::Transform& transform,
-    int radius) {
+    float radius) {
   DCHECK(overview_started_);
 
-  if (ash::MaterialDesignController::IsOverviewMaterial() && !mask_) {
-    mask_.reset(new OverviewContentMask(
-        window()->GetIntProperty(WmWindowProperty::TOP_VIEW_INSET), radius));
-    mask_->layer()->SetBounds(GetTargetBoundsInScreen());
+  if (ash::MaterialDesignController::IsOverviewMaterial() &&
+      &transform != &original_transform_) {
+    gfx::Rect bounds(GetTargetBoundsInScreen().size());
+    mask_.reset(new OverviewContentMask(radius));
+    mask_->layer()->SetFillsBoundsOpaquely(false);
+    mask_->layer()->SetBounds(bounds);
     window()->GetLayer()->SetMaskLayer(mask_->layer());
+
+    SkRegion* window_shape = window()->GetLayer()->alpha_shape();
+    if (!original_window_shape_ && window_shape)
+      original_window_shape_.reset(new SkRegion(*window_shape));
+    const int inset =
+        window()->GetIntProperty(WmWindowProperty::TOP_VIEW_INSET);
+    if (inset > 0) {
+      bounds.Inset(0, inset, 0, 0);
+      SkRegion* region = new SkRegion;
+      region->setRect(RectToSkIRect(bounds));
+      if (original_window_shape_)
+        region->op(*original_window_shape_, SkRegion::kIntersect_Op);
+      window()->GetLayer()->SetAlphaShape(base::WrapUnique(region));
+      window()->SetMasksToBounds(true);
+    }
   }
 
   gfx::Point target_origin(GetTargetBoundsInScreen().origin());
diff --git a/ash/common/wm/overview/scoped_transform_overview_window.h b/ash/common/wm/overview/scoped_transform_overview_window.h
index 7445d76..e593106 100644
--- a/ash/common/wm/overview/scoped_transform_overview_window.h
+++ b/ash/common/wm/overview/scoped_transform_overview_window.h
@@ -15,6 +15,8 @@
 #include "ui/gfx/geometry/size.h"
 #include "ui/gfx/transform.h"
 
+class SkRegion;
+
 namespace gfx {
 class Rect;
 }
@@ -106,7 +108,7 @@
   // using rounded corners of |radius|.
   void SetTransform(WmWindow* root_window,
                     const gfx::Transform& transform,
-                    int radius);
+                    float radius);
 
   // Set's the opacity of the managed windows.
   void SetOpacity(float opacity);
@@ -135,6 +137,9 @@
   // Mask layer that hides the original window header.
   std::unique_ptr<OverviewContentMask> mask_;
 
+  // Original window shape, if it was set on a window.
+  std::unique_ptr<SkRegion> original_window_shape_;
+
   // If true, the window was minimized and should be restored if the window
   // was not selected.
   bool minimized_;
diff --git a/ash/common/wm/overview/window_grid.cc b/ash/common/wm/overview/window_grid.cc
index acc094f..10003074 100644
--- a/ash/common/wm/overview/window_grid.cc
+++ b/ash/common/wm/overview/window_grid.cc
@@ -783,6 +783,10 @@
   // Immediately finish any active bounds animation.
   window->StopAnimatingProperty(ui::LayerAnimationElement::BOUNDS);
 
+  if (ash::MaterialDesignController::IsOverviewMaterial()) {
+    PositionWindows(false);
+    return;
+  }
   // Recompute the transform for the window.
   (*iter)->RecomputeWindowTransforms();
 }
diff --git a/ash/common/wm/overview/window_selector_item.cc b/ash/common/wm/overview/window_selector_item.cc
index ffa9104..7f58f4a 100644
--- a/ash/common/wm/overview/window_selector_item.cc
+++ b/ash/common/wm/overview/window_selector_item.cc
@@ -477,8 +477,7 @@
   // which when scaled will yield |kLabelBackgroundRadius|.
   transform_window_.SetTransform(
       root_window_, transform,
-      gfx::ToFlooredInt(kLabelBackgroundRadius /
-                        GetItemScale(target_bounds.size())));
+      (kLabelBackgroundRadius / GetItemScale(target_bounds.size())));
   transform_window_.set_overview_transform(transform);
 }
 
diff --git a/ash/mus/user_window_controller_impl.cc b/ash/mus/user_window_controller_impl.cc
index ba64ffb..365acc7 100644
--- a/ash/mus/user_window_controller_impl.cc
+++ b/ash/mus/user_window_controller_impl.cc
@@ -197,10 +197,12 @@
   user_window_observer_->OnUserWindowObserverAdded(std::move(user_windows));
 }
 
-void UserWindowControllerImpl::FocusUserWindow(uint32_t window_id) {
+void UserWindowControllerImpl::ActivateUserWindow(uint32_t window_id) {
   ::ui::Window* window = GetUserWindowById(window_id);
-  if (window)
+  if (window) {
+    window->SetVisible(true);
     window->SetFocus();
+  }
 }
 
 }  // namespace mus
diff --git a/ash/mus/user_window_controller_impl.h b/ash/mus/user_window_controller_impl.h
index 47bcfe3..c3a17f9 100644
--- a/ash/mus/user_window_controller_impl.h
+++ b/ash/mus/user_window_controller_impl.h
@@ -56,7 +56,7 @@
 
   // mojom::UserWindowController:
   void AddUserWindowObserver(mojom::UserWindowObserverPtr observer) override;
-  void FocusUserWindow(uint32_t window_id) override;
+  void ActivateUserWindow(uint32_t window_id) override;
 
   RootWindowController* root_controller_;
   mojom::UserWindowObserverPtr user_window_observer_;
diff --git a/ash/public/interfaces/user_window_controller.mojom b/ash/public/interfaces/user_window_controller.mojom
index 2add8cca..0352ff07 100644
--- a/ash/public/interfaces/user_window_controller.mojom
+++ b/ash/public/interfaces/user_window_controller.mojom
@@ -34,5 +34,5 @@
 // TODO(msw): Add minimization, restoration, opening a chooser view, etc.
 interface UserWindowController {
   AddUserWindowObserver(UserWindowObserver observer);
-  FocusUserWindow(uint32 window_id);
+  ActivateUserWindow(uint32 window_id);
 };
diff --git a/ash/sysui/shelf_delegate_mus.cc b/ash/sysui/shelf_delegate_mus.cc
index 5a925bd1..773a9ad 100644
--- a/ash/sysui/shelf_delegate_mus.cc
+++ b/ash/sysui/shelf_delegate_mus.cc
@@ -94,7 +94,7 @@
       return false;
     }
     void ExecuteCommand(int command_id, int event_flags) override {
-      item_delegate_->user_window_controller_->FocusUserWindow(command_id);
+      item_delegate_->user_window_controller_->ActivateUserWindow(command_id);
     }
 
    private:
@@ -111,7 +111,7 @@
       return kNewWindowCreated;
     }
     if (window_id_to_title_.size() == 1) {
-      user_window_controller_->FocusUserWindow(
+      user_window_controller_->ActivateUserWindow(
           window_id_to_title_.begin()->first);
       return kExistingWindowActivated;
     }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
index 8349b199..bc7d3d0d 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/background/keymaps/next_keymap.json
@@ -570,6 +570,24 @@
           "keyCode": [65, 66]
         }
       }
+    },
+    {
+      "command": "toggleSelection",
+      "sequence": {
+        "cvoxModifier": true,
+        "keys": {
+          "keyCode": [83]
+        }
+      }
+    },
+    {
+      "command": "copy",
+      "sequence": {
+        "keys": {
+          "keyCode": [67],
+          "ctrlKey": [true]
+        }
+      }
     }
   ]
 }
diff --git a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
index cbc4fa7..db0f689 100644
--- a/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
+++ b/chrome/browser/resources/chromeos/chromevox/chromevox/injected/event_watcher.js
@@ -888,8 +888,6 @@
     return true;
   }
 
-  cvox.ChromeVox.tts.speak(Msgs.getMsg(evt.type).toLowerCase(),
-                           cvox.QueueMode.QUEUE);
   var text = '';
   switch (evt.type) {
   case 'paste':
@@ -900,7 +898,8 @@
     text = window.getSelection().toString();
     break;
   }
-  cvox.ChromeVox.tts.speak(text, cvox.QueueMode.QUEUE);
+  cvox.ChromeVox.tts.speak(
+      Msgs.getMsg(evt.type, [text]), cvox.QueueMode.QUEUE);
   cvox.ChromeVox.navigationManager.clearPageSel();
   return true;
 };
diff --git a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
index 5d8213d..de39262 100644
--- a/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
+++ b/chrome/browser/resources/chromeos/chromevox/common/chrome_extension_externs.js
@@ -430,7 +430,7 @@
 
 
 /**
- * @type {chrome.automation.AutomationNode}
+ * @type {chrome.automation.AutomationRootNode}
  */
 chrome.automation.AutomationNode.prototype.root;
 
@@ -618,5 +618,31 @@
  */
 chrome.automation.AutomationNode.prototype.activeDescendant;
 
+/**
+ * @extends {chrome.automation.AutomationNode}
+ * @constructor
+ */
+chrome.automation.AutomationRootNode = function() {};
+
+/**
+ * @type {chrome.automation.AutomationNode}
+ */
+chrome.automation.AutomationRootNode.prototype.anchorObject;
+
+/**
+ * @type {number}
+ */
+chrome.automation.AutomationRootNode.prototype.anchorOffset;
+
+/**
+ * @type {chrome.automation.AutomationNode}
+ */
+chrome.automation.AutomationRootNode.prototype.focusObject;
+
+/**
+ * @type {number}
+ */
+chrome.automation.AutomationRootNode.prototype.focusOffset;
+
 /** @type {function() : !Object} */
 chrome.app.getDetails;
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
index 453b1d2..3748fd1 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js
@@ -796,6 +796,36 @@
         cvox.BrailleCaptionsBackground.setActive(
             !cvox.BrailleCaptionsBackground.isEnabled());
         return false;
+      case 'copy':
+        var textarea = document.createElement('textarea');
+        document.body.appendChild(textarea);
+        textarea.focus();
+        document.execCommand('paste');
+        var clipboardContent = textarea.value;
+        textarea.remove();
+        cvox.ChromeVox.tts.speak(
+            Msgs.getMsg('copy', [clipboardContent]), cvox.QueueMode.FLUSH);
+        this.pageSel_ = null;
+        return true;
+      case 'toggleSelection':
+        if (!this.pageSel_) {
+          this.pageSel_ = this.currentRange;
+        } else {
+          var root = this.currentRange_.start.node.root;
+          if (root && root.anchorObject && root.focusObject) {
+            var sel = new cursors.Range(
+                new cursors.Cursor(root.anchorObject, root.anchorOffset),
+                new cursors.Cursor(root.focusObject, root.focusOffset)
+                );
+            var o = new Output()
+                .format('@end_selection')
+                .withSpeechAndBraille(sel, sel, Output.EventType.NAVIGATE)
+                .go();
+          }
+          this.pageSel_ = null;
+          return false;
+        }
+        break;
       default:
         return true;
     }
@@ -884,12 +914,64 @@
     var prevRange = this.currentRange_;
     this.setCurrentRange(range);
 
-    range.select();
+    var o = new Output();
+    var selectedRange;
+    if (this.pageSel_ &&
+        this.pageSel_.isValid() &&
+        range.isValid()) {
+      // Compute the direction of the endpoints of each range.
 
-    var o = new Output().withRichSpeechAndBraille(
-        range, prevRange, Output.EventType.NAVIGATE)
+      // Casts are ok because isValid checks node start and end nodes are
+      // non-null; Closure just doesn't eval enough to see it.
+      var startDir =
+          AutomationUtil.getDirection(this.pageSel_.start.node,
+              /** @type {!AutomationNode} */ (range.start.node));
+      var endDir =
+          AutomationUtil.getDirection(this.pageSel_.end.node,
+              /** @type {!AutomationNode} */ (range.end.node));
+
+      // Selection across roots isn't supported.
+      var pageRootStart = this.pageSel_.start.node.root;
+      var pageRootEnd = this.pageSel_.end.node.root;
+      var curRootStart = range.start.node.root;
+      var curRootEnd = range.end.node.root;
+
+      // Disallow crossing over the start of the page selection and roots.
+      if (startDir == Dir.BACKWARD ||
+          pageRootStart != pageRootEnd ||
+          pageRootStart != curRootStart ||
+          pageRootEnd != curRootEnd) {
+        o.format('@end_selection');
+        this.pageSel_ = null;
+      } else {
+        // Expand or shrink requires different feedback.
+        var msg;
+        if (endDir == Dir.FORWARD &&
+            (this.pageSel_.end.node != range.end.node ||
+                this.pageSel_.end.index <= range.end.index)) {
+          msg = '@selected';
+        } else {
+          msg = '@unselected';
+          selectedRange = prevRange;
+        }
+        this.pageSel_ = new cursors.Range(
+            this.pageSel_.start,
+            range.end
+            );
+        if (this.pageSel_)
+          this.pageSel_.select();
+      }
+    } else {
+      range.select();
+    }
+
+    o.withRichSpeechAndBraille(
+        selectedRange || range, prevRange, Output.EventType.NAVIGATE)
             .withQueueMode(cvox.QueueMode.FLUSH);
 
+    if (msg)
+      o.format(msg);
+
     for (var prop in opt_speechProps)
       o.format('!' + prop);
 
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
index 847f58a..d995120 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background_test.extjs
@@ -967,3 +967,28 @@
         .replay();
   });
 });
+
+TEST_F('BackgroundTest', 'Selection', function() {
+  var mockFeedback = this.createMockFeedback();
+  this.runWithLoadedTree(function(root) {/*!
+    <p>simple</p>
+    <p>doc</p>
+  */}, function(root) {
+    // Fakes a toggleSelection command.
+    root.addEventListener('textSelectionChanged', function() {
+      if (root.focusOffset == 3)
+        ChromeVoxState.instance.onGotCommand('toggleSelection');
+    }, true);
+
+    mockFeedback.call(doCmd('toggleSelection'))
+        .expectSpeech('simple', 'selected')
+        .call(doCmd('nextCharacter'))
+        .expectSpeech('i', 'selected')
+        .call(doCmd('previousCharacter'))
+        .expectSpeech('i', 'unselected')
+        .call(doCmd('nextCharacter'))
+        .call(doCmd('nextCharacter'))
+        .expectSpeech('End selection', 'sim')
+        .replay();
+  });
+});
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
index 5b33f7dc..b855f909 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors.js
@@ -130,25 +130,61 @@
     return this.index_;
   },
 
+
   /**
-   * An index appropriate for making selections.
+   * A node appropriate for making selections.
+   * @return {AutomationNode}
+   * @private
+   */
+  get selectionNode_() {
+    if (!this.node)
+      return null;
+
+    if (this.node.role == RoleType.inlineTextBox ||
+        this.index == cursors.NODE_INDEX)
+      return this.node.parent;
+
+    return this.node;
+  },
+
+  /**
+   * An index appropriate for making selections.  If this cursor has a
+   * cursors.NODE_INDEX index, the selection index is a node offset e.g. the
+   * index in parent. If not, the index is a character offset.
    * @return {number}
    * @private
    */
   get selectionIndex_() {
-    if (this.index_ == cursors.NODE_INDEX)
-      return 0;
-
     var adjustedIndex = this.index_;
-
     if (this.node.role == RoleType.inlineTextBox) {
+      if (adjustedIndex == cursors.NODE_INDEX)
+        adjustedIndex = 0;
+
       var sibling = this.node.previousSibling;
       while (sibling) {
         adjustedIndex += sibling.name.length;
         sibling = sibling.previousSibling;
       }
-    }
 
+      // Work around Blink's somewhat unexpected offset calculation which
+      // requires us to consider all previous siblings of the parenting static
+      // text.
+      var parent = this.node.parent;
+      if (parent.role == RoleType.staticText) {
+        sibling = parent.previousSibling;
+        while (sibling) {
+          if (sibling.name)
+            adjustedIndex += sibling.name.length;
+          sibling = sibling.previousSibling;
+        }
+      }
+    } else if (this.index_ == cursors.NODE_INDEX) {
+      if (this.index_ == cursors.NODE_INDEX) {
+        // Translate the index into a selection on the parent.
+        if (this.node.parent)
+          adjustedIndex = this.node.indexInParent;
+      }
+    }
     return adjustedIndex;
   },
 
@@ -546,15 +582,15 @@
    * Select the text contained within this range.
    */
   select: function() {
-    var start = this.start.node;
-    var end = this.end.node;
+    var startNode = this.start.selectionNode_;
+    var endNode = this.end.selectionNode_;
 
-    if (!start || !end)
+    if (!startNode || !endNode)
       return;
 
     // Find the most common root.
-    var uniqueAncestors = AutomationUtil.getUniqueAncestors(start, end);
-    var mcr = start.root;
+    var uniqueAncestors = AutomationUtil.getUniqueAncestors(startNode, endNode);
+    var mcr = startNode.root;
     if (uniqueAncestors) {
       var common = uniqueAncestors.pop().parent;
       if (common)
@@ -564,18 +600,19 @@
     if (!mcr || mcr.role == RoleType.desktop)
       return;
 
-    if (mcr === start.root && mcr === end.root) {
-      start = start.role == RoleType.inlineTextBox ? start.parent : start;
-      end = end.role == RoleType.inlineTextBox ? end.parent : end;
+    if (mcr === startNode.root && mcr === endNode.root) {
+      var startIndex = this.start.selectionIndex_;
 
-      if (!start || !end)
-        return;
+      // We want to adjust to select the entire node for node offsets;
+      // otherwise, use the plain character offset.
+      var endIndex = this.end.index == cursors.NODE_INDEX ?
+          this.end.selectionIndex_ + 1 : this.end.selectionIndex_;
 
       chrome.automation.setDocumentSelection(
-          { anchorObject: start,
-            anchorOffset: this.start.selectionIndex_,
-            focusObject: end,
-            focusOffset: this.end.selectionIndex_ }
+          { anchorObject: startNode,
+            anchorOffset: startIndex,
+            focusObject: endNode,
+            focusOffset: endIndex }
       );
     }
   },
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
index b2d30fc..cd09156 100644
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/cursors_test.extjs
@@ -369,9 +369,23 @@
     assertEquals('diff ', secondLine.name);
 
     var secondLineCursor = new cursors.Cursor(secondLine, -1);
-    assertEquals(0, secondLineCursor.selectionIndex_);
-    secondLineCursor = new cursors.Cursor(secondLine, 0);
-    secondLineCursor.index = 0;
+    // The selected node moves to the static text node.
+    assertEquals(secondLineCursor.node.parent,
+                 secondLineCursor.selectionNode_);
+    // This selects the entire node via a character offset.
     assertEquals(6, secondLineCursor.selectionIndex_);
+
+    // Index into the characters.
+    secondLineCursor = new cursors.Cursor(secondLine, 1);
+    assertEquals(7, secondLineCursor.selectionIndex_);
+
+    // Now, try selecting via node offsets.
+    var cursor = new cursors.Cursor(root.firstChild, -1);
+    assertEquals(root, cursor.selectionNode_);
+    assertEquals(0, cursor.selectionIndex_);
+
+    cursor = new cursors.Cursor(root.firstChild.nextSibling, -1);
+    assertEquals(root, cursor.selectionNode_);
+    assertEquals(1, cursor.selectionIndex_);
   });
 });
diff --git a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2 b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
index c2cd99c..9a3f5f3 100644
--- a/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
+++ b/chrome/browser/resources/chromeos/chromevox/manifest.json.jinja2
@@ -16,6 +16,7 @@
     "accessibilityPrivate",
     "bookmarks",
     "brailleDisplayPrivate",
+    "clipboardRead",        
     "commands.accessibility",
     "commandLinePrivate",
     "experimental",
diff --git a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
index a2e714a..8cd5d2958 100644
--- a/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
+++ b/chrome/browser/resources/chromeos/chromevox/strings/chromevox_strings.grd
@@ -1845,13 +1845,13 @@
         Start or end selection.
       </message>
       <message desc="Spoken when the browser's copy command is invoked." name="IDS_CHROMEVOX_COPY">
-        copy.
+        copy $1.
       </message>
       <message desc="Spoken when the browser's cut command is invoked." name="IDS_CHROMEVOX_CUT">
-        cut.
+        cut $1.
       </message>
       <message desc="Spoken when the browser's paste command is invoked." name="IDS_CHROMEVOX_PASTE">
-        paste.
+        paste $1.
       </message>
       <message desc="Spoken when additional characters are selected in editable text." name="IDS_CHROMEVOX_SELECTED">
         selected
diff --git a/chrome/browser/resources/net_internals/quic_view.html b/chrome/browser/resources/net_internals/quic_view.html
index 9ee0590..e5cf4bd 100644
--- a/chrome/browser/resources/net_internals/quic_view.html
+++ b/chrome/browser/resources/net_internals/quic_view.html
@@ -15,6 +15,7 @@
     <li>Idle Connection Timeout In Seconds: <span jscontent="$this.idle_connection_timeout_seconds"></span></li>
     <li>Disable PreConnect If 0RTT: <span jscontent="$this.disable_preconnect_if_0rtt"></span></li>
     <li>Disable QUIC On Timeout With Open Streams: <span jscontent="$this.disable_quic_on_timeout_with_open_streams"></span></li>
+    <li>Race Cert Verification: <span jscontent="!!race_cert_verification"></span></li>
     <li jsdisplay="$this.disabled_reason && disabled_reason.length > 0">QUIC dynamically disabled: <span jscontent="disabled_reason"></span></li>
   </ul>
 
diff --git a/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js b/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js
index cc68ec1..7fd7faef 100644
--- a/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js
+++ b/chrome/test/data/extensions/api_test/uncaught_exception_logging/test.js
@@ -5,8 +5,8 @@
 function createTestFunction(expected_message) {
   return function(tab) {
     function onDebuggerEvent(debuggee, method, params) {
-      if (debuggee.tabId == tab.id && method == 'Console.messageAdded') {
-        if (params.message.text.indexOf(expected_message) > -1) {
+      if (debuggee.tabId == tab.id && method == 'Runtime.exceptionThrown') {
+        if (params.details.text.indexOf(expected_message) > -1) {
           chrome.debugger.onEvent.removeListener(onDebuggerEvent);
           chrome.test.succeed();
         }
@@ -16,7 +16,7 @@
     chrome.debugger.attach({ tabId: tab.id }, "1.1", function() {
       // Enabling console provides both stored and new messages via the
       // Console.messageAdded event.
-      chrome.debugger.sendCommand({ tabId: tab.id }, "Console.enable");
+      chrome.debugger.sendCommand({ tabId: tab.id }, "Runtime.enable");
     });
   }
 }
diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
index ec7d461..a346282c 100644
--- a/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
+++ b/components/cronet/android/test/javatests/src/org/chromium/net/QuicTest.java
@@ -51,7 +51,8 @@
                                         .put("idle_connection_timeout_seconds", 300)
                                         .put("close_sessions_on_ip_change", false)
                                         .put("migrate_sessions_on_network_change", true)
-                                        .put("migrate_sessions_early", true);
+                                        .put("migrate_sessions_early", true)
+                                        .put("race_cert_verification", true);
         JSONObject experimentalOptions = new JSONObject().put("QUIC", quicParams);
         mBuilder.setExperimentalOptions(experimentalOptions.toString());
         mBuilder.setMockCertVerifierForTesting(QuicTestServer.createMockCertVerifier());
diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc
index fa080d2d..61dc9d4 100644
--- a/components/cronet/url_request_context_config.cc
+++ b/components/cronet/url_request_context_config.cc
@@ -53,6 +53,7 @@
 const char kQuicMigrateSessionsEarly[] = "migrate_sessions_early";
 const char kQuicDisableBidirectionalStreams[] =
     "quic_disable_bidirectional_streams";
+const char kQuicRaceCertVerification[] = "race_cert_verification";
 
 // AsyncDNS experiment dictionary name.
 const char kAsyncDnsFieldTrialName[] = "AsyncDNS";
@@ -187,6 +188,13 @@
       context_builder->set_quic_disable_bidirectional_streams(
           quic_disable_bidirectional_streams);
     }
+
+    bool quic_race_cert_verification = false;
+    if (quic_args->GetBoolean(kQuicRaceCertVerification,
+                              &quic_race_cert_verification)) {
+      context_builder->set_quic_race_cert_verification(
+          quic_race_cert_verification);
+    }
   }
 
   const base::DictionaryValue* async_dns_args = nullptr;
diff --git a/components/cronet/url_request_context_config_unittest.cc b/components/cronet/url_request_context_config_unittest.cc
index a0cef2a..d06522291 100644
--- a/components/cronet/url_request_context_config_unittest.cc
+++ b/components/cronet/url_request_context_config_unittest.cc
@@ -46,6 +46,7 @@
       "\"packet_loss_threshold\":0.5,"
       "\"idle_connection_timeout_seconds\":300,"
       "\"close_sessions_on_ip_change\":true,"
+      "\"race_cert_verification\":true,"
       "\"connection_options\":\"TIME,TBBR,REJ\"},"
       "\"AsyncDNS\":{\"enable\":true}}",
       // Data reduction proxy key.
@@ -103,6 +104,9 @@
   EXPECT_TRUE(params->quic_close_sessions_on_ip_change);
   EXPECT_FALSE(params->quic_migrate_sessions_on_network_change);
 
+  // Check race_cert_verification.
+  EXPECT_TRUE(params->quic_race_cert_verification);
+
   // Check AsyncDNS resolver is enabled.
   EXPECT_TRUE(context->host_resolver()->GetDnsConfigAsValue());
 }
diff --git a/content/browser/frame_host/navigation_handle_impl_browsertest.cc b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
index d9ab400..5f5d0c8 100644
--- a/content/browser/frame_host/navigation_handle_impl_browsertest.cc
+++ b/content/browser/frame_host/navigation_handle_impl_browsertest.cc
@@ -21,9 +21,12 @@
 
 namespace {
 
+// Gathers data from the NavigationHandle assigned to navigations that start
+// with the expected URL.
 class NavigationHandleObserver : public WebContentsObserver {
  public:
-  NavigationHandleObserver(WebContents* web_contents, const GURL& expected_url)
+  NavigationHandleObserver(WebContents* web_contents,
+                           const GURL& expected_start_url)
       : WebContentsObserver(web_contents),
         handle_(nullptr),
         has_committed_(false),
@@ -36,10 +39,10 @@
         was_redirected_(false),
         frame_tree_node_id_(-1),
         page_transition_(ui::PAGE_TRANSITION_LINK),
-        expected_url_(expected_url) {}
+        expected_start_url_(expected_start_url) {}
 
   void DidStartNavigation(NavigationHandle* navigation_handle) override {
-    if (handle_ || navigation_handle->GetURL() != expected_url_)
+    if (handle_ || navigation_handle->GetURL() != expected_start_url_)
       return;
 
     handle_ = navigation_handle;
@@ -115,12 +118,13 @@
   bool was_redirected_;
   int frame_tree_node_id_;
   ui::PageTransition page_transition_;
-  GURL expected_url_;
+  GURL expected_start_url_;
   GURL last_committed_url_;
 };
 
 // A test NavigationThrottle that will return pre-determined checks and run
-// callbacks when the various NavigationThrottle methods are called.
+// callbacks when the various NavigationThrottle methods are called. It is
+// not instantiated directly but through a TestNavigationThrottleInstaller.
 class TestNavigationThrottle : public NavigationThrottle {
  public:
   TestNavigationThrottle(
@@ -169,8 +173,10 @@
   base::Closure did_call_will_process_;
 };
 
-// Install a TestNavigationThrottle on all requests and allows waiting for
-// various NavigationThrottle related events.
+// Install a TestNavigationThrottle on all following requests and allows waiting
+// for various NavigationThrottle related events. Waiting works only for the
+// immediately next navigation. New instances are needed to wait for further
+// navigations.
 class TestNavigationThrottleInstaller : public WebContentsObserver {
  public:
   TestNavigationThrottleInstaller(
@@ -270,6 +276,22 @@
   scoped_refptr<MessageLoopRunner> will_process_loop_runner_;
 };
 
+// Records all navigation start URLs from the WebContents.
+class NavigationStartUrlRecorder : public WebContentsObserver {
+ public:
+  NavigationStartUrlRecorder(WebContents* web_contents)
+      : WebContentsObserver(web_contents) {}
+
+  void DidStartNavigation(NavigationHandle* navigation_handle) override {
+    urls_.push_back(navigation_handle->GetURL());
+  }
+
+  const std::vector<GURL>& urls() const { return urls_; }
+
+ private:
+  std::vector<GURL> urls_;
+};
+
 }  // namespace
 
 class NavigationHandleImplBrowserTest : public ContentBrowserTest {
@@ -632,4 +654,67 @@
             GURL(embedded_test_server()->GetURL("bar.com", "/title2.html")));
 }
 
+// Specialized test that verifies the NavigationHandle gets the HTTPS upgraded
+// URL from the very beginning of the navigation.
+class NavigationHandleImplHttpsUpgradeBrowserTest
+    : public NavigationHandleImplBrowserTest {
+ public:
+  void CheckHttpsUpgradedIframeNavigation(const GURL& start_url,
+                                          const GURL& iframe_secure_url) {
+    ASSERT_TRUE(start_url.SchemeIs(url::kHttpScheme));
+    ASSERT_TRUE(iframe_secure_url.SchemeIs(url::kHttpsScheme));
+
+    NavigationStartUrlRecorder url_recorder(shell()->web_contents());
+    TestNavigationThrottleInstaller installer(
+        shell()->web_contents(), NavigationThrottle::PROCEED,
+        NavigationThrottle::PROCEED, NavigationThrottle::PROCEED);
+    TestNavigationManager navigation_manager(shell()->web_contents(),
+                                             iframe_secure_url);
+
+    // Load the page and wait for the frame load with the expected URL.
+    // Note: if the test times out while waiting then a navigation to
+    // iframe_secure_url never happened and the expected upgrade may not be
+    // working.
+    shell()->LoadURL(start_url);
+    navigation_manager.WaitForWillStartRequest();
+
+    // The main frame should have finished navigating while the iframe should
+    // have just started.
+    EXPECT_EQ(2, installer.will_start_called());
+    EXPECT_EQ(0, installer.will_redirect_called());
+    EXPECT_EQ(1, installer.will_process_called());
+
+    // Check the correct start URLs have been registered.
+    EXPECT_EQ(iframe_secure_url, url_recorder.urls().back());
+    EXPECT_EQ(start_url, url_recorder.urls().front());
+    EXPECT_EQ(2ul, url_recorder.urls().size());
+  }
+};
+
+// Tests that the start URL is HTTPS upgraded for a same site navigation.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplHttpsUpgradeBrowserTest,
+                       StartUrlIsHttpsUpgradedSameSite) {
+  GURL start_url(
+      embedded_test_server()->GetURL("/https_upgrade_same_site.html"));
+
+  // Builds the expected upgraded same site URL.
+  GURL::Replacements replace_scheme;
+  replace_scheme.SetSchemeStr("https");
+  GURL cross_site_iframe_secure_url = embedded_test_server()
+                                          ->GetURL("/title1.html")
+                                          .ReplaceComponents(replace_scheme);
+
+  CheckHttpsUpgradedIframeNavigation(start_url, cross_site_iframe_secure_url);
+}
+
+// Tests that the start URL is HTTPS upgraded for a cross site navigation.
+IN_PROC_BROWSER_TEST_F(NavigationHandleImplHttpsUpgradeBrowserTest,
+                       StartUrlIsHttpsUpgradedCrossSite) {
+  GURL start_url(
+      embedded_test_server()->GetURL("/https_upgrade_cross_site.html"));
+  GURL cross_site_iframe_secure_url("https://other.com/title1.html");
+
+  CheckHttpsUpgradedIframeNavigation(start_url, cross_site_iframe_secure_url);
+}
+
 }  // namespace content
diff --git a/content/test/data/https_upgrade_cross_site.html b/content/test/data/https_upgrade_cross_site.html
new file mode 100644
index 0000000..4591ec1
--- /dev/null
+++ b/content/test/data/https_upgrade_cross_site.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<title>Cross Site HTTPS upgrade</title>
+
+<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+
+<body>
+  <iframe src="http://other.com/title1.html"></iframe>
+</body>
+</html>
diff --git a/content/test/data/https_upgrade_same_site.html b/content/test/data/https_upgrade_same_site.html
new file mode 100644
index 0000000..bf1dd6e
--- /dev/null
+++ b/content/test/data/https_upgrade_same_site.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<title>Same Site HTTPS upgrade</title>
+
+<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
+
+<body>
+  <iframe src="/title1.html"></iframe>
+</body>
+</html>
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index 06651e96..532f5b1 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -129,6 +129,7 @@
       quic_migrate_sessions_early(false),
       quic_disable_bidirectional_streams(false),
       quic_force_hol_blocking(false),
+      quic_race_cert_verification(false),
       proxy_delegate(NULL),
       enable_token_binding(false) {
   quic_supported_versions.push_back(QUIC_VERSION_34);
@@ -188,6 +189,7 @@
           params.quic_migrate_sessions_on_network_change,
           params.quic_migrate_sessions_early,
           params.quic_force_hol_blocking,
+          params.quic_race_cert_verification,
           params.quic_connection_options,
           params.enable_token_binding),
       spdy_session_pool_(params.host_resolver,
@@ -340,6 +342,8 @@
   dict->SetString("disabled_reason",
                   quic_stream_factory_.QuicDisabledReasonString());
   dict->SetBoolean("force_hol_blocking", params_.quic_force_hol_blocking);
+  dict->SetBoolean("race_cert_verification",
+                   params_.quic_race_cert_verification);
   return std::move(dict);
 }
 
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index b4a72ea..2a40a0c 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -180,6 +180,8 @@
     bool quic_disable_bidirectional_streams;
     // If true, enable force HOL blocking.  For measurement purposes.
     bool quic_force_hol_blocking;
+    // If true, race cert verification with host resolution.
+    bool quic_race_cert_verification;
 
     ProxyDelegate* proxy_delegate;
     // Enable support for Token Binding.
diff --git a/net/quic/crypto/proof_verifier.h b/net/quic/crypto/proof_verifier.h
index d05e1dd..c551e40 100644
--- a/net/quic/crypto/proof_verifier.h
+++ b/net/quic/crypto/proof_verifier.h
@@ -86,6 +86,26 @@
       std::string* error_details,
       std::unique_ptr<ProofVerifyDetails>* details,
       std::unique_ptr<ProofVerifierCallback> callback) = 0;
+
+  // VerifyCertChain checks that |certs| is a valid chain for |hostname|. On
+  // success, it returns QUIC_SUCCESS. On failure, it returns QUIC_FAILURE and
+  // sets |*error_details| to a description of the problem. In either case it
+  // may set |*details|, which the caller takes ownership of.
+  //
+  // |context| specifies an implementation specific struct (which may be nullptr
+  // for some implementations) that provides useful information for the
+  // verifier, e.g. logging handles.
+  //
+  // This function may also return QUIC_PENDING, in which case the ProofVerifier
+  // will call back, on the original thread, via |callback| when complete.
+  // In this case, the ProofVerifier will take ownership of |callback|.
+  virtual QuicAsyncStatus VerifyCertChain(
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
+      const ProofVerifyContext* context,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* details,
+      std::unique_ptr<ProofVerifierCallback> callback) = 0;
 };
 
 }  // namespace net
diff --git a/net/quic/crypto/proof_verifier_chromium.cc b/net/quic/crypto/proof_verifier_chromium.cc
index 621eb1f..61eea5a 100644
--- a/net/quic/crypto/proof_verifier_chromium.cc
+++ b/net/quic/crypto/proof_verifier_chromium.cc
@@ -78,6 +78,16 @@
       std::unique_ptr<ProofVerifyDetails>* verify_details,
       std::unique_ptr<ProofVerifierCallback> callback);
 
+  // Starts the certificate chain verification of |certs|.  If |QUIC_PENDING| is
+  // returned, then |callback| will be invoked asynchronously when the
+  // verification completes.
+  QuicAsyncStatus VerifyCertChain(
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback);
+
  private:
   enum State {
     STATE_NONE,
@@ -85,6 +95,19 @@
     STATE_VERIFY_CERT_COMPLETE,
   };
 
+  // Convert |certs| to |cert_|(X509Certificate). Returns true if successful.
+  bool GetX509Certificate(const vector<string>& certs,
+                          std::string* error_details,
+                          std::unique_ptr<ProofVerifyDetails>* verify_details);
+
+  // Start the cert verification.
+  QuicAsyncStatus VerifyCert(
+      const string& hostname,
+      const uint16_t port,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback);
+
   int DoLoop(int last_io_result);
   void OnIOComplete(int result);
   int DoVerifyCert(int result);
@@ -125,6 +148,9 @@
   // passed to CertVerifier::Verify.
   int cert_verify_flags_;
 
+  // If set to true, enforces policy checking in DoVerifyCertComplete().
+  bool enforce_policy_checking_;
+
   State next_state_;
 
   base::TimeTicks start_time_;
@@ -148,6 +174,7 @@
       transport_security_state_(transport_security_state),
       cert_transparency_verifier_(cert_transparency_verifier),
       cert_verify_flags_(cert_verify_flags),
+      enforce_policy_checking_(true),
       next_state_(STATE_NONE),
       start_time_(base::TimeTicks::Now()),
       net_log_(net_log) {
@@ -195,27 +222,9 @@
 
   verify_details_.reset(new ProofVerifyDetailsChromium);
 
-  if (certs.empty()) {
-    *error_details = "Failed to create certificate chain. Certs are empty.";
-    DLOG(WARNING) << *error_details;
-    verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
-    *verify_details = std::move(verify_details_);
+  // Converts |certs| to |cert_|.
+  if (!GetX509Certificate(certs, error_details, verify_details))
     return QUIC_FAILURE;
-  }
-
-  // Convert certs to X509Certificate.
-  vector<StringPiece> cert_pieces(certs.size());
-  for (unsigned i = 0; i < certs.size(); i++) {
-    cert_pieces[i] = base::StringPiece(certs[i]);
-  }
-  cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces);
-  if (!cert_.get()) {
-    *error_details = "Failed to create certificate chain";
-    DLOG(WARNING) << *error_details;
-    verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
-    *verify_details = std::move(verify_details_);
-    return QUIC_FAILURE;
-  }
 
   if (!cert_sct.empty()) {
     // Note that this is a completely synchronous operation: The CT Log Verifier
@@ -237,6 +246,75 @@
     return QUIC_FAILURE;
   }
 
+  DCHECK(enforce_policy_checking_);
+  return VerifyCert(hostname, port, error_details, verify_details,
+                    std::move(callback));
+}
+
+QuicAsyncStatus ProofVerifierChromium::Job::VerifyCertChain(
+    const string& hostname,
+    const vector<string>& certs,
+    std::string* error_details,
+    std::unique_ptr<ProofVerifyDetails>* verify_details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
+  DCHECK(error_details);
+  DCHECK(verify_details);
+  DCHECK(callback);
+
+  error_details->clear();
+
+  if (STATE_NONE != next_state_) {
+    *error_details = "Certificate is already set and VerifyCertChain has begun";
+    DLOG(DFATAL) << *error_details;
+    return QUIC_FAILURE;
+  }
+
+  verify_details_.reset(new ProofVerifyDetailsChromium);
+
+  // Converts |certs| to |cert_|.
+  if (!GetX509Certificate(certs, error_details, verify_details))
+    return QUIC_FAILURE;
+
+  enforce_policy_checking_ = false;
+  // |port| is not needed because |enforce_policy_checking_| is false.
+  return VerifyCert(hostname, /*port=*/0, error_details, verify_details,
+                    std::move(callback));
+}
+
+bool ProofVerifierChromium::Job::GetX509Certificate(
+    const vector<string>& certs,
+    std::string* error_details,
+    std::unique_ptr<ProofVerifyDetails>* verify_details) {
+  if (certs.empty()) {
+    *error_details = "Failed to create certificate chain. Certs are empty.";
+    DLOG(WARNING) << *error_details;
+    verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
+    *verify_details = std::move(verify_details_);
+    return false;
+  }
+
+  // Convert certs to X509Certificate.
+  vector<StringPiece> cert_pieces(certs.size());
+  for (unsigned i = 0; i < certs.size(); i++) {
+    cert_pieces[i] = base::StringPiece(certs[i]);
+  }
+  cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces);
+  if (!cert_.get()) {
+    *error_details = "Failed to create certificate chain";
+    DLOG(WARNING) << *error_details;
+    verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID;
+    *verify_details = std::move(verify_details_);
+    return false;
+  }
+  return true;
+}
+
+QuicAsyncStatus ProofVerifierChromium::Job::VerifyCert(
+    const string& hostname,
+    const uint16_t port,
+    std::string* error_details,
+    std::unique_ptr<ProofVerifyDetails>* verify_details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
   hostname_ = hostname;
   port_ = port;
 
@@ -315,7 +393,8 @@
 
   // If the connection was good, check HPKP and CT status simultaneously,
   // but prefer to treat the HPKP error as more serious, if there was one.
-  if ((result == OK ||
+  if (enforce_policy_checking_ &&
+      (result == OK ||
        (IsCertificateError(result) && IsCertStatusMinorError(cert_status)))) {
     if ((cert_verify_result.cert_status & CERT_STATUS_IS_EV)) {
       ct::EVPolicyCompliance ev_policy_compliance =
@@ -505,9 +584,32 @@
   QuicAsyncStatus status = job->VerifyProof(
       hostname, port, server_config, quic_version, chlo_hash, certs, cert_sct,
       signature, error_details, verify_details, std::move(callback));
-  if (status == QUIC_PENDING) {
+  if (status == QUIC_PENDING)
     active_jobs_.insert(job.release());
+  return status;
+}
+
+QuicAsyncStatus ProofVerifierChromium::VerifyCertChain(
+    const std::string& hostname,
+    const std::vector<std::string>& certs,
+    const ProofVerifyContext* verify_context,
+    std::string* error_details,
+    std::unique_ptr<ProofVerifyDetails>* verify_details,
+    std::unique_ptr<ProofVerifierCallback> callback) {
+  if (!verify_context) {
+    *error_details = "Missing context";
+    return QUIC_FAILURE;
   }
+  const ProofVerifyContextChromium* chromium_context =
+      reinterpret_cast<const ProofVerifyContextChromium*>(verify_context);
+  std::unique_ptr<Job> job(
+      new Job(this, cert_verifier_, ct_policy_enforcer_,
+              transport_security_state_, cert_transparency_verifier_,
+              chromium_context->cert_verify_flags, chromium_context->net_log));
+  QuicAsyncStatus status = job->VerifyCertChain(
+      hostname, certs, error_details, verify_details, std::move(callback));
+  if (status == QUIC_PENDING)
+    active_jobs_.insert(job.release());
   return status;
 }
 
diff --git a/net/quic/crypto/proof_verifier_chromium.h b/net/quic/crypto/proof_verifier_chromium.h
index b15751c..ee9ddab 100644
--- a/net/quic/crypto/proof_verifier_chromium.h
+++ b/net/quic/crypto/proof_verifier_chromium.h
@@ -85,6 +85,13 @@
       std::string* error_details,
       std::unique_ptr<ProofVerifyDetails>* verify_details,
       std::unique_ptr<ProofVerifierCallback> callback) override;
+  QuicAsyncStatus VerifyCertChain(
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
+      const ProofVerifyContext* verify_context,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback) override;
 
  private:
   class Job;
diff --git a/net/quic/crypto/proof_verifier_chromium_test.cc b/net/quic/crypto/proof_verifier_chromium_test.cc
index fff1bf6d..47d2637 100644
--- a/net/quic/crypto/proof_verifier_chromium_test.cc
+++ b/net/quic/crypto/proof_verifier_chromium_test.cc
@@ -587,5 +587,34 @@
               CERT_STATUS_CERTIFICATE_TRANSPARENCY_REQUIRED);
 }
 
+// Tests that the VerifyCertChain verifies certificates.
+TEST_F(ProofVerifierChromiumTest, VerifyCertChain) {
+  scoped_refptr<X509Certificate> test_cert = GetTestServerCertificate();
+  ASSERT_TRUE(test_cert);
+
+  CertVerifyResult dummy_result;
+  dummy_result.verified_cert = test_cert;
+  dummy_result.cert_status = 0;
+
+  MockCertVerifier dummy_verifier;
+  dummy_verifier.AddResultForCert(test_cert.get(), dummy_result, OK);
+
+  ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_,
+                                       &transport_security_state_,
+                                       ct_verifier_.get());
+
+  std::unique_ptr<DummyProofVerifierCallback> callback(
+      new DummyProofVerifierCallback);
+  QuicAsyncStatus status = proof_verifier.VerifyCertChain(
+      kTestHostname, certs_, verify_context_.get(), &error_details_, &details_,
+      std::move(callback));
+  ASSERT_EQ(QUIC_SUCCESS, status);
+
+  ASSERT_TRUE(details_.get());
+  ProofVerifyDetailsChromium* verify_details =
+      static_cast<ProofVerifyDetailsChromium*>(details_.get());
+  EXPECT_EQ(0u, verify_details->cert_verify_result.cert_status);
+}
+
 }  // namespace test
 }  // namespace net
diff --git a/net/quic/quic_stream_factory.cc b/net/quic/quic_stream_factory.cc
index 91e442d..e3aa922 100644
--- a/net/quic/quic_stream_factory.cc
+++ b/net/quic/quic_stream_factory.cc
@@ -34,6 +34,7 @@
 #include "net/http/bidirectional_stream_impl.h"
 #include "net/quic/bidirectional_stream_quic_impl.h"
 #include "net/quic/crypto/channel_id_chromium.h"
+#include "net/quic/crypto/proof_verifier.h"
 #include "net/quic/crypto/proof_verifier_chromium.h"
 #include "net/quic/crypto/properties_based_quic_server_info.h"
 #include "net/quic/crypto/quic_random.h"
@@ -167,6 +168,88 @@
 
 }  // namespace
 
+// Responsible for verifying the certificates saved in
+// QuicCryptoClientConfig, and for notifying any associated requests when
+// complete. Results from cert verification are ignored.
+class QuicStreamFactory::CertVerifierJob {
+ public:
+  // ProofVerifierCallbackImpl is passed as the callback method to
+  // VerifyCertChain. The ProofVerifier calls this class with the result of cert
+  // verification when verification is performed asynchronously.
+  class ProofVerifierCallbackImpl : public ProofVerifierCallback {
+   public:
+    explicit ProofVerifierCallbackImpl(CertVerifierJob* job) : job_(job) {}
+
+    ~ProofVerifierCallbackImpl() override {}
+
+    void Run(bool ok,
+             const std::string& error_details,
+             std::unique_ptr<ProofVerifyDetails>* details) override {
+      if (job_ == nullptr)
+        return;
+      job_->verify_callback_ = nullptr;
+      job_->OnComplete();
+    }
+
+    void Cancel() { job_ = nullptr; }
+
+   private:
+    CertVerifierJob* job_;
+  };
+
+  CertVerifierJob(const QuicServerId& server_id,
+                  int cert_verify_flags,
+                  const BoundNetLog& net_log)
+      : server_id_(server_id),
+        verify_callback_(nullptr),
+        verify_context_(base::WrapUnique(
+            new ProofVerifyContextChromium(cert_verify_flags, net_log))),
+        net_log_(net_log),
+        weak_factory_(this) {}
+
+  ~CertVerifierJob() {
+    if (verify_callback_)
+      verify_callback_->Cancel();
+  }
+
+  // Starts verification of certs cached in the |crypto_config|.
+  QuicAsyncStatus Run(QuicCryptoClientConfig* crypto_config,
+                      const CompletionCallback& callback) {
+    QuicCryptoClientConfig::CachedState* cached =
+        crypto_config->LookupOrCreate(server_id_);
+    ProofVerifierCallbackImpl* verify_callback =
+        new ProofVerifierCallbackImpl(this);
+    QuicAsyncStatus status = crypto_config->proof_verifier()->VerifyCertChain(
+        server_id_.host(), cached->certs(), verify_context_.get(),
+        &verify_error_details_, &verify_details_,
+        std::unique_ptr<ProofVerifierCallback>(verify_callback));
+    if (status == QUIC_PENDING) {
+      verify_callback_ = verify_callback;
+      callback_ = callback;
+    }
+    return status;
+  }
+
+  void OnComplete() {
+    if (!callback_.is_null())
+      callback_.Run(OK);
+  }
+
+  const QuicServerId& server_id() const { return server_id_; }
+
+ private:
+  QuicServerId server_id_;
+  ProofVerifierCallbackImpl* verify_callback_;
+  std::unique_ptr<ProofVerifyContext> verify_context_;
+  std::unique_ptr<ProofVerifyDetails> verify_details_;
+  std::string verify_error_details_;
+  const BoundNetLog net_log_;
+  CompletionCallback callback_;
+  base::WeakPtrFactory<CertVerifierJob> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(CertVerifierJob);
+};
+
 // Responsible for creating a new QUIC session to the specified server, and
 // for notifying any associated requests when complete.
 class QuicStreamFactory::Job {
@@ -364,9 +447,8 @@
 
 int QuicStreamFactory::Job::DoResolveHost() {
   // Start loading the data now, and wait for it after we resolve the host.
-  if (server_info_) {
+  if (server_info_)
     server_info_->Start();
-  }
 
   io_state_ = STATE_RESOLVE_HOST_COMPLETE;
   dns_resolution_start_time_ = base::TimeTicks::Now();
@@ -388,9 +470,8 @@
 
   // Inform the factory of this resolution, which will set up
   // a session alias, if possible.
-  if (factory_->OnResolution(key_, address_list_)) {
+  if (factory_->OnResolution(key_, address_list_))
     return OK;
-  }
 
   if (server_info_)
     io_state_ = STATE_LOAD_SERVER_INFO;
@@ -469,14 +550,12 @@
     return rv;
   }
 
-  if (!session_->connection()->connected()) {
+  if (!session_->connection()->connected())
     return ERR_CONNECTION_CLOSED;
-  }
 
   session_->StartReading();
-  if (!session_->connection()->connected()) {
+  if (!session_->connection()->connected())
     return ERR_QUIC_PROTOCOL_ERROR;
-  }
   bool require_confirmation = factory_->require_confirmation() ||
                               was_alternative_service_recently_broken_;
 
@@ -485,8 +564,9 @@
       base::Bind(&QuicStreamFactory::Job::OnIOComplete, GetWeakPtr()));
 
   if (!session_->connection()->connected() &&
-      session_->error() == QUIC_PROOF_INVALID)
+      session_->error() == QUIC_PROOF_INVALID) {
     return ERR_QUIC_HANDSHAKE_FAILED;
+  }
 
   return rv;
 }
@@ -503,9 +583,8 @@
 int QuicStreamFactory::Job::DoConnectComplete(int rv) {
   if (session_ && session_->error() == QUIC_CRYPTO_HANDSHAKE_STATELESS_REJECT) {
     num_sent_client_hellos_ += session_->GetNumSentClientHellos();
-    if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos) {
+    if (num_sent_client_hellos_ >= QuicCryptoClientStream::kMaxClientHellos)
       return ERR_QUIC_HANDSHAKE_FAILED;
-    }
     // The handshake was rejected statelessly, so create another connection
     // to resume the handshake.
     io_state_ = STATE_CONNECT;
@@ -633,6 +712,7 @@
     bool migrate_sessions_on_network_change,
     bool migrate_sessions_early,
     bool force_hol_blocking,
+    bool race_cert_verification,
     const QuicTagVector& connection_options,
     bool enable_token_binding)
     : require_confirmation_(true),
@@ -687,6 +767,7 @@
       migrate_sessions_early_(migrate_sessions_early &&
                               migrate_sessions_on_network_change_),
       force_hol_blocking_(force_hol_blocking),
+      race_cert_verification_(race_cert_verification),
       port_seed_(random_generator_->RandUint64()),
       check_persisted_supports_quic_(true),
       has_initialized_data_(false),
@@ -754,6 +835,8 @@
     STLDeleteElements(&(active_jobs_[server_id]));
     active_jobs_.erase(server_id);
   }
+  while (!active_cert_verifier_jobs_.empty())
+    active_cert_verifier_jobs_.erase(active_cert_verifier_jobs_.begin());
   if (ssl_config_service_.get())
     ssl_config_service_->RemoveObserver(this);
   if (migrate_sessions_on_network_change_) {
@@ -885,11 +968,12 @@
       // don't wait for Cache thread to load the data for that server.
       load_from_disk_cache = false;
     }
-    if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id)) {
+    if (load_from_disk_cache && CryptoConfigCacheIsEmpty(server_id))
       quic_server_info = quic_server_info_factory_->GetForServer(server_id);
-    }
   }
 
+  ignore_result(StartCertVerifyJob(server_id, cert_verify_flags, net_log));
+
   QuicSessionKey key(destination, server_id);
   std::unique_ptr<Job> job(
       new Job(this, host_resolver_, key, WasQuicRecentlyBroken(server_id),
@@ -950,9 +1034,8 @@
                                      const AddressList& address_list) {
   const QuicServerId& server_id(key.server_id());
   DCHECK(!HasActiveSession(server_id));
-  if (disable_connection_pooling_) {
+  if (disable_connection_pooling_)
     return false;
-  }
   for (const IPEndPoint& address : address_list) {
     if (!ContainsKey(ip_aliases_, address))
       continue;
@@ -1022,6 +1105,10 @@
   job_requests_map_.erase(server_id);
 }
 
+void QuicStreamFactory::OnCertVerifyJobComplete(CertVerifierJob* job, int rv) {
+  active_cert_verifier_jobs_.erase(job->server_id());
+}
+
 std::unique_ptr<QuicHttpStream> QuicStreamFactory::CreateFromSession(
     QuicChromiumClientSession* session) {
   return std::unique_ptr<QuicHttpStream>(
@@ -1137,9 +1224,8 @@
     DCHECK_EQ(session, active_sessions_[server_id]);
     // Track sessions which have recently gone away so that we can disable
     // port suggestions.
-    if (session->goaway_received()) {
+    if (session->goaway_received())
       gone_away_aliases_.insert(*it);
-    }
 
     active_sessions_.erase(server_id);
     ProcessGoingAwaySession(session, server_id, true);
@@ -1148,9 +1234,8 @@
   if (!aliases.empty()) {
     const IPEndPoint peer_address = session->connection()->peer_address();
     ip_aliases_[peer_address].erase(session);
-    if (ip_aliases_[peer_address].empty()) {
+    if (ip_aliases_[peer_address].empty())
       ip_aliases_.erase(peer_address);
-    }
   }
   session_aliases_.erase(session);
 }
@@ -1255,9 +1340,8 @@
     QuicChromiumClientSession* session) {
   const AliasSet& aliases = session_aliases_[session];
 
-  if (aliases.empty()) {
+  if (aliases.empty())
     return;
-  }
 
   for (const QuicSessionKey& key : aliases) {
     const QuicServerId& server_id = key.server_id();
@@ -1269,9 +1353,8 @@
 
   const IPEndPoint peer_address = session->connection()->peer_address();
   ip_aliases_[peer_address].erase(session);
-  if (ip_aliases_[peer_address].empty()) {
+  if (ip_aliases_[peer_address].empty())
     ip_aliases_.erase(peer_address);
-  }
   QuicSessionKey key = *aliases.begin();
   session_aliases_.erase(session);
   Job* job = new Job(this, host_resolver_, session, key);
@@ -1367,9 +1450,8 @@
   NetworkChangeNotifier::NetworkList network_list;
   NetworkChangeNotifier::GetConnectedNetworks(&network_list);
   for (NetworkHandle new_network : network_list) {
-    if (new_network != old_network) {
+    if (new_network != old_network)
       return new_network;
-    }
   }
   return NetworkChangeNotifier::kInvalidNetworkHandle;
 }
@@ -1557,6 +1639,11 @@
   return ContainsKey(active_jobs_, server_id);
 }
 
+bool QuicStreamFactory::HasActiveCertVerifierJob(
+    const QuicServerId& server_id) const {
+  return ContainsKey(active_cert_verifier_jobs_, server_id);
+}
+
 int QuicStreamFactory::ConfigureSocket(DatagramClientSocket* socket,
                                        IPEndPoint addr,
                                        NetworkHandle network) {
@@ -1645,15 +1732,13 @@
   // Passing in kInvalidNetworkHandle binds socket to default network.
   int rv = ConfigureSocket(socket.get(), addr,
                            NetworkChangeNotifier::kInvalidNetworkHandle);
-  if (rv != OK) {
+  if (rv != OK)
     return rv;
-  }
 
-  if (enable_port_selection) {
+  if (enable_port_selection)
     DCHECK_LE(1u, port_suggester->call_count());
-  } else {
+  else
     DCHECK_EQ(0u, port_suggester->call_count());
-  }
 
   if (!helper_.get()) {
     helper_.reset(
@@ -1767,6 +1852,29 @@
   return cached->IsEmpty();
 }
 
+QuicAsyncStatus QuicStreamFactory::StartCertVerifyJob(
+    const QuicServerId& server_id,
+    int cert_verify_flags,
+    const BoundNetLog& net_log) {
+  if (!race_cert_verification_)
+    return QUIC_FAILURE;
+  QuicCryptoClientConfig::CachedState* cached =
+      crypto_config_.LookupOrCreate(server_id);
+  if (!cached || cached->certs().empty() ||
+      HasActiveCertVerifierJob(server_id)) {
+    return QUIC_FAILURE;
+  }
+  std::unique_ptr<CertVerifierJob> cert_verifier_job(
+      new CertVerifierJob(server_id, cert_verify_flags, net_log));
+  QuicAsyncStatus status = cert_verifier_job->Run(
+      &crypto_config_,
+      base::Bind(&QuicStreamFactory::OnCertVerifyJobComplete,
+                 base::Unretained(this), cert_verifier_job.get()));
+  if (status == QUIC_PENDING)
+    active_cert_verifier_jobs_[server_id] = std::move(cert_verifier_job);
+  return status;
+}
+
 void QuicStreamFactory::InitializeCachedStateInCryptoConfig(
     const QuicServerId& server_id,
     const std::unique_ptr<QuicServerInfo>& server_info,
diff --git a/net/quic/quic_stream_factory.h b/net/quic/quic_stream_factory.h
index fbb91ab..7fcb960 100644
--- a/net/quic/quic_stream_factory.h
+++ b/net/quic/quic_stream_factory.h
@@ -193,6 +193,7 @@
       bool migrate_sessions_on_network_change,
       bool migrate_sessions_early,
       bool force_hol_blocking,
+      bool race_cert_verification,
       const QuicTagVector& connection_options,
       bool enable_token_binding);
   ~QuicStreamFactory() override;
@@ -365,6 +366,7 @@
 
  private:
   class Job;
+  class CertVerifierJob;
   friend class test::QuicStreamFactoryPeer;
   FRIEND_TEST_ALL_PREFIXES(HttpStreamFactoryTest, QuicLossyProxyMarkedAsBad);
 
@@ -382,6 +384,8 @@
   typedef std::map<QuicServerId, RequestSet> ServerIDRequestsMap;
   typedef std::deque<enum QuicChromiumClientSession::QuicDisabledReason>
       DisabledReasonsQueue;
+  typedef std::map<QuicServerId, std::unique_ptr<CertVerifierJob>>
+      CertVerifierJobMap;
 
   enum FactoryStatus {
     OPEN,     // New streams may be created.
@@ -401,8 +405,10 @@
 
   bool OnResolution(const QuicSessionKey& key, const AddressList& address_list);
   void OnJobComplete(Job* job, int rv);
+  void OnCertVerifyJobComplete(CertVerifierJob* job, int rv);
   bool HasActiveSession(const QuicServerId& server_id) const;
   bool HasActiveJob(const QuicServerId& server_id) const;
+  bool HasActiveCertVerifierJob(const QuicServerId& server_id) const;
   int CreateSession(const QuicSessionKey& key,
                     int cert_verify_flags,
                     std::unique_ptr<QuicServerInfo> quic_server_info,
@@ -424,6 +430,13 @@
 
   bool CryptoConfigCacheIsEmpty(const QuicServerId& server_id);
 
+  // Starts an asynchronous job for cert verification if
+  // |race_cert_verification_| is enabled and if there are cached certs for the
+  // given |server_id|.
+  QuicAsyncStatus StartCertVerifyJob(const QuicServerId& server_id,
+                                     int cert_verify_flags,
+                                     const BoundNetLog& net_log);
+
   // Initializes the cached state associated with |server_id| in
   // |crypto_config_| with the information in |server_info|. Populates
   // |connection_id| with the next server designated connection id,
@@ -491,6 +504,8 @@
   ServerIDRequestsMap job_requests_map_;
   RequestMap active_requests_;
 
+  CertVerifierJobMap active_cert_verifier_jobs_;
+
   QuicVersionVector supported_versions_;
 
   // Determine if we should consistently select a client UDP port. If false,
@@ -576,6 +591,9 @@
   // If set, force HOL blocking.  For measurement purposes.
   const bool force_hol_blocking_;
 
+  // Set if cert verification is to be raced with host resolution.
+  bool race_cert_verification_;
+
   // Each profile will (probably) have a unique port_seed_ value.  This value
   // is used to help seed a pseudo-random number generator (PortSuggester) so
   // that we consistently (within this profile) suggest the same ephemeral
diff --git a/net/quic/quic_stream_factory_test.cc b/net/quic/quic_stream_factory_test.cc
index cb159de8..a536b22 100644
--- a/net/quic/quic_stream_factory_test.cc
+++ b/net/quic/quic_stream_factory_test.cc
@@ -321,16 +321,16 @@
         idle_connection_timeout_seconds_(kIdleConnectionTimeoutSeconds),
         migrate_sessions_on_network_change_(false),
         migrate_sessions_early_(false),
-        force_hol_blocking_(false) {
+        force_hol_blocking_(false),
+        race_cert_verification_(false) {
     clock_->AdvanceTime(QuicTime::Delta::FromSeconds(1));
   }
 
   ~QuicStreamFactoryTestBase() {
     // If |factory_| was initialized, then it took over ownership of |clock_|.
     // If |factory_| was not initialized, then |clock_| needs to be destroyed.
-    if (!factory_) {
+    if (!factory_)
       delete clock_;
-    }
   }
 
   void Initialize() {
@@ -353,8 +353,8 @@
         close_sessions_on_ip_change_,
         disable_quic_on_timeout_with_open_streams_,
         idle_connection_timeout_seconds_, migrate_sessions_on_network_change_,
-        migrate_sessions_early_, force_hol_blocking_, QuicTagVector(),
-        /*enable_token_binding*/ false));
+        migrate_sessions_early_, force_hol_blocking_, race_cert_verification_,
+        QuicTagVector(), /*enable_token_binding*/ false));
     factory_->set_require_confirmation(false);
     EXPECT_FALSE(factory_->has_quic_server_info_factory());
     factory_->set_quic_server_info_factory(new MockQuicServerInfoFactory());
@@ -379,6 +379,11 @@
     return QuicStreamFactoryPeer::HasActiveSession(factory_.get(), server_id);
   }
 
+  bool HasActiveCertVerifierJob(const QuicServerId& server_id) {
+    return QuicStreamFactoryPeer::HasActiveCertVerifierJob(factory_.get(),
+                                                           server_id);
+  }
+
   QuicChromiumClientSession* GetActiveSession(
       const HostPortPair& host_port_pair) {
     QuicServerId server_id(host_port_pair, PRIVACY_MODE_DISABLED);
@@ -547,6 +552,7 @@
   bool migrate_sessions_on_network_change_;
   bool migrate_sessions_early_;
   bool force_hol_blocking_;
+  bool race_cert_verification_;
 };
 
 class QuicStreamFactoryTest : public QuicStreamFactoryTestBase,
@@ -4136,6 +4142,64 @@
   EXPECT_EQ(test_cert2, cached2->certs()[0]);
 }
 
+TEST_P(QuicStreamFactoryTest, StartCertVerifyJob) {
+  Initialize();
+
+  MockRead reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING, 0)};
+  SequencedSocketData socket_data(reads, arraysize(reads), nullptr, 0);
+  socket_factory_.AddSocketDataProvider(&socket_data);
+
+  // Save current state of |race_cert_verification|.
+  bool race_cert_verification =
+      QuicStreamFactoryPeer::GetRaceCertVerification(factory_.get());
+
+  // Load server config.
+  HostPortPair host_port_pair("test.example.com", kDefaultServerPort);
+  QuicServerId quic_server_id(host_port_pair_, privacy_mode_);
+  QuicStreamFactoryPeer::CacheDummyServerConfig(factory_.get(), quic_server_id);
+
+  QuicStreamFactoryPeer::SetRaceCertVerification(factory_.get(), true);
+
+  // Start CertVerifyJob.
+  QuicAsyncStatus status = QuicStreamFactoryPeer::StartCertVerifyJob(
+      factory_.get(), quic_server_id, /*cert_verify_flags=*/0, net_log_);
+  EXPECT_NE(QUIC_FAILURE, status);
+
+  if (status == QUIC_PENDING) {
+    // Verify CertVerifierJob has started.
+    EXPECT_TRUE(HasActiveCertVerifierJob(quic_server_id));
+
+    while (HasActiveCertVerifierJob(quic_server_id)) {
+      base::RunLoop().RunUntilIdle();
+    }
+  }
+  // Verify CertVerifierJob has finished.
+  EXPECT_FALSE(HasActiveCertVerifierJob(quic_server_id));
+
+  // Start a QUIC request.
+  QuicStreamRequest request(factory_.get());
+  EXPECT_EQ(ERR_IO_PENDING,
+            request.Request(host_port_pair_, privacy_mode_,
+                            /*cert_verify_flags=*/0, url_, "GET", net_log_,
+                            callback_.callback()));
+
+  EXPECT_EQ(OK, callback_.WaitForResult());
+
+  std::unique_ptr<QuicHttpStream> stream = request.CreateStream();
+  EXPECT_TRUE(stream.get());
+
+  // Restore |race_cert_verification|.
+  QuicStreamFactoryPeer::SetRaceCertVerification(factory_.get(),
+                                                 race_cert_verification);
+
+  EXPECT_TRUE(socket_data.AllReadDataConsumed());
+  EXPECT_TRUE(socket_data.AllWriteDataConsumed());
+
+  // Verify there are no outstanding CertVerifierJobs after request has
+  // finished.
+  EXPECT_FALSE(HasActiveCertVerifierJob(quic_server_id));
+}
+
 TEST_P(QuicStreamFactoryTest, QuicDoingZeroRTT) {
   Initialize();
 
diff --git a/net/quic/test_tools/quic_stream_factory_peer.cc b/net/quic/test_tools/quic_stream_factory_peer.cc
index d891cfe..ec7034a4 100644
--- a/net/quic/test_tools/quic_stream_factory_peer.cc
+++ b/net/quic/test_tools/quic_stream_factory_peer.cc
@@ -7,11 +7,14 @@
 #include <string>
 #include <vector>
 
+#include "net/cert/x509_certificate.h"
 #include "net/quic/crypto/quic_crypto_client_config.h"
 #include "net/quic/quic_chromium_client_session.h"
 #include "net/quic/quic_clock.h"
 #include "net/quic/quic_http_stream.h"
 #include "net/quic/quic_stream_factory.h"
+#include "net/test/cert_test_util.h"
+#include "net/test/test_data_directory.h"
 
 using std::string;
 using std::vector;
@@ -33,6 +36,12 @@
   return factory->HasActiveSession(server_id);
 }
 
+bool QuicStreamFactoryPeer::HasActiveCertVerifierJob(
+    QuicStreamFactory* factory,
+    const QuicServerId& server_id) {
+  return factory->HasActiveCertVerifierJob(server_id);
+}
+
 QuicChromiumClientSession* QuicStreamFactoryPeer::GetActiveSession(
     QuicStreamFactory* factory,
     const QuicServerId& server_id) {
@@ -82,6 +91,25 @@
   factory->delay_tcp_race_ = delay_tcp_race;
 }
 
+bool QuicStreamFactoryPeer::GetRaceCertVerification(
+    QuicStreamFactory* factory) {
+  return factory->race_cert_verification_;
+}
+
+void QuicStreamFactoryPeer::SetRaceCertVerification(
+    QuicStreamFactory* factory,
+    bool race_cert_verification) {
+  factory->race_cert_verification_ = race_cert_verification;
+}
+
+QuicAsyncStatus QuicStreamFactoryPeer::StartCertVerifyJob(
+    QuicStreamFactory* factory,
+    const QuicServerId& server_id,
+    int cert_verify_flags,
+    const BoundNetLog& net_log) {
+  return factory->StartCertVerifyJob(server_id, cert_verify_flags, net_log);
+}
+
 void QuicStreamFactoryPeer::SetYieldAfterPackets(QuicStreamFactory* factory,
                                                  int yield_after_packets) {
   factory->yield_after_packets_ = yield_after_packets;
@@ -149,9 +177,15 @@
   string server_config(reinterpret_cast<const char*>(&scfg), sizeof(scfg));
   string source_address_token("test_source_address_token");
   string signature("test_signature");
-  string test_cert("test_cert");
+
   vector<string> certs;
-  certs.push_back(test_cert);
+  // Load a certificate that is valid for *.example.org
+  scoped_refptr<X509Certificate> cert(
+      ImportCertFromFile(GetTestCertsDirectory(), "wildcard.pem"));
+  DCHECK(cert);
+  std::string der_bytes;
+  DCHECK(X509Certificate::GetDEREncoded(cert->os_cert_handle(), &der_bytes));
+  certs.push_back(der_bytes);
 
   QuicCryptoClientConfig* crypto_config = &factory->crypto_config_;
   QuicCryptoClientConfig::CachedState* cached =
diff --git a/net/quic/test_tools/quic_stream_factory_peer.h b/net/quic/test_tools/quic_stream_factory_peer.h
index bf18a18..385ebe3d 100644
--- a/net/quic/test_tools/quic_stream_factory_peer.h
+++ b/net/quic/test_tools/quic_stream_factory_peer.h
@@ -12,6 +12,7 @@
 #include "base/task_runner.h"
 #include "net/base/host_port_pair.h"
 #include "net/base/privacy_mode.h"
+#include "net/log/net_log.h"
 #include "net/quic/quic_protocol.h"
 #include "net/quic/quic_server_id.h"
 #include "net/quic/quic_time.h"
@@ -36,6 +37,9 @@
   static bool HasActiveSession(QuicStreamFactory* factory,
                                const QuicServerId& server_id);
 
+  static bool HasActiveCertVerifierJob(QuicStreamFactory* factory,
+                                       const QuicServerId& server_id);
+
   static QuicChromiumClientSession* GetActiveSession(
       QuicStreamFactory* factory,
       const QuicServerId& server_id);
@@ -59,6 +63,16 @@
 
   static void SetDelayTcpRace(QuicStreamFactory* factory, bool delay_tcp_race);
 
+  static bool GetRaceCertVerification(QuicStreamFactory* factory);
+
+  static void SetRaceCertVerification(QuicStreamFactory* factory,
+                                      bool race_cert_verification);
+
+  static QuicAsyncStatus StartCertVerifyJob(QuicStreamFactory* factory,
+                                            const QuicServerId& server_id,
+                                            int cert_verify_flags,
+                                            const BoundNetLog& net_log);
+
   static void SetYieldAfterPackets(QuicStreamFactory* factory,
                                    int yield_after_packets);
 
diff --git a/net/tools/quic/test_tools/quic_test_client.cc b/net/tools/quic/test_tools/quic_test_client.cc
index ad5748d..0e80d61d 100644
--- a/net/tools/quic/test_tools/quic_test_client.cc
+++ b/net/tools/quic/test_tools/quic_test_client.cc
@@ -83,6 +83,16 @@
     return QUIC_SUCCESS;
   }
 
+  QuicAsyncStatus VerifyCertChain(
+      const std::string& hostname,
+      const std::vector<std::string>& certs,
+      const ProofVerifyContext* verify_context,
+      std::string* error_details,
+      std::unique_ptr<ProofVerifyDetails>* verify_details,
+      std::unique_ptr<ProofVerifierCallback> callback) override {
+    return QUIC_SUCCESS;
+  }
+
   const string& common_name() const { return common_name_; }
 
   const string& cert_sct() const { return cert_sct_; }
diff --git a/net/url_request/url_request_context_builder.cc b/net/url_request/url_request_context_builder.cc
index 3f93b3e..8eded65 100644
--- a/net/url_request/url_request_context_builder.cc
+++ b/net/url_request/url_request_context_builder.cc
@@ -193,7 +193,8 @@
       quic_close_sessions_on_ip_change(false),
       quic_migrate_sessions_on_network_change(false),
       quic_migrate_sessions_early(false),
-      quic_disable_bidirectional_streams(false) {}
+      quic_disable_bidirectional_streams(false),
+      quic_race_cert_verification(false) {}
 
 URLRequestContextBuilder::HttpNetworkSessionParams::~HttpNetworkSessionParams()
 {}
@@ -441,6 +442,8 @@
       http_network_session_params_.quic_migrate_sessions_early;
   network_session_params.quic_disable_bidirectional_streams =
       http_network_session_params_.quic_disable_bidirectional_streams;
+  network_session_params.quic_race_cert_verification =
+      http_network_session_params_.quic_race_cert_verification;
   if (proxy_delegate_) {
     network_session_params.proxy_delegate = proxy_delegate_.get();
     storage->set_proxy_delegate(std::move(proxy_delegate_));
diff --git a/net/url_request/url_request_context_builder.h b/net/url_request/url_request_context_builder.h
index ed76c00..58f0516a 100644
--- a/net/url_request/url_request_context_builder.h
+++ b/net/url_request/url_request_context_builder.h
@@ -105,6 +105,7 @@
     bool quic_migrate_sessions_on_network_change;
     bool quic_migrate_sessions_early;
     bool quic_disable_bidirectional_streams;
+    bool quic_race_cert_verification;
   };
 
   URLRequestContextBuilder();
@@ -283,6 +284,11 @@
         quic_disable_bidirectional_streams;
   }
 
+  void set_quic_race_cert_verification(bool quic_race_cert_verification) {
+    http_network_session_params_.quic_race_cert_verification =
+        quic_race_cert_verification;
+  }
+
   void set_throttling_enabled(bool throttling_enabled) {
     throttling_enabled_ = throttling_enabled;
   }
diff --git a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
index d5580351..c6ff6ef 100644
--- a/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
+++ b/testing/buildbot/filters/browser-side-navigation.linux.content_browsertests.filter
@@ -2,6 +2,7 @@
 -IFrameZoomBrowserTest.RedirectToPageWithSubframeZoomsCorrectly
 -IFrameZoomBrowserTest.SubframesDontZoomIndependently
 -NavigationControllerBrowserTest.BackTwiceToIframeWithContent
+-NavigationHandleImplHttpsUpgradeBrowserTest.*
 -RenderFrameHostManagerTest.RestoreSubframeFileAccessForHistoryNavigation
 -RequestDataResourceDispatcherHostBrowserTest.*
 -ServiceWorker*
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 61b8d73..a9dbff4 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -1384,3 +1384,4 @@
 # Minor image differences after Skia GPU batching change.
 crbug.com/625909 virtual/gpu-rasterization/fast/images/color-profile-group.html  [ NeedsRebaseline ]
 crbug.com/625909 virtual/gpu/fast/canvas/image-object-in-canvas.html [ NeedsRebaseline ]
+crbug.com/626823 virtual/gpu-rasterization/fast/images/color-profile-iframe.html [ NeedsManualRebaseline ]
diff --git a/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html
new file mode 100644
index 0000000..e528d75
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value-expected.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<p>crbug.com/625604: Don't display fallback content when value attribute is empty rather than null. </p>
diff --git a/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html
new file mode 100644
index 0000000..c471250
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/forms/input-image-button-with-empty-value.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<p>crbug.com/625604: Don't display fallback content when value attribute is empty rather than null. </p>
+<input value="" type="image" style="background-image: url('data:image/gif;base64,R0lGODdhAgACAIABAAAAAP///ywAAAAAAgACAAACA0QCBQA7'); display: block;">
+<input value="" type="image" style="display: block;">
+
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt
new file mode 100644
index 0000000..0713d4f
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column-expected.txt
@@ -0,0 +1,43 @@
+CONSOLE MESSAGE: line 1: 239
+CONSOLE MESSAGE: line 2: 239
+{
+    column : 9
+    level : log
+    line : 1
+    source : console-api
+    stack : {
+        callFrames : [
+            [0] : {
+                columnNumber : 9
+                functionName : 
+                lineNumber : 1
+                scriptId : 0
+                url : 
+            }
+        ]
+    }
+    text : 239
+    type : log
+    url : 
+}
+{
+    column : 3
+    level : log
+    line : 2
+    source : console-api
+    stack : {
+        callFrames : [
+            [0] : {
+                columnNumber : 3
+                functionName : 
+                lineNumber : 2
+                scriptId : 0
+                url : 
+            }
+        ]
+    }
+    text : 239
+    type : log
+    url : 
+}
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html
new file mode 100644
index 0000000..53bef3dc
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/console-line-and-column.html
@@ -0,0 +1,41 @@
+<html>
+<head>
+<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script>
+<script>
+
+function test()
+{
+    InspectorTest.sendCommand("Console.enable", {});
+
+    addConsoleMessagePromise("console.log(239)")
+        .then(dumpMessage)
+        .then(() => addConsoleMessagePromise("var l = console.log;\n  l(239)"))
+        .then(dumpMessage)
+        .then(() => InspectorTest.completeTest());
+
+    function addConsoleMessagePromise(expression)
+    {
+        var cb;
+        var p = new Promise((resolver) => cb = resolver);
+        InspectorTest.eventHandler["Console.messageAdded"] = (messageObject) => cb(messageObject);
+        InspectorTest.sendCommand("Runtime.evaluate", { expression: expression });
+        return p;
+    }
+
+    function dumpMessage(messageObject)
+    {
+        var msg = messageObject.params.message;
+        delete msg.executionContextId;
+        delete msg.parameters;
+        delete msg.timestamp;
+        for (var frame of msg.stack.callFrames)
+            frame.scriptId = 0;
+        InspectorTest.logObject(msg);
+    }
+}
+
+</script>
+</head>
+<body onload="runTest()">
+</body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html b/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html
index 88a3050..4a8f0f1 100644
--- a/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/console/exception-from-worker-contains-stack.html
@@ -43,7 +43,7 @@
     {
         var workerId = messageObject["params"]["workerId"];
         InspectorTest.log("Worker created");
-        sendCommandToWorker("Console.enable", {}, workerId);
+        sendCommandToWorker("Runtime.enable", {}, workerId);
         if (!--waitForWorkers)
             InspectorTest.sendCommandOrDie("Runtime.evaluate", { expression: "worker1.postMessage(239);worker2.postMessage(42);" });
     }
@@ -53,11 +53,9 @@
     InspectorTest.eventHandler["Worker.dispatchMessageFromWorker"] = function(messageObject)
     {
         var message = JSON.parse(messageObject["params"]["message"]);
-        if (message["method"] === "Console.messageAdded") {
-            var callFrames = message.params.message.stack.callFrames;
-            if (callFrames.length < 2 || callFrames[1].functionName !== "boo1")
-                return;
-            InspectorTest.log(message.params.message.stack.callFrames.length > 0 ? "Message with stack trace received." : "[FAIL] Message contains empty stack trace");
+        if (message["method"] === "Runtime.exceptionThrown") {
+            var callFrames = message.params.details.stack ? message.params.details.stack.callFrames : [];
+            InspectorTest.log(callFrames.length > 0 ? "Message with stack trace received." : "[FAIL] Message contains empty stack trace");
             messageReceived = true;
             if (messageReceived && workerTerminated)
                 InspectorTest.completeTest();
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt
new file mode 100644
index 0000000..06a7cb8b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript-expected.txt
@@ -0,0 +1,24 @@
+{
+    exceptionDetails : {
+        columnNumber : 2
+        lineNumber : 1
+        scriptId : 0
+        text : Uncaught SyntaxError: Unexpected end of input
+        url : foo1.js
+    }
+}
+{
+    scriptId : 0
+}
+{
+}
+{
+    exceptionDetails : {
+        columnNumber : 13
+        lineNumber : 0
+        scriptId : 0
+        text : Uncaught SyntaxError: Unexpected identifier
+        url : foo4.js
+    }
+}
+
diff --git a/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html
new file mode 100644
index 0000000..699e55b
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/inspector-protocol/runtime/runtime-compileScript.html
@@ -0,0 +1,45 @@
+<html>
+<head>
+<script type="text/javascript" src="../../http/tests/inspector-protocol/inspector-protocol-test.js"></script>
+<script>
+function test()
+{
+    var executionContextId;
+
+    InspectorTest.sendCommand("Runtime.enable", {});
+    InspectorTest.eventHandler["Runtime.executionContextCreated"] = function(messageObject)
+    {
+        executionContextId = messageObject.params.context.id;
+
+        compileScriptPromise("\n  (", false, "foo1.js")
+            .then(dumpResult)
+            .then(() => compileScriptPromise("239", true, "foo2.js"))
+            .then(dumpResult)
+            .then(() => compileScriptPromise("239", false, "foo3.js"))
+            .then(dumpResult)
+            .then(() => compileScriptPromise("testfunction f()\n{\n    return 0;\n}\n", false, "foo4.js"))
+            .then(dumpResult)
+            .then(() => InspectorTest.completeTest());
+    }
+
+    function dumpResult(messageObject)
+    {
+        if (messageObject.result.exceptionDetails)
+            messageObject.result.exceptionDetails.scriptId = 0;
+        if (messageObject.result.scriptId)
+            messageObject.result.scriptId = 0;
+        InspectorTest.logObject(messageObject.result);
+    }
+
+    function compileScriptPromise(expression, persistScript, sourceURL)
+    {
+        var cb;
+        var p = new Promise((resolver) => cb = resolver);
+        InspectorTest.sendCommand("Runtime.compileScript", { expression: expression, sourceURL: sourceURL, persistScript: persistScript, executionContextId: executionContextId }, cb);
+        return p;
+    }
+}
+</script>
+</head>
+<body onLoad="runTest();"></body>
+</html>
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt
index b5fbe8f7..1758b56 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-expected.txt
@@ -1,8 +1,6 @@
 CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error
 CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error
 CONSOLE ERROR: line 12: Uncaught (in promise) Error: Handled error
-CONSOLE MESSAGE: Handler added to rejected promise
-CONSOLE MESSAGE: Handler added to rejected promise
 Tests that console revokes lazily handled promise rejections.
 
 Creating promise
diff --git a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt
index 8e84e0eb..7bcabaa 100644
--- a/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/console/console-revoke-error-in-worker-expected.txt
@@ -1,5 +1,4 @@
 CONSOLE ERROR: line 1: Uncaught (in promise) Error: Handled error
-CONSOLE MESSAGE: Handler added to rejected promise
 Tests that console revokes lazily handled promise rejections.
 
 Creating worker with promise
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt
index 593cc8c5..6c599e61 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/sources/compile-javascript-expected.txt
@@ -1,4 +1,4 @@
 Verifies proactive javascript compilation.
 
-0:14 [Error] Uncaught SyntaxError: Unexpected identifier
+0:13 [Error] Uncaught SyntaxError: Unexpected identifier
 
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt
index 64ac2c0..878de58c 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run-expected.txt
@@ -12,7 +12,7 @@
 Running script
 exceptionDetails:
    Uncaught ReferenceError: c is not defined
-   line: 1, column: 15
+   line: 0, column: 15
    exceptionDetails stack trace:
        url: test.js
        function: 
@@ -22,6 +22,6 @@
 Compiling script
 exceptionDetails:
    Uncaught SyntaxError: Unexpected token }
-   line: 1, column: 0
+   line: 0, column: 0
    no stack trace attached to exceptionDetails
 
diff --git a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html
index 1378028..b1f7ca4 100644
--- a/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html
+++ b/third_party/WebKit/LayoutTests/inspector/sources/debugger/debugger-compile-and-run.html
@@ -9,7 +9,7 @@
     {
         InspectorTest.addResult("exceptionDetails:")
         InspectorTest.addResult("   " + exceptionDetails.text);
-        InspectorTest.addResult("   line: " + exceptionDetails.line + ", column: " + exceptionDetails.column);
+        InspectorTest.addResult("   line: " + exceptionDetails.lineNumber + ", column: " + exceptionDetails.columnNumber);
 
         var stack = exceptionDetails.stack ? exceptionDetails.stack.callFrames : null;
         if (!stack) {
@@ -19,7 +19,7 @@
             for (var i = 0; i < stack.length && i < 100; ++i) {
                 InspectorTest.addResult("       url: " + stack[i].url);
                 InspectorTest.addResult("       function: " + stack[i].functionName);
-                InspectorTest.addResult("       line: " + stack[i].lineNumber);            
+                InspectorTest.addResult("       line: " + stack[i].lineNumber);
             }
         }
     }
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 179c4229..9b9530c9 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -175,6 +175,7 @@
 #include "core/inspector/InspectorInstrumentation.h"
 #include "core/inspector/InspectorTraceEvents.h"
 #include "core/inspector/InstanceCounters.h"
+#include "core/inspector/MainThreadDebugger.h"
 #include "core/layout/HitTestResult.h"
 #include "core/layout/LayoutPart.h"
 #include "core/layout/LayoutView.h"
@@ -2890,10 +2891,9 @@
     return domWindow();
 }
 
-void Document::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+void Document::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
 {
-    ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location));
-    addConsoleMessage(consoleMessage);
+    MainThreadDebugger::instance()->exceptionThrown(m_frame.get(), errorMessage, std::move(location));
 }
 
 void Document::setURL(const KURL& url)
diff --git a/third_party/WebKit/Source/core/dom/Document.h b/third_party/WebKit/Source/core/dom/Document.h
index e0bc025..44e65c6b 100644
--- a/third_party/WebKit/Source/core/dom/Document.h
+++ b/third_party/WebKit/Source/core/dom/Document.h
@@ -951,7 +951,7 @@
     void cancelIdleCallback(int id);
 
     EventTarget* errorEventTarget() final;
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) final;
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) final;
 
     void initDNSPrefetch();
 
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
index 4251bcfb..c2e51cd 100644
--- a/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
+++ b/third_party/WebKit/Source/core/dom/ExecutionContext.cpp
@@ -153,14 +153,14 @@
 
     // First report the original exception and only then all the nested ones.
     if (!dispatchErrorEvent(errorEvent, corsStatus))
-        logExceptionToConsole(errorEvent->messageForConsole(), errorEvent->location()->clone());
+        exceptionThrown(errorEvent->messageForConsole(), errorEvent->location()->clone());
 
     if (!m_pendingExceptions)
         return;
 
     for (size_t i = 0; i < m_pendingExceptions->size(); i++) {
         PendingException* e = m_pendingExceptions->at(i).get();
-        logExceptionToConsole(e->m_errorMessage, std::move(e->m_location));
+        exceptionThrown(e->m_errorMessage, std::move(e->m_location));
     }
     m_pendingExceptions.reset();
 }
diff --git a/third_party/WebKit/Source/core/dom/ExecutionContext.h b/third_party/WebKit/Source/core/dom/ExecutionContext.h
index b13a279..8655591 100644
--- a/third_party/WebKit/Source/core/dom/ExecutionContext.h
+++ b/third_party/WebKit/Source/core/dom/ExecutionContext.h
@@ -107,7 +107,7 @@
     void reportException(ErrorEvent*, AccessControlStatus);
 
     virtual void addConsoleMessage(ConsoleMessage*) = 0;
-    virtual void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) = 0;
+    virtual void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) = 0;
 
     PublicURLManager& publicURLManager();
 
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.cpp b/third_party/WebKit/Source/core/fetch/FetchContext.cpp
index 207af0f..ca03599 100644
--- a/third_party/WebKit/Source/core/fetch/FetchContext.cpp
+++ b/third_party/WebKit/Source/core/fetch/FetchContext.cpp
@@ -109,7 +109,7 @@
 {
 }
 
-void FetchContext::upgradeInsecureRequest(FetchRequest&)
+void FetchContext::upgradeInsecureRequest(ResourceRequest&)
 {
 }
 
diff --git a/third_party/WebKit/Source/core/fetch/FetchContext.h b/third_party/WebKit/Source/core/fetch/FetchContext.h
index d4b5455..7a8400f 100644
--- a/third_party/WebKit/Source/core/fetch/FetchContext.h
+++ b/third_party/WebKit/Source/core/fetch/FetchContext.h
@@ -104,7 +104,7 @@
     virtual void sendImagePing(const KURL&);
     virtual void addConsoleMessage(const String&) const;
     virtual SecurityOrigin* getSecurityOrigin() const { return nullptr; }
-    virtual void upgradeInsecureRequest(FetchRequest&);
+    virtual void upgradeInsecureRequest(ResourceRequest&);
     virtual void addClientHintsIfNecessary(FetchRequest&);
     virtual void addCSPHeaderIfNecessary(Resource::Type, FetchRequest&);
 
diff --git a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
index 31e8389..125574198 100644
--- a/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
+++ b/third_party/WebKit/Source/core/fetch/ResourceFetcher.cpp
@@ -410,7 +410,8 @@
 {
     ASSERT(request.options().synchronousPolicy == RequestAsynchronously || factory.type() == Resource::Raw || factory.type() == Resource::XSLStyleSheet);
 
-    context().upgradeInsecureRequest(request);
+    if (request.resourceRequest().httpHeaderField("Upgrade-Insecure-Requests") != AtomicString("1"))
+        context().upgradeInsecureRequest(request.mutableResourceRequest());
     context().addClientHintsIfNecessary(request);
     context().addCSPHeaderIfNecessary(factory.type(), request);
 
diff --git a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
index 46d5ea4..24ed78e2 100644
--- a/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLInputElement.cpp
@@ -824,7 +824,7 @@
         alt = fastGetAttribute(titleAttr);
     if (alt.isNull())
         alt = fastGetAttribute(valueAttr);
-    if (alt.isEmpty())
+    if (alt.isNull())
         alt = locale().queryString(WebLocalizedString::InputElementAltText);
     return alt;
 }
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
index 687ba7ea..e8c6b9c 100644
--- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.cpp
@@ -123,6 +123,11 @@
     debugger()->contextDestroyed(scriptState->context());
 }
 
+void MainThreadDebugger::exceptionThrown(LocalFrame* frame, const String& errorMessage, std::unique_ptr<SourceLocation> location)
+{
+    debugger()->exceptionThrown(contextGroupId(frame), errorMessage, location->url(), location->lineNumber(), location->columnNumber(), location->cloneStackTrace(), location->scriptId());
+}
+
 int MainThreadDebugger::contextGroupId(LocalFrame* frame)
 {
     LocalFrame* localFrameRoot = frame->localFrameRoot();
diff --git a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
index f2af18b..9c649e4 100644
--- a/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
+++ b/third_party/WebKit/Source/core/inspector/MainThreadDebugger.h
@@ -43,6 +43,7 @@
 
 class LocalFrame;
 class SecurityOrigin;
+class SourceLocation;
 
 class CORE_EXPORT MainThreadDebugger final : public ThreadDebugger {
     WTF_MAKE_NONCOPYABLE(MainThreadDebugger);
@@ -64,10 +65,12 @@
     InspectorTaskRunner* taskRunner() const { return m_taskRunner.get(); }
     bool isWorker() override { return false; }
     void setClientMessageLoop(std::unique_ptr<ClientMessageLoop>);
+    // TODO(dgozman): by making this method virtual, we can move many methods to ThreadDebugger and avoid some duplication. Should be careful about performance.
     int contextGroupId(LocalFrame*);
     void didClearContextsForFrame(LocalFrame*);
     void contextCreated(ScriptState*, LocalFrame*, SecurityOrigin*);
     void contextWillBeDestroyed(ScriptState*);
+    void exceptionThrown(LocalFrame*, const String& errorMessage, std::unique_ptr<SourceLocation>);
 
     void installAdditionalCommandLineAPI(v8::Local<v8::Context>, v8::Local<v8::Object>) override;
 
diff --git a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp
index 80129d3..9aaf4a3 100644
--- a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp
+++ b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.cpp
@@ -71,6 +71,11 @@
     debugger()->contextDestroyed(context);
 }
 
+void WorkerThreadDebugger::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+{
+    debugger()->exceptionThrown(workerContextGroupId, errorMessage, location->url(), location->lineNumber(), location->columnNumber(), location->cloneStackTrace(), location->scriptId());
+}
+
 int WorkerThreadDebugger::contextGroupId()
 {
     return workerContextGroupId;
diff --git a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
index b95f354..ba96fd9 100644
--- a/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
+++ b/third_party/WebKit/Source/core/inspector/WorkerThreadDebugger.h
@@ -31,13 +31,15 @@
 #ifndef WorkerThreadDebugger_h
 #define WorkerThreadDebugger_h
 
+#include "core/CoreExport.h"
 #include "core/inspector/ThreadDebugger.h"
 
 namespace blink {
 
+class SourceLocation;
 class WorkerThread;
 
-class WorkerThreadDebugger final : public ThreadDebugger {
+class CORE_EXPORT WorkerThreadDebugger final : public ThreadDebugger {
     WTF_MAKE_NONCOPYABLE(WorkerThreadDebugger);
 public:
     explicit WorkerThreadDebugger(WorkerThread*, v8::Isolate*);
@@ -48,6 +50,7 @@
     int contextGroupId();
     void contextCreated(v8::Local<v8::Context>);
     void contextWillBeDestroyed(v8::Local<v8::Context>);
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>);
 
     // V8DebuggerClient implementation.
     void runMessageLoopOnPause(int contextGroupId) override;
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
index d200c0cec..e219e446 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.cpp
@@ -624,6 +624,11 @@
     return true;
 }
 
+void DocumentLoader::upgradeInsecureRequest()
+{
+    fetcher()->context().upgradeInsecureRequest(m_request);
+}
+
 void DocumentLoader::startLoadingMainResource()
 {
     timing().markNavigationStart();
diff --git a/third_party/WebKit/Source/core/loader/DocumentLoader.h b/third_party/WebKit/Source/core/loader/DocumentLoader.h
index 9bf58f7f..a8b4216 100644
--- a/third_party/WebKit/Source/core/loader/DocumentLoader.h
+++ b/third_party/WebKit/Source/core/loader/DocumentLoader.h
@@ -113,6 +113,8 @@
     NavigationType getNavigationType() const { return m_navigationType; }
     void setNavigationType(NavigationType navigationType) { m_navigationType = navigationType; }
 
+    void upgradeInsecureRequest();
+
     void startLoadingMainResource();
 
     void acceptDataFromThreadedReceiver(const char* data, int dataLength, int encodedDataLength);
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index d73172a9..7838ecc 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -676,14 +676,20 @@
     return m_document ? m_document->getSecurityOrigin() : nullptr;
 }
 
-void FrameFetchContext::upgradeInsecureRequest(FetchRequest& fetchRequest)
+void FrameFetchContext::upgradeInsecureRequest(ResourceRequest& resourceRequest)
 {
-    KURL url = fetchRequest.resourceRequest().url();
-
     // Tack an 'Upgrade-Insecure-Requests' header to outgoing navigational requests, as described in
     // https://w3c.github.io/webappsec/specs/upgrade/#feature-detect
-    if (fetchRequest.resourceRequest().frameType() != WebURLRequest::FrameTypeNone)
-        fetchRequest.mutableResourceRequest().addHTTPHeaderField("Upgrade-Insecure-Requests", "1");
+    if (resourceRequest.frameType() != WebURLRequest::FrameTypeNone) {
+
+        // Early return if the request has already been upgraded.
+        if (resourceRequest.httpHeaderField("Upgrade-Insecure-Requests") == AtomicString("1"))
+            return;
+
+        resourceRequest.addHTTPHeaderField("Upgrade-Insecure-Requests", "1");
+    }
+
+    KURL url = resourceRequest.url();
 
     // If we don't yet have an |m_document| (because we're loading an iframe, for instance), check the FrameLoader's policy.
     WebInsecureRequestPolicy relevantPolicy = m_document ? m_document->getInsecureRequestPolicy() : frame()->loader().getInsecureRequestPolicy();
@@ -695,17 +701,15 @@
         // 1. Are for subresources (including nested frames).
         // 2. Are form submissions.
         // 3. Whose hosts are contained in the document's InsecureNavigationSet.
-        const ResourceRequest& request = fetchRequest.resourceRequest();
-        if (request.frameType() == WebURLRequest::FrameTypeNone
-            || request.frameType() == WebURLRequest::FrameTypeNested
-            || request.requestContext() == WebURLRequest::RequestContextForm
-            || (!url.host().isNull() && relevantNavigationSet->contains(url.host().impl()->hash())))
-        {
+        if (resourceRequest.frameType() == WebURLRequest::FrameTypeNone
+            || resourceRequest.frameType() == WebURLRequest::FrameTypeNested
+            || resourceRequest.requestContext() == WebURLRequest::RequestContextForm
+            || (!url.host().isNull() && relevantNavigationSet->contains(url.host().impl()->hash()))) {
             UseCounter::count(m_document, UseCounter::UpgradeInsecureRequestsUpgradedRequest);
             url.setProtocol("https");
             if (url.port() == 80)
                 url.setPort(443);
-            fetchRequest.mutableResourceRequest().setURL(url);
+            resourceRequest.setURL(url);
         }
     }
 }
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.h b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
index e04fd81..281f18f 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.h
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.h
@@ -99,7 +99,7 @@
     void sendImagePing(const KURL&) override;
     void addConsoleMessage(const String&) const override;
     SecurityOrigin* getSecurityOrigin() const override;
-    void upgradeInsecureRequest(FetchRequest&) override;
+    void upgradeInsecureRequest(ResourceRequest&) override;
     void addClientHintsIfNecessary(FetchRequest&) override;
     void addCSPHeaderIfNecessary(Resource::Type, FetchRequest&) override;
 
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
index 8e8aa81b..c9e1ebff 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContextTest.cpp
@@ -186,7 +186,7 @@
         fetchRequest.mutableResourceRequest().setRequestContext(requestContext);
         fetchRequest.mutableResourceRequest().setFrameType(frameType);
 
-        fetchContext->upgradeInsecureRequest(fetchRequest);
+        fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest());
 
         EXPECT_STREQ(expectedURL.getString().utf8().data(), fetchRequest.resourceRequest().url().getString().utf8().data());
         EXPECT_EQ(expectedURL.protocol(), fetchRequest.resourceRequest().url().protocol());
@@ -204,10 +204,16 @@
         fetchRequest.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextScript);
         fetchRequest.mutableResourceRequest().setFrameType(frameType);
 
-        fetchContext->upgradeInsecureRequest(fetchRequest);
+        fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest());
 
         EXPECT_STREQ(shouldPrefer ? "1" : "",
             fetchRequest.resourceRequest().httpHeaderField(HTTPNames::Upgrade_Insecure_Requests).utf8().data());
+
+        // Calling upgradeInsecureRequest more than once shouldn't affect the header.
+        if (shouldPrefer) {
+            fetchContext->upgradeInsecureRequest(fetchRequest.mutableResourceRequest());
+            EXPECT_STREQ("1", fetchRequest.resourceRequest().httpHeaderField(HTTPNames::Upgrade_Insecure_Requests).utf8().data());
+        }
     }
 
     RefPtr<SecurityOrigin> exampleOrigin;
diff --git a/third_party/WebKit/Source/core/loader/FrameLoader.cpp b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
index 71242fc..cf11fc8a 100644
--- a/third_party/WebKit/Source/core/loader/FrameLoader.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameLoader.cpp
@@ -1451,6 +1451,7 @@
     if (m_provisionalDocumentLoader->isClientRedirect())
         m_provisionalDocumentLoader->appendRedirect(m_frame->document()->url());
     m_provisionalDocumentLoader->appendRedirect(m_provisionalDocumentLoader->request().url());
+    m_provisionalDocumentLoader->upgradeInsecureRequest();
     double triggeringEventTime = frameLoadRequest.triggeringEvent() ? frameLoadRequest.triggeringEvent()->platformTimeStamp() : 0;
     client()->dispatchDidStartProvisionalLoad(triggeringEventTime);
     ASSERT(m_provisionalDocumentLoader);
diff --git a/third_party/WebKit/Source/core/testing/NullExecutionContext.h b/third_party/WebKit/Source/core/testing/NullExecutionContext.h
index fb09789..379de81 100644
--- a/third_party/WebKit/Source/core/testing/NullExecutionContext.h
+++ b/third_party/WebKit/Source/core/testing/NullExecutionContext.h
@@ -38,7 +38,7 @@
     DOMTimerCoordinator* timers() override { return nullptr; }
 
     void addConsoleMessage(ConsoleMessage*) override { }
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override { }
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override { }
 
     void setIsSecureContext(bool);
     bool isSecureContext(String& errorMessage, const SecureContextCheck = StandardSecureContextCheck) const override;
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp
index 6d858ae8..1d605348 100644
--- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.cpp
@@ -7,6 +7,7 @@
 #include "bindings/core/v8/ScriptSourceCode.h"
 #include "bindings/core/v8/WorkerOrWorkletScriptController.h"
 #include "core/frame/FrameConsole.h"
+#include "core/inspector/MainThreadDebugger.h"
 
 namespace blink {
 
@@ -35,4 +36,9 @@
     frame()->console().addMessage(consoleMessage);
 }
 
+void MainThreadWorkletGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+{
+    MainThreadDebugger::instance()->exceptionThrown(frame(), errorMessage, std::move(location));
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h
index 14d4478..eb966b54 100644
--- a/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/MainThreadWorkletGlobalScope.h
@@ -27,6 +27,7 @@
 
     using LocalFrameLifecycleObserver::frame;
     void addConsoleMessage(ConsoleMessage*) final;
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) final;
 
     DEFINE_INLINE_VIRTUAL_TRACE()
     {
diff --git a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp
index a43edb0..ecbb0fee 100644
--- a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.cpp
@@ -34,6 +34,7 @@
 #include "core/events/MessageEvent.h"
 #include "core/frame/LocalDOMWindow.h"
 #include "core/inspector/ConsoleMessage.h"
+#include "core/inspector/WorkerThreadDebugger.h"
 #include "core/origin_trials/OriginTrialContext.h"
 #include "core/workers/SharedWorkerThread.h"
 #include "core/workers/WorkerClients.h"
@@ -83,11 +84,11 @@
     return static_cast<SharedWorkerThread*>(Base::thread());
 }
 
-void SharedWorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+void SharedWorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
 {
-    WorkerGlobalScope::logExceptionToConsole(errorMessage, location->clone());
-    ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location));
-    addMessageToWorkerConsole(consoleMessage);
+    WorkerGlobalScope::exceptionThrown(errorMessage, location->clone());
+    if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate()))
+        debugger->exceptionThrown(errorMessage, std::move(location));
 }
 
 DEFINE_TRACE(SharedWorkerGlobalScope)
diff --git a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h
index 4879f9a..8df3b94f 100644
--- a/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/SharedWorkerGlobalScope.h
@@ -65,7 +65,7 @@
 
 private:
     SharedWorkerGlobalScope(const String& name, const KURL&, const String& userAgent, SharedWorkerThread*, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*);
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
 
     String m_name;
 };
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
index d20f8ba..0567dfa7 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -267,7 +267,7 @@
     return this;
 }
 
-void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+void WorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
 {
     thread()->workerReportingProxy().reportException(errorMessage, std::move(location));
 }
@@ -355,7 +355,8 @@
 
 void WorkerGlobalScope::exceptionUnhandled(const String& errorMessage, std::unique_ptr<SourceLocation> location)
 {
-    addConsoleMessage(ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location)));
+    if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate()))
+        debugger->exceptionThrown(errorMessage, std::move(location));
 }
 
 bool WorkerGlobalScope::isSecureContext(String& errorMessage, const SecureContextCheck privilegeContextCheck) const
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
index 76538ca1..d1de075 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.h
@@ -154,7 +154,7 @@
     WorkerGlobalScope(const KURL&, const String& userAgent, WorkerThread*, double timeOrigin, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*);
     void applyContentSecurityPolicyFromVector(const Vector<CSPHeaderAndType>& headers);
 
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
     void addMessageToWorkerConsole(ConsoleMessage*);
     void setV8CacheOptions(V8CacheOptions v8CacheOptions) { m_v8CacheOptions = v8CacheOptions; }
 
diff --git a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
index 2271421..5ceb933 100644
--- a/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
+++ b/third_party/WebKit/Source/core/workers/WorkerThreadTestHelper.h
@@ -164,7 +164,7 @@
         return EventTargetNames::DedicatedWorkerGlobalScope;
     }
 
-    void logExceptionToConsole(const String&, std::unique_ptr<SourceLocation>) override
+    void exceptionThrown(const String&, std::unique_ptr<SourceLocation>) override
     {
     }
 
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
index 9a5d5da2..632f875 100644
--- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.cpp
@@ -69,12 +69,6 @@
     InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText);
 }
 
-void WorkletGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location)
-{
-    ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location));
-    addConsoleMessage(consoleMessage);
-}
-
 KURL WorkletGlobalScope::virtualCompleteURL(const String& url) const
 {
     // Always return a null URL when passed a null string.
diff --git a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
index 4e475c93..b8ec117 100644
--- a/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
+++ b/third_party/WebKit/Source/core/workers/WorkletGlobalScope.h
@@ -55,7 +55,6 @@
     }
 
     void reportBlockedScriptExecutionToInspector(const String& directiveText) final;
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) final;
 
     DECLARE_VIRTUAL_TRACE();
 
diff --git a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
index 79457d97..0d134561 100644
--- a/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
+++ b/third_party/WebKit/Source/devtools/front_end/console/ConsoleView.js
@@ -808,7 +808,7 @@
         if (!wasThrown)
             message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, "", WebInspector.ConsoleMessage.MessageType.Result, undefined, undefined, undefined, undefined, [result]);
         else
-            message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, exceptionDetails.text, WebInspector.ConsoleMessage.MessageType.Result, exceptionDetails.url, exceptionDetails.line, exceptionDetails.column, undefined, [WebInspector.UIString("Uncaught"), result], exceptionDetails.stack, undefined, undefined, exceptionDetails.scriptId);
+            message = new WebInspector.ConsoleMessage(result.target(), WebInspector.ConsoleMessage.MessageSource.JS, level, exceptionDetails.text, WebInspector.ConsoleMessage.MessageType.Result, exceptionDetails.url, exceptionDetails.lineNumber + 1, exceptionDetails.columnNumber + 1, undefined, [WebInspector.UIString("Uncaught"), result], exceptionDetails.stack, undefined, undefined, exceptionDetails.scriptId);
         message.setOriginatingMessage(originatingConsoleMessage);
         result.target().consoleModel.addMessage(message);
     },
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
index 523ecabc..44f865d7 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ConsoleModel.js
@@ -40,7 +40,7 @@
     /** @type {!Array.<!WebInspector.ConsoleMessage>} */
     this._messages = [];
     /** @type {!Map<number, !WebInspector.ConsoleMessage>} */
-    this._messageById = new Map();
+    this._messageByExceptionId = new Map();
     this._warnings = 0;
     this._errors = 0;
     this._revokedErrors = 0;
@@ -79,20 +79,20 @@
         if (this._isBlacklisted(msg))
             return;
 
-        if (msg.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError && msg._relatedMessageId) {
-            var relatedMessage = this._messageById.get(msg._relatedMessageId);
-            if (!relatedMessage)
+        if (msg.level === WebInspector.ConsoleMessage.MessageLevel.RevokedError && msg._revokedExceptionId) {
+            var exceptionMessage = this._messageByExceptionId.get(msg._revokedExceptionId);
+            if (!exceptionMessage)
                 return;
             this._errors--;
             this._revokedErrors++;
-            relatedMessage.level = WebInspector.ConsoleMessage.MessageLevel.RevokedError;
-            this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageUpdated, relatedMessage);
+            exceptionMessage.level = WebInspector.ConsoleMessage.MessageLevel.RevokedError;
+            this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageUpdated, exceptionMessage);
             return;
         }
 
         this._messages.push(msg);
-        if (msg._messageId)
-            this._messageById.set(msg._messageId, msg);
+        if (msg._exceptionId)
+            this._messageByExceptionId.set(msg._exceptionId, msg);
         this._incrementErrorWarningCount(msg);
         this.dispatchEventToListeners(WebInspector.ConsoleModel.Events.MessageAdded, msg);
     },
@@ -148,7 +148,7 @@
     _messagesCleared: function()
     {
         this._messages = [];
-        this._messageById.clear();
+        this._messageByExceptionId.clear();
         this._errors = 0;
         this._revokedErrors = 0;
         this._warnings = 0;
@@ -244,10 +244,8 @@
  * @param {number=} timestamp
  * @param {!RuntimeAgent.ExecutionContextId=} executionContextId
  * @param {?string=} scriptId
- * @param {number=} messageId
- * @param {number=} relatedMessageId
  */
-WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, executionContextId, scriptId, messageId, relatedMessageId)
+WebInspector.ConsoleMessage = function(target, source, level, messageText, type, url, line, column, requestId, parameters, stackTrace, timestamp, executionContextId, scriptId)
 {
     this._target = target;
     this.source = source;
@@ -266,8 +264,6 @@
     this.timestamp = timestamp || Date.now();
     this.executionContextId = executionContextId || 0;
     this.scriptId = scriptId || null;
-    this._messageId = messageId || 0;
-    this._relatedMessageId = relatedMessageId || 0;
 
     var networkLog = target && WebInspector.NetworkLog.fromTarget(target);
     this.request = (requestId && networkLog) ? networkLog.requestForId(requestId) : null;
@@ -311,6 +307,22 @@
     },
 
     /**
+     * @param {number} exceptionId
+     */
+    setExceptionId: function(exceptionId)
+    {
+        this._exceptionId = exceptionId;
+    },
+
+    /**
+     * @param {number} revokedExceptionId
+     */
+    setRevokedExceptionId: function(revokedExceptionId)
+    {
+        this._revokedExceptionId = revokedExceptionId;
+    },
+
+    /**
      * @return {?WebInspector.ConsoleMessage}
      */
     originatingMessage: function()
@@ -354,9 +366,9 @@
         if (!msg)
             return false;
 
-        if (this._messageId || msg._messageId)
+        if (this._exceptionId || msg._exceptionId)
             return false;
-        if (this._relatedMessageId || msg._relatedMessageId)
+        if (this._revokedExceptionId || msg._revokedExceptionId)
             return false;
 
         if (!this._isEqualStackTraces(this.stackTrace, msg.stackTrace))
@@ -458,7 +470,7 @@
     Warning: "warning",
     Error: "error",
     Debug: "debug",
-    RevokedError: "revokedError"
+    RevokedError: "revokedError"  // This is frontend-only level, used to put exceptions to console.
 };
 
 /**
@@ -502,9 +514,7 @@
             payload.stack,
             payload.timestamp * 1000, // Convert to ms.
             payload.executionContextId,
-            payload.scriptId,
-            payload.messageId,
-            payload.relatedMessageId);
+            payload.scriptId);
         this._console.addMessage(consoleMessage);
     },
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
index c83a4fa..4013efa2 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/RuntimeModel.js
@@ -339,6 +339,62 @@
 
     /**
      * @override
+     * @param {number} exceptionId
+     * @param {number} timestamp
+     * @param {!RuntimeAgent.ExceptionDetails} details
+     * @param {!RuntimeAgent.RemoteObject=} exception
+     * @param {number=} executionContextId
+     */
+    exceptionThrown: function(exceptionId, timestamp, details, exception, executionContextId)
+    {
+        var consoleMessage = new WebInspector.ConsoleMessage(
+            this._runtimeModel.target(),
+            WebInspector.ConsoleMessage.MessageSource.JS,
+            WebInspector.ConsoleMessage.MessageLevel.Error,
+            details.text,
+            undefined,
+            details.url,
+            typeof details.lineNumber === "undefined" ? undefined : details.lineNumber + 1,
+            typeof details.columnNumber === "undefined" ? undefined : details.columnNumber + 1,
+            undefined,
+            exception ? ["Uncaught (in promise)", exception] : undefined,
+            details.stack,
+            timestamp,
+            executionContextId,
+            details.scriptId);
+        consoleMessage.setExceptionId(exceptionId);
+        this._runtimeModel.target().consoleModel.addMessage(consoleMessage);
+    },
+
+    /**
+     * @override
+     * @param {number} timestamp
+     * @param {string} message
+     * @param {number} exceptionId
+     */
+    exceptionRevoked: function(timestamp, message, exceptionId)
+    {
+        var consoleMessage = new WebInspector.ConsoleMessage(
+            this._runtimeModel.target(),
+            WebInspector.ConsoleMessage.MessageSource.JS,
+            WebInspector.ConsoleMessage.MessageLevel.RevokedError,
+            message,
+            undefined,
+            undefined,
+            undefined,
+            undefined,
+            undefined,
+            undefined,
+            undefined,
+            timestamp,
+            undefined,
+            undefined);
+        consoleMessage.setRevokedExceptionId(exceptionId);
+        this._runtimeModel.target().consoleModel.addMessage(consoleMessage);
+    },
+
+    /**
+     * @override
      * @param {!RuntimeAgent.RemoteObject} payload
      * @param {!Object=} hints
      */
diff --git a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
index 9c350e3..cbd9c613 100644
--- a/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/snippets/ScriptSnippetModel.js
@@ -313,8 +313,8 @@
             exceptionDetails.text,
             undefined,
             sourceURL,
-            exceptionDetails.line,
-            exceptionDetails.column,
+            exceptionDetails.lineNumber + 1,
+            exceptionDetails.columnNumber + 1,
             undefined,
             undefined,
             exceptionDetails.stack);
diff --git a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js
index 48c38108..c3f8b49 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources/JavaScriptCompiler.js
@@ -69,9 +69,9 @@
                 this.scheduleCompile();
                 return;
             }
-            if (!exceptionDetails)
+            if (!exceptionDetails || !exceptionDetails.hasOwnProperty("lineNumber"))
                 return;
-            this._sourceFrame.uiSourceCode().addLineMessage(WebInspector.UISourceCode.Message.Level.Error, exceptionDetails.text, exceptionDetails.line - 1, exceptionDetails.column + 1);
+            this._sourceFrame.uiSourceCode().addLineMessage(WebInspector.UISourceCode.Message.Level.Error, exceptionDetails.text, /** @type {number} */(exceptionDetails.lineNumber), exceptionDetails.columnNumber);
             this._compilationFinishedForTest();
         }
     },
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
index 50780b6..f0c7f372 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.cpp
@@ -42,6 +42,7 @@
 #include "core/fetch/ResourceLoaderOptions.h"
 #include "core/inspector/ConsoleMessage.h"
 #include "core/inspector/WorkerInspectorController.h"
+#include "core/inspector/WorkerThreadDebugger.h"
 #include "core/loader/ThreadableLoader.h"
 #include "core/origin_trials/OriginTrialContext.h"
 #include "core/workers/WorkerClients.h"
@@ -212,11 +213,11 @@
     return ServiceWorkerScriptCachedMetadataHandler::create(this, scriptURL, metaData);
 }
 
-void ServiceWorkerGlobalScope::logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation> location)
+void ServiceWorkerGlobalScope::exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation> location)
 {
-    WorkerGlobalScope::logExceptionToConsole(errorMessage, location->clone());
-    ConsoleMessage* consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, std::move(location));
-    addMessageToWorkerConsole(consoleMessage);
+    WorkerGlobalScope::exceptionThrown(errorMessage, location->clone());
+    if (WorkerThreadDebugger* debugger = WorkerThreadDebugger::from(thread()->isolate()))
+        debugger->exceptionThrown(errorMessage, std::move(location));
 }
 
 void ServiceWorkerGlobalScope::scriptLoaded(size_t scriptSize, size_t cachedMetadataSize)
diff --git a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
index 7abf63fb..3921a13 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
+++ b/third_party/WebKit/Source/modules/serviceworkers/ServiceWorkerGlobalScope.h
@@ -96,7 +96,7 @@
     ServiceWorkerGlobalScope(const KURL&, const String& userAgent, ServiceWorkerThread*, double timeOrigin, std::unique_ptr<SecurityOrigin::PrivilegeData>, WorkerClients*);
     void importScripts(const Vector<String>& urls, ExceptionState&) override;
     CachedMetadataHandler* createWorkerScriptCachedMetadataHandler(const KURL& scriptURL, const Vector<char>* metaData) override;
-    void logExceptionToConsole(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
+    void exceptionThrown(const String& errorMessage, std::unique_ptr<SourceLocation>) override;
     void scriptLoaded(size_t scriptSize, size_t cachedMetadataSize) override;
 
     Member<ServiceWorkerClients> m_clients;
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
index 7f85e9e..5e552c2b 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositor.cpp
@@ -345,14 +345,27 @@
 public:
     PropertyTreeManager(cc::PropertyTrees& propertyTrees, cc::Layer* rootLayer)
         : m_propertyTrees(propertyTrees)
-        , m_rootLayer(rootLayer) {}
+        , m_rootLayer(rootLayer)
+#if DCHECK_IS_ON()
+        , m_isFirstEffectEver(true)
+#endif
+    {
+        m_effectStack.append(BlinkEffectAndCcIdPair{nullptr, kSecondaryRootNodeId});
+    }
 
     int compositorIdForTransformNode(const TransformPaintPropertyNode*);
     int compositorIdForClipNode(const ClipPaintPropertyNode*);
+    int switchToEffectNode(const EffectPaintPropertyNode& nextEffect);
+    int compositorIdForCurrentEffectNode() const { return m_effectStack.last().id; }
 
 private:
+    void buildEffectNodesRecursively(const EffectPaintPropertyNode* nextEffect);
+
     cc::TransformTree& transformTree() { return m_propertyTrees.transform_tree; }
     cc::ClipTree& clipTree() { return m_propertyTrees.clip_tree; }
+    cc::EffectTree& effectTree() { return m_propertyTrees.effect_tree; }
+
+    const EffectPaintPropertyNode* currentEffectNode() const { return m_effectStack.last().effect; }
 
     // Property trees which should be updated by the manager.
     cc::PropertyTrees& m_propertyTrees;
@@ -364,6 +377,17 @@
     // Maps from Blink-side property tree nodes to cc property node indices.
     HashMap<const TransformPaintPropertyNode*, int> m_transformNodeMap;
     HashMap<const ClipPaintPropertyNode*, int> m_clipNodeMap;
+
+    struct BlinkEffectAndCcIdPair {
+        const EffectPaintPropertyNode* effect;
+        int id;
+    };
+    Vector<BlinkEffectAndCcIdPair> m_effectStack;
+
+#if DCHECK_IS_ON()
+    HashSet<const EffectPaintPropertyNode*> m_effectNodesConverted;
+    bool m_isFirstEffectEver;
+#endif
 };
 
 int PropertyTreeManager::compositorIdForTransformNode(const TransformPaintPropertyNode* transformNode)
@@ -442,6 +466,91 @@
     return id;
 }
 
+unsigned depth(const EffectPaintPropertyNode* node)
+{
+    unsigned result = 0;
+    for (; node; node = node->parent())
+        result++;
+    return result;
+}
+
+const EffectPaintPropertyNode* lowestCommonAncestor(const EffectPaintPropertyNode* nodeA, const EffectPaintPropertyNode* nodeB)
+{
+    // Optimized common case.
+    if (nodeA == nodeB)
+        return nodeA;
+
+    unsigned depthA = depth(nodeA), depthB = depth(nodeB);
+    while (depthA > depthB) {
+        nodeA = nodeA->parent();
+        depthA--;
+    }
+    while (depthB > depthA) {
+        nodeB = nodeB->parent();
+        depthB--;
+    }
+    DCHECK_EQ(depthA, depthB);
+    while (nodeA != nodeB) {
+        nodeA = nodeA->parent();
+        nodeB = nodeB->parent();
+    }
+    return nodeA;
+}
+
+int PropertyTreeManager::switchToEffectNode(const EffectPaintPropertyNode& nextEffect)
+{
+    const EffectPaintPropertyNode* ancestor = lowestCommonAncestor(currentEffectNode(), &nextEffect);
+    while (currentEffectNode() != ancestor)
+        m_effectStack.removeLast();
+
+#if DCHECK_IS_ON()
+    DCHECK(m_isFirstEffectEver || currentEffectNode()) << "Malformed effect tree. Nodes in the same property tree should have common root.";
+    m_isFirstEffectEver = false;
+#endif
+    buildEffectNodesRecursively(&nextEffect);
+
+    return compositorIdForCurrentEffectNode();
+}
+
+void PropertyTreeManager::buildEffectNodesRecursively(const EffectPaintPropertyNode* nextEffect)
+{
+    if (nextEffect == currentEffectNode())
+        return;
+    DCHECK(nextEffect);
+
+    buildEffectNodesRecursively(nextEffect->parent());
+    DCHECK_EQ(nextEffect->parent(), currentEffectNode());
+
+#if DCHECK_IS_ON()
+    DCHECK(!m_effectNodesConverted.contains(nextEffect)) << "Malformed paint artifact. Paint chunks under the same effect should be contiguous.";
+    m_effectNodesConverted.add(nextEffect);
+#endif
+
+    // We currently create dummy layers to host effect nodes and corresponding render surface.
+    // This should be removed once cc implements better support for freestanding property trees.
+    scoped_refptr<cc::Layer> dummyLayer = cc::Layer::Create();
+    m_rootLayer->AddChild(dummyLayer);
+
+    // Also cc assumes a clip node is always created by a layer that creates render surface.
+    cc::ClipNode& dummyClip = *clipTree().Node(clipTree().Insert(cc::ClipNode(), kSecondaryRootNodeId));
+    dummyClip.owner_id = dummyLayer->id();
+    dummyClip.transform_id = kRealRootNodeId;
+    dummyClip.target_transform_id = kRealRootNodeId;
+
+    cc::EffectNode& effectNode = *effectTree().Node(effectTree().Insert(cc::EffectNode(), compositorIdForCurrentEffectNode()));
+    effectNode.owner_id = dummyLayer->id();
+    effectNode.clip_id = dummyClip.id;
+    effectNode.has_render_surface = true;
+    effectNode.opacity = nextEffect->opacity();
+    m_effectStack.append(BlinkEffectAndCcIdPair{nextEffect, effectNode.id});
+
+    dummyLayer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
+    dummyLayer->SetTransformTreeIndex(kSecondaryRootNodeId);
+    dummyLayer->SetClipTreeIndex(dummyClip.id);
+    dummyLayer->SetEffectTreeIndex(effectNode.id);
+    dummyLayer->SetScrollTreeIndex(kRealRootNodeId);
+}
+
 } // namespace
 
 void PaintArtifactCompositor::updateInLayerListMode(const PaintArtifact& paintArtifact)
@@ -465,6 +574,7 @@
 
         int transformId = propertyTreeManager.compositorIdForTransformNode(paintChunk.properties.transform.get());
         int clipId = propertyTreeManager.compositorIdForClipNode(paintChunk.properties.clip.get());
+        int effectId = propertyTreeManager.switchToEffectNode(*paintChunk.properties.effect.get());
 
         layer->set_offset_to_transform_parent(layerOffset);
 
@@ -472,7 +582,7 @@
         layer->set_property_tree_sequence_number(kPropertyTreeSequenceNumber);
         layer->SetTransformTreeIndex(transformId);
         layer->SetClipTreeIndex(clipId);
-        layer->SetEffectTreeIndex(kSecondaryRootNodeId);
+        layer->SetEffectTreeIndex(effectId);
         layer->SetScrollTreeIndex(kRealRootNodeId);
 
         if (m_extraDataForTestingEnabled)
diff --git a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
index 4640c277..f51287bf 100644
--- a/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
+++ b/third_party/WebKit/Source/platform/graphics/compositing/PaintArtifactCompositorTest.cpp
@@ -9,9 +9,11 @@
 #include "cc/layers/layer.h"
 #include "cc/test/fake_output_surface.h"
 #include "cc/trees/clip_node.h"
+#include "cc/trees/effect_node.h"
 #include "cc/trees/layer_tree_host.h"
 #include "cc/trees/layer_tree_settings.h"
 #include "platform/RuntimeEnabledFeatures.h"
+#include "platform/graphics/paint/EffectPaintPropertyNode.h"
 #include "platform/graphics/paint/PaintArtifact.h"
 #include "platform/testing/PictureMatchers.h"
 #include "platform/testing/TestPaintArtifact.h"
@@ -33,6 +35,12 @@
     return transform;
 }
 
+EffectPaintPropertyNode* dummyRootEffect()
+{
+    DEFINE_STATIC_REF(EffectPaintPropertyNode, node, EffectPaintPropertyNode::create(1.0));
+    return node;
+}
+
 class PaintArtifactCompositorTest : public ::testing::Test {
 protected:
     void SetUp() override
@@ -97,11 +105,11 @@
         TransformationMatrix().rotate(90), FloatPoint3D(100, 100, 0));
 
     TestPaintArtifact artifact;
-    artifact.chunk(transform, nullptr, nullptr)
+    artifact.chunk(transform, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 100, 100), Color::white);
-    artifact.chunk(nullptr, nullptr, nullptr)
+    artifact.chunk(nullptr, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 100, 100), Color::gray);
-    artifact.chunk(transform, nullptr, nullptr)
+    artifact.chunk(transform, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(100, 100, 200, 100), Color::black);
     update(artifact.build());
 
@@ -139,9 +147,9 @@
         TransformationMatrix().translate(5, 5), FloatPoint3D(), transform1);
 
     TestPaintArtifact artifact;
-    artifact.chunk(transform1, nullptr, nullptr)
+    artifact.chunk(transform1, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 300, 200), Color::white);
-    artifact.chunk(transform2, nullptr, nullptr)
+    artifact.chunk(transform2, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 300, 200), Color::black);
     update(artifact.build());
 
@@ -223,13 +231,13 @@
         nullptr, FloatRoundedRect(200, 200, 700, 100), clip1);
 
     TestPaintArtifact artifact;
-    artifact.chunk(nullptr, clip1, nullptr)
+    artifact.chunk(nullptr, clip1, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::white);
-    artifact.chunk(nullptr, clip2, nullptr)
+    artifact.chunk(nullptr, clip2, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::lightGray);
-    artifact.chunk(nullptr, clip1, nullptr)
+    artifact.chunk(nullptr, clip1, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::darkGray);
-    artifact.chunk(nullptr, clip2, nullptr)
+    artifact.chunk(nullptr, clip2, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::black);
     update(artifact.build());
 
@@ -449,11 +457,11 @@
         TransformationMatrix().rotate(90), FloatPoint3D(100, 100, 0));
 
     TestPaintArtifact artifact;
-    artifact.chunk(transform, nullptr, nullptr)
+    artifact.chunk(transform, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 100, 100), Color::white);
-    artifact.chunk(nullptr, nullptr, nullptr)
+    artifact.chunk(nullptr, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 100, 100), Color::gray);
-    artifact.chunk(transform, nullptr, nullptr)
+    artifact.chunk(transform, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(100, 100, 200, 100), Color::black);
     update(artifact.build());
 
@@ -491,9 +499,9 @@
         TransformationMatrix().translate(5, 5), FloatPoint3D(), transform1);
 
     TestPaintArtifact artifact;
-    artifact.chunk(transform1, nullptr, nullptr)
+    artifact.chunk(transform1, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 300, 200), Color::white);
-    artifact.chunk(transform2, nullptr, nullptr)
+    artifact.chunk(transform2, nullptr, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 300, 200), Color::black);
     update(artifact.build());
 
@@ -549,13 +557,13 @@
         nullptr, FloatRoundedRect(200, 200, 700, 100), clip1);
 
     TestPaintArtifact artifact;
-    artifact.chunk(nullptr, clip1, nullptr)
+    artifact.chunk(nullptr, clip1, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::white);
-    artifact.chunk(nullptr, clip2, nullptr)
+    artifact.chunk(nullptr, clip2, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::lightGray);
-    artifact.chunk(nullptr, clip1, nullptr)
+    artifact.chunk(nullptr, clip1, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::darkGray);
-    artifact.chunk(nullptr, clip2, nullptr)
+    artifact.chunk(nullptr, clip2, dummyRootEffect())
         .rectDrawing(FloatRect(300, 350, 100, 100), Color::black);
     update(artifact.build());
 
@@ -605,7 +613,7 @@
     }
 
     TestPaintArtifact artifact;
-    artifact.chunk(nullptr, clips.last(), nullptr)
+    artifact.chunk(nullptr, clips.last(), dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 200, 200), Color::white);
     update(artifact.build());
 
@@ -637,9 +645,9 @@
         nullptr, FloatRoundedRect(400, 0, 400, 600), commonClip);
 
     TestPaintArtifact artifact;
-    artifact.chunk(nullptr, clip1, nullptr)
+    artifact.chunk(nullptr, clip1, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 640, 480), Color::white);
-    artifact.chunk(nullptr, clip2, nullptr)
+    artifact.chunk(nullptr, clip2, dummyRootEffect())
         .rectDrawing(FloatRect(0, 0, 640, 480), Color::black);
     update(artifact.build());
 
@@ -685,5 +693,47 @@
     EXPECT_EQ(translation(50, 100), layer->screen_space_transform());
 }
 
+TEST_F(PaintArtifactCompositorTestWithPropertyTrees, EffectTreeConversion)
+{
+    RefPtr<EffectPaintPropertyNode> effect1 = EffectPaintPropertyNode::create(0.5, dummyRootEffect());
+    RefPtr<EffectPaintPropertyNode> effect2 = EffectPaintPropertyNode::create(0.3, effect1.get());
+    RefPtr<EffectPaintPropertyNode> effect3 = EffectPaintPropertyNode::create(0.2, dummyRootEffect());
+
+    TestPaintArtifact artifact;
+    artifact.chunk(nullptr, nullptr, effect2.get())
+        .rectDrawing(FloatRect(0, 0, 100, 100), Color::white);
+    artifact.chunk(nullptr, nullptr, effect1.get())
+        .rectDrawing(FloatRect(0, 0, 100, 100), Color::white);
+    artifact.chunk(nullptr, nullptr, effect3.get())
+        .rectDrawing(FloatRect(0, 0, 100, 100), Color::white);
+    update(artifact.build());
+
+    ASSERT_EQ(3u, contentLayerCount());
+
+    const cc::EffectTree& effectTree = propertyTrees().effect_tree;
+    // Node #0 reserved for null; #1 for root render surface; #2 for dummyRootEffect,
+    // plus 3 nodes for those created by this test.
+    ASSERT_EQ(6u, effectTree.size());
+
+    const cc::EffectNode& convertedDummyRootEffect = *effectTree.Node(2);
+    EXPECT_EQ(1, convertedDummyRootEffect.parent_id);
+
+    const cc::EffectNode& convertedEffect1 = *effectTree.Node(3);
+    EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect1.parent_id);
+    EXPECT_FLOAT_EQ(0.5, convertedEffect1.opacity);
+
+    const cc::EffectNode& convertedEffect2 = *effectTree.Node(4);
+    EXPECT_EQ(convertedEffect1.id, convertedEffect2.parent_id);
+    EXPECT_FLOAT_EQ(0.3, convertedEffect2.opacity);
+
+    const cc::EffectNode& convertedEffect3 = *effectTree.Node(5);
+    EXPECT_EQ(convertedDummyRootEffect.id, convertedEffect3.parent_id);
+    EXPECT_FLOAT_EQ(0.2, convertedEffect3.opacity);
+
+    EXPECT_EQ(convertedEffect2.id, contentLayerAt(0)->effect_tree_index());
+    EXPECT_EQ(convertedEffect1.id, contentLayerAt(1)->effect_tree_index());
+    EXPECT_EQ(convertedEffect3.id, contentLayerAt(2)->effect_tree_index());
+}
+
 } // namespace
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp
index 330a8473..6594dbe 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/InjectedScript.cpp
@@ -314,10 +314,10 @@
 
     v8::Maybe<int> lineNumber = message->GetLineNumber(m_context->context());
     if (lineNumber.IsJust())
-        exceptionDetailsObject->setLine(lineNumber.FromJust());
+        exceptionDetailsObject->setLineNumber(lineNumber.FromJust() - 1);
     v8::Maybe<int> columnNumber = message->GetStartColumn(m_context->context());
     if (columnNumber.IsJust())
-        exceptionDetailsObject->setColumn(columnNumber.FromJust());
+        exceptionDetailsObject->setColumnNumber(columnNumber.FromJust());
 
     v8::Local<v8::StackTrace> stackTrace = message->GetStackTrace();
     if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp
index 310e57d..55eff2c4 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleAgentImpl.cpp
@@ -95,13 +95,16 @@
         m_frontend.messageAdded(std::move(expired));
         m_frontend.flush();
     }
-    for (const auto& message : storage->messages())
-        reportMessage(message.get(), false);
+    for (const auto& message : storage->messages()) {
+        if (message->origin() == V8MessageOrigin::kConsole)
+            reportMessage(message.get(), false);
+    }
 }
 
 void V8ConsoleAgentImpl::reportMessage(V8ConsoleMessage* message, bool generatePreview)
 {
-    m_frontend.messageAdded(message->buildInspectorObject(m_session, generatePreview));
+    DCHECK_EQ(V8MessageOrigin::kConsole, message->origin());
+    message->reportToFrontend(&m_frontend, m_session, generatePreview);
     m_frontend.flush();
 }
 
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp
index 3fcdf36..e4c9083 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.cpp
@@ -8,6 +8,7 @@
 #include "platform/v8_inspector/V8ConsoleAgentImpl.h"
 #include "platform/v8_inspector/V8DebuggerImpl.h"
 #include "platform/v8_inspector/V8InspectorSessionImpl.h"
+#include "platform/v8_inspector/V8RuntimeAgentImpl.h"
 #include "platform/v8_inspector/V8StackTraceImpl.h"
 #include "platform/v8_inspector/V8StringUtil.h"
 #include "platform/v8_inspector/public/V8DebuggerClient.h"
@@ -61,7 +62,6 @@
     case WarningMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Warning;
     case ErrorMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Error;
     case InfoMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::Info;
-    case RevokedErrorMessageLevel: return protocol::Console::ConsoleMessage::LevelEnum::RevokedError;
     }
     return protocol::Console::ConsoleMessage::LevelEnum::Log;
 }
@@ -198,7 +198,7 @@
 } // namespace
 
 V8ConsoleMessage::V8ConsoleMessage(
-    double timestampMS,
+    double timestamp,
     MessageSource source,
     MessageLevel level,
     const String16& message,
@@ -208,7 +208,8 @@
     std::unique_ptr<V8StackTrace> stackTrace,
     int scriptId,
     const String16& requestIdentifier)
-    : m_timestamp(timestampMS / 1000.0)
+    : m_origin(V8MessageOrigin::kConsole)
+    , m_timestamp(timestamp)
     , m_source(source)
     , m_level(level)
     , m_message(message)
@@ -220,8 +221,8 @@
     , m_requestIdentifier(requestIdentifier)
     , m_contextId(0)
     , m_type(LogMessageType)
-    , m_messageId(0)
-    , m_relatedMessageId(0)
+    , m_exceptionId(0)
+    , m_revokedExceptionId(0)
 {
 }
 
@@ -229,14 +230,15 @@
 {
 }
 
-std::unique_ptr<protocol::Console::ConsoleMessage> V8ConsoleMessage::buildInspectorObject(V8InspectorSessionImpl* session, bool generatePreview) const
+void V8ConsoleMessage::reportToFrontend(protocol::Console::Frontend* frontend, V8InspectorSessionImpl* session, bool generatePreview) const
 {
+    DCHECK_EQ(V8MessageOrigin::kConsole, m_origin);
     std::unique_ptr<protocol::Console::ConsoleMessage> result =
         protocol::Console::ConsoleMessage::create()
         .setSource(messageSourceValue(m_source))
         .setLevel(messageLevelValue(m_level))
         .setText(m_message)
-        .setTimestamp(m_timestamp)
+        .setTimestamp(m_timestamp / 1000) // TODO(dgozman): migrate this to milliseconds.
         .build();
     result->setType(messageTypeValue(m_type));
     result->setLine(static_cast<int>(m_lineNumber));
@@ -248,23 +250,21 @@
         result->setNetworkRequestId(m_requestIdentifier);
     if (m_contextId)
         result->setExecutionContextId(m_contextId);
-    appendArguments(result.get(), session, generatePreview);
+    std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> args = wrapArguments(session, generatePreview);
+    if (args)
+        result->setParameters(std::move(args));
     if (m_stackTrace)
         result->setStack(m_stackTrace->buildInspectorObject());
-    if (m_messageId)
-        result->setMessageId(m_messageId);
-    if (m_relatedMessageId)
-        result->setRelatedMessageId(m_relatedMessageId);
-    return result;
+    frontend->messageAdded(std::move(result));
 }
 
-void V8ConsoleMessage::appendArguments(protocol::Console::ConsoleMessage* result, V8InspectorSessionImpl* session, bool generatePreview) const
+std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> V8ConsoleMessage::wrapArguments(V8InspectorSessionImpl* session, bool generatePreview) const
 {
     if (!m_arguments.size() || !m_contextId)
-        return;
+        return nullptr;
     InspectedContext* inspectedContext = session->debugger()->getContext(session->contextGroupId(), m_contextId);
     if (!inspectedContext)
-        return;
+        return nullptr;
 
     v8::Isolate* isolate = inspectedContext->isolate();
     v8::HandleScope handles(isolate);
@@ -289,8 +289,57 @@
             args->addItem(std::move(wrapped));
         }
     }
-    if (args)
-        result->setParameters(std::move(args));
+    return args;
+}
+
+void V8ConsoleMessage::reportToFrontend(protocol::Runtime::Frontend* frontend, V8InspectorSessionImpl* session, bool generatePreview) const
+{
+    if (m_origin == V8MessageOrigin::kException) {
+        // TODO(dgozman): unify with InjectedScript::createExceptionDetails.
+        std::unique_ptr<protocol::Runtime::ExceptionDetails> details = protocol::Runtime::ExceptionDetails::create().setText(m_message).build();
+        details->setUrl(m_url);
+        if (m_lineNumber)
+            details->setLineNumber(static_cast<int>(m_lineNumber) - 1);
+        if (m_columnNumber)
+            details->setColumnNumber(static_cast<int>(m_columnNumber) - 1);
+        if (m_scriptId)
+            details->setScriptId(String::number(m_scriptId));
+        if (m_stackTrace)
+            details->setStack(m_stackTrace->buildInspectorObject());
+
+        std::unique_ptr<protocol::Runtime::RemoteObject> exception = wrapException(session, generatePreview);
+
+        if (exception)
+            frontend->exceptionThrown(m_exceptionId, m_timestamp, std::move(details), std::move(exception), m_contextId);
+        else
+            frontend->exceptionThrown(m_exceptionId, m_timestamp, std::move(details));
+        return;
+    }
+    if (m_origin == V8MessageOrigin::kRevokedException) {
+        frontend->exceptionRevoked(m_timestamp, m_message, m_revokedExceptionId);
+        return;
+    }
+    NOTREACHED();
+}
+
+std::unique_ptr<protocol::Runtime::RemoteObject> V8ConsoleMessage::wrapException(V8InspectorSessionImpl* session, bool generatePreview) const
+{
+    if (!m_arguments.size() || !m_contextId)
+        return nullptr;
+    DCHECK_EQ(1u, m_arguments.size());
+    InspectedContext* inspectedContext = session->debugger()->getContext(session->contextGroupId(), m_contextId);
+    if (!inspectedContext)
+        return nullptr;
+
+    v8::Isolate* isolate = inspectedContext->isolate();
+    v8::HandleScope handles(isolate);
+    // TODO(dgozman): should we use different object group?
+    return session->wrapObject(inspectedContext->context(), m_arguments[0]->Get(isolate), "console", generatePreview);
+}
+
+V8MessageOrigin V8ConsoleMessage::origin() const
+{
+    return m_origin;
 }
 
 unsigned V8ConsoleMessage::argumentCount() const
@@ -304,7 +353,7 @@
 }
 
 // static
-std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(double timestampMS, MessageType type, MessageLevel level, const String16& messageText, std::vector<v8::Local<v8::Value>>* arguments, std::unique_ptr<V8StackTrace> stackTrace, InspectedContext* context)
+std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForConsoleAPI(double timestamp, MessageType type, MessageLevel level, const String16& messageText, std::vector<v8::Local<v8::Value>>* arguments, std::unique_ptr<V8StackTrace> stackTrace, InspectedContext* context)
 {
     String16 url;
     unsigned lineNumber = 0;
@@ -325,7 +374,7 @@
             actualMessage = V8ValueStringBuilder::toString(messageArguments.at(0)->Get(context->isolate()), context->isolate());
     }
 
-    std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestampMS, ConsoleAPIMessageSource, level, actualMessage, url, lineNumber, columnNumber, std::move(stackTrace), 0 /* scriptId */, String16() /* requestIdentifier */));
+    std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, ConsoleAPIMessageSource, level, actualMessage, url, lineNumber, columnNumber, std::move(stackTrace), 0 /* scriptId */, String16() /* requestIdentifier */));
     message->m_type = type;
     if (messageArguments.size()) {
         message->m_contextId = context->contextId();
@@ -336,6 +385,28 @@
     return message;
 }
 
+// static
+std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForException(double timestamp, const String16& messageText, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId, v8::Isolate* isolate, int contextId, v8::Local<v8::Value> exception, unsigned exceptionId)
+{
+    std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, JSMessageSource, ErrorMessageLevel, messageText, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, String16() /* requestIdentifier */));
+    message->m_exceptionId = exceptionId;
+    message->m_origin = V8MessageOrigin::kException;
+    if (contextId && !exception.IsEmpty()) {
+        message->m_contextId = contextId;
+        message->m_arguments.push_back(wrapUnique(new v8::Global<v8::Value>(isolate, exception)));
+    }
+    return message;
+}
+
+// static
+std::unique_ptr<V8ConsoleMessage> V8ConsoleMessage::createForRevokedException(double timestamp, const String16& messageText, unsigned revokedExceptionId)
+{
+    std::unique_ptr<V8ConsoleMessage> message = wrapUnique(new V8ConsoleMessage(timestamp, JSMessageSource, ErrorMessageLevel, messageText, String16(), 0, 0, nullptr, 0, String16()));
+    message->m_origin = V8MessageOrigin::kRevokedException;
+    message->m_revokedExceptionId = revokedExceptionId;
+    return message;
+}
+
 void V8ConsoleMessage::contextDestroyed(int contextId)
 {
     if (contextId != m_contextId)
@@ -347,25 +418,6 @@
     m_arguments.swap(empty);
 }
 
-void V8ConsoleMessage::assignId(unsigned id)
-{
-    m_messageId = id;
-}
-
-void V8ConsoleMessage::assignRelatedId(unsigned id)
-{
-    m_relatedMessageId = id;
-}
-
-void V8ConsoleMessage::addArguments(v8::Isolate* isolate, int contextId, std::vector<v8::Local<v8::Value>>* arguments)
-{
-    if (!arguments || !contextId)
-        return;
-    m_contextId = contextId;
-    for (size_t i = 0; i < arguments->size(); ++i)
-        m_arguments.push_back(wrapUnique(new v8::Global<v8::Value>(isolate, arguments->at(i))));
-}
-
 // ------------------------ V8ConsoleMessageStorage ----------------------------
 
 V8ConsoleMessageStorage::V8ConsoleMessageStorage(V8DebuggerImpl* debugger, int contextGroupId)
@@ -386,8 +438,12 @@
         clear();
 
     V8InspectorSessionImpl* session = m_debugger->sessionForContextGroup(m_contextGroupId);
-    if (session)
-        session->consoleAgent()->messageAdded(message.get());
+    if (session) {
+        if (message->origin() == V8MessageOrigin::kConsole)
+            session->consoleAgent()->messageAdded(message.get());
+        else
+            session->runtimeAgent()->exceptionMessageAdded(message.get());
+    }
 
     DCHECK(m_messages.size() <= maxConsoleMessageCount);
     if (m_messages.size() == maxConsoleMessageCount) {
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h
index 2f35957..9cdf132 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8ConsoleMessage.h
@@ -8,6 +8,7 @@
 #include "platform/inspector_protocol/Collections.h"
 #include "platform/inspector_protocol/String16.h"
 #include "platform/v8_inspector/protocol/Console.h"
+#include "platform/v8_inspector/protocol/Runtime.h"
 #include "platform/v8_inspector/public/V8ConsoleTypes.h"
 #include "platform/v8_inspector/public/V8StackTrace.h"
 #include <deque>
@@ -20,10 +21,12 @@
 class V8InspectorSessionImpl;
 class V8StackTrace;
 
+enum class V8MessageOrigin { kConsole, kException, kRevokedException };
+
 class V8ConsoleMessage {
 public:
     V8ConsoleMessage(
-        double timestampMS,
+        double timestamp,
         MessageSource,
         MessageLevel,
         const String16& message,
@@ -36,7 +39,7 @@
     ~V8ConsoleMessage();
 
     static std::unique_ptr<V8ConsoleMessage> createForConsoleAPI(
-        double timestampMS,
+        double timestamp,
         MessageType,
         MessageLevel,
         const String16& message,
@@ -44,18 +47,37 @@
         std::unique_ptr<V8StackTrace>,
         InspectedContext*);
 
-    std::unique_ptr<protocol::Console::ConsoleMessage> buildInspectorObject(V8InspectorSessionImpl*, bool generatePreview) const;
+    static std::unique_ptr<V8ConsoleMessage> createForException(
+        double timestamp,
+        const String16& message,
+        const String16& url,
+        unsigned lineNumber,
+        unsigned columnNumber,
+        std::unique_ptr<V8StackTrace>,
+        int scriptId,
+        v8::Isolate*,
+        int contextId,
+        v8::Local<v8::Value> exception,
+        unsigned exceptionId);
+
+    static std::unique_ptr<V8ConsoleMessage> createForRevokedException(
+        double timestamp,
+        const String16& message,
+        unsigned revokedExceptionId);
+
+    V8MessageOrigin origin() const;
+    void reportToFrontend(protocol::Console::Frontend*, V8InspectorSessionImpl*, bool generatePreview) const;
+    void reportToFrontend(protocol::Runtime::Frontend*, V8InspectorSessionImpl*, bool generatePreview) const;
     unsigned argumentCount() const;
     MessageType type() const;
     void contextDestroyed(int contextId);
-    void assignId(unsigned);
-    void assignRelatedId(unsigned);
-    void addArguments(v8::Isolate*, int contextId, std::vector<v8::Local<v8::Value>>*);
 
 private:
     using Arguments = std::vector<std::unique_ptr<v8::Global<v8::Value>>>;
-    void appendArguments(protocol::Console::ConsoleMessage*, V8InspectorSessionImpl*, bool generatePreview) const;
+    std::unique_ptr<protocol::Array<protocol::Runtime::RemoteObject>> wrapArguments(V8InspectorSessionImpl*, bool generatePreview) const;
+    std::unique_ptr<protocol::Runtime::RemoteObject> wrapException(V8InspectorSessionImpl*, bool generatePreview) const;
 
+    V8MessageOrigin m_origin;
     double m_timestamp;
     MessageSource m_source;
     MessageLevel m_level;
@@ -68,8 +90,8 @@
     String16 m_requestIdentifier;
     int m_contextId;
     MessageType m_type;
-    unsigned m_messageId;
-    unsigned m_relatedMessageId;
+    unsigned m_exceptionId;
+    unsigned m_revokedExceptionId;
     Arguments m_arguments;
 };
 
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
index e945af4..a0b0639 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.cpp
@@ -85,7 +85,7 @@
     , m_client(client)
     , m_capturingStackTracesCount(0)
     , m_muteConsoleCount(0)
-    , m_lastConsoleMessageId(0)
+    , m_lastExceptionId(0)
     , m_enabledAgentsCount(0)
     , m_breakpointsActivated(true)
     , m_runningNestedMessageLoop(false)
@@ -1029,7 +1029,17 @@
     ensureConsoleMessageStorage(contextGroupId)->addMessage(V8ConsoleMessage::createForConsoleAPI(m_client->currentTimeMS(), LogMessageType, LogMessageLevel, message, arguments.size() ? &arguments : nullptr, captureStackTrace(false), inspectedContext));
 }
 
-unsigned V8DebuggerImpl::promiseRejected(v8::Local<v8::Context> context, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId)
+void V8DebuggerImpl::exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId)
+{
+    if (m_muteConsoleCount || !contextGroupId)
+        return;
+    m_client->messageAddedToConsole(contextGroupId, JSMessageSource, ErrorMessageLevel, errorMessage, url, lineNumber, columnNumber, stackTrace.get());
+    unsigned exceptionId = ++m_lastExceptionId;
+    std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForException(m_client->currentTimeMS(), errorMessage, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, m_isolate, 0, v8::Local<v8::Value>(), exceptionId);
+    ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage));
+}
+
+unsigned V8DebuggerImpl::promiseRejected(v8::Local<v8::Context> context, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace> stackTrace, int scriptId)
 {
     if (m_muteConsoleCount)
         return 0;
@@ -1045,17 +1055,10 @@
         message = message.substring(0, 8) + " (in promise)" + message.substring(8);
 
     m_client->messageAddedToConsole(contextGroupId, JSMessageSource, ErrorMessageLevel, message, url, lineNumber, columnNumber, stackTrace.get());
-    std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(new V8ConsoleMessage(m_client->currentTimeMS(), JSMessageSource, ErrorMessageLevel, message, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, String16()));
-    unsigned id = ++m_lastConsoleMessageId;
-    consoleMessage->assignId(id);
-
-    std::vector<v8::Local<v8::Value>> arguments;
-    arguments.push_back(toV8String(m_isolate, defaultMessage));
-    arguments.push_back(reason);
-    consoleMessage->addArguments(m_isolate, contextId(context), &arguments);
-
+    unsigned exceptionId = ++m_lastExceptionId;
+    std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForException(m_client->currentTimeMS(), message, url, lineNumber, columnNumber, std::move(stackTrace), scriptId, m_isolate, contextId(context), exception, exceptionId);
     ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage));
-    return id;
+    return exceptionId;
 }
 
 void V8DebuggerImpl::promiseRejectionRevoked(v8::Local<v8::Context> context, unsigned promiseRejectionId)
@@ -1066,10 +1069,7 @@
     if (!contextGroupId)
         return;
 
-    const String16 message = "Handler added to rejected promise";
-    m_client->messageAddedToConsole(contextGroupId, JSMessageSource, RevokedErrorMessageLevel, message, String16(), 0, 0, nullptr);
-    std::unique_ptr<V8ConsoleMessage> consoleMessage = wrapUnique(new V8ConsoleMessage(m_client->currentTimeMS(), JSMessageSource, RevokedErrorMessageLevel, message, String16(), 0, 0, nullptr, 0, String16()));
-    consoleMessage->assignRelatedId(promiseRejectionId);
+    std::unique_ptr<V8ConsoleMessage> consoleMessage = V8ConsoleMessage::createForRevokedException(m_client->currentTimeMS(), "Handler added to rejected promise", promiseRejectionId);
     ensureConsoleMessageStorage(contextGroupId)->addMessage(std::move(consoleMessage));
 }
 
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
index 1ce6d5b..a4596af 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8DebuggerImpl.h
@@ -131,7 +131,8 @@
     void idleFinished() override;
     bool addConsoleMessage(int contextGroupId, MessageSource, MessageLevel, const String16& message, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId, const String16& requestIdentifier) override;
     void logToConsole(v8::Local<v8::Context>, const String16& message, v8::Local<v8::Value> arg1, v8::Local<v8::Value> arg2) override;
-    unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override;
+    void exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override;
+    unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) override;
     void promiseRejectionRevoked(v8::Local<v8::Context>, unsigned promiseRejectionId) override;
     void consoleMessagesCount(int contextGroupId, unsigned* total, unsigned* withArguments) override;
     std::unique_ptr<V8StackTrace> createStackTrace(v8::Local<v8::StackTrace>) override;
@@ -186,7 +187,7 @@
     ConsoleStorageMap m_consoleStorageMap;
     int m_capturingStackTracesCount;
     int m_muteConsoleCount;
-    unsigned m_lastConsoleMessageId;
+    unsigned m_lastExceptionId;
     int m_enabledAgentsCount;
     bool m_breakpointsActivated;
     v8::Global<v8::Object> m_debuggerScript;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp
index d32cd3f..86601c1 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.cpp
@@ -34,6 +34,7 @@
 #include "platform/v8_inspector/InjectedScript.h"
 #include "platform/v8_inspector/InspectedContext.h"
 #include "platform/v8_inspector/RemoteObjectId.h"
+#include "platform/v8_inspector/V8ConsoleMessage.h"
 #include "platform/v8_inspector/V8DebuggerImpl.h"
 #include "platform/v8_inspector/V8InspectorSessionImpl.h"
 #include "platform/v8_inspector/V8StringUtil.h"
@@ -365,8 +366,13 @@
     m_session->changeInstrumentationCounter(+1);
     m_enabled = true;
     m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, true);
-    v8::HandleScope handles(m_debugger->isolate());
+    m_session->debugger()->enableStackCapturingIfNeeded();
     m_session->reportAllContexts(this);
+    V8ConsoleMessageStorage* storage = m_session->debugger()->ensureConsoleMessageStorage(m_session->contextGroupId());
+    for (const auto& message : storage->messages()) {
+        if (message->origin() == V8MessageOrigin::kException || message->origin() == V8MessageOrigin::kRevokedException)
+            reportMessage(message.get(), false);
+    }
 }
 
 void V8RuntimeAgentImpl::disable(ErrorString* errorString)
@@ -375,6 +381,7 @@
         return;
     m_enabled = false;
     m_state->setBoolean(V8RuntimeAgentImplState::runtimeEnabled, false);
+    m_session->debugger()->disableStackCapturingIfNeeded();
     m_session->discardInjectedScripts();
     reset();
     m_session->changeInstrumentationCounter(-1);
@@ -420,4 +427,17 @@
         m_frontend.inspectRequested(std::move(objectToInspect), std::move(hints));
 }
 
+void V8RuntimeAgentImpl::exceptionMessageAdded(V8ConsoleMessage* message)
+{
+    if (m_enabled)
+        reportMessage(message, true);
+}
+
+void V8RuntimeAgentImpl::reportMessage(V8ConsoleMessage* message, bool generatePreview)
+{
+    DCHECK(message->origin() == V8MessageOrigin::kException || message->origin() == V8MessageOrigin::kRevokedException);
+    message->reportToFrontend(&m_frontend, m_session, generatePreview);
+    m_frontend.flush();
+}
+
 } // namespace blink
diff --git a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h
index 7eb2b135..b8bfceb3 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/V8RuntimeAgentImpl.h
@@ -42,6 +42,7 @@
 class InjectedScript;
 class InspectedContext;
 class RemoteObjectIdBase;
+class V8ConsoleMessage;
 class V8DebuggerImpl;
 class V8InspectorSessionImpl;
 
@@ -115,8 +116,11 @@
     void reportExecutionContextCreated(InspectedContext*);
     void reportExecutionContextDestroyed(InspectedContext*);
     void inspect(std::unique_ptr<protocol::Runtime::RemoteObject> objectToInspect, std::unique_ptr<protocol::DictionaryValue> hints);
+    void exceptionMessageAdded(V8ConsoleMessage*);
 
 private:
+    void reportMessage(V8ConsoleMessage*, bool generatePreview);
+
     V8InspectorSessionImpl* m_session;
     protocol::DictionaryValue* m_state;
     protocol::Runtime::Frontend m_frontend;
diff --git a/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json b/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json
index 5a72748d..d01f37c 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json
+++ b/third_party/WebKit/Source/platform/v8_inspector/js_protocol.json
@@ -133,13 +133,14 @@
             {
                 "id": "ExceptionDetails",
                 "type": "object",
+                "hidden": true,
                 "description": "Detailed information on exception (or error) that was thrown during script compilation or execution.",
                 "properties": [
                     { "name": "text", "type": "string", "description": "Exception text." },
                     { "name": "url", "type": "string", "optional": true, "description": "URL of the message origin." },
                     { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." },
-                    { "name": "line", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message." },
-                    { "name": "column", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message." },
+                    { "name": "lineNumber", "type": "integer", "optional": true, "description": "Line number in the resource that generated this message. (0-based)." },
+                    { "name": "columnNumber", "type": "integer", "optional": true, "description": "Column number in the resource that generated this message. (0-based)." },
                     { "name": "stack", "$ref": "StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." }
                 ]
             },
@@ -308,6 +309,28 @@
                 "description": "Issued when all executionContexts were cleared in browser"
             },
             {
+                "name": "exceptionThrown",
+                "description": "Issued when exception was thrown and unhandled.",
+                "parameters": [
+                    { "name": "exceptionId", "type": "integer", "description": "Exception id." },
+                    { "name": "timestamp", "type": "number", "description": "Number of milliseconds since epoch. TODO(dgozman): unify with Console.Timestamp" },
+                    { "name": "details", "$ref": "ExceptionDetails" },
+                    { "name": "exception", "$ref": "RemoteObject", "optional": true, "description": "Exception object." },
+                    { "name": "executionContextId", "$ref": "ExecutionContextId", "optional": true, "description": "Identifier of the context where exception happened." }
+                ],
+                "hidden": true
+            },
+            {
+                "name": "exceptionRevoked",
+                "description": "Issued when unhandled exception was revoked.",
+                "parameters": [
+                    { "name": "timestamp", "type": "number", "description": "Number of milliseconds since epoch. TODO(dgozman): unify with Console.Timestamp" },
+                    { "name": "message", "type": "string", "description": "Message describing why exception was revoked." },
+                    { "name": "exceptionId", "type": "integer", "description": "The id of revoked exception, as reported in <code>exceptionUnhandled</code>." }
+                ],
+                "hidden": true
+            },
+            {
                 "name": "inspectRequested",
                 "parameters": [
                     { "name": "object", "$ref": "RemoteObject" },
@@ -719,7 +742,7 @@
                 "description": "Console message.",
                 "properties": [
                     { "name": "source", "type": "string", "enum": ["xml", "javascript", "network", "console-api", "storage", "appcache", "rendering", "security", "other", "deprecation"], "description": "Message source." },
-                    { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info", "revokedError"], "description": "Message severity." },
+                    { "name": "level", "type": "string", "enum": ["log", "warning", "error", "debug", "info"], "description": "Message severity." },
                     { "name": "text", "type": "string", "description": "Message text." },
                     { "name": "type", "type": "string", "optional": true, "enum": ["log", "dir", "dirxml", "table", "trace", "clear", "startGroup", "startGroupCollapsed", "endGroup", "assert", "profile", "profileEnd"], "description": "Console message type." },
                     { "name": "scriptId", "type": "string", "optional": true, "description": "Script ID of the message origin." },
@@ -731,9 +754,7 @@
                     { "name": "stack", "$ref": "Runtime.StackTrace", "optional": true, "description": "JavaScript stack trace for assertions and error messages." },
                     { "name": "networkRequestId", "type": "string", "optional": true, "description": "Identifier of the network request associated with this message." },
                     { "name": "timestamp", "$ref": "Timestamp", "description": "Timestamp, when this message was fired.", "hidden": true },
-                    { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true },
-                    { "name": "messageId", "type": "integer", "hidden": true, "optional": true, "description": "Message id." },
-                    { "name": "relatedMessageId", "type": "integer", "hidden": true, "optional": true, "description": "Related message id." }
+                    { "name": "executionContextId", "$ref": "Runtime.ExecutionContextId", "optional": true, "description": "Identifier of the context where this message was created", "hidden": true }
                 ]
             }
         ],
diff --git a/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h b/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h
index 82f1a2b..75f26fe2 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/public/V8ConsoleTypes.h
@@ -25,10 +25,10 @@
     LogMessageLevel = 1,
     InfoMessageLevel = 5,
     WarningMessageLevel = 2,
-    ErrorMessageLevel = 3,
-    RevokedErrorMessageLevel = 6
+    ErrorMessageLevel = 3
 };
 
+// TODO(dgozman): move from public to private.
 enum MessageType {
     LogMessageType = 1,
     DirMessageType,
diff --git a/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h b/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h
index 5ac5e66..c0ebce29 100644
--- a/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h
+++ b/third_party/WebKit/Source/platform/v8_inspector/public/V8Debugger.h
@@ -43,7 +43,9 @@
     virtual bool addConsoleMessage(int contextGroupId, MessageSource, MessageLevel, const String16& message, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId, const String16& requestIdentifier) = 0;
     // TODO(dgozman): can we remove this method?
     virtual void logToConsole(v8::Local<v8::Context>, const String16& message, v8::Local<v8::Value> arg1, v8::Local<v8::Value> arg2) = 0;
-    virtual unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> reason, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0;
+    // TODO(dgozman): can we pass exception object?
+    virtual void exceptionThrown(int contextGroupId, const String16& errorMessage, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0;
+    virtual unsigned promiseRejected(v8::Local<v8::Context>, const String16& errorMessage, v8::Local<v8::Value> exception, const String16& url, unsigned lineNumber, unsigned columnNumber, std::unique_ptr<V8StackTrace>, int scriptId) = 0;
     virtual void promiseRejectionRevoked(v8::Local<v8::Context>, unsigned promiseRejectionId) = 0;
     virtual void consoleMessagesCount(int contextGroupId, unsigned* total, unsigned* withArguments) = 0;
     // TODO(dgozman): remove mute methods.
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index c8a165a..a4a1f40 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -766,7 +766,6 @@
         break;
     // Unsupported values.
     case WebConsoleMessage::LevelInfo:
-    case WebConsoleMessage::LevelRevokedError:
         break;
     }
 
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
index 4fe23f3..5db74a0 100644
--- a/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
+++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/net/layouttestresults.py
@@ -159,3 +159,6 @@
 
     def unexpected_mismatch_results(self):
         return self._filter_tests(lambda r: r.has_mismatch_result() and not r.did_run_as_expected())
+
+    def didnt_run_as_expected_results(self):
+        return self._filter_tests(lambda r: not r.did_run_as_expected())
diff --git a/third_party/WebKit/public/web/WebConsoleMessage.h b/third_party/WebKit/public/web/WebConsoleMessage.h
index d0a109c..2c2c062 100644
--- a/third_party/WebKit/public/web/WebConsoleMessage.h
+++ b/third_party/WebKit/public/web/WebConsoleMessage.h
@@ -42,7 +42,6 @@
         LevelInfo = 5,
         LevelWarning = 2,
         LevelError = 3,
-        LevelRevokedError = 6,
         LevelLast = LevelInfo
     };