diff --git a/DEPS b/DEPS
index 623814e..e7d245f6 100644
--- a/DEPS
+++ b/DEPS
@@ -40,11 +40,11 @@
   # 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': '0396434adf4f7cdc1880046181d72167207f8ca5',
+  'skia_revision': 'dbc8eeb592123619d9c5bb4b6c6225b9fd45d03b',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'd2c38959a71552b4e39e4d9ee3a7cae2c84e5843',
+  'v8_revision': '6b70e6608fb664588720103720a9066bccfe181e',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
diff --git a/WATCHLISTS b/WATCHLISTS
index 484e25b..3a0cdb3a0 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -108,6 +108,11 @@
     'arc_auth': {
       'filepath': 'chrome/browser/chromeos/arc/arc_auth'
     },
+    'arc_kiosk': {
+      'filepath': 'chrome/browser/chromeos/app_mode/arc/'\
+                  '|components/arc/kiosk/'\
+                  '|arc_kiosk'
+    },
     'arc_net': {
       'filepath': 'components/arc/net/',
     },
@@ -1529,6 +1534,7 @@
             'lhchavez+watch@chromium.org',
             'yusukes+watch@chromium.org'],
     'arc_auth': ['khmel+watch@chromium.org'],
+    'arc_kiosk': ['poromov+watch@chromium.org'],
     'arc_net': ['abhishekbh@chromium.org',
                 'cernekee@chromium.org',
                 'snanda@chromium.org'],
diff --git a/ash/common/system/status_area_widget_delegate.cc b/ash/common/system/status_area_widget_delegate.cc
index 920c171e6..26c3ddc3b 100644
--- a/ash/common/system/status_area_widget_delegate.cc
+++ b/ash/common/system/status_area_widget_delegate.cc
@@ -146,14 +146,10 @@
   views::ColumnSet* columns = layout->AddColumnSet(0);
 
   if (IsHorizontalAlignment(alignment_)) {
-    bool is_first_visible_child = true;
     for (int c = child_count() - 1; c >= 0; --c) {
       views::View* child = child_at(c);
       if (!child->visible())
         continue;
-      if (!is_first_visible_child)
-        columns->AddPaddingColumn(0, GetTrayConstant(TRAY_SPACING));
-      is_first_visible_child = false;
       columns->AddColumn(views::GridLayout::CENTER, views::GridLayout::FILL,
                          0, /* resize percent */
                          views::GridLayout::USE_PREF, 0, 0);
@@ -168,14 +164,10 @@
     columns->AddColumn(views::GridLayout::FILL, views::GridLayout::CENTER,
                        0, /* resize percent */
                        views::GridLayout::USE_PREF, 0, 0);
-    bool is_first_visible_child = true;
     for (int c = child_count() - 1; c >= 0; --c) {
       views::View* child = child_at(c);
       if (!child->visible())
         continue;
-      if (!is_first_visible_child)
-        layout->AddPaddingRow(0, GetTrayConstant(TRAY_SPACING));
-      is_first_visible_child = false;
       layout->StartRow(0, 0);
       layout->AddView(child);
     }
diff --git a/ash/common/system/tray/system_tray_item.cc b/ash/common/system/tray/system_tray_item.cc
index 164a0e4..2d34391 100644
--- a/ash/common/system/tray/system_tray_item.cc
+++ b/ash/common/system/tray/system_tray_item.cc
@@ -42,16 +42,11 @@
 void SystemTrayItem::DestroyNotificationView() {}
 
 void SystemTrayItem::TransitionDetailedView() {
-  const int transition_delay =
-      GetTrayConstant(TRAY_POPUP_TRANSITION_TO_DETAILED_DELAY);
-  if (transition_delay <= 0) {
-    DoTransitionToDetailedView();
-    return;
-  }
-  transition_delay_timer_.reset(new base::OneShotTimer());
-  transition_delay_timer_->Start(
-      FROM_HERE, base::TimeDelta::FromMilliseconds(transition_delay), this,
-      &SystemTrayItem::DoTransitionToDetailedView);
+  transition_delay_timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(kTrayDetailedViewTransitionDelayMs),
+      base::Bind(&SystemTray::ShowDetailedView, base::Unretained(system_tray()),
+                 this, 0, true, BUBBLE_USE_EXISTING));
 }
 
 void SystemTrayItem::UpdateAfterLoginStatusChange(LoginStatus status) {}
@@ -84,9 +79,4 @@
   return true;
 }
 
-void SystemTrayItem::DoTransitionToDetailedView() {
-  transition_delay_timer_.reset();
-  system_tray()->ShowDetailedView(this, 0, true, BUBBLE_USE_EXISTING);
-}
-
 }  // namespace ash
diff --git a/ash/common/system/tray/system_tray_item.h b/ash/common/system/tray/system_tray_item.h
index 2e7ca00..68f1966 100644
--- a/ash/common/system/tray/system_tray_item.h
+++ b/ash/common/system/tray/system_tray_item.h
@@ -11,10 +11,7 @@
 #include "ash/common/login_status.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "base/macros.h"
-
-namespace base {
-class OneShotTimer;
-}  // namespace base
+#include "base/timer/timer.h"
 
 namespace views {
 class View;
@@ -154,9 +151,6 @@
   void set_restore_focus(bool restore_focus) { restore_focus_ = restore_focus; }
 
  private:
-  // Actually transitions to the detailed view.
-  void DoTransitionToDetailedView();
-
   // Accesses uma_type().
   friend class SystemTrayBubble;
 
@@ -167,7 +161,7 @@
   bool restore_focus_;
 
   // Used to delay the transition to the detailed view.
-  std::unique_ptr<base::OneShotTimer> transition_delay_timer_;
+  base::OneShotTimer transition_delay_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(SystemTrayItem);
 };
diff --git a/ash/common/system/tray/tray_constants.cc b/ash/common/system/tray/tray_constants.cc
index a08b49e..dd28557b 100644
--- a/ash/common/system/tray/tray_constants.cc
+++ b/ash/common/system/tray/tray_constants.cc
@@ -46,6 +46,9 @@
 const int kTrayPopupPaddingBetweenItems = 10;
 const int kTrayPopupButtonEndMargin = 10;
 const int kTrayPopupLabelHorizontalPadding = 4;
+
+const int kTrayDetailedViewTransitionDelayMs = 100;
+
 const int kTrayPopupSliderPaddingMD = 16;
 const int kTrayPopupLabelRightPadding = 8;
 
@@ -107,7 +110,6 @@
 
 int GetTrayConstant(TrayConstant constant) {
   const int kTrayItemHeightLegacy[] = {38, kTrayItemSize, kTrayItemSize};
-  const int kTraySpacing[] = {4, 0, 0};
   const int kTrayPaddingFromEdgeOfShelf[] = {3, 3, 3};
   const int kTrayPopupItemMinHeight[] = {46, 48, 48};
   const int kTrayPopupItemMaxHeight[] = {138, 144, 144};
@@ -116,11 +118,8 @@
   const int kTrayPopupItemMoreImageSize[] = {25, kMenuIconSize, kMenuIconSize};
   const int kTrayPopupItemMoreRegionHorizontalInset[] = {10, 10, 10};
   const int kTrayPopupItemLeftInset[] = {0, 4, 4};
-  const int kTrayPopupItemRightInset[] = {0, 0, 0};
   const int kTrayPopupItemMinStartWidth[] = {46, 48, 48};
   const int kTrayPopupItemMinEndWidth[] = {40, 40, 40};
-  const int kTrayPopupTransitionToDefaultViewDelayMs[] = {0, 100, 100};
-  const int kTrayPopupTransitionToDetailedViewDelayMs[] = {0, 100, 100};
   const int kVirtualKeyboardButtonSize[] = {39, kTrayItemSize, kTrayItemSize};
   const int kTrayImeMenuIcon[] = {40, kTrayItemSize, kTrayItemSize};
   const int kTrayImageItemPadding[] = {1, 3, 3};
@@ -131,8 +130,6 @@
   switch (constant) {
     case TRAY_ITEM_HEIGHT_LEGACY:
       return kTrayItemHeightLegacy[mode];
-    case TRAY_SPACING:
-      return kTraySpacing[mode];
     case TRAY_PADDING_FROM_EDGE_OF_SHELF:
       return kTrayPaddingFromEdgeOfShelf[mode];
     case TRAY_POPUP_ITEM_MIN_HEIGHT:
@@ -147,16 +144,10 @@
       return kTrayPopupItemMoreRegionHorizontalInset[mode];
     case TRAY_POPUP_ITEM_LEFT_INSET:
       return kTrayPopupItemLeftInset[mode];
-    case TRAY_POPUP_ITEM_RIGHT_INSET:
-      return kTrayPopupItemRightInset[mode];
     case TRAY_POPUP_ITEM_MIN_START_WIDTH:
       return kTrayPopupItemMinStartWidth[mode];
     case TRAY_POPUP_ITEM_MIN_END_WIDTH:
       return kTrayPopupItemMinEndWidth[mode];
-    case TRAY_POPUP_TRANSITION_TO_DEFAULT_DELAY:
-      return kTrayPopupTransitionToDefaultViewDelayMs[mode];
-    case TRAY_POPUP_TRANSITION_TO_DETAILED_DELAY:
-      return kTrayPopupTransitionToDetailedViewDelayMs[mode];
     case VIRTUAL_KEYBOARD_BUTTON_SIZE:
       return kVirtualKeyboardButtonSize[mode];
     case TRAY_IME_MENU_ICON:
diff --git a/ash/common/system/tray/tray_constants.h b/ash/common/system/tray/tray_constants.h
index 29665ae7..87f6175b 100644
--- a/ash/common/system/tray/tray_constants.h
+++ b/ash/common/system/tray/tray_constants.h
@@ -48,6 +48,10 @@
 // in the system menu.
 extern const int kTrayPopupLabelHorizontalPadding;
 
+// When transitioning between a detailed and a default view, this delay is used
+// before the transition starts.
+ASH_EXPORT extern const int kTrayDetailedViewTransitionDelayMs;
+
 // Padding used to adjust the slider position in volume row and brightness
 // row horizontally.
 extern const int kTrayPopupSliderPaddingMD;
@@ -143,9 +147,6 @@
   // borders on tray items.
   TRAY_ITEM_HEIGHT_LEGACY,
 
-  // Padding between items in the status tray area.
-  TRAY_SPACING,
-
   // Padding between the edge of shelf and the item in status tray area.
   TRAY_PADDING_FROM_EDGE_OF_SHELF,
 
@@ -168,21 +169,12 @@
   // The left inset for all tray system menu rows.
   TRAY_POPUP_ITEM_LEFT_INSET,
 
-  // The right inset for all tray system menu rows.
-  TRAY_POPUP_ITEM_RIGHT_INSET,
-
   // The minimum default width for the left container of the system menu rows.
   TRAY_POPUP_ITEM_MIN_START_WIDTH,
 
   // The minimum default width for the right container of the system menu rows.
   TRAY_POPUP_ITEM_MIN_END_WIDTH,
 
-  // Duration to delay transitions to the default view.
-  TRAY_POPUP_TRANSITION_TO_DEFAULT_DELAY,
-
-  // Duration to delay transitions to the detailed view.
-  TRAY_POPUP_TRANSITION_TO_DETAILED_DELAY,
-
   // The width and height of the virtual keyboard button in the status tray
   // area. For non-MD, adjustments are made to the button dimensions based on
   // the shelf orientation, so this constant does not specify the true
diff --git a/ash/common/system/tray/tray_details_view.cc b/ash/common/system/tray/tray_details_view.cc
index b7749501..4cf0f97b 100644
--- a/ash/common/system/tray/tray_details_view.cc
+++ b/ash/common/system/tray/tray_details_view.cc
@@ -15,7 +15,6 @@
 #include "ash/common/system/tray/tri_view.h"
 #include "base/containers/adapters.h"
 #include "base/memory/ptr_util.h"
-#include "base/timer/timer.h"
 #include "grit/ash_strings.h"
 #include "third_party/skia/include/core/SkDrawLooper.h"
 #include "ui/base/resource/resource_bundle.h"
@@ -458,22 +457,13 @@
     return;
   }
 
-  const int transition_delay =
-      GetTrayConstant(TRAY_POPUP_TRANSITION_TO_DEFAULT_DELAY);
-  if (transition_delay <= 0) {
-    DoTransitionToDefaultView();
-    return;
-  }
-
-  transition_delay_timer_.reset(new base::OneShotTimer());
-  transition_delay_timer_->Start(
-      FROM_HERE, base::TimeDelta::FromMilliseconds(transition_delay), this,
-      &TrayDetailsView::DoTransitionToDefaultView);
+  transition_delay_timer_.Start(
+      FROM_HERE,
+      base::TimeDelta::FromMilliseconds(kTrayDetailedViewTransitionDelayMs),
+      this, &TrayDetailsView::DoTransitionToDefaultView);
 }
 
 void TrayDetailsView::DoTransitionToDefaultView() {
-  transition_delay_timer_.reset();
-
   // Cache pointer to owner in this function scope. TrayDetailsView will be
   // deleted after called ShowDefaultView.
   SystemTrayItem* owner = owner_;
diff --git a/ash/common/system/tray/tray_details_view.h b/ash/common/system/tray/tray_details_view.h
index d7046e7..2828763 100644
--- a/ash/common/system/tray/tray_details_view.h
+++ b/ash/common/system/tray/tray_details_view.h
@@ -12,14 +12,10 @@
 #include "ash/common/system/tray/tray_constants.h"
 #include "ash/common/system/tray/view_click_listener.h"
 #include "base/macros.h"
-#include "grit/ash_strings.h"
+#include "base/timer/timer.h"
 #include "ui/views/controls/button/button.h"
 #include "ui/views/view.h"
 
-namespace base {
-class OneShotTimer;
-}  // namespace base
-
 namespace views {
 class BoxLayout;
 class CustomButton;
@@ -139,7 +135,7 @@
   views::Button* back_button_;
 
   // Used to delay the transition to the default view.
-  std::unique_ptr<base::OneShotTimer> transition_delay_timer_;
+  base::OneShotTimer transition_delay_timer_;
 
   DISALLOW_COPY_AND_ASSIGN(TrayDetailsView);
 };
diff --git a/ash/common/system/tray/tray_details_view_unittest.cc b/ash/common/system/tray/tray_details_view_unittest.cc
index a62f8b6..e8543c4 100644
--- a/ash/common/system/tray/tray_details_view_unittest.cc
+++ b/ash/common/system/tray/tray_details_view_unittest.cc
@@ -126,8 +126,8 @@
 
   void TransitionFromDetailedToDefaultView(TestDetailsView* detailed) {
     detailed->TransitionToDefaultView();
-    scoped_task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(
-        GetTrayConstant(TRAY_POPUP_TRANSITION_TO_DEFAULT_DELAY)));
+    scoped_task_runner_->FastForwardBy(
+        base::TimeDelta::FromMilliseconds(kTrayDetailedViewTransitionDelayMs));
   }
 
   void FocusBackButton(TestDetailsView* detailed) {
diff --git a/ash/common/system/tray/tray_popup_utils.cc b/ash/common/system/tray/tray_popup_utils.cc
index b409a3ec..e0481892 100644
--- a/ash/common/system/tray/tray_popup_utils.cc
+++ b/ash/common/system/tray/tray_popup_utils.cc
@@ -173,9 +173,7 @@
 
 TriView* TrayPopupUtils::CreateSubHeaderRowView() {
   TriView* tri_view = CreateMultiTargetRowView();
-  tri_view->SetInsets(
-      gfx::Insets(0, kTrayPopupPaddingHorizontal, 0,
-                  GetTrayConstant(TRAY_POPUP_ITEM_RIGHT_INSET)));
+  tri_view->SetInsets(gfx::Insets(0, kTrayPopupPaddingHorizontal, 0, 0));
   tri_view->SetContainerVisible(TriView::Container::START, false);
   tri_view->SetContainerLayout(
       TriView::Container::END,
@@ -187,8 +185,7 @@
   TriView* tri_view = new TriView(0 /* padding_between_items */);
 
   tri_view->SetInsets(
-      gfx::Insets(0, GetTrayConstant(TRAY_POPUP_ITEM_LEFT_INSET), 0,
-                  GetTrayConstant(TRAY_POPUP_ITEM_RIGHT_INSET)));
+      gfx::Insets(0, GetTrayConstant(TRAY_POPUP_ITEM_LEFT_INSET), 0, 0));
 
   ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::START);
   ConfigureDefaultSizeAndFlex(tri_view, TriView::Container::CENTER);
diff --git a/base/debug/stack_trace.cc b/base/debug/stack_trace.cc
index 83eb415..94ff7d07 100644
--- a/base/debug/stack_trace.cc
+++ b/base/debug/stack_trace.cc
@@ -111,14 +111,12 @@
   // Check alignment.
   if (fp & (sizeof(uintptr_t) - 1)) return false;
 
-  // A PC that is too small means we've gone off the end of the stack.
-  const uintptr_t kMinimumReasonablePC = 32768;
-  if (GetStackFramePC(fp) < kMinimumReasonablePC)
-    return false;
-
   if (stack_end) {
     // Both fp[0] and fp[1] must be within the stack.
     if (fp > stack_end - 2 * sizeof(uintptr_t)) return false;
+
+    // Additional check to filter out false positives.
+    if (GetStackFramePC(fp) < 32768) return false;
   }
 
   return true;
diff --git a/base/security_unittest.cc b/base/security_unittest.cc
index d5637bb..24fbbd7 100644
--- a/base/security_unittest.cc
+++ b/base/security_unittest.cc
@@ -87,16 +87,14 @@
   }
 }
 
-#if defined(OS_IOS) || defined(OS_LINUX) || defined(ADDRESS_SANITIZER)
+#if defined(OS_IOS) || defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
 #define MAYBE_NewOverflow DISABLED_NewOverflow
 #else
 #define MAYBE_NewOverflow NewOverflow
 #endif
 // Test array[TooBig][X] and array[X][TooBig] allocations for int overflows.
 // IOS doesn't honor nothrow, so disable the test there.
-// Disabled on Linux because failing Linux Valgrind bot, and Valgrind exclusions
-// are not currently read. See http://crbug.com/582398
-// Disabled under ASan because asan aborts when new returns nullptr,
+// Disabled under XSan because asan aborts when new returns nullptr,
 // https://bugs.chromium.org/p/chromium/issues/detail?id=690271#c15
 TEST(SecurityTest, MAYBE_NewOverflow) {
   const size_t kArraySize = 4096;
diff --git a/cc/trees/layer_tree_impl.cc b/cc/trees/layer_tree_impl.cc
index 2a05f4f..0171087 100644
--- a/cc/trees/layer_tree_impl.cc
+++ b/cc/trees/layer_tree_impl.cc
@@ -565,7 +565,12 @@
   LayerImpl* existing_layer = LayerByElementId(element_id);
   bool element_id_collision_detected =
       existing_layer && existing_layer != layer;
-  DCHECK(!element_id_collision_detected);
+
+  // TODO(pdr): Remove this suppression and always check for id collisions.
+  // This is a temporary suppression for SPV2 which generates unnecessary
+  // layers that collide. Remove once crbug.com/693693 is fixed.
+  if (!settings().use_layer_lists)
+    DCHECK(!element_id_collision_detected);
 #endif
 
   element_layers_map_[element_id] = layer->id();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
index 64b5858..e2799f6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/BottomSheet.java
@@ -262,7 +262,7 @@
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent e) {
-        if (isToolbarAndroidViewHidden()) return false;
+        if (!canMoveSheet()) return false;
 
         // The incoming motion event may have been adjusted by the view sending it down. Create a
         // motion event with the raw (x, y) coordinates of the original so the gesture detector
@@ -755,4 +755,11 @@
 
     @Override
     public void onFadingViewVisibilityChanged(boolean visible) {}
+
+    private boolean canMoveSheet() {
+        boolean isInOverviewMode = mTabModelSelector != null
+                && (mTabModelSelector.getCurrentTab() == null
+                           || mTabModelSelector.getCurrentTab().getActivity().isInOverviewMode());
+        return !isToolbarAndroidViewHidden() && !isInOverviewMode;
+    }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
new file mode 100644
index 0000000..87d040abf
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/OWNERS
@@ -0,0 +1,2 @@
+per-file BottomSheet*=mdjones@chromium.org   
+per-file ToolbarProgressBar*=mdjones@chromium.org   
diff --git a/chrome/app/settings_chromium_strings.grdp b/chrome/app/settings_chromium_strings.grdp
index 69dd33d6..9fc22be 100644
--- a/chrome/app/settings_chromium_strings.grdp
+++ b/chrome/app/settings_chromium_strings.grdp
@@ -54,7 +54,7 @@
 
   <!-- Privacy Page -->
   <message name="IDS_SETTINGS_IMPROVE_BROWSING_EXPERIENCE" desc="The text in the options panel that describes how we use web services to improve browsing experience.">
-    Chromium may use <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>web services<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> to improve your browsing experience. You may optionally disable these services at any time.
+    Chromium may use web services to improve your browsing experience. You may optionally disable these services. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
 
   <!-- People Page -->
diff --git a/chrome/app/settings_google_chrome_strings.grdp b/chrome/app/settings_google_chrome_strings.grdp
index 5cf2399..127499502 100644
--- a/chrome/app/settings_google_chrome_strings.grdp
+++ b/chrome/app/settings_google_chrome_strings.grdp
@@ -54,7 +54,7 @@
 
   <!-- Privacy Page -->
   <message name="IDS_SETTINGS_IMPROVE_BROWSING_EXPERIENCE" desc="The text in the options panel that describes how we use web services to improve browsing experience.">
-    Google Chrome may use <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>web services<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph> to improve your browsing experience. You may optionally disable these services at any time.
+    Google Chrome may use web services to improve your browsing experience. You may optionally disable these services. <ph name="BEGIN_LINK">&lt;a target="_blank" href="$1"&gt;</ph>Learn more<ph name="END_LINK">&lt;/a&gt;<ex>&lt;/a&gt;</ex></ph>
   </message>
 
   <!-- People Page -->
diff --git a/chrome/browser/android/vr_shell/ui_elements.cc b/chrome/browser/android/vr_shell/ui_elements.cc
index d90f5d7..3d6cf00 100644
--- a/chrome/browser/android/vr_shell/ui_elements.cc
+++ b/chrome/browser/android/vr_shell/ui_elements.cc
@@ -196,7 +196,7 @@
 }
 
 bool ContentRectangle::IsHitTestable() const {
-  return IsVisible() && hit_testable;
+  return hit_testable;
 }
 
 }  // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc
index 8824338..a85fd877 100644
--- a/chrome/browser/android/vr_shell/ui_scene.cc
+++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -16,6 +16,17 @@
 
 namespace {
 
+bool ParseFloat(const base::DictionaryValue& dict,
+                const std::string& key,
+                float* output) {
+  double value;
+  if (!dict.GetDouble(key, &value)) {
+    return false;
+  }
+  *output = value;
+  return true;
+}
+
 bool ParseRecti(const base::DictionaryValue& dict,
                 const std::string& key,
                 Recti* output) {
@@ -323,6 +334,7 @@
 
 void UiScene::UpdateBackgroundFromDict(const base::DictionaryValue& dict) {
   ParseColorf(dict, "color", &background_color_);
+  ParseFloat(dict, "distance", &background_distance_);
 }
 
 void UiScene::HandleCommands(std::unique_ptr<base::ListValue> commands,
@@ -390,14 +402,14 @@
   return nullptr;
 }
 
-ContentRectangle* UiScene::GetContentQuad() {
-  return content_element_;
-}
-
 const Colorf& UiScene::GetBackgroundColor() {
   return background_color_;
 }
 
+float UiScene::GetBackgroundDistance() {
+  return background_distance_;
+}
+
 const std::vector<std::unique_ptr<ContentRectangle>>&
 UiScene::GetUiElements() const {
   return ui_elements_;
diff --git a/chrome/browser/android/vr_shell/ui_scene.h b/chrome/browser/android/vr_shell/ui_scene.h
index 1b1686f..3ef5eec1 100644
--- a/chrome/browser/android/vr_shell/ui_scene.h
+++ b/chrome/browser/android/vr_shell/ui_scene.h
@@ -71,9 +71,8 @@
 
   ContentRectangle* GetUiElementById(int element_id);
 
-  ContentRectangle* GetContentQuad();
-
   const Colorf& GetBackgroundColor();
+  float GetBackgroundDistance();
 
  private:
   void ApplyRecursiveTransforms(const ContentRectangle& element,
@@ -85,6 +84,7 @@
   std::vector<std::unique_ptr<ContentRectangle>> ui_elements_;
   ContentRectangle* content_element_ = nullptr;
   Colorf background_color_ = {0.1f, 0.1f, 0.1f, 1.0f};
+  float background_distance_ = 10.0f;
 
   DISALLOW_COPY_AND_ASSIGN(UiScene);
 };
diff --git a/chrome/browser/android/vr_shell/ui_scene_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
index 02823d75..dd8ccb9f 100644
--- a/chrome/browser/android/vr_shell/ui_scene_unittest.cc
+++ b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
@@ -69,35 +69,6 @@
   EXPECT_EQ(scene.GetUiElements().size(), 0u);
 }
 
-TEST(UiScene, AddRemoveContentQuad) {
-  UiScene scene;
-
-  EXPECT_EQ(scene.GetContentQuad(), nullptr);
-
-  base::DictionaryValue dict;
-  dict.SetInteger("id", 0);
-  dict.SetInteger("fillType", Fill::CONTENT);
-  scene.AddUiElementFromDict(dict);
-  EXPECT_NE(scene.GetContentQuad(), nullptr);
-
-  dict.SetInteger("fillType", Fill::SPRITE);
-  std::unique_ptr<base::DictionaryValue> copy_rect(new base::DictionaryValue);
-  copy_rect->SetInteger("x", 100);
-  copy_rect->SetInteger("y", 101);
-  copy_rect->SetInteger("width", 102);
-  copy_rect->SetInteger("height", 103);
-  dict.Set("copyRect", std::move(copy_rect));
-  scene.UpdateUiElementFromDict(dict);
-  EXPECT_EQ(scene.GetContentQuad(), nullptr);
-
-  dict.SetInteger("fillType", Fill::CONTENT);
-  scene.UpdateUiElementFromDict(dict);
-  EXPECT_NE(scene.GetContentQuad(), nullptr);
-
-  scene.RemoveUiElement(0);
-  EXPECT_EQ(scene.GetContentQuad(), nullptr);
-}
-
 TEST(UiScene, AddRemoveAnimations) {
   UiScene scene;
   addElement(&scene, 0);
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc
index ac346c5..73afb711 100644
--- a/chrome/browser/android/vr_shell/vr_shell_gl.cc
+++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -57,19 +57,10 @@
 // TODO(mthiesse): Handedness options.
 static constexpr gvr::Vec3f kHandPosition = {0.2f, -0.5f, -0.2f};
 
-// If there is no content quad, and the reticle isn't hitting another element,
-// draw the reticle at this distance.
-static constexpr float kDefaultReticleDistance = 2.0f;
-
 // Fraction of the distance to the object the cursor is drawn at to avoid
 // rounding errors drawing the cursor behind the object.
 static constexpr float kReticleOffset = 0.99f;
 
-// Limit the rendering distance of the reticle to the distance to a corner of
-// the content quad, times this value. This lets the rendering distance
-// adjust according to content quad placement.
-static constexpr float kReticleDistanceMultiplier = 1.5f;
-
 // GVR buffer indices for use with viewport->SetSourceBufferIndex
 // or frame.BindBuffer. We use one for world content (with reprojection)
 // including main VrShell and WebVR content plus world-space UI.
@@ -471,37 +462,23 @@
   // in the field of view. This is physically correct, but hard to use. For
   // usability, do the following instead:
   //
-  // - Project the controller laser onto an outer surface, which is the
-  //   closer of the desktop plane, or a distance-limiting sphere.
+  // - Project the controller laser onto a distance-limiting sphere.
   // - Create a vector between the eyes and the outer surface point.
-  // - If any UI elements intersect this vector, choose the closest to the eyes,
-  //   and place the reticle at the intersection point.
+  // - If any UI elements intersect this vector, and is within the bounding
+  //   sphere, choose the closest to the eyes, and place the reticle at the
+  //   intersection point.
 
-  // Find distance to a corner of the content quad, and limit the cursor
-  // distance to a multiple of that distance. This lets us keep the reticle on
-  // the content plane near the content window, and on the surface of a sphere
-  // in other directions. Note that this approach uses distance from controller,
-  // rather than eye, for simplicity. This will make the sphere slightly
-  // off-center.
-  float distance = kDefaultReticleDistance;
-  ContentRectangle* content_plane = scene_->GetContentQuad();
-  if (content_plane) {
-    distance = content_plane->GetRayDistance(origin, forward);
-    gvr::Vec3f corner = {0.5f, 0.5f, 0.0f};
-    corner = MatrixVectorMul(content_plane->transform.to_world, corner);
-    float max_distance = Distance(origin, corner) * kReticleDistanceMultiplier;
-    if (distance > max_distance || distance <= 0.0f) {
-      distance = max_distance;
-    }
-  }
-
+  // Compute the distance from the eyes to the distance limiting sphere. Note
+  // that the sphere is centered at the controller, rather than the eye, for
+  // simplicity.
+  float distance = scene_->GetBackgroundDistance();
   target_point_ = GetRayPoint(origin, forward, distance);
   gvr::Vec3f eye_to_target = target_point_;
   NormalizeVector(eye_to_target);
 
   // Determine which UI element (if any) intersects the line between the eyes
   // and the controller target position.
-  float closest_element_distance = std::numeric_limits<float>::infinity();
+  float closest_element_distance = VectorLength(target_point_);
   int pixel_x = 0;
   int pixel_y = 0;
   target_element_ = nullptr;
diff --git a/chrome/browser/chromeos/login/login_browsertest.cc b/chrome/browser/chromeos/login/login_browsertest.cc
index 139cdd8..8563c3c6 100644
--- a/chrome/browser/chromeos/login/login_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_browsertest.cc
@@ -209,7 +209,8 @@
 }
 
 // Verifies the cursor is not hidden at startup when user is logged in.
-IN_PROC_BROWSER_TEST_F(LoginUserTest, CursorShown) {
+// Test is flaky https://crbug.com/693106
+IN_PROC_BROWSER_TEST_F(LoginUserTest, DISABLED_CursorShown) {
   EXPECT_TRUE(ash::Shell::GetInstance()->cursor_manager()->IsCursorVisible());
 
   TestSystemTrayIsVisible();
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
index 434f1bd..1f9948c2 100644
--- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
+++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.cc
@@ -324,12 +324,11 @@
   return false;
 }
 
-bool ChromeRuntimeAPIDelegate::OpenOptionsPage(const Extension* extension) {
-  Profile* profile = Profile::FromBrowserContext(browser_context_);
-  Browser* browser = chrome::FindLastActiveWithProfile(profile);
-  if (!browser)
-    return false;
-  return extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser);
+bool ChromeRuntimeAPIDelegate::OpenOptionsPage(
+    const Extension* extension,
+    content::BrowserContext* browser_context) {
+  return extensions::ExtensionTabUtil::OpenOptionsPageFromAPI(extension,
+                                                              browser_context);
 }
 
 void ChromeRuntimeAPIDelegate::Observe(
diff --git a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
index 6ff1a05..e8649ed 100644
--- a/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
+++ b/chrome/browser/extensions/api/runtime/chrome_runtime_api_delegate.h
@@ -58,7 +58,8 @@
   void OpenURL(const GURL& uninstall_url) override;
   bool GetPlatformInfo(extensions::api::runtime::PlatformInfo* info) override;
   bool RestartDevice(std::string* error_message) override;
-  bool OpenOptionsPage(const extensions::Extension* extension) override;
+  bool OpenOptionsPage(const extensions::Extension* extension,
+                       content::BrowserContext* browser_context) override;
 
   // content::NotificationObserver implementation.
   void Observe(int type,
diff --git a/chrome/browser/extensions/extension_tab_util.cc b/chrome/browser/extensions/extension_tab_util.cc
index 163ebfb5..9e28e8c 100644
--- a/chrome/browser/extensions/extension_tab_util.cc
+++ b/chrome/browser/extensions/extension_tab_util.cc
@@ -623,15 +623,35 @@
   return NULL;
 }
 
+bool ExtensionTabUtil::OpenOptionsPageFromAPI(
+    const Extension* extension,
+    content::BrowserContext* browser_context) {
+  if (!OptionsPageInfo::HasOptionsPage(extension))
+    return false;
+  Profile* profile = Profile::FromBrowserContext(browser_context);
+  // This version of OpenOptionsPage() is only called when the extension
+  // initiated the command via chrome.runtime.openOptionsPage. For a spanning
+  // mode extension, this API could only be called from a regular profile, since
+  // that's the only place it's running.
+  DCHECK(!profile->IsOffTheRecord() || IncognitoInfo::IsSplitMode(extension));
+  Browser* browser = chrome::FindBrowserWithProfile(profile);
+  if (!browser)
+    browser = new Browser(Browser::CreateParams(profile));
+  return extensions::ExtensionTabUtil::OpenOptionsPage(extension, browser);
+}
+
 bool ExtensionTabUtil::OpenOptionsPage(const Extension* extension,
                                        Browser* browser) {
   if (!OptionsPageInfo::HasOptionsPage(extension))
     return false;
 
-  // Force the options page to open in non-OTR window, because it won't be
-  // able to save settings from OTR.
+  // Force the options page to open in non-OTR window if the extension is not
+  // running in split mode, because it won't be able to save settings from OTR.
+  // This version of OpenOptionsPage() can be called from an OTR window via e.g.
+  // the action menu, since that's not initiated by the extension.
   std::unique_ptr<chrome::ScopedTabbedBrowserDisplayer> displayer;
-  if (browser->profile()->IsOffTheRecord()) {
+  if (browser->profile()->IsOffTheRecord() &&
+      !IncognitoInfo::IsSplitMode(extension)) {
     displayer.reset(new chrome::ScopedTabbedBrowserDisplayer(
         browser->profile()->GetOriginalProfile()));
     browser = displayer->browser();
diff --git a/chrome/browser/extensions/extension_tab_util.h b/chrome/browser/extensions/extension_tab_util.h
index b01d362..06dac73a 100644
--- a/chrome/browser/extensions/extension_tab_util.h
+++ b/chrome/browser/extensions/extension_tab_util.h
@@ -169,6 +169,13 @@
 
   // Open the extension's options page. Returns true if an options page was
   // successfully opened (though it may not necessarily *load*, e.g. if the
+  // URL does not exist). This call to open the options page is iniatiated by
+  // the extension via chrome.runtime.openOptionsPage.
+  static bool OpenOptionsPageFromAPI(const Extension* extension,
+                                     content::BrowserContext* browser_context);
+
+  // Open the extension's options page. Returns true if an options page was
+  // successfully opened (though it may not necessarily *load*, e.g. if the
   // URL does not exist).
   static bool OpenOptionsPage(const Extension* extension, Browser* browser);
 
diff --git a/chrome/browser/extensions/extension_tab_util_browsertest.cc b/chrome/browser/extensions/extension_tab_util_browsertest.cc
index 4362f4e76..a458cd1 100644
--- a/chrome/browser/extensions/extension_tab_util_browsertest.cc
+++ b/chrome/browser/extensions/extension_tab_util_browsertest.cc
@@ -5,6 +5,8 @@
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_finder.h"
+#include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
 #include "content/public/test/browser_test_utils.h"
@@ -117,4 +119,184 @@
   EXPECT_EQ(options_url, GetActiveUrl(browser()));
 }
 
+IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,
+                       OpenSplitModeExtensionOptionsPageIncognito) {
+  const Extension* options_split_extension = LoadExtensionIncognito(
+      test_data_dir_.AppendASCII("options_page_split_incognito"));
+  ASSERT_TRUE(options_split_extension);
+  ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_split_extension));
+  GURL options_url = OptionsPageInfo::GetOptionsPage(options_split_extension);
+
+  Browser* incognito = CreateIncognitoBrowser();
+
+  // There should be two browser windows open, regular and incognito.
+  EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+
+  // In the regular browser window, start at the new tab page, and then open the
+  // extension options page.
+  ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab"));
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_TRUE(
+      ExtensionTabUtil::OpenOptionsPage(options_split_extension, browser()));
+
+  // Opening the options page should take the new tab and use it, so we should
+  // have only one tab, and it should be open to the options page.
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(browser()));
+
+  // If the options page is already opened from a regular window, calling
+  // OpenOptionsPage() from an incognito window should not refocus to the
+  // options page in the regular window, but instead open the options page in
+  // the incognito window.
+  ui_test_utils::NavigateToURL(incognito, GURL("chrome://newtab"));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
+                                                       incognito->profile()));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      incognito->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(incognito));
+
+  // Both regular and incognito windows should have one tab each.
+  EXPECT_EQ(1, browser()->tab_strip_model()->count());
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+
+  // Reset the incognito browser.
+  CloseBrowserSynchronously(incognito);
+  EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+  incognito = CreateIncognitoBrowser();
+
+  // Close the regular browser.
+  CloseBrowserSynchronously(browser());
+  EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+
+  // In the incognito browser, start at the new tab page, and then open the
+  // extension options page.
+  ui_test_utils::NavigateToURL(incognito, GURL("chrome://newtab"));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
+                                                       incognito->profile()));
+
+  // Opening the options page should take the new tab and use it, so we should
+  // have only one tab, and it should be open to the options page.
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      incognito->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(incognito));
+
+  // Calling OpenOptionsPage again shouldn't result in any new tabs, since we
+  // re-use the existing options page.
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
+                                                       incognito->profile()));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      incognito->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(incognito));
+
+  // Navigate to google.com (something non-newtab, non-options). Calling
+  // OpenOptionsPage() should create a new tab and navigate it to the options
+  // page. So we should have two total tabs, with the active tab pointing to
+  // options.
+  ui_test_utils::NavigateToURL(incognito, GURL("http://www.google.com/"));
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(options_split_extension,
+                                                       incognito->profile()));
+  EXPECT_EQ(2, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      incognito->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(incognito));
+}
+
+IN_PROC_BROWSER_TEST_F(ExtensionTabUtilBrowserTest,
+                       OpenSpanningModeExtensionOptionsPageIncognito) {
+  const Extension* options_spanning_extension = LoadExtensionIncognito(
+      test_data_dir_.AppendASCII("options_page_spanning_incognito"));
+  ASSERT_TRUE(options_spanning_extension);
+  ASSERT_TRUE(OptionsPageInfo::HasOptionsPage(options_spanning_extension));
+  GURL options_url =
+      OptionsPageInfo::GetOptionsPage(options_spanning_extension);
+
+  // Start a regular browser window with two tabs, one that is non-options,
+  // non-newtab and the other that is the options page.
+  ui_test_utils::NavigateToURL(browser(), GURL("http://www.google.com/"));
+  EXPECT_TRUE(
+      ExtensionTabUtil::OpenOptionsPage(options_spanning_extension, browser()));
+  EXPECT_EQ(2, browser()->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(browser()));
+  // Switch to tab containing google.com such that it is the active tab.
+  browser()->tab_strip_model()->SelectPreviousTab();
+  EXPECT_EQ(GURL("http://www.google.com/"), GetActiveUrl(browser()));
+
+  // Spanning mode extensions can never open pages in incognito so a regular
+  // (non-OTR) profile must be used. If the options page is already opened from
+  // a regular window, calling OpenOptionsPage() from an incognito window should
+  // refocus to the options page in the regular window.
+  Browser* incognito = CreateIncognitoBrowser();
+  ui_test_utils::NavigateToURL(incognito, GURL("chrome://newtab"));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(
+      options_spanning_extension, profile()));
+  // There should be two browser windows open, regular and incognito.
+  EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+  // Ensure that the regular browser is the foreground browser.
+  EXPECT_EQ(browser(), BrowserList::GetInstance()->GetLastActive());
+  // The options page in the regular window should be in focus instead of
+  // the tab pointing to www.google.com.
+  EXPECT_TRUE(content::WaitForLoadStop(
+      browser()->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(browser()));
+
+  // Only the incognito browser should be left.
+  CloseBrowserSynchronously(browser());
+  EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+
+  // Start at the new tab page in incognito and open the extension options page.
+  ui_test_utils::NavigateToURL(incognito, GURL("chrome://newtab"));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  EXPECT_TRUE(ExtensionTabUtil::OpenOptionsPageFromAPI(
+      options_spanning_extension, profile()));
+
+  // Opening the options page from an incognito window should open a new regular
+  // profile window, which should have one tab open to the options page.
+  ASSERT_EQ(2u, chrome::GetTotalBrowserCount());
+  BrowserList* browser_list = BrowserList::GetInstance();
+  Browser* regular = !browser_list->get(0u)->profile()->IsOffTheRecord()
+                         ? browser_list->get(0u)
+                         : browser_list->get(1u);
+  EXPECT_EQ(1, regular->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      regular->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(regular));
+
+  // Leave only incognito browser open.
+  CloseBrowserSynchronously(regular);
+  EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
+
+  // Right-clicking on an extension action icon in the toolbar and selecting
+  // options should open the options page in a regular window. In this case, the
+  // profile is an OTR profile instead of a non-OTR profile, as described above.
+  ui_test_utils::NavigateToURL(incognito, GURL("chrome://newtab"));
+  EXPECT_EQ(1, incognito->tab_strip_model()->count());
+  // Because the OpenOptionsPage() call originates from an OTR window via, e.g.
+  // the action menu, instead of initiated by the extension, the
+  // OpenOptionsPage() version that takes a Browser* is used.
+  EXPECT_TRUE(
+      ExtensionTabUtil::OpenOptionsPage(options_spanning_extension, incognito));
+  // There should be two browser windows open, regular and incognito.
+  EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
+  browser_list = BrowserList::GetInstance();
+  regular = !browser_list->get(0u)->profile()->IsOffTheRecord()
+                ? browser_list->get(0u)
+                : browser_list->get(1u);
+  // Ensure that the regular browser is the foreground browser.
+  EXPECT_EQ(regular, browser_list->GetLastActive());
+  EXPECT_EQ(1, regular->tab_strip_model()->count());
+  EXPECT_TRUE(content::WaitForLoadStop(
+      regular->tab_strip_model()->GetActiveWebContents()));
+  EXPECT_EQ(options_url, GetActiveUrl(regular));
+}
+
 }  // namespace extensions
diff --git a/chrome/browser/first_run/first_run.cc b/chrome/browser/first_run/first_run.cc
index ca397ec..a13c299c 100644
--- a/chrome/browser/first_run/first_run.cc
+++ b/chrome/browser/first_run/first_run.cc
@@ -326,27 +326,6 @@
   std::transform(src.begin(), src.end(), ret->begin(), &UrlFromString);
 }
 
-bool IsOnWelcomePage(content::WebContents* contents) {
-  // We have to check both the GetURL() similar to the other checks below, but
-  // also the original request url because the welcome page we use is a
-  // redirect.
-  // TODO(crbug.com/651465): Remove this once kUseConsolidatedStartupFlow is on
-  // by default.
-  const GURL deprecated_welcome_page(
-      l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL));
-  if (contents->GetURL() == deprecated_welcome_page ||
-      (contents->GetController().GetVisibleEntry() &&
-       contents->GetController().GetVisibleEntry()->GetOriginalRequestURL() ==
-           deprecated_welcome_page)) {
-    return true;
-  }
-
-  const GURL welcome_page(chrome::kChromeUIWelcomeURL);
-  const GURL welcome_page_win10(chrome::kChromeUIWelcomeWin10URL);
-  const GURL current = contents->GetURL().GetWithEmptyPath();
-  return current == welcome_page || current == welcome_page_win10;
-}
-
 // Show the first run search engine bubble at the first appropriate opportunity.
 // This bubble may be delayed by other UI, like global errors and sync promos.
 class FirstRunBubbleLauncher : public content::NotificationObserver {
@@ -418,7 +397,7 @@
                    gaia::IsGaiaSignonRealm(contents->GetURL().GetOrigin()) ||
                    contents->GetURL() ==
                        chrome::GetSettingsUrl(chrome::kSyncSetupSubPage) ||
-                   IsOnWelcomePage(contents))) {
+                   first_run::IsOnWelcomePage(contents))) {
     return;
   }
 
@@ -715,6 +694,27 @@
   return retval;
 }
 
+bool IsOnWelcomePage(content::WebContents* contents) {
+  // We have to check both the GetURL() similar to the other checks below, but
+  // also the original request url because the welcome page we use is a
+  // redirect.
+  // TODO(crbug.com/651465): Remove this once kUseConsolidatedStartupFlow is on
+  // by default.
+  const GURL deprecated_welcome_page(
+      l10n_util::GetStringUTF8(IDS_WELCOME_PAGE_URL));
+  if (contents->GetURL() == deprecated_welcome_page ||
+      (contents->GetController().GetVisibleEntry() &&
+       contents->GetController().GetVisibleEntry()->GetOriginalRequestURL() ==
+           deprecated_welcome_page)) {
+    return true;
+  }
+
+  const GURL welcome_page(chrome::kChromeUIWelcomeURL);
+  const GURL welcome_page_win10(chrome::kChromeUIWelcomeWin10URL);
+  const GURL current = contents->GetURL().GetWithEmptyPath();
+  return current == welcome_page || current == welcome_page_win10;
+}
+
 void SetShouldDoPersonalDataManagerFirstRun() {
   g_should_do_autofill_personal_data_manager_first_run = true;
 }
diff --git a/chrome/browser/first_run/first_run.h b/chrome/browser/first_run/first_run.h
index d8abcf5..001b339 100644
--- a/chrome/browser/first_run/first_run.h
+++ b/chrome/browser/first_run/first_run.h
@@ -20,6 +20,10 @@
 class FilePath;
 }
 
+namespace content {
+class WebContents;
+}
+
 namespace user_prefs {
 class PrefRegistrySyncable;
 }
@@ -136,6 +140,9 @@
 // SetShouldShowWelcomePage() is called.
 bool ShouldShowWelcomePage();
 
+// Returns true if |contents| hosts one of the welcome pages.
+bool IsOnWelcomePage(content::WebContents* contents);
+
 // Iterates over the given tabs, replacing "magic words" designated for
 // use in Master Preferences files with corresponding URLs.
 std::vector<GURL> ProcessMasterPrefsTabs(const std::vector<GURL>& tabs);
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js
index d0082161..2fd4a7a 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui.js
@@ -38,6 +38,7 @@
       /** @const */ this.MENU_MODE_SCREEN_DISTANCE = 1.2;
       /** @const */ this.MENU_MODE_SCREEN_HEIGHT = 0.5;
       /** @const */ this.MENU_MODE_SCREEN_ELEVATION = 0.1;
+      /** @const */ this.BACKGROUND_DISTANCE_MULTIPLIER = 1.414;
 
       this.menuMode = false;
       this.fullscreen = false;
@@ -49,6 +50,18 @@
           this.SCREEN_HEIGHT * this.SCREEN_RATIO, this.SCREEN_HEIGHT);
       element.setTranslation(0, 0, -this.BROWSING_SCREEN_DISTANCE);
       this.elementId = ui.addElement(element);
+
+      // Place an invisible but hittable plane behind the content quad, to keep
+      // the reticle roughly planar with the content if near content.
+      let backPlane = new api.UiElement(0, 0, 0, 0);
+      backPlane.setVisible(false);
+      backPlane.setHitTestable(true);
+      backPlane.setSize(1000, 1000);
+      backPlane.setTranslation(0, 0, -0.01);
+      backPlane.setParentId(this.elementId);
+      ui.addElement(backPlane);
+
+      this.updateState();
     }
 
     setEnabled(enabled) {
@@ -81,25 +94,27 @@
     updateState() {
       // Defaults content quad parameters.
       let y = 0;
-      let z = -this.BROWSING_SCREEN_DISTANCE;
+      let distance = this.BROWSING_SCREEN_DISTANCE;
       let height = this.SCREEN_HEIGHT;
 
       // Mode-specific overrides.
       if (this.menuMode) {
         y = this.MENU_MODE_SCREEN_ELEVATION;
-        z = -this.MENU_MODE_SCREEN_DISTANCE;
+        distance = this.MENU_MODE_SCREEN_DISTANCE;
         height = this.MENU_MODE_SCREEN_HEIGHT;
       } else if (this.fullscreen) {
-        z = -this.FULLSCREEN_DISTANCE;
+        distance = this.FULLSCREEN_DISTANCE;
       }
 
       let anim;
       anim = new api.Animation(this.elementId, ANIM_DURATION);
-      anim.setTranslation(0, y, z);
+      anim.setTranslation(0, y, -distance);
       ui.addAnimation(anim);
       anim = new api.Animation(this.elementId, ANIM_DURATION);
       anim.setSize(height * this.SCREEN_RATIO, height);
       ui.addAnimation(anim);
+
+      ui.setBackgroundDistance(distance * this.BACKGROUND_DISTANCE_MULTIPLIER);
     }
 
     // TODO(crbug/643815): Add a method setting aspect ratio (and possible
@@ -252,8 +267,8 @@
 
       let update = new api.UiElementUpdate();
       update.setVisible(false);
-      update.setSize(0.5, 0.2);
-      update.setTranslation(0, -2, -1);
+      update.setSize(0.25, 0.1);
+      update.setTranslation(0, -1.5, -1.5);
       update.setRotation(1, 0, 0, -0.8);
       ui.updateElement(this.uiElement.uiElementId, update);
     }
@@ -566,6 +581,8 @@
       groundGrid.setRotation(1.0, 0.0, 0.0, -Math.PI / 2);
       groundGrid.setDrawPhase(0);
       this.groundGridId = ui.addElement(groundGrid);
+
+      ui.setBackgroundColor(this.HORIZON_COLOR);
     }
 
     setEnabled(enabled) {
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js b/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
index cefeffd..751372d 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui_api.js
@@ -322,6 +322,9 @@
   constructor(pixelX, pixelY, pixelWidth, pixelHeight) {
     super();
 
+    // Apply defaults to new elements.
+    this.setVisible(true);
+    this.setHitTestable(true);
     this.setFill(new api.Sprite(pixelX, pixelY, pixelWidth, pixelHeight));
   }
 };
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js b/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js
index d1651a5..4ed691a 100644
--- a/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js
+++ b/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js
@@ -129,12 +129,27 @@
     delete this.animations[id];
   }
 
+  /**
+   * Set the background color of the scene.
+   * @param {{r: number, b: number, g: number, a: number}} color
+   */
   setBackgroundColor(color) {
     this.commands.push(
         {'type': api.Command.UPDATE_BACKGROUND, 'data': {'color': color}});
   }
 
   /**
+   * Set the radius of background-bounding sphere.
+   * @param {number} distance
+   */
+  setBackgroundDistance(distance) {
+    this.commands.push({
+      'type': api.Command.UPDATE_BACKGROUND,
+      'data': {'distance': distance}
+    });
+  }
+
+  /**
    * Purge all elements in the scene.
    */
   purge() {
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
index 06bfc0f..cc84db9a 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.cc
@@ -38,9 +38,7 @@
 
 void AutomationManagerAura::Enable(BrowserContext* context) {
   enabled_ = true;
-  if (!current_tree_.get())
-    current_tree_.reset(new AXTreeSourceAura());
-  ResetSerializer();
+  Reset(false);
 
   SendEvent(context, current_tree_->GetRoot(), ui::AX_EVENT_LOAD_COMPLETE);
   views::AXAuraObjCache::GetInstance()->SetDelegate(this);
@@ -57,9 +55,7 @@
 
 void AutomationManagerAura::Disable() {
   enabled_ = false;
-
-  // Reset the serializer to save memory.
-  current_tree_serializer_->Reset();
+  Reset(true);
 }
 
 void AutomationManagerAura::HandleEvent(BrowserContext* context,
@@ -151,9 +147,12 @@
 AutomationManagerAura::~AutomationManagerAura() {
 }
 
-void AutomationManagerAura::ResetSerializer() {
-  current_tree_serializer_.reset(
-      new AuraAXTreeSerializer(current_tree_.get()));
+void AutomationManagerAura::Reset(bool reset_serializer) {
+  if (!current_tree_)
+    current_tree_.reset(new AXTreeSourceAura());
+  reset_serializer ? current_tree_serializer_.reset()
+                   : current_tree_serializer_.reset(
+                         new AuraAXTreeSerializer(current_tree_.get()));
 }
 
 void AutomationManagerAura::SendEvent(BrowserContext* context,
diff --git a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
index 9da79fc..811e5aa 100644
--- a/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
+++ b/chrome/browser/ui/aura/accessibility/automation_manager_aura.h
@@ -67,8 +67,9 @@
  private:
   friend struct base::DefaultSingletonTraits<AutomationManagerAura>;
 
-  // Reset all state in this manager.
-  void ResetSerializer();
+  // Reset state in this manager. If |reset_serializer| is true, reset the
+  // serializer to save memory.
+  void Reset(bool reset_serializer);
 
   void SendEvent(content::BrowserContext* context,
                  views::AXAuraObjWrapper* aura_obj,
diff --git a/chrome/browser/ui/startup/default_browser_prompt.cc b/chrome/browser/ui/startup/default_browser_prompt.cc
index fb88173..6402a3f 100644
--- a/chrome/browser/ui/startup/default_browser_prompt.cc
+++ b/chrome/browser/ui/startup/default_browser_prompt.cc
@@ -16,6 +16,7 @@
 #include "base/version.h"
 #include "build/build_config.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/first_run/first_run.h"
 #include "chrome/browser/infobars/infobar_service.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
@@ -52,6 +53,14 @@
   if (!web_contents)
     return;
 
+  // Never show the default browser prompt over the first run promos.
+  // TODO(pmonette): The whole logic that determines when to show the default
+  // browser prompt is due for a refactor. ShouldShowDefaultBrowserPrompt()
+  // should be aware of the first run promos and return false instead of
+  // counting on the early return here. See bug crbug.com/693292.
+  if (first_run::IsOnWelcomePage(web_contents))
+    return;
+
   DefaultBrowserInfoBarDelegate::Create(
       InfoBarService::FromWebContents(web_contents), browser->profile());
 }
diff --git a/chrome/browser/ui/webui/profiler_ui.cc b/chrome/browser/ui/webui/profiler_ui.cc
index a9e9fee..2bf297e4 100644
--- a/chrome/browser/ui/webui/profiler_ui.cc
+++ b/chrome/browser/ui/webui/profiler_ui.cc
@@ -15,6 +15,7 @@
 
 #include "base/bind.h"
 #include "base/debug/debugging_flags.h"
+#include "base/debug/thread_heap_usage_tracker.h"
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/strings/string_util.h"
@@ -107,8 +108,9 @@
   source->AddResourcePath("profiler.js", IDR_PROFILER_JS);
   source->SetDefaultResource(IDR_PROFILER_HTML);
   source->UseGzip(std::unordered_set<std::string>());
-  source->AddBoolean("enableMemoryTaskProfiler",
-                     BUILDFLAG(ENABLE_MEMORY_TASK_PROFILER));
+  source->AddBoolean(
+      "enableMemoryTaskProfiler",
+      base::debug::ThreadHeapUsageTracker::IsHeapTrackingEnabled());
 
   return source;
 }
diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc
index d490a6f7..122dd54 100644
--- a/chrome/common/chrome_paths.cc
+++ b/chrome/common/chrome_paths.cc
@@ -146,18 +146,11 @@
 #else
       // Debug builds write next to the binary (in the build tree)
 #if defined(OS_MACOSX)
-      if (!PathService::Get(base::DIR_EXE, result))
-        return false;
+      // Apps may not write into their own bundle.
       if (base::mac::AmIBundled()) {
-        // If we're called from chrome, dump it beside the app (outside the
-        // app bundle), if we're called from a unittest, we'll already
-        // outside the bundle so use the exe dir.
-        // exe_dir gave us .../Chromium.app/Contents/MacOS/Chromium.
-        *result = result->DirName();
-        *result = result->DirName();
-        *result = result->DirName();
+        return PathService::Get(chrome::DIR_USER_DATA, result);
       }
-      return true;
+      return PathService::Get(base::DIR_EXE, result);
 #else
       return PathService::Get(base::DIR_EXE, result);
 #endif  // defined(OS_MACOSX)
diff --git a/chrome/test/data/extensions/options_page_spanning_incognito/manifest.json b/chrome/test/data/extensions/options_page_spanning_incognito/manifest.json
new file mode 100644
index 0000000..b955557
--- /dev/null
+++ b/chrome/test/data/extensions/options_page_spanning_incognito/manifest.json
@@ -0,0 +1,8 @@
+{
+  "name": "Extension With Options Page",
+  "description": "A spanning mode extension with an options page",
+  "options_page": "options.html",
+  "incognito": "spanning",
+  "version": "0.1.1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/options_page_spanning_incognito/options.html b/chrome/test/data/extensions/options_page_spanning_incognito/options.html
new file mode 100644
index 0000000..67fc120
--- /dev/null
+++ b/chrome/test/data/extensions/options_page_spanning_incognito/options.html
@@ -0,0 +1,8 @@
+<html>
+  <style>
+    html {
+      border: 1px solid green;
+    }
+  </style>
+  An options page
+</html>
diff --git a/chrome/test/data/extensions/options_page_split_incognito/manifest.json b/chrome/test/data/extensions/options_page_split_incognito/manifest.json
new file mode 100644
index 0000000..15ac1f9
--- /dev/null
+++ b/chrome/test/data/extensions/options_page_split_incognito/manifest.json
@@ -0,0 +1,8 @@
+{
+  "name": "Extension With Options Page",
+  "description": "A split mode extension with an options page",
+  "options_page": "options.html",
+  "incognito": "split",
+  "version": "0.1.1",
+  "manifest_version": 2
+}
diff --git a/chrome/test/data/extensions/options_page_split_incognito/options.html b/chrome/test/data/extensions/options_page_split_incognito/options.html
new file mode 100644
index 0000000..67fc120
--- /dev/null
+++ b/chrome/test/data/extensions/options_page_split_incognito/options.html
@@ -0,0 +1,8 @@
+<html>
+  <style>
+    html {
+      border: 1px solid green;
+    }
+  </style>
+  An options page
+</html>
diff --git a/extensions/browser/api/runtime/runtime_api.cc b/extensions/browser/api/runtime/runtime_api.cc
index 7e08c04..f5644c4b 100644
--- a/extensions/browser/api/runtime/runtime_api.cc
+++ b/extensions/browser/api/runtime/runtime_api.cc
@@ -417,8 +417,9 @@
   return ScheduleDelayedRestart(now, seconds_from_now);
 }
 
-bool RuntimeAPI::OpenOptionsPage(const Extension* extension) {
-  return delegate_->OpenOptionsPage(extension);
+bool RuntimeAPI::OpenOptionsPage(const Extension* extension,
+                                 content::BrowserContext* browser_context) {
+  return delegate_->OpenOptionsPage(extension, browser_context);
 }
 
 void RuntimeAPI::MaybeCancelRunningDelayedRestartTimer() {
@@ -660,7 +661,7 @@
 
 ExtensionFunction::ResponseAction RuntimeOpenOptionsPageFunction::Run() {
   RuntimeAPI* api = RuntimeAPI::GetFactoryInstance()->Get(browser_context());
-  return RespondNow(api->OpenOptionsPage(extension())
+  return RespondNow(api->OpenOptionsPage(extension(), browser_context())
                         ? NoArguments()
                         : Error(kFailedToCreateOptionsPage));
 }
diff --git a/extensions/browser/api/runtime/runtime_api.h b/extensions/browser/api/runtime/runtime_api.h
index e3329a4..3797908f 100644
--- a/extensions/browser/api/runtime/runtime_api.h
+++ b/extensions/browser/api/runtime/runtime_api.h
@@ -86,7 +86,8 @@
       const std::string& extension_id,
       int seconds_from_now);
 
-  bool OpenOptionsPage(const Extension* extension);
+  bool OpenOptionsPage(const Extension* extension,
+                       content::BrowserContext* browser_context);
 
  private:
   friend class BrowserContextKeyedAPIFactory<RuntimeAPI>;
diff --git a/extensions/browser/api/runtime/runtime_api_delegate.cc b/extensions/browser/api/runtime/runtime_api_delegate.cc
index b012c74..ce23440 100644
--- a/extensions/browser/api/runtime/runtime_api_delegate.cc
+++ b/extensions/browser/api/runtime/runtime_api_delegate.cc
@@ -13,7 +13,9 @@
     : success(success), response(response), version(version) {
 }
 
-bool RuntimeAPIDelegate::OpenOptionsPage(const Extension* extension) {
+bool RuntimeAPIDelegate::OpenOptionsPage(
+    const Extension* extension,
+    content::BrowserContext* browser_context) {
   return false;
 }
 
diff --git a/extensions/browser/api/runtime/runtime_api_delegate.h b/extensions/browser/api/runtime/runtime_api_delegate.h
index bd73f7a..f0ac21c4 100644
--- a/extensions/browser/api/runtime/runtime_api_delegate.h
+++ b/extensions/browser/api/runtime/runtime_api_delegate.h
@@ -10,6 +10,10 @@
 
 class GURL;
 
+namespace content {
+class BrowserContext;
+}
+
 namespace extensions {
 
 namespace api {
@@ -74,7 +78,8 @@
   // Open |extension|'s options page, if it has one. Returns true if an
   // options page was opened, false otherwise. See the docs of the
   // chrome.runtime.openOptionsPage function for the gritty details.
-  virtual bool OpenOptionsPage(const Extension* extension);
+  virtual bool OpenOptionsPage(const Extension* extension,
+                               content::BrowserContext* browser_context);
 };
 
 }  // namespace extensions
diff --git a/ios/chrome/app/BUILD.gn b/ios/chrome/app/BUILD.gn
index 9b17b92..56695088 100644
--- a/ios/chrome/app/BUILD.gn
+++ b/ios/chrome/app/BUILD.gn
@@ -9,6 +9,7 @@
 import("//ios/public/provider/chrome/browser/build_config.gni")
 
 source_set("app") {
+  configs += [ "//build/config/compiler:enable_arc" ]
   sources = [
     "deferred_initialization_runner.h",
     "deferred_initialization_runner.mm",
diff --git a/ios/chrome/app/deferred_initialization_runner.mm b/ios/chrome/app/deferred_initialization_runner.mm
index 7e175b4..01fa251 100644
--- a/ios/chrome/app/deferred_initialization_runner.mm
+++ b/ios/chrome/app/deferred_initialization_runner.mm
@@ -6,19 +6,16 @@
 
 #include <stdint.h>
 
-#import "base/ios/weak_nsobject.h"
 #include "base/logging.h"
 #include "base/mac/scoped_block.h"
-#include "base/mac/scoped_nsobject.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 // An object encapsulating the deferred execution of a block of initialization
 // code.
-@interface DeferredInitializationBlock : NSObject {
-  // A string to reference the initialization block.
-  base::scoped_nsobject<NSString> _name;
-  // A block of code to execute.
-  base::mac::ScopedBlock<ProceduralBlock> _runBlock;
-}
+@interface DeferredInitializationBlock : NSObject
 
 - (instancetype)init NS_UNAVAILABLE;
 
@@ -34,7 +31,12 @@
 
 @end
 
-@implementation DeferredInitializationBlock
+@implementation DeferredInitializationBlock {
+  // A string to reference the initialization block.
+  NSString* _name;
+  // A block of code to execute.
+  ProceduralBlock _runBlock;
+}
 
 // Overrides default designated initializer.
 - (instancetype)init {
@@ -46,15 +48,15 @@
   DCHECK(block);
   self = [super init];
   if (self) {
-    _name.reset([name copy]);
-    _runBlock.reset(block, base::scoped_policy::RETAIN);
+    _name = [name copy];
+    _runBlock = block;
   }
   return self;
 }
 
 - (void)run {
   DCHECK([NSThread isMainThread]);
-  ProceduralBlock deferredBlock = _runBlock.get();
+  ProceduralBlock deferredBlock = _runBlock;
   if (!deferredBlock)
     return;
   deferredBlock();
@@ -62,14 +64,14 @@
 }
 
 - (void)cancel {
-  _runBlock.reset();
+  _runBlock = nil;
 }
 
 @end
 
 @interface DeferredInitializationRunner () {
-  base::scoped_nsobject<NSMutableArray> _blocksNameQueue;
-  base::scoped_nsobject<NSMutableDictionary> _runBlocks;
+  NSMutableArray* _blocksNameQueue;
+  NSMutableDictionary* _runBlocks;
   BOOL _isBlockScheduled;
 }
 
@@ -102,8 +104,8 @@
 - (instancetype)init {
   self = [super init];
   if (self) {
-    _blocksNameQueue.reset([[NSMutableArray array] retain]);
-    _runBlocks.reset([[NSMutableDictionary dictionary] retain]);
+    _blocksNameQueue = [NSMutableArray array];
+    _runBlocks = [NSMutableDictionary dictionary];
     _isBlockScheduled = NO;
     _delayBetweenBlocks = 0.2;
     _delayBeforeFirstBlock = 3.0;
@@ -117,8 +119,8 @@
   [self cancelBlockNamed:name];
   [_blocksNameQueue addObject:name];
 
-  base::scoped_nsobject<DeferredInitializationBlock> deferredBlock(
-      [[DeferredInitializationBlock alloc] initWithName:name block:block]);
+  DeferredInitializationBlock* deferredBlock =
+      [[DeferredInitializationBlock alloc] initWithName:name block:block];
   [_runBlocks setObject:deferredBlock forKey:name];
 
   if (!_isBlockScheduled) {
@@ -137,7 +139,7 @@
       [_runBlocks objectForKey:nextBlockName];
   DCHECK(nextBlock);
 
-  base::WeakNSObject<DeferredInitializationRunner> weakSelf(self);
+  __weak DeferredInitializationRunner* weakSelf = self;
 
   dispatch_after(
       dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
diff --git a/ios/chrome/app/safe_mode_crashing_modules_config.mm b/ios/chrome/app/safe_mode_crashing_modules_config.mm
index e9ec67c..2cde7ad 100644
--- a/ios/chrome/app/safe_mode_crashing_modules_config.mm
+++ b/ios/chrome/app/safe_mode_crashing_modules_config.mm
@@ -6,7 +6,10 @@
 
 #include "base/logging.h"
 #include "base/mac/foundation_util.h"
-#import "base/mac/scoped_nsobject.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
 
 namespace {
 
@@ -15,12 +18,9 @@
 
 }  // namespace
 
-@interface SafeModeCrashingModulesConfig () {
-  base::scoped_nsobject<NSDictionary> _configuration;
+@implementation SafeModeCrashingModulesConfig {
+  NSDictionary* _configuration;
 }
-@end
-
-@implementation SafeModeCrashingModulesConfig
 
 + (SafeModeCrashingModulesConfig*)sharedInstance {
   static SafeModeCrashingModulesConfig* instance =
@@ -34,8 +34,7 @@
     NSString* configPath =
         [[NSBundle mainBundle] pathForResource:@"SafeModeCrashingModules"
                                         ofType:@"plist"];
-    _configuration.reset(
-        [[NSDictionary alloc] initWithContentsOfFile:configPath]);
+    _configuration = [[NSDictionary alloc] initWithContentsOfFile:configPath];
   }
   return self;
 }
diff --git a/ios/chrome/browser/tabs/tab.mm b/ios/chrome/browser/tabs/tab.mm
index 0e52721..a6eda18 100644
--- a/ios/chrome/browser/tabs/tab.mm
+++ b/ios/chrome/browser/tabs/tab.mm
@@ -372,6 +372,14 @@
 
 // Called when the UIApplication's state becomes active.
 - (void)applicationDidBecomeActive;
+
+// Returns YES if popups requested by a page with |URL| should be blocked.
+- (BOOL)shouldBlockPopupForPageWithURL:(const GURL&)URL;
+
+// Blocks popup for page with |popupURL|, requested by the page with
+// |openerURL|.
+- (void)blockPopupForURL:(const GURL&)popupURL openerURL:(const GURL&)openerURL;
+
 @end
 
 namespace {
@@ -1470,6 +1478,40 @@
   }
 }
 
+- (BOOL)shouldBlockPopupForPageWithURL:(const GURL&)URL {
+  HostContentSettingsMap* settingMap =
+      ios::HostContentSettingsMapFactory::GetForBrowserState(browserState_);
+  ContentSetting setting = settingMap->GetContentSetting(
+      URL, URL, CONTENT_SETTINGS_TYPE_POPUPS, std::string());
+  return setting != CONTENT_SETTING_ALLOW;
+}
+
+- (void)blockPopupForURL:(const GURL&)popupURL
+               openerURL:(const GURL&)openerURL {
+  web::NavigationItem* item = [self navigationManager]->GetLastCommittedItem();
+  web::Referrer referrer(openerURL, item->GetReferrer().policy);
+  GURL localPopupURL(popupURL);
+  base::WeakNSObject<Tab> weakSelf(self);
+  // TODO(crbug.com/692117): Remove |window_name| from constructor.
+  web::BlockedPopupInfo poupInfo(popupURL, referrer, nil /* window_name */, ^{
+    base::scoped_nsobject<Tab> strongSelf([weakSelf retain]);
+    if (!strongSelf) {
+      return;
+    }
+    [strongSelf updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
+    [strongSelf.get()->parentTabModel_
+        insertOrUpdateTabWithURL:localPopupURL
+                        referrer:referrer
+                      transition:ui::PAGE_TRANSITION_LINK
+                      windowName:nil
+                          opener:self
+                     openedByDOM:YES
+                         atIndex:TabModelConstants::kTabPositionAutomatically
+                    inBackground:NO];
+  });
+  BlockedPopupTabHelper::FromWebState(self.webState)->HandlePopup(poupInfo);
+}
+
 #pragma mark -
 #pragma mark FindInPageControllerDelegate
 
@@ -1584,40 +1626,6 @@
 
 #pragma mark - CRWWebDelegate and CRWWebStateObserver protocol methods.
 
-- (CRWWebController*)webPageOrderedOpen:(const GURL&)URL
-                               referrer:(const web::Referrer&)referrer
-                             windowName:(NSString*)windowName
-                           inBackground:(BOOL)inBackground {
-  DCHECK(parentTabModel_);
-  if (!inBackground)
-    [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
-  // Open a new tab or update an existing one. Tabs opened from a web page are
-  Tab* tab = [parentTabModel_
-      insertOrUpdateTabWithURL:URL
-                      referrer:referrer
-                    transition:ui::PAGE_TRANSITION_LINK
-                    windowName:windowName
-                        opener:self
-                   openedByDOM:YES
-                       atIndex:TabModelConstants::kTabPositionAutomatically
-                  inBackground:inBackground];
-  return tab.webController;
-}
-
-// This can be combined with the other versions once Tab loading is separated
-// from creation.
-- (CRWWebController*)webPageOrderedOpen {
-  [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
-
-  Tab* tab = [parentTabModel_
-      insertBlankTabWithTransition:ui::PAGE_TRANSITION_LINK
-                            opener:self
-                       openedByDOM:YES
-                           atIndex:TabModelConstants::kTabPositionAutomatically
-                      inBackground:NO];
-  return tab.webController;
-}
-
 // The web page wants to close its own window.
 - (void)webPageOrderedClose {
   // Only allow a web page to close itself if it was opened by DOM, or if there
@@ -1990,21 +1998,26 @@
   [delegate_ discardPrerender];
 }
 
-- (BOOL)webController:(CRWWebController*)webController
-    shouldBlockPopupWithURL:(const GURL&)popupURL
-                  sourceURL:(const GURL&)sourceURL {
-  ContentSetting setting =
-      ios::HostContentSettingsMapFactory::GetForBrowserState(browserState_)
-          ->GetContentSetting(sourceURL, sourceURL,
-                              CONTENT_SETTINGS_TYPE_POPUPS, std::string());
+- (CRWWebController*)webController:(CRWWebController*)webController
+         createWebControllerForURL:(const GURL&)URL
+                         openerURL:(const GURL&)openerURL
+                   initiatedByUser:(BOOL)initiatedByUser {
+  BOOL shouldBlockPopUp =
+      !initiatedByUser && [self shouldBlockPopupForPageWithURL:openerURL];
 
-  return setting != CONTENT_SETTING_ALLOW;
-}
+  if (shouldBlockPopUp) {
+    [self blockPopupForURL:URL openerURL:openerURL];
+    return nil;
+  }
 
-- (void)webController:(CRWWebController*)webController
-        didBlockPopup:(const web::BlockedPopupInfo&)blockedPopupInfo {
-  BlockedPopupTabHelper::FromWebState(self.webState)
-      ->HandlePopup(blockedPopupInfo);
+  [self updateSnapshotWithOverlay:YES visibleFrameOnly:YES];
+  Tab* tab = [parentTabModel_
+      insertBlankTabWithTransition:ui::PAGE_TRANSITION_LINK
+                            opener:self
+                       openedByDOM:YES
+                           atIndex:TabModelConstants::kTabPositionAutomatically
+                      inBackground:NO];
+  return tab.webController;
 }
 
 - (CGFloat)headerHeightForWebController:(CRWWebController*)webController {
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index dedf5a9..102e29ed 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -4317,6 +4317,7 @@
   CRWWebController* webController = tab.webController;
   NSString* script = @"document.documentElement.outerHTML;";
   base::WeakNSObject<Tab> weakTab(tab);
+  base::WeakNSObject<BrowserViewController> weakSelf(self);
   web::JavaScriptResultBlock completionHandlerBlock = ^(id result, NSError*) {
     base::scoped_nsobject<Tab> strongTab([weakTab retain]);
     if (!strongTab)
@@ -4327,10 +4328,16 @@
     base::Base64Encode(base::SysNSStringToUTF8(result), &base64HTML);
     GURL URL(std::string("data:text/plain;charset=utf-8;base64,") + base64HTML);
     web::Referrer referrer([strongTab url], web::ReferrerPolicyDefault);
-    [strongTab webPageOrderedOpen:URL
-                         referrer:referrer
-                       windowName:nil
-                     inBackground:NO];
+
+    [[weakSelf tabModel]
+        insertOrUpdateTabWithURL:URL
+                        referrer:referrer
+                      transition:ui::PAGE_TRANSITION_LINK
+                      windowName:nil
+                          opener:strongTab
+                     openedByDOM:YES
+                         atIndex:TabModelConstants::kTabPositionAutomatically
+                    inBackground:NO];
   };
   [webController executeJavaScript:script
                  completionHandler:completionHandlerBlock];
diff --git a/ios/web/public/web_state/ui/crw_web_delegate.h b/ios/web/public/web_state/ui/crw_web_delegate.h
index d64f8044..4069c877 100644
--- a/ios/web/public/web_state/ui/crw_web_delegate.h
+++ b/ios/web/public/web_state/ui/crw_web_delegate.h
@@ -21,32 +21,12 @@
 @class CRWSessionEntry;
 @class CRWWebController;
 
-namespace web {
-class BlockedPopupInfo;
-struct Referrer;
-}
-
 // Methods implemented by the delegate of the CRWWebController.
 // DEPRECATED, do not conform to this protocol and do not add any methods to it.
 // Use web::WebStateDelegate instead.
 // TODO(crbug.com/674991): Remove this protocol.
 @protocol CRWWebDelegate<NSObject>
 
-// Called when the page wants to open a new window by DOM (e.g. with
-// |window.open| JavaScript call or by clicking a link with |_blank| target) or
-// wants to open a window with a new tab. |inBackground| allows a page to force
-// a new window to open in the background. CRWSessionController's openedByDOM
-// property of the returned CRWWebController must be YES.
-- (CRWWebController*)webPageOrderedOpen:(const GURL&)url
-                               referrer:(const web::Referrer&)referrer
-                             windowName:(NSString*)windowName
-                           inBackground:(BOOL)inBackground;
-
-// Called when the page wants to open a new window by DOM.
-// CRWSessionController's openedByDOM property of the returned CRWWebController
-// must be YES.
-- (CRWWebController*)webPageOrderedOpen;
-
 // Called when the page calls window.close() on itself. Begin the shut-down
 // sequence for this controller.
 - (void)webPageOrderedClose;
@@ -103,6 +83,16 @@
 - (void)webWillFinishHistoryNavigationFromEntry:(CRWSessionEntry*)fromEntry;
 // ---------------------------------------------------------------------
 
+// Called when |webController| wants to open a new window. |URL| is the URL of
+// the new window; |openerURL| is the URL of the page which requested a window
+// to be open; |initiatedByUser| is YES if action was caused by the user.
+// |webController| will not open a window if this method returns nil. This
+// method can not return |webController|.
+- (CRWWebController*)webController:(CRWWebController*)webController
+         createWebControllerForURL:(const GURL&)URL
+                         openerURL:(const GURL&)openerURL
+                   initiatedByUser:(BOOL)initiatedByUser;
+
 @optional
 
 // Called to ask CRWWebDelegate if |CRWWebController| should open the given URL.
@@ -140,20 +130,6 @@
 - (void)webController:(CRWWebController*)webController
        titleDidChange:(NSString*)title;
 
-// Called when CRWWebController has detected a popup. If NO is returned then
-// popup will be shown, otherwise |webController:didBlockPopup:| will be called
-// and CRWWebDelegate will have a chance to unblock the popup later. NO is
-// assumed by default if this method is not implemented.
-- (BOOL)webController:(CRWWebController*)webController
-    shouldBlockPopupWithURL:(const GURL&)popupURL
-                  sourceURL:(const GURL&)sourceURL;
-
-// Called when CRWWebController has detected and blocked a popup. In order to
-// allow the blocked pop up CRWWebDelegate must call
-// |blockedPopupInfo.ShowPopup()| instead of attempting to open a new window.
-- (void)webController:(CRWWebController*)webController
-        didBlockPopup:(const web::BlockedPopupInfo&)blockedPopupInfo;
-
 // Called when CRWWebController did suppress a dialog (JavaScript, HTTP
 // authentication or window.open).
 // NOTE: Called only if CRWWebController.shouldSuppressDialogs is set to YES.
diff --git a/ios/web/web_state/js/resources/core.js b/ios/web/web_state/js/resources/core.js
index d38360928..f1a302f2 100644
--- a/ios/web/web_state/js/resources/core.js
+++ b/ios/web/web_state/js/resources/core.js
@@ -363,12 +363,6 @@
     return 'default';
   };
 
-  // Provides a way for other injected javascript to access the page's referrer
-  // policy.
-  __gCrWeb['getPageReferrerPolicy'] = function() {
-    return getReferrerPolicy_();
-  };
-
   // Various aspects of global DOM behavior are overridden here.
 
   // A popstate event needs to be fired anytime the active history entry
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index d7b23f9..9be54a3 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -78,7 +78,6 @@
 #include "ios/web/public/web_state/url_verification_constants.h"
 #import "ios/web/public/web_state/web_state.h"
 #include "ios/web/public/webui/web_ui_ios.h"
-#import "ios/web/web_state/blocked_popup_info.h"
 #import "ios/web/web_state/crw_pass_kit_downloader.h"
 #import "ios/web/web_state/crw_web_view_proxy_impl.h"
 #import "ios/web/web_state/error_translation_util.h"
@@ -103,6 +102,7 @@
 #import "ios/web/webui/mojo_facade.h"
 #import "net/base/mac/url_conversions.h"
 #include "net/base/net_errors.h"
+#include "net/ssl/ssl_info.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
@@ -533,8 +533,8 @@
 // Returns the WKWebViewConfigurationProvider associated with the web
 // controller's BrowserState.
 - (web::WKWebViewConfigurationProvider&)webViewConfigurationProvider;
-// Extracts Referer value from WKNavigationAction request header.
-- (NSString*)refererFromNavigationAction:(WKNavigationAction*)action;
+// Extracts "Referer" [sic] value from WKNavigationAction request header.
+- (NSString*)referrerFromNavigationAction:(WKNavigationAction*)action;
 
 // Returns the current URL of the web view, and sets |trustLevel| accordingly
 // based on the confidence in the verification.
@@ -689,8 +689,6 @@
     (const web::PageScrollState&)scrollState;
 // Returns the referrer for the current page.
 - (web::Referrer)currentReferrer;
-// Asynchronously returns the referrer policy for the current page.
-- (void)queryPageReferrerPolicy:(void (^)(NSString*))responseHandler;
 // Adds a new CRWSessionEntry with the given URL and state object to the history
 // stack. A state object is a serialized generic JavaScript object that contains
 // details of the UI's state for a given CRWSessionEntry/URL.
@@ -717,9 +715,6 @@
 
 // Inject windowID if not yet injected.
 - (void)injectWindowID;
-// Creates a new opened by DOM window and returns its autoreleased web
-// controller.
-- (CRWWebController*)createChildWebController;
 
 // Returns YES if the given WKBackForwardListItem is valid to use for
 // navigation.
@@ -747,13 +742,6 @@
 // Called when a JavaScript dialog, HTTP authentication dialog or window.open
 // call has been suppressed.
 - (void)didSuppressDialog;
-// Convenience method to inform CWRWebDelegate about a blocked popup.
-- (void)didBlockPopupWithURL:(GURL)popupURL sourceURL:(GURL)sourceURL;
-// Informs CWRWebDelegate that CRWWebController has detected and blocked a
-// popup.
-- (void)didBlockPopupWithURL:(GURL)popupURL
-                   sourceURL:(GURL)sourceURL
-              referrerPolicy:(const std::string&)referrerPolicyString;
 // Returns YES if the navigation action is associated with a main frame request.
 - (BOOL)isMainFrameNavigationAction:(WKNavigationAction*)action;
 // Returns whether external URL navigation action should be opened.
@@ -768,10 +756,6 @@
 // Called when a load ends in an SSL error and certificate chain.
 - (void)handleSSLCertError:(NSError*)error;
 
-// Returns YES if the popup should be blocked, NO otherwise.
-- (BOOL)shouldBlockPopupWithURL:(const GURL&)popupURL
-                      sourceURL:(const GURL&)sourceURL;
-
 // Used in webView:didReceiveAuthenticationChallenge:completionHandler: to
 // reply with NSURLSessionAuthChallengeDisposition and credentials.
 - (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
@@ -1343,15 +1327,6 @@
                        web::ReferrerPolicyAlways);
 }
 
-- (void)queryPageReferrerPolicy:(void (^)(NSString*))responseHandler {
-  DCHECK(responseHandler);
-  [self executeJavaScript:@"__gCrWeb.getPageReferrerPolicy()"
-        completionHandler:^(id referrer, NSError* error) {
-          DCHECK_NE(error.code, WKErrorJavaScriptExceptionOccurred);
-          responseHandler(base::mac::ObjCCast<NSString>(referrer));
-        }];
-}
-
 - (void)pushStateWithPageURL:(const GURL&)pageURL
                  stateObject:(NSString*)stateObject
                   transition:(ui::PageTransition)transition {
@@ -1484,12 +1459,6 @@
   [_windowIDJSManager inject];
 }
 
-- (CRWWebController*)createChildWebController {
-  CRWWebController* result = [self.delegate webPageOrderedOpen];
-  DCHECK(!result || result.sessionController.openedByDOM);
-  return result;
-}
-
 - (BOOL)canUseViewForGeneratingOverlayPlaceholderView {
   return _containerView != nil;
 }
@@ -1718,7 +1687,7 @@
     _pendingNavigationInfo.reset(
         [[CRWWebControllerPendingNavigationInfo alloc] init]);
     [_pendingNavigationInfo
-        setReferrer:[self refererFromNavigationAction:action]];
+        setReferrer:[self referrerFromNavigationAction:action]];
     [_pendingNavigationInfo setNavigationType:action.navigationType];
     [_pendingNavigationInfo setHTTPMethod:action.request.HTTPMethod];
   }
@@ -3410,21 +3379,6 @@
 }
 
 #pragma mark -
-#pragma mark Popup handling
-
-- (BOOL)shouldBlockPopupWithURL:(const GURL&)popupURL
-                      sourceURL:(const GURL&)sourceURL {
-  if (![_delegate respondsToSelector:@selector(webController:
-                                         shouldBlockPopupWithURL:
-                                                       sourceURL:)]) {
-    return NO;
-  }
-  return [_delegate webController:self
-          shouldBlockPopupWithURL:popupURL
-                        sourceURL:sourceURL];
-}
-
-#pragma mark -
 #pragma mark Auth Challenge
 
 - (void)processAuthChallenge:(NSURLAuthenticationChallenge*)challenge
@@ -4012,42 +3966,6 @@
     [_delegate webControllerDidSuppressDialog:self];
 }
 
-- (void)didBlockPopupWithURL:(GURL)popupURL
-                   sourceURL:(GURL)sourceURL
-              referrerPolicy:(const std::string&)referrerPolicyString {
-  web::ReferrerPolicy referrerPolicy =
-      web::ReferrerPolicyFromString(referrerPolicyString);
-  web::Referrer referrer(sourceURL, referrerPolicy);
-  NSString* const kWindowName = @"";  // obsoleted
-  base::WeakNSObject<CRWWebController> weakSelf(self);
-  void (^showPopupHandler)() = ^{
-    // On Desktop cross-window comunication is not supported for unblocked
-    // popups; so it's ok to create a new independent page.
-    CRWWebController* child =
-        [[weakSelf delegate] webPageOrderedOpen:popupURL
-                                       referrer:referrer
-                                     windowName:kWindowName
-                                   inBackground:NO];
-    DCHECK(!child || child.sessionController.openedByDOM);
-  };
-
-  web::BlockedPopupInfo info(popupURL, referrer, kWindowName, showPopupHandler);
-  [self.delegate webController:self didBlockPopup:info];
-}
-
-- (void)didBlockPopupWithURL:(GURL)popupURL sourceURL:(GURL)sourceURL {
-  if ([_delegate respondsToSelector:@selector(webController:didBlockPopup:)]) {
-    base::WeakNSObject<CRWWebController> weakSelf(self);
-    dispatch_async(dispatch_get_main_queue(), ^{
-      [self queryPageReferrerPolicy:^(NSString* policy) {
-        [weakSelf didBlockPopupWithURL:popupURL
-                             sourceURL:sourceURL
-                        referrerPolicy:base::SysNSStringToUTF8(policy)];
-      }];
-    });
-  }
-}
-
 - (BOOL)isMainFrameNavigationAction:(WKNavigationAction*)action {
   if (action.targetFrame) {
     return action.targetFrame.mainFrame;
@@ -4442,28 +4360,23 @@
     return nil;
   }
 
+  // Do not create windows for non-empty invalid URLs.
   GURL requestURL = net::GURLWithNSURL(action.request.URL);
-
-  // Don't create windows for non-empty invalid URLs.
   if (!requestURL.is_empty() && !requestURL.is_valid()) {
     DLOG(WARNING) << "Unable to open a window with invalid URL: "
                   << requestURL.spec();
     return nil;
   }
 
-  if (![self userIsInteracting]) {
-    NSString* referer = [self refererFromNavigationAction:action];
-    GURL referrerURL =
-        referer ? GURL(base::SysNSStringToUTF8(referer)) : [self currentURL];
-    if ([self shouldBlockPopupWithURL:requestURL sourceURL:referrerURL]) {
-      [self didBlockPopupWithURL:requestURL sourceURL:referrerURL];
-      // Desktop Chrome does not return a window for the blocked popups;
-      // follow the same approach by returning nil;
-      return nil;
-    }
-  }
+  NSString* referrer = [self referrerFromNavigationAction:action];
+  GURL openerURL =
+      referrer ? GURL(base::SysNSStringToUTF8(referrer)) : _documentURL;
+  CRWWebController* child = [_delegate webController:self
+                           createWebControllerForURL:requestURL
+                                           openerURL:openerURL
+                                     initiatedByUser:[self userIsInteracting]];
+  DCHECK(!child || child.sessionController.openedByDOM);
 
-  CRWWebController* child = [self createChildWebController];
   // WKWebView requires WKUIDelegate to return a child view created with
   // exactly the same |configuration| object (exception is raised if config is
   // different). |configuration| param and config returned by
@@ -5320,8 +5233,8 @@
   _loadPhase = web::LOAD_REQUESTED;
 }
 
-- (NSString*)refererFromNavigationAction:(WKNavigationAction*)action {
-  return [action.request valueForHTTPHeaderField:@"Referer"];
+- (NSString*)referrerFromNavigationAction:(WKNavigationAction*)action {
+  return [action.request valueForHTTPHeaderField:kReferrerHeaderName];
 }
 
 @end
diff --git a/ios/web/web_state/ui/crw_web_controller_unittest.mm b/ios/web/web_state/ui/crw_web_controller_unittest.mm
index 16fad14..5f076056 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -31,7 +31,6 @@
 #include "ios/web/public/web_state/web_state_observer.h"
 #import "ios/web/test/web_test_with_web_controller.h"
 #import "ios/web/test/wk_web_view_crash_utils.h"
-#import "ios/web/web_state/blocked_popup_info.h"
 #import "ios/web/web_state/ui/crw_web_controller_container_view.h"
 #import "ios/web/web_state/web_state_impl.h"
 #import "ios/web/web_state/wk_web_view_security_util.h"
@@ -56,35 +55,27 @@
 
 // Used to mock CRWWebDelegate methods with C++ params.
 @interface MockInteractionLoader : OCMockComplexTypeHelper
-// popupURL passed to webController:shouldBlockPopupWithURL:sourceURL:
-// Used for testing.
-@property(nonatomic, assign) GURL popupURL;
-// sourceURL passed to webController:shouldBlockPopupWithURL:sourceURL:
-// Used for testing.
-@property(nonatomic, assign) GURL sourceURL;
 // Whether or not the delegate should block popups.
 @property(nonatomic, assign) BOOL blockPopups;
 // A web controller that will be returned by webPageOrdered... methods.
 @property(nonatomic, assign) CRWWebController* childWebController;
-// Blocked popup info received in |webController:didBlockPopup:| call.
-// nullptr if that delegate method was not called.
-@property(nonatomic, readonly) web::BlockedPopupInfo* blockedPopupInfo;
+
+// Values of arguments passed to
+// |webController:createWebControllerForURL:openerURL:initiatedByUser:|.
+@property(nonatomic, readonly) CRWWebController* webController;
+@property(nonatomic, readonly) GURL childURL;
+@property(nonatomic, readonly) GURL openerURL;
+@property(nonatomic, readonly) BOOL initiatedByUser;
 @end
 
-@implementation MockInteractionLoader {
-  // Backs up the property with the same name.
-  std::unique_ptr<web::BlockedPopupInfo> _blockedPopupInfo;
-}
-@synthesize popupURL = _popupURL;
-@synthesize sourceURL = _sourceURL;
+@implementation MockInteractionLoader
+
 @synthesize blockPopups = _blockPopups;
 @synthesize childWebController = _childWebController;
-
-typedef void (^webPageOrderedOpenBlankBlockType)();
-typedef void (^webPageOrderedOpenBlockType)(const GURL&,
-                                            const web::Referrer&,
-                                            NSString*,
-                                            BOOL);
+@synthesize webController = _webController;
+@synthesize childURL = _childURL;
+@synthesize openerURL = _openerURL;
+@synthesize initiatedByUser = _initiatedByUser;
 
 - (instancetype)initWithRepresentedObject:(id)representedObject {
   self = [super initWithRepresentedObject:representedObject];
@@ -94,18 +85,16 @@
   return self;
 }
 
-- (CRWWebController*)webPageOrderedOpen {
-  static_cast<webPageOrderedOpenBlankBlockType>([self blockForSelector:_cmd])();
-  return _childWebController;
-}
+- (CRWWebController*)webController:(CRWWebController*)webController
+         createWebControllerForURL:(const GURL&)childURL
+                         openerURL:(const GURL&)openerURL
+                   initiatedByUser:(BOOL)initiatedByUser {
+  _webController = webController;
+  _childURL = childURL;
+  _openerURL = openerURL;
+  _initiatedByUser = initiatedByUser;
 
-- (CRWWebController*)webPageOrderedOpen:(const GURL&)url
-                               referrer:(const web::Referrer&)referrer
-                             windowName:(NSString*)windowName
-                           inBackground:(BOOL)inBackground {
-  static_cast<webPageOrderedOpenBlockType>([self blockForSelector:_cmd])(
-      url, referrer, windowName, inBackground);
-  return _childWebController;
+  return (_blockPopups && !initiatedByUser) ? nil : _childWebController;
 }
 
 typedef BOOL (^openExternalURLBlockType)(const GURL&);
@@ -116,22 +105,6 @@
 }
 
 - (BOOL)webController:(CRWWebController*)webController
-    shouldBlockPopupWithURL:(const GURL&)popupURL
-                  sourceURL:(const GURL&)sourceURL {
-  self.popupURL = popupURL;
-  self.sourceURL = sourceURL;
-  return _blockPopups;
-}
-
-- (void)webController:(CRWWebController*)webController
-        didBlockPopup:(const web::BlockedPopupInfo&)blockedPopupInfo {
-  _blockedPopupInfo.reset(new web::BlockedPopupInfo(blockedPopupInfo));
-}
-
-- (web::BlockedPopupInfo*)blockedPopupInfo {
-  return _blockedPopupInfo.get();
-}
-- (BOOL)webController:(CRWWebController*)webController
         shouldOpenURL:(const GURL&)URL
       mainDocumentURL:(const GURL&)mainDocumentURL
           linkClicked:(BOOL)linkClicked {
@@ -911,7 +884,7 @@
     child_web_state_->GetNavigationManagerImpl().SetSessionController(
         sessionController);
 
-    LoadHtml(@"<html><body></body></html>");
+    LoadHtml(@"<html><body></body></html>", GURL("http://test"));
   }
   void TearDown() override {
     EXPECT_OCMOCK_VERIFY(delegate_);
@@ -941,76 +914,57 @@
 
   EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
 
-  EXPECT_FALSE([delegate_ blockedPopupInfo]);
+  EXPECT_FALSE([delegate_ webController]);
+  EXPECT_FALSE([delegate_ childURL].is_valid());
+  EXPECT_FALSE([delegate_ openerURL].is_valid());
+  EXPECT_FALSE([delegate_ initiatedByUser]);
 }
 
 // Tests that window.open triggered by user gesture opens a new non-popup
 // window.
 TEST_F(CRWWebControllerWindowOpenTest, OpenWithUserGesture) {
-  SEL selector = @selector(webPageOrderedOpen);
-  [delegate_ onSelector:selector
-      callBlockExpectation:^(){
-      }];
-
   [web_controller() touched:YES];
   EXPECT_NSEQ(@"[object Window]", OpenWindowByDOM());
-  EXPECT_FALSE([delegate_ blockedPopupInfo]);
+
+  EXPECT_EQ(web_controller(), [delegate_ webController]);
+  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
+  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
+  EXPECT_TRUE([delegate_ initiatedByUser]);
 }
 
 // Tests that window.open executed w/o user gesture does not open a new window.
 // Once the blocked popup is allowed a new window is opened.
 TEST_F(CRWWebControllerWindowOpenTest, AllowPopup) {
-  SEL selector =
-      @selector(webPageOrderedOpen:referrer:windowName:inBackground:);
-  [delegate_ onSelector:selector
-      callBlockExpectation:^(const GURL& new_window_url,
-                             const web::Referrer& referrer,
-                             NSString* obsoleted_window_name,
-                             BOOL in_background) {
-        EXPECT_EQ("javascript:void(0);", new_window_url.spec());
-        EXPECT_EQ("", referrer.url.spec());
-        EXPECT_FALSE(in_background);
-      }];
-
   ASSERT_FALSE([web_controller() userIsInteracting]);
   EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
-  base::test::ios::WaitUntilCondition(^bool() {
-    return [delegate_ blockedPopupInfo];
-  });
 
-  if ([delegate_ blockedPopupInfo])
-    [delegate_ blockedPopupInfo]->ShowPopup();
-
-  EXPECT_EQ("", [delegate_ sourceURL].spec());
-  EXPECT_EQ("javascript:void(0);", [delegate_ popupURL].spec());
+  EXPECT_EQ(web_controller(), [delegate_ webController]);
+  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
+  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
+  EXPECT_FALSE([delegate_ initiatedByUser]);
 }
 
 // Tests that window.open executed w/o user gesture opens a new window, assuming
 // that delegate allows popups.
 TEST_F(CRWWebControllerWindowOpenTest, DontBlockPopup) {
   [delegate_ setBlockPopups:NO];
-  SEL selector = @selector(webPageOrderedOpen);
-  [delegate_ onSelector:selector
-      callBlockExpectation:^(){
-      }];
-
   EXPECT_NSEQ(@"[object Window]", OpenWindowByDOM());
-  EXPECT_FALSE([delegate_ blockedPopupInfo]);
 
-  EXPECT_EQ("", [delegate_ sourceURL].spec());
-  EXPECT_EQ("javascript:void(0);", [delegate_ popupURL].spec());
+  EXPECT_EQ(web_controller(), [delegate_ webController]);
+  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
+  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
+  EXPECT_FALSE([delegate_ initiatedByUser]);
 }
 
 // Tests that window.open executed w/o user gesture does not open a new window.
 TEST_F(CRWWebControllerWindowOpenTest, BlockPopup) {
   ASSERT_FALSE([web_controller() userIsInteracting]);
   EXPECT_NSEQ([NSNull null], OpenWindowByDOM());
-  base::test::ios::WaitUntilCondition(^bool() {
-    return [delegate_ blockedPopupInfo];
-  });
 
-  EXPECT_EQ("", [delegate_ sourceURL].spec());
-  EXPECT_EQ("javascript:void(0);", [delegate_ popupURL].spec());
+  EXPECT_EQ(web_controller(), [delegate_ webController]);
+  EXPECT_EQ("javascript:void(0);", [delegate_ childURL].spec());
+  EXPECT_EQ("http://test/", [delegate_ openerURL].spec());
+  EXPECT_FALSE([delegate_ initiatedByUser]);
 };
 
 // Fixture class to test WKWebView crashes.
diff --git a/net/docs/life-of-a-feature.md b/net/docs/life-of-a-feature.md
new file mode 100644
index 0000000..163947b
--- /dev/null
+++ b/net/docs/life-of-a-feature.md
@@ -0,0 +1,330 @@
+# Life of a Feature
+
+In the years since the Chromium browser was first open-sourced, the `//net`
+directory has expanded from being the basis of loading web content in the
+Chromium browser to accommodating a wide variety of networking needs,
+both in the Chromium browser and in other Google and third-party products
+and projects.
+
+This brings with it many new opportunities, such as the ability to
+introduce new protocols rapidly or push Web security forward, as well as
+new challenges, such as how to balance the needs of various `//net`
+consumers effectively.
+
+To make it easier to contribute new features or to change existing
+behaviors in `//net`, this document tries to capture the life of a
+feature in `//net`, from initial design to the eventual possibility of
+deprecation and removal.
+
+## Supported Projects
+
+When considering the introduction of a new `//net` feature or changing
+a `//net` behavior, it's first necessary to understand where `//net`
+is used, how it is used, and what the various constraints and limits are.
+
+To understand a more comprehensive matrix of the supported platforms and
+constraints, see [Supported Projects](supported-projects.md). When
+examining a new feature request, or a change in behavior, it's necessary
+to consider dimensions such as:
+
+  * Does this feature apply to all supported projects, or is this something
+    like a Browser-only feature?
+  * Does this feature apply to all supported platforms, or is this something
+    specific to a particular subset?
+  * Is the feature a basic networking library feature, or is it specific to
+    something in the Web Platform?
+  * Will some projects wish to strip the feature in order to meet targets
+    such as memory usage (RAM) or binary size?
+  * Does it depend on Google services or Google-specific behaviors or
+    features?
+  * How will this feature be tested / experimented with? For example,
+    __Field Trials (Finch)__ and __User Metrics (UMA)__ may not be available
+    on a number of platforms.
+  * How risky is the feature towards compatibility/stability? How will it
+    be undone if there is a bug?
+  * Are the power/memory/CPU/bandwidth requirements appropriate for the
+    targeted projects and/or platforms? 
+
+## Design and Layering
+
+Once the supported platforms and constraints are identified, it's necessary
+to determine how to actually design the feature to meet those constraints,
+in hopefully the easiest way possible both for implementation and consumption.
+
+### Designing for multiple platforms
+
+In general, `//net` features try to support all platforms with a common
+interface, and generally eschew OS-specific interfaces from being exposed as
+part of `//net`.
+
+Cross-platform code is generally done via declaring an interface named
+`foo.h`, which is common for all platforms, and then using the build-system to
+do compile-time switching between implementations in `foo_win.cc`, `foo_mac.cc`,
+`foo_android.cc`, etc.
+
+The goal is to ensure that consumers generally don't have to think about
+OS-specific considerations, and can instead code to the interface.
+
+### Designing for multiple products
+
+While premature abstraction can significantly harm readability, if it is
+anticipated that different products will have different implementation needs,
+or may wish to selectively disable the feature, it's often necessary to
+abstract that interface sufficiently in `//net` to allow for dependency
+injection.
+
+This is true whether discussing concrete classes and interfaces or something
+as simple a boolean configuration flag that different consumers wish to set
+differently.
+
+The two most common approaches in `//net` are injection and delegation.
+
+#### Injection
+
+Injection refers to the pattern of defining the interface or concrete
+configuration parameter (such as a boolean), along with the concrete
+implementation, but requiring the `//net` embedder to supply an instance
+of the interface or the configuration parameters (perhaps optionally).
+
+Examples of this pattern include things such as the `ProxyConfigService`,
+which has concrete implementations in `//net` for a variety of platforms'
+configuration of proxies, but which requires it be supplied as part of the
+`URLRequestContextGetter` building, if proxies are going to be supported.
+
+An example of injecting configuration flags can be seen in the
+`HttpNetworkSession::Params` structure, which is used to provide much of
+the initialization parameters for the HTTP layer.
+
+The ideal form of injection is to pass ownership of the injected object,
+such as via a `std::unique_ptr<Foo>`. While this is not consistently
+applied in `//net`, as there are a number of places in which ownership is
+either shared or left to the embedder, with the injected object passed
+around as a naked/raw pointer, this is generally seen as an anti-pattern
+and not to be mirrored for new features.
+
+#### Delegation
+
+Delegation refers to forcing the `//net` embedder to respond to specific
+delegated calls via a Delegate interface that it implements. In general,
+when using the delegate pattern, ownership of the delegate should be
+transferred, so that the lifetime and threading semantics are clear and
+unambiguous.
+
+That is, for a given class `Foo`, which has a `Foo::Delegate` interface
+defined to allow embedders to alter behavior, prefer a constructor that
+is
+```
+explicit Foo(std::unique_ptr<Delegate> delegate);
+```
+so that it is clear that the lifetime of `delegate` is determined by
+`Foo`.
+
+While this may appear similar to Injection, the general difference
+between the two approaches is determining where the bulk of the
+implementation lies. With Injection, the interface describes a
+behavior contract that concrete implementations must adhere to; this
+allows for much more flexibility with behavior, but with the downside
+of significantly more work to implement or extend. Delegation attempts
+to keep the bulk of the implementation in `//net`, and the decision as
+to which implementation to use in `//net`, but allows `//net` to
+provide specific ways in which embedders can alter behaviors.
+
+The most notable example of the delegate pattern is `URLRequest::Delegate`,
+which keeps the vast majority of the loading logic within `URLRequest`,
+but allows the `URLRequest::Delegate` to participate during specific times
+in the request lifetime and alter specific behaviors as necessary. (Note:
+`URLRequest::Delegate`, like much of the original `//net` code, doesn't
+adhere to the recommended lifetime patterns of passing ownership of the
+Delegate. It is from the experience debugging and supporting these APIs
+that the `//net` team strongly encourages all new code pass explicit
+ownership, to reduce the complexity and risk of lifetime issues).
+
+While the use of a `base::Callback` can also be considered a form of
+delegation, the `//net` layer tries to eschew any callbacks that can be
+called more than once, and instead favors defining class interfaces
+with concrete behavioral requirements in order to ensure the correct
+lifetimes of objects and to adjust over time. When `//net` takes a
+callback (e.g. `net::CompletionCallback`), the intended pattern is to
+signal the asynchronous completion of a single method, invoking that
+callback at most once before deallocating it. For more discussion
+of these patterns, see [Code Patterns](code-patterns.md).
+
+### Understanding the Layering
+
+A significant challenge many feature proposals face is understanding the
+layering in `//net` and what different portions of `//net` are allowed to
+know. 
+
+#### Socket Pools
+
+The most common challenge feature proposals encounter is the awareness
+that the act of associating an actual request to make with a socket is
+done lazily, referred to as "late-binding".
+
+With late-bound sockets, a given `URLRequest` will not be assigned an actual
+transport connection until the request is ready to be sent. This allows for
+reprioritizing requests as they come in, to ensure that higher priority requests
+get preferential treatment, but it also means that features or data associated
+with a `URLRequest` generally don't participate in socket establishment or
+maintenance.
+
+For example, a feature that wants to associate the low-level network socket
+with a `URLRequest` during connection establishment is not something that the
+`//net` design supports, since the `URLRequest` is kept unaware of how sockets
+are established by virtue of the socket pools and late binding. This allows for
+more flexibility when working to improve performance, such as the ability to
+coalesce multiple logical 'sockets' over a single HTTP/2 or QUIC stream, which
+may only have a single physical network socket involved.
+
+#### Making Additional Requests
+
+From time to time, `//net` feature proposals will involve needing to load
+a secondary resource as part of processing. For example, SDCH involves loading
+additional dictionaries that are advertised in a header, and other feature
+proposals have involved fetching a `/.well-known/` URI or reporting errors to
+a particular URL.
+
+This is particularly challenging, because often, these features are implemented
+deeper in the network stack, such as [`//net/cert`](../cert), [`//net/http`](../http),
+or [`//net/filter`](../filter), which [`//net/url_request`](../url_request) depends
+on. Because `//net/url_request` depends on these low-level directories, it would
+be a circular dependency to have these directories depend on `//net/url_request`,
+and circular dependencies are forbidden.
+
+The recommended solution to address this is to adopt the delegation or injection
+patterns. The lower-level directory will define some interface that represents the
+"I need this URL" request, and then elsewhere, in a directory allowed to depend
+on `//net/url_request`, an implementation of that interface/delegate that uses
+`//net/url_request` is implemented.
+
+### Understanding the Lifetimes
+
+Understanding the object lifetime and dependency graph can be one of the largest
+challenges to contributing and maintaining `//net`. As a consequence, features
+which require introducing more complexity to the lifetimes of objects generally
+have a greater challenge to acceptance.
+
+The `//net` stack is designed heavily around a sync-or-async pattern, as
+documented in [Code Patterns](code-patterns.md), while also having a strong
+requirement that it be possible to cleanly shutdown the network stack. As a
+consequence, features should have precise, well-defined lifetime semantics
+and support graceful cleanup. Further, because much of the network stack can
+have web-observable side-effects, it is often required for tasks to have
+defined sequencing that cannot be reordered. To be ensure these requirements
+are met, features should attempt to model object lifetimes as a hierarchical
+DAG, using explicit ownership and avoiding the use of reference counting or
+weak pointers as part of any of the exposed API contracts (even for features
+only consumed in `//net`). Features that pay close attention to the lifetime
+semantics are more likely to be reviewed and accepted than those that leave
+it ambiguous.
+
+In addition to preferring explicit lifetimes, such as through judicious use of
+`std::unique_ptr<>` to indicate ownership transfer of dependencies, many
+features in `//net` also expect that if a `base::Callback` is involved (which
+includes `net::CompletionCallback`), then it's possible that invoking the
+callback may result in the deletion of the current (calling) object. As
+further expanded upon in [Code Patterns](code-patterns.md), features and
+changes should be designed such that any callback invocation is the last
+bit of code executed, and that the callback is accessed via the stack (such
+as through the use of either `base::ResetAndReturn(callback_).Run()` or
+`std::move(callback_).Run()`.
+
+### Specs: What Are They Good For
+
+As `//net` is used as the basis for a number of browsers, it's an important part
+of the design philosophy to ensure behaviors are well-specified, and that the
+implementation conforms to those specifications. This may be seen as burdensome
+when it's unclear whether or not a feature will 'take off,' but it's equally
+critical to ensure that the Chromium projects do not fork the Web Platform.
+
+#### Incubation Is Required
+
+`//net` respects Chromium's overall position of [incubation first](https://groups.google.com/a/chromium.org/d/msg/blink-dev/PJ_E04kcFb8/baiLN3DTBgAJ) standards development.
+
+With an incubation first approach, before introducing any new features that
+might be exposed over the wire to servers, whether they are explicit behaviors,
+such as adding new headers, or implicit behaviors such as
+[Happy Eyeballs](https://tools.ietf.org/html/rfc6555), should have some form
+of specification written. That specification should at least be on an
+incubation track, and the expectation is that the specification should have a
+direct path to an appropriate standards track. Features which don't adhere to
+this pattern, or which are not making progress towards a standards track, will
+require high-level approvals, to ensure that the Platform doesn't fragment.
+
+#### Introducing New Headers
+
+A common form of feature request is the introduction of new headers, either via
+the `//net` implementation directly, or through consuming `//net` interfaces
+and modifying headers on the fly.
+
+The introduction of any additional headers SHOULD have an incubated spec attached,
+ideally with cross-vendor interest. Particularly, headers which only apply to
+Google or Google services are very likely to be rejected outright.
+
+#### Making Additional Requests
+
+While it's necessary to provide abstraction around `//net/url_request` for
+any lower-level components that may need to make additional requests, for most
+features, that's not all that is necessary. Because `//net/url_request` only
+provides a basic HTTP fetching mechanism, it's insufficient for any Web Platform
+feature, because it doesn't consider the broader platform concerns such as
+interactions with CORS, Service Workers, cookie and authentication policies, or
+even basic interactions with optional features like Extensions or SafeBrowsing.
+
+To account for all of these things, any resource fetching that is to support
+a feature of the Web Platform, whether because the resource will be directly
+exposed to web content (for example, an image load or prefetch) or because it
+is in response to invoking a Web Platform API (for example, invoking the
+credential management API), the feature's resource fetching should be
+explainable in terms of the  [Fetch Living Standard](https://fetch.spec.whatwg.org/).
+The Fetch standard defines a JavaScript API for fetching resources, but more
+importantly, defines a common set of infrastructure and terminology that
+tries to define how all resource loads in the Web Platform happen - whether
+it be through the JavaScript API, through `XMLHttpRequest`, or the `src`
+attribute in HTML tags, for example.
+
+This also includes any resource fetching that wishes to use the same socket
+pools or caches as the Web Platform, to ensure that every resource that is
+web exposed (directly or indirectly) is fetched in a consistent and
+well-documented way, thus minimizing platform fragmentation and security
+issues.
+
+There are exceptions to this, however, but they're generally few and far
+between. In general, any feature that needs to define an abstraction to
+allow it to "fetch resources," likely needs to also be "explained in terms
+of Fetch".
+
+## Implementation
+
+In general, prior to implementing, try to get a review on net-dev@chromium.org
+for the general feedback and design review.
+
+In addition to the net-dev@chromium.org early review, `//net` requires that any
+browser-exposed behavior should also adhere to the
+[Blink Process](https://www.chromium.org/blink#new-features), which includes an
+"Intent to Implement" message to blink-dev@chromium.org
+
+For features that are unclear about their future, such as experiments or trials,
+it's also expected that the design planning will also account for how features
+will be removed cleanly. For features that radically affect the architecture of
+`//net`, expect a high bar of justification, since reversing those changes if
+they fail to pan out can cause significant disruption to productivity and
+stability.
+
+## Deprecation
+
+Plan for obsolence, hope for success. Similar to implementation, features that
+are to be removed should also go through the
+[Blink Process](https://www.chromium.org/blink#TOC-Web-Platform-Changes:-Process)
+for removing features.
+
+Note that due to the diversity of [Supported Projects](supported-projects.md),
+removing a feature while minimizing disruption can be just as complex as adding
+a feature. For example, relying solely on __User Metrics (UMA)__ to signal the
+safety of removing a feature may not consider all projects, and relying on
+__Field Trials (Finch)__ to assess risk or restore the 'legacy' behavior may not
+work on all projects either.
+
+It's precisely because of these challenges that there's such a high bar for
+adding features, because they may represent multi-year commitments to support,
+even when the feature itself is deprecated or targeted for removal.
diff --git a/net/docs/supported-projects.md b/net/docs/supported-projects.md
new file mode 100644
index 0000000..1403dc3
--- /dev/null
+++ b/net/docs/supported-projects.md
@@ -0,0 +1,124 @@
+# Supported Projects
+
+The `//net` stack is used on a variety of platforms and in a variety of open
+source projects. These different platforms and projects have differing
+degrees of support from `//net` OWNERS as well as differing requirements for
+designs and technical requirements.
+
+Note that this is a rough high-level overview of the major projects; as more
+of `//net` is broken into consumable services/components as part of the Mojo
+servicificaiton efforts, it's likely that there will be a larger number of
+'variants' of these projects with different constraints and features.
+
+## Google Chrome Browser
+
+The Google Chrome browser, which lives in `//chrome`, is the most important
+`//net` consumer and shapes many of the core design decisions. In general,
+features that are not intended with or not compatible with the needs of
+the Google Chrome browser will have a very high bar for acceptance in `//net`.
+
+The feature matrix 
+
+  * **Supported Platforms**: Windows, macOS, Linux, Chromium OS, iOS, Android
+  * **Release Frequency**: ~6 weeks between releases
+  * **Automatic Updates**: Yes
+  * **Command-line Flags**:
+    * __Yes__: Windows, macOS, Linux, Chromium OS (Dev image), Android (rooted)
+    * __No__: Chromium OS (Release image), iOS, Android (Release)
+  * **Field Trials (Finch)**: Yes
+  * **Enterprise Policy**: Yes
+  * **User Metrics (UMA)**: Yes
+  * **Component Updater**: Yes
+
+## Chromium Browser
+
+The Chromium browser refers to the practice of certain Linux distributions to
+bundle the open-source components of Chrome Browser in `//chrome`, branded
+as Chromium. This version is not distributed by Google, but by individual
+Linux distributions (primarily). Other distributions based on building Chromium
+for other platforms exist, although do not see as wide of usage.
+
+  * **Supported Platforms**: Windows, macOS, Linux, Chromium OS, iOS, Android
+  * **Release Frequency**: Varies by distributor; some Linux distributions
+    treat versions of Chromium as "Long Term Stable" and support a single
+    version for a longer time than the Chromium projects do, others track
+    the Google Chrome release frequency.
+  * **Automatic Updates**: Varies by distributor
+  * **Command-line Flags**:
+    * __Yes__: Windows, macOS, Linux, Chromium OS (dev image), Android (rooted)
+    * __No__: Chromium OS (Release image), iOS, Android (Release)
+  * **Field Trials (Finch)**: No
+  * **Enterprise Policy**: Yes
+  * **User Metrics (UMA)**: Varies by distributor
+  * **Component Updater**: Varies by distributor
+
+## Android WebView
+
+Distinct from the Chromium browser, the Android WebView is itself based on
+the Chromium browser. On official Android devices running Android N or later,
+WebView is automatically updated when Google Chrome is updated on the
+device. For earlier devices, Android WebView is updated by the System WebView
+component.
+
+Android WebView may also be used on non-official Android devices, such as
+those based on the Android Open Source Project but do not go through the
+Android [Compatability Test Suite](https://source.android.com/compatibility/cts/).
+Such releases have limited to no interaction with the Chromium projects, and
+so their capabilities cannot be conclusively documented.
+
+For official Android devices, WebView has the following capabilities.
+
+  * **Supported Platforms**: Android
+  * **Release Frequency**: ~6 weeks between releases
+  * **Automatic Updates**: Varies. Updates are made available on the Android
+    App Store, but users must explicitly choose to update. As such, the
+    rate of update varies much more than for the Chromium browser.
+  * **Command-line Flags**: No
+  * **Field Trials (Finch)**: No
+  * **Enterprise Policy**: No
+  * **User Metrics (UMA)**: No
+  * **Component Updater**: No
+
+## `//content` Embedders
+
+In addition to Chromium, there are a number of other of embedders of
+`//content`, such as projects like [Chromium Embedded Framework](https://bitbucket.org/chromiumembedded/cef)
+or [Electron](http://electron.atom.io/). While `//net` does not directly
+support these consumers, it does support the `//content` embedding API that
+these projects use. Note that this excludes the
+[content_shell](../../content/shell) test framework.
+
+  * **Supported Platforms**: Windows, macOS, Linux, Chromium OS, iOS, Android
+  * **Release Frequency**: Varies by consumer; Officially ~6 weeks
+  * **Command-line Flags**: Varies by consumer
+  * **Field Trials (Finch)**: No
+  * **Enterprise Policy**: No
+  * **User Metrics (UMA)**: No
+  * **Component Updater**: No
+
+## Cronet
+
+[Cronet](../../components/cronet/README.md) is a version of the `//net`
+network stack for use in mobile applications on iOS and Android. While
+primarily targetting first-party Google applications, Cronet's status as an
+open-source project, similar to the Chromium browser, means that it may
+find itself embedded in a variety of other projects.
+
+Unlike some of the other `//net` consumers, Cronet does not necessarily
+implement "The Web Platform" (as the holistic set of user agent-focused
+features), and instead is more akin to an HTTP(s) client networking library.
+
+  * **Supported Platforms**: iOS, Android
+  * **Release Frequency**: Varies. While "releases" are made following the
+    same frequency as Google Chrome, because it is treated similar to
+    a "third-party" library, different products and projects will ship
+    different versions of Cronet for differing periods of time.
+  * **Command-line Flags**: No
+  * **Field Trials (Finch)**: No
+  * **Enterprise Policy**: No
+  * **User Metrics (UMA)**:
+    * __Yes__: First-party (Google) projects, although they will be
+      reported in project-specific buckets (e.g. no overarching set of
+      metrics for all Cronet consumers).
+    * __No__: In general
+  * **Component Updater**: No
diff --git a/remoting/base/oauth_token_getter.cc b/remoting/base/oauth_token_getter.cc
index ba9bb58..ed571324 100644
--- a/remoting/base/oauth_token_getter.cc
+++ b/remoting/base/oauth_token_getter.cc
@@ -6,7 +6,9 @@
 
 namespace remoting {
 
-OAuthTokenGetter::OAuthCredentials::OAuthCredentials(
+// OAuthAuthorizationCredentials implementation.
+
+OAuthTokenGetter::OAuthAuthorizationCredentials::OAuthAuthorizationCredentials(
     const std::string& login,
     const std::string& refresh_token,
     bool is_service_account)
@@ -14,4 +16,18 @@
       refresh_token(refresh_token),
       is_service_account(is_service_account) {}
 
+OAuthTokenGetter::OAuthAuthorizationCredentials::
+    ~OAuthAuthorizationCredentials() {}
+
+// OAuthIntermediateCredentials implementation.
+
+OAuthTokenGetter::OAuthIntermediateCredentials::OAuthIntermediateCredentials(
+    const std::string& authorization_code,
+    bool is_service_account)
+    : authorization_code(authorization_code),
+      is_service_account(is_service_account) {}
+
+OAuthTokenGetter::OAuthIntermediateCredentials::
+    ~OAuthIntermediateCredentials() {}
+
 }  // namespace remoting
diff --git a/remoting/base/oauth_token_getter.h b/remoting/base/oauth_token_getter.h
index 8df65ce..80f9cbf 100644
--- a/remoting/base/oauth_token_getter.h
+++ b/remoting/base/oauth_token_getter.h
@@ -30,15 +30,22 @@
                               const std::string& access_token)>
       TokenCallback;
 
-  // This structure contains information required to perform
-  // authentication to OAuth2.
-  struct OAuthCredentials {
+  typedef base::Callback<void(const std::string& user_email,
+                              const std::string& refresh_token)>
+      CredentialsUpdatedCallback;
+
+  // This structure contains information required to perform authorization
+  // with the authorization server.
+  struct OAuthAuthorizationCredentials {
+    // |login| is used to valdiate |refresh_token| match.
     // |is_service_account| should be True if the OAuth refresh token is for a
     // service account, False for a user account, to allow the correct client-ID
     // to be used.
-    OAuthCredentials(const std::string& login,
-                     const std::string& refresh_token,
-                     bool is_service_account);
+    OAuthAuthorizationCredentials(const std::string& login,
+                                  const std::string& refresh_token,
+                                  bool is_service_account);
+
+    ~OAuthAuthorizationCredentials();
 
     // The user's account name (i.e. their email address).
     std::string login;
@@ -50,6 +57,30 @@
     bool is_service_account;
   };
 
+  // This structure contains information required to perform authentication
+  // with the authorization server.
+  struct OAuthIntermediateCredentials {
+    // |authorization_code| is a one time use code used to authenticate with
+    // the authorization server.
+    // |is_service_account| should be True if the OAuth refresh token is for a
+    // service account, False for a user account, to allow the correct client-ID
+    // to be used.
+    OAuthIntermediateCredentials(const std::string& authorization_code,
+                                 bool is_service_account);
+
+    ~OAuthIntermediateCredentials();
+
+    // Code used to check out a access token from the authrozation service.
+    std::string authorization_code;
+
+    // Override uri for oauth redirect. This is used for client accounts only
+    // and is optionally set to override the default used for authentication.
+    std::string oauth_redirect_uri;
+
+    // Whether these credentials belong to a service account.
+    bool is_service_account;
+  };
+
   OAuthTokenGetter() {}
   virtual ~OAuthTokenGetter() {}
 
diff --git a/remoting/base/oauth_token_getter_impl.cc b/remoting/base/oauth_token_getter_impl.cc
index 9a8e384..4f49a455 100644
--- a/remoting/base/oauth_token_getter_impl.cc
+++ b/remoting/base/oauth_token_getter_impl.cc
@@ -12,6 +12,7 @@
 #include "google_apis/google_api_keys.h"
 #include "net/url_request/url_request_context_getter.h"
 #include "remoting/base/logging.h"
+#include "remoting/base/oauth_helper.h"
 
 namespace remoting {
 
@@ -26,11 +27,27 @@
 }  // namespace
 
 OAuthTokenGetterImpl::OAuthTokenGetterImpl(
-    std::unique_ptr<OAuthCredentials> oauth_credentials,
+    std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials,
+    const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update,
     const scoped_refptr<net::URLRequestContextGetter>&
         url_request_context_getter,
     bool auto_refresh)
-    : oauth_credentials_(std::move(oauth_credentials)),
+    : intermediate_credentials_(std::move(intermediate_credentials)),
+      gaia_oauth_client_(
+          new gaia::GaiaOAuthClient(url_request_context_getter.get())),
+      credentials_updated_callback_(on_credentials_update),
+      url_request_context_getter_(url_request_context_getter) {
+  if (auto_refresh) {
+    refresh_timer_.reset(new base::OneShotTimer());
+  }
+}
+
+OAuthTokenGetterImpl::OAuthTokenGetterImpl(
+    std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials,
+    const scoped_refptr<net::URLRequestContextGetter>&
+        url_request_context_getter,
+    bool auto_refresh)
+    : authorization_credentials_(std::move(authorization_credentials)),
       gaia_oauth_client_(
           new gaia::GaiaOAuthClient(url_request_context_getter.get())),
       url_request_context_getter_(url_request_context_getter) {
@@ -41,47 +58,62 @@
 
 OAuthTokenGetterImpl::~OAuthTokenGetterImpl() {}
 
-void OAuthTokenGetterImpl::OnGetTokensResponse(const std::string& user_email,
+void OAuthTokenGetterImpl::OnGetTokensResponse(const std::string& refresh_token,
                                                const std::string& access_token,
                                                int expires_seconds) {
-  NOTREACHED();
+  DCHECK(CalledOnValidThread());
+  DCHECK(intermediate_credentials_);
+  VLOG(1) << "Received OAuth tokens.";
+
+  // Update the access token and any other auto-update timers.
+  UpdateAccessToken(access_token, expires_seconds);
+
+  // Keep the refresh token in the authorization_credentials.
+  authorization_credentials_.reset(
+      new OAuthTokenGetter::OAuthAuthorizationCredentials(
+          std::string(), refresh_token,
+          intermediate_credentials_->is_service_account));
+
+  // Clear out the one time use token.
+  intermediate_credentials_.reset();
+
+  // At this point we don't know the email address so we need to fetch it.
+  email_discovery_ = true;
+  gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this);
 }
 
 void OAuthTokenGetterImpl::OnRefreshTokenResponse(
     const std::string& access_token,
     int expires_seconds) {
   DCHECK(CalledOnValidThread());
-  DCHECK(oauth_credentials_.get());
+  DCHECK(authorization_credentials_);
   VLOG(1) << "Received OAuth token.";
 
-  oauth_access_token_ = access_token;
-  base::TimeDelta token_expiration =
-      base::TimeDelta::FromSeconds(expires_seconds) -
-      base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
-  auth_token_expiry_time_ = base::Time::Now() + token_expiration;
+  // Update the access token and any other auto-update timers.
+  UpdateAccessToken(access_token, expires_seconds);
 
-  if (refresh_timer_) {
-    refresh_timer_->Stop();
-    refresh_timer_->Start(FROM_HERE, token_expiration, this,
-                          &OAuthTokenGetterImpl::RefreshOAuthToken);
-  }
-
-  if (!oauth_credentials_->is_service_account && !email_verified_) {
+  if (!authorization_credentials_->is_service_account && !email_verified_) {
     gaia_oauth_client_->GetUserEmail(access_token, kMaxRetries, this);
   } else {
-    refreshing_oauth_token_ = false;
-    NotifyCallbacks(OAuthTokenGetterImpl::SUCCESS, oauth_credentials_->login,
-                    oauth_access_token_);
+    response_pending_ = false;
+    NotifyTokenCallbacks(OAuthTokenGetterImpl::SUCCESS,
+                         authorization_credentials_->login,
+                         oauth_access_token_);
   }
 }
 
 void OAuthTokenGetterImpl::OnGetUserEmailResponse(
     const std::string& user_email) {
   DCHECK(CalledOnValidThread());
-  DCHECK(oauth_credentials_.get());
+  DCHECK(authorization_credentials_);
   VLOG(1) << "Received user info.";
 
-  if (user_email != oauth_credentials_->login) {
+  if (email_discovery_) {
+    authorization_credentials_->login = user_email;
+    email_discovery_ = false;
+    NotifyUpdatedCallbacks(authorization_credentials_->login,
+                           authorization_credentials_->refresh_token);
+  } else if (user_email != authorization_credentials_->login) {
     LOG(ERROR) << "OAuth token and email address do not refer to "
                   "the same account.";
     OnOAuthError();
@@ -89,17 +121,34 @@
   }
 
   email_verified_ = true;
-  refreshing_oauth_token_ = false;
+  response_pending_ = false;
 
   // Now that we've refreshed the token and verified that it's for the correct
   // user account, try to connect using the new token.
-  NotifyCallbacks(OAuthTokenGetterImpl::SUCCESS, user_email,
-                  oauth_access_token_);
+  NotifyTokenCallbacks(OAuthTokenGetterImpl::SUCCESS, user_email,
+                       oauth_access_token_);
 }
 
-void OAuthTokenGetterImpl::NotifyCallbacks(Status status,
-                                           const std::string& user_email,
-                                           const std::string& access_token) {
+void OAuthTokenGetterImpl::UpdateAccessToken(const std::string& access_token,
+                                             int expires_seconds) {
+  oauth_access_token_ = access_token;
+  base::TimeDelta token_expiration =
+      base::TimeDelta::FromSeconds(expires_seconds) -
+      base::TimeDelta::FromSeconds(kTokenUpdateTimeBeforeExpirySeconds);
+  access_token_expiry_time_ = base::Time::Now() + token_expiration;
+
+  if (refresh_timer_) {
+    refresh_timer_->Stop();
+    refresh_timer_->Start(FROM_HERE, token_expiration, this,
+                          &OAuthTokenGetterImpl::RefreshAccessToken);
+  }
+}
+
+void OAuthTokenGetterImpl::NotifyTokenCallbacks(
+    Status status,
+    const std::string& user_email,
+    const std::string& access_token) {
+  DCHECK(CalledOnValidThread());
   std::queue<TokenCallback> callbacks(pending_callbacks_);
   pending_callbacks_ = std::queue<TokenCallback>();
 
@@ -109,72 +158,123 @@
   }
 }
 
+void OAuthTokenGetterImpl::NotifyUpdatedCallbacks(
+    const std::string& user_email,
+    const std::string& refresh_token) {
+  DCHECK(CalledOnValidThread());
+  if (credentials_updated_callback_) {
+    credentials_updated_callback_.Run(user_email, refresh_token);
+  }
+}
+
 void OAuthTokenGetterImpl::OnOAuthError() {
   DCHECK(CalledOnValidThread());
   LOG(ERROR) << "OAuth: invalid credentials.";
-  refreshing_oauth_token_ = false;
+  response_pending_ = false;
 
   // Throw away invalid credentials and force a refresh.
   oauth_access_token_.clear();
-  auth_token_expiry_time_ = base::Time();
+  access_token_expiry_time_ = base::Time();
   email_verified_ = false;
 
-  NotifyCallbacks(OAuthTokenGetterImpl::AUTH_ERROR, std::string(),
-                  std::string());
+  NotifyTokenCallbacks(OAuthTokenGetterImpl::AUTH_ERROR, std::string(),
+                       std::string());
 }
 
 void OAuthTokenGetterImpl::OnNetworkError(int response_code) {
   DCHECK(CalledOnValidThread());
   LOG(ERROR) << "Network error when trying to update OAuth token: "
              << response_code;
-  refreshing_oauth_token_ = false;
-  NotifyCallbacks(OAuthTokenGetterImpl::NETWORK_ERROR, std::string(),
-                  std::string());
+  response_pending_ = false;
+  NotifyTokenCallbacks(OAuthTokenGetterImpl::NETWORK_ERROR, std::string(),
+                       std::string());
 }
 
 void OAuthTokenGetterImpl::CallWithToken(const TokenCallback& on_access_token) {
   DCHECK(CalledOnValidThread());
-  bool need_new_auth_token =
-      auth_token_expiry_time_.is_null() ||
-      base::Time::Now() >= auth_token_expiry_time_ ||
-      (!oauth_credentials_->is_service_account && !email_verified_);
-
-  if (need_new_auth_token) {
+  if (intermediate_credentials_) {
     pending_callbacks_.push(on_access_token);
-    if (!refreshing_oauth_token_)
-      RefreshOAuthToken();
+    if (!response_pending_) {
+      GetOauthTokensFromAuthCode();
+    }
   } else {
-    on_access_token.Run(SUCCESS, oauth_credentials_->login,
-                        oauth_access_token_);
+    bool need_new_auth_token =
+        access_token_expiry_time_.is_null() ||
+        base::Time::Now() >= access_token_expiry_time_ ||
+        (!authorization_credentials_->is_service_account && !email_verified_);
+
+    if (need_new_auth_token) {
+      pending_callbacks_.push(on_access_token);
+      if (!response_pending_) {
+        RefreshAccessToken();
+      }
+    } else {
+      on_access_token.Run(SUCCESS, authorization_credentials_->login,
+                          oauth_access_token_);
+    }
   }
 }
 
 void OAuthTokenGetterImpl::InvalidateCache() {
   DCHECK(CalledOnValidThread());
-  auth_token_expiry_time_ = base::Time();
+  access_token_expiry_time_ = base::Time();
 }
 
-void OAuthTokenGetterImpl::RefreshOAuthToken() {
+void OAuthTokenGetterImpl::GetOauthTokensFromAuthCode() {
   DCHECK(CalledOnValidThread());
-  VLOG(1) << "Refreshing OAuth token.";
-  DCHECK(!refreshing_oauth_token_);
+  VLOG(1) << "Fetching OAuth token from Auth Code.";
+  DCHECK(!response_pending_);
 
   // Service accounts use different API keys, as they use the client app flow.
   google_apis::OAuth2Client oauth2_client =
-      oauth_credentials_->is_service_account ? google_apis::CLIENT_REMOTING_HOST
-                                             : google_apis::CLIENT_REMOTING;
+      intermediate_credentials_->is_service_account
+          ? google_apis::CLIENT_REMOTING_HOST
+          : google_apis::CLIENT_REMOTING;
+
+  std::string redirect_uri;
+  if (intermediate_credentials_->oauth_redirect_uri.empty()) {
+    if (intermediate_credentials_->is_service_account) {
+      redirect_uri = "oob";
+    } else {
+      redirect_uri = GetDefaultOauthRedirectUrl();
+    }
+  } else {
+    redirect_uri = intermediate_credentials_->oauth_redirect_uri;
+  }
+
+  gaia::OAuthClientInfo client_info = {
+      google_apis::GetOAuth2ClientID(oauth2_client),
+      google_apis::GetOAuth2ClientSecret(oauth2_client), redirect_uri};
+
+  response_pending_ = true;
+
+  gaia_oauth_client_->GetTokensFromAuthCode(
+      client_info, intermediate_credentials_->authorization_code, kMaxRetries,
+      this);
+}
+
+void OAuthTokenGetterImpl::RefreshAccessToken() {
+  DCHECK(CalledOnValidThread());
+  VLOG(1) << "Refreshing OAuth Access token.";
+  DCHECK(!response_pending_);
+
+  // Service accounts use different API keys, as they use the client app flow.
+  google_apis::OAuth2Client oauth2_client =
+      authorization_credentials_->is_service_account
+          ? google_apis::CLIENT_REMOTING_HOST
+          : google_apis::CLIENT_REMOTING;
 
   gaia::OAuthClientInfo client_info = {
       google_apis::GetOAuth2ClientID(oauth2_client),
       google_apis::GetOAuth2ClientSecret(oauth2_client),
       // Redirect URL is only used when getting tokens from auth code. It
-      // is not required when getting access tokens.
+      // is not required when getting access tokens from refresh tokens.
       ""};
 
-  refreshing_oauth_token_ = true;
+  response_pending_ = true;
   std::vector<std::string> empty_scope_list;  // Use scope from refresh token.
   gaia_oauth_client_->RefreshToken(client_info,
-                                   oauth_credentials_->refresh_token,
+                                   authorization_credentials_->refresh_token,
                                    empty_scope_list, kMaxRetries, this);
 }
 
diff --git a/remoting/base/oauth_token_getter_impl.h b/remoting/base/oauth_token_getter_impl.h
index f3d2291e..c725d524 100644
--- a/remoting/base/oauth_token_getter_impl.h
+++ b/remoting/base/oauth_token_getter_impl.h
@@ -20,15 +20,29 @@
 
 namespace remoting {
 
-// OAuthTokenGetter caches OAuth access tokens and refreshes them as needed.
+// OAuthTokenGetter accepts an authorization code in the intermediate
+// credentials or a refresh token in the authorization credentials. It will
+// convert authorization code into a refresh token and access token, you may
+// pass in a callback to be notified when a refresh token has been updated.
+// OAuthTokenGetter will exchange refresh tokens for access tokens and will
+// cache access tokens, refreshing them as needed.
+// On first usage it is likely an application will only have an auth code,
+// from this you can get a refresh token which can be reused next app launch.
 class OAuthTokenGetterImpl : public OAuthTokenGetter,
                              public base::NonThreadSafe,
                              public gaia::GaiaOAuthClient::Delegate {
  public:
-  OAuthTokenGetterImpl(std::unique_ptr<OAuthCredentials> oauth_credentials,
-                       const scoped_refptr<net::URLRequestContextGetter>&
-                           url_request_context_getter,
-                       bool auto_refresh);
+  OAuthTokenGetterImpl(
+      std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials,
+      const OAuthTokenGetter::CredentialsUpdatedCallback& on_credentials_update,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter,
+      bool auto_refresh);
+  OAuthTokenGetterImpl(
+      std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials,
+      const scoped_refptr<net::URLRequestContextGetter>&
+          url_request_context_getter,
+      bool auto_refresh);
   ~OAuthTokenGetterImpl() override;
 
   // OAuthTokenGetter interface.
@@ -47,19 +61,26 @@
   void OnOAuthError() override;
   void OnNetworkError(int response_code) override;
 
-  void NotifyCallbacks(Status status,
-                       const std::string& user_email,
-                       const std::string& access_token);
-  void RefreshOAuthToken();
+  void UpdateAccessToken(const std::string& access_token, int expires_seconds);
+  void NotifyTokenCallbacks(Status status,
+                            const std::string& user_email,
+                            const std::string& access_token);
+  void NotifyUpdatedCallbacks(const std::string& user_email,
+                              const std::string& refresh_token);
+  void GetOauthTokensFromAuthCode();
+  void RefreshAccessToken();
 
-  std::unique_ptr<OAuthCredentials> oauth_credentials_;
+  std::unique_ptr<OAuthIntermediateCredentials> intermediate_credentials_;
+  std::unique_ptr<OAuthAuthorizationCredentials> authorization_credentials_;
   std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
+  OAuthTokenGetter::CredentialsUpdatedCallback credentials_updated_callback_;
   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter_;
 
-  bool refreshing_oauth_token_ = false;
+  bool response_pending_ = false;
   bool email_verified_ = false;
+  bool email_discovery_ = false;
   std::string oauth_access_token_;
-  base::Time auth_token_expiry_time_;
+  base::Time access_token_expiry_time_;
   std::queue<OAuthTokenGetter::TokenCallback> pending_callbacks_;
   std::unique_ptr<base::OneShotTimer> refresh_timer_;
 };
diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc
index d4851ad..6c0cfd41 100644
--- a/remoting/host/remoting_me2me_host.cc
+++ b/remoting/host/remoting_me2me_host.cc
@@ -1355,10 +1355,10 @@
   std::unique_ptr<DnsBlackholeChecker> dns_blackhole_checker(
       new DnsBlackholeChecker(context_->url_request_context_getter(),
                               talkgadget_prefix_));
-  std::unique_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials(
-      new OAuthTokenGetter::OAuthCredentials(xmpp_server_config_.username,
-                                             oauth_refresh_token_,
-                                             use_service_account_));
+  std::unique_ptr<OAuthTokenGetter::OAuthAuthorizationCredentials>
+      oauth_credentials(new OAuthTokenGetter::OAuthAuthorizationCredentials(
+          xmpp_server_config_.username, oauth_refresh_token_,
+          use_service_account_));
   oauth_token_getter_.reset(
       new OAuthTokenGetterImpl(std::move(oauth_credentials),
                                context_->url_request_context_getter(), false));
diff --git a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
index fa4bb572..47a336c 100644
--- a/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
+++ b/third_party/WebKit/LayoutTests/FlagExpectations/enable-browser-side-navigation
@@ -9,8 +9,10 @@
 crbug.com/551000 virtual/mojo-loading/http/tests/inspector/console-resource-errors.html [ Failure ]
 
 # Flipped order between diStartProvisionalLoad & willSendRequest
-crbug.com/625765 virtual/mojo-loading/http/tests/loading/redirect-methods.html [ Crash Failure ]
+crbug.com/647698 http/tests/loading/307-after-303-after-post.html [ Failure ]
 crbug.com/625765 http/tests/loading/redirect-methods.html [ Crash Failure ]
+crbug.com/647698 virtual/mojo-loading/http/tests/loading/307-after-303-after-post.html [ Failure ]
+crbug.com/625765 virtual/mojo-loading/http/tests/loading/redirect-methods.html [ Crash Failure ]
 
 # https://crbug.com/555418: Move `X-Frame-Options` and CSP's `frame-ancestor`
 # checks up out of the renderer.
@@ -29,11 +31,6 @@
 crbug.com/638900 http/tests/loading/doc-write-sync-third-party-script-reload.html [ Failure ]
 crbug.com/638900 virtual/mojo-loading/http/tests/loading/doc-write-sync-third-party-script-reload.html [ Failure ]
 
-# https://crbug.com/647698: Failing due to absence of call to
-# WebFrameClient::didReceiveServerRedirectForProvisionalLoad.
-crbug.com/647698 http/tests/loading/307-after-303-after-post.html [ Failure ]
-crbug.com/647698 virtual/mojo-loading/http/tests/loading/307-after-303-after-post.html [ Failure ]
-
 # Issue with ServiceWorker timing.
 Bug(none) external/wpt/service-workers/service-worker/navigate-window.https.html [ Failure ]
 
@@ -47,9 +44,11 @@
 Bug(none) http/tests/security/location-href-clears-username-password.html [ Failure ]
 Bug(none) virtual/mojo-loading/http/tests/security/location-href-clears-username-password.html [ Failure ]
 
-# This test seems to be partially failing without PlzNavigate as well.
+# Timeout
+Bug(none) fast/loader/crash-replacing-location-before-load.html [ Timeout ]
 
 # These tests are flaky.
+Bug(none) external/wpt/service-workers/service-worker/fetch-event-redirect.https.html [ Crash ]
 Bug(none) http/tests/misc/window-open-then-write.html [ Timeout ]
 Bug(none) external/wpt/html/browsers/browsing-the-web/unloading-documents/beforeunload-on-history-back.html [ Failure ]
 Bug(none) virtual/mojo-loading/http/tests/misc/window-open-then-write.html [ Timeout ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index 74f72b0..32ce71d 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -771,6 +771,10 @@
 
 crbug.com/617152 external/wpt/mediacapture-streams/GUM-impossible-constraint.https.html [ Skip ]
 
+crbug.com/281504 fast/canvas/canvas-large-pattern.html [ Failure ]
+crbug.com/281504 virtual/display_list_2d_canvas/fast/canvas/canvas-large-pattern.html [ Failure ]
+crbug.com/281504 virtual/gpu/fast/canvas/canvas-large-pattern.html [ Failure ]
+
 crbug.com/417782 [ Linux Win ] virtual/rootlayerscrolls/fast/scrolling/fractional-scroll-offset-fixed-position-non-composited.html [ Failure ]
 crbug.com/492664 [ Linux ] external/csswg-test/css-writing-modes-3/box-offsets-rel-pos-vlr-005.xht [ Failure ]
 crbug.com/492664 [ Linux ] external/csswg-test/css-writing-modes-3/box-offsets-rel-pos-vrl-004.xht [ Failure ]
@@ -1402,6 +1406,7 @@
 # These tests are skipped as there is no touch support on Mac.
 crbug.com/613672 [ Mac ] fast/events/pointerevents/multi-pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/pointerevents/pointerevent_touch-action-pinch_zoom_touch.html [ Skip ]
+crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] fast/events/pointerevents/pointer-event-in-slop-region.html [ Skip ]
 crbug.com/613672 [ Mac ] external/wpt/pointerevents/pointerlock/pointerevent_movementxy-manual.html [ Skip ]
 crbug.com/613672 [ Mac ] external/wpt/pointerevents/compat/pointerevent_touch-action_two-finger_interaction-manual.html [ Skip ]
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding-expected.txt
deleted file mode 100644
index 13904cd3..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding-expected.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-Series of tests to ensure correct results of the winding rule in isPointInPath.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Testing default isPointInPath
-PASS ctx.isPointInPath(50, 50) is true
-PASS ctx.isPointInPath(NaN, 50) is false
-PASS ctx.isPointInPath(50, NaN) is false
-
-Testing nonzero isPointInPath
-PASS ctx.isPointInPath(50, 50, 'nonzero') is true
-
-Testing evenodd isPointInPath
-PASS ctx.isPointInPath(50, 50, 'evenodd') is false
-
-Testing default isPointInPath with Path object
-PASS ctx.isPointInPath(path, 50, 50) is true
-PASS ctx.isPointInPath(path, 50, 50, undefined) is true
-PASS ctx.isPointInPath(path, NaN, 50) is false
-PASS ctx.isPointInPath(path, 50, NaN) is false
-
-Testing nonzero isPointInPath with Path object
-PASS ctx.isPointInPath(path, 50, 50, 'nonzero') is true
-
-Testing evenodd isPointInPath with Path object
-PASS ctx.isPointInPath(path, 50, 50, 'evenodd') is false
-
-Testing invalid enumeration isPointInPath (w/ and w/o Path object
-PASS ctx.isPointInPath(path, 50, 50, 'gazonk') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value 'gazonk' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath(50, 50, 'gazonk') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value 'gazonk' is not a valid enum value of type CanvasFillRule..
-
-Testing invalid type isPointInPath with Path object
-PASS ctx.isPointInPath(null, 50, 50) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value '50' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath(null, 50, 50, 'nonzero') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath(null, 50, 50, 'evenodd') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath(null, 50, 50, null) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath(path, 50, 50, null) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value 'null' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath(undefined, 50, 50) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value '50' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath(undefined, 50, 50, 'nonzero') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath(undefined, 50, 50, 'evenodd') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath(undefined, 50, 50, undefined) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath([], 50, 50) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value '50' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath([], 50, 50, 'nonzero') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath([], 50, 50, 'evenodd') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath({}, 50, 50) threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': The provided value '50' is not a valid enum value of type CanvasFillRule..
-PASS ctx.isPointInPath({}, 50, 50, 'nonzero') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInPath({}, 50, 50, 'evenodd') threw exception TypeError: Failed to execute 'isPointInPath' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-
-Testing extremely large scale
-PASS ctx.isPointInPath(0, 0, 'nonzero') is true
-PASS ctx.isPointInPath(0, 0, 'evenodd') is true
-Check with non-invertible ctm.
-PASS ctx.isPointInPath(0, 0, 'nonzero') is false
-PASS ctx.isPointInPath(0, 0, 'evenodd') is false
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding.html
index 3da36651..82851c0 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInPath-winding.html
@@ -1,8 +1,95 @@
-<!doctype html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
-<body>
-<script src="script-tests/canvas-isPointInPath-winding.js"></script>
-</body>
\ No newline at end of file
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
+
+var canvas = document.createElement('canvas');
+canvas.width = 200;
+canvas.height = 200;
+ctx = canvas.getContext('2d');
+
+test(function(t) {
+    // Testing default isPointInPath');
+    ctx.beginPath();
+    ctx.rect(0, 0, 100, 100);
+    ctx.rect(25, 25, 50, 50);
+    assert_true(ctx.isPointInPath(50, 50));
+    assert_false(ctx.isPointInPath(NaN, 50));
+    assert_false(ctx.isPointInPath(50, NaN));
+
+    // Testing nonzero isPointInPath');
+    ctx.beginPath();
+    ctx.rect(0, 0, 100, 100);
+    ctx.rect(25, 25, 50, 50);
+    assert_true(ctx.isPointInPath(50, 50, 'nonzero'));
+    
+    // Testing evenodd isPointInPath');
+    ctx.beginPath();
+    ctx.rect(0, 0, 100, 100);
+    ctx.rect(25, 25, 50, 50);
+    assert_false(ctx.isPointInPath(50, 50, 'evenodd'));
+
+    // reset path in context
+    ctx.beginPath();
+
+    // Testing default isPointInPath with Path object');
+    path = new Path2D();
+    path.rect(0, 0, 100, 100);
+    path.rect(25, 25, 50, 50);
+    assert_true(ctx.isPointInPath(path, 50, 50));
+    assert_true(ctx.isPointInPath(path, 50, 50, undefined));
+    assert_false(ctx.isPointInPath(path, NaN, 50));
+    assert_false(ctx.isPointInPath(path, 50, NaN));
+
+    // Testing nonzero isPointInPath with Path object');
+    path = new Path2D();
+    path.rect(0, 0, 100, 100);
+    path.rect(25, 25, 50, 50);
+    assert_true(ctx.isPointInPath(path, 50, 50, 'nonzero'));
+
+    // Testing evenodd isPointInPath with Path object');
+    path = new Path2D();
+    path.rect(0, 0, 100, 100);
+    path.rect(25, 25, 50, 50);
+    assert_false(ctx.isPointInPath(path, 50, 50, 'evenodd'));
+
+    // Testing invalid enumeration isPointInPath (w/ and w/o Path object');
+    assert_throws(null, function(){ ctx.isPointInPath(path, 50, 50, 'gazonk');});
+    assert_throws(null, function(){ ctx.isPointInPath(50, 50, 'gazonk');});
+
+    // Testing invalid type isPointInPath with Path object');
+    assert_throws(null, function(){ ctx.isPointInPath(null, 50, 50);});
+    assert_throws(null, function(){ ctx.isPointInPath(null, 50, 50, 'nonzero');});
+    assert_throws(null, function(){ ctx.isPointInPath(null, 50, 50, 'evenodd');});
+    assert_throws(null, function(){ ctx.isPointInPath(null, 50, 50, null);});
+    assert_throws(null, function(){ ctx.isPointInPath(path, 50, 50, null);});
+    assert_throws(null, function(){ ctx.isPointInPath(undefined, 50, 50);});
+    assert_throws(null, function(){ ctx.isPointInPath(undefined, 50, 50, 'nonzero');});
+    assert_throws(null, function(){ ctx.isPointInPath(undefined, 50, 50, 'evenodd');});
+    assert_throws(null, function(){ ctx.isPointInPath(undefined, 50, 50, undefined);});
+    assert_throws(null, function(){ ctx.isPointInPath([], 50, 50);});
+    assert_throws(null, function(){ ctx.isPointInPath([], 50, 50, 'nonzero');});
+    assert_throws(null, function(){ ctx.isPointInPath([], 50, 50, 'evenodd');});
+    assert_throws(null, function(){ ctx.isPointInPath({}, 50, 50);});
+    assert_throws(null, function(){ ctx.isPointInPath({}, 50, 50, 'nonzero');});
+    assert_throws(null, function(){ ctx.isPointInPath({}, 50, 50, 'evenodd');});
+
+    // Testing extremely large scale
+    ctx.save();
+    ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+    ctx.beginPath();
+    ctx.rect(-10, -10, 20, 20);
+    assert_true(ctx.isPointInPath(0, 0, 'nonzero'));
+    assert_true(ctx.isPointInPath(0, 0, 'evenodd'));
+    ctx.restore();
+
+    // Check with non-invertible ctm.
+    ctx.save();
+    ctx.scale(0, 0);
+    ctx.beginPath();
+    ctx.rect(-10, -10, 20, 20);
+    assert_false(ctx.isPointInPath(0, 0, 'nonzero'));
+    assert_false(ctx.isPointInPath(0, 0, 'evenodd'));
+    ctx.restore();
+}, "Series of tests to ensure correct results of the winding rule in isPointInPath.");
+
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-expected.txt
deleted file mode 100644
index 9bbd037..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-expected.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-Test the behavior of isPointInStroke in Canvas
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Initial behavior: lineWith = 1.0
-PASS ctx.isPointInStroke(20,20) is true
-PASS ctx.isPointInStroke(120,20) is true
-PASS ctx.isPointInStroke(20,120) is true
-PASS ctx.isPointInStroke(120,120) is true
-PASS ctx.isPointInStroke(70,20) is true
-PASS ctx.isPointInStroke(20,70) is true
-PASS ctx.isPointInStroke(120,70) is true
-PASS ctx.isPointInStroke(70,120) is true
-PASS ctx.isPointInStroke(22,22) is false
-PASS ctx.isPointInStroke(118,22) is false
-PASS ctx.isPointInStroke(22,118) is false
-PASS ctx.isPointInStroke(118,118) is false
-PASS ctx.isPointInStroke(70,18) is false
-PASS ctx.isPointInStroke(122,70) is false
-PASS ctx.isPointInStroke(70,122) is false
-PASS ctx.isPointInStroke(18,70) is false
-
-Set lineWith = 10.0
-PASS ctx.isPointInStroke(22,22) is true
-PASS ctx.isPointInStroke(118,22) is true
-PASS ctx.isPointInStroke(22,118) is true
-PASS ctx.isPointInStroke(118,118) is true
-PASS ctx.isPointInStroke(70,18) is true
-PASS ctx.isPointInStroke(122,70) is true
-PASS ctx.isPointInStroke(70,122) is true
-PASS ctx.isPointInStroke(18,70) is true
-PASS ctx.isPointInStroke(26,70) is false
-PASS ctx.isPointInStroke(70,26) is false
-PASS ctx.isPointInStroke(70,114) is false
-PASS ctx.isPointInStroke(114,70) is false
-
-Check lineJoin = 'bevel'
-PASS ctx.isPointInStroke(113,20) is false
-
-Check lineJoin = 'miter'
-PASS ctx.isPointInStroke(113,20) is true
-
-Check miterLimit = 2.0
-PASS ctx.isPointInStroke(113,20) is false
-
-Check lineCap = 'butt'
-PASS ctx.isPointInStroke(112,10) is false
-
-Check lineCap = 'round'
-PASS ctx.isPointInStroke(112,10) is true
-PASS ctx.isPointInStroke(117,10) is false
-
-Check lineCap = 'square'
-PASS ctx.isPointInStroke(112,10) is true
-PASS ctx.isPointInStroke(117,10) is false
-
-Check setLineDash([10,10])
-PASS ctx.isPointInStroke(15,10) is true
-PASS ctx.isPointInStroke(25,10) is false
-PASS ctx.isPointInStroke(35,10) is true
-
-Check dashOffset = 10
-PASS ctx.isPointInStroke(15,10) is false
-PASS ctx.isPointInStroke(25,10) is true
-PASS ctx.isPointInStroke(35,10) is false
-Check extremely large scale
-PASS ctx.isPointInStroke(0, 0) is true
-Check with non-invertible ctm.
-PASS ctx.isPointInStroke(0, 0) is false
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path-expected.txt
deleted file mode 100644
index dec4240..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path-expected.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-Test the behavior of isPointInStroke in Canvas with path object
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-Initial behavior: lineWidth = 1.0
-PASS ctx.isPointInStroke(path,20,20) is true
-PASS ctx.isPointInStroke(path,120,20) is true
-PASS ctx.isPointInStroke(path,20,120) is true
-PASS ctx.isPointInStroke(path,120,120) is true
-PASS ctx.isPointInStroke(path,70,20) is true
-PASS ctx.isPointInStroke(path,20,70) is true
-PASS ctx.isPointInStroke(path,120,70) is true
-PASS ctx.isPointInStroke(path,70,120) is true
-PASS ctx.isPointInStroke(path,22,22) is false
-PASS ctx.isPointInStroke(path,118,22) is false
-PASS ctx.isPointInStroke(path,22,118) is false
-PASS ctx.isPointInStroke(path,118,118) is false
-PASS ctx.isPointInStroke(path,70,18) is false
-PASS ctx.isPointInStroke(path,122,70) is false
-PASS ctx.isPointInStroke(path,70,122) is false
-PASS ctx.isPointInStroke(path,18,70) is false
-PASS ctx.isPointInStroke(path,NaN,122) is false
-PASS ctx.isPointInStroke(path,18,NaN) is false
-
-Check invalid type
-PASS ctx.isPointInStroke(null,70,20) threw exception TypeError: Failed to execute 'isPointInStroke' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInStroke(undefined,70,20) threw exception TypeError: Failed to execute 'isPointInStroke' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInStroke([],20,70) threw exception TypeError: Failed to execute 'isPointInStroke' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-PASS ctx.isPointInStroke({},120,70) threw exception TypeError: Failed to execute 'isPointInStroke' on 'CanvasRenderingContext2D': parameter 1 is not of type 'Path2D'..
-
-Set lineWidth = 10.0
-PASS ctx.isPointInStroke(path,22,22) is true
-PASS ctx.isPointInStroke(path,118,22) is true
-PASS ctx.isPointInStroke(path,22,118) is true
-PASS ctx.isPointInStroke(path,118,118) is true
-PASS ctx.isPointInStroke(path,70,18) is true
-PASS ctx.isPointInStroke(path,122,70) is true
-PASS ctx.isPointInStroke(path,70,122) is true
-PASS ctx.isPointInStroke(path,18,70) is true
-PASS ctx.isPointInStroke(path,26,70) is false
-PASS ctx.isPointInStroke(path,70,26) is false
-PASS ctx.isPointInStroke(path,70,114) is false
-PASS ctx.isPointInStroke(path,114,70) is false
-
-Check lineJoin = 'bevel'
-PASS ctx.isPointInStroke(path,113,20) is false
-
-Check lineJoin = 'miter'
-PASS ctx.isPointInStroke(path,113,20) is true
-
-Check miterLimit = 2.0
-PASS ctx.isPointInStroke(path,113,20) is false
-
-Check lineCap = 'butt'
-PASS ctx.isPointInStroke(path,112,10) is false
-
-Check lineCap = 'round'
-PASS ctx.isPointInStroke(path,112,10) is true
-PASS ctx.isPointInStroke(path,117,10) is false
-
-Check lineCap = 'square'
-PASS ctx.isPointInStroke(path,112,10) is true
-PASS ctx.isPointInStroke(path,117,10) is false
-
-Check setLineDash([10,10])
-PASS ctx.isPointInStroke(path,15,10) is true
-PASS ctx.isPointInStroke(path,25,10) is false
-PASS ctx.isPointInStroke(path,35,10) is true
-
-Check dashOffset = 10
-PASS ctx.isPointInStroke(path,15,10) is false
-PASS ctx.isPointInStroke(path,25,10) is true
-PASS ctx.isPointInStroke(path,35,10) is false
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path.html
index 48c355d..3ba08b0 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke-with-path.html
@@ -1,8 +1,113 @@
-<!doctype html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <body>
-<script src="script-tests/canvas-isPointInStroke-with-path.js"></script>
+<script>
+var ctx = document.createElement('canvas').getContext('2d');
+document.body.appendChild(ctx.canvas);
+ctx.strokeStyle = '#0ff';
+
+// Create new path.
+var path = new Path2D();
+path.rect(20, 20, 100, 100);
+
+function testPointInStroke(x, y, isPointInStroke) {
+    if(isPointInStroke)
+        assert_true(ctx.isPointInStroke(path, x, y));
+    else
+        assert_false(ctx.isPointInStroke(path, x, y));
+}
+
+testScenarios1 = 
+    [        
+        ['TestScenario 1, Case 1', 20, 20, true],
+        ['TestScenario 1, Case 2', 120, 20, true],
+        ['TestScenario 1, Case 3', 20, 120, true],
+        ['TestScenario 1, Case 4', 120, 120, true],
+        ['TestScenario 1, Case 5', 70, 20, true],
+        ['TestScenario 1, Case 6', 20, 70, true],
+        ['TestScenario 1, Case 7', 120, 70, true],
+        ['TestScenario 1, Case 8', 70, 120, true],
+
+        ['TestScenario 1, Case 9', 22, 22, false],
+        ['TestScenario 1, Case 10', 118, 22, false],
+        ['TestScenario 1, Case 11', 22, 118, false],
+        ['TestScenario 1, Case 12', 118, 118, false],
+        ['TestScenario 1, Case 13', 70, 18, false],
+        ['TestScenario 1, Case 14', 122, 70, false],
+        ['TestScenario 1, Case 15', 70, 122, false],
+        ['TestScenario 1, Case 16', 18, 70, false],
+        ['TestScenario 1, Case 17', NaN, 122, false],
+        ['TestScenario 1, Case 18', 18, NaN, false]
+    ];
+generate_tests(testPointInStroke, testScenarios1);
+
+test(function(t) {
+    assert_throws(new TypeError(), function() {ctx.isPointInStroke(null, 70, 20);});
+    assert_throws(new TypeError(), function() {ctx.isPointInStroke(undefined, 70, 20);});
+    assert_throws(new TypeError(), function() {ctx.isPointInStroke([], 20, 70);});
+    assert_throws(new TypeError(), function() {ctx.isPointInStroke({}, 120, 70);});
+}, "isPointInStroke in Canvas throws exception with invalid parameters.");
+
+ctx.lineWidth = 10;
+testScenarios2 = 
+    [        
+        ['TestScenario 2, Case 1', 22, 22, true],
+        ['TestScenario 2, Case 2', 118, 22, true],
+        ['TestScenario 2, Case 3', 22, 118, true],
+        ['TestScenario 2, Case 4', 118, 118, true],
+        ['TestScenario 2, Case 5', 70, 18, true],
+        ['TestScenario 2, Case 6', 122, 70, true],
+        ['TestScenario 2, Case 7', 70, 122, true],
+        ['TestScenario 2, Case 8', 18, 70, true],
+       
+        ['TestScenario 2, Case 9', 26, 70, false],
+        ['TestScenario 2, Case 10', 70, 26, false],
+        ['TestScenario 2, Case 11', 70, 114, false],
+        ['TestScenario 2, Case 12', 114, 70, false]
+    ];
+generate_tests(testPointInStroke, testScenarios2);
+
+test(function(t) {
+
+    path = new Path2D();
+    path.moveTo(10,10);
+    path.lineTo(110,20);
+    path.lineTo(10,30);
+    ctx.lineJoin = "bevel";
+    assert_false(ctx.isPointInStroke(path,113,20));
+    
+    ctx.miterLimit = 40.0;
+    ctx.lineJoin = "miter";
+    assert_true(ctx.isPointInStroke(path,113,20));
+    
+    ctx.miterLimit = 2.0;
+    assert_false(ctx.isPointInStroke(path,113,20));
+    
+    path = new Path2D();
+    path.moveTo(10,10);
+    path.lineTo(110,10);
+    ctx.lineCap = "butt";
+    assert_false(ctx.isPointInStroke(path,112,10));
+    
+    ctx.lineCap = "round";
+    assert_true(ctx.isPointInStroke(path,112,10));
+    assert_false(ctx.isPointInStroke(path,117,10));
+    
+    ctx.lineCap = "square";
+    assert_true(ctx.isPointInStroke(path,112,10));
+    assert_false(ctx.isPointInStroke(path,117,10));
+    
+    ctx.lineCap = "butt";
+    ctx.setLineDash([10,10]);
+    assert_true(ctx.isPointInStroke(path,15,10));
+    assert_false(ctx.isPointInStroke(path,25,10));
+    assert_true(ctx.isPointInStroke(path,35,10));
+    
+    ctx.lineDashOffset = 10;
+    assert_false(ctx.isPointInStroke(path,15,10));
+    assert_true(ctx.isPointInStroke(path,25,10));
+    assert_false(ctx.isPointInStroke(path,35,10));
+    
+}, "Test the behavior of isPointInStroke in Canvas with path object");
+</script>
 </body>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke.html
index 4c0b842..ab62eda 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-isPointInStroke.html
@@ -1,8 +1,120 @@
-<!doctype html>
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <body>
-<script src="script-tests/canvas-isPointInStroke.js"></script>
+<script>
+var ctx = document.createElement('canvas').getContext('2d');
+document.body.appendChild(ctx.canvas);
+ctx.strokeStyle = '#0ff';
+
+// Create new path.
+ctx.beginPath();
+ctx.rect(20,20,100,100);
+
+function testPointInStroke(x, y, isPointInStroke) {
+    if(isPointInStroke)
+        assert_true(ctx.isPointInStroke(x, y));
+    else
+        assert_false(ctx.isPointInStroke(x, y));
+}
+
+testScenarios1 = 
+    [
+        ['TestScenario 1, Case 1', 20, 20, true],
+        ['TestScenario 1, Case 2', 120, 20, true],
+        ['TestScenario 1, Case 3', 20, 120, true],
+        ['TestScenario 1, Case 4', 120, 120, true],
+        ['TestScenario 1, Case 5', 70, 20, true],
+        ['TestScenario 1, Case 6', 20, 70, true],
+        ['TestScenario 1, Case 7', 120, 70, true],
+        ['TestScenario 1, Case 8', 70, 120, true],
+
+        ['TestScenario 1, Case 9', 22, 22, false],
+        ['TestScenario 1, Case 10', 118, 22, false],
+        ['TestScenario 1, Case 11', 22, 118, false],
+        ['TestScenario 1, Case 12', 118, 118, false],
+        ['TestScenario 1, Case 13', 70, 18, false],
+        ['TestScenario 1, Case 14', 122, 70, false],
+        ['TestScenario 1, Case 15', 70, 122, false],
+        ['TestScenario 1, Case 16', 18, 70, false],
+    ];
+
+testScenarios2 = 
+    [
+        ['TestScenario 2, Case 1', 22, 22, true],
+        ['TestScenario 2, Case 2', 118, 22, true],
+        ['TestScenario 2, Case 3', 22, 118, true],
+        ['TestScenario 2, Case 4', 118, 118, true],
+        ['TestScenario 2, Case 5', 70, 18, true],
+        ['TestScenario 2, Case 6', 122, 70, true],
+        ['TestScenario 2, Case 7', 70, 122, true],
+        ['TestScenario 2, Case 8', 18, 70, true],
+
+        ['TestScenario 2, Case 9', 26, 70, false],
+        ['TestScenario 2, Case 10', 70, 26, false],
+        ['TestScenario 2, Case 11', 70, 114, false],
+        ['TestScenario 2, Case 12', 114, 70, false],
+    ];
+
+generate_tests(testPointInStroke, testScenarios1);
+ctx.lineWidth = 10;
+generate_tests(testPointInStroke, testScenarios2);
+
+test(function(t) {
+    ctx.beginPath();
+    ctx.moveTo(10,10);
+    ctx.lineTo(110,20);
+    ctx.lineTo(10,30);
+    ctx.lineJoin = "bevel";
+    assert_false(ctx.isPointInStroke(113,20));
+    
+    ctx.miterLimit = 40.0;
+    ctx.lineJoin = "miter";
+    assert_true(ctx.isPointInStroke(113,20));
+    
+    ctx.miterLimit = 2.0;
+    assert_false(ctx.isPointInStroke(113,20));
+    
+    ctx.beginPath();
+    ctx.moveTo(10,10);
+    ctx.lineTo(110,10);
+    ctx.lineCap = "butt";
+    assert_false(ctx.isPointInStroke(112,10));
+    
+    ctx.lineCap = "round";
+    assert_true(ctx.isPointInStroke(112,10));
+    assert_false(ctx.isPointInStroke(117,10));
+    
+    ctx.lineCap = "square";
+    assert_true(ctx.isPointInStroke(112,10));
+    assert_false(ctx.isPointInStroke(117,10));
+    
+    ctx.lineCap = "butt";
+    ctx.setLineDash([10,10]);
+    assert_true(ctx.isPointInStroke(15,10));
+    assert_false(ctx.isPointInStroke(25,10));
+    assert_true(ctx.isPointInStroke(35,10));
+    
+    ctx.lineDashOffset = 10;
+    assert_false(ctx.isPointInStroke(15,10));
+    assert_true(ctx.isPointInStroke(25,10));
+    assert_false(ctx.isPointInStroke(35,10));
+    
+    ctx.save();
+    ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
+    ctx.beginPath();
+    ctx.moveTo(-10, -10);
+    ctx.lineTo(10, 10);
+    assert_true(ctx.isPointInStroke(0, 0));
+    ctx.restore();
+    
+    ctx.save();
+    ctx.scale(0, 0);
+    ctx.beginPath();
+    ctx.moveTo(-10, -10);
+    ctx.lineTo(10, 10);
+    assert_false(ctx.isPointInStroke(0, 0));
+    ctx.restore();
+}, "Test the behavior of isPointInStroke in Canvas");
+
+</script>
 </body>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions-expected.txt
deleted file mode 100644
index bd110c6..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions-expected.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-Tests that using reasonably large values for canvas.height and canvas.height don't cause a crash"
-
-PASS height == 1000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS height == 10000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS height == 32000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS width == 1000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS width == 10000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS width == 32000
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS Actual: 255 Expected: 255
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions.html
index 08f38a5b..51cd946 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-dimensions.html
@@ -1,66 +1,34 @@
-<!DOCTYPE html>
-<title>Canvas test: test large width/height values</title>
-<script src="../../resources/js-test.js"></script>
-<body>
-<p>Tests that using reasonably large values for canvas.height and canvas.height don't cause a crash"</p>
-<pre id="console"></pre>
-<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script src='../../resources/testharness.js'></script>
+<script src='../../resources/testharnessreport.js'></script>
+<canvas id='c' width='100' height='50'></canvas>
 <script>
-var canvas = document.getElementById("c");
-var x, y, w=1, h=1;
+// Tests that using reasonably large values for canvas.height and canvas.height do not cause a crash
+var canvas = document.getElementById('c');
+var x, y, w = 1, h = 1;
 
-testHeight(canvas, 1000);
-testHeight(canvas, 10000);
-testHeight(canvas, 32000);
-
-testWidth(canvas, 1000);
-testWidth(canvas, 10000);
-testWidth(canvas, 32000);
-
-function testHeight(canvas, height) {
-    canvas.width = 50;
-    canvas.height = height;
-    var ctx = canvas.getContext("2d");
-    ctx.fillStyle = "rgba(255, 255, 255, 1)";
-    var msg = "height == "+height;
-    if (canvas.height == height)
-        testPassed(msg);
-    else
-        testFailed(msg);
-    x = canvas.width-2;
-    y = canvas.height-2;
-    ctx.fillRect(x,y,w,h);
-    var data = ctx.getImageData(x,y,w,h);
-    for (var x = 0; x < 4; x++) {
-        var msg = "Actual: " + data.data[x] + " Expected: 255";
-        if (data.data[x] == 255)
-            testPassed(msg);
-        else
-            testFailed(msg);
-    }
+function testLargeDimension(size, isWidth) {
+    canvas.width = (isWidth ? size : 50);
+    canvas.height = (isWidth ? 50 : size);
+    var ctx = canvas.getContext('2d');
+    ctx.fillStyle = 'rgba(255, 255, 255, 1)';
+    assert_equals((isWidth ? canvas.width : canvas.height), size);
+    x = canvas.width - 2;
+    y = canvas.height - 2;
+    ctx.fillRect(x, y, w, h);
+    var data = ctx.getImageData(x, y, w, h).data;
+    for (var i = 0; i < 4; i++)
+        assert_equals(data[i], 255);
 }
 
-function testWidth(canvas, width) {
-    canvas.height = 50;
-    canvas.width = width;
-    var ctx = canvas.getContext("2d");
-    ctx.fillStyle = "rgba(255, 255, 255, 1)";
-    var msg = "width == "+width;
-    if (canvas.width == width)
-        testPassed(msg);
-    else
-        testFailed(msg);
-    x = canvas.width-2;
-    y = canvas.height-2;
-    ctx.fillRect(x,y,w,h);
-    var data = ctx.getImageData(x,y,w,h);
-    for (var x = 0; x < 4; x++) {
-        var msg = "Actual: " + data.data[x] + " Expected: 255";
-        if (data.data[x] == 255)
-            testPassed(msg);
-        else
-            testFailed(msg);
-    }
-}
+testScenarios = [['Test Width = 1000', 1000, true],
+                 ['Test Width = 10000', 10000, true],
+                 ['Test Width = 32000', 32000, true],
+                 
+                 ['Test Height = 1000', 1000, false],
+                 ['Test Height = 10000', 10000, false],
+                 ['Test Height = 32000', 32000, false]];
+
+generate_tests(testLargeDimension, testScenarios);
+
 </script>
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills-expected.txt
deleted file mode 100644
index 070bba2..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills-expected.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-Tests that using the different composite modes to fill large rects doesn't crash and works as expected.
-PASS Fill Size 10000, source-over: #0000FF
-PASS Fill Size 10000, source-in: #0000FF
-PASS Fill Size 10000, source-out: #000000
-PASS Fill Size 10000, source-atop: #0000FF
-PASS Fill Size 10000, destination-over: #00FF00
-PASS Fill Size 10000, destination-in: #00FF00
-PASS Fill Size 10000, destination-out: #000000
-PASS Fill Size 10000, destination-atop: #00FF00
-PASS Fill Size 10000, lighter: #00FFFF
-PASS Fill Size 10000, copy: #0000FF
-PASS Fill Size 10000, xor: #000000
-PASS Fill Size 50000, source-over: #0000FF
-PASS Fill Size 50000, source-in: #0000FF
-PASS Fill Size 50000, source-out: #000000
-PASS Fill Size 50000, source-atop: #0000FF
-PASS Fill Size 50000, destination-over: #00FF00
-PASS Fill Size 50000, destination-in: #00FF00
-PASS Fill Size 50000, destination-out: #000000
-PASS Fill Size 50000, destination-atop: #00FF00
-PASS Fill Size 50000, lighter: #00FFFF
-PASS Fill Size 50000, copy: #0000FF
-PASS Fill Size 50000, xor: #000000
-PASS Fill Size 100000, source-over: #0000FF
-PASS Fill Size 100000, source-in: #0000FF
-PASS Fill Size 100000, source-out: #000000
-PASS Fill Size 100000, source-atop: #0000FF
-PASS Fill Size 100000, destination-over: #00FF00
-PASS Fill Size 100000, destination-in: #00FF00
-PASS Fill Size 100000, destination-out: #000000
-PASS Fill Size 100000, destination-atop: #00FF00
-PASS Fill Size 100000, lighter: #00FFFF
-PASS Fill Size 100000, copy: #0000FF
-PASS Fill Size 100000, xor: #000000
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills.html
index dd4c03ee..5b81aef 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-fills.html
@@ -1,11 +1,11 @@
-<!DOCTYPE html>
-
-<script src="../../resources/js-test.js"></script>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 
 <pre id="console"></pre>
-<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<canvas id="c" width="100" height="50"></canvas>
 
 <script>
+// Tests that using the different composite modes to fill large rects doesn't crash and works as expected.
 var canvas = document.getElementById("c");
 var ctx = canvas.getContext("2d");
 
@@ -15,57 +15,38 @@
     ctx.fillRect(0, 0, canvas.width, canvas.height);
 }
 
-var testData = [
-    {compositeMode: 'source-over', expected: [0, 0, 255]},
-    {compositeMode: 'source-in', expected: [0, 0, 255]},
-    {compositeMode: 'source-out', expected: [0, 0, 0]},
-    {compositeMode: 'source-atop', expected: [0, 0, 255]},
-    {compositeMode: 'destination-over', expected: [0, 255, 0]},
-    {compositeMode: 'destination-in', expected: [0, 255, 0]},
-    {compositeMode: 'destination-out', expected: [0, 0, 0]},
-    {compositeMode: 'destination-atop', expected: [0, 255, 0]},
-    {compositeMode: 'lighter', expected: [0, 255, 255]},
-    {compositeMode: 'copy', expected: [0, 0, 255]},
-    {compositeMode: 'xor', expected: [0, 0, 0]},
+var testScenarios = [
+    ['Test source-over', 'source-over', [0, 0, 255]],
+    ['Test source-in', 'source-in', [0, 0, 255]],
+    ['Test source-out', 'source-out', [0, 0, 0]],
+    ['Test source-atop', 'source-atop', [0, 0, 255]],
+    ['Test destination-over', 'destination-over', [0, 255, 0]],
+    ['Test destiation-in', 'destination-in', [0, 255, 0]],
+    ['Test destination-out', 'destination-out', [0, 0, 0]],
+    ['Test destination-atop', 'destination-atop', [0, 255, 0]],
+    ['Test lighter', 'lighter', [0, 255, 255]],
+    ['Test copy', 'copy', [0, 0, 255]],
+    ['Test xor', 'xor', [0, 0, 0]]
 ];
 
-function toHexString(number) {
-    var hexString = number.toString(16).toUpperCase();
-    if (number <= 9)
-        hexString = '0' + hexString;
-    return hexString;
-}
-
-function doTest(dataItem, fillSize) {
+var fillSize = 0;
+function testLargeRect(compositeMode, expected) {
     clearContextToGreen();
     ctx.fillStyle = "rgb(0, 0, 255)";
-    ctx.globalCompositeOperation = dataItem.compositeMode;
+    ctx.globalCompositeOperation = compositeMode;
     ctx.fillRect(0, 0, fillSize, fillSize);
 
-    var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
-    var pixelOK = true;
-    var pixelString = '#';
-    var expectedString = '#';
-
-    for (var x = 0; x < 3; x++) {
-        pixelString = pixelString + toHexString(data.data[x]);
-        expectedString = expectedString + toHexString(dataItem.expected[x]);
-        if (data.data[x] != dataItem.expected[x])
-            pixelOK = false;
-    }
-
-    var testName = "Fill Size " + fillSize + ', ' + dataItem.compositeMode;
-    if (pixelOK)
-        testPassed(testName + ': ' + pixelString);
-    else
-        testFailed(testName + ': EXPECTED ' + expectedString + ', GOT ' + pixelString);
+    var data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
+    var testPassed = true;
+    for (var i = 0; i < 3; i++)
+        if (data[i] != expected[i])
+            testPassed = false;
+    assert_true(testPassed);
 }
 
-debug("Tests that using the different composite modes to fill large rects doesn't crash and works as expected.");
-[10000, 50000, 100000].forEach(function(fillSize) {
-    testData.forEach(function(dataItem) {
-        doTest(dataItem, fillSize)
-    })});
+[10000, 50000, 100000].forEach(function(fillSizeItem) {
+    fillSize = fillSizeItem;
+    generate_tests(testLargeRect, testScenarios);
+});
 
 </script>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern-expected.txt
deleted file mode 100644
index 29ff342..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern-expected.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-Verifies createPattern using a source image that is a canvas 40k pixels wide.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS imgdata[0] is 0
-FAIL imgdata[1] should be 255. Was 0.
-PASS imgdata[2] is 0
-FAIL imgdata[3] should be 255. Was 0.
-PASS imgdata[0] is 0
-PASS imgdata[1] is 0
-PASS imgdata[2] is 0
-PASS imgdata[3] is 0
-PASS imgdata[0] is 0
-PASS imgdata[1] is 0
-PASS imgdata[2] is 0
-PASS imgdata[3] is 0
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern.html
index 1ba4fa6a..c766406 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-large-pattern.html
@@ -1,47 +1,28 @@
-<!DOCTYPE HTML>
-<html>
-<body>
-    <script src="../../resources/js-test.js"></script>
-    <script type="text/javascript">
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
+<script>
 
-        description("Verifies createPattern using a source image that is a canvas 40k pixels wide.");
-        // This test does not currently succeed because skia does not handle
-        // canvases more than 32k pixels wide. For now, this test serves the
-        // purpose of verifying that this use case does not crash the browser.
-        // Crasher bug: crbug.com/281504
+test(function(t) {
+    var canvas = document.createElement('canvas');
+    canvas.width = 40000;
+    var context = canvas.getContext('2d');
+    context.fillStyle = '#0f0';
+    context.fillRect(0, 0, 1, 1);
 
-            var canvas = document.createElement('canvas');
-            canvas.width = 40000;
-            var context = canvas.getContext('2d');
-            context.fillStyle = '#0f0';
-            context.fillRect(0, 0, 1, 1);
+    var dstCanvas = document.createElement('canvas');
+    var dstContext = dstCanvas.getContext('2d');
+    var pattern = dstContext.createPattern(canvas, 'repeat');
+    dstContext.fillStyle = pattern;
+    dstContext.fillRect(0, 0, dstCanvas.width, dstCanvas.height);
 
-            var dstCanvas = document.createElement('canvas');
-            var dstContext = dstCanvas.getContext('2d');
-            var pattern = dstContext.createPattern(canvas, 'repeat');
-            dstContext.fillStyle = pattern;
-            dstContext.fillRect(0, 0, dstCanvas.width, dstCanvas.height);
-
-            var imageData = dstContext.getImageData(0, 0, 1, 1);
-            var imgdata = imageData.data;
-            shouldBe("imgdata[0]", "0");
-            shouldBe("imgdata[1]", "255");
-            shouldBe("imgdata[2]", "0");
-            shouldBe("imgdata[3]", "255");
-
-            imageData = dstContext.getImageData(1, 0, 1, 1);
-            imgdata = imageData.data;
-            shouldBe("imgdata[0]", "0");
-            shouldBe("imgdata[1]", "0");
-            shouldBe("imgdata[2]", "0");
-            shouldBe("imgdata[3]", "0");
-
-            imageData = dstContext.getImageData(0, 1, 1, 1);
-            imgdata = imageData.data;
-            shouldBe("imgdata[0]", "0");
-            shouldBe("imgdata[1]", "0");
-            shouldBe("imgdata[2]", "0");
-            shouldBe("imgdata[3]", "0");
-    </script>
-</body>
-</html>
+    // This test does not currently succeed because skia does not handle
+    // canvases more than 32k pixels wide. For now, this test serves the
+    // purpose of verifying that this use case does not crash the browser.
+    // Crasher bug: crbug.com/281504.
+    assert_array_equals(dstContext.getImageData(0, 0, 1, 1).data, [0, 255, 0, 255]);
+    
+    assert_array_equals(dstContext.getImageData(1, 0, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(dstContext.getImageData(0, 1, 1, 1).data, [0, 0, 0, 0]);
+    
+}, 'Tests createPattern using a source image that is a canvas 40k pixels wide.');
+</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws-expected.txt
deleted file mode 100644
index 9fc405a..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws-expected.txt
+++ /dev/null
@@ -1 +0,0 @@
-PASS: Draw commands with big numbers cause no problems.
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws.html
index a5f1bc8..f69ef6a 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-largedraws.html
@@ -1,47 +1,49 @@
-<!DOCTYPE html>
-<html>
-  <body>
-    <script>
-      if (window.testRunner)
-          testRunner.dumpAsText();
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 
-      function newCanvasContext() {
-        canvas = document.createElement("canvas");
-        canvas.width = 100;
-        canvas.height = 100;
-        return canvas.getContext("2d");
-      }
+<script>
+function newCanvasContext() {
+    canvas = document.createElement("canvas");
+    canvas.width = 100;
+    canvas.height = 100;
+    return canvas.getContext("2d");
+}
 
-      window.onload = function () {
-        ctx = newCanvasContext();
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.moveTo(10500000000, 10500000000);
-        ctx.lineTo(110, -10);
-        ctx.lineTo(-10, 60);
-        ctx.fill();
-        ctx.getImageData(50, 25, 1, 1);
+function runTest() {
+    ctx = newCanvasContext();
+    ctx.fillStyle = '#0f0';
+    ctx.fillRect(0, 0, 100, 50);
+    ctx.moveTo(10500000000, 10500000000);
+    ctx.lineTo(110, -10);
+    ctx.lineTo(-10, 60);
+    ctx.fill();
+    ctx.getImageData(50, 25, 1, 1);
 
-        ctx = newCanvasContext();
-        ctx.fillStyle = '#0f0';
-        ctx.scale(4500000000, 4500000000);
-        ctx.moveTo(0, 0.5);
-        ctx.lineTo(2, 0.5);
-        ctx.stroke();
-        ctx.getImageData(50, 25, 1, 1);
+    ctx = newCanvasContext();
+    ctx.fillStyle = '#0f0';
+    ctx.scale(4500000000, 4500000000);
+    ctx.moveTo(0, 0.5);
+    ctx.lineTo(2, 0.5);
+    ctx.stroke();
+    ctx.getImageData(50, 25, 1, 1);
   
-        ctx = newCanvasContext();
-        ctx.fillStyle = '#0f0';
-        ctx.fillRect(0, 0, 100, 50);
-        ctx.scale(2, -4500000000);
-        ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
-        ctx.fill();
-        ctx.arc(105000000005, -105000000005, 105000000004, 0, 10500000000*Math.PI, false);
-        ctx.fill();
-        ctx.getImageData(50, 25, 1, 1);
-      }
-    </script>
-    PASS: Draw commands with big numbers cause no problems.
-  </body>
-</html>
+    ctx = newCanvasContext();
+    ctx.fillStyle = '#0f0';
+    ctx.fillRect(0, 0, 100, 50);
+    ctx.scale(2, -4500000000);
+    ctx.arc(25, 50, 56, 0, 2*Math.PI, false);
+    ctx.fill();
+    ctx.arc(105000000005, -105000000005, 105000000004, 0, 10500000000*Math.PI, false);
+    ctx.fill();
+    ctx.getImageData(50, 25, 1, 1);
+}
+
+async_test(t => {
+        window.onload = function() {
+            t.step(runTest);
+            t.done();
+        }
+}, 'Draw commands with big numbers cause no problems.');
+
+</script>
 
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-expected.txt
deleted file mode 100644
index 358c0922..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-expected.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-Basic test for setLineDash, getLineDash and lineDashOffset
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS ctx.lineDashOffset is 0
-PASS lineDash[0] is 15
-PASS lineDash[1] is 10
-PASS ctx.lineDashOffset is 5
-PASS lineDash[0] is 0
-PASS lineDash[1] is 0
-PASS lineDash[0] is 5
-PASS lineDash[1] is 10
-PASS lineDash[2] is 15
-PASS lineDash[3] is 5
-PASS lineDash[4] is 10
-PASS lineDash[5] is 15
-PASS lineDash[0] is 1
-PASS lineDash[1] is 2
-PASS ctx.lineDashOffset is 5
-PASS getPixel(25,10) is [0,255,0,255]
-PASS getPixel(35,10) is [0,0,0,0]
-PASS getPixel(40,25) is [0,255,0,255]
-PASS getPixel(40,35) is [0,0,0,0]
-PASS getPixel(25,40) is [0,255,0,255]
-PASS getPixel(15,40) is [0,0,0,0]
-PASS getPixel(10,25) is [0,255,0,255]
-PASS getPixel(10,15) is [0,0,0,0]
-PASS getPixel(55,10) is [0,0,0,0]
-PASS getPixel(65,10) is [0,255,0,255]
-PASS getPixel(80,15) is [0,0,0,0]
-PASS getPixel(80,25) is [0,255,0,255]
-PASS getPixel(75,40) is [0,0,0,0]
-PASS getPixel(65,40) is [0,255,0,255]
-PASS getPixel(50,35) is [0,0,0,0]
-PASS getPixel(50,25) is [0,255,0,255]
-PASS getPixel(95,10) is [0,0,0,0]
-PASS getPixel(105,10) is [0,255,0,255]
-PASS getPixel(120,15) is [0,0,0,0]
-PASS getPixel(120,25) is [0,255,0,255]
-PASS getPixel(115,40) is [0,0,0,0]
-PASS getPixel(105,40) is [0,255,0,255]
-PASS getPixel(90,35) is [0,0,0,0]
-PASS getPixel(90,25) is [0,255,0,255]
-PASS getPixel(130,10) is [0,255,0,255]
-PASS getPixel(130,15) is [0,255,0,255]
-PASS getPixel(130,25) is [0,255,0,255]
-PASS getPixel(130,35) is [0,255,0,255]
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence-expected.txt
deleted file mode 100644
index f316f8c..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence-expected.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-Test that setLineDash converts input argument into a Web IDL sequence
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-* Test passing a Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Int8Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Int16Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Int32Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Uint8Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Uint16Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Uint32Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Float32Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Float64Array as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Uint8ClampedArray as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Object as input.
-PASS lineDash[0] is 5
-PASS lineDash[1] is 15
-PASS lineDash[2] is 25
-* Test passing a Date as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing a RegExp as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing an Object without length as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing a Number as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing a String as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing a Boolean as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing null as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-* Test passing undefined as input.
-PASS ctx.setLineDash(inputArray) threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': The 1st argument is neither an array, nor does it have indexed properties..
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence.html
index 061d9f4..1cbcb5f 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-input-sequence.html
@@ -1,77 +1,75 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
 <link rel="help" href="http://www.w3.org/TR/2013/WD-2dcontext2-20130528/#dom-context-2d-setlinedash">
-<script src="../../resources/js-test.js"></script>
-</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <body>
 <script>
-description("Test that setLineDash converts input argument into a Web IDL sequence");
-
-var canvas = document.createElement('canvas');
-document.body.appendChild(canvas);
-canvas.setAttribute('width', '700');
-canvas.setAttribute('height', '700');
-var ctx = canvas.getContext('2d');
-
-var arrayValues = [5, 15, 25];
-
-function createTestArray(arrayType) {
-  var array;
-  if (arrayType == Object) {
-    // Test a "sequence" (Object with length property).
-    array = {length: arrayValues.length};
-  } else {
-    array = new arrayType(arrayValues.length);
-  }
-
-  for (var i = 0; i < arrayValues.length; ++i)
-      array[i] = arrayValues[i]
-  return array;
-}
-
-var lineDash;
-var inputArray;
-function checkLineDash(testArray, shouldFail) {
-    inputArray = testArray;
-    // Reset line dash.
-    ctx.setLineDash([]);
-    // Set line dash.
-    if (shouldFail) {
-        shouldThrow("ctx.setLineDash(inputArray)", "'TypeError: Failed to execute \\'setLineDash\\' on \\'CanvasRenderingContext2D\\': The 1st argument is neither an array, nor does it have indexed properties.'");
-    } else {
-        ctx.setLineDash(inputArray);
-        lineDash = ctx.getLineDash();
-        for (var i = 0; i < arrayValues.length; ++i)
-            shouldBe("lineDash[" + i + "]", "" + arrayValues[i]);
+test(function(t) {
+    
+    var canvas = document.createElement('canvas');
+    document.body.appendChild(canvas);
+    canvas.setAttribute('width', '700');
+    canvas.setAttribute('height', '700');
+    var ctx = canvas.getContext('2d');
+    
+    var arrayValues = [5, 15, 25];
+    
+    function createTestArray(arrayType) {
+      var array;
+      if (arrayType == Object) {
+        // Test a "sequence" (Object with length property).
+        array = {length: arrayValues.length};
+      } else {
+        array = new arrayType(arrayValues.length);
+      }
+    
+      for (var i = 0; i < arrayValues.length; ++i)
+          array[i] = arrayValues[i]
+      return array;
     }
-}
-
-var arrayTypes = [Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, Uint8ClampedArray, Object];
-
-// Success cases.
-for (var i = 0; i < arrayTypes.length; ++i) {
-    debug("* Test passing a " + arrayTypes[i].name + " as input.");
-    checkLineDash(createTestArray(arrayTypes[i]), false);
-}
-
-// Failure cases.
-debug("* Test passing a Date as input.");
-checkLineDash(new Date(), true);
-debug("* Test passing a RegExp as input.");
-checkLineDash(new RegExp(), true);
-debug("* Test passing an Object without length as input.");
-checkLineDash({test: 1}, true);
-debug("* Test passing a Number as input.");
-checkLineDash(3, true);
-debug("* Test passing a String as input.");
-checkLineDash("Test", true);
-debug("* Test passing a Boolean as input.");
-checkLineDash(true, true);
-debug("* Test passing null as input.");
-checkLineDash(null, true);
-debug("* Test passing undefined as input.");
-checkLineDash(undefined, true);
+    
+    var lineDash;
+    var inputArray;
+    function checkLineDash(testArray, shouldFail) {
+        inputArray = testArray;
+        // Reset line dash.
+        ctx.setLineDash([]);
+        // Set line dash.
+        if (shouldFail) {
+            assert_throws(null, function() {ctx.setLineDash(inputArray);}, "'TypeError: Failed to execute \\'setLineDash\\' on \\'CanvasRenderingContext2D\\': The 1st argument is neither an array, nor does it have indexed properties.'");
+        } else {
+            ctx.setLineDash(inputArray);
+            lineDash = ctx.getLineDash();
+            for (var i = 0; i < arrayValues.length; ++i)
+                assert_equals(lineDash[i], arrayValues[i]);
+        }
+    }
+    
+    var arrayTypes = [Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint16Array, Uint32Array, Float32Array, Float64Array, Uint8ClampedArray, Object];
+    
+    // Success cases.
+    for (var i = 0; i < arrayTypes.length; ++i) {
+        // Test passing a valid array time as input.
+        checkLineDash(createTestArray(arrayTypes[i]), false);
+    }
+    
+    // Failure cases.
+    // Test passing a Date as input.
+    checkLineDash(new Date(), true);
+    // Test passing a RegExp as input.
+    checkLineDash(new RegExp(), true);
+    // Test passing an Object without length as input.
+    checkLineDash({test: 1}, true);
+    // Test passing a Number as input.
+    checkLineDash(3, true);
+    // Test passing a String as input.
+    checkLineDash("Test", true);
+    // Test passing a Boolean as input.
+    checkLineDash(true, true);
+    // Test passing null as input.
+    checkLineDash(null, true);
+    // Test passing undefined as input.
+    checkLineDash(undefined, true);
+    
+}, 'Test that setLineDash converts input argument into a Web IDL sequence');
 </script>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid-expected.txt
deleted file mode 100644
index 1c4ff67..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid-expected.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Test for invalid input of setLineDash, getLineDash and lineDashOffset
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-
-PASS trySettingLineDash([1, -1]) is initialLineDash
-PASS trySettingLineDash([1, Infinity]) is initialLineDash
-PASS trySettingLineDash([1, -Infinity]) is initialLineDash
-PASS trySettingLineDash([1, NaN]) is initialLineDash
-PASS trySettingLineDash([1, 'string']) is initialLineDash
-PASS trySettingLineDashWithNoArgs() threw exception TypeError: Failed to execute 'setLineDash' on 'CanvasRenderingContext2D': 1 argument required, but only 0 present..
-PASS trySettingLineDashOffset(Infinity) is initialLineDashOffset
-PASS trySettingLineDashOffset(-Infinity) is initialLineDashOffset
-PASS trySettingLineDashOffset(NaN) is initialLineDashOffset
-PASS trySettingLineDashOffset('string') is initialLineDashOffset
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid.html
index a9c0e4c..ae72a51 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash-invalid.html
@@ -1,9 +1,52 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <body>
-<script src="script-tests/canvas-lineDash-invalid.js"></script>
+<script>
+
+var canvas = document.createElement('canvas');
+document.body.appendChild(canvas);
+canvas.setAttribute('width', '700');
+canvas.setAttribute('height', '700');
+var ctx = canvas.getContext('2d');
+var initialLineDash = [1.5, 2.5];
+var initialLineDashOffset = 1.5;
+
+function resetLineDash() {
+    ctx.setLineDash(initialLineDash);
+    ctx.lineDashOffset = initialLineDashOffset;
+}
+
+function trySettingLineDash(value) {
+    resetLineDash();
+    ctx.setLineDash(value);
+    return ctx.getLineDash();
+}
+
+function trySettingLineDashWithNoArgs() {
+    resetLineDash();
+    ctx.setLineDash();
+    return ctx.getLineDash();
+}
+
+function trySettingLineDashOffset(value) {
+    resetLineDash();
+    ctx.lineDashOffset = value;
+    return ctx.lineDashOffset;
+}
+
+test(function(t) {
+    assert_array_equals(trySettingLineDash([1, -1]), initialLineDash);
+    assert_array_equals(trySettingLineDash([1, Infinity]), initialLineDash);
+    assert_array_equals(trySettingLineDash([1, -Infinity]), initialLineDash);
+    assert_array_equals(trySettingLineDash([1, NaN]), initialLineDash);
+    assert_array_equals(trySettingLineDash([1, 'string']), initialLineDash);
+    assert_throws(null, function() {trySettingLineDashWithNoArgs();}, '"TypeError: Failed to execute \'setLineDash\' on \'CanvasRenderingContext2D\': 1 argument required, but only 0 present."');
+    
+    assert_array_equals(trySettingLineDashOffset(Infinity), initialLineDashOffset);
+    assert_array_equals(trySettingLineDashOffset(-Infinity), initialLineDashOffset);
+    assert_array_equals(trySettingLineDashOffset(NaN), initialLineDashOffset);
+    assert_array_equals(trySettingLineDashOffset('string'), initialLineDashOffset);
+    
+}, "Test for invalid input of setLineDash, getLineDash and lineDashOffset");
+</script>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash.html
index a546137..fb10f98 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-lineDash.html
@@ -1,9 +1,110 @@
-<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-</head>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <body>
-<script src="script-tests/canvas-lineDash.js"></script>
+<script>
+test(function(t) {
+
+    var canvas = document.createElement('canvas');
+    document.body.appendChild(canvas);
+    canvas.setAttribute('width', '700');
+    canvas.setAttribute('height', '700');
+    var ctx = canvas.getContext('2d');
+
+    // Verify default values.
+    assert_equals(ctx.lineDashOffset, 0);
+
+    // Set dash-style.
+    ctx.setLineDash([15, 10]);
+    ctx.lineDashOffset = 5;
+    ctx.strokeRect (10,10,100,100);
+
+    // Verify dash and offset.
+    var lineDash;
+    lineDash = ctx.getLineDash();
+    assert_equals(lineDash[0], 15);
+    assert_equals(lineDash[1], 10);
+    assert_equals(ctx.lineDashOffset, 5);
+
+    // Verify setting line dash to sequence of nulls is interpreted as zeros
+    ctx.setLineDash([null, null]);
+    lineDash = ctx.getLineDash();
+    assert_equals(lineDash[0], 0);
+    assert_equals(lineDash[1], 0);
+    
+    // Set dash style to even number
+    ctx.setLineDash([5, 10, 15]);
+    ctx.strokeRect(20, 20, 120, 120);
+    
+    // Verify dash pattern is normalized
+    lineDash = ctx.getLineDash();
+    assert_equals(lineDash[0], 5);
+    assert_equals(lineDash[1], 10);
+    assert_equals(lineDash[2], 15);
+    assert_equals(lineDash[3], 5);
+    assert_equals(lineDash[4], 10);
+    assert_equals(lineDash[5], 15);
+    
+    // Verify that conversion from string works
+    ctx.setLineDash(["1", 2]);
+    lineDash = ctx.getLineDash();
+    assert_equals(lineDash[0], 1);
+    assert_equals(lineDash[1], 2);
+    
+    // Verify that line dash offset persists after
+    // clearRect (which causes a save/restore of the context
+    // state to the stack).
+    ctx.clearRect(0, 0, 700, 700);
+    assert_equals(ctx.lineDashOffset, 5);
+    
+    // Verify dash rendering
+    ctx.setLineDash([20, 10]);
+    ctx.lineDashOffset = 0;
+    ctx.lineWidth = 4; // To make the test immune to plaform anti-aliasing discrepancies
+    ctx.strokeStyle = '#00FF00';
+    ctx.strokeRect(10.5, 10.5, 30, 30);
+    
+    assert_array_equals(ctx.getImageData(25, 10, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(35, 10, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(40, 25, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(40, 35, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(25, 40, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(15, 40, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(10, 25, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(10, 15, 1, 1).data, [0, 0, 0, 0]);
+    
+    // Verify that lineDashOffset works as expected
+    ctx.lineDashOffset = 20;
+    ctx.strokeRect(50.5, 10.5, 30, 30);
+    assert_array_equals(ctx.getImageData(55, 10, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(65, 10, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(80, 15, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(80, 25, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(75, 40, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(65, 40, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(50, 35, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(50, 25, 1, 1).data, [0, 255, 0, 255]);
+    
+    // Verify negative lineDashOffset
+    ctx.lineDashOffset = -10;
+    ctx.strokeRect(90.5, 10.5, 30, 30);
+    assert_array_equals(ctx.getImageData(95, 10, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(105, 10, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(120, 15, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(120, 25, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(115, 40, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(105, 40, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(90, 35, 1, 1).data, [0, 0, 0, 0]);
+    assert_array_equals(ctx.getImageData(90, 25, 1, 1).data, [0, 255, 0, 255]);
+    
+    // Verify that all zero dash sequence results in no dashing
+    ctx.setLineDash([0, 0]);
+    ctx.lineDashOffset = 0;
+    ctx.strokeRect(130.5, 10.5, 30, 30);
+    assert_array_equals(ctx.getImageData(130, 10, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(130, 15, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(130, 25, 1, 1).data, [0, 255, 0, 255]);
+    assert_array_equals(ctx.getImageData(130, 35, 1, 1).data, [0, 255, 0, 255]);
+    
+}, "Basic test for setLineDash, getLineDash and lineDashOffset");
+</script>
 </body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInPath-winding.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInPath-winding.js
deleted file mode 100644
index 3d788aa..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInPath-winding.js
+++ /dev/null
@@ -1,103 +0,0 @@
-description("Series of tests to ensure correct results of the winding rule in isPointInPath.");
-
-var tmpimg = document.createElement('canvas');
-tmpimg.width = 200;
-tmpimg.height = 200;
-ctx = tmpimg.getContext('2d');
-
-// Execute test.
-function prepareTestScenario() {
-    debug('Testing default isPointInPath');
-    ctx.beginPath();
-    ctx.rect(0, 0, 100, 100);
-    ctx.rect(25, 25, 50, 50);
-    shouldBeTrue("ctx.isPointInPath(50, 50)");
-    shouldBeFalse("ctx.isPointInPath(NaN, 50)");
-    shouldBeFalse("ctx.isPointInPath(50, NaN)");
-    debug('');
-
-    debug('Testing nonzero isPointInPath');
-    ctx.beginPath();
-    ctx.rect(0, 0, 100, 100);
-    ctx.rect(25, 25, 50, 50);
-    shouldBeTrue("ctx.isPointInPath(50, 50, 'nonzero')");
-    debug('');
-	
-    debug('Testing evenodd isPointInPath');
-    ctx.beginPath();
-    ctx.rect(0, 0, 100, 100);
-    ctx.rect(25, 25, 50, 50);
-    shouldBeFalse("ctx.isPointInPath(50, 50, 'evenodd')");
-    debug('');
-
-    // reset path in context
-    ctx.beginPath();
-
-    debug('Testing default isPointInPath with Path object');
-    path = new Path2D();
-    path.rect(0, 0, 100, 100);
-    path.rect(25, 25, 50, 50);
-    shouldBeTrue("ctx.isPointInPath(path, 50, 50)");
-    shouldBeTrue("ctx.isPointInPath(path, 50, 50, undefined)");
-    shouldBeFalse("ctx.isPointInPath(path, NaN, 50)");
-    shouldBeFalse("ctx.isPointInPath(path, 50, NaN)");
-    debug('');
-
-    debug('Testing nonzero isPointInPath with Path object');
-    path = new Path2D();
-    path.rect(0, 0, 100, 100);
-    path.rect(25, 25, 50, 50);
-    shouldBeTrue("ctx.isPointInPath(path, 50, 50, 'nonzero')");
-    debug('');
-
-    debug('Testing evenodd isPointInPath with Path object');
-    path = new Path2D();
-    path.rect(0, 0, 100, 100);
-    path.rect(25, 25, 50, 50);
-    shouldBeFalse("ctx.isPointInPath(path, 50, 50, 'evenodd')");
-    debug('');
-
-    debug('Testing invalid enumeration isPointInPath (w/ and w/o Path object');
-    shouldThrow("ctx.isPointInPath(path, 50, 50, 'gazonk')");
-    shouldThrow("ctx.isPointInPath(50, 50, 'gazonk')");
-    debug('');
-
-    debug('Testing invalid type isPointInPath with Path object');
-    shouldThrow("ctx.isPointInPath(null, 50, 50)");
-    shouldThrow("ctx.isPointInPath(null, 50, 50, 'nonzero')");
-    shouldThrow("ctx.isPointInPath(null, 50, 50, 'evenodd')");
-    shouldThrow("ctx.isPointInPath(null, 50, 50, null)");
-    shouldThrow("ctx.isPointInPath(path, 50, 50, null)");
-    shouldThrow("ctx.isPointInPath(undefined, 50, 50)");
-    shouldThrow("ctx.isPointInPath(undefined, 50, 50, 'nonzero')");
-    shouldThrow("ctx.isPointInPath(undefined, 50, 50, 'evenodd')");
-    shouldThrow("ctx.isPointInPath(undefined, 50, 50, undefined)");
-    shouldThrow("ctx.isPointInPath([], 50, 50)");
-    shouldThrow("ctx.isPointInPath([], 50, 50, 'nonzero')");
-    shouldThrow("ctx.isPointInPath([], 50, 50, 'evenodd')");
-    shouldThrow("ctx.isPointInPath({}, 50, 50)");
-    shouldThrow("ctx.isPointInPath({}, 50, 50, 'nonzero')");
-    shouldThrow("ctx.isPointInPath({}, 50, 50, 'evenodd')");
-    debug('');
-
-    debug("Testing extremely large scale")
-    ctx.save();
-    ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
-    ctx.beginPath();
-    ctx.rect(-10, -10, 20, 20);
-    shouldBeTrue("ctx.isPointInPath(0, 0, 'nonzero')");
-    shouldBeTrue("ctx.isPointInPath(0, 0, 'evenodd')");
-    ctx.restore();
-
-    debug("Check with non-invertible ctm.")
-    ctx.save();
-    ctx.scale(0, 0);
-    ctx.beginPath();
-    ctx.rect(-10, -10, 20, 20);
-    shouldBeFalse("ctx.isPointInPath(0, 0, 'nonzero')");
-    shouldBeFalse("ctx.isPointInPath(0, 0, 'evenodd')");
-    ctx.restore();
-}
-
-// Run test and allow variation of results.
-prepareTestScenario();
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke-with-path.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke-with-path.js
deleted file mode 100644
index 383b00d0..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke-with-path.js
+++ /dev/null
@@ -1,108 +0,0 @@
-description("Test the behavior of isPointInStroke in Canvas with path object");
-var ctx = document.createElement('canvas').getContext('2d');
-
-document.body.appendChild(ctx.canvas);
-
-ctx.strokeStyle = '#0ff';
-
-// Create new path.
-var path = new Path2D();
-path.rect(20,20,100,100);
-
-debug("Initial behavior: lineWidth = 1.0")
-shouldBeTrue("ctx.isPointInStroke(path,20,20)");
-shouldBeTrue("ctx.isPointInStroke(path,120,20)");
-shouldBeTrue("ctx.isPointInStroke(path,20,120)");
-shouldBeTrue("ctx.isPointInStroke(path,120,120)");
-shouldBeTrue("ctx.isPointInStroke(path,70,20)");
-shouldBeTrue("ctx.isPointInStroke(path,20,70)");
-shouldBeTrue("ctx.isPointInStroke(path,120,70)");
-shouldBeTrue("ctx.isPointInStroke(path,70,120)");
-shouldBeFalse("ctx.isPointInStroke(path,22,22)");
-shouldBeFalse("ctx.isPointInStroke(path,118,22)");
-shouldBeFalse("ctx.isPointInStroke(path,22,118)");
-shouldBeFalse("ctx.isPointInStroke(path,118,118)");
-shouldBeFalse("ctx.isPointInStroke(path,70,18)");
-shouldBeFalse("ctx.isPointInStroke(path,122,70)");
-shouldBeFalse("ctx.isPointInStroke(path,70,122)");
-shouldBeFalse("ctx.isPointInStroke(path,18,70)");
-shouldBeFalse("ctx.isPointInStroke(path,NaN,122)");
-shouldBeFalse("ctx.isPointInStroke(path,18,NaN)");
-debug("");
-
-debug("Check invalid type");
-shouldThrow("ctx.isPointInStroke(null,70,20)");
-shouldThrow("ctx.isPointInStroke(undefined,70,20)");
-shouldThrow("ctx.isPointInStroke([],20,70)");
-shouldThrow("ctx.isPointInStroke({},120,70)");
-debug("");
-
-debug("Set lineWidth = 10.0");
-ctx.lineWidth = 10;
-shouldBeTrue("ctx.isPointInStroke(path,22,22)");
-shouldBeTrue("ctx.isPointInStroke(path,118,22)");
-shouldBeTrue("ctx.isPointInStroke(path,22,118)");
-shouldBeTrue("ctx.isPointInStroke(path,118,118)");
-shouldBeTrue("ctx.isPointInStroke(path,70,18)");
-shouldBeTrue("ctx.isPointInStroke(path,122,70)");
-shouldBeTrue("ctx.isPointInStroke(path,70,122)");
-shouldBeTrue("ctx.isPointInStroke(path,18,70)");
-shouldBeFalse("ctx.isPointInStroke(path,26,70)");
-shouldBeFalse("ctx.isPointInStroke(path,70,26)");
-shouldBeFalse("ctx.isPointInStroke(path,70,114)");
-shouldBeFalse("ctx.isPointInStroke(path,114,70)");
-debug("");
-
-debug("Check lineJoin = 'bevel'");
-path = new Path2D();
-path.moveTo(10,10);
-path.lineTo(110,20);
-path.lineTo(10,30);
-ctx.lineJoin = "bevel";
-shouldBeFalse("ctx.isPointInStroke(path,113,20)");
-debug("");
-
-debug("Check lineJoin = 'miter'");
-ctx.miterLimit = 40.0;
-ctx.lineJoin = "miter";
-shouldBeTrue("ctx.isPointInStroke(path,113,20)");
-debug("");
-
-debug("Check miterLimit = 2.0");
-ctx.miterLimit = 2.0;
-shouldBeFalse("ctx.isPointInStroke(path,113,20)");
-debug("");
-
-debug("Check lineCap = 'butt'");
-path = new Path2D();
-path.moveTo(10,10);
-path.lineTo(110,10);
-ctx.lineCap = "butt";
-shouldBeFalse("ctx.isPointInStroke(path,112,10)");
-debug("");
-
-debug("Check lineCap = 'round'");
-ctx.lineCap = "round";
-shouldBeTrue("ctx.isPointInStroke(path,112,10)");
-shouldBeFalse("ctx.isPointInStroke(path,117,10)");
-debug("");
-
-debug("Check lineCap = 'square'");
-ctx.lineCap = "square";
-shouldBeTrue("ctx.isPointInStroke(path,112,10)");
-shouldBeFalse("ctx.isPointInStroke(path,117,10)");
-debug("");
-
-debug("Check setLineDash([10,10])");
-ctx.lineCap = "butt";
-ctx.setLineDash([10,10]);
-shouldBeTrue("ctx.isPointInStroke(path,15,10)");
-shouldBeFalse("ctx.isPointInStroke(path,25,10)");
-shouldBeTrue("ctx.isPointInStroke(path,35,10)");
-debug("");
-
-debug("Check dashOffset = 10");
-ctx.lineDashOffset = 10;
-shouldBeFalse("ctx.isPointInStroke(path,15,10)");
-shouldBeTrue("ctx.isPointInStroke(path,25,10)");
-shouldBeFalse("ctx.isPointInStroke(path,35,10)");
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke.js
deleted file mode 100644
index 2a75b3ad..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-isPointInStroke.js
+++ /dev/null
@@ -1,117 +0,0 @@
-description("Test the behavior of isPointInStroke in Canvas");
-var ctx = document.createElement('canvas').getContext('2d');
-
-document.body.appendChild(ctx.canvas);
-
-ctx.strokeStyle = '#0ff';
-
-// Create new path.
-ctx.beginPath();
-ctx.rect(20,20,100,100);
-
-debug("Initial behavior: lineWith = 1.0")
-shouldBeTrue("ctx.isPointInStroke(20,20)");
-shouldBeTrue("ctx.isPointInStroke(120,20)");
-shouldBeTrue("ctx.isPointInStroke(20,120)");
-shouldBeTrue("ctx.isPointInStroke(120,120)");
-shouldBeTrue("ctx.isPointInStroke(70,20)");
-shouldBeTrue("ctx.isPointInStroke(20,70)");
-shouldBeTrue("ctx.isPointInStroke(120,70)");
-shouldBeTrue("ctx.isPointInStroke(70,120)");
-shouldBeFalse("ctx.isPointInStroke(22,22)");
-shouldBeFalse("ctx.isPointInStroke(118,22)");
-shouldBeFalse("ctx.isPointInStroke(22,118)");
-shouldBeFalse("ctx.isPointInStroke(118,118)");
-shouldBeFalse("ctx.isPointInStroke(70,18)");
-shouldBeFalse("ctx.isPointInStroke(122,70)");
-shouldBeFalse("ctx.isPointInStroke(70,122)");
-shouldBeFalse("ctx.isPointInStroke(18,70)");
-debug("");
-
-debug("Set lineWith = 10.0");
-ctx.lineWidth = 10;
-shouldBeTrue("ctx.isPointInStroke(22,22)");
-shouldBeTrue("ctx.isPointInStroke(118,22)");
-shouldBeTrue("ctx.isPointInStroke(22,118)");
-shouldBeTrue("ctx.isPointInStroke(118,118)");
-shouldBeTrue("ctx.isPointInStroke(70,18)");
-shouldBeTrue("ctx.isPointInStroke(122,70)");
-shouldBeTrue("ctx.isPointInStroke(70,122)");
-shouldBeTrue("ctx.isPointInStroke(18,70)");
-shouldBeFalse("ctx.isPointInStroke(26,70)");
-shouldBeFalse("ctx.isPointInStroke(70,26)");
-shouldBeFalse("ctx.isPointInStroke(70,114)");
-shouldBeFalse("ctx.isPointInStroke(114,70)");
-debug("");
-
-debug("Check lineJoin = 'bevel'");
-ctx.beginPath();
-ctx.moveTo(10,10);
-ctx.lineTo(110,20);
-ctx.lineTo(10,30);
-ctx.lineJoin = "bevel";
-shouldBeFalse("ctx.isPointInStroke(113,20)");
-debug("");
-
-debug("Check lineJoin = 'miter'");
-ctx.miterLimit = 40.0;
-ctx.lineJoin = "miter";
-shouldBeTrue("ctx.isPointInStroke(113,20)");
-debug("");
-
-debug("Check miterLimit = 2.0");
-ctx.miterLimit = 2.0;
-shouldBeFalse("ctx.isPointInStroke(113,20)");
-debug("");
-
-debug("Check lineCap = 'butt'");
-ctx.beginPath();
-ctx.moveTo(10,10);
-ctx.lineTo(110,10);
-ctx.lineCap = "butt";
-shouldBeFalse("ctx.isPointInStroke(112,10)");
-debug("");
-
-debug("Check lineCap = 'round'");
-ctx.lineCap = "round";
-shouldBeTrue("ctx.isPointInStroke(112,10)");
-shouldBeFalse("ctx.isPointInStroke(117,10)");
-debug("");
-
-debug("Check lineCap = 'square'");
-ctx.lineCap = "square";
-shouldBeTrue("ctx.isPointInStroke(112,10)");
-shouldBeFalse("ctx.isPointInStroke(117,10)");
-debug("");
-
-debug("Check setLineDash([10,10])");
-ctx.lineCap = "butt";
-ctx.setLineDash([10,10]);
-shouldBeTrue("ctx.isPointInStroke(15,10)");
-shouldBeFalse("ctx.isPointInStroke(25,10)");
-shouldBeTrue("ctx.isPointInStroke(35,10)");
-debug("");
-
-debug("Check dashOffset = 10");
-ctx.lineDashOffset = 10;
-shouldBeFalse("ctx.isPointInStroke(15,10)");
-shouldBeTrue("ctx.isPointInStroke(25,10)");
-shouldBeFalse("ctx.isPointInStroke(35,10)");
-
-debug("Check extremely large scale")
-ctx.save();
-ctx.scale(Number.MAX_VALUE, Number.MAX_VALUE);
-ctx.beginPath();
-ctx.moveTo(-10, -10);
-ctx.lineTo(10, 10);
-shouldBeTrue("ctx.isPointInStroke(0, 0)");
-ctx.restore();
-
-debug("Check with non-invertible ctm.")
-ctx.save();
-ctx.scale(0, 0);
-ctx.beginPath();
-ctx.moveTo(-10, -10);
-ctx.lineTo(10, 10);
-shouldBeFalse("ctx.isPointInStroke(0, 0)");
-ctx.restore();
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash-invalid.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash-invalid.js
deleted file mode 100644
index 35f73df..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash-invalid.js
+++ /dev/null
@@ -1,44 +0,0 @@
-description("Test for invalid input of setLineDash, getLineDash and lineDashOffset");
-
-var canvas = document.createElement('canvas');
-document.body.appendChild(canvas);
-canvas.setAttribute('width', '700');
-canvas.setAttribute('height', '700');
-var ctx = canvas.getContext('2d');
-var initialLineDash = [1.5, 2.5];
-var initialLineDashOffset = 1.5;
-
-function resetLineDash() {
-    ctx.setLineDash(initialLineDash);
-    ctx.lineDashOffset = initialLineDashOffset;
-}
-
-function trySettingLineDash(value) {
-    resetLineDash();
-    ctx.setLineDash(value);
-    return ctx.getLineDash();
-}
-
-function trySettingLineDashWithNoArgs() {
-    resetLineDash();
-    ctx.setLineDash();
-    return ctx.getLineDash();
-}
-
-function trySettingLineDashOffset(value) {
-    resetLineDash();
-    ctx.lineDashOffset = value;
-    return ctx.lineDashOffset;
-}
-
-shouldBe("trySettingLineDash([1, -1])", "initialLineDash");
-shouldBe("trySettingLineDash([1, Infinity])", "initialLineDash");
-shouldBe("trySettingLineDash([1, -Infinity])", "initialLineDash");
-shouldBe("trySettingLineDash([1, NaN])", "initialLineDash");
-shouldBe("trySettingLineDash([1, 'string'])", "initialLineDash");
-shouldThrow("trySettingLineDashWithNoArgs()", '"TypeError: Failed to execute \'setLineDash\' on \'CanvasRenderingContext2D\': 1 argument required, but only 0 present."');
-
-shouldBe("trySettingLineDashOffset(Infinity)", "initialLineDashOffset");
-shouldBe("trySettingLineDashOffset(-Infinity)", "initialLineDashOffset");
-shouldBe("trySettingLineDashOffset(NaN)", "initialLineDashOffset");
-shouldBe("trySettingLineDashOffset('string')", "initialLineDashOffset");
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash.js b/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash.js
deleted file mode 100644
index 8c3c8002..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/script-tests/canvas-lineDash.js
+++ /dev/null
@@ -1,120 +0,0 @@
-description("Basic test for setLineDash, getLineDash and lineDashOffset");
-
-var canvas = document.createElement('canvas');
-document.body.appendChild(canvas);
-canvas.setAttribute('width', '700');
-canvas.setAttribute('height', '700');
-var ctx = canvas.getContext('2d');
-
-function dataToArray(data) {
-    var result = new Array(data.length)
-    for (var i = 0; i < data.length; i++)
-        result[i] = data[i];
-    return result;
-}
-
-function getPixel(x, y) {
-    var data = ctx.getImageData(x,y,1,1);
-    if (!data) // getImageData failed, which should never happen
-        return [-1,-1,-1,-1];
-    return dataToArray(data.data);
-}
-
-function pixelShouldBe(x, y, colour) {
-    shouldBe("getPixel(" + [x, y] +")", "["+colour+"]");
-}
-
-// Verify default values.
-shouldBe('ctx.lineDashOffset', '0');
-
-// Set dash-style.
-ctx.setLineDash([15, 10]);
-ctx.lineDashOffset = 5;
-ctx.strokeRect (10,10,100,100);
-
-// Verify dash and offset.
-var lineDash;
-lineDash = ctx.getLineDash();
-shouldBe('lineDash[0]', '15');
-shouldBe('lineDash[1]', '10');
-shouldBe('ctx.lineDashOffset', '5');
-
-// Verify setting line dash to sequence of nulls is interpreted as zeros
-ctx.setLineDash([null, null]);
-lineDash = ctx.getLineDash();
-shouldBe('lineDash[0]', '0');
-shouldBe('lineDash[1]', '0');
-
-// Set dash style to even number
-ctx.setLineDash([5, 10, 15]);
-ctx.strokeRect(20, 20, 120, 120);
-
-// Verify dash pattern is normalized
-lineDash = ctx.getLineDash();
-shouldBe('lineDash[0]', '5');
-shouldBe('lineDash[1]', '10');
-shouldBe('lineDash[2]', '15');
-shouldBe('lineDash[3]', '5');
-shouldBe('lineDash[4]', '10');
-shouldBe('lineDash[5]', '15');
-
-// Verify that conversion from string works
-ctx.setLineDash(["1", 2]);
-lineDash = ctx.getLineDash();
-shouldBe('lineDash[0]', '1');
-shouldBe('lineDash[1]', '2');
-
-// Verify that line dash offset persists after
-// clearRect (which causes a save/restore of the context
-// state to the stack).
-ctx.clearRect(0, 0, 700, 700);
-shouldBe('ctx.lineDashOffset', '5');
-
-// Verify dash rendering
-ctx.setLineDash([20, 10]);
-ctx.lineDashOffset = 0;
-ctx.lineWidth = 4; // To make the test immune to plaform anti-aliasing discrepancies
-ctx.strokeStyle = '#00FF00';
-ctx.strokeRect(10.5, 10.5, 30, 30);
-
-pixelShouldBe(25, 10, [0, 255, 0, 255]);
-pixelShouldBe(35, 10, [0, 0, 0, 0]);
-pixelShouldBe(40, 25, [0, 255, 0, 255]);
-pixelShouldBe(40, 35, [0, 0, 0, 0]);
-pixelShouldBe(25, 40, [0, 255, 0, 255]);
-pixelShouldBe(15, 40, [0, 0, 0, 0]);
-pixelShouldBe(10, 25, [0, 255, 0, 255]);
-pixelShouldBe(10, 15, [0, 0, 0, 0]);
-
-// Verify that lineDashOffset works as expected
-ctx.lineDashOffset = 20;
-ctx.strokeRect(50.5, 10.5, 30, 30);
-pixelShouldBe(55, 10, [0, 0, 0, 0]);
-pixelShouldBe(65, 10, [0, 255, 0, 255]);
-pixelShouldBe(80, 15, [0, 0, 0, 0]);
-pixelShouldBe(80, 25, [0, 255, 0, 255]);
-pixelShouldBe(75, 40, [0, 0, 0, 0]);
-pixelShouldBe(65, 40, [0, 255, 0, 255]);
-pixelShouldBe(50, 35, [0, 0, 0, 0]);
-pixelShouldBe(50, 25, [0, 255, 0, 255]);
-
-// Verify negative lineDashOffset
-ctx.lineDashOffset = -10;
-ctx.strokeRect(90.5, 10.5, 30, 30);
-pixelShouldBe(95, 10, [0, 0, 0, 0]);
-pixelShouldBe(105, 10, [0, 255, 0, 255]);
-pixelShouldBe(120, 15, [0, 0, 0, 0]);
-pixelShouldBe(120, 25, [0, 255, 0, 255]);
-pixelShouldBe(115, 40, [0, 0, 0, 0]);
-pixelShouldBe(105, 40, [0, 255, 0, 255]);
-pixelShouldBe(90, 35, [0, 0, 0, 0]);
-pixelShouldBe(90, 25, [0, 255, 0, 255]);
-
-// Verify that all zero dash sequence results in no dashing
-ctx.setLineDash([0, 0]);
-ctx.lineDashOffset = 0;
-ctx.strokeRect(130.5, 10.5, 30, 30);
-pixelShouldBe(130, 10, [0, 255, 0, 255]);
-pixelShouldBe(130, 15, [0, 255, 0, 255]);
-pixelShouldBe(130, 25, [0, 255, 0, 255]);
-pixelShouldBe(130, 35, [0, 255, 0, 255]);
diff --git a/third_party/WebKit/LayoutTests/fast/events/pointerevents/multi-pointer-event-in-slop-region.html b/third_party/WebKit/LayoutTests/fast/events/pointerevents/multi-pointer-event-in-slop-region.html
index bf2d937..9e4d1b3 100644
--- a/third_party/WebKit/LayoutTests/fast/events/pointerevents/multi-pointer-event-in-slop-region.html
+++ b/third_party/WebKit/LayoutTests/fast/events/pointerevents/multi-pointer-event-in-slop-region.html
@@ -49,18 +49,18 @@
             [{source: "touch",
               actions: [
                 { name: "pointerDown", x: x, y: y },
-                { name: "pointerMove", x: x, y: y + 10 },
+                { name: "pointerMove", x: x, y: y + 1 },
                 { name: "pause" },
                 { name: "pause" },
                 { name: "pause" },
-                { name: "pointerMove", x: x, y: y + 6 },
+                { name: "pointerMove", x: x, y: y + 2 },
                 { name: "pointerUp" }]},
              {source: "touch",
               actions: [
                 { name: "pause" },
                 { name: "pause" },
                 { name: "pointerDown", x: x, y: y },
-                { name: "pointerMove", x: x, y: y + 10 },
+                { name: "pointerMove", x: x, y: y + 1 },
                 { name: "pointerUp"}]}];
         chrome.gpuBenchmarking.pointerActionSequence(pointerActions, callbackValidMoveCount);
     }
diff --git a/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html b/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html
new file mode 100644
index 0000000..e4f8298d
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-consumed-touchstart-in-slop-region.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<script src='../../../resources/testharness.js'></script>
+<script src='../../../resources/testharnessreport.js'></script>
+<style type="text/css">
+#box {
+    width: 600px;
+    height: 600px;
+    touch-action: none;
+}
+</style>
+<div id="box" ></div>
+
+<script type="text/javascript">
+
+var touchMoveCount = 0;
+var pointerMoveCount = 0;
+var box = document.getElementById("box");
+var targetRect = box.getBoundingClientRect();
+var offset = 50;
+var x = targetRect.left + offset;
+var y = targetRect.top + offset;
+
+function touchstartHandler(event) {
+    event.preventDefault();
+}
+
+function validTouchMoveResult(event) {
+    touchMoveCount++;
+    testTouchMove.step(function () {
+        assert_equals(event.target.id, "box");
+    });
+}
+
+function validPointerMoveResult(event) {
+    pointerMoveCount++;
+    testTouchMove.step(function () {
+        assert_equals(event.target.id, "box");
+        assert_equals(event.pointerType, "touch");
+    });
+}
+
+function callbackValidMoveCount() {
+    testTouchMove.step(function () {
+        assert_equals(touchMoveCount, 3);
+        assert_equals(pointerMoveCount, 3);
+    });
+    testTouchMove.done();
+}
+
+function testTouchMoveSuppressionInSlopRegion() {
+    if (window.chrome && chrome.gpuBenchmarking) {
+        var pointerActions =
+            [{source: "touch",
+              actions: [
+                { name: "pointerDown", x: x, y: y },
+                { name: "pointerMove", x: x, y: y + 1 },
+                { name: "pointerMove", x: x, y: y + 10 },
+                { name: "pointerMove", x: x, y: y + 20 }]}];
+        chrome.gpuBenchmarking.pointerActionSequence(pointerActions, callbackValidMoveCount);
+    }
+}
+
+var testTouchMove = async_test('Tests that the TouchMoves are not suppressed if the touch start is consumed.');
+box.addEventListener('touchstart', touchstartHandler);
+box.addEventListener('touchmove', validTouchMoveResult);
+box.addEventListener('pointermove', validPointerMoveResult);
+testTouchMoveSuppressionInSlopRegion();
+
+</script>
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-in-slop-region.html b/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-in-slop-region.html
index 6306b2c..e7432e25 100644
--- a/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-in-slop-region.html
+++ b/third_party/WebKit/LayoutTests/fast/events/pointerevents/pointer-event-in-slop-region.html
@@ -38,7 +38,7 @@
 function callbackValidMoveCount() {
     testTouchMove.step(function () {
         assert_equals(touchMoveCount, 3);
-        assert_equals(pointerMoveCount, 5);
+        assert_equals(pointerMoveCount, 7);
     });
     testTouchMove.done();
 }
@@ -49,11 +49,13 @@
             [{source: "touch",
               actions: [
                 { name: "pointerDown", x: x, y: y },
+                { name: "pointerMove", x: x, y: y + 1 },
                 { name: "pointerMove", x: x, y: y + 10 },
                 { name: "pointerMove", x: x, y: y + 20 },
                 { name: "pointerMove", x: x, y: y + 10 },
                 { name: "pointerUp" },
                 { name: "pointerDown", x: x, y: y },
+                { name: "pointerMove", x: x, y: y + 1 },
                 { name: "pointerMove", x: x, y: y + 10 },
                 { name: "pointerMove", x: x, y: y + 20 }]}];
         chrome.gpuBenchmarking.pointerActionSequence(pointerActions, callbackValidMoveCount);
diff --git a/third_party/WebKit/Source/core/editing/Editor.cpp b/third_party/WebKit/Source/core/editing/Editor.cpp
index 3567c9a6..5a50fac 100644
--- a/third_party/WebKit/Source/core/editing/Editor.cpp
+++ b/third_party/WebKit/Source/core/editing/Editor.cpp
@@ -316,7 +316,8 @@
   if (imageElementFromImageDocument(frame().document()))
     return true;
   FrameSelection& selection = frame().selection();
-  return selection.isRange() && !selection.isInPasswordField();
+  return selection.computeVisibleSelectionInDOMTreeDeprecated().isRange() &&
+         !selection.isInPasswordField();
 }
 
 bool Editor::canPaste() const {
@@ -325,7 +326,8 @@
 
 bool Editor::canDelete() const {
   FrameSelection& selection = frame().selection();
-  return selection.isRange() && selection.rootEditableElement();
+  return selection.computeVisibleSelectionInDOMTreeDeprecated().isRange() &&
+         selection.rootEditableElement();
 }
 
 bool Editor::smartInsertDeleteEnabled() const {
@@ -353,7 +355,10 @@
     return false;
 
   EditingState editingState;
-  if (frame().selection().isRange()) {
+  if (frame()
+          .selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .isRange()) {
     if (isTypingAction) {
       DCHECK(frame().document());
       TypingCommand::deleteKeyPressed(
diff --git a/third_party/WebKit/Source/core/editing/FrameSelection.h b/third_party/WebKit/Source/core/editing/FrameSelection.h
index 87be069..e13838a 100644
--- a/third_party/WebKit/Source/core/editing/FrameSelection.h
+++ b/third_party/WebKit/Source/core/editing/FrameSelection.h
@@ -211,9 +211,6 @@
   bool isNone() const {
     return computeVisibleSelectionInDOMTreeDeprecated().isNone();
   }
-  bool isRange() const {
-    return computeVisibleSelectionInDOMTreeDeprecated().isRange();
-  }
   bool isInPasswordField() const;
   bool isDirectional() const { return selectionInDOMTree().isDirectional(); }
 
diff --git a/third_party/WebKit/Source/core/editing/SelectionController.cpp b/third_party/WebKit/Source/core/editing/SelectionController.cpp
index be8d850..88d9742 100644
--- a/third_party/WebKit/Source/core/editing/SelectionController.cpp
+++ b/third_party/WebKit/Source/core/editing/SelectionController.cpp
@@ -725,7 +725,7 @@
   if (event.event().button != WebPointerProperties::Button::Left)
     return false;
 
-  if (selection().isRange()) {
+  if (selection().computeVisibleSelectionInDOMTreeDeprecated().isRange()) {
     // A double-click when range is already selected
     // should not change the selection.  So, do not call
     // selectClosestWordFromMouseEvent, but do set
@@ -853,7 +853,7 @@
   if (m_mouseDownWasSingleClickInSelection &&
       m_selectionState != SelectionState::ExtendedSelection &&
       dragStartPos == flooredIntPoint(event.event().positionInRootFrame()) &&
-      selection().isRange() &&
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange() &&
       event.event().button != WebPointerProperties::Button::Right) {
     // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
     // needs to be audited.  See http://crbug.com/590369 for more details.
diff --git a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
index e20f2f310..b86f321e 100644
--- a/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/EditorCommand.cpp
@@ -420,7 +420,7 @@
             selection.computeVisibleSelectionInDOMTreeDeprecated().start(),
             tagName))
       return TrueTriState;
-  } else if (selection.isRange()) {
+  } else if (selection.computeVisibleSelectionInDOMTreeDeprecated().isRange()) {
     Element* startElement = enclosingElementWithTag(
         selection.computeVisibleSelectionInDOMTreeDeprecated().start(),
         tagName);
@@ -1939,7 +1939,10 @@
   // We should update selection to canonicalize with current layout and style,
   // before accessing |FrameSelection::selection()|.
   frame.selection().updateIfNeeded();
-  return frame.selection().isRange() && frame.selection().isContentEditable();
+  return frame.selection()
+             .computeVisibleSelectionInDOMTreeDeprecated()
+             .isRange() &&
+         frame.selection().isContentEditable();
 }
 
 static bool enabledRangeInRichlyEditableText(LocalFrame& frame,
@@ -1950,7 +1953,9 @@
   // We should update selection to canonicalize with current layout and style,
   // before accessing |FrameSelection::selection()|.
   frame.selection().updateIfNeeded();
-  return frame.selection().isRange() &&
+  return frame.selection()
+             .computeVisibleSelectionInDOMTreeDeprecated()
+             .isRange() &&
          frame.selection()
              .computeVisibleSelectionInDOMTreeDeprecated()
              .isContentRichlyEditable();
diff --git a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
index ab9a3abc..a1a928da 100644
--- a/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
+++ b/third_party/WebKit/Source/core/editing/commands/TypingCommand.cpp
@@ -153,7 +153,9 @@
   LocalFrame* frame = document.frame();
   DCHECK(frame);
 
-  if (!frame->selection().isRange())
+  if (!frame->selection()
+           .computeVisibleSelectionInDOMTreeDeprecated()
+           .isRange())
     return;
 
   if (TypingCommand* lastTypingCommand =
diff --git a/third_party/WebKit/Source/core/frame/LocalFrame.cpp b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
index b698e348..6d585396 100644
--- a/third_party/WebKit/Source/core/frame/LocalFrame.cpp
+++ b/third_party/WebKit/Source/core/frame/LocalFrame.cpp
@@ -725,7 +725,7 @@
 }
 
 std::unique_ptr<DragImage> LocalFrame::dragImageForSelection(float opacity) {
-  if (!selection().isRange())
+  if (!selection().computeVisibleSelectionInDOMTreeDeprecated().isRange())
     return nullptr;
 
   m_view->updateAllLifecyclePhasesExceptPaint();
diff --git a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
index 546d8a5c..80deafa 100644
--- a/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLLabelElement.cpp
@@ -168,7 +168,9 @@
         // Check if there is a selection and click is not on the
         // selection.
         if (layoutObject() && layoutObject()->isSelectable() &&
-            frame->selection().isRange() &&
+            frame->selection()
+                .computeVisibleSelectionInDOMTreeDeprecated()
+                .isRange() &&
             !frame->eventHandler()
                  .selectionController()
                  .mouseDownWasSingleClickInSelection())
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
index df5a192..dd2b05ff 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
@@ -188,6 +188,14 @@
   return m_parserBlockingScript->isReady();
 }
 
+// This has two callers and corresponds to different concepts in the spec:
+// - When called from executeParsingBlockingScripts(), this corresponds to some
+//   steps of the "Otherwise" Clause of 'An end tag whose tag name is "script"'
+//   https://html.spec.whatwg.org/#scriptEndTag
+// - When called from executeScriptsWaitingForParsing(), this corresponds
+//   https://html.spec.whatwg.org/#execute-the-script-block
+//   and thus currently this function does more than specced.
+// TODO(hiroshige): Make the spec and implementation consistent.
 void HTMLParserScriptRunner::executePendingScriptAndDispatchEvent(
     PendingScript* pendingScript,
     ScriptStreamer::Type pendingScriptType) {
@@ -214,16 +222,24 @@
   TextPosition scriptStartPosition = pendingScript->startingPosition();
   double scriptParserBlockingTime =
       pendingScript->parserBlockingLoadStartTime();
-  // Clear the pending script before possible re-entrancy from executeScript()
   Element* element = pendingScript->element();
+
+  // 1. "Let the script be the pending parsing-blocking script.
+  //     There is no longer a pending parsing-blocking script."
+  // Clear the pending script before possible re-entrancy from executeScript()
   pendingScript->dispose();
 
   if (ScriptLoader* scriptLoader = toScriptLoaderIfPossible(element)) {
+    // 7. "Increment the parser's script nesting level by one (it should be
+    //     zero before this step, so this sets it to one)."
     HTMLParserReentryPermit::ScriptNestingLevelIncrementer
         nestingLevelIncrementer =
             m_reentryPermit->incrementScriptNestingLevel();
+
     IgnoreDestructiveWriteCountIncrementer
         ignoreDestructiveWriteCountIncrementer(m_document);
+
+    // 8. "Execute the script."
     if (errorOccurred) {
       TRACE_EVENT_WITH_FLOW1(
           "blink", "HTMLParserScriptRunner ExecuteScriptFailed", element,
@@ -244,6 +260,12 @@
         element->dispatchEvent(Event::create(EventTypeNames::load));
       }
     }
+
+    // 9. "Decrement the parser's script nesting level by one.
+    //     If the parser's script nesting level is zero
+    //     (which it always should be at this point),
+    //     then set the parser pause flag to false."
+    // This is implemented by ~ScriptNestingLevelIncrementer().
   }
 
   DCHECK(!isExecutingScript());
@@ -344,8 +366,9 @@
   m_host->notifyScriptLoaded(pendingScript);
 }
 
-// Implements the steps for 'An end tag whose tag name is "script"'
-// http://whatwg.org/html#scriptEndTag
+// 'An end tag whose tag name is "script"'
+// https://html.spec.whatwg.org/#scriptEndTag
+//
 // Script handling lives outside the tree builder to keep each class simple.
 void HTMLParserScriptRunner::processScriptElement(
     Element* scriptElement,
@@ -358,16 +381,27 @@
 
   bool hadPreloadScanner = m_host->hasPreloadScanner();
 
+  // Initial steps of 'An end tag whose tag name is "script"'.
   // Try to execute the script given to us.
   processScriptElementInternal(scriptElement, scriptStartPosition);
 
+  // "At this stage, if there is a pending parsing-blocking script, then:"
   if (hasParserBlockingScript()) {
+    // - "If the script nesting level is not zero:"
     if (isExecutingScript()) {
+      // "Set the parser pause flag to true, and abort the processing of any
+      //  nested invocations of the tokenizer, yielding control back to the
+      //  caller. (Tokenization will resume when the caller returns to the
+      //  "outer" tree construction stage.)"
+      // TODO(hiroshige): set the parser pause flag to true here.
+
       // Unwind to the outermost HTMLParserScriptRunner::processScriptElement
       // before continuing parsing.
       return;
     }
 
+    // - "Otherwise":
+
     traceParserBlockingScript(m_parserBlockingScript.get(),
                               !m_document->isScriptExecutionReady());
     m_parserBlockingScript->markParserBlockingLoadStartTime();
@@ -376,6 +410,7 @@
     // current insertion point. Append it and scan.
     if (!hadPreloadScanner && m_host->hasPreloadScanner())
       m_host->appendCurrentInputStreamToPreloadScannerAndScan();
+
     executeParsingBlockingScripts();
   }
 }
@@ -384,15 +419,37 @@
   return !!m_parserBlockingScript->element();
 }
 
+// The "Otherwise" Clause of 'An end tag whose tag name is "script"'
+// https://html.spec.whatwg.org/#scriptEndTag
 void HTMLParserScriptRunner::executeParsingBlockingScripts() {
+  // 3. "If (1) the parser's Document has a style sheet that is blocking scripts
+  //     or (2) the script's "ready to be parser-executed" flag is not set:
+  //     spin the event loop
+  //     until the parser's Document has no style sheet that is blocking scripts
+  //     and the script's "ready to be parser-executed" flag is set."
+  //
+  // These conditions correspond to isParserBlockingScriptReady() and
+  // if it is false, executeParsingBlockingScripts() will be called later
+  // when isParserBlockingScriptReady() becomes true:
+  // (1) from HTMLParserScriptRunner::executeScriptsWaitingForResources(), or
+  // (2) from HTMLParserScriptRunner::executeScriptsWaitingForLoad().
   while (hasParserBlockingScript() && isParserBlockingScriptReady()) {
     DCHECK(m_document);
     DCHECK(!isExecutingScript());
     DCHECK(m_document->isScriptExecutionReady());
 
+    // 6. "Let the insertion point be just before the next input character."
     InsertionPointRecord insertionPointRecord(m_host->inputStream());
+
+    // 1., 7.--9.
     executePendingScriptAndDispatchEvent(m_parserBlockingScript.get(),
                                          ScriptStreamer::ParsingBlocking);
+
+    // 10. "Let the insertion point be undefined again."
+    // Implemented as ~InsertionPointRecord().
+
+    // 11. "If there is once again a pending parsing-blocking script, then
+    //      repeat these steps from step 1."
   }
 }
 
@@ -415,13 +472,23 @@
   executeParsingBlockingScripts();
 }
 
+// Step 3 of https://html.spec.whatwg.org/#the-end:
+// "If the list of scripts that will execute when the document has
+//  finished parsing is not empty, run these substeps:"
 bool HTMLParserScriptRunner::executeScriptsWaitingForParsing() {
   TRACE_EVENT0("blink",
                "HTMLParserScriptRunner::executeScriptsWaitingForParsing");
+
   while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
     DCHECK(!isExecutingScript());
     DCHECK(!hasParserBlockingScript());
     DCHECK(m_scriptsToExecuteAfterParsing.first()->resource());
+
+    // 1. "Spin the event loop until the first script in the list of scripts
+    //     that will execute when the document has finished parsing
+    //     has its "ready to be parser-executed" flag set and
+    //     the parser's Document has no style sheet that is blocking scripts."
+    // TODO(hiroshige): Is the latter part checked anywhere?
     if (!m_scriptsToExecuteAfterParsing.first()->isReady()) {
       m_scriptsToExecuteAfterParsing.first()->watchForLoad(this);
       traceParserBlockingScript(m_scriptsToExecuteAfterParsing.first().get(),
@@ -429,11 +496,23 @@
       m_scriptsToExecuteAfterParsing.first()->markParserBlockingLoadStartTime();
       return false;
     }
+
+    // 3. "Remove the first script element from the list of scripts that will
+    //     execute when the document has finished parsing (i.e. shift out the
+    //     first entry in the list)."
     PendingScript* first = m_scriptsToExecuteAfterParsing.takeFirst();
+
+    // 2. "Execute the first script in the list of scripts that will execute
+    //     when the document has finished parsing."
     executePendingScriptAndDispatchEvent(first, ScriptStreamer::Deferred);
+
     // FIXME: What is this m_document check for?
     if (!m_document)
       return false;
+
+    // 4. "If the list of scripts that will execute when the document has
+    //     finished parsing is still not empty, repeat these substeps again
+    //     from substep 1."
   }
   return true;
 }
@@ -504,8 +583,8 @@
   return true;
 }
 
-// Implements the initial steps for 'An end tag whose tag name is "script"'
-// http://whatwg.org/html#scriptEndTag
+// The initial steps for 'An end tag whose tag name is "script"'
+// https://html.spec.whatwg.org/#scriptEndTag
 void HTMLParserScriptRunner::processScriptElementInternal(
     Element* script,
     const TextPosition& scriptStartPosition) {
@@ -527,11 +606,20 @@
     if (!isExecutingScript())
       Microtask::performCheckpoint(V8PerIsolateData::mainThreadIsolate());
 
+    // "Let the old insertion point have the same value as the current
+    //  insertion point.
+    //  Let the insertion point be just before the next input character."
     InsertionPointRecord insertionPointRecord(m_host->inputStream());
+
+    // "Increment the parser's script nesting level by one."
     HTMLParserReentryPermit::ScriptNestingLevelIncrementer
         nestingLevelIncrementer =
             m_reentryPermit->incrementScriptNestingLevel();
 
+    // "Prepare the script. This might cause some script to execute, which
+    //  might cause new characters to be inserted into the tokenizer, and
+    //  might cause the tokenizer to output more tokens, resulting in a
+    //  reentrant invocation of the parser."
     scriptLoader->prepareScript(scriptStartPosition);
 
     // A part of Step 23 of https://html.spec.whatwg.org/#prepare-a-script:
@@ -567,6 +655,14 @@
       // 2nd Clause of Step 23.
       requestParsingBlockingScript(script);
     }
+
+    // "Decrement the parser's script nesting level by one.
+    //  If the parser's script nesting level is zero, then set the parser
+    //  pause flag to false."
+    // Implemented by ~ScriptNestingLevelIncrementer().
+
+    // "Let the insertion point have the value of the old insertion point."
+    // Implemented by ~InsertionPointRecord().
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.h b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.h
index f61cf2f..2c9a3b9 100644
--- a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.h
+++ b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.h
@@ -121,8 +121,11 @@
   RefPtr<HTMLParserReentryPermit> m_reentryPermit;
   Member<Document> m_document;
   Member<HTMLParserScriptRunnerHost> m_host;
+
+  // https://html.spec.whatwg.org/#pending-parsing-blocking-script
   Member<PendingScript> m_parserBlockingScript;
-  // http://www.whatwg.org/specs/web-apps/current-work/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing
+
+  // https://html.spec.whatwg.org/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing
   HeapDeque<Member<PendingScript>> m_scriptsToExecuteAfterParsing;
 };
 
diff --git a/third_party/WebKit/Source/core/input/EventHandler.cpp b/third_party/WebKit/Source/core/input/EventHandler.cpp
index c96ff69..29fe2e4 100644
--- a/third_party/WebKit/Source/core/input/EventHandler.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandler.cpp
@@ -1839,7 +1839,8 @@
   VisualViewport& visualViewport = frameHost()->visualViewport();
 
   if (!overrideTargetElement && start.anchorNode() &&
-      (selection.rootEditableElement() || selection.isRange())) {
+      (selection.rootEditableElement() ||
+       selection.computeVisibleSelectionInDOMTreeDeprecated().isRange())) {
     // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
     // needs to be audited.  See http://crbug.com/590369 for more details.
     doc->updateStyleAndLayoutIgnorePendingStylesheets();
diff --git a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
index 7055cd55..0d46806 100644
--- a/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
+++ b/third_party/WebKit/Source/core/input/EventHandlerTest.cpp
@@ -134,7 +134,8 @@
   mouseUpEvent.setFrameScale(1);
   document().frame()->eventHandler().handleMouseReleaseEvent(mouseUpEvent);
 
-  ASSERT_TRUE(selection().isRange());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange());
   Range* range = createRange(selection()
                                  .computeVisibleSelectionInDOMTreeDeprecated()
                                  .toNormalizedEphemeralRange());
@@ -161,7 +162,8 @@
   // like multi-click events.
   TapEventBuilder doubleTapEvent(IntPoint(0, 0), 2);
   document().frame()->eventHandler().handleGestureEvent(doubleTapEvent);
-  ASSERT_TRUE(selection().isRange());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange());
   EXPECT_EQ(Position(line, 0), selection().start());
   if (document().frame()->editor().isSelectTrailingWhitespaceEnabled()) {
     EXPECT_EQ(Position(line, 4),
@@ -175,7 +177,8 @@
 
   TapEventBuilder tripleTapEvent(IntPoint(0, 0), 3);
   document().frame()->eventHandler().handleGestureEvent(tripleTapEvent);
-  ASSERT_TRUE(selection().isRange());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange());
   EXPECT_EQ(Position(line, 0), selection().start());
   EXPECT_EQ(Position(line, 13),
             selection().computeVisibleSelectionInDOMTreeDeprecated().end());
@@ -401,7 +404,8 @@
   document().frame()->eventHandler().handleMousePressEvent(
       doubleClickMousePressEvent);
 
-  ASSERT_TRUE(selection().isRange());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange());
   ASSERT_FALSE(selection().isHandleVisible());
 
   MousePressEventBuilder tripleClickMousePressEvent(
@@ -409,7 +413,8 @@
   document().frame()->eventHandler().handleMousePressEvent(
       tripleClickMousePressEvent);
 
-  ASSERT_TRUE(selection().isRange());
+  ASSERT_TRUE(
+      selection().computeVisibleSelectionInDOMTreeDeprecated().isRange());
   ASSERT_FALSE(selection().isHandleVisible());
 }
 
diff --git a/third_party/WebKit/Source/core/input/MouseEventManager.cpp b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
index 2be66b1a..524f298 100644
--- a/third_party/WebKit/Source/core/input/MouseEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/MouseEventManager.cpp
@@ -425,7 +425,10 @@
   // be focused if the user does a mouseup over it, however, because the
   // mouseup will set a selection inside it, which will call
   // FrameSelection::setFocusedNodeIfNeeded.
-  if (element && m_frame->selection().isRange()) {
+  if (element &&
+      m_frame->selection()
+          .computeVisibleSelectionInDOMTreeDeprecated()
+          .isRange()) {
     // TODO(yosin) We should not create |Range| object for calling
     // |isNodeFullyContained()|.
     if (createRange(m_frame->selection()
diff --git a/third_party/WebKit/Source/core/input/TouchEventManager.cpp b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
index 149cff36..2d91d07 100644
--- a/third_party/WebKit/Source/core/input/TouchEventManager.cpp
+++ b/third_party/WebKit/Source/core/input/TouchEventManager.cpp
@@ -126,10 +126,6 @@
   // http://www.w3.org/TR/touch-events/#touchevent-interface for how these
   // lists fit together.
 
-  // Suppress all the touch moves in the slop region.
-  if (IsTouchSequenceStart(event))
-    m_suppressingTouchmovesWithinSlop = true;
-
   if (event.type() == WebInputEvent::TouchEnd ||
       event.type() == WebInputEvent::TouchCancel || event.touchesLength > 1) {
     m_suppressingTouchmovesWithinSlop = false;
@@ -292,6 +288,12 @@
     }
   }
 
+  // Do not suppress any touchmoves if the touchstart is consumed.
+  if (IsTouchSequenceStart(event) &&
+      eventResult == WebInputEventResult::NotHandled) {
+    m_suppressingTouchmovesWithinSlop = true;
+  }
+
   return eventResult;
 }
 
diff --git a/third_party/WebKit/Source/core/page/DragController.cpp b/third_party/WebKit/Source/core/page/DragController.cpp
index 4622e1d..9483dab 100644
--- a/third_party/WebKit/Source/core/page/DragController.cpp
+++ b/third_party/WebKit/Source/core/page/DragController.cpp
@@ -199,7 +199,8 @@
 
 bool DragController::dragIsMove(FrameSelection& selection, DragData* dragData) {
   return m_documentUnderMouse == m_dragInitiator &&
-         selection.isContentEditable() && selection.isRange() &&
+         selection.isContentEditable() &&
+         selection.computeVisibleSelectionInDOMTreeDeprecated().isRange() &&
          !isCopyKeyDown(dragData);
 }
 
diff --git a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
index 89d55678..7a1c25a 100644
--- a/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
+++ b/third_party/WebKit/Source/web/WebLocalFrameImpl.cpp
@@ -1149,7 +1149,8 @@
 bool WebLocalFrameImpl::selectWordAroundCaret() {
   TRACE_EVENT0("blink", "WebLocalFrameImpl::selectWordAroundCaret");
   FrameSelection& selection = frame()->selection();
-  if (selection.isNone() || selection.isRange())
+  if (selection.isNone() ||
+      selection.computeVisibleSelectionInDOMTreeDeprecated().isRange())
     return false;
 
   // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets
diff --git a/tools/chrome_proxy/webdriver/video.py b/tools/chrome_proxy/webdriver/video.py
index 9755e045..ead67e11 100644
--- a/tools/chrome_proxy/webdriver/video.py
+++ b/tools/chrome_proxy/webdriver/video.py
@@ -42,6 +42,37 @@
           self.assertHasChromeProxyViaHeader(response)
       self.assertTrue(saw_video_response, 'No video request seen in test!')
 
+  # Check the compressed video has the same frame count, width, height, and
+  # duration as uncompressed.
+  def testVideoMetrics(self):
+    expected = {
+      'duration': 3.124,
+      'webkitDecodedFrameCount': 54.0,
+      'videoWidth': 1280.0,
+      'videoHeight': 720.0
+    }
+    with TestDriver() as t:
+      t.AddChromeArg('--enable-spdy-proxy-auth')
+      t.LoadURL('http://check.googlezip.net/cacheable/video/buck_bunny_tiny.html')
+      # Check request was proxied and we got a compressed video back.
+      for response in t.GetHTTPResponses():
+        self.assertHasChromeProxyViaHeader(response)
+        if ('content-type' in response.response_headers
+            and 'video' in response.response_headers['content-type']):
+          self.assertEqual('video/webm',
+            response.response_headers['content-type'])
+      t.ExecuteJavascriptStatement(
+        'document.querySelectorAll("video")[0].play()')
+      # Wait for the video to finish playing, plus some headroom.
+      time.sleep(5)
+      # Check each metric against its expected value.
+      for metric in expected:
+        actual = float(t.ExecuteJavascriptStatement(
+          'document.querySelectorAll("video")[0].%s' % metric))
+        self.assertAlmostEqual(expected[metric], actual, msg="Compressed video "
+          "metric doesn't match expected! Metric=%s Expected=%f Actual=%f"
+          % (metric, expected[metric], actual), places=None, delta=0.001)
+
   # Check the frames of a compressed video.
   def testVideoFrames(self):
     self.instrumentedVideoTest('http://check.googlezip.net/cacheable/video/buck_bunny_640x360_24fps_video.html')
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 48e0bb9b..5aaf095 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -73156,6 +73156,13 @@
   <summary>Reason a scavenge garbage collection was started in V8.</summary>
 </histogram>
 
+<histogram name="V8.GCYoungGenerationHandling" enum="YoungGenerationHandling">
+  <owner>mlippautz@chromium.org</owner>
+  <summary>
+    Type of garbage collection strategy used to collect the young generation.
+  </summary>
+</histogram>
+
 <histogram name="V8.Initializer.LoadV8Snapshot.Result"
     enum="V8InitializerLoadV8SnapshotResult">
   <owner>oth@chromium.org</owner>
@@ -112104,6 +112111,11 @@
   <int value="1" label="XMLHttpRequestSendArrayBufferView"/>
 </enum>
 
+<enum name="YoungGenerationHandling" type="int">
+  <int value="0" label="Regular Scavenge"/>
+  <int value="1" label="Scavenge using fast promotion mode"/>
+</enum>
+
 <enum name="YouTubeRewriteStatus" type="int">
   <int value="0" label="Success">Embed was properly rewritten.</int>
   <int value="1" label="Success, params were rewritten">
@@ -119225,6 +119237,12 @@
 <histogram_suffixes name="ScrollUpdateHandledThread">
   <suffix name="Main" label="ScrollUpdate handled on main thread"/>
   <suffix name="Impl" label="ScrollUpdate handled on impl thread"/>
+  <affected-histogram
+      name="Event.Latency.ScrollBegin.Touch.HandledToRendererSwap2"/>
+  <affected-histogram name="Event.Latency.ScrollBegin.Touch.TimeToHandled2"/>
+  <affected-histogram
+      name="Event.Latency.ScrollBegin.Wheel.HandledToRendererSwap2"/>
+  <affected-histogram name="Event.Latency.ScrollBegin.Wheel.TimeToHandled2"/>
   <affected-histogram name="Event.Latency.ScrollUpdate.HandledToRendererSwap"/>
   <affected-histogram
       name="Event.Latency.ScrollUpdate.Touch.HandledToRendererSwap2"/>
diff --git a/tools/perf/benchmarks/battor.py b/tools/perf/benchmarks/battor.py
index aa0a077..3ba750a 100644
--- a/tools/perf/benchmarks/battor.py
+++ b/tools/perf/benchmarks/battor.py
@@ -65,23 +65,6 @@
     return 'battor.power_cases'
 
 
-@benchmark.Disabled('all')  # crbug.com/651384.
-class BattOrPowerCasesNoChromeTrace(_BattOrBenchmark):
-  page_set = page_sets.power_cases.PowerCasesPageSet
-
-  def CreateTimelineBasedMeasurementOptions(self):
-    options = timeline_based_measurement.Options()
-    options.config.enable_battor_trace = True
-    options.config.enable_chrome_trace = False
-    options.config.chrome_trace_config.SetDefaultOverheadFilter()
-    options.SetTimelineBasedMetrics(['powerMetric', 'clockSyncLatencyMetric'])
-    return options
-
-  @classmethod
-  def Name(cls):
-    return 'battor.power_cases_no_chrome_trace'
-
-
 @benchmark.Enabled('mac')
 class BattOrTrivialPages(_BattOrBenchmark):
 
diff --git a/ui/views/BUILD.gn b/ui/views/BUILD.gn
index 332eeb5..2d9f7b2 100644
--- a/ui/views/BUILD.gn
+++ b/ui/views/BUILD.gn
@@ -265,7 +265,6 @@
     "controls/table/table_view.cc",
     "controls/table/table_view.h",
     "controls/table/table_view_observer.h",
-    "controls/table/table_view_row_background_painter.h",
     "controls/textfield/textfield.cc",
     "controls/textfield/textfield.h",
     "controls/textfield/textfield_controller.cc",
diff --git a/ui/views/controls/table/table_view.cc b/ui/views/controls/table/table_view.cc
index db200b5..94fe60c 100644
--- a/ui/views/controls/table/table_view.cc
+++ b/ui/views/controls/table/table_view.cc
@@ -28,7 +28,6 @@
 #include "ui/views/controls/table/table_header.h"
 #include "ui/views/controls/table/table_utils.h"
 #include "ui/views/controls/table/table_view_observer.h"
-#include "ui/views/controls/table/table_view_row_background_painter.h"
 
 // Padding around the text (on each side).
 static const int kTextVerticalPadding = 3;
@@ -180,11 +179,6 @@
   return scroll_view;
 }
 
-void TableView::SetRowBackgroundPainter(
-    std::unique_ptr<TableViewRowBackgroundPainter> painter) {
-  row_background_painter_ = std::move(painter);
-}
-
 void TableView::SetGrouper(TableGrouper* grouper) {
   grouper_ = grouper;
   SortItemsAndUpdateMapping();
@@ -550,13 +544,8 @@
   for (int i = region.min_row; i < region.max_row; ++i) {
     const int model_index = ViewToModel(i);
     const bool is_selected = selection_model_.IsSelected(model_index);
-    if (is_selected) {
+    if (is_selected)
       canvas->FillRect(GetRowBounds(i), selected_bg_color);
-    } else if (row_background_painter_) {
-      row_background_painter_->PaintRowBackground(model_index,
-                                                  GetRowBounds(i),
-                                                  canvas);
-    }
     if (selection_model_.active() == model_index && HasFocus())
       canvas->DrawFocusRect(GetRowBounds(i));
     for (int j = region.min_column; j < region.max_column; ++j) {
diff --git a/ui/views/controls/table/table_view.h b/ui/views/controls/table/table_view.h
index 6d1c184..e6524ecf 100644
--- a/ui/views/controls/table/table_view.h
+++ b/ui/views/controls/table/table_view.h
@@ -5,7 +5,6 @@
 #ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
 #define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_VIEWS_H_
 
-#include <memory>
 #include <vector>
 
 #include "base/macros.h"
@@ -37,7 +36,6 @@
 class TableGrouper;
 class TableHeader;
 class TableViewObserver;
-class TableViewRowBackgroundPainter;
 class TableViewTestHelper;
 
 // The cells in the first column of a table can contain:
@@ -106,9 +104,6 @@
   // Returns a new ScrollView that contains the receiver.
   View* CreateParentIfNecessary();
 
-  void SetRowBackgroundPainter(
-      std::unique_ptr<TableViewRowBackgroundPainter> painter);
-
   // Sets the TableGrouper. TableView does not own |grouper| (common use case is
   // to have TableModel implement TableGrouper).
   void SetGrouper(TableGrouper* grouper);
@@ -347,8 +342,6 @@
   std::vector<int> view_to_model_;
   std::vector<int> model_to_view_;
 
-  std::unique_ptr<TableViewRowBackgroundPainter> row_background_painter_;
-
   TableGrouper* grouper_;
 
   // True if in SetVisibleColumnWidth().
diff --git a/ui/views/controls/table/table_view_row_background_painter.h b/ui/views/controls/table/table_view_row_background_painter.h
deleted file mode 100644
index f904131..0000000
--- a/ui/views/controls/table/table_view_row_background_painter.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (c) 2012 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_ROW_BACKGROUND_PAINTER_H_
-#define UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_ROW_BACKGROUND_PAINTER_H_
-
-#include "ui/views/views_export.h"
-
-namespace gfx {
-class Canvas;
-class Rect;
-}
-
-namespace views {
-
-// TableViewRowBackgroundPainter is used to paint the background of a row in the
-// table.
-class VIEWS_EXPORT TableViewRowBackgroundPainter {
- public:
-  virtual ~TableViewRowBackgroundPainter() {}
-  virtual void PaintRowBackground(int model_index,
-                                  const gfx::Rect& row_bounds,
-                                  gfx::Canvas* canvas) = 0;
-};
-
-}
-
-#endif  // UI_VIEWS_CONTROLS_TABLE_TABLE_VIEW_ROW_BACKGROUND_PAINTER_H_