diff --git a/DEPS b/DEPS
index d0b7f9f6..7d6b083 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': '7d3d8723319038d16456137ba932f238c1e65dbf',
+  'skia_revision': '4e3abc1ad5f078ed55cbc0c0ef0e14062a39bd13',
   # 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': 'c5350347a7ff55ad11f0944279b28dc8a3079636',
+  'v8_revision': 'ef8c92ef29465d65b6108ed5b2a26f75df631953',
   # 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.
@@ -52,7 +52,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling ANGLE
   # and whatever else without interference from each other.
-  'angle_revision': 'fe48632f2f47f9513c0557c331a8f346ed92c82d',
+  'angle_revision': 'bf67aa801a19e4d3877871bfea6a209226b94759',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling build tools
   # and whatever else without interference from each other.
@@ -64,7 +64,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': '0e5d892fe86d7c2527d8f7b7ac2c5aa8fc77a7be',
+  'pdfium_revision': '6a5c20cd08748da5969cbab756c3e8a6dd27bfbc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling openmax_dl
   # and whatever else without interference from each other.
@@ -196,13 +196,13 @@
     Var('chromium_git') + '/external/bidichecker/lib.git' + '@' + '97f2aa645b74c28c57eca56992235c79850fa9e0',
 
   'src/third_party/webgl/src':
-    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + 'd0783b85bd445f700576dba25bf452d58c112d33',
+    Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '32cfddc9e452c93bcbe443d6ddcc0c18ac556501',
 
   'src/third_party/webdriver/pylib':
     Var('chromium_git') + '/external/selenium/py.git' + '@' + '5fd78261a75fe08d27ca4835fb6c5ce4b42275bd',
 
   'src/third_party/libvpx/source/libvpx':
-    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '32b3d2f174f69f1484e024b276aff9226a751d4f',
+    Var('chromium_git') + '/webm/libvpx.git' + '@' +  '6af42f5102ad7c00d3fed389b186663a88d812ee',
 
   'src/third_party/ffmpeg':
     Var('chromium_git') + '/chromium/third_party/ffmpeg.git' + '@' + '9751ef06f90fb7ebf02b4e9ecc963a4e36a221d3',
@@ -232,7 +232,7 @@
     Var('chromium_git') + '/native_client/src/third_party/scons-2.0.1.git' + '@' + '1c1550e17fc26355d08627fbdec13d8291227067',
 
   'src/third_party/webrtc':
-    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '05fb319d932116ccd7fe4d27d420e9694c560d69', # commit position 17589
+    Var('chromium_git') + '/external/webrtc/trunk/webrtc.git' + '@' + '4b544defc44efd77c96a63c199c7edbda8e7e69c', # commit position 17592
 
   'src/third_party/openmax_dl':
     Var('chromium_git') + '/external/webrtc/deps/third_party/openmax.git' + '@' +  Var('openmax_dl_revision'),
@@ -721,6 +721,16 @@
     ],
   },
   {
+    'name': 'checkstyle',
+    'pattern': '.',
+    'action': ['python',
+               'src/build/android/update_deps/update_third_party_deps.py',
+               'download',
+               '-b', 'chromium-android-tools/checkstyle',
+               '-l', 'third_party/checkstyle'
+    ],
+  },
+  {
     'name': 'apk-patch-size-estimator',
     'pattern': '.',
     'action': ['python',
diff --git a/WATCHLISTS b/WATCHLISTS
index dd6d356..0a62c83d 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -836,8 +836,9 @@
       'filepath': 'chrome/browser/ui/page_info/'\
                   '|chrome/browser/ui/.*/page_info/'\
                   '|chrome/android/java/src/org/chromium/chrome/browser/page_info/'\
+                  '|components/page_info_strings.grdp'\
                   '|ios/chrome/browser/ui/omnibox/page_info_'
-  },
+    },
     'page_load_metrics' : {
       'filepath': 'chrome/browser/page_load_metrics/'\
                   '|chrome/common/page_load_metrics/'\
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 86afc07..bcd1472 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -342,6 +342,12 @@
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_KEYBOARD_FOCUS" desc="The label used in the additional settings of accessibility menu of the system tray to toggle on/off highlight keyboard focus.">
         Highlight object with keyboard focus
       </message>
+      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_STICKY_KEYS" desc="The label used in the additional settings of accessibility menu of the system tray to toggle on/off sticky keys.">
+        Sticky keys
+      </message>
+      <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TAP_DRAGGING" desc="The label used in the additional settings of accessibility menu of the system tray to toggle on/off tap dragging.">
+        Tap dragging
+      </message>
       <message name="IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SETTINGS" desc="The label used in the accessibility menu of system tray to open accessibility setting page.">
         Accessibility settings
       </message>
diff --git a/ash/common/accessibility_delegate.h b/ash/common/accessibility_delegate.h
index 25c0665..e7bc2de 100644
--- a/ash/common/accessibility_delegate.h
+++ b/ash/common/accessibility_delegate.h
@@ -84,6 +84,18 @@
   // Returns if focus highlighting is enabled.
   virtual bool IsFocusHighlightEnabled() const = 0;
 
+  // Invoked to enable or disable sticky keys.
+  virtual void SetStickyKeysEnabled(bool enabled) = 0;
+
+  // Returns if sticky keys is enabled.
+  virtual bool IsStickyKeysEnabled() const = 0;
+
+  // Invoked to enable or disable tap dragging.
+  virtual void SetTapDraggingEnabled(bool enabled) = 0;
+
+  // Returns if tap dragging is enabled.
+  virtual bool IsTapDraggingEnabled() const = 0;
+
   // Invoked to enable or disable select-to-speak.
   virtual void SetSelectToSpeakEnabled(bool enabled) = 0;
 
diff --git a/ash/common/default_accessibility_delegate.cc b/ash/common/default_accessibility_delegate.cc
index 6a1bd37..0c21c05 100644
--- a/ash/common/default_accessibility_delegate.cc
+++ b/ash/common/default_accessibility_delegate.cc
@@ -96,6 +96,22 @@
   return focus_highligh_enabled_;
 }
 
+void DefaultAccessibilityDelegate::SetStickyKeysEnabled(bool enabled) {
+  sticky_keys_enabled_ = enabled;
+}
+
+bool DefaultAccessibilityDelegate::IsStickyKeysEnabled() const {
+  return sticky_keys_enabled_;
+}
+
+void DefaultAccessibilityDelegate::SetTapDraggingEnabled(bool enabled) {
+  tap_dragging_enabled_ = enabled;
+}
+
+bool DefaultAccessibilityDelegate::IsTapDraggingEnabled() const {
+  return tap_dragging_enabled_;
+}
+
 void DefaultAccessibilityDelegate::SetSelectToSpeakEnabled(bool enabled) {
   select_to_speak_enabled_ = enabled;
 }
diff --git a/ash/common/default_accessibility_delegate.h b/ash/common/default_accessibility_delegate.h
index 46af280..d9f920c 100644
--- a/ash/common/default_accessibility_delegate.h
+++ b/ash/common/default_accessibility_delegate.h
@@ -38,6 +38,10 @@
   bool IsCursorHighlightEnabled() const override;
   void SetFocusHighlightEnabled(bool enabled) override;
   bool IsFocusHighlightEnabled() const override;
+  void SetStickyKeysEnabled(bool enabled) override;
+  bool IsStickyKeysEnabled() const override;
+  void SetTapDraggingEnabled(bool enabled) override;
+  bool IsTapDraggingEnabled() const override;
   void SetSelectToSpeakEnabled(bool enabled) override;
   bool IsSelectToSpeakEnabled() const override;
   void SetSwitchAccessEnabled(bool enabled) override;
@@ -70,6 +74,8 @@
   bool caret_highlight_enabled_ = false;
   bool cursor_highlight_enabled_ = false;
   bool focus_highligh_enabled_ = false;
+  bool sticky_keys_enabled_ = false;
+  bool tap_dragging_enabled_ = false;
   bool select_to_speak_enabled_ = false;
   bool switch_access_enabled_ = false;
   AccessibilityAlert accessibility_alert_ = A11Y_ALERT_NONE;
diff --git a/ash/common/metrics/user_metrics_action.h b/ash/common/metrics/user_metrics_action.h
index 1164d6b..a472595b 100644
--- a/ash/common/metrics/user_metrics_action.h
+++ b/ash/common/metrics/user_metrics_action.h
@@ -75,6 +75,8 @@
   UMA_STATUS_AREA_DISABLE_MAGNIFIER,
   UMA_STATUS_AREA_DISABLE_MONO_AUDIO,
   UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK,
+  UMA_STATUS_AREA_DISABLE_STICKY_KEYS,
+  UMA_STATUS_AREA_DISABLE_TAP_DRAGGING,
   UMA_STATUS_AREA_DISABLE_WIFI,
   UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD,
   UMA_STATUS_AREA_DISPLAY_DEFAULT_SELECTED,
@@ -93,6 +95,8 @@
   UMA_STATUS_AREA_ENABLE_MAGNIFIER,
   UMA_STATUS_AREA_ENABLE_MONO_AUDIO,
   UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK,
+  UMA_STATUS_AREA_ENABLE_STICKY_KEYS,
+  UMA_STATUS_AREA_ENABLE_TAP_DRAGGING,
   UMA_STATUS_AREA_ENABLE_WIFI,
   UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD,
   UMA_STATUS_AREA_IME_SHOW_DETAILED,
diff --git a/ash/common/system/tray_accessibility.cc b/ash/common/system/tray_accessibility.cc
index 3d32c7a..928887b 100644
--- a/ash/common/system/tray_accessibility.cc
+++ b/ash/common/system/tray_accessibility.cc
@@ -54,6 +54,8 @@
   A11Y_CARET_HIGHLIGHT = 1 << 8,
   A11Y_HIGHLIGHT_MOUSE_CURSOR = 1 << 9,
   A11Y_HIGHLIGHT_KEYBOARD_FOCUS = 1 << 10,
+  A11Y_STICKY_KEYS = 1 << 11,
+  A11Y_TAP_DRAGGING = 1 << 12,
 };
 
 uint32_t GetAccessibilityState() {
@@ -81,6 +83,10 @@
     state |= A11Y_HIGHLIGHT_MOUSE_CURSOR;
   if (delegate->IsFocusHighlightEnabled())
     state |= A11Y_HIGHLIGHT_KEYBOARD_FOCUS;
+  if (delegate->IsStickyKeysEnabled())
+    state |= A11Y_STICKY_KEYS;
+  if (delegate->IsTapDraggingEnabled())
+    state |= A11Y_TAP_DRAGGING;
   return state;
 }
 
@@ -291,6 +297,16 @@
             IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_KEYBOARD_FOCUS),
         highlight_keyboard_focus_enabled_);
   }
+
+  sticky_keys_enabled_ = delegate->IsStickyKeysEnabled();
+  sticky_keys_view_ = AddScrollListItemWithoutIcon(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_STICKY_KEYS),
+      sticky_keys_enabled_);
+
+  tap_dragging_enabled_ = delegate->IsTapDraggingEnabled();
+  tap_dragging_view_ = AddScrollListItemWithoutIcon(
+      l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_TAP_DRAGGING),
+      tap_dragging_enabled_);
 }
 
 HoverHighlightView* AccessibilityDetailedView::AddScrollListItem(
@@ -386,6 +402,16 @@
                       ? ash::UMA_STATUS_AREA_DISABLE_HIGHLIGHT_KEYBOARD_FOCUS
                       : ash::UMA_STATUS_AREA_ENABLE_HIGHLIGHT_KEYBOARD_FOCUS;
     delegate->SetFocusHighlightEnabled(!delegate->IsFocusHighlightEnabled());
+  } else if (sticky_keys_view_ && view == sticky_keys_view_) {
+    user_action = delegate->IsStickyKeysEnabled()
+                      ? ash::UMA_STATUS_AREA_DISABLE_STICKY_KEYS
+                      : ash::UMA_STATUS_AREA_ENABLE_STICKY_KEYS;
+    delegate->SetStickyKeysEnabled(!delegate->IsStickyKeysEnabled());
+  } else if (tap_dragging_view_ && view == tap_dragging_view_) {
+    user_action = delegate->IsTapDraggingEnabled()
+                      ? ash::UMA_STATUS_AREA_DISABLE_TAP_DRAGGING
+                      : ash::UMA_STATUS_AREA_ENABLE_TAP_DRAGGING;
+    delegate->SetTapDraggingEnabled(!delegate->IsTapDraggingEnabled());
   } else {
     return;
   }
diff --git a/ash/common/system/tray_accessibility.h b/ash/common/system/tray_accessibility.h
index ac67a07..2321aa8 100644
--- a/ash/common/system/tray_accessibility.h
+++ b/ash/common/system/tray_accessibility.h
@@ -103,6 +103,8 @@
   views::View* caret_highlight_view_ = nullptr;
   views::View* highlight_mouse_cursor_view_ = nullptr;
   views::View* highlight_keyboard_focus_view_ = nullptr;
+  views::View* sticky_keys_view_ = nullptr;
+  views::View* tap_dragging_view_ = nullptr;
 
   bool spoken_feedback_enabled_ = false;
   bool high_contrast_enabled_ = false;
@@ -114,6 +116,8 @@
   bool caret_highlight_enabled_ = false;
   bool highlight_mouse_cursor_enabled_ = false;
   bool highlight_keyboard_focus_enabled_ = false;
+  bool sticky_keys_enabled_ = false;
+  bool tap_dragging_enabled_ = false;
   LoginStatus login_;
 
   friend class chromeos::TrayAccessibilityTest;
diff --git a/ash/common/wallpaper/wallpaper_controller_unittest.cc b/ash/common/wallpaper/wallpaper_controller_unittest.cc
index 13503743..895692e7 100644
--- a/ash/common/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/common/wallpaper/wallpaper_controller_unittest.cc
@@ -165,18 +165,15 @@
     gfx::Canvas canvas(size, device_scale_factor, true);
     view->OnPaint(&canvas);
 
-    int canvas_width = canvas.sk_canvas()->imageInfo().width();
-    int canvas_height = canvas.sk_canvas()->imageInfo().height();
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(canvas_width, canvas_height);
-    canvas.sk_canvas()->readPixels(&bitmap, 0, 0);
-
-    for (int i = 0; i < canvas_width; i++) {
-      for (int j = 0; j < canvas_height; j++) {
-        if (i >= (canvas_width - image_width) / 2 &&
-            i < (canvas_width + image_width) / 2 &&
-            j >= (canvas_height - image_height) / 2 &&
-            j < (canvas_height + image_height) / 2) {
+    SkBitmap bitmap = canvas.GetBitmap();
+    int bitmap_width = bitmap.width();
+    int bitmap_height = bitmap.height();
+    for (int i = 0; i < bitmap_width; i++) {
+      for (int j = 0; j < bitmap_height; j++) {
+        if (i >= (bitmap_width - image_width) / 2 &&
+            i < (bitmap_width + image_width) / 2 &&
+            j >= (bitmap_height - image_height) / 2 &&
+            j < (bitmap_height + image_height) / 2) {
           EXPECT_EQ(color, bitmap.getColor(i, j));
         } else {
           EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(i, j));
diff --git a/ash/laser/laser_pointer_view.cc b/ash/laser/laser_pointer_view.cc
index d104eaff..026f950 100644
--- a/ash/laser/laser_pointer_view.cc
+++ b/ash/laser/laser_pointer_view.cc
@@ -568,7 +568,7 @@
         gfx::ScaleToEnclosingRect(update_rect, scale_factor_);
     uint8_t* data = static_cast<uint8_t*>(gpu_memory_buffer_->memory(0));
     int stride = gpu_memory_buffer_->stride(0);
-    canvas.sk_canvas()->readPixels(
+    canvas.GetBitmap().readPixels(
         SkImageInfo::MakeN32Premul(pixel_rect.width(), pixel_rect.height()),
         data + pixel_rect.y() * stride + pixel_rect.x() * 4, stride, 0, 0);
   }
diff --git a/ash/metrics/user_metrics_recorder.cc b/ash/metrics/user_metrics_recorder.cc
index 7ca11171..ed5af502 100644
--- a/ash/metrics/user_metrics_recorder.cc
+++ b/ash/metrics/user_metrics_recorder.cc
@@ -407,6 +407,12 @@
     case UMA_STATUS_AREA_DISABLE_SPOKEN_FEEDBACK:
       RecordAction(UserMetricsAction("StatusArea_SpokenFeedbackDisabled"));
       break;
+    case UMA_STATUS_AREA_DISABLE_STICKY_KEYS:
+      RecordAction(UserMetricsAction("StatusArea_StickyKeysDisabled"));
+      break;
+    case UMA_STATUS_AREA_DISABLE_TAP_DRAGGING:
+      RecordAction(UserMetricsAction("StatusArea_TapDraggingDisabled"));
+      break;
     case UMA_STATUS_AREA_DISABLE_VIRTUAL_KEYBOARD:
       RecordAction(UserMetricsAction("StatusArea_VirtualKeyboardDisabled"));
       break;
@@ -466,6 +472,12 @@
     case UMA_STATUS_AREA_ENABLE_SPOKEN_FEEDBACK:
       RecordAction(UserMetricsAction("StatusArea_SpokenFeedbackEnabled"));
       break;
+    case UMA_STATUS_AREA_ENABLE_STICKY_KEYS:
+      RecordAction(UserMetricsAction("StatusArea_StickyKeysEnabled"));
+      break;
+    case UMA_STATUS_AREA_ENABLE_TAP_DRAGGING:
+      RecordAction(UserMetricsAction("StatusArea_TapDraggingEnabled"));
+      break;
     case UMA_STATUS_AREA_ENABLE_VIRTUAL_KEYBOARD:
       RecordAction(UserMetricsAction("StatusArea_VirtualKeyboardEnabled"));
       break;
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index d3f4f493..5aa073c0 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -41,6 +41,7 @@
 #include "ash/common/wm_window.h"
 #include "ash/high_contrast/high_contrast_controller.h"
 #include "ash/host/ash_window_tree_host.h"
+#include "ash/public/cpp/config.h"
 #include "ash/public/cpp/shelf_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/root_window_settings.h"
@@ -251,7 +252,7 @@
 WmWindow* CreateContainer(int window_id, const char* name, WmWindow* parent) {
   aura::Window* window = new aura::Window(nullptr, ui::wm::WINDOW_TYPE_UNKNOWN);
   window->Init(ui::LAYER_NOT_DRAWN);
-  if (WmShell::Get()->IsRunningInMash()) {
+  if (Shell::GetAshConfig() != Config::CLASSIC) {
     aura::WindowPortMus::Get(window)->SetEventTargetingPolicy(
         ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY);
   }
diff --git a/ash/shell.cc b/ash/shell.cc
index 1d7e60c4..e9718cf 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -791,8 +791,6 @@
   wallpaper_delegate_ = shell_delegate_->CreateWallpaperDelegate();
 
   // Can be null in tests.
-  // TODO(jonross): reenable once the cause of crbug.com/707321 is determined.
-  /*
   if (wm_shell_->IsRunningInMash() && shell_delegate_->GetShellConnector()) {
     prefs::ConnectToPrefService(
         shell_delegate_->GetShellConnector(),
@@ -800,7 +798,7 @@
         std::vector<PrefValueStore::PrefStoreType>(),
         base::Bind(&Shell::OnPrefServiceInitialized, base::Unretained(this)),
         prefs::mojom::kForwarderServiceName);
-  }*/
+  }
 
   // Some delegates access WmShell during their construction. Create them here
   // instead of the WmShell constructor.
diff --git a/base/android/callback_android.cc b/base/android/callback_android.cc
index 2326d85..a97deab 100644
--- a/base/android/callback_android.cc
+++ b/base/android/callback_android.cc
@@ -4,6 +4,8 @@
 
 #include "base/android/callback_android.h"
 
+#include "base/android/jni_array.h"
+#include "base/android/scoped_java_ref.h"
 #include "jni/Callback_jni.h"
 
 namespace base {
@@ -25,5 +27,13 @@
                                       callback, arg);
 }
 
+void RunCallbackAndroid(const JavaRef<jobject>& callback,
+                        const std::vector<uint8_t>& arg) {
+  JNIEnv* env = base::android::AttachCurrentThread();
+  base::android::ScopedJavaLocalRef<jbyteArray> j_bytes =
+      base::android::ToJavaByteArray(env, arg);
+  Java_Callback_onResultFromNativeV_AB(env, callback, j_bytes);
+}
+
 }  // namespace android
 }  // namespace base
diff --git a/base/android/callback_android.h b/base/android/callback_android.h
index f118827c..5cefe0f 100644
--- a/base/android/callback_android.h
+++ b/base/android/callback_android.h
@@ -6,24 +6,27 @@
 #define BASE_ANDROID_CALLBACK_ANDROID_H_
 
 #include <jni.h>
+#include <vector>
 
 #include "base/android/scoped_java_ref.h"
 #include "base/base_export.h"
 
+// Provides helper utility methods that run the given callback with the
+// specified argument.
 namespace base {
 namespace android {
 
-// Runs the given |callback| with the specified |arg|.
 void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback,
                                     const JavaRef<jobject>& arg);
 
-// Runs the given |callback| with the specified |arg|.
 void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback,
                                     bool arg);
 
-// Runs the given |callback| with the specified |arg|.
 void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback, int arg);
 
+void BASE_EXPORT RunCallbackAndroid(const JavaRef<jobject>& callback,
+                                    const std::vector<uint8_t>& arg);
+
 }  // namespace android
 }  // namespace base
 
diff --git a/base/android/java/src/org/chromium/base/Callback.java b/base/android/java/src/org/chromium/base/Callback.java
index ee5857d6..0810b62 100644
--- a/base/android/java/src/org/chromium/base/Callback.java
+++ b/base/android/java/src/org/chromium/base/Callback.java
@@ -34,4 +34,10 @@
     private void onResultFromNative(int result) {
         onResult((T) Integer.valueOf(result));
     }
+
+    @SuppressWarnings("unchecked")
+    @CalledByNative
+    private void onResultFromNative(byte[] result) {
+        onResult((T) result);
+    }
 }
diff --git a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
index ea680c3..32a5347 100644
--- a/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
+++ b/base/android/java/src/org/chromium/base/process_launcher/IChildProcessService.aidl
@@ -16,5 +16,5 @@
   int setupConnection(in Bundle args, IBinder callback);
 
   // Asks the child service to crash so that we can test the termination logic.
-  void crashIntentionallyForTesting();
+  oneway void crashIntentionallyForTesting();
 }
diff --git a/base/files/file_util_win.cc b/base/files/file_util_win.cc
index 30749906..294726a 100644
--- a/base/files/file_util_win.cc
+++ b/base/files/file_util_win.cc
@@ -364,7 +364,6 @@
     }
   }
 
-  // Exist early if we can't create an unique name.
   if (!create_file_success) {
     DPLOG(WARNING) << "Failed to get temporary file name in "
                    << UTF16ToUTF8(dir.value());
diff --git a/base/mac/sdk_forward_declarations.h b/base/mac/sdk_forward_declarations.h
index b477347..86fac9c 100644
--- a/base/mac/sdk_forward_declarations.h
+++ b/base/mac/sdk_forward_declarations.h
@@ -144,6 +144,7 @@
 
 @interface NSView (YosemiteSDK)
 - (BOOL)isAccessibilitySelectorAllowed:(SEL)selector;
+@property(copy) NSString* accessibilityLabel;
 @end
 
 #endif  // MAC_OS_X_VERSION_10_10
diff --git a/base/metrics/bucket_ranges.h b/base/metrics/bucket_ranges.h
index c356195..db82e552 100644
--- a/base/metrics/bucket_ranges.h
+++ b/base/metrics/bucket_ranges.h
@@ -24,6 +24,7 @@
 
 #include <limits.h>
 
+#include "base/atomicops.h"
 #include "base/base_export.h"
 #include "base/macros.h"
 #include "base/metrics/histogram_base.h"
@@ -58,6 +59,17 @@
   // Return true iff |other| object has same ranges_ as |this| object's ranges_.
   bool Equals(const BucketRanges* other) const;
 
+  // Set and get a reference into persistent memory where this bucket data
+  // can be found (and re-used). These calls are internally atomic with no
+  // safety against overwriting an existing value since though it is wasteful
+  // to have multiple identical persistent records, it is still safe.
+  void set_persistent_reference(uint32_t ref) const {
+    subtle::NoBarrier_Store(&persistent_reference_, ref);
+  }
+  uint32_t persistent_reference() const {
+    return subtle::NoBarrier_Load(&persistent_reference_);
+  }
+
  private:
   // A monotonically increasing list of values which determine which bucket to
   // put a sample into.  For each index, show the smallest sample that can be
@@ -71,6 +83,12 @@
   // noise on UMA dashboard.
   uint32_t checksum_;
 
+  // A reference into a global PersistentMemoryAllocator where the ranges
+  // information is stored. This allows for the record to be created once and
+  // re-used simply by having all histograms with the same ranges use the
+  // same reference.
+  mutable subtle::Atomic32 persistent_reference_ = 0;
+
   DISALLOW_COPY_AND_ASSIGN(BucketRanges);
 };
 
diff --git a/base/metrics/persistent_histogram_allocator.cc b/base/metrics/persistent_histogram_allocator.cc
index 5f44b673..b2dae99 100644
--- a/base/metrics/persistent_histogram_allocator.cc
+++ b/base/metrics/persistent_histogram_allocator.cc
@@ -340,24 +340,49 @@
       return nullptr;
     }
 
-    size_t ranges_count = bucket_count + 1;
-    size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample);
+    // Since the StasticsRecorder keeps a global collection of BucketRanges
+    // objects for re-use, it would be dangerous for one to hold a reference
+    // from a persistent allocator that is not the global one (which is
+    // permanent once set). If this stops being the case, this check can
+    // become an "if" condition beside "!ranges_ref" below and before
+    // set_persistent_reference() farther down.
+    DCHECK_EQ(this, GlobalHistogramAllocator::Get());
+
+    // Re-use an existing BucketRanges persistent allocation if one is known;
+    // otherwise, create one.
+    PersistentMemoryAllocator::Reference ranges_ref =
+        bucket_ranges->persistent_reference();
+    if (!ranges_ref) {
+      size_t ranges_count = bucket_count + 1;
+      size_t ranges_bytes = ranges_count * sizeof(HistogramBase::Sample);
+      ranges_ref =
+          memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
+      if (ranges_ref) {
+        HistogramBase::Sample* ranges_data =
+            memory_allocator_->GetAsArray<HistogramBase::Sample>(
+                ranges_ref, kTypeIdRangesArray, ranges_count);
+        if (ranges_data) {
+          for (size_t i = 0; i < bucket_ranges->size(); ++i)
+            ranges_data[i] = bucket_ranges->range(i);
+          bucket_ranges->set_persistent_reference(ranges_ref);
+        } else {
+          // This should never happen but be tolerant if it does.
+          NOTREACHED();
+          ranges_ref = PersistentMemoryAllocator::kReferenceNull;
+        }
+      }
+    } else {
+      DCHECK_EQ(kTypeIdRangesArray, memory_allocator_->GetType(ranges_ref));
+    }
+
     PersistentMemoryAllocator::Reference counts_ref =
         memory_allocator_->Allocate(counts_bytes, kTypeIdCountsArray);
-    PersistentMemoryAllocator::Reference ranges_ref =
-        memory_allocator_->Allocate(ranges_bytes, kTypeIdRangesArray);
-    HistogramBase::Sample* ranges_data =
-        memory_allocator_->GetAsArray<HistogramBase::Sample>(
-            ranges_ref, kTypeIdRangesArray, ranges_count);
 
     // Only continue here if all allocations were successful. If they weren't,
     // there is no way to free the space but that's not really a problem since
     // the allocations only fail because the space is full or corrupt and so
     // any future attempts will also fail.
-    if (counts_ref && ranges_data && histogram_data) {
-      for (size_t i = 0; i < bucket_ranges->size(); ++i)
-        ranges_data[i] = bucket_ranges->range(i);
-
+    if (counts_ref && ranges_ref && histogram_data) {
       histogram_data->minimum = minimum;
       histogram_data->maximum = maximum;
       // |bucket_count| must fit within 32-bits or the allocation of the counts
diff --git a/base/metrics/persistent_histogram_allocator_unittest.cc b/base/metrics/persistent_histogram_allocator_unittest.cc
index df250a37..cfd20a8 100644
--- a/base/metrics/persistent_histogram_allocator_unittest.cc
+++ b/base/metrics/persistent_histogram_allocator_unittest.cc
@@ -281,4 +281,41 @@
   EXPECT_EQ(1, snapshot->GetCount(7));
 }
 
+TEST_F(PersistentHistogramAllocatorTest, RangesDeDuplication) {
+  // This corresponds to the "ranges_ref" field of the PersistentHistogramData
+  // structure defined (privately) inside persistent_histogram_allocator.cc.
+  const int kRangesRefIndex = 5;
+
+  // Create two histograms with the same ranges.
+  HistogramBase* histogram1 =
+      Histogram::FactoryGet("TestHistogram1", 1, 1000, 10, 0);
+  HistogramBase* histogram2 =
+      Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, 0);
+  const uint32_t ranges_ref = static_cast<Histogram*>(histogram1)
+                                  ->bucket_ranges()
+                                  ->persistent_reference();
+  ASSERT_NE(0U, ranges_ref);
+  EXPECT_EQ(ranges_ref, static_cast<Histogram*>(histogram2)
+                            ->bucket_ranges()
+                            ->persistent_reference());
+
+  // Make sure that the persistent data record is also correct. Two histograms
+  // will be fetched; other allocations are not "iterable".
+  PersistentMemoryAllocator::Iterator iter(allocator_);
+  uint32_t type;
+  uint32_t ref1 = iter.GetNext(&type);
+  uint32_t ref2 = iter.GetNext(&type);
+  EXPECT_EQ(0U, iter.GetNext(&type));
+  EXPECT_NE(0U, ref1);
+  EXPECT_NE(0U, ref2);
+  EXPECT_NE(ref1, ref2);
+
+  uint32_t* data1 =
+      allocator_->GetAsArray<uint32_t>(ref1, 0, kRangesRefIndex + 1);
+  uint32_t* data2 =
+      allocator_->GetAsArray<uint32_t>(ref2, 0, kRangesRefIndex + 1);
+  EXPECT_EQ(ranges_ref, data1[kRangesRefIndex]);
+  EXPECT_EQ(ranges_ref, data2[kRangesRefIndex]);
+}
+
 }  // namespace base
diff --git a/base/metrics/statistics_recorder.cc b/base/metrics/statistics_recorder.cc
index ba2101b..409e637 100644
--- a/base/metrics/statistics_recorder.cc
+++ b/base/metrics/statistics_recorder.cc
@@ -431,8 +431,24 @@
 
 // static
 void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) {
-  if (histograms_)
-    histograms_->erase(name);
+  if (!histograms_)
+    return;
+
+  HistogramMap::iterator found = histograms_->find(name);
+  if (found == histograms_->end())
+    return;
+
+  HistogramBase* base = found->second;
+  if (base->GetHistogramType() != SPARSE_HISTOGRAM) {
+    // When forgetting a histogram, it's likely that other information is
+    // also becoming invalid. Clear the persistent reference that may no
+    // longer be valid. There's no danger in this as, at worst, duplicates
+    // will be created in persistent memory.
+    Histogram* histogram = static_cast<Histogram*>(base);
+    histogram->bucket_ranges()->set_persistent_reference(0);
+  }
+
+  histograms_->erase(found);
 }
 
 // static
diff --git a/blink/OWNERS b/blink/OWNERS
new file mode 100644
index 0000000..79837a2
--- /dev/null
+++ b/blink/OWNERS
@@ -0,0 +1,3 @@
+file://third_party/WebKit/API_OWNERS
+
+# COMPONENT: Blink
diff --git a/blink/tools/OWNERS b/blink/tools/OWNERS
index 4fc050a..5c5aa17 100644
--- a/blink/tools/OWNERS
+++ b/blink/tools/OWNERS
@@ -1,3 +1,9 @@
-ojan@chromium.org
 dpranke@chromium.org
-*
+jeffcarp@chromium.org
+qyearsley@chromium.org
+tansell@chromium.org
+tkent@chromium.org
+wangxianzhu@chromium.org
+
+# TEAM: blink-infra@chromium.org
+# COMPONENT: Blink>Infra
diff --git a/build/experimental/install-build-deps.py b/build/experimental/install-build-deps.py
index 1687b94..04a26ab 100755
--- a/build/experimental/install-build-deps.py
+++ b/build/experimental/install-build-deps.py
@@ -13,7 +13,6 @@
 
 
 SUPPORTED_UBUNTU_VERSIONS = (
-  {'number': '12.04', 'codename': 'precise'},
   {'number': '14.04', 'codename': 'trusty'},
   {'number': '14.10', 'codename': 'utopic'},
   {'number': '15.04', 'codename': 'vivid'},
@@ -300,9 +299,7 @@
   lsb_codename = lsb_release_short_codename()
 
   # Find the proper version of libstdc++6-4.x-dbg.
-  if lsb_codename == 'precise':
-    _packages_dbg += ('libstdc++6-4.6-dbg',)
-  elif lsb_codename == 'trusty':
+  if lsb_codename == 'trusty':
     _packages_dbg += ('libstdc++6-4.8-dbg',)
   else:
     _packages_dbg += ('libstdc++6-4.9-dbg',)
diff --git a/build/install-build-deps-android.sh b/build/install-build-deps-android.sh
index cbf9931..06f79ae 100755
--- a/build/install-build-deps-android.sh
+++ b/build/install-build-deps-android.sh
@@ -32,11 +32,7 @@
 
 # Some binaries in the Android SDK require 32-bit libraries on the host.
 # See https://developer.android.com/sdk/installing/index.html?pkg=tools
-if [[ $lsb_release == "precise" ]]; then
-  sudo apt-get -y install ia32-libs
-else
-  sudo apt-get -y install libncurses5:i386 libstdc++6:i386 zlib1g:i386
-fi
+sudo apt-get -y install libncurses5:i386 libstdc++6:i386 zlib1g:i386
 
 # Required by //components/cronet/tools/generate_javadoc.py
 # TODO(375324): Stop requiring ANT.
diff --git a/build/linux/sysroot_scripts/install-sysroot.py b/build/linux/sysroot_scripts/install-sysroot.py
index c8e88ee..90b7068 100755
--- a/build/linux/sysroot_scripts/install-sysroot.py
+++ b/build/linux/sysroot_scripts/install-sysroot.py
@@ -122,12 +122,6 @@
   if host_arch == 'amd64':
     InstallDefaultSysrootForArch('i386')
 
-  # Desktop Chromium OS builds require the precise sysroot.
-  # TODO(thomasanderson): only download this when the GN arg target_os
-  # == 'chromeos', when the functionality to perform the check becomes
-  # available.
-  InstallSysroot('Precise', 'amd64')
-
   # If we can detect a non-standard target_arch such as ARM or MIPS,
   # then install the sysroot too.  Don't attempt to install arm64
   # since this is currently and android-only architecture.
diff --git a/build/linux/sysroot_scripts/packagelist.precise.amd64 b/build/linux/sysroot_scripts/packagelist.precise.amd64
deleted file mode 100644
index 8e6fc70..0000000
--- a/build/linux/sysroot_scripts/packagelist.precise.amd64
+++ /dev/null
@@ -1,179 +0,0 @@
-main/a/alsa-lib/libasound2_1.0.25-1ubuntu10.2_amd64.deb
-main/a/alsa-lib/libasound2-dev_1.0.25-1ubuntu10.2_amd64.deb
-main/a/atk1.0/libatk1.0-0_2.4.0-0ubuntu1_amd64.deb
-main/a/atk1.0/libatk1.0-dev_2.4.0-0ubuntu1_amd64.deb
-main/a/avahi/libavahi-client3_0.6.30-5ubuntu2.2_amd64.deb
-main/a/avahi/libavahi-common3_0.6.30-5ubuntu2.2_amd64.deb
-main/b/bluez/libbluetooth3_4.98-2ubuntu7.2_amd64.deb
-main/b/bluez/libbluetooth-dev_4.98-2ubuntu7.2_amd64.deb
-main/b/brltty/libbrlapi0.5_4.3-1ubuntu5_amd64.deb
-main/b/brltty/libbrlapi-dev_4.3-1ubuntu5_amd64.deb
-main/c/cairo/libcairo2_1.10.2-6.1ubuntu3_amd64.deb
-main/c/cairo/libcairo2-dev_1.10.2-6.1ubuntu3_amd64.deb
-main/c/cairo/libcairo-gobject2_1.10.2-6.1ubuntu3_amd64.deb
-main/c/cairo/libcairo-script-interpreter2_1.10.2-6.1ubuntu3_amd64.deb
-main/c/cups/libcups2_1.5.3-0ubuntu8.7_amd64.deb
-main/c/cups/libcups2-dev_1.5.3-0ubuntu8.7_amd64.deb
-main/d/dbus-glib/libdbus-glib-1-2_0.98-1ubuntu1.1_amd64.deb
-main/d/dbus/libdbus-1-3_1.4.18-1ubuntu1.8_amd64.deb
-main/d/dbus/libdbus-1-dev_1.4.18-1ubuntu1.8_amd64.deb
-main/e/e2fsprogs/comerr-dev_2.1-1.42-1ubuntu2.3_amd64.deb
-main/e/e2fsprogs/libcomerr2_1.42-1ubuntu2.3_amd64.deb
-main/e/eglibc/libc6_2.15-0ubuntu10.15_amd64.deb
-main/e/eglibc/libc6-dev_2.15-0ubuntu10.15_amd64.deb
-main/e/elfutils/libelf1_0.152-1ubuntu3.1_amd64.deb
-main/e/elfutils/libelf-dev_0.152-1ubuntu3.1_amd64.deb
-main/e/expat/libexpat1_2.0.1-7.2ubuntu1.4_amd64.deb
-main/e/expat/libexpat1-dev_2.0.1-7.2ubuntu1.4_amd64.deb
-main/f/fontconfig/libfontconfig1_2.8.0-3ubuntu9.2_amd64.deb
-main/f/fontconfig/libfontconfig1-dev_2.8.0-3ubuntu9.2_amd64.deb
-main/f/freetype/libfreetype6_2.4.8-1ubuntu2.3_amd64.deb
-main/f/freetype/libfreetype6-dev_2.4.8-1ubuntu2.3_amd64.deb
-main/g/gcc-4.6/gcc-4.6_4.6.3-1ubuntu5_amd64.deb
-main/g/gcc-4.6/libgcc1_4.6.3-1ubuntu5_amd64.deb
-main/g/gcc-4.6/libgomp1_4.6.3-1ubuntu5_amd64.deb
-main/g/gcc-4.6/libquadmath0_4.6.3-1ubuntu5_amd64.deb
-main/g/gcc-4.6/libstdc++6_4.6.3-1ubuntu5_amd64.deb
-main/g/gcc-4.6/libstdc++6-4.6-dev_4.6.3-1ubuntu5_amd64.deb
-main/g/gconf/libgconf-2-4_3.2.5-0ubuntu2_amd64.deb
-main/g/gconf/libgconf2-4_3.2.5-0ubuntu2_amd64.deb
-main/g/gconf/libgconf2-dev_3.2.5-0ubuntu2_amd64.deb
-main/g/gdk-pixbuf/libgdk-pixbuf2.0-0_2.26.1-1ubuntu1.5_amd64.deb
-main/g/gdk-pixbuf/libgdk-pixbuf2.0-dev_2.26.1-1ubuntu1.5_amd64.deb
-main/g/glib2.0/libglib2.0-0_2.32.4-0ubuntu1_amd64.deb
-main/g/glib2.0/libglib2.0-dev_2.32.4-0ubuntu1_amd64.deb
-main/g/gnutls26/libgnutls26_2.12.14-5ubuntu3.13_amd64.deb
-main/g/gnutls26/libgnutls-dev_2.12.14-5ubuntu3.13_amd64.deb
-main/g/gnutls26/libgnutls-openssl27_2.12.14-5ubuntu3.13_amd64.deb
-main/g/gnutls26/libgnutlsxx27_2.12.14-5ubuntu3.13_amd64.deb
-main/g/gtk+2.0/libgtk2.0-0_2.24.10-0ubuntu6.3_amd64.deb
-main/g/gtk+2.0/libgtk2.0-dev_2.24.10-0ubuntu6.3_amd64.deb
-main/g/gtk+3.0/libgtk-3-0_3.4.2-0ubuntu0.9_amd64.deb
-main/g/gtk+3.0/libgtk-3-dev_3.4.2-0ubuntu0.9_amd64.deb
-main/k/keyutils/libkeyutils1_1.5.2-2_amd64.deb
-main/k/krb5/krb5-multidev_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libgssapi-krb5-2_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libgssrpc4_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libk5crypto3_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkadm5clnt-mit8_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkadm5srv-mit8_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkdb5-6_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkrb5-3_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkrb5-dev_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/k/krb5/libkrb5support0_1.10+dfsg~beta1-2ubuntu0.7_amd64.deb
-main/libc/libcap2/libcap2_2.22-1ubuntu3_amd64.deb
-main/libc/libcap2/libcap-dev_2.22-1ubuntu3_amd64.deb
-main/libd/libdrm/libdrm2_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libdrm-dev_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libdrm-intel1_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libdrm-nouveau1a_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libdrm-nouveau2_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libdrm-radeon1_2.4.52-1~precise2_amd64.deb
-main/libd/libdrm/libkms1_2.4.46-1ubuntu0.0.0.1_amd64.deb
-main/libf/libffi/libffi6_3.0.11~rc1-5_amd64.deb
-main/libf/libffi/libffi-dev_3.0.11~rc1-5_amd64.deb
-main/libg/libgcrypt11/libgcrypt11_1.5.0-3ubuntu0.6_amd64.deb
-main/libg/libgcrypt11/libgcrypt11-dev_1.5.0-3ubuntu0.6_amd64.deb
-main/libg/libgnome-keyring/libgnome-keyring0_3.2.2-2_amd64.deb
-main/libg/libgnome-keyring/libgnome-keyring-dev_3.2.2-2_amd64.deb
-main/libg/libgpg-error/libgpg-error0_1.10-2ubuntu1_amd64.deb
-main/libg/libgpg-error/libgpg-error-dev_1.10-2ubuntu1_amd64.deb
-main/libn/libnss-db/libnss-db_2.2.3pre1-3.2ubuntu3_amd64.deb
-main/libp/libp11/libp11-2_0.2.8-2_amd64.deb
-main/libp/libpng/libpng12-0_1.2.46-3ubuntu4.2_amd64.deb
-main/libp/libpng/libpng12-dev_1.2.46-3ubuntu4.2_amd64.deb
-main/libp/libpthread-stubs/libpthread-stubs0-dev_0.3-3_amd64.deb
-main/libs/libselinux/libselinux1_2.1.0-4.1ubuntu1_amd64.deb
-main/libt/libtasn1-3/libtasn1-3_2.10-1ubuntu1.5_amd64.deb
-main/libx/libx11/libx11-6_1.4.99.1-0ubuntu2.3_amd64.deb
-main/libx/libx11/libx11-dev_1.4.99.1-0ubuntu2.3_amd64.deb
-main/libx/libx11/libx11-xcb1_1.4.99.1-0ubuntu2.3_amd64.deb
-main/libx/libx11/libx11-xcb-dev_1.4.99.1-0ubuntu2.3_amd64.deb
-main/libx/libxau/libxau6_1.0.6-4_amd64.deb
-main/libx/libxau/libxau-dev_1.0.6-4_amd64.deb
-main/libx/libxcb/libxcb1_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb1-dev_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb-glx0_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb-render0_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb-render0-dev_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb-shm0_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcb/libxcb-shm0-dev_1.8.1-1ubuntu0.2_amd64.deb
-main/libx/libxcomposite/libxcomposite1_0.4.3-2build1_amd64.deb
-main/libx/libxcomposite/libxcomposite-dev_0.4.3-2build1_amd64.deb
-main/libx/libxcursor/libxcursor1_1.1.12-1ubuntu0.1_amd64.deb
-main/libx/libxcursor/libxcursor-dev_1.1.12-1ubuntu0.1_amd64.deb
-main/libx/libxdamage/libxdamage1_1.1.3-2build1_amd64.deb
-main/libx/libxdamage/libxdamage-dev_1.1.3-2build1_amd64.deb
-main/libx/libxdmcp/libxdmcp6_1.1.0-4_amd64.deb
-main/libx/libxdmcp/libxdmcp-dev_1.1.0-4_amd64.deb
-main/libx/libxext/libxext6_1.3.0-3ubuntu0.2_amd64.deb
-main/libx/libxext/libxext-dev_1.3.0-3ubuntu0.2_amd64.deb
-main/libx/libxfixes/libxfixes3_5.0-4ubuntu4.4_amd64.deb
-main/libx/libxfixes/libxfixes-dev_5.0-4ubuntu4.4_amd64.deb
-main/libx/libxi/libxi6_1.7.1.901-1ubuntu1~precise3_amd64.deb
-main/libx/libxi/libxi-dev_1.7.1.901-1ubuntu1~precise3_amd64.deb
-main/libx/libxinerama/libxinerama1_1.1.1-3ubuntu0.1_amd64.deb
-main/libx/libxinerama/libxinerama-dev_1.1.1-3ubuntu0.1_amd64.deb
-main/libx/libxrandr/libxrandr2_1.3.2-2ubuntu0.3_amd64.deb
-main/libx/libxrandr/libxrandr-dev_1.3.2-2ubuntu0.3_amd64.deb
-main/libx/libxrender/libxrender1_0.9.6-2ubuntu0.2_amd64.deb
-main/libx/libxrender/libxrender-dev_0.9.6-2ubuntu0.2_amd64.deb
-main/libx/libxss/libxss1_1.2.1-2_amd64.deb
-main/libx/libxss/libxss-dev_1.2.1-2_amd64.deb
-main/libx/libxt/libxt6_1.1.1-2ubuntu0.1_amd64.deb
-main/libx/libxt/libxt-dev_1.1.1-2ubuntu0.1_amd64.deb
-main/libx/libxtst/libxtst6_1.2.0-4ubuntu0.1_amd64.deb
-main/libx/libxtst/libxtst-dev_1.2.0-4ubuntu0.1_amd64.deb
-main/libx/libxxf86vm/libxxf86vm1_1.1.1-2ubuntu0.1_amd64.deb
-main/l/linux/linux-libc-dev_3.2.0-123.166_amd64.deb
-main/m/mesa/libegl1-mesa_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libegl1-mesa-dev_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libegl1-mesa-drivers_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libgbm1_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libgbm-dev_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libgl1-mesa-dev_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libgl1-mesa-glx_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/libglapi-mesa_8.0.4-0ubuntu0.7_amd64.deb
-main/m/mesa/mesa-common-dev_8.0.4-0ubuntu0.7_amd64.deb
-main/n/nspr/libnspr4_4.12-0ubuntu0.12.04.1_amd64.deb
-main/n/nspr/libnspr4-dev_4.12-0ubuntu0.12.04.1_amd64.deb
-main/n/nss/libnss3_3.26.2-0ubuntu0.12.04.1_amd64.deb
-main/n/nss/libnss3-dev_3.26.2-0ubuntu0.12.04.1_amd64.deb
-main/o/openssl/libssl1.0.0_1.0.1-4ubuntu5.39_amd64.deb
-main/o/openssl/libssl-dev_1.0.1-4ubuntu5.39_amd64.deb
-main/o/orbit2/liborbit2_2.14.19-0.1ubuntu1_amd64.deb
-main/p/p11-kit/libp11-kit0_0.12-2ubuntu1_amd64.deb
-main/p/pam/libpam0g_1.1.3-7ubuntu2.3_amd64.deb
-main/p/pam/libpam0g-dev_1.1.3-7ubuntu2.3_amd64.deb
-main/p/pango1.0/libpango1.0-0_1.30.0-0ubuntu3.1_amd64.deb
-main/p/pango1.0/libpango1.0-dev_1.30.0-0ubuntu3.1_amd64.deb
-main/p/pciutils/libpci3_3.1.8-2ubuntu6_amd64.deb
-main/p/pciutils/libpci-dev_3.1.8-2ubuntu6_amd64.deb
-main/p/pcre3/libpcre3_8.12-4ubuntu0.2_amd64.deb
-main/p/pcre3/libpcre3-dev_8.12-4ubuntu0.2_amd64.deb
-main/p/pcre3/libpcrecpp0_8.12-4ubuntu0.2_amd64.deb
-main/p/pixman/libpixman-1-0_0.30.2-1ubuntu0.0.0.0.3_amd64.deb
-main/p/pixman/libpixman-1-dev_0.30.2-1ubuntu0.0.0.0.3_amd64.deb
-main/p/pulseaudio/libpulse0_1.1-0ubuntu15.4_amd64.deb
-main/p/pulseaudio/libpulse-dev_1.1-0ubuntu15.4_amd64.deb
-main/p/pulseaudio/libpulse-mainloop-glib0_1.1-0ubuntu15.4_amd64.deb
-main/s/speech-dispatcher/libspeechd2_0.7.1-6ubuntu3_amd64.deb
-main/s/speech-dispatcher/libspeechd-dev_0.7.1-6ubuntu3_amd64.deb
-main/s/speech-dispatcher/speech-dispatcher_0.7.1-6ubuntu3_amd64.deb
-main/w/wayland/libwayland0_0.85.0-1ubuntu2_amd64.deb
-main/w/wayland/libwayland-dev_0.85.0-1ubuntu2_amd64.deb
-main/x/x11proto-composite/x11proto-composite-dev_0.4.2-2_all.deb
-main/x/x11proto-core/x11proto-core-dev_7.0.22-1ubuntu0.2_all.deb
-main/x/x11proto-damage/x11proto-damage-dev_1.2.1-2_all.deb
-main/x/x11proto-fixes/x11proto-fixes-dev_5.0-2ubuntu1_all.deb
-main/x/x11proto-input/x11proto-input-dev_2.3-1~precise2_all.deb
-main/x/x11proto-kb/x11proto-kb-dev_1.0.5-2_all.deb
-main/x/x11proto-randr/x11proto-randr-dev_1.4.0+git20120101.is.really.1.4.0-0ubuntu1~precise2_all.deb
-main/x/x11proto-record/x11proto-record-dev_1.14.1-2_all.deb
-main/x/x11proto-render/x11proto-render-dev_0.11.1-2_all.deb
-main/x/x11proto-scrnsaver/x11proto-scrnsaver-dev_1.2.1-2_all.deb
-main/x/x11proto-xext/x11proto-xext-dev_7.3.0-1~precise2_all.deb
-main/z/zlib/zlib1g_1.2.3.4.dfsg-3ubuntu4_amd64.deb
-main/z/zlib/zlib1g-dev_1.2.3.4.dfsg-3ubuntu4_amd64.deb
-universe/libx/libxkbcommon/libxkbcommon0_0.1.0~1-0ubuntu1_amd64.deb
-universe/libx/libxkbcommon/libxkbcommon-dev_0.1.0~1-0ubuntu1_amd64.deb
diff --git a/build/linux/sysroot_scripts/sysroot-creator-precise.sh b/build/linux/sysroot_scripts/sysroot-creator-precise.sh
deleted file mode 100755
index 8c536b4a..0000000
--- a/build/linux/sysroot_scripts/sysroot-creator-precise.sh
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/bin/bash
-# Copyright 2016 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.
-
-SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
-
-DISTRO=ubuntu
-DIST=precise
-DIST_UPDATES=precise-updates
-REPO_EXTRA="universe"
-
-# This is where we get all the debian packages from.
-APT_REPO=http://archive.ubuntu.com/ubuntu
-APT_REPO_ARM=http://ports.ubuntu.com
-APT_REPO_ARM64=http://ports.ubuntu.com
-KEYRING_FILE=/usr/share/keyrings/ubuntu-archive-keyring.gpg
-
-HAS_ARCH_AMD64=1
-
-# Precise supports these architectures but they are not needed by chrome.
-# HAS_ARCH_I386=1
-# HAS_ARCH_ARM=1
-
-# Sysroot packages: these are the packages needed to build chrome.
-# NOTE: When DEBIAN_PACKAGES is modified, the packagelist files must be updated
-# by running this script in GeneratePackageList mode.
-DEBIAN_PACKAGES="\
-  comerr-dev
-  gcc-4.6
-  krb5-multidev
-  libasound2
-  libasound2-dev
-  libatk1.0-0
-  libatk1.0-dev
-  libavahi-client3
-  libavahi-common3
-  libbluetooth3
-  libbluetooth-dev
-  libbrlapi0.5
-  libbrlapi-dev
-  libc6
-  libc6-dev
-  libcairo2
-  libcairo2-dev
-  libcairo-gobject2
-  libcairo-script-interpreter2
-  libcap-dev
-  libcap2
-  libcomerr2
-  libcups2
-  libcups2-dev
-  libdbus-1-3
-  libdbus-1-dev
-  libdbus-glib-1-2
-  libdrm-dev
-  libdrm-intel1
-  libdrm-nouveau1a
-  libdrm-nouveau2
-  libdrm-radeon1
-  libdrm2
-  libegl1-mesa
-  libegl1-mesa-dev
-  libegl1-mesa-drivers
-  libelf1
-  libelf-dev
-  libexpat1
-  libexpat1-dev
-  libffi6
-  libffi-dev
-  libfontconfig1
-  libfontconfig1-dev
-  libfreetype6
-  libfreetype6-dev
-  libgbm1
-  libgbm-dev
-  libgcc1
-  libgconf-2-4
-  libgconf2-4
-  libgconf2-dev
-  libgcrypt11
-  libgcrypt11-dev
-  libgdk-pixbuf2.0-0
-  libgdk-pixbuf2.0-dev
-  libgl1-mesa-dev
-  libgl1-mesa-glx
-  libglapi-mesa
-  libglib2.0-0
-  libglib2.0-dev
-  libgnome-keyring0
-  libgnome-keyring-dev
-  libgnutls26
-  libgnutls-dev
-  libgnutls-openssl27
-  libgnutlsxx27
-  libgomp1
-  libgpg-error0
-  libgpg-error-dev
-  libgssapi-krb5-2
-  libgssrpc4
-  libgtk-3-0
-  libgtk-3-dev
-  libgtk2.0-0
-  libgtk2.0-dev
-  libk5crypto3
-  libkadm5clnt-mit8
-  libkadm5srv-mit8
-  libkdb5-6
-  libkeyutils1
-  libkms1
-  libkrb5-3
-  libkrb5-dev
-  libkrb5support0
-  libnspr4
-  libnspr4-dev
-  libnss3
-  libnss3-dev
-  libnss-db
-  liborbit2
-  libp11-2
-  libp11-kit0
-  libpam0g
-  libpam0g-dev
-  libpango1.0-0
-  libpango1.0-dev
-  libpci3
-  libpci-dev
-  libpcre3
-  libpcre3-dev
-  libpcrecpp0
-  libpixman-1-0
-  libpixman-1-dev
-  libpng12-0
-  libpng12-dev
-  libpthread-stubs0-dev
-  libpulse0
-  libpulse-dev
-  libpulse-mainloop-glib0
-  libselinux1
-  libspeechd2
-  libspeechd-dev
-  libssl1.0.0
-  libssl-dev
-  libstdc++6
-  libstdc++6-4.6-dev
-  libtasn1-3
-  libwayland0
-  libwayland-dev
-  libx11-6
-  libx11-dev
-  libx11-xcb1
-  libx11-xcb-dev
-  libxau6
-  libxau-dev
-  libxcb1
-  libxcb1-dev
-  libxcb-glx0
-  libxcb-render0
-  libxcb-render0-dev
-  libxcb-shm0
-  libxcb-shm0-dev
-  libxcomposite1
-  libxcomposite-dev
-  libxcursor1
-  libxcursor-dev
-  libxdamage1
-  libxdamage-dev
-  libxdmcp6
-  libxdmcp-dev
-  libxext6
-  libxext-dev
-  libxfixes3
-  libxfixes-dev
-  libxi6
-  libxi-dev
-  libxinerama1
-  libxinerama-dev
-  libxkbcommon0
-  libxkbcommon-dev
-  libxrandr2
-  libxrandr-dev
-  libxrender1
-  libxrender-dev
-  libxss1
-  libxss-dev
-  libxt6
-  libxt-dev
-  libxtst6
-  libxtst-dev
-  libxxf86vm1
-  linux-libc-dev
-  mesa-common-dev
-  speech-dispatcher
-  x11proto-composite-dev
-  x11proto-core-dev
-  x11proto-damage-dev
-  x11proto-fixes-dev
-  x11proto-input-dev
-  x11proto-kb-dev
-  x11proto-randr-dev
-  x11proto-record-dev
-  x11proto-render-dev
-  x11proto-scrnsaver-dev
-  x11proto-xext-dev
-  zlib1g
-  zlib1g-dev
-"
-
-DEBIAN_PACKAGES_X86="libquadmath0"
-DEBIAN_PACKAGES_ARM="libdrm-omap1"
-
-. "${SCRIPT_DIR}/sysroot-creator.sh"
diff --git a/build/linux/sysroot_scripts/sysroots.json b/build/linux/sysroot_scripts/sysroots.json
index d06cea0..d4b2f77 100644
--- a/build/linux/sysroot_scripts/sysroots.json
+++ b/build/linux/sysroot_scripts/sysroots.json
@@ -29,12 +29,6 @@
         "SysrootDir": "debian_jessie_mips-sysroot",
         "Tarball": "debian_jessie_mips_sysroot.tgz"
     },
-    "precise_amd64": {
-        "Revision": "d3d82f7c4e34a753953581a48e62ef577b334529",
-        "Sha1Sum": "8fffb717217b2dc4e29b3a1305877bcd0552f55e",
-        "SysrootDir": "ubuntu_precise_amd64-sysroot",
-        "Tarball": "ubuntu_precise_amd64_sysroot.tgz"
-    },
     "trusty_arm": {
         "Revision": "d3d82f7c4e34a753953581a48e62ef577b334529",
         "Sha1Sum": "f78eb929410b94cdf48276db82a7e7adcafcc277",
diff --git a/build/sanitizers/tsan_suppressions.cc b/build/sanitizers/tsan_suppressions.cc
index d9753ef8..74b802ae 100644
--- a/build/sanitizers/tsan_suppressions.cc
+++ b/build/sanitizers/tsan_suppressions.cc
@@ -246,6 +246,9 @@
 "race:net::(anonymous namespace)::ProxyResolverV8TracingImpl::RequestImpl"
 "::~RequestImpl()\n"
 
+// http://crbug.com/691029
+"deadlock:libGLX.so*\n"
+
 // End of suppressions.
 ;  // Please keep this semicolon.
 
diff --git a/cc/BUILD.gn b/cc/BUILD.gn
index 6ffaedd..48686b8 100644
--- a/cc/BUILD.gn
+++ b/cc/BUILD.gn
@@ -48,6 +48,7 @@
     "input/selection.h",
     "input/single_scrollbar_animation_controller_thinning.cc",
     "input/single_scrollbar_animation_controller_thinning.h",
+    "layers/append_quads_data.cc",
     "layers/append_quads_data.h",
     "layers/content_layer_client.h",
     "layers/draw_properties.cc",
diff --git a/cc/animation/element_animations_unittest.cc b/cc/animation/element_animations_unittest.cc
index c6cb2d27..75e69c9 100644
--- a/cc/animation/element_animations_unittest.cc
+++ b/cc/animation/element_animations_unittest.cc
@@ -3588,5 +3588,27 @@
   EXPECT_EQ(0u, host_impl_->ticking_players_for_testing().size());
 }
 
+TEST_F(ElementAnimationsTest, RemoveAndReAddPlayerToTicking) {
+  CreateTestLayer(false, false);
+  AttachTimelinePlayerLayer();
+  EXPECT_EQ(0u, host_->ticking_players_for_testing().size());
+
+  // Add an animation and ensure the player is in the host's ticking players.
+  // Remove the player using RemoveFromTicking().
+  player_->AddAnimation(CreateAnimation(
+      std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)),
+      2, TargetProperty::OPACITY));
+  ASSERT_EQ(1u, host_->ticking_players_for_testing().size());
+  player_->RemoveFromTicking();
+  ASSERT_EQ(0u, host_->ticking_players_for_testing().size());
+
+  // Ensure that adding a new animation will correctly update the ticking
+  // players list.
+  player_->AddAnimation(CreateAnimation(
+      std::unique_ptr<AnimationCurve>(new FakeFloatTransition(1.0, 1.f, 0.5f)),
+      2, TargetProperty::OPACITY));
+  EXPECT_EQ(1u, host_->ticking_players_for_testing().size());
+}
+
 }  // namespace
 }  // namespace cc
diff --git a/cc/ipc/BUILD.gn b/cc/ipc/BUILD.gn
index babff70..13e4325 100644
--- a/cc/ipc/BUILD.gn
+++ b/cc/ipc/BUILD.gn
@@ -54,6 +54,7 @@
     "render_pass.mojom",
     "returned_resource.mojom",
     "selection.mojom",
+    "shared_bitmap_manager.mojom",
     "shared_quad_state.mojom",
     "surface_id.mojom",
     "surface_info.mojom",
diff --git a/cc/ipc/cc_param_traits_macros.h b/cc/ipc/cc_param_traits_macros.h
index 8c65677d..39a4ecf 100644
--- a/cc/ipc/cc_param_traits_macros.h
+++ b/cc/ipc/cc_param_traits_macros.h
@@ -205,6 +205,7 @@
   IPC_STRUCT_TRAITS_MEMBER(selection)
   IPC_STRUCT_TRAITS_MEMBER(latency_info)
   IPC_STRUCT_TRAITS_MEMBER(referenced_surfaces)
+  IPC_STRUCT_TRAITS_MEMBER(embedded_surfaces)
   IPC_STRUCT_TRAITS_MEMBER(content_source_id)
   IPC_STRUCT_TRAITS_MEMBER(begin_frame_ack)
   IPC_STRUCT_TRAITS_MEMBER(frame_token)
diff --git a/cc/ipc/compositor_frame_metadata.mojom b/cc/ipc/compositor_frame_metadata.mojom
index eafa877..0b1a3d7 100644
--- a/cc/ipc/compositor_frame_metadata.mojom
+++ b/cc/ipc/compositor_frame_metadata.mojom
@@ -31,6 +31,7 @@
   Selection selection;
   array<ui.mojom.LatencyInfo> latency_info;
   array<SurfaceId> referenced_surfaces;
+  array<SurfaceId> embedded_surfaces;
   bool can_activate_before_dependencies;
   uint32 content_source_id;
   BeginFrameAck begin_frame_ack;
diff --git a/cc/ipc/compositor_frame_metadata_struct_traits.cc b/cc/ipc/compositor_frame_metadata_struct_traits.cc
index fae339a..ab79ed7 100644
--- a/cc/ipc/compositor_frame_metadata_struct_traits.cc
+++ b/cc/ipc/compositor_frame_metadata_struct_traits.cc
@@ -45,6 +45,7 @@
   return data.ReadSelection(&out->selection) &&
          data.ReadLatencyInfo(&out->latency_info) &&
          data.ReadReferencedSurfaces(&out->referenced_surfaces) &&
+         data.ReadEmbeddedSurfaces(&out->embedded_surfaces) &&
          data.ReadBeginFrameAck(&out->begin_frame_ack);
 }
 
diff --git a/cc/ipc/compositor_frame_metadata_struct_traits.h b/cc/ipc/compositor_frame_metadata_struct_traits.h
index 46bb130d..f3077ce 100644
--- a/cc/ipc/compositor_frame_metadata_struct_traits.h
+++ b/cc/ipc/compositor_frame_metadata_struct_traits.h
@@ -106,6 +106,11 @@
     return metadata.referenced_surfaces;
   }
 
+  static const std::vector<cc::SurfaceId>& embedded_surfaces(
+      const cc::CompositorFrameMetadata& metadata) {
+    return metadata.embedded_surfaces;
+  }
+
   static bool can_activate_before_dependencies(
       const cc::CompositorFrameMetadata& metadata) {
     return metadata.can_activate_before_dependencies;
diff --git a/cc/ipc/shared_bitmap_manager.mojom b/cc/ipc/shared_bitmap_manager.mojom
new file mode 100644
index 0000000..6b2d54dd
--- /dev/null
+++ b/cc/ipc/shared_bitmap_manager.mojom
@@ -0,0 +1,21 @@
+// Copyright 2017 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.
+
+module cc.mojom;
+
+import "gpu/ipc/common/mailbox.mojom";
+
+// This interface is used when allocating shared bitmap memory to be shared
+// with a display compositor.
+// This interface needs to be associated with the RenderMessageFilter interface
+// to prevent running into message ordering issues (CC trying to access a
+// shared bitmap before the registration message below made it to the display
+// compositor).
+interface SharedBitmapManager {
+  // Informs the display compositor that the child allocated a shared bitmap.
+  DidAllocateSharedBitmap(handle<shared_buffer> buffer, gpu.mojom.Mailbox id);
+
+  // Informs the display compositor that the child deleted a shared bitmap.
+  DidDeleteSharedBitmap(gpu.mojom.Mailbox id);
+};
diff --git a/cc/ipc/struct_traits_unittest.cc b/cc/ipc/struct_traits_unittest.cc
index 5115809..d132d29 100644
--- a/cc/ipc/struct_traits_unittest.cc
+++ b/cc/ipc/struct_traits_unittest.cc
@@ -374,6 +374,10 @@
   SurfaceId id(FrameSinkId(1234, 4321),
                LocalSurfaceId(5678, base::UnguessableToken::Create()));
   referenced_surfaces.push_back(id);
+  std::vector<SurfaceId> embedded_surfaces;
+  SurfaceId id2(FrameSinkId(4321, 1234),
+                LocalSurfaceId(8765, base::UnguessableToken::Create()));
+  embedded_surfaces.push_back(id2);
   uint32_t frame_token = 0xdeadbeef;
 
   CompositorFrameMetadata input;
@@ -397,6 +401,7 @@
   input.selection = selection;
   input.latency_info = latency_infos;
   input.referenced_surfaces = referenced_surfaces;
+  input.embedded_surfaces = embedded_surfaces;
   input.frame_token = frame_token;
 
   mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
@@ -429,6 +434,9 @@
   EXPECT_EQ(referenced_surfaces.size(), output.referenced_surfaces.size());
   for (uint32_t i = 0; i < referenced_surfaces.size(); ++i)
     EXPECT_EQ(referenced_surfaces[i], output.referenced_surfaces[i]);
+  EXPECT_EQ(embedded_surfaces.size(), output.embedded_surfaces.size());
+  for (uint32_t i = 0; i < embedded_surfaces.size(); ++i)
+    EXPECT_EQ(embedded_surfaces[i], output.embedded_surfaces[i]);
   EXPECT_EQ(frame_token, output.frame_token);
 }
 
diff --git a/cc/layers/append_quads_data.cc b/cc/layers/append_quads_data.cc
new file mode 100644
index 0000000..63d6d17
--- /dev/null
+++ b/cc/layers/append_quads_data.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "cc/layers/append_quads_data.h"
+
+namespace cc {
+
+AppendQuadsData::AppendQuadsData() = default;
+
+AppendQuadsData::~AppendQuadsData() = default;
+
+}  // namespace cc
diff --git a/cc/layers/append_quads_data.h b/cc/layers/append_quads_data.h
index cf17b5b..45afa469 100644
--- a/cc/layers/append_quads_data.h
+++ b/cc/layers/append_quads_data.h
@@ -6,11 +6,19 @@
 #define CC_LAYERS_APPEND_QUADS_DATA_H_
 
 #include <stdint.h>
+#include <vector>
+
+#include "cc/cc_export.h"
+#include "cc/surfaces/surface_id.h"
 
 namespace cc {
 
 // Set by the layer appending quads.
-struct AppendQuadsData {
+class CC_EXPORT AppendQuadsData {
+ public:
+  AppendQuadsData();
+  ~AppendQuadsData();
+
   int64_t num_incomplete_tiles = 0;
   int64_t num_missing_tiles = 0;
   int64_t visible_layer_area = 0;
@@ -22,6 +30,8 @@
   int64_t checkerboarded_no_recording_content_area = 0;
   // This is the area within interest rect.
   int64_t checkerboarded_needs_raster_content_area = 0;
+  // This is the set of surface IDs embedded in SurfaceDrawQuads.
+  std::vector<SurfaceId> embedded_surfaces;
 };
 
 }  // namespace cc
diff --git a/cc/layers/layer_impl.h b/cc/layers/layer_impl.h
index a25a7d4..a8181a66 100644
--- a/cc/layers/layer_impl.h
+++ b/cc/layers/layer_impl.h
@@ -51,6 +51,7 @@
 
 namespace cc {
 
+class AppendQuadsData;
 class LayerTreeImpl;
 class MicroBenchmarkImpl;
 class MutatorHost;
@@ -60,7 +61,6 @@
 class SimpleEnclosedRegion;
 class Tile;
 
-struct AppendQuadsData;
 
 enum DrawMode {
   DRAW_MODE_NONE,
diff --git a/cc/layers/picture_layer_impl.h b/cc/layers/picture_layer_impl.h
index 306c602..0e85d8d 100644
--- a/cc/layers/picture_layer_impl.h
+++ b/cc/layers/picture_layer_impl.h
@@ -23,7 +23,7 @@
 
 namespace cc {
 
-struct AppendQuadsData;
+class AppendQuadsData;
 class MicroBenchmarkImpl;
 class Tile;
 
diff --git a/cc/layers/render_surface_impl.h b/cc/layers/render_surface_impl.h
index 7ccc3df..5cf15e8 100644
--- a/cc/layers/render_surface_impl.h
+++ b/cc/layers/render_surface_impl.h
@@ -24,14 +24,13 @@
 
 namespace cc {
 
+class AppendQuadsData;
 class DamageTracker;
 class FilterOperations;
 class Occlusion;
 class LayerImpl;
 class LayerTreeImpl;
 
-struct AppendQuadsData;
-
 class CC_EXPORT RenderSurfaceImpl {
  public:
   RenderSurfaceImpl(LayerTreeImpl* layer_tree_impl, int stable_effect_id);
diff --git a/cc/layers/surface_layer_impl.cc b/cc/layers/surface_layer_impl.cc
index 167cd71..ed5b443a 100644
--- a/cc/layers/surface_layer_impl.cc
+++ b/cc/layers/surface_layer_impl.cc
@@ -8,6 +8,7 @@
 
 #include "base/trace_event/trace_event_argument.h"
 #include "cc/debug/debug_colors.h"
+#include "cc/layers/append_quads_data.h"
 #include "cc/quads/solid_color_draw_quad.h"
 #include "cc/quads/surface_draw_quad.h"
 #include "cc/trees/layer_tree_impl.h"
@@ -65,19 +66,22 @@
                                    AppendQuadsData* append_quads_data) {
   AppendRainbowDebugBorder(render_pass);
   auto* primary = CreateSurfaceDrawQuad(
-      render_pass, SurfaceDrawQuadType::PRIMARY, primary_surface_info_);
+      render_pass, SurfaceDrawQuadType::PRIMARY, primary_surface_info_,
+      &append_quads_data->embedded_surfaces);
   // Emitting a fallback SurfaceDrawQuad is unnecessary if the primary and
   // fallback surface Ids match.
   if (primary && fallback_surface_info_.id() != primary_surface_info_.id()) {
     primary->fallback_quad = CreateSurfaceDrawQuad(
-        render_pass, SurfaceDrawQuadType::FALLBACK, fallback_surface_info_);
+        render_pass, SurfaceDrawQuadType::FALLBACK, fallback_surface_info_,
+        &append_quads_data->embedded_surfaces);
   }
 }
 
 SurfaceDrawQuad* SurfaceLayerImpl::CreateSurfaceDrawQuad(
     RenderPass* render_pass,
     SurfaceDrawQuadType surface_draw_quad_type,
-    const SurfaceInfo& surface_info) {
+    const SurfaceInfo& surface_info,
+    std::vector<SurfaceId>* embedded_surfaces) {
   if (!surface_info.is_valid())
     return nullptr;
 
@@ -117,6 +121,8 @@
       render_pass->CreateAndAppendDrawQuad<SurfaceDrawQuad>();
   surface_draw_quad->SetNew(shared_quad_state, quad_rect, visible_quad_rect,
                             surface_info.id(), surface_draw_quad_type, nullptr);
+  embedded_surfaces->push_back(surface_info.id());
+
   return surface_draw_quad;
 }
 
diff --git a/cc/layers/surface_layer_impl.h b/cc/layers/surface_layer_impl.h
index a5bb8c5..9b853df5 100644
--- a/cc/layers/surface_layer_impl.h
+++ b/cc/layers/surface_layer_impl.h
@@ -50,7 +50,8 @@
   SurfaceDrawQuad* CreateSurfaceDrawQuad(
       RenderPass* render_pass,
       SurfaceDrawQuadType surface_draw_quad_type,
-      const SurfaceInfo& surface_info);
+      const SurfaceInfo& surface_info,
+      std::vector<SurfaceId>* embedded_surfaces);
 
   void GetDebugBorderProperties(SkColor* color, float* width) const override;
   void AppendRainbowDebugBorder(RenderPass* render_pass);
diff --git a/cc/layers/surface_layer_impl_unittest.cc b/cc/layers/surface_layer_impl_unittest.cc
index 1b41a538..a526a516 100644
--- a/cc/layers/surface_layer_impl_unittest.cc
+++ b/cc/layers/surface_layer_impl_unittest.cc
@@ -9,8 +9,11 @@
 #include "cc/layers/append_quads_data.h"
 #include "cc/test/layer_test_common.h"
 #include "cc/trees/layer_tree_host_common.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::UnorderedElementsAre;
+
 namespace cc {
 namespace {
 
@@ -168,6 +171,7 @@
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
   surface_layer_impl->AppendQuads(render_pass.get(), &data);
+  EXPECT_THAT(data.embedded_surfaces, UnorderedElementsAre(surface_id));
 
   const QuadList& quads = render_pass->quad_list;
   ASSERT_EQ(1u, quads.size());
@@ -236,6 +240,8 @@
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
   surface_layer_impl->AppendQuads(render_pass.get(), &data);
+  EXPECT_THAT(data.embedded_surfaces,
+              UnorderedElementsAre(surface_id1, surface_id2));
 
   ASSERT_EQ(2u, render_pass->quad_list.size());
   const SurfaceDrawQuad* surface_draw_quad1 =
@@ -286,6 +292,7 @@
   std::unique_ptr<RenderPass> render_pass = RenderPass::Create();
   AppendQuadsData data;
   surface_layer_impl->AppendQuads(render_pass.get(), &data);
+  EXPECT_THAT(data.embedded_surfaces, UnorderedElementsAre(surface_id1));
 
   ASSERT_EQ(1u, render_pass->quad_list.size());
   const SurfaceDrawQuad* surface_draw_quad1 =
diff --git a/cc/output/compositor_frame_metadata.h b/cc/output/compositor_frame_metadata.h
index 98bed56..a3ebfc2 100644
--- a/cc/output/compositor_frame_metadata.h
+++ b/cc/output/compositor_frame_metadata.h
@@ -77,8 +77,17 @@
   std::vector<ui::LatencyInfo> latency_info;
 
   // This is the set of Surfaces that are referenced by this frame.
+  // Note: this includes occluded and clipped surfaces and surfaces that may
+  // be accessed by this CompositorFrame in the future.
+  // TODO(fsamuel): In the future, a generalized frame eviction system will
+  // determine which surfaces to retain and which to evict. It will likely
+  // be unnecessary for the embedder to explicitly specify which surfaces to
+  // retain. Thus, this field will likely go away.
   std::vector<SurfaceId> referenced_surfaces;
 
+  // This is the set of SurfaceIds embedded in DrawQuads.
+  std::vector<SurfaceId> embedded_surfaces;
+
   // This indicates whether this CompositorFrame can be activated before
   // dependencies have been resolved.
   bool can_activate_before_dependencies = true;
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h
index 8796447..daacc301 100644
--- a/cc/paint/paint_canvas.h
+++ b/cc/paint/paint_canvas.h
@@ -34,13 +34,6 @@
   virtual void flush() = 0;
 
   virtual SkISize getBaseLayerSize() const = 0;
-  virtual bool readPixels(const SkImageInfo& dest_info,
-                          void* dest_pixels,
-                          size_t dest_row_bytes,
-                          int src_x,
-                          int src_y) = 0;
-  virtual bool readPixels(SkBitmap* bitmap, int src_x, int src_y) = 0;
-  virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) = 0;
   virtual bool writePixels(const SkImageInfo& info,
                            const void* pixels,
                            size_t row_bytes,
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc
index 35515bab6..94b0cfb7 100644
--- a/cc/paint/skia_paint_canvas.cc
+++ b/cc/paint/skia_paint_canvas.cc
@@ -42,23 +42,6 @@
   return canvas_->getBaseLayerSize();
 }
 
-bool SkiaPaintCanvas::readPixels(const SkImageInfo& dest_info,
-                                 void* dest_pixels,
-                                 size_t dest_row_bytes,
-                                 int src_x,
-                                 int src_y) {
-  return canvas_->readPixels(dest_info, dest_pixels, dest_row_bytes, src_x,
-                             src_y);
-}
-
-bool SkiaPaintCanvas::readPixels(SkBitmap* bitmap, int src_x, int src_y) {
-  return canvas_->readPixels(bitmap, src_x, src_y);
-}
-
-bool SkiaPaintCanvas::readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
-  return canvas_->readPixels(srcRect, bitmap);
-}
-
 bool SkiaPaintCanvas::writePixels(const SkImageInfo& info,
                                   const void* pixels,
                                   size_t row_bytes,
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h
index 8d2be88..669ca9b 100644
--- a/cc/paint/skia_paint_canvas.h
+++ b/cc/paint/skia_paint_canvas.h
@@ -39,13 +39,6 @@
   void flush() override;
 
   SkISize getBaseLayerSize() const override;
-  bool readPixels(const SkImageInfo& dest_info,
-                  void* dest_pixels,
-                  size_t dest_row_bytes,
-                  int src_x,
-                  int src_y) override;
-  bool readPixels(SkBitmap* bitmap, int src_x, int src_y) override;
-  bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) override;
   bool writePixels(const SkImageInfo& info,
                    const void* pixels,
                    size_t row_bytes,
diff --git a/cc/surfaces/compositor_frame_sink_support_unittest.cc b/cc/surfaces/compositor_frame_sink_support_unittest.cc
index 44771bf..319471a 100644
--- a/cc/surfaces/compositor_frame_sink_support_unittest.cc
+++ b/cc/surfaces/compositor_frame_sink_support_unittest.cc
@@ -71,32 +71,43 @@
       LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
 }
 
-CompositorFrame MakeCompositorFrame() {
+CompositorFrame MakeCompositorFrame(std::vector<SurfaceId> embedded_surfaces,
+                                    std::vector<SurfaceId> referenced_surfaces,
+                                    TransferableResourceArray resource_list) {
   CompositorFrame compositor_frame;
   compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true);
-  return compositor_frame;
-}
-
-CompositorFrame MakeCompositorFrame(
-    std::vector<SurfaceId> referenced_surfaces) {
-  CompositorFrame compositor_frame;
-  compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true);
-  compositor_frame.metadata.referenced_surfaces =
-      std::move(referenced_surfaces);
-  return compositor_frame;
-}
-
-CompositorFrame MakeCompositorFrameWithResources(
-    std::vector<SurfaceId> referenced_surfaces,
-    TransferableResourceArray resource_list) {
-  CompositorFrame compositor_frame;
-  compositor_frame.metadata.begin_frame_ack = BeginFrameAck(0, 1, 1, true);
+  compositor_frame.metadata.embedded_surfaces = std::move(embedded_surfaces);
   compositor_frame.metadata.referenced_surfaces =
       std::move(referenced_surfaces);
   compositor_frame.resource_list = std::move(resource_list);
   return compositor_frame;
 }
 
+CompositorFrame MakeCompositorFrame() {
+  return MakeCompositorFrame(empty_surface_ids(), empty_surface_ids(),
+                             TransferableResourceArray());
+}
+
+CompositorFrame MakeCompositorFrame(std::vector<SurfaceId> embedded_surfaces) {
+  return MakeCompositorFrame(embedded_surfaces, embedded_surfaces,
+                             TransferableResourceArray());
+}
+
+CompositorFrame MakeCompositorFrame(
+    std::vector<SurfaceId> embedded_surfaces,
+    std::vector<SurfaceId> referenced_surfaces) {
+  return MakeCompositorFrame(std::move(embedded_surfaces),
+                             std::move(referenced_surfaces),
+                             TransferableResourceArray());
+}
+
+CompositorFrame MakeCompositorFrameWithResources(
+    std::vector<SurfaceId> embedded_surfaces,
+    TransferableResourceArray resource_list) {
+  return MakeCompositorFrame(embedded_surfaces, embedded_surfaces,
+                             std::move(resource_list));
+}
+
 TransferableResource MakeResource(ResourceId id,
                                   ResourceFormat format,
                                   uint32_t filter,
@@ -1194,5 +1205,42 @@
   EXPECT_FALSE(dependency_tracker().has_deadline());
 }
 
+// This test verifies that a CompositorFrame will only blocked on embedded
+// surfaces but not on other retained surface IDs in the CompositorFrame.
+TEST_F(CompositorFrameSinkSupportTest, OnlyBlockOnEmbeddedSurfaces) {
+  const SurfaceId display_id = MakeSurfaceId(kDisplayFrameSink, 1);
+  const SurfaceId parent_id1 = MakeSurfaceId(kParentFrameSink, 1);
+  const SurfaceId parent_id2 = MakeSurfaceId(kParentFrameSink, 2);
+
+  display_support().SubmitCompositorFrame(
+      display_id.local_surface_id(),
+      MakeCompositorFrame({parent_id1}, {parent_id1, parent_id2}));
+
+  EXPECT_TRUE(display_surface()->HasPendingFrame());
+  EXPECT_FALSE(display_surface()->HasActiveFrame());
+  EXPECT_TRUE(dependency_tracker().has_deadline());
+
+  // Verify that the display CompositorFrame will only block on |parent_id1| but
+  // not |parent_id2|.
+  EXPECT_THAT(display_surface()->blocking_surfaces(),
+              UnorderedElementsAre(parent_id1));
+  // Verify that the display CompositorFrame holds refernces to both
+  // |parent_id1| and |parent_id2|.
+  EXPECT_THAT(GetChildReferences(display_id),
+              UnorderedElementsAre(parent_id1, parent_id2));
+
+  // Submitting a CompositorFrame with |parent_id1| should unblock the display
+  // CompositorFrame.
+  parent_support().SubmitCompositorFrame(parent_id1.local_surface_id(),
+                                         MakeCompositorFrame());
+
+  EXPECT_FALSE(dependency_tracker().has_deadline());
+  EXPECT_FALSE(display_surface()->HasPendingFrame());
+  EXPECT_TRUE(display_surface()->HasActiveFrame());
+  EXPECT_THAT(display_surface()->blocking_surfaces(), IsEmpty());
+  EXPECT_THAT(GetChildReferences(display_id),
+              UnorderedElementsAre(parent_id1, parent_id2));
+}
+
 }  // namespace test
 }  // namespace cc
diff --git a/cc/surfaces/surface.cc b/cc/surfaces/surface.cc
index 3e16c69..3290f84b 100644
--- a/cc/surfaces/surface.cc
+++ b/cc/surfaces/surface.cc
@@ -216,8 +216,7 @@
 
   base::flat_set<SurfaceId> new_blocking_surfaces;
 
-  for (const SurfaceId& surface_id :
-       current_frame.metadata.referenced_surfaces) {
+  for (const SurfaceId& surface_id : current_frame.metadata.embedded_surfaces) {
     Surface* surface = factory_->manager()->GetSurfaceForId(surface_id);
     // If a referenced surface does not have a corresponding active frame in the
     // display compositor, then it blocks this frame.
diff --git a/cc/surfaces/surface_aggregator.cc b/cc/surfaces/surface_aggregator.cc
index 19fbc42..a1d015f7 100644
--- a/cc/surfaces/surface_aggregator.cc
+++ b/cc/surfaces/surface_aggregator.cc
@@ -369,7 +369,7 @@
       color_conversion_pass->CreateAndAppendDrawQuad<RenderPassDrawQuad>();
   quad->SetNew(shared_quad_state, output_rect, output_rect,
                root_render_pass->id, 0, gfx::RectF(), gfx::Size(),
-               gfx::Vector2dF(), gfx::PointF(), gfx::RectF());
+               gfx::Vector2dF(), gfx::PointF(), gfx::RectF(output_rect));
   dest_pass_list_->push_back(std::move(color_conversion_pass));
 }
 
diff --git a/cc/surfaces/surface_dependency_tracker.cc b/cc/surfaces/surface_dependency_tracker.cc
index 302a021..9e05907 100644
--- a/cc/surfaces/surface_dependency_tracker.cc
+++ b/cc/surfaces/surface_dependency_tracker.cc
@@ -43,8 +43,7 @@
 
   // Referenced surface IDs that aren't currently known to the surface manager
   // or do not have an active CompsotiorFrame block this frame.
-  for (const SurfaceId& surface_id :
-       pending_frame.metadata.referenced_surfaces) {
+  for (const SurfaceId& surface_id : pending_frame.metadata.embedded_surfaces) {
     Surface* surface_dependency = surface_manager_->GetSurfaceForId(surface_id);
     if (!surface_dependency || !surface_dependency->HasActiveFrame())
       blocked_surfaces_from_dependency_[surface_id].insert(
@@ -117,10 +116,9 @@
 
   const CompositorFrame& pending_frame = surface->GetPendingFrame();
 
-  DCHECK(!pending_frame.metadata.referenced_surfaces.empty());
+  DCHECK(!pending_frame.metadata.embedded_surfaces.empty());
 
-  for (const SurfaceId& surface_id :
-       pending_frame.metadata.referenced_surfaces) {
+  for (const SurfaceId& surface_id : pending_frame.metadata.embedded_surfaces) {
     auto it = blocked_surfaces_from_dependency_.find(surface_id);
     if (it == blocked_surfaces_from_dependency_.end())
       continue;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 7adf840..62654865 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -973,6 +973,9 @@
         layer->set_was_ever_ready_since_last_transform_animation(true);
       }
     }
+    frame->embedded_surfaces.insert(frame->embedded_surfaces.end(),
+                                    append_quads_data.embedded_surfaces.begin(),
+                                    append_quads_data.embedded_surfaces.end());
   }
 
   // If CommitToActiveTree() is true, then we wait to draw until
@@ -1704,6 +1707,7 @@
 
   CompositorFrameMetadata metadata = MakeCompositorFrameMetadata();
   metadata.may_contain_video = frame->may_contain_video;
+  metadata.embedded_surfaces = std::move(frame->embedded_surfaces);
   active_tree()->FinishSwapPromises(&metadata);
   for (auto& latency : metadata.latency_info) {
     TRACE_EVENT_WITH_FLOW1("input,benchmark", "LatencyInfo.Flow",
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index 103a98c45..0b72dc4 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -36,6 +36,7 @@
 #include "cc/scheduler/draw_result.h"
 #include "cc/scheduler/video_frame_controller.h"
 #include "cc/surfaces/local_surface_id.h"
+#include "cc/surfaces/surface_id.h"
 #include "cc/tiles/decoded_image_tracker.h"
 #include "cc/tiles/image_decode_cache.h"
 #include "cc/tiles/tile_manager.h"
@@ -222,6 +223,7 @@
     ~FrameData();
     void AsValueInto(base::trace_event::TracedValue* value) const;
 
+    std::vector<SurfaceId> embedded_surfaces;
     std::vector<gfx::Rect> occluding_screen_space_rects;
     std::vector<gfx::Rect> non_occluding_screen_space_rects;
     RenderPassList render_passes;
diff --git a/cc/trees/layer_tree_host_impl_unittest.cc b/cc/trees/layer_tree_host_impl_unittest.cc
index 5350b1a..a400b8b 100644
--- a/cc/trees/layer_tree_host_impl_unittest.cc
+++ b/cc/trees/layer_tree_host_impl_unittest.cc
@@ -30,6 +30,7 @@
 #include "cc/layers/render_surface_impl.h"
 #include "cc/layers/solid_color_layer_impl.h"
 #include "cc/layers/solid_color_scrollbar_layer_impl.h"
+#include "cc/layers/surface_layer_impl.h"
 #include "cc/layers/texture_layer_impl.h"
 #include "cc/layers/video_layer_impl.h"
 #include "cc/layers/viewport.h"
@@ -93,6 +94,12 @@
 namespace cc {
 namespace {
 
+SurfaceId MakeSurfaceId(const FrameSinkId& frame_sink_id, uint32_t local_id) {
+  return SurfaceId(
+      frame_sink_id,
+      LocalSurfaceId(local_id, base::UnguessableToken::Deserialize(0, 1u)));
+}
+
 struct TestFrameData : public LayerTreeHostImpl::FrameData {
   TestFrameData() {
     // Set ack to something valid, so DCHECKs don't complain.
@@ -3481,6 +3488,43 @@
   SetupMouseMoveAtWithDeviceScale(2.f);
 }
 
+// This test verifies that only SurfaceLayers in the viewport are included
+// in CompositorFrameMetadata's |embedded_surfaces|.
+TEST_F(LayerTreeHostImplTest, EmbeddedSurfacesInMetadata) {
+  SetupScrollAndContentsLayers(gfx::Size(100, 100));
+  host_impl_->SetViewportSize(gfx::Size(50, 50));
+  LayerImpl* root = host_impl_->active_tree()->root_layer_for_testing();
+
+  std::vector<SurfaceId> children = {MakeSurfaceId(FrameSinkId(1, 1), 1),
+                                     MakeSurfaceId(FrameSinkId(2, 2), 2),
+                                     MakeSurfaceId(FrameSinkId(3, 3), 3)};
+  for (size_t i = 0; i < children.size(); ++i) {
+    std::unique_ptr<SurfaceLayerImpl> child =
+        SurfaceLayerImpl::Create(host_impl_->active_tree(), i + 6);
+    child->SetPosition(gfx::PointF(25.f * i, 0.f));
+    child->SetBounds(gfx::Size(1, 1));
+    child->SetDrawsContent(true);
+    child->SetPrimarySurfaceInfo(
+        SurfaceInfo(children[i], 1.f /* device_scale_factor */,
+                    gfx::Size(10, 10) /* size_in_pixels */));
+    root->test_properties()->AddChild(std::move(child));
+  }
+
+  host_impl_->active_tree()->BuildPropertyTreesForTesting();
+  DrawFrame();
+
+  FakeCompositorFrameSink* fake_compositor_frame_sink =
+      static_cast<FakeCompositorFrameSink*>(
+          host_impl_->compositor_frame_sink());
+  const CompositorFrameMetadata& metadata =
+      fake_compositor_frame_sink->last_sent_frame()->metadata;
+  EXPECT_THAT(metadata.embedded_surfaces,
+              testing::UnorderedElementsAre(children[0], children[1]));
+  EXPECT_THAT(
+      metadata.referenced_surfaces,
+      testing::UnorderedElementsAre(children[0], children[1], children[2]));
+}
+
 TEST_F(LayerTreeHostImplTest, CompositorFrameMetadata) {
   SetupScrollAndContentsLayers(gfx::Size(100, 100));
   host_impl_->SetViewportSize(gfx::Size(50, 50));
diff --git a/chrome/android/java/res/layout/search_activity.xml b/chrome/android/java/res/layout/search_activity.xml
new file mode 100644
index 0000000..378e268
--- /dev/null
+++ b/chrome/android/java/res/layout/search_activity.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 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. -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/control_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#9affffff" >
+
+    <!-- This view is unnecessary visually, but required for the LocationBarLayout. -->
+    <org.chromium.chrome.browser.searchwidget.SearchActivityFadingBackgroundView
+            android:id="@+id/fading_focus_target"
+            android:layout_width="0dp"
+            android:layout_height="0dp" />
+
+    <ViewStub
+            android:id="@+id/omnibox_results_container_stub"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@android:color/white"
+            android:layout="@layout/omnibox_results_container" />
+
+    <ImageView
+            android:id="@+id/toolbar_shadow"
+            android:src="@drawable/toolbar_shadow"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="@dimen/toolbar_height_no_shadow"
+            android:scaleType="fitXY"
+            android:contentDescription="@null" />
+
+    <FrameLayout
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/toolbar_height_no_shadow"
+            android:background="@android:color/white"
+            android:clickable="true" >
+
+        <!-- TODO(dfalcantara): Ask UX about what to do for tablets. -->
+        <org.chromium.chrome.browser.searchwidget.SearchActivityLocationBarLayout
+                android:id="@+id/search_location_bar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="@dimen/tablet_toolbar_start_padding_no_buttons"
+                android:layout_marginEnd="@dimen/tablet_toolbar_start_padding_no_buttons"
+                android:layout_marginTop="@dimen/tablet_toolbar_start_padding_no_buttons" />
+
+    </FrameLayout>
+
+    <FrameLayout
+            android:id="@+id/bottom_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/chrome/android/java/res/layout/search_widget_template_transparent.xml b/chrome/android/java/res/layout/search_widget_template_transparent.xml
deleted file mode 100644
index 8e0b4294..0000000
--- a/chrome/android/java/res/layout/search_widget_template_transparent.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2017 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. -->
-
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:alpha="0.0" >
-
-    <include layout="@layout/search_widget_template" />
-
-</FrameLayout>
diff --git a/chrome/android/java/res_template/xml/launchershortcuts.xml b/chrome/android/java/res_template/xml/launchershortcuts.xml
index 5e6ebfb..bd6cd4a 100644
--- a/chrome/android/java/res_template/xml/launchershortcuts.xml
+++ b/chrome/android/java/res_template/xml/launchershortcuts.xml
@@ -15,16 +15,13 @@
         </intent>
     </shortcut>
 
-    <!-- The "New incognito tab" shortcut is now provided as a dynamic shortcut.
-         See crbug.com/707446.
-         TODO(twellington): Either remove this XML shorcut or add a disabled
-                            message. -->
     <shortcut
         android:shortcutId="new-incognito-tab-shortcut"
         android:enabled="false"
         android:icon="@drawable/shortcut_incognito"
         android:shortcutShortLabel="@string/accessibility_tabstrip_incognito_identifier"
-        android:shortcutLongLabel="@string/menu_new_incognito_tab" >
+        android:shortcutLongLabel="@string/menu_new_incognito_tab"
+        android:shortcutDisabledMessage="@string/disabled_incognito_launcher_shortcut_message" >
         <intent
             android:action="chromium.shortcut.action.OPEN_NEW_INCOGNITO_TAB"
             android:targetPackage="{{manifest_package}}"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
index 6df7874..b0f114e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -612,6 +612,11 @@
                         (ControlContainer) findViewById(R.id.control_container);
                 controlContainer.getToolbarResourceAdapter().invalidate(null);
             }
+
+            @Override
+            public void onContentChanged(Tab tab) {
+                if (getBottomSheet() != null) setStatusBarColor(tab, tab.getDefaultThemeColor());
+            }
         };
 
         if (mAssistStatusHandler != null) {
@@ -794,11 +799,6 @@
     protected void setStatusBarColor(Tab tab, int color) {
         int statusBarColor = (tab != null && tab.isDefaultThemeColor())
                 ? Color.BLACK : ColorUtils.getDarkenedColorForStatusBar(color);
-        if (getBottomSheet() != null) {
-            statusBarColor =
-                    ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color);
-            getBottomSheet().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
-        }
         ApiCompatibilityUtils.setStatusBarColor(getWindow(), statusBarColor);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
index 37fb5dde..4303f54 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -1838,6 +1838,22 @@
     @Override
     protected void setStatusBarColor(Tab tab, int color) {
         if (DeviceFormFactor.isTablet(getApplicationContext())) return;
+
+        // If Chrome Home is enabled, the super of this function is not called because it only
+        // performs unnecessary transformations on the theme color.
+        if (getBottomSheet() != null) {
+            color = ApiCompatibilityUtils.getColor(getResources(), R.color.default_primary_color);
+            getBottomSheet().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
+
+            // Special case the incognito NTP and the tab switcher.
+            if ((tab != null && NewTabPage.isNTPUrl(tab.getUrl()) && tab.isIncognito())
+                    || isInOverviewMode()) {
+                color = Color.BLACK;
+            }
+            ApiCompatibilityUtils.setStatusBarColor(getWindow(), color);
+            return;
+        }
+
         super.setStatusBarColor(tab, isInOverviewMode() ? Color.BLACK : color);
     }
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index afeda54..cd508545 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -28,10 +28,14 @@
 
 import java.util.List;
 
+import javax.annotation.Nullable;
+
 /**
  * A helper class that handles generating context menus for {@link ContentViewCore}s.
  */
 public class ContextMenuHelper implements OnCreateContextMenuListener {
+    private static final int MAX_SHARE_DIMEN_PX = 2048;
+
     private long mNativeContextMenuHelper;
 
     private ContextMenuPopulator mPopulator;
@@ -40,8 +44,6 @@
     private Callback<Integer> mCallback;
     private Runnable mOnMenuShown;
     private Runnable mOnMenuClosed;
-    private Callback<Bitmap> mOnThumbnailReceivedCallback;
-    private ComponentName mComponentName;
 
     private ContextMenuHelper(long nativeContextMenuHelper) {
         mNativeContextMenuHelper = nativeContextMenuHelper;
@@ -169,50 +171,47 @@
      * Share the image that triggered the current context menu.
      */
     public void shareImage() {
-        if (mNativeContextMenuHelper == 0) return;
-        nativeShareImage(mNativeContextMenuHelper);
-    }
-
-    @CalledByNative
-    private void onShareImageReceived(
-            WindowAndroid windowAndroid, byte[] jpegImageData) {
-        Activity activity = windowAndroid.getActivity().get();
-        if (activity == null) return;
-
-        ShareHelper.shareImage(activity, jpegImageData, mComponentName);
-        // This needs to be reset to null after a share. This way the next time a user shares an
-        // image it won't share with the last shared app unless explicitly told.
-        mComponentName = null;
+        shareImageDirectly(null);
     }
 
     /**
      * Share image triggered with the current context menu directly with a specific app.
      * @param name The {@link ComponentName} of the app to share the image directly with.
      */
-    public void shareImageDirectly(ComponentName name) {
-        mComponentName = name;
-        shareImage();
+    public void shareImageDirectly(@Nullable final ComponentName name) {
+        if (mNativeContextMenuHelper == 0) return;
+        Callback<byte[]> callback = new Callback<byte[]>() {
+            @Override
+            public void onResult(byte[] result) {
+                WebContents webContents = nativeGetJavaWebContents(mNativeContextMenuHelper);
+                WindowAndroid windowAndroid = webContents.getTopLevelNativeWindow();
+
+                Activity activity = windowAndroid.getActivity().get();
+                if (activity == null) return;
+
+                ShareHelper.shareImage(activity, result, name);
+            }
+        };
+        nativeRetrieveImage(mNativeContextMenuHelper, callback, MAX_SHARE_DIMEN_PX);
     }
 
     /**
      * Gets the thumbnail of the current image that triggered the context menu.
      * @param callback Called once the the thumbnail is received.
      */
-    private void getThumbnail(Callback<Bitmap> callback) {
-        mOnThumbnailReceivedCallback = callback;
+    private void getThumbnail(final Callback<Bitmap> callback) {
         if (mNativeContextMenuHelper == 0) return;
         int maxSizePx = mActivity.getResources().getDimensionPixelSize(
                 R.dimen.context_menu_header_image_max_size);
-        nativeRetrieveHeaderThumbnail(mNativeContextMenuHelper, maxSizePx);
-    }
-
-    @CalledByNative
-    private void onHeaderThumbnailReceived(WindowAndroid windowAndroid, byte[] jpegImageData) {
-        // TODO(tedchoc): Decode in separate process before launch.
-        Bitmap bitmap = BitmapFactory.decodeByteArray(jpegImageData, 0, jpegImageData.length);
-        if (mOnThumbnailReceivedCallback != null) {
-            mOnThumbnailReceivedCallback.onResult(bitmap);
-        }
+        Callback<byte[]> rawDataCallback = new Callback<byte[]>() {
+            @Override
+            public void onResult(byte[] result) {
+                // TODO(tedchoc): Decode in separate process before launch.
+                Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
+                callback.onResult(bitmap);
+            }
+        };
+        nativeRetrieveImage(mNativeContextMenuHelper, rawDataCallback, maxSizePx);
     }
 
     @Override
@@ -232,10 +231,11 @@
         return mPopulator;
     }
 
+    private native WebContents nativeGetJavaWebContents(long nativeContextMenuHelper);
     private native void nativeOnStartDownload(
             long nativeContextMenuHelper, boolean isLink, boolean isDataReductionProxyEnabled);
     private native void nativeSearchForImage(long nativeContextMenuHelper);
-    private native void nativeShareImage(long nativeContextMenuHelper);
+    private native void nativeRetrieveImage(
+            long nativeContextMenuHelper, Callback<byte[]> callback, int maxSizePx);
     private native void nativeOnContextMenuClosed(long nativeContextMenuHelper);
-    private native void nativeRetrieveHeaderThumbnail(long nativeContextMenuHelper, int maxSizePx);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java
index 9e1ea15..92670c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java
@@ -29,17 +29,6 @@
     }
 
     /**
-     * Reports provided entity to on-device index.
-     * Base class does not implement any reporting, and call is a no-op. Child classes should
-     * implement this functionality.
-     *
-     * Deprecated, to be removed in follow-up cl, after downstream change.
-     */
-    public void reportWebPage(org.chromium.blink.mojom.WebPage webpage) {
-        // Overriden by private class. Base class does nothing.
-    }
-
-    /**
      * Clears history of reported entities.
      * Currently, we do not support clearing only a subset of history. Base class does not implement
      * any reporting, and call is a no-op. Child classes should implement this functionality.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
index 6367531..15dea8564 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouter.java
@@ -43,6 +43,14 @@
             new HashMap<String, Map<MediaRouteProvider, List<MediaSink>>>();
     private final Map<String, List<MediaSink>> mSinksPerSource =
             new HashMap<String, List<MediaSink>>();
+    private static boolean sAndroidMediaRouterSetForTest = false;
+    private static MediaRouter sAndroidMediaRouterForTest = null;
+
+    @VisibleForTesting
+    public static void setAndroidMediaRouterForTest(MediaRouter router) {
+        sAndroidMediaRouterSetForTest = true;
+        sAndroidMediaRouterForTest = router;
+    }
 
     @VisibleForTesting
     public static void setRouteProviderBuilderForTest(MediaRouteProvider.Builder builder) {
@@ -76,6 +84,7 @@
      */
     @Nullable
     public static MediaRouter getAndroidMediaRouter() {
+        if (sAndroidMediaRouterSetForTest) return sAndroidMediaRouterForTest;
         try {
             // Pre-MR1 versions of JB do not have the complete MediaRouter APIs,
             // so getting the MediaRouter instance will throw an exception.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java
index 7217bb8b..e895de9 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterDialogController.java
@@ -44,7 +44,10 @@
         if (isShowingDialog()) return;
 
         MediaSource source = MediaSource.from(sourceUrn);
-        if (source == null) return;
+        if (source == null) {
+            nativeOnMediaSourceNotSupported(mNativeDialogController);
+            return;
+        }
 
         mDialogManager = new MediaRouteChooserDialogManager(source, this);
         mDialogManager.openDialog();
@@ -60,7 +63,10 @@
         if (isShowingDialog()) return;
 
         MediaSource source = MediaSource.from(sourceUrn);
-        if (source == null) return;
+        if (source == null) {
+            nativeOnMediaSourceNotSupported(mNativeDialogController);
+            return;
+        }
 
         mDialogManager = new MediaRouteControllerDialogManager(source, mediaRouteId, this);
         mDialogManager.openDialog();
@@ -117,4 +123,5 @@
     native void nativeOnSinkSelected(
             long nativeMediaRouterDialogControllerAndroid, String sinkId);
     native void nativeOnRouteClosed(long nativeMediaRouterDialogControllerAndroid, String routeId);
+    native void nativeOnMediaSourceNotSupported(long nativeMediaRouterDialogControllerAndroid);
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
index 594e5ffe..fc92cfd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProvider.java
@@ -91,7 +91,6 @@
     @Nullable
     public static CastMediaRouteProvider create(MediaRouteManager manager) {
         MediaRouter androidMediaRouter = ChromeMediaRouter.getAndroidMediaRouter();
-        if (androidMediaRouter == null) return null;
 
         return new CastMediaRouteProvider(androidMediaRouter, manager);
     }
@@ -182,10 +181,19 @@
 
     @Override
     public void startObservingMediaSinks(String sourceId) {
-        if (mAndroidMediaRouter == null) return;
+        if (mAndroidMediaRouter == null) {
+            // If the MediaRouter API is not available, report no devices so the page doesn't even
+            // try to cast.
+            onSinksReceived(sourceId, new ArrayList<MediaSink>());
+            return;
+        }
 
         MediaSource source = MediaSource.from(sourceId);
-        if (source == null) return;
+        if (source == null) {
+            // If the source is invalid, report no devices available.
+            onSinksReceived(sourceId, new ArrayList<MediaSink>());
+            return;
+        }
 
         MediaRouteSelector routeSelector = source.buildRouteSelector();
         if (routeSelector == null) {
@@ -322,7 +330,7 @@
         }
 
         ClientRecord client = getClientRecordByRouteId(routeId);
-        if (client != null) {
+        if (client != null && mAndroidMediaRouter != null) {
             MediaSink sink = MediaSink.fromSinkId(mSession.getSinkId(), mAndroidMediaRouter);
             if (sink != null) sendReceiverAction(routeId, sink, client.clientId, "stop");
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
index 1da0d0b..76c409ca 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/NewTabPageScrollView.java
@@ -19,35 +19,16 @@
 
 import org.chromium.base.Log;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ntp.ContextMenuManager.TouchDisableableView;
 import org.chromium.chrome.browser.widget.FadingShadow;
 
 /**
  * Simple wrapper on top of a ScrollView that will acquire focus when tapped.  Ensures the
  * New Tab page receives focus when clicked. This is only used in the Incognito NTP.
  */
-public class NewTabPageScrollView extends ScrollView implements TouchDisableableView {
+public class NewTabPageScrollView extends ScrollView {
     private static final String TAG = "NewTabPageScrollView";
 
-    /** Whether the ScrollView and its children should react to touch events. */
-    private boolean mTouchEnabled = true;
-
-    /**
-     * Listener for scroll changes.
-     */
-    public interface OnScrollListener {
-        /**
-         * Triggered when the scroll changes.  See ScrollView#onScrollChanged for more
-         * details.
-         */
-        void onScrollChanged(int l, int t, int oldl, int oldt);
-    }
-
     private GestureDetector mGestureDetector;
-    private OnScrollListener mOnScrollListener;
-
-    private NewTabPageLayout mNewTabPageLayout;
-
     private FadingShadow mFadingShadow;
 
     /**
@@ -80,38 +61,14 @@
     }
 
     @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-
-        // Incognito also uses this scroll view but will not have the id so will return null.
-        mNewTabPageLayout = (NewTabPageLayout) findViewById(R.id.ntp_content);
-    }
-
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mNewTabPageLayout != null) {
-            mNewTabPageLayout.setParentViewportHeight(MeasureSpec.getSize(heightMeasureSpec));
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-    }
-
-    @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         mGestureDetector.onTouchEvent(ev);
-        if (!mTouchEnabled) return true;
         return super.onInterceptTouchEvent(ev);
     }
 
     @Override
-    public void setTouchEnabled(boolean enabled) {
-        mTouchEnabled = enabled;
-    }
-
-    @Override
     @SuppressLint("ClickableViewAccessibility")
     public boolean onTouchEvent(MotionEvent ev) {
-        if (!mTouchEnabled) return false;
-
         // Action down would already have been handled in onInterceptTouchEvent
         if (ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
             mGestureDetector.onTouchEvent(ev);
@@ -133,20 +90,6 @@
         }
     }
 
-    /**
-     * Sets the listener to be notified of scroll changes.
-     * @param listener The listener to be updated on scroll changes.
-     */
-    public void setOnScrollListener(OnScrollListener listener) {
-        mOnScrollListener = listener;
-    }
-
-    @Override
-    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
-        super.onScrollChanged(l, t, oldl, oldt);
-        if (mOnScrollListener != null) mOnScrollListener.onScrollChanged(l, t, oldl, oldt);
-    }
-
     @Override
     public void focusableViewAvailable(View v) {
         // To avoid odd jumps during NTP animation transitions, we do not attempt to give focus
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java
index bd2fcef..e69cd59 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/snippets/SnippetsLauncher.java
@@ -4,6 +4,9 @@
 
 package org.chromium.chrome.browser.ntp.snippets;
 
+import android.content.Context;
+import android.net.ConnectivityManager;
+
 import com.google.android.gms.gcm.GcmNetworkManager;
 import com.google.android.gms.gcm.PeriodicTask;
 import com.google.android.gms.gcm.Task;
@@ -165,6 +168,14 @@
         return schedule(0, 0);
     }
 
+    @CalledByNative
+    public boolean isOnUnmeteredConnection() {
+        Context context = ContextUtils.getApplicationContext();
+        ConnectivityManager manager =
+                (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+        return !manager.isActiveNetworkMetered();
+    }
+
     public static boolean shouldRescheduleTasksOnUpgrade() {
         // Reschedule the periodic tasks if they were enabled previously.
         return ContextUtils.getAppSharedPreferences().getBoolean(PREF_IS_SCHEDULED, false);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
index 2a95de7..8fa4ae6 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/UrlBar.java
@@ -71,9 +71,6 @@
     /** The contents of the URL that precede the path/query before formatting. */
     private String mOriginalUrlLocation;
 
-    /** Overrides the text announced during accessibility events. */
-    private String mAccessibilityTextOverride;
-
     private boolean mShowKeyboardOnWindowFocus;
 
     private boolean mFirstDrawComplete;
@@ -109,7 +106,6 @@
     private Boolean mUseDarkColors;
 
     private AccessibilityManager mAccessibilityManager;
-    private boolean mDisableTextAccessibilityEvents;
 
     /**
      * Whether default TextView scrolling should be disabled because autocomplete has been added.
@@ -136,7 +132,7 @@
 
     // Set to true when the URL bar text is modified programmatically. Initially set
     // to true until the old state has been loaded.
-    private boolean mIgnoreAutocomplete = true;
+    private boolean mIgnoreTextChangeFromAutocomplete = true;
     private boolean mLastUrlEditWasDelete;
 
     /** This tracks whether or not the last ACTION_DOWN event was when the url bar had focus. */
@@ -286,7 +282,7 @@
     public void setIgnoreTextChangesForAutocomplete(boolean ignoreAutocomplete) {
         assert mUrlBarDelegate != null;
 
-        mIgnoreAutocomplete = ignoreAutocomplete;
+        mIgnoreTextChangeFromAutocomplete = ignoreAutocomplete;
     }
 
     /**
@@ -301,7 +297,7 @@
      *         at the beginning of the inline autocomplete text if present otherwise the very
      *         end of the current text).
      */
-    public boolean isCursorAtEndOfTypedText() {
+    private boolean isCursorAtEndOfTypedText() {
         final int selectionStart = getSelectionStart();
         final int selectionEnd = getSelectionEnd();
 
@@ -320,7 +316,7 @@
      */
     // isInBatchEditMode is a package protected method on TextView, so we intentionally chose
     // a different name.
-    public boolean isHandlingBatchInput() {
+    private boolean isHandlingBatchInput() {
         return mInBatchEditMode;
     }
 
@@ -743,15 +739,19 @@
                     + currentText.substring(mFormattedUrlLocation.length());
             selectedEndIndex = selectedEndIndex - mFormattedUrlLocation.length()
                     + mOriginalUrlLocation.length();
+
             setIgnoreTextChangesForAutocomplete(true);
             setText(newText);
             setSelection(0, selectedEndIndex);
+            setIgnoreTextChangesForAutocomplete(false);
+
             boolean retVal = super.onTextContextMenuItem(id);
             if (getText().toString().equals(newText)) {
+                setIgnoreTextChangesForAutocomplete(true);
                 setText(currentText);
                 setSelection(getText().length());
+                setIgnoreTextChangesForAutocomplete(false);
             }
-            setIgnoreTextChangesForAutocomplete(false);
             return retVal;
         }
         return super.onTextContextMenuItem(id);
@@ -811,7 +811,6 @@
         CharSequence newText = TextUtils.concat(userText, inlineAutocompleteText);
 
         setIgnoreTextChangesForAutocomplete(true);
-        mDisableTextAccessibilityEvents = true;
 
         if (!TextUtils.equals(previousText, newText)) {
             // The previous text may also have included autocomplete text, so we only
@@ -843,7 +842,6 @@
         }
 
         setIgnoreTextChangesForAutocomplete(false);
-        mDisableTextAccessibilityEvents = false;
     }
 
     /**
@@ -857,16 +855,6 @@
     }
 
     /**
-     * Overrides the text announced when focusing on the field for accessibility.  This value will
-     * be cleared automatically when the text content changes for this view.
-     * @param accessibilityOverride The text to be announced instead of the current text value
-     *                              (or null if the text content should be read).
-     */
-    public void setAccessibilityTextOverride(String accessibilityOverride) {
-        mAccessibilityTextOverride = accessibilityOverride;
-    }
-
-    /**
      * Scroll to ensure the TLD is visible.
      * @return Whether the TLD was discovered and successfully scrolled to.
      */
@@ -923,10 +911,11 @@
         // URL is being edited).
         if (!TextUtils.equals(getEditableText(), text)) {
             super.setText(text, type);
-            mAccessibilityTextOverride = null;
         }
 
         // Verify the autocomplete is still valid after the text change.
+        // Note: mAutocompleteSpan may be still null here if setText() is called in View
+        // constructor.
         if (mAutocompleteSpan != null
                 && mAutocompleteSpan.mUserText != null
                 && mAutocompleteSpan.mAutocompleteText != null) {
@@ -1011,7 +1000,7 @@
 
     @Override
     public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
-        if (mDisableTextAccessibilityEvents) {
+        if (mIgnoreTextChangeFromAutocomplete) {
             if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
                     || event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED) {
                 return;
@@ -1030,10 +1019,6 @@
         } finally {
             StrictMode.setThreadPolicy(oldPolicy);
         }
-
-        if (mAccessibilityTextOverride != null) {
-            info.setText(mAccessibilityTextOverride);
-        }
     }
 
     @VisibleForTesting
@@ -1204,7 +1189,7 @@
     private void notifyAutocompleteTextStateChanged(boolean textDeleted) {
         if (mUrlBarDelegate == null) return;
         if (!hasFocus()) return;
-        if (mIgnoreAutocomplete) return;
+        if (mIgnoreTextChangeFromAutocomplete) return;
 
         mLastUrlEditWasDelete = textDeleted;
         mUrlBarDelegate.onTextChangedForAutocomplete(textDeleted);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
index 39da8b00..b253f41 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinder.java
@@ -60,7 +60,7 @@
      * Meta data name of an app's supported default payment method name.
      */
     static final String META_DATA_NAME_OF_DEFAULT_PAYMENT_METHOD_NAME =
-            "org.chromum.default_payment_method_name";
+            "org.chromium.default_payment_method_name";
 
     /** The maximum number of payment method manifests to download. */
     private static final int MAX_NUMBER_OF_MANIFESTS = 10;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/BoxAnimatorScrim.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/BoxAnimatorScrim.java
deleted file mode 100644
index ae7a4d8..0000000
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/BoxAnimatorScrim.java
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2017 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.
-
-package org.chromium.chrome.browser.searchwidget;
-
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.View;
-
-import org.chromium.base.ApiCompatibilityUtils;
-import org.chromium.chrome.R;
-
-/** Draws a scrim and animates a search box moving into place. */
-class BoxAnimatorScrim extends View implements ValueAnimator.AnimatorUpdateListener {
-    /** Transparency used for the scrim. */
-    private static final int BACKGROUND_TRANSPARENCY = 154;
-
-    private final Drawable mCardDrawable;
-    private final Rect mAnimationRect = new Rect();
-    private final int mBackgroundColor;
-
-    private Rect mSourceRect;
-    private Rect mTargetRect;
-    private float mInterpolatedValue;
-
-    public BoxAnimatorScrim(Context context, AttributeSet attrs) {
-        super(context, attrs);
-
-        mBackgroundColor =
-                ApiCompatibilityUtils.getColor(getResources(), R.color.light_normal_color)
-                & 0x00ffffff;
-
-        // Mutate to prevent the animation from affecting other places the Drawable is used.
-        mCardDrawable = ApiCompatibilityUtils.getDrawable(getResources(), R.drawable.card_single);
-        mCardDrawable.mutate();
-    }
-
-    /** Sets where the box will animate from and where it will go. */
-    public void setAnimationRects(Rect source, Rect target) {
-        mSourceRect = source;
-        mTargetRect = target;
-    }
-
-    /** Returns the last known value of the animation. */
-    public float getInterpolatedValue() {
-        return mInterpolatedValue;
-    }
-
-    /** Set the current value of the animation. */
-    public void setInterpolatedValue(float value) {
-        mInterpolatedValue = Math.max(0.0f, Math.min(1.0f, value));
-        postInvalidateOnAnimation();
-    }
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        // Draw the background.
-        int currentAlpha = (int) (BACKGROUND_TRANSPARENCY * mInterpolatedValue);
-        int color = (currentAlpha << 24) + mBackgroundColor;
-        canvas.drawColor(color);
-
-        // Draw the search box moving into place, if needed.
-        if (mSourceRect == null || mTargetRect == null) return;
-        if (mInterpolatedValue >= 1.0f) return;
-
-        mAnimationRect.left = (int) interpolate(mSourceRect.left, mTargetRect.left);
-        mAnimationRect.right = (int) interpolate(mSourceRect.right, mTargetRect.right);
-        mAnimationRect.top = (int) interpolate(mSourceRect.top, mTargetRect.top);
-        mAnimationRect.bottom = (int) interpolate(mSourceRect.bottom, mTargetRect.bottom);
-        mCardDrawable.setBounds(mAnimationRect);
-        mCardDrawable.draw(canvas);
-    }
-
-    @Override
-    public void onAnimationUpdate(ValueAnimator animation) {
-        setInterpolatedValue((Float) animation.getAnimatedValue());
-    }
-
-    private float interpolate(float start, float end) {
-        return ((1.0f - mInterpolatedValue) * start) + (mInterpolatedValue * end);
-    }
-}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
index 28cac5d..77aff29 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -4,9 +4,6 @@
 
 package org.chromium.chrome.browser.searchwidget;
 
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -15,13 +12,9 @@
 import android.support.customtabs.CustomTabsIntent;
 import android.support.v4.app.ActivityOptionsCompat;
 import android.text.TextUtils;
-import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.MeasureSpec;
 import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewStub;
-import android.widget.FrameLayout;
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.base.ContextUtils;
@@ -43,7 +36,6 @@
 import org.chromium.chrome.browser.tab.TabIdManager;
 import org.chromium.chrome.browser.tabmodel.TabModel.TabLaunchType;
 import org.chromium.chrome.browser.util.IntentUtils;
-import org.chromium.chrome.browser.widget.FadingBackgroundView;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.content_public.browser.LoadUrlParams;
 import org.chromium.ui.UiUtils;
@@ -51,55 +43,30 @@
 
 /** Prototype that queries the user's default search engine and shows autocomplete suggestions. */
 public class SearchActivity extends AsyncInitializationActivity
-        implements SnackbarManageable, SearchLocationBarLayout.Delegate,
+        implements SnackbarManageable, SearchActivityLocationBarLayout.Delegate,
                    View.OnLayoutChangeListener {
     private static final String TAG = "searchwidget";
 
-    /** How long the animation should run for. */
-    private static final int ANIMATION_DURATION_MS = 200;
-
     /** Padding gleaned from the background Drawable of the search box. */
     private final Rect mSearchBoxPadding = new Rect();
 
-    /** Location of the search box on the home screen. */
-    private Rect mSearchBoxWidgetBounds;
-
-    /** Small margin/padding used for the search box. */
-    private int mSpacingSmall;
-
     /** Medium margin/padding used for the search box. */
     private int mSpacingMedium;
 
     /** Large margin/padding used for the search box. */
     private int mSpacingLarge;
 
-    /**
-     * View that the omnibox suggestion list anchors to. This is different from the main search box
-     * because the upstream LocationBarLayout code expects a full white box instead of a floating
-     * box with shadows.
-     */
-    private View mAnchorView;
-
-    /** See {@link BoxAnimatorScrim}. */
-    private BoxAnimatorScrim mScrimView;
-
     /** Main content view. */
     private ViewGroup mContentView;
 
-    /** Whether or not the library has begun loading. */
-    private boolean mIsNativeLoading;
-
     /** Whether the native library has been loaded. */
     private boolean mIsNativeReady;
 
     /** Input submitted before before the native library was loaded. */
     private String mQueuedUrl;
 
-    /** Animates the SearchActivity's controls moving into place. */
-    private ValueAnimator mAnimator;
-
     /** The View that represents the search box. */
-    private SearchLocationBarLayout mSearchBox;
+    private SearchActivityLocationBarLayout mSearchBox;
 
     private UrlBar mUrlBar;
 
@@ -122,12 +89,6 @@
 
     @Override
     public void finish() {
-        if (SearchWidgetProvider.ANIMATE_TRANSITION) {
-            // Update the search widgets so that they all show up as opaque again.
-            SearchWidgetProvider.setLaunchingWidgetId(SearchWidgetProvider.INVALID_WIDGET_ID);
-            SearchWidgetProvider.updateAllWidgets();
-        }
-
         super.finish();
         overridePendingTransition(0, android.R.anim.fade_out);
     }
@@ -155,78 +116,28 @@
 
         mWindowAndroid = new ActivityWindowAndroid(this);
         mSnackbarManager = new SnackbarManager(this);
-
-        // Build the Views that {@link SearchLocationBarLayout} expects to exist.
-        ViewStub resultsStub = new ViewStub(this);
-        resultsStub.setId(R.id.omnibox_results_container_stub);
-        resultsStub.setLayoutResource(R.layout.omnibox_results_container);
-
-        // The FadingBackgroundView isn't used here, but is still animated by the LocationBarLayout.
-        // This interferes with the animation we need to show the widget moving to the right place.
-        FadingBackgroundView fadingView = new FadingBackgroundView(this, null) {
-            @Override
-            public void showFadingOverlay() {}
-            @Override
-            public void hideFadingOverlay(boolean fadeOut) {}
-        };
-
-        fadingView.setId(R.id.fading_focus_target);
-        FrameLayout.LayoutParams fadingBackgroundLayoutParams = new FrameLayout.LayoutParams(0, 0);
-
-        // Add an empty view to prevent crashing.
-        ViewGroup bottomContainer = new FrameLayout(this);
-        bottomContainer.setId(R.id.bottom_container);
-
-        // Build the search box, set it to invisible until the animation is done.
-        mSearchBox = new SearchLocationBarLayout(this, null);
-        mSearchBox.setDelegate(this);
-        mSearchBox.setVisibility(mSearchBoxWidgetBounds == null ? View.VISIBLE : View.INVISIBLE);
-        mSearchBox.setBackgroundResource(R.drawable.card_single);
-        mSearchBox.setPadding(mSpacingLarge, mSpacingMedium, mSpacingLarge, mSpacingMedium);
-        mSearchBox.initializeControls(new WindowDelegate(getWindow()), mWindowAndroid);
-        mSearchBox.setUrlBarFocusable(true);
         mSearchBoxDataProvider = new SearchBoxDataProvider();
-        mSearchBox.setToolbarDataProvider(mSearchBoxDataProvider);
-        FrameLayout.LayoutParams searchParams =
-                new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        ApiCompatibilityUtils.setMarginStart(searchParams, mSpacingSmall);
-        ApiCompatibilityUtils.setMarginEnd(searchParams, mSpacingSmall);
-        searchParams.topMargin = mSpacingSmall;
 
-        // The scrim animates the search box moving into place.
-        mScrimView = new BoxAnimatorScrim(this, null);
-        mScrimView.setOnClickListener(new View.OnClickListener() {
+        mContentView = createContentView(mSearchBox);
+        mContentView.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                // Don't allow clicking on the scrim until the animation completes.
-                if (mScrimView.getInterpolatedValue() < 1.0f) return;
-
                 // Finish the Activity if the user clicks on the scrim.
                 finish();
             }
         });
-        if (!SearchWidgetProvider.ANIMATE_TRANSITION) mScrimView.setInterpolatedValue(1.0f);
 
-        // The anchor view puts the omnibox suggestions in the correct place, visually.
-        mAnchorView = new View(this);
-        mAnchorView.setId(R.id.toolbar);
-        mAnchorView.setClickable(true);
-        FrameLayout.LayoutParams anchorParams =
-                new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
-        anchorParams.gravity = Gravity.CENTER_HORIZONTAL;
+        // Build the search box.
+        mSearchBox = (SearchActivityLocationBarLayout) mContentView.findViewById(
+                R.id.search_location_bar);
+        mSearchBox.setDelegate(this);
+        mSearchBox.setPadding(mSpacingLarge, mSpacingMedium, mSpacingLarge, mSpacingMedium);
+        mSearchBox.initializeControls(new WindowDelegate(getWindow()), mWindowAndroid);
+        mSearchBox.setUrlBarFocusable(true);
+        mSearchBox.setToolbarDataProvider(mSearchBoxDataProvider);
 
-        // Initialize and build the View hierarchy.
-        mContentView = createContentView(mSearchBox);
-        mContentView.addView(fadingView, fadingBackgroundLayoutParams);
-        mContentView.addView(mScrimView);
-        mContentView.addView(resultsStub);
-        mContentView.addView(mAnchorView, anchorParams);
-        mContentView.addView(mSearchBox, searchParams);
-        mContentView.addView(bottomContainer);
         setContentView(mContentView);
         mUrlBar = (UrlBar) mSearchBox.findViewById(R.id.url_bar);
-
-        mSearchBox.setShowCachedZeroSuggestResults(true);
     }
 
     @Override
@@ -243,6 +154,7 @@
         mSearchBoxDataProvider.onNativeLibraryReady(mTab);
         mSearchBox.onNativeLibraryReady();
         mSearchBox.setAutocompleteProfile(Profile.getLastUsedProfile().getOriginalProfile());
+        mSearchBox.setShowCachedZeroSuggestResults(true);
 
         if (mQueuedUrl != null) loadUrl(mQueuedUrl);
 
@@ -307,10 +219,13 @@
 
     private void focusTextBox(boolean clearQuery) {
         if (mIsNativeReady) mSearchBox.onUrlFocusChange(true);
+
+        if (clearQuery) {
+            mUrlBar.setIgnoreTextChangesForAutocomplete(true);
+            mUrlBar.setUrl("", null);
+            mUrlBar.setIgnoreTextChangesForAutocomplete(false);
+        }
         mUrlBar.setCursorVisible(true);
-        mUrlBar.setIgnoreTextChangesForAutocomplete(true);
-        if (clearQuery) mUrlBar.setUrl("", null);
-        mUrlBar.setIgnoreTextChangesForAutocomplete(false);
         mUrlBar.setSelection(0, mUrlBar.getText().length());
         new Handler().post(new Runnable() {
             @Override
@@ -359,136 +274,33 @@
     private ViewGroup createContentView(final View searchBox) {
         assert mContentView == null;
 
-        ViewGroup contentView = new FrameLayout(this) {
-            @Override
-            public void onMeasure(int widthSpec, int heightSpec) {
-                super.onMeasure(widthSpec, heightSpec);
-                if (mAnchorView == null) return;
-
-                // Calculate how big the box is without shadow-induced padding.
-                FrameLayout.LayoutParams anchorParams =
-                        (FrameLayout.LayoutParams) mAnchorView.getLayoutParams();
-                int anchorViewWidth = searchBox.getMeasuredWidth() - mSearchBoxPadding.left
-                        - mSearchBoxPadding.right;
-                int anchorViewHeight =
-                        mSpacingSmall + mSearchBox.getMeasuredHeight() - mSearchBoxPadding.bottom;
-                if (anchorParams.width == anchorViewWidth
-                        && anchorParams.height == anchorViewHeight) {
-                    return;
-                }
-
-                // Move the anchor view up a little bit as a dirty hack until we can add a
-                // dimension. This allows the suggestion list to move up past the rounded corners of
-                // the search box.
-                anchorParams.topMargin = -(mSpacingSmall / 4);
-                anchorParams.width = anchorViewWidth;
-                anchorParams.height = anchorViewHeight;
-
-                // Measure the anchor view to match the search box's height without its side or
-                // bottom shadow padding. This isn't exactly what we need, but it's hard to get the
-                // correct behavior because LocationBarLayout overrides how the suggestions are
-                // laid out.
-                int anchorWidthSpec =
-                        MeasureSpec.makeMeasureSpec(anchorViewWidth, MeasureSpec.EXACTLY);
-                int anchorHeightSpec =
-                        MeasureSpec.makeMeasureSpec(anchorViewHeight, MeasureSpec.EXACTLY);
-                measureChild(mAnchorView, anchorWidthSpec, anchorHeightSpec);
-            }
-        };
-
+        ViewGroup contentView = (ViewGroup) LayoutInflater.from(this).inflate(
+                R.layout.search_activity, null, false);
         contentView.addOnLayoutChangeListener(this);
-        contentView.setId(R.id.control_container);
         return contentView;
     }
 
     @Override
     public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
-        if (mSearchBoxWidgetBounds != null) {
-            if (mAnimator == null) initializeAnimation();
-        } else {
-            // If there's no animation, then we can load the library immediately without worrying
-            // about jank.
-            beginLoadingLibrary();
-        }
         mContentView.removeOnLayoutChangeListener(this);
+        beginLoadingLibrary();
     }
 
     private void initializeDimensions() {
-        mSearchBoxWidgetBounds = getIntent().getSourceBounds();
-
         // Cache the padding of the Drawable that is used as the background for the search box.
         Drawable searchBackground =
                 ApiCompatibilityUtils.getDrawable(getResources(), R.drawable.card_single);
         searchBackground.getPadding(mSearchBoxPadding);
 
         // TODO(dfalcantara): Add values to the XML files instead of reusing random ones.
-        mSpacingSmall = getResources().getDimensionPixelSize(
-                R.dimen.tablet_toolbar_start_padding_no_buttons);
         mSpacingMedium =
                 getResources().getDimensionPixelSize(R.dimen.location_bar_incognito_badge_padding);
         mSpacingLarge =
                 getResources().getDimensionPixelSize(R.dimen.contextual_search_peek_promo_padding);
     }
 
-    private void initializeAnimation() {
-        assert SearchWidgetProvider.ANIMATE_TRANSITION;
-
-        // The bounds of the home screen widget are given in window space coordinates, so they need
-        // to be converted into conrdinates that are relative from the root View.  The converted
-        // bounds are used to animate the box moving from its widget location to the location at the
-        // top of the screen.
-        int[] rootWindowLocation = new int[2];
-        mContentView.getLocationInWindow(rootWindowLocation);
-
-        final Rect sourceRect = new Rect();
-        sourceRect.left = mSearchBoxWidgetBounds.left - rootWindowLocation[0];
-        sourceRect.right = mSearchBoxWidgetBounds.right - rootWindowLocation[0];
-        sourceRect.top = mSearchBoxWidgetBounds.top - rootWindowLocation[1];
-        sourceRect.bottom = mSearchBoxWidgetBounds.bottom - rootWindowLocation[1];
-
-        int[] targetBoxLocation = new int[2];
-        mSearchBox.getLocationInWindow(targetBoxLocation);
-        final Rect targetRect = new Rect();
-        targetRect.left = targetBoxLocation[0] - rootWindowLocation[0];
-        targetRect.right = targetRect.left + mSearchBox.getMeasuredWidth();
-        targetRect.top = targetBoxLocation[1] - rootWindowLocation[1];
-        targetRect.bottom = targetRect.top + mSearchBox.getMeasuredHeight();
-
-        mScrimView.setAnimationRects(sourceRect, targetRect);
-
-        mAnimator = ValueAnimator.ofFloat(0, 1);
-        mAnimator.setDuration(ANIMATION_DURATION_MS);
-        mAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                // Make the widget on the homescreen hide itself.
-                SearchWidgetProvider.updateAllWidgets();
-            }
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                // Defer loading the library until the box is in the right place to prevent jank.
-                beginLoadingLibrary();
-            }
-        });
-        mAnimator.addUpdateListener(mScrimView);
-
-        new Handler().post(new Runnable() {
-            @Override
-            public void run() {
-                mAnimator.start();
-            }
-        });
-    }
-
     private void beginLoadingLibrary() {
-        if (mIsNativeLoading) return;
-        mIsNativeLoading = true;
-
-        // Show the real search box and let the user type in it.
-        mScrimView.setInterpolatedValue(1.0f);
-        mSearchBox.setVisibility(View.VISIBLE);
         beginQuery();
         mHandler.post(new Runnable() {
             @Override
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityFadingBackgroundView.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityFadingBackgroundView.java
new file mode 100644
index 0000000..0f210a5
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityFadingBackgroundView.java
@@ -0,0 +1,26 @@
+// Copyright 2017 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.
+
+package org.chromium.chrome.browser.searchwidget;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import org.chromium.chrome.browser.widget.FadingBackgroundView;
+
+/**
+ * The FadingBackgroundView isn't used here, but is still animated by the LocationBarLayout.
+ * This interferes with the animation we need to show the widget moving to the right place.
+ */
+public class SearchActivityFadingBackgroundView extends FadingBackgroundView {
+    public SearchActivityFadingBackgroundView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    public void showFadingOverlay() {}
+
+    @Override
+    public void hideFadingOverlay(boolean fadeOut) {}
+}
\ No newline at end of file
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
similarity index 85%
rename from chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchLocationBarLayout.java
rename to chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
index 6cf4865..42ae712 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchLocationBarLayout.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -10,9 +10,10 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.omnibox.LocationBarLayout;
+import org.chromium.chrome.browser.omnibox.LocationBarPhone;
 
 /** Implementation of the {@link LocationBarLayout} that is displayed for widget searches. */
-class SearchLocationBarLayout extends LocationBarLayout {
+public class SearchActivityLocationBarLayout extends LocationBarPhone {
     /** Delegates calls out to the containing Activity. */
     public static interface Delegate {
         /** Load a URL in the associated tab. */
@@ -24,12 +25,9 @@
 
     private Delegate mDelegate;
 
-    public SearchLocationBarLayout(Context context, AttributeSet attrs) {
+    public SearchActivityLocationBarLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
         findViewById(R.id.google_g_container).setVisibility(View.GONE);
-
-        // TODO(dfalcantara|yusufo): Remove once upstream and the bar is inflated from XML.
-        onFinishInflate();
     }
 
     /** Set the {@link Delegate}. */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
index 035a48a..8527aed 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -11,14 +11,12 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Bundle;
 import android.support.v4.app.ActivityOptionsCompat;
 import android.text.TextUtils;
 import android.widget.RemoteViews;
 
-import org.chromium.base.ApplicationStatus;
 import org.chromium.base.ContextUtils;
 import org.chromium.base.Log;
 import org.chromium.base.ThreadUtils;
@@ -51,10 +49,6 @@
         }
     }
 
-    static final int INVALID_WIDGET_ID = -1;
-
-    static final boolean ANIMATE_TRANSITION = false;
-
     private static final String ACTION_START_TEXT_QUERY =
             "org.chromium.chrome.browser.searchwidget.START_TEXT_QUERY";
     private static final String ACTION_START_VOICE_QUERY =
@@ -64,11 +58,7 @@
 
     static final String EXTRA_START_VOICE_SEARCH =
             "org.chromium.chrome.browser.searchwidget.START_VOICE_SEARCH";
-    private static final String EXTRA_WIDGET_ID =
-            "org.chromium.chrome.browser.searchwidget.WIDGET_ID";
 
-    private static final String PREF_LAUNCHING_WIDGET_ID =
-            "org.chromium.chrome.browser.searchwidget.LAUNCHING_WIDGET_ID";
     private static final String PREF_SEARCH_ENGINE_SHORTNAME =
             "org.chromium.chrome.browser.searchwidget.SEARCH_ENGINE_SHORTNAME";
     static final String PREF_USE_HERB_TAB = "org.chromium.chrome.browser.searchwidget.USE_HERB_TAB";
@@ -107,10 +97,10 @@
     public void onReceive(Context context, Intent intent) {
         if (IntentHandler.isIntentChromeOrFirstParty(intent)) {
             if (ACTION_START_TEXT_QUERY.equals(intent.getAction())) {
-                startSearchActivity(context, intent, false);
+                startSearchActivity(context, false);
                 return;
             } else if (ACTION_START_VOICE_QUERY.equals(intent.getAction())) {
-                startSearchActivity(context, intent, true);
+                startSearchActivity(context, true);
                 return;
             }
         } else if (ACTION_UPDATE_ALL_WIDGETS.equals(intent.getAction())) {
@@ -120,10 +110,8 @@
         super.onReceive(context, intent);
     }
 
-    private void startSearchActivity(Context context, Intent intent, boolean startVoiceSearch) {
-        int widgetId = getLaunchingWidgetIdFromIntent(intent);
-        SearchWidgetProvider.setLaunchingWidgetId(widgetId);
-        Log.d(TAG, "Launching SearchActivity: ID=" + widgetId + " VOICE=" + startVoiceSearch);
+    private void startSearchActivity(Context context, boolean startVoiceSearch) {
+        Log.d(TAG, "Launching SearchActivity: VOICE=" + startVoiceSearch);
 
         // Launch the SearchActivity.
         Intent searchIntent = new Intent();
@@ -133,16 +121,9 @@
         searchIntent.putExtra(EXTRA_START_VOICE_SEARCH, startVoiceSearch);
 
         Bundle optionsBundle;
-        if (ANIMATE_TRANSITION) {
-            // Pass the widget's bounds along to allow moving the box into place.
-            Rect rect = intent.getSourceBounds();
-            if (rect != null) searchIntent.setSourceBounds(rect);
-            optionsBundle = ActivityOptionsCompat.makeCustomAnimation(context, 0, 0).toBundle();
-        } else {
-            optionsBundle = ActivityOptionsCompat
-                                    .makeCustomAnimation(context, R.anim.activity_open_enter, 0)
-                                    .toBundle();
-        }
+        optionsBundle =
+                ActivityOptionsCompat.makeCustomAnimation(context, R.anim.activity_open_enter, 0)
+                        .toBundle();
         context.startActivity(searchIntent, optionsBundle);
     }
 
@@ -162,12 +143,9 @@
     }
 
     private void updateWidget(Context context, int id) {
-        int launchingWidgetId = ANIMATE_TRANSITION ? getLaunchingWidgetId() : INVALID_WIDGET_ID;
-
         AppWidgetManager manager = AppWidgetManager.getInstance(context);
-        RemoteViews views = new RemoteViews(context.getPackageName(),
-                id == launchingWidgetId ? R.layout.search_widget_template_transparent
-                                        : R.layout.search_widget_template);
+        RemoteViews views =
+                new RemoteViews(context.getPackageName(), R.layout.search_widget_template);
 
         // Clicking on the widget fires an Intent back at this BroadcastReceiver, allowing control
         // over how the Activity is animated when it starts up.
@@ -239,38 +217,6 @@
                 ContextUtils.getApplicationContext(), SearchWidgetProvider.class.getName()));
     }
 
-    private int getLaunchingWidgetId() {
-        int launchingWidgetId = INVALID_WIDGET_ID;
-
-        // If the SearchActivity isn't in the foreground, the user must have exited it.
-        if (ApplicationStatus.getLastTrackedFocusedActivity() instanceof SearchActivity) {
-            launchingWidgetId = ContextUtils.getAppSharedPreferences().getInt(
-                    PREF_LAUNCHING_WIDGET_ID, INVALID_WIDGET_ID);
-        }
-
-        return launchingWidgetId;
-    }
-
-    /** Cache the ID of the widget that was used to launch the SearchActivity. */
-    static void setLaunchingWidgetId(int id) {
-        SharedPreferences prefs = ContextUtils.getAppSharedPreferences();
-        SharedPreferences.Editor editor = prefs.edit();
-        editor.putInt(PREF_LAUNCHING_WIDGET_ID, id);
-        editor.apply();
-    }
-
-    /** Parse out which widget launched the Activity from the given Intent. */
-    private static int getLaunchingWidgetIdFromIntent(Intent intent) {
-        String data = intent.getData() == null ? null : intent.getData().toString();
-        if (data == null) return INVALID_WIDGET_ID;
-
-        try {
-            return Integer.parseInt(data);
-        } catch (NumberFormatException e) {
-            return INVALID_WIDGET_ID;
-        }
-    }
-
     /** Creates a trusted Intent that lets the user begin performing queries. */
     private Intent createStartQueryIntent(Context context, String action, int widgetId) {
         Intent intent = new Intent(action, Uri.parse(String.valueOf(widgetId)));
diff --git a/chrome/android/java/strings/android_chrome_strings.grd b/chrome/android/java/strings/android_chrome_strings.grd
index 33f6215f..1b3bfd3 100644
--- a/chrome/android/java/strings/android_chrome_strings.grd
+++ b/chrome/android/java/strings/android_chrome_strings.grd
@@ -2852,12 +2852,17 @@
       </message>
 
       <!-- Search Widget strings -->
-      <message name="IDS_SEARCH_WIDGET_DEFAULT" desc= "Default text for the search widget">
+      <message name="IDS_SEARCH_WIDGET_DEFAULT" desc="Default text for the search widget">
         Search
       </message>
-      <message name="IDS_SEARCH_WITH_PRODUCT" desc= "Text for search widget with a search engine name">
+      <message name="IDS_SEARCH_WITH_PRODUCT" desc="Text for search widget with a search engine name">
         Search with <ph name="PRODUCT_NAME">%1$s<ex>Google</ex></ph>
       </message>
+
+      <!-- Launcher Shortcuts -->
+      <message name="IDS_DISABLED_INCOGNITO_LAUNCHER_SHORTCUT_MESSAGE" desc="Text for a toast displayed prompting the user to remove the disabled 'New incognito tab' app shortcut and recreate it.">
+        Remove and recreate this shortcut
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni
index 2111dae..229cb937 100644
--- a/chrome/android/java_sources.gni
+++ b/chrome/android/java_sources.gni
@@ -911,10 +911,10 @@
   "java/src/org/chromium/chrome/browser/rappor/RapporServiceBridge.java",
   "java/src/org/chromium/chrome/browser/rlz/RevenueStats.java",
   "java/src/org/chromium/chrome/browser/search_engines/TemplateUrlService.java",
-  "java/src/org/chromium/chrome/browser/searchwidget/BoxAnimatorScrim.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java",
+  "java/src/org/chromium/chrome/browser/searchwidget/SearchActivityFadingBackgroundView.java",
+  "java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchBoxDataProvider.java",
-  "java/src/org/chromium/chrome/browser/searchwidget/SearchLocationBarLayout.java",
   "java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java",
   "java/src/org/chromium/chrome/browser/services/AccountsChangedReceiver.java",
   "java/src/org/chromium/chrome/browser/services/AndroidEduAndChildAccountHelper.java",
@@ -1463,7 +1463,6 @@
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompletePhoneTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompleteServerCardTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerTest.java",
-  "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsUnitTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMultipleContactDetailsTest.java",
   "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java",
@@ -1618,6 +1617,7 @@
   "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterRouteTest.java",
   "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterSinkObservationTest.java",
   "junit/src/org/chromium/chrome/browser/media/router/ChromeMediaRouterTestBase.java",
+  "junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java",
   "junit/src/org/chromium/chrome/browser/media/router/cast/CastMessageHandlerTest.java",
   "junit/src/org/chromium/chrome/browser/media/router/cast/DiscoveryCallbackTest.java",
   "junit/src/org/chromium/chrome/browser/media/router/cast/JSONTestUtils.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerUnitTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerUnitTest.java
deleted file mode 100644
index b7f8bbb..0000000
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestJourneyLoggerUnitTest.java
+++ /dev/null
@@ -1,726 +0,0 @@
-// Copyright 2017 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.
-
-package org.chromium.chrome.browser.payments;
-
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import org.chromium.base.metrics.RecordHistogram;
-import org.chromium.base.test.BaseJUnit4ClassRunner;
-import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.test.util.ApplicationData;
-import org.chromium.components.payments.JourneyLogger;
-import org.chromium.content.browser.test.NativeLibraryTestRule;
-
-/**
- * Tests for the PaymentRequestJourneyLogger class.
- */
-@RunWith(BaseJUnit4ClassRunner.class)
-public class PaymentRequestJourneyLoggerUnitTest {
-    @Rule
-    public NativeLibraryTestRule mNativeLibraryTestRule = new NativeLibraryTestRule();
-
-    @Before
-    public void setUp() throws Exception {
-        ApplicationData.clearAppData(
-                InstrumentationRegistry.getInstrumentation().getTargetContext());
-        mNativeLibraryTestRule.loadNativeLibraryAndInitBrowserProcess();
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant does not use it and does not
-     * show the PaymentRequest to the user.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentNotCalled_NoShow() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // CanMakePayment was not used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_NOT_USED));
-
-        // There should be no completion stats since PR was not shown to the user
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant does not use it and the
-     * transaction is aborted.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndUserAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The merchant does not query CanMakePayment, show the PaymentRequest and the user
-        // aborts it.
-        logger.setShowCalled();
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // CanMakePayment was not used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_NOT_USED));
-
-        // There should be a record for an abort when CanMakePayment is not used but the PR is shown
-        // to the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant does not use it and the
-     * transaction is aborted.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndOtherAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The merchant does not query CanMakePayment, show the PaymentRequest and the user
-        // aborts it.
-        logger.setShowCalled();
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // CanMakePayment was not used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_NOT_USED));
-
-        // There should be a record for an abort when CanMakePayment is not used but the PR is shown
-        // to the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant does not use it and the
-     * transaction is completed.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndComplete() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The merchant does not query CanMakePayment, show the PaymentRequest and the user
-        // completes it.
-        logger.setShowCalled();
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // CanMakePayment was not used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_NOT_USED));
-
-        // There should be a record for a completion when CanMakePayment is not used but the PR is
-        // shown to the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns false and
-     * show is not called.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_FalseAndNoShow() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make payment and the PaymentRequest is not shown.
-        logger.setCanMakePaymentValue(false);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being false and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW));
-
-        // There should be no completion stats since PR was not shown to the user.
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns true and
-     * show is not called.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_TrueAndNoShow() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user can make a payment but the Payment Request is not shown.
-        logger.setCanMakePaymentValue(true);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_COULD_MAKE_PAYMENT));
-
-        // There should be no completion stats since PR was not shown to the user.
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns false, show
-     * is called but the transaction is aborted by the user.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndUserAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown but aborted.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(false);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW));
-
-        // There should be a record for an abort when CanMakePayment is false but the PR is shown to
-        // the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns false, show
-     * is called but the transaction is aborted.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndOtherAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown but aborted.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(false);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW));
-
-        // There should be a record for an abort when CanMakePayment is false but the PR is shown to
-        // the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns false,
-     * show is called and the transaction is completed.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndComplete() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown and completed.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(false);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW));
-
-        // There should be a record for a completion when CanMakePayment is false and the PR is
-        // shown to the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns true, show
-     * is called but the transaction is aborted by the user.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndUserAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown and completed.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(true);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW
-                                | JourneyLogger.CMP_SHOW_COULD_MAKE_PAYMENT));
-
-        // There should be a record for an abort when CanMakePayment is true and the PR is shown to
-        // the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns true, show
-     * is called but the transaction is aborted.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndOtherAbort() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown and completed.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(true);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW
-                                | JourneyLogger.CMP_SHOW_COULD_MAKE_PAYMENT));
-
-        // There should be a record for an abort when CanMakePayment is true and the PR is shown to
-        // the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-    }
-
-    /**
-     * Tests the canMakePayment stats for the case where the merchant uses it, returns true, show
-     * is called and the transaction is completed.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndComplete() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user cannot make a payment. the payment request is shown and completed.
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(true);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // CanMakePayment was used.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-
-        // The CanMakePayment effect on show should be recorded as being true and not shown.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.EffectOnShow",
-                        JourneyLogger.CMP_SHOW_DID_SHOW
-                                | JourneyLogger.CMP_SHOW_COULD_MAKE_PAYMENT));
-
-        // There should be a record for a completion when CanMakePayment is true and the PR is shown
-        // to the user.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests the canMakePayment metrics are not logged if the Payment Request was done in an
-     * incognito tab.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_CanMakePayment_IncognitoTab() {
-        JourneyLogger logger = new JourneyLogger(true /* is_incognito */);
-        assertNoLogForCanMakePayment();
-
-        // The user is in an incognito tab so CanMakePayment is always true;
-        logger.setShowCalled();
-        logger.setCanMakePaymentValue(true);
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // There should be no CanMakePayment metrics logged.
-        assertNoLogForCanMakePayment();
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_SuggestionsForEverything_Completed() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user had suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 1);
-
-        // Simulate that the user completes the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_SuggestionsForEverything_UserAborted() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user had suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 1);
-
-        // Simulate that the user aborts the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_SuggestionsForEverything_OtherAborted() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user had suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 1);
-
-        // Simulate that the user aborts the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_NoSuggestionsForEverything_Completed() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user did not have suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 0);
-
-        // Simulate that the user completes the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_NoSuggestionsForEverything_UserAborted() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user did not have suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 0);
-
-        // Simulate that the user aborts the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Tests that the completion status metrics based on whether the user had suggestions for all
-     * the requested sections are logged as correctly.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_NoSuggestionsForEverything_OtherAborted() {
-        JourneyLogger logger = new JourneyLogger(false /* is_incognito */);
-
-        // Simulate that the user did not have suggestions for all the requested sections.
-        logger.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 0);
-
-        // Simulate that the user aborts the checkout.
-        logger.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED);
-
-        // Make sure the appropriate metric was logged.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-
-        Assert.assertEquals(0,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_OTHER_ABORTED));
-    }
-
-    /**
-     * Tests that the metrics are logged correctly for two simultaneous Payment Requests.
-     */
-    @Test
-    @SmallTest
-    @Feature({"Payments"})
-    public void testRecordJourneyStatsHistograms_TwoPaymentRequests() {
-        JourneyLogger logger1 = new JourneyLogger(false /* is_incognito */);
-        JourneyLogger logger2 = new JourneyLogger(false /* is_incognito */);
-
-        // Make the two loggers have different data.
-        logger1.setShowCalled();
-        logger2.setShowCalled();
-
-        logger1.setCanMakePaymentValue(true);
-
-        logger1.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 1);
-        logger2.setNumberOfSuggestionsShown(JourneyLogger.SECTION_CREDIT_CARDS, 0);
-
-        // Simulate that the user completes one checkout and aborts the other.
-        logger1.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_COMPLETED);
-        logger2.recordJourneyStatsHistograms(JourneyLogger.COMPLETION_STATUS_USER_ABORTED);
-
-        // Make sure the appropriate metric was logged for logger1.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_USED));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_COMPLETED));
-
-        // Make sure the appropriate metric was logged for logger2.
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.UserDidNotHaveSuggestionsForEverything.EffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.Usage",
-                        JourneyLogger.CAN_MAKE_PAYMENT_NOT_USED));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramValueCountForTesting(
-                        "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
-                        JourneyLogger.COMPLETION_STATUS_USER_ABORTED));
-    }
-
-    /**
-     * Asserts that no histogram is logged.
-     */
-    private void assertNoLogForCanMakePayment() {
-        // Use stats.
-        for (int i = 0; i < JourneyLogger.CAN_MAKE_PAYMENT_USE_MAX; ++i) {
-            Assert.assertEquals(0,
-                    RecordHistogram.getHistogramValueCountForTesting(
-                            "PaymentRequest.CanMakePayment.Usage", i));
-        }
-
-        // Effect on show stats.
-        for (int i = 0; i < JourneyLogger.CMP_SHOW_MAX; ++i) {
-            Assert.assertEquals(0,
-                    RecordHistogram.getHistogramValueCountForTesting(
-                            "PaymentRequest.CanMakePayment.Used.EffectOnShow", i));
-        }
-
-        // Effect on completion stats.
-        for (int i = 0; i < JourneyLogger.COMPLETION_STATUS_MAX; ++i) {
-            Assert.assertEquals(0,
-                    RecordHistogram.getHistogramValueCountForTesting(
-                            "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion", i));
-            Assert.assertEquals(0,
-                    RecordHistogram.getHistogramValueCountForTesting(
-                            "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
-                            i));
-            Assert.assertEquals(0,
-                    RecordHistogram.getHistogramValueCountForTesting(
-                            "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
-                            i));
-        }
-    }
-}
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
index 3e5b997..f964109 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
@@ -12,6 +12,7 @@
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_VIEWER_NON_DAYDREAM;
 import static org.chromium.chrome.test.util.ChromeRestriction.RESTRICTION_TYPE_WEBVR_SUPPORTED;
 
+import android.os.Build;
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.filters.SmallTest;
@@ -327,7 +328,7 @@
         mockChecker.setMockReturnValue(checkerReturnValue);
         VrShellDelegate.getInstanceForTesting().overrideVrCoreVersionCheckerForTesting(mockChecker);
         String testName = "generic_webvr_page";
-        loadUrl(getHtmlTestFile(testName), 10);
+        loadUrl(getHtmlTestFile(testName), PAGE_LOAD_TIMEOUT_S);
         String displayFound = "VRDisplay Found";
         String barPresent = "InfoBar present";
         if (checkerReturnValue == VrCoreVersionChecker.VR_READY) {
@@ -406,4 +407,17 @@
     public void testInfoBarNotPresentWhenVrServicesNotSupported() throws InterruptedException {
         infoBarTestHelper(VrCoreVersionChecker.VR_NOT_SUPPORTED);
     }
+
+    /**
+     * Tests that the reported WebVR capabilities match expectations on the
+     * devices the WebVR tests are run on continuously.
+     */
+    @MediumTest
+    public void testDeviceCapabilitiesMatchExpectations() throws InterruptedException {
+        String testName = "test_device_capabilities_match_expectations";
+        loadUrl(getHtmlTestFile(testName), PAGE_LOAD_TIMEOUT_S);
+        assertTrue("VRDisplayFound", vrDisplayFound(mWebContents));
+        executeStepAndWait("stepCheckDeviceCapabilities('" + Build.DEVICE + "')", mWebContents);
+        endTest(mWebContents);
+    }
 }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java
new file mode 100644
index 0000000..04578c68
--- /dev/null
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/router/cast/CastMediaRouteProviderTest.java
@@ -0,0 +1,65 @@
+// Copyright 2017 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.
+
+package org.chromium.chrome.browser.media.router.cast;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+
+import android.support.v7.media.MediaRouter;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.media.router.ChromeMediaRouter;
+import org.chromium.chrome.browser.media.router.MediaRouteManager;
+import org.chromium.testing.local.LocalRobolectricTestRunner;
+
+import java.util.ArrayList;
+
+/**
+ * Robolectric tests for {@link CastMediaRouteProvider}.
+ */
+@RunWith(LocalRobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class CastMediaRouteProviderTest {
+    private static final String SUPPORTED_SOURCE =
+            "https://google.com/cast/#__castAppId__=DEADBEEF";
+    private static final String UNSUPPORTED_SOURCE = "https://example.com";
+
+    @Test
+    @Feature({"MediaRouter"})
+    public void testStartObservingMediaSinksNoMediaRouter() {
+        ChromeMediaRouter.setAndroidMediaRouterForTest(null);
+
+        MediaRouteManager mockManager = mock(MediaRouteManager.class);
+        CastMediaRouteProvider provider = CastMediaRouteProvider.create(mockManager);
+
+        provider.startObservingMediaSinks(SUPPORTED_SOURCE);
+
+        verify(mockManager, timeout(100))
+                .onSinksReceived(
+                        eq(SUPPORTED_SOURCE), same(provider), eq(new ArrayList<MediaSink>()));
+    }
+
+    @Test
+    @Feature({"MediaRouter"})
+    public void testStartObservingMediaSinksUnsupportedSource() {
+        ChromeMediaRouter.setAndroidMediaRouterForTest(mock(MediaRouter.class));
+
+        MediaRouteManager mockManager = mock(MediaRouteManager.class);
+        CastMediaRouteProvider provider = CastMediaRouteProvider.create(mockManager);
+
+        provider.startObservingMediaSinks(UNSUPPORTED_SOURCE);
+
+        verify(mockManager, timeout(100))
+                .onSinksReceived(
+                        eq(UNSUPPORTED_SOURCE), same(provider), eq(new ArrayList<MediaSink>()));
+    }
+}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index b982327f..1c468937 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -1926,10 +1926,16 @@
           Search or type URL
         </message>
         <message name="IDS_TOUCH_BAR_SEARCH"
-                   desc="Text for the search button in the touch bar when Google is not the default search provider. Pressing the button will bring focus to the omnibox.">
-          Search $1
+                   desc="Text for the search button in the touch bar when Google is not the default search provider. Pressing the button will bring focus to the omnibox. $1 is the name of the search provider)">
+          Search <ph name="SEARCH_ENGINE">$1<ex>Bing</ex></ph>
         </message>
 
+        <message name="IDS_TOOLTIP_TOUCH_BAR_BACK" desc="The tooltip for the touch bar back button.">
+        Touch to go back.
+        </message>
+        <message name="IDS_TOOLTIP_TOUCH_BAR_FORWARD" desc="The tooltip for the touch bar forward button.">
+        Touch to go forward.
+        </message>
       </if>
 
       <!-- Remove in-progress downloads confirmation dialog -->
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index b10d8287..91e464c 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2755,8 +2755,6 @@
       "android/download/intercept_download_resource_throttle.h",
       "android/download/items/offline_content_aggregator_factory_android.cc",
       "android/download/items/offline_content_aggregator_factory_android.h",
-      "android/download/mock_download_controller.cc",
-      "android/download/mock_download_controller.h",
       "android/download/ui/thumbnail_provider.cc",
       "android/download/ui/thumbnail_provider.h",
       "android/favicon_helper.cc",
@@ -4450,7 +4448,12 @@
     "//ui/gfx",
   ]
 
-  if (!is_android) {
+  if (is_android) {
+    sources += [
+      "android/download/mock_download_controller.cc",
+      "android/download/mock_download_controller.h",
+    ]
+  } else {
     sources += [
       "chooser_controller/mock_chooser_controller.cc",
       "chooser_controller/mock_chooser_controller.h",
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d52f73551..c2471f3 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1528,11 +1528,11 @@
     {"translate-lang-by-ulp", flag_descriptions::kTranslateLanguageByUlpName,
      flag_descriptions::kTranslateLanguageByUlpDescription, kOsAll,
      FEATURE_VALUE_TYPE(translate::kTranslateLanguageByULP)},
-#if defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+#if BUILDFLAG(ENABLE_NATIVE_NOTIFICATIONS)
     {"enable-native-notifications", flag_descriptions::kNotificationsNativeFlag,
      flag_descriptions::kNotificationsNativeFlagDescription, kOsMac | kOsLinux,
      FEATURE_VALUE_TYPE(features::kNativeNotifications)},
-#endif  // defined(OS_MACOSX) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+#endif  // ENABLE_NATIVE_NOTIFICATIONS
 #if defined(TOOLKIT_VIEWS)
     {"disable-views-rect-based-targeting",
      flag_descriptions::kViewsRectBasedTargetingName,
@@ -2193,9 +2193,9 @@
      kOsCrOS,
      SINGLE_VALUE_TYPE(
          chromeos::switches::kDisableSystemTimezoneAutomaticDetectionPolicy)},
-    {"enable-native-cups", flag_descriptions::kEnableNativeCupsName,
-     flag_descriptions::kEnableNativeCupsDescription, kOsCrOS,
-     SINGLE_VALUE_TYPE(switches::kEnableNativeCups)},
+    {"disable-native-cups", flag_descriptions::kDisableNativeCupsName,
+     flag_descriptions::kDisableNativeCupsDescription, kOsCrOS,
+     SINGLE_VALUE_TYPE(switches::kDisableNativeCups)},
     {"enable-encryption-migration",
      flag_descriptions::kEnableEncryptionMigrationName,
      flag_descriptions::kEnableEncryptionMigrationDescription, kOsCrOS,
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
index f726666..c0b69b8 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/android/banners/app_banner_infobar_delegate_android.h"
 
+#include <utility>
+
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
 #include "base/location.h"
@@ -50,15 +52,16 @@
     base::WeakPtr<AppBannerManager> weak_manager,
     const base::string16& app_title,
     std::unique_ptr<ShortcutInfo> shortcut_info,
-    std::unique_ptr<SkBitmap> icon,
+    const SkBitmap& primary_icon,
+    const SkBitmap& badge_icon,
     int event_request_id,
     webapk::InstallSource webapk_install_source) {
   bool is_webapk = ChromeWebApkHost::CanInstallWebApk();
   const GURL url = shortcut_info->url;
   auto infobar_delegate =
       base::WrapUnique(new banners::AppBannerInfoBarDelegateAndroid(
-          weak_manager, app_title, std::move(shortcut_info), std::move(icon),
-          event_request_id, is_webapk, webapk_install_source));
+          weak_manager, app_title, std::move(shortcut_info), primary_icon,
+          badge_icon, event_request_id, is_webapk, webapk_install_source));
   auto* raw_delegate = infobar_delegate.get();
   auto infobar = base::MakeUnique<AppBannerInfoBarAndroid>(
       std::move(infobar_delegate), url, is_webapk);
@@ -85,12 +88,12 @@
     content::WebContents* web_contents,
     const base::string16& app_title,
     const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-    std::unique_ptr<SkBitmap> icon,
+    const SkBitmap& icon,
     const std::string& native_app_package,
     const std::string& referrer,
     int event_request_id) {
   auto infobar_delegate = base::WrapUnique(new AppBannerInfoBarDelegateAndroid(
-      app_title, native_app_data, std::move(icon), native_app_package, referrer,
+      app_title, native_app_data, icon, native_app_package, referrer,
       event_request_id));
   return InfoBarService::FromWebContents(web_contents)
       ->AddInfoBar(base::MakeUnique<AppBannerInfoBarAndroid>(
@@ -207,14 +210,16 @@
     base::WeakPtr<AppBannerManager> weak_manager,
     const base::string16& app_title,
     std::unique_ptr<ShortcutInfo> shortcut_info,
-    std::unique_ptr<SkBitmap> icon,
+    const SkBitmap& primary_icon,
+    const SkBitmap& badge_icon,
     int event_request_id,
     bool is_webapk,
     webapk::InstallSource webapk_install_source)
     : weak_manager_(weak_manager),
       app_title_(app_title),
       shortcut_info_(std::move(shortcut_info)),
-      icon_(std::move(icon)),
+      primary_icon_(primary_icon),
+      badge_icon_(badge_icon),
       event_request_id_(event_request_id),
       has_user_interaction_(false),
       is_webapk_(is_webapk),
@@ -228,13 +233,13 @@
 AppBannerInfoBarDelegateAndroid::AppBannerInfoBarDelegateAndroid(
     const base::string16& app_title,
     const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-    std::unique_ptr<SkBitmap> icon,
+    const SkBitmap& icon,
     const std::string& native_app_package,
     const std::string& referrer,
     int event_request_id)
     : app_title_(app_title),
       native_app_data_(native_app_data),
-      icon_(std::move(icon)),
+      primary_icon_(icon),
       native_app_package_(native_app_package),
       referrer_(referrer),
       event_request_id_(event_request_id),
@@ -284,7 +289,7 @@
       web_contents, shortcut_info_->url.spec(), AppBannerSettingsHelper::WEB);
 
   ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, *shortcut_info_,
-                                            *icon_.get());
+                                            primary_icon_);
 
   SendBannerAccepted();
   return true;
@@ -332,8 +337,9 @@
   WebApkInstallService::FinishCallback callback =
       base::Bind(&AppBannerInfoBarDelegateAndroid::OnWebApkInstallFinished,
                  weak_ptr_factory_.GetWeakPtr());
-  ShortcutHelper::InstallWebApkWithSkBitmap(web_contents, *shortcut_info_,
-                                            *icon_.get(), callback);
+  ShortcutHelper::InstallWebApkWithSkBitmap(
+      web_contents, *shortcut_info_, primary_icon_, badge_icon_, callback);
+
   SendBannerAccepted();
 
   // Prevent the infobar from disappearing, because the infobar will show
@@ -379,7 +385,7 @@
         InfoBarService::WebContentsFromInfoBar(infobar());
     // Add webapp shortcut to the homescreen.
     ShortcutHelper::AddToLauncherWithSkBitmap(web_contents, *shortcut_info_,
-                                              *icon_.get());
+                                              primary_icon_);
   }
 
   infobar()->RemoveSelf();
@@ -404,7 +410,7 @@
 }
 
 gfx::Image AppBannerInfoBarDelegateAndroid::GetIcon() const {
-  return gfx::Image::CreateFrom1xBitmap(*icon_.get());
+  return gfx::Image::CreateFrom1xBitmap(primary_icon_);
 }
 
 void AppBannerInfoBarDelegateAndroid::InfoBarDismissed() {
diff --git a/chrome/browser/android/banners/app_banner_infobar_delegate_android.h b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
index d440cd45..6552a14 100644
--- a/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
+++ b/chrome/browser/android/banners/app_banner_infobar_delegate_android.h
@@ -14,6 +14,7 @@
 #include "base/strings/string16.h"
 #include "chrome/browser/android/webapk/webapk_metrics.h"
 #include "components/infobars/core/confirm_infobar_delegate.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 #include "ui/gfx/image/image.h"
 
 namespace content {
@@ -36,14 +37,14 @@
  public:
   // Creates an infobar and delegate for promoting the installation of a web
   // app, and adds the infobar to the InfoBarManager for |web_contents|.
-  static bool Create(
-      content::WebContents* web_contents,
-      base::WeakPtr<AppBannerManager> weak_manager,
-      const base::string16& app_title,
-      std::unique_ptr<ShortcutInfo> info,
-      std::unique_ptr<SkBitmap> icon,
-      int event_request_id,
-      webapk::InstallSource webapk_install_source);
+  static bool Create(content::WebContents* web_contents,
+                     base::WeakPtr<AppBannerManager> weak_manager,
+                     const base::string16& app_title,
+                     std::unique_ptr<ShortcutInfo> info,
+                     const SkBitmap& primary_icon,
+                     const SkBitmap& badge_icon,
+                     int event_request_id,
+                     webapk::InstallSource webapk_install_source);
 
   // Creates an infobar and delegate for promoting the installation of an
   // Android app, and adds the infobar to the InfoBarManager for |web_contents|.
@@ -51,7 +52,7 @@
       content::WebContents* web_contents,
       const base::string16& app_title,
       const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-      std::unique_ptr<SkBitmap> icon,
+      const SkBitmap& icon,
       const std::string& native_app_package,
       const std::string& referrer,
       int event_request_id);
@@ -94,7 +95,8 @@
       base::WeakPtr<AppBannerManager> weak_manager,
       const base::string16& app_title,
       std::unique_ptr<ShortcutInfo> info,
-      std::unique_ptr<SkBitmap> icon,
+      const SkBitmap& primary_icon,
+      const SkBitmap& badge_icon,
       int event_request_id,
       bool is_webapk,
       webapk::InstallSource webapk_install_source);
@@ -103,7 +105,7 @@
   AppBannerInfoBarDelegateAndroid(
       const base::string16& app_title,
       const base::android::ScopedJavaGlobalRef<jobject>& native_app_data,
-      std::unique_ptr<SkBitmap> icon,
+      const SkBitmap& icon,
       const std::string& native_app_package,
       const std::string& referrer,
       int event_request_id);
@@ -148,7 +150,8 @@
 
   base::android::ScopedJavaGlobalRef<jobject> native_app_data_;
 
-  std::unique_ptr<SkBitmap> icon_;
+  const SkBitmap primary_icon_;
+  const SkBitmap badge_icon_;
 
   std::string native_app_package_;
   std::string referrer_;
diff --git a/chrome/browser/android/banners/app_banner_manager_android.cc b/chrome/browser/android/banners/app_banner_manager_android.cc
index 81d5e0ac..2e0c8ab 100644
--- a/chrome/browser/android/banners/app_banner_manager_android.cc
+++ b/chrome/browser/android/banners/app_banner_manager_android.cc
@@ -5,7 +5,6 @@
 #include "chrome/browser/android/banners/app_banner_manager_android.h"
 
 #include <memory>
-#include <utility>
 
 #include "base/android/jni_android.h"
 #include "base/android/jni_string.h"
@@ -24,7 +23,6 @@
 #include "content/public/common/frame_navigate_params.h"
 #include "jni/AppBannerManager_jni.h"
 #include "net/base/url_util.h"
-#include "third_party/skia/include/core/SkBitmap.h"
 
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertJavaStringToUTF16;
@@ -39,12 +37,14 @@
 std::unique_ptr<ShortcutInfo> CreateShortcutInfo(
     const GURL& manifest_url,
     const content::Manifest& manifest,
-    const GURL& icon_url) {
+    const GURL& primary_icon_url,
+    const GURL& badge_icon_url) {
   auto shortcut_info = base::MakeUnique<ShortcutInfo>(GURL());
   if (!manifest.IsEmpty()) {
     shortcut_info->UpdateFromManifest(manifest);
     shortcut_info->manifest_url = manifest_url;
-    shortcut_info->best_primary_icon_url = icon_url;
+    shortcut_info->best_primary_icon_url = primary_icon_url;
+    shortcut_info->best_badge_icon_url = badge_icon_url;
     shortcut_info->UpdateSource(ShortcutInfo::SOURCE_APP_BANNER);
   }
 
@@ -210,7 +210,7 @@
     DCHECK(!data.badge_icon_url.is_empty());
 
     badge_icon_url_ = data.badge_icon_url;
-    badge_icon_.reset(new SkBitmap(*data.badge_icon));
+    badge_icon_ = *data.badge_icon;
   }
 
   AppBannerManager::OnDidPerformInstallableCheck(data);
@@ -225,7 +225,7 @@
   if (!is_active())
     return;
 
-  primary_icon_.reset(new SkBitmap(bitmap));
+  primary_icon_ = bitmap;
   SendBannerPromptRequest();
 }
 
@@ -240,11 +240,11 @@
   DCHECK(contents);
 
   if (native_app_data_.is_null()) {
-    // TODO(zpeng): Add badge to WebAPK installation flow.
     if (AppBannerInfoBarDelegateAndroid::Create(
             contents, GetWeakPtr(), app_title_,
-            CreateShortcutInfo(manifest_url_, manifest_, primary_icon_url_),
-            std::move(primary_icon_), event_request_id(),
+            CreateShortcutInfo(manifest_url_, manifest_, primary_icon_url_,
+                               badge_icon_url_),
+            primary_icon_, badge_icon_, event_request_id(),
             webapk::INSTALL_SOURCE_BANNER)) {
       RecordDidShowBanner("AppBanner.WebApp.Shown");
       TrackDisplayEvent(DISPLAY_EVENT_WEB_APP_BANNER_CREATED);
@@ -254,7 +254,7 @@
     }
   } else {
     if (AppBannerInfoBarDelegateAndroid::Create(
-            contents, app_title_, native_app_data_, std::move(primary_icon_),
+            contents, app_title_, native_app_data_, primary_icon_,
             native_app_package_, referrer_, event_request_id())) {
       RecordDidShowBanner("AppBanner.NativeApp.Shown");
       TrackDisplayEvent(DISPLAY_EVENT_NATIVE_APP_BANNER_CREATED);
diff --git a/chrome/browser/android/banners/app_banner_manager_android.h b/chrome/browser/android/banners/app_banner_manager_android.h
index 8eaddb4..d784833 100644
--- a/chrome/browser/android/banners/app_banner_manager_android.h
+++ b/chrome/browser/android/banners/app_banner_manager_android.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "chrome/browser/banners/app_banner_manager.h"
 #include "content/public/browser/web_contents_user_data.h"
+#include "third_party/skia/include/core/SkBitmap.h"
 
 namespace banners {
 
@@ -116,7 +117,7 @@
   GURL badge_icon_url_;
 
   // The badge icon object.
-  std::unique_ptr<SkBitmap> badge_icon_;
+  SkBitmap badge_icon_;
 
   // The Java-side AppBannerManager.
   base::android::ScopedJavaGlobalRef<jobject> java_banner_manager_;
diff --git a/chrome/browser/android/devtools_manager_delegate_android.cc b/chrome/browser/android/devtools_manager_delegate_android.cc
index 96f58833..0d84421 100644
--- a/chrome/browser/android/devtools_manager_delegate_android.cc
+++ b/chrome/browser/android/devtools_manager_delegate_android.cc
@@ -186,14 +186,6 @@
       DevToolsAgentHost::kTypeOther;
 }
 
-std::string DevToolsManagerDelegateAndroid::GetTargetTitle(
-    content::RenderFrameHost* host) {
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(host);
-  TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
-  return tab ? base::UTF16ToUTF8(tab->GetTitle()) : "";
-}
-
 bool DevToolsManagerDelegateAndroid::DiscoverTargets(
       const DevToolsAgentHost::DiscoveryCallback& callback) {
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/android/devtools_manager_delegate_android.h b/chrome/browser/android/devtools_manager_delegate_android.h
index 8b9f817..4aa8d17 100644
--- a/chrome/browser/android/devtools_manager_delegate_android.h
+++ b/chrome/browser/android/devtools_manager_delegate_android.h
@@ -27,7 +27,6 @@
       content::DevToolsAgentHost* agent_host,
       base::DictionaryValue* command_dict) override;
   std::string GetTargetType(content::RenderFrameHost* host) override;
-  std::string GetTargetTitle(content::RenderFrameHost* host) override;
   bool DiscoverTargets(
       const content::DevToolsAgentHost::DiscoveryCallback& callback) override;
   scoped_refptr<content::DevToolsAgentHost> CreateNewTarget(
diff --git a/chrome/browser/android/ntp/ntp_snippets_launcher.cc b/chrome/browser/android/ntp/ntp_snippets_launcher.cc
index 819e569e..b2476800 100644
--- a/chrome/browser/android/ntp/ntp_snippets_launcher.cc
+++ b/chrome/browser/android/ntp/ntp_snippets_launcher.cc
@@ -39,6 +39,13 @@
   return Java_SnippetsLauncher_unschedule(env, java_launcher_);
 }
 
+bool NTPSnippetsLauncher::IsOnUnmeteredConnection() {
+  DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+  JNIEnv* env = base::android::AttachCurrentThread();
+  return Java_SnippetsLauncher_isOnUnmeteredConnection(env, java_launcher_);
+}
+
 NTPSnippetsLauncher::NTPSnippetsLauncher() {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
 
diff --git a/chrome/browser/android/ntp/ntp_snippets_launcher.h b/chrome/browser/android/ntp/ntp_snippets_launcher.h
index 002b6e7..7964ba8 100644
--- a/chrome/browser/android/ntp/ntp_snippets_launcher.h
+++ b/chrome/browser/android/ntp/ntp_snippets_launcher.h
@@ -22,6 +22,7 @@
   bool Schedule(base::TimeDelta period_wifi,
                 base::TimeDelta period_fallback) override;
   bool Unschedule() override;
+  bool IsOnUnmeteredConnection() override;
 
  private:
   friend struct base::LazyInstanceTraitsBase<NTPSnippetsLauncher>;
diff --git a/chrome/browser/android/shortcut_helper.cc b/chrome/browser/android/shortcut_helper.cc
index 4016277..72b389a6b 100644
--- a/chrome/browser/android/shortcut_helper.cc
+++ b/chrome/browser/android/shortcut_helper.cc
@@ -157,10 +157,11 @@
 void ShortcutHelper::InstallWebApkWithSkBitmap(
     content::WebContents* web_contents,
     const ShortcutInfo& info,
-    const SkBitmap& icon_bitmap,
+    const SkBitmap& primary_icon_bitmap,
+    const SkBitmap& badge_icon_bitmap,
     const WebApkInstallService::FinishCallback& callback) {
   WebApkInstallService::Get(web_contents->GetBrowserContext())
-      ->InstallAsync(info, icon_bitmap, callback);
+      ->InstallAsync(info, primary_icon_bitmap, badge_icon_bitmap, callback);
   webapk::TrackGooglePlayInstallState(GooglePlayInstallState::SUPPORTED);
 }
 
diff --git a/chrome/browser/android/shortcut_helper.h b/chrome/browser/android/shortcut_helper.h
index 331c952..43755d6dd 100644
--- a/chrome/browser/android/shortcut_helper.h
+++ b/chrome/browser/android/shortcut_helper.h
@@ -33,18 +33,17 @@
   static bool RegisterShortcutHelper(JNIEnv* env);
 
   // Adds a shortcut to the launcher using a SkBitmap. The type of shortcut
-  // added depends on the properties in |info|. Calls one of
-  // InstallWebApkInBackgroundWithSkBitmap, AddWebappInBackgroundWithSkBitmap,
-  // or AddShortcutInBackgroundWithSkBitmap.
+  // added depends on the properties in |info|.
   static void AddToLauncherWithSkBitmap(content::WebContents* web_contents,
                                         const ShortcutInfo& info,
                                         const SkBitmap& icon_bitmap);
 
   // Installs WebAPK and adds shortcut to the launcher.
   static void InstallWebApkWithSkBitmap(
-      content::WebContents* web_conetnts,
+      content::WebContents* web_contents,
       const ShortcutInfo& info,
-      const SkBitmap& icon_bitmap,
+      const SkBitmap& primary_icon_bitmap,
+      const SkBitmap& badge_icon_bitmap,
       const WebApkInstallService::FinishCallback& callback);
 
   // Shows toast notifying user that a WebAPK install is already in progress
@@ -70,8 +69,8 @@
 
   // Fetches the splash screen image and stores it inside the WebappDataStorage
   // of the webapp. The WebappDataStorage object *must* have been previously
-  // created by |AddShortcutInBackgroundWithSkBitmap|; this method should be
-  // passed as a closure to that method.
+  // created by AddToLauncherWithSkBitmap(); this method should be passed as a
+  // closure to that method.
   static void FetchSplashScreenImage(content::WebContents* web_contents,
                                      const GURL& image_url,
                                      const int ideal_splash_image_size_in_px,
diff --git a/chrome/browser/android/webapk/webapk_install_service.cc b/chrome/browser/android/webapk/webapk_install_service.cc
index 4319c03..49c7b5d 100644
--- a/chrome/browser/android/webapk/webapk_install_service.cc
+++ b/chrome/browser/android/webapk/webapk_install_service.cc
@@ -27,14 +27,15 @@
 }
 
 void WebApkInstallService::InstallAsync(const ShortcutInfo& shortcut_info,
-                                        const SkBitmap& shortcut_icon,
+                                        const SkBitmap& primary_icon,
+                                        const SkBitmap& badge_icon,
                                         const FinishCallback& finish_callback) {
   DCHECK(!IsInstallInProgress(shortcut_info.manifest_url));
 
   installs_.insert(shortcut_info.manifest_url);
 
   WebApkInstaller::InstallAsync(
-      browser_context_, shortcut_info, shortcut_icon,
+      browser_context_, shortcut_info, primary_icon, badge_icon,
       base::Bind(&WebApkInstallService::OnFinishedInstall,
                  weak_ptr_factory_.GetWeakPtr(), shortcut_info.manifest_url,
                  finish_callback));
diff --git a/chrome/browser/android/webapk/webapk_install_service.h b/chrome/browser/android/webapk/webapk_install_service.h
index 5e5a0f4..5914698 100644
--- a/chrome/browser/android/webapk/webapk_install_service.h
+++ b/chrome/browser/android/webapk/webapk_install_service.h
@@ -58,7 +58,8 @@
   // Google Play to install the downloaded WebAPK. Calls |callback| once the
   // install completed or failed.
   void InstallAsync(const ShortcutInfo& shortcut_info,
-                    const SkBitmap& shortcut_icon,
+                    const SkBitmap& primary_icon,
+                    const SkBitmap& badge_icon,
                     const FinishCallback& finish_callback);
 
   // Talks to the Chrome WebAPK server to update a WebAPK on the server and to
diff --git a/chrome/browser/android/webapk/webapk_installer.cc b/chrome/browser/android/webapk/webapk_installer.cc
index 8e1d019..0f13ea69 100644
--- a/chrome/browser/android/webapk/webapk_installer.cc
+++ b/chrome/browser/android/webapk/webapk_installer.cc
@@ -98,11 +98,19 @@
 #endif
 }
 
+// Populates the webapk::Image::image_data field of |image| with |icon|.
+void SetImageData(webapk::Image* image, const SkBitmap& icon) {
+  std::vector<unsigned char> png_bytes;
+  gfx::PNGCodec::EncodeBGRASkBitmap(icon, false, &png_bytes);
+  image->set_image_data(&png_bytes.front(), png_bytes.size());
+}
+
 // Populates webapk::WebApk and returns it.
 // Must be called on a worker thread because it encodes an SkBitmap.
 std::unique_ptr<webapk::WebApk> BuildWebApkProtoInBackground(
     const ShortcutInfo& shortcut_info,
-    const SkBitmap& shortcut_icon,
+    const SkBitmap& primary_icon,
+    const SkBitmap& badge_icon,
     const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
     bool is_manifest_stale) {
   DCHECK(content::BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
@@ -130,21 +138,32 @@
   std::string* scope = web_app_manifest->add_scopes();
   scope->assign(GetScope(shortcut_info).spec());
 
-  webapk::Image* best_image = web_app_manifest->add_icons();
-  std::string best_primary_icon_url =
-      shortcut_info.best_primary_icon_url.spec();
-  best_image->set_src(best_primary_icon_url);
-  auto it = icon_url_to_murmur2_hash.find(best_primary_icon_url);
-  if (it != icon_url_to_murmur2_hash.end())
-    best_image->set_hash(it->second);
-  std::vector<unsigned char> png_bytes;
-  gfx::PNGCodec::EncodeBGRASkBitmap(shortcut_icon, false, &png_bytes);
-  best_image->set_image_data(&png_bytes.front(), png_bytes.size());
+  if (shortcut_info.best_primary_icon_url.is_empty()) {
+    // Update when web manifest is no longer available.
+    webapk::Image* best_primary_icon_image = web_app_manifest->add_icons();
+    SetImageData(best_primary_icon_image, primary_icon);
+    best_primary_icon_image->add_usages(webapk::Image::PRIMARY_ICON);
+
+    if (!badge_icon.drawsNothing()) {
+      webapk::Image* best_badge_icon_image = web_app_manifest->add_icons();
+      SetImageData(best_badge_icon_image, badge_icon);
+      best_badge_icon_image->add_usages(webapk::Image::BADGE_ICON);
+    }
+  }
 
   for (const auto& entry : icon_url_to_murmur2_hash) {
-    if (entry.first == shortcut_info.best_primary_icon_url.spec())
-      continue;
     webapk::Image* image = web_app_manifest->add_icons();
+    if (entry.first == shortcut_info.best_primary_icon_url.spec()) {
+      SetImageData(image, primary_icon);
+      image->add_usages(webapk::Image::PRIMARY_ICON);
+    }
+    if (entry.first == shortcut_info.best_badge_icon_url.spec()) {
+      if (shortcut_info.best_badge_icon_url !=
+          shortcut_info.best_primary_icon_url) {
+        SetImageData(image, badge_icon);
+      }
+      image->add_usages(webapk::Image::BADGE_ICON);
+    }
     image->set_src(entry.first);
     image->set_hash(entry.second);
   }
@@ -177,11 +196,12 @@
 // static
 void WebApkInstaller::InstallAsync(content::BrowserContext* context,
                                    const ShortcutInfo& shortcut_info,
-                                   const SkBitmap& shortcut_icon,
+                                   const SkBitmap& primary_icon,
+                                   const SkBitmap& badge_icon,
                                    const FinishCallback& finish_callback) {
   // The installer will delete itself when it is done.
   WebApkInstaller* installer =
-      new WebApkInstaller(context, shortcut_info, shortcut_icon);
+      new WebApkInstaller(context, shortcut_info, primary_icon, badge_icon);
   installer->InstallAsync(finish_callback);
 }
 
@@ -189,15 +209,15 @@
 void WebApkInstaller::UpdateAsync(
     content::BrowserContext* context,
     const ShortcutInfo& shortcut_info,
-    const SkBitmap& shortcut_icon,
+    const SkBitmap& primary_icon,
     const std::string& webapk_package,
     int webapk_version,
     const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
     bool is_manifest_stale,
     const FinishCallback& finish_callback) {
   // The installer will delete itself when it is done.
-  WebApkInstaller* installer =
-      new WebApkInstaller(context, shortcut_info, shortcut_icon);
+  WebApkInstaller* installer = new WebApkInstaller(
+      context, shortcut_info, primary_icon, SkBitmap());
   installer->UpdateAsync(webapk_package, webapk_version,
                          icon_url_to_murmur2_hash, is_manifest_stale,
                          finish_callback);
@@ -240,8 +260,8 @@
     bool is_manifest_stale) {
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
-                 icon_url_to_murmur2_hash, is_manifest_stale),
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
+                 badge_icon_, icon_url_to_murmur2_hash, is_manifest_stale),
       base::Bind(&OnWebApkProtoBuilt, callback));
 }
 
@@ -299,11 +319,13 @@
 
 WebApkInstaller::WebApkInstaller(content::BrowserContext* browser_context,
                                  const ShortcutInfo& shortcut_info,
-                                 const SkBitmap& shortcut_icon)
+                                 const SkBitmap& primary_icon,
+                                 const SkBitmap& badge_icon)
     : request_context_getter_(
           Profile::FromBrowserContext(browser_context)->GetRequestContext()),
       shortcut_info_(shortcut_info),
-      shortcut_icon_(shortcut_icon),
+      primary_icon_(primary_icon),
+      badge_icon_(badge_icon),
       server_url_(GetServerUrl()),
       webapk_server_timeout_ms_(kWebApkDownloadUrlTimeoutMs),
       relax_updates_(false),
@@ -334,7 +356,7 @@
   // should be fast because the icon should be in the HTTP cache.
   WebApkIconHasher::DownloadAndComputeMurmur2Hash(
       request_context_getter_, shortcut_info_.best_primary_icon_url,
-      base::Bind(&WebApkInstaller::OnGotIconMurmur2Hash,
+      base::Bind(&WebApkInstaller::OnGotPrimaryIconMurmur2Hash,
                  weak_ptr_factory_.GetWeakPtr()));
 }
 
@@ -350,8 +372,8 @@
 
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
-                 icon_url_to_murmur2_hash, is_manifest_stale),
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
+                 badge_icon_, icon_url_to_murmur2_hash, is_manifest_stale),
       base::Bind(&WebApkInstaller::SendUpdateWebApkRequest,
                  weak_ptr_factory_.GetWeakPtr(), webapk_version));
 }
@@ -402,26 +424,52 @@
   InstallOrUpdateWebApk(response->package_name(), version, response->token());
 }
 
-void WebApkInstaller::OnGotIconMurmur2Hash(
-    const std::string& icon_murmur2_hash) {
-  // An empty hash indicates that |icon_hasher_| encountered an error.
-  if (icon_murmur2_hash.empty()) {
+void WebApkInstaller::OnGotPrimaryIconMurmur2Hash(
+    const std::string& primary_icon_hash) {
+  // An empty hash indicates an error during hash calculation.
+  if (primary_icon_hash.empty()) {
     OnResult(WebApkInstallResult::FAILURE);
     return;
   }
 
+  if (!shortcut_info_.best_badge_icon_url.is_empty() &&
+      shortcut_info_.best_badge_icon_url !=
+          shortcut_info_.best_primary_icon_url) {
+    WebApkIconHasher::DownloadAndComputeMurmur2Hash(
+        request_context_getter_, shortcut_info_.best_badge_icon_url,
+        base::Bind(&WebApkInstaller::OnGotBadgeIconMurmur2Hash,
+                   weak_ptr_factory_.GetWeakPtr(), true, primary_icon_hash));
+  } else {
+    OnGotBadgeIconMurmur2Hash(false, primary_icon_hash, "");
+  }
+}
+
+void WebApkInstaller::OnGotBadgeIconMurmur2Hash(
+    bool did_fetch_badge_icon,
+    const std::string& primary_icon_hash,
+    const std::string& badge_icon_hash) {
+  // An empty hash indicates an error during hash calculation.
+  if (did_fetch_badge_icon && badge_icon_hash.empty()) {
+    OnResult(WebApkInstallResult::FAILURE);
+    return;
+  }
+
+  // Maps icon URLs to Murmur2 hashes.
   std::map<std::string, std::string> icon_url_to_murmur2_hash;
   for (const std::string& icon_url : shortcut_info_.icon_urls) {
-    if (icon_url != shortcut_info_.best_primary_icon_url.spec())
-      icon_url_to_murmur2_hash[icon_url] = "";
+    if (icon_url == shortcut_info_.best_primary_icon_url.spec())
+      icon_url_to_murmur2_hash[icon_url] = primary_icon_hash;
+    else if (icon_url == shortcut_info_.best_badge_icon_url.spec())
+      icon_url_to_murmur2_hash[icon_url] = badge_icon_hash;
     else
-      icon_url_to_murmur2_hash[icon_url] = icon_murmur2_hash;
+      icon_url_to_murmur2_hash[icon_url] = "";
   }
 
   base::PostTaskAndReplyWithResult(
       GetBackgroundTaskRunner().get(), FROM_HERE,
-      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, shortcut_icon_,
-                 icon_url_to_murmur2_hash, false /* is_manifest_stale */),
+      base::Bind(&BuildWebApkProtoInBackground, shortcut_info_, primary_icon_,
+                 badge_icon_, icon_url_to_murmur2_hash,
+                 false /* is_manifest_stale */),
       base::Bind(&WebApkInstaller::SendCreateWebApkRequest,
                  weak_ptr_factory_.GetWeakPtr()));
 }
diff --git a/chrome/browser/android/webapk/webapk_installer.h b/chrome/browser/android/webapk/webapk_installer.h
index be39742..1b7cd4e 100644
--- a/chrome/browser/android/webapk/webapk_installer.h
+++ b/chrome/browser/android/webapk/webapk_installer.h
@@ -46,16 +46,18 @@
   // APK to be installed. Calls |callback| once the install completed or failed.
   static void InstallAsync(content::BrowserContext* context,
                            const ShortcutInfo& shortcut_info,
-                           const SkBitmap& shortcut_icon,
+                           const SkBitmap& primary_icon,
+                           const SkBitmap& badge_icon,
                            const FinishCallback& finish_callback);
 
+  // TODO(zpeng): Add badge icon to WebAPK update route.
   // Creates a self-owned WebApkInstaller instance and talks to the Chrome
   // WebAPK server to update a WebAPK on the server and locally requests the
   // APK to be installed. Calls |callback| once the install completed or failed.
   static void UpdateAsync(
       content::BrowserContext* context,
       const ShortcutInfo& shortcut_info,
-      const SkBitmap& shortcut_icon,
+      const SkBitmap& primary_icon,
       const std::string& webapk_package,
       int webapk_version,
       const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
@@ -98,7 +100,8 @@
  protected:
   WebApkInstaller(content::BrowserContext* browser_context,
                   const ShortcutInfo& shortcut_info,
-                  const SkBitmap& shortcut_icon);
+                  const SkBitmap& primary_icon,
+                  const SkBitmap& badge_icon);
 
   // Returns whether the device supports installing WebAPKs.
   virtual bool CanInstallWebApks();
@@ -140,8 +143,15 @@
   // net::URLFetcherDelegate:
   void OnURLFetchComplete(const net::URLFetcher* source) override;
 
-  // Called with the computed Murmur2 hash for the app icon.
-  void OnGotIconMurmur2Hash(const std::string& icon_murmur2_hash);
+  // Called with the computed Murmur2 hash for the primary icon.
+  void OnGotPrimaryIconMurmur2Hash(const std::string& primary_icon_hash);
+
+  // Called with the computed Murmur2 hash for the badge icon, and
+  // |did_fetch_badge_icon| to indicate whether there was an attempt to fetch
+  // badge icon.
+  void OnGotBadgeIconMurmur2Hash(bool did_fetch_badge_icon,
+                                 const std::string& primary_icon_hash,
+                                 const std::string& badge_icon_hash);
 
   // Sends request to WebAPK server to create WebAPK. During a successful
   // request the WebAPK server responds with the URL of the generated WebAPK.
@@ -178,8 +188,11 @@
   // Web Manifest info.
   const ShortcutInfo shortcut_info_;
 
-  // WebAPK app icon.
-  const SkBitmap shortcut_icon_;
+  // WebAPK primary icon.
+  const SkBitmap primary_icon_;
+
+  // WebAPK badge icon.
+  const SkBitmap badge_icon_;
 
   // WebAPK server URL.
   GURL server_url_;
diff --git a/chrome/browser/android/webapk/webapk_installer_unittest.cc b/chrome/browser/android/webapk/webapk_installer_unittest.cc
index cad8103..bd885d49 100644
--- a/chrome/browser/android/webapk/webapk_installer_unittest.cc
+++ b/chrome/browser/android/webapk/webapk_installer_unittest.cc
@@ -28,6 +28,7 @@
 #include "net/test/embedded_test_server/http_request.h"
 #include "net/test/embedded_test_server/http_response.h"
 #include "net/url_request/url_request_test_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "url/gurl.h"
@@ -40,10 +41,11 @@
 // URL of mock WebAPK server.
 const char* kServerUrl = "/webapkserver/";
 
-// The best primary icon URL from Web Manifest. We use a random file in the test
+// The URLs of best icons from Web Manifest. We use a random file in the test
 // data directory. Since WebApkInstaller does not try to decode the file as an
 // image it is OK that the file is not an image.
 const char* kBestPrimaryIconUrl = "/simple.html";
+const char* kBestBadgeIconUrl = "/nostore.html";
 
 // URL of file to download from the WebAPK server. We use a random file in the
 // test data directory.
@@ -61,9 +63,13 @@
  public:
   TestWebApkInstaller(content::BrowserContext* browser_context,
                       const ShortcutInfo& shortcut_info,
-                      const SkBitmap& shortcut_icon,
+                      const SkBitmap& primary_icon,
+                      const SkBitmap& badge_icon,
                       bool can_install_webapks)
-      : WebApkInstaller(browser_context, shortcut_info, shortcut_icon),
+      : WebApkInstaller(browser_context,
+                        shortcut_info,
+                        primary_icon,
+                        badge_icon),
         can_install_webapks_(can_install_webapks) {}
 
   bool CanInstallWebApks() override { return can_install_webapks_; }
@@ -93,9 +99,11 @@
 class WebApkInstallerRunner {
  public:
   WebApkInstallerRunner(content::BrowserContext* browser_context,
-                        const GURL& best_primary_icon_url)
+                        const GURL& best_primary_icon_url,
+                        const GURL& best_badge_icon_url)
       : browser_context_(browser_context),
         best_primary_icon_url_(best_primary_icon_url),
+        best_badge_icon_url_(best_badge_icon_url),
         can_install_webapks_(true) {}
 
   ~WebApkInstallerRunner() {}
@@ -115,7 +123,8 @@
     const int kWebApkVersion = 1;
 
     std::map<std::string, std::string> icon_url_to_murmur2_hash{
-        {best_primary_icon_url_.spec(), "0"}};
+        {best_primary_icon_url_.spec(), "0"},
+        {best_badge_icon_url_.spec(), "0"}};
 
     WebApkInstaller::UpdateAsyncForTesting(
         CreateWebApkInstaller(), kDownloadedWebApkPackageName, kWebApkVersion,
@@ -128,10 +137,11 @@
   WebApkInstaller* CreateWebApkInstaller() {
     ShortcutInfo info(GURL::EmptyGURL());
     info.best_primary_icon_url = best_primary_icon_url_;
+    info.best_badge_icon_url = best_badge_icon_url_;
 
     // WebApkInstaller owns itself.
     WebApkInstaller* installer = new TestWebApkInstaller(
-        browser_context_, info, SkBitmap(), can_install_webapks_);
+        browser_context_, info, SkBitmap(), SkBitmap(), can_install_webapks_);
     installer->SetTimeoutMs(100);
     return installer;
   }
@@ -154,8 +164,9 @@
 
   content::BrowserContext* browser_context_;
 
-  // The Web Manifest's icon URL.
+  // The Web Manifest's icon URLs.
   const GURL best_primary_icon_url_;
+  const GURL best_badge_icon_url_;
 
   // Called after the installation process has succeeded or failed.
   base::Closure on_completed_callback_;
@@ -197,14 +208,16 @@
 
   void BuildSync(
       const GURL& best_primary_icon_url,
+      const GURL& best_badge_icon_url,
       const std::map<std::string, std::string>& icon_url_to_murmur2_hash,
       bool is_manifest_stale) {
     ShortcutInfo info(GURL::EmptyGURL());
     info.best_primary_icon_url = best_primary_icon_url;
+    info.best_badge_icon_url = best_badge_icon_url;
 
     // WebApkInstaller owns itself.
-    WebApkInstaller* installer =
-        new TestWebApkInstaller(browser_context_, info, SkBitmap(), false);
+    WebApkInstaller* installer = new TestWebApkInstaller(
+        browser_context_, info, SkBitmap(), SkBitmap(), false);
     installer->BuildWebApkProtoInBackgroundForTesting(
         base::Bind(&BuildProtoRunner::OnBuiltWebApkProto,
                    base::Unretained(this)),
@@ -267,6 +280,11 @@
     best_primary_icon_url_ = best_primary_icon_url;
   }
 
+  // Sets the best Web Manifest's badge icon URL.
+  void SetBestBadgeIconUrl(const GURL& best_badge_icon_url) {
+    best_badge_icon_url_ = best_badge_icon_url;
+  }
+
   // Sets the URL to send the webapk::CreateWebApkRequest to. WebApkInstaller
   // should fail if the URL is not |kServerUrl|.
   void SetWebApkServerUrl(const GURL& server_url) {
@@ -281,8 +299,8 @@
   }
 
   std::unique_ptr<WebApkInstallerRunner> CreateWebApkInstallerRunner() {
-    return std::unique_ptr<WebApkInstallerRunner>(
-        new WebApkInstallerRunner(profile_.get(), best_primary_icon_url_));
+    return std::unique_ptr<WebApkInstallerRunner>(new WebApkInstallerRunner(
+        profile_.get(), best_primary_icon_url_, best_badge_icon_url_));
   }
 
   std::unique_ptr<BuildProtoRunner> CreateBuildProtoRunner() {
@@ -295,13 +313,11 @@
  private:
   // Sets default configuration for running WebApkInstaller.
   void SetDefaults() {
-    GURL best_primary_icon_url = test_server_.GetURL(kBestPrimaryIconUrl);
-    SetBestPrimaryIconUrl(best_primary_icon_url);
-    GURL server_url = test_server_.GetURL(kServerUrl);
-    SetWebApkServerUrl(server_url);
-    GURL download_url = test_server_.GetURL(kDownloadUrl);
-    SetWebApkResponseBuilder(
-        base::Bind(&BuildValidWebApkResponse, download_url));
+    SetBestPrimaryIconUrl(test_server_.GetURL(kBestPrimaryIconUrl));
+    SetBestBadgeIconUrl(test_server_.GetURL(kBestBadgeIconUrl));
+    SetWebApkServerUrl(test_server_.GetURL(kServerUrl));
+    SetWebApkResponseBuilder(base::Bind(&BuildValidWebApkResponse,
+                                        test_server_.GetURL(kDownloadUrl)));
   }
 
   std::unique_ptr<net::test_server::HttpResponse> HandleWebApkRequest(
@@ -315,8 +331,9 @@
   content::TestBrowserThreadBundle thread_bundle_;
   net::EmbeddedTestServer test_server_;
 
-  // Web Manifest's icon URL.
+  // Web Manifest's icon URLs.
   GURL best_primary_icon_url_;
+  GURL best_badge_icon_url_;
 
   // Builds response to the WebAPK creation request.
   WebApkResponseBuilder webapk_response_builder_;
@@ -335,8 +352,18 @@
 // URL times out. In a perfect world the fetch would never time out because the
 // bitmap at the best primary icon URL should be in the HTTP cache.
 TEST_F(WebApkInstallerTest, BestPrimaryIconUrlDownloadTimesOut) {
-  GURL best_primary_icon_url = test_server()->GetURL("/slow?1000");
-  SetBestPrimaryIconUrl(best_primary_icon_url);
+  SetBestPrimaryIconUrl(test_server()->GetURL("/slow?1000"));
+
+  std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
+  runner->RunInstallWebApk();
+  EXPECT_EQ(WebApkInstallResult::FAILURE, runner->result());
+}
+
+// Test that installation fails if fetching the bitmap at the best badge icon
+// URL times out. In a perfect world the fetch would never time out because the
+// bitmap at the best badge icon URL should be in the HTTP cache.
+TEST_F(WebApkInstallerTest, BestBadgeIconUrlDownloadTimesOut) {
+  SetBestBadgeIconUrl(test_server()->GetURL("/slow?1000"));
 
   std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
   runner->RunInstallWebApk();
@@ -345,8 +372,7 @@
 
 // Test that installation fails if the WebAPK creation request times out.
 TEST_F(WebApkInstallerTest, CreateWebApkRequestTimesOut) {
-  GURL server_url = test_server()->GetURL("/slow?1000");
-  SetWebApkServerUrl(server_url);
+  SetWebApkServerUrl(test_server()->GetURL("/slow?1000"));
 
   std::unique_ptr<WebApkInstallerRunner> runner = CreateWebApkInstallerRunner();
   runner->RunInstallWebApk();
@@ -403,16 +429,14 @@
 // |best_primary_icon_url| is used to build a WebApk update request. Tests the
 // request can be built properly.
 TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsObsolete) {
-  GURL icon_url_1 = test_server()->GetURL("/icon1.png");
-  GURL icon_url_2 = test_server()->GetURL("/icon2.png");
-  std::string icon_murmur2_hash_1 = "1";
-  std::string icon_murmur2_hash_2 = "2";
+  std::string icon_url_1 = test_server()->GetURL("/icon1.png").spec();
+  std::string icon_url_2 = test_server()->GetURL("/icon2.png").spec();
   std::map<std::string, std::string> icon_url_to_murmur2_hash;
-  icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1;
-  icon_url_to_murmur2_hash[icon_url_2.spec()] = icon_murmur2_hash_2;
+  icon_url_to_murmur2_hash[icon_url_1] = "1";
+  icon_url_to_murmur2_hash[icon_url_2] = "2";
 
   std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner();
-  runner->BuildSync(GURL(""), icon_url_to_murmur2_hash,
+  runner->BuildSync(GURL(), GURL(), icon_url_to_murmur2_hash,
                     true /* is_manifest_stale*/);
   webapk::WebApk* webapk_request = runner->GetWebApkRequest();
   ASSERT_NE(nullptr, webapk_request);
@@ -428,30 +452,75 @@
   EXPECT_FALSE(icons[0].has_hash());
   EXPECT_TRUE(icons[0].has_image_data());
 
-  EXPECT_EQ(icon_url_1.spec(), icons[1].src());
-  EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash());
+  EXPECT_EQ(icon_url_1, icons[1].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[icon_url_1], icons[1].hash());
   EXPECT_FALSE(icons[1].has_image_data());
 
-  EXPECT_EQ(icon_url_2.spec(), icons[2].src());
-  EXPECT_EQ(icon_murmur2_hash_2, icons[2].hash());
+  EXPECT_EQ(icon_url_2, icons[2].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[icon_url_2], icons[2].hash());
   EXPECT_FALSE(icons[2].has_image_data());
 }
 
 // Tests a WebApk install or update request is built properly when the Chrome
 // knows the best icon URL of a site after fetching its Web Manifest.
 TEST_F(WebApkInstallerTest, BuildWebApkProtoWhenManifestIsAvailable) {
-  GURL icon_url_1 = test_server()->GetURL("/icon.png");
-  GURL best_primary_icon_url = test_server()->GetURL(kBestPrimaryIconUrl);
-  std::string icon_murmur2_hash_1 = "1";
-  std::string best_primary_icon_murmur2_hash = "0";
+  std::string icon_url_1 = test_server()->GetURL("/icon.png").spec();
+  std::string best_primary_icon_url =
+      test_server()->GetURL(kBestPrimaryIconUrl).spec();
+  std::string best_badge_icon_url =
+      test_server()->GetURL(kBestBadgeIconUrl).spec();
   std::map<std::string, std::string> icon_url_to_murmur2_hash;
-  icon_url_to_murmur2_hash[icon_url_1.spec()] = icon_murmur2_hash_1;
-  icon_url_to_murmur2_hash[best_primary_icon_url.spec()] =
-      best_primary_icon_murmur2_hash;
+  icon_url_to_murmur2_hash[icon_url_1] = "0";
+  icon_url_to_murmur2_hash[best_primary_icon_url] = "1";
+  icon_url_to_murmur2_hash[best_badge_icon_url] = "2";
 
   std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner();
-  runner->BuildSync(best_primary_icon_url, icon_url_to_murmur2_hash,
-                    false /* is_manifest_stale*/);
+  runner->BuildSync(GURL(best_primary_icon_url), GURL(best_badge_icon_url),
+                    icon_url_to_murmur2_hash, false /* is_manifest_stale*/);
+  webapk::WebApk* webapk_request = runner->GetWebApkRequest();
+  ASSERT_NE(nullptr, webapk_request);
+
+  webapk::WebAppManifest manifest = webapk_request->manifest();
+  ASSERT_EQ(3, manifest.icons_size());
+
+  webapk::Image icons[3];
+  for (int i = 0; i < 3; ++i)
+    icons[i] = manifest.icons(i);
+
+  // Check protobuf fields for /icon.png.
+  EXPECT_EQ(icon_url_1, icons[0].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[icon_url_1], icons[0].hash());
+  EXPECT_EQ(0, icons[0].usages_size());
+  EXPECT_FALSE(icons[0].has_image_data());
+
+  // Check protobuf fields for kBestBadgeIconUrl.
+  EXPECT_EQ(best_badge_icon_url, icons[1].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[best_badge_icon_url], icons[1].hash());
+  EXPECT_THAT(icons[1].usages(),
+              testing::ElementsAre(webapk::Image::BADGE_ICON));
+  EXPECT_TRUE(icons[1].has_image_data());
+
+  // Check protobuf fields for kBestPrimaryIconUrl.
+  EXPECT_EQ(best_primary_icon_url, icons[2].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[best_primary_icon_url], icons[2].hash());
+  EXPECT_THAT(icons[2].usages(),
+              testing::ElementsAre(webapk::Image::PRIMARY_ICON));
+  EXPECT_TRUE(icons[2].has_image_data());
+}
+
+// Tests a WebApk install or update request is built properly when the Chrome
+// knows the best icon URL of a site after fetching its Web Manifest, and
+// primary icon and badge icon share the same URL.
+TEST_F(WebApkInstallerTest, BuildWebApkProtoPrimaryIconAndBadgeIconSameUrl) {
+  std::string icon_url_1 = test_server()->GetURL("/icon.png").spec();
+  std::string best_icon_url = test_server()->GetURL(kBestPrimaryIconUrl).spec();
+  std::map<std::string, std::string> icon_url_to_murmur2_hash;
+  icon_url_to_murmur2_hash[icon_url_1] = "1";
+  icon_url_to_murmur2_hash[best_icon_url] = "0";
+
+  std::unique_ptr<BuildProtoRunner> runner = CreateBuildProtoRunner();
+  runner->BuildSync(GURL(best_icon_url), GURL(best_icon_url),
+                    icon_url_to_murmur2_hash, false /* is_manifest_stale*/);
   webapk::WebApk* webapk_request = runner->GetWebApkRequest();
   ASSERT_NE(nullptr, webapk_request);
 
@@ -462,13 +531,19 @@
   for (int i = 0; i < 2; ++i)
     icons[i] = manifest.icons(i);
 
-  EXPECT_EQ(best_primary_icon_url.spec(), icons[0].src());
-  EXPECT_EQ(best_primary_icon_murmur2_hash, icons[0].hash());
-  EXPECT_TRUE(icons[0].has_image_data());
+  // Check protobuf fields for /icon.png.
+  EXPECT_EQ(icon_url_1, icons[0].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[icon_url_1], icons[0].hash());
+  EXPECT_EQ(0, icons[0].usages_size());
+  EXPECT_FALSE(icons[0].has_image_data());
 
-  EXPECT_EQ(icon_url_1.spec(), icons[1].src());
-  EXPECT_EQ(icon_murmur2_hash_1, icons[1].hash());
-  EXPECT_FALSE(icons[1].has_image_data());
+  // Check protobuf fields for kBestPrimaryIconUrl.
+  EXPECT_EQ(best_icon_url, icons[1].src());
+  EXPECT_EQ(icon_url_to_murmur2_hash[best_icon_url], icons[1].hash());
+  EXPECT_THAT(icons[1].usages(),
+              testing::ElementsAre(webapk::Image::PRIMARY_ICON,
+                                   webapk::Image::BADGE_ICON));
+  EXPECT_TRUE(icons[1].has_image_data());
 }
 
 TEST_F(WebApkInstallerTest, FailsWhenInstallDisabled) {
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.cc b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
index c892d5c..65ab26320 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.cc
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.cc
@@ -163,14 +163,13 @@
                                              const SkBitmap& primary_icon,
                                              const SkBitmap& badge_icon) {
   if (is_webapk_compatible_) {
-    // TODO(zpeng): Add badge to WebAPK installation flow.
     WebApkInstallService* install_service =
         WebApkInstallService::Get(
             data_fetcher_->web_contents()->GetBrowserContext());
     if (install_service->IsInstallInProgress(info.manifest_url))
       ShortcutHelper::ShowWebApkInstallInProgressToast();
     else
-      CreateInfoBarForWebApk(info, primary_icon);
+      CreateInfoBarForWebApk(info, primary_icon, badge_icon);
 
     JNIEnv* env = base::android::AttachCurrentThread();
     Java_AddToHomescreenManager_onFinished(env, java_ref_);
@@ -188,11 +187,13 @@
     AddShortcut(info, primary_icon);
 }
 
-void AddToHomescreenManager::CreateInfoBarForWebApk(const ShortcutInfo& info,
-                                                    const SkBitmap& icon) {
+void AddToHomescreenManager::CreateInfoBarForWebApk(
+    const ShortcutInfo& info,
+    const SkBitmap& primary_icon,
+    const SkBitmap& badge_icon) {
   banners::AppBannerInfoBarDelegateAndroid::Create(
       data_fetcher_->web_contents(), nullptr, info.user_title,
-      base::MakeUnique<ShortcutInfo>(info), base::MakeUnique<SkBitmap>(icon),
+      base::MakeUnique<ShortcutInfo>(info), primary_icon, badge_icon,
       -1 /* event_request_id */, webapk::INSTALL_SOURCE_MENU);
 }
 
diff --git a/chrome/browser/android/webapps/add_to_homescreen_manager.h b/chrome/browser/android/webapps/add_to_homescreen_manager.h
index 1db089bab..36de2b12 100644
--- a/chrome/browser/android/webapps/add_to_homescreen_manager.h
+++ b/chrome/browser/android/webapps/add_to_homescreen_manager.h
@@ -52,7 +52,9 @@
 
   // Called only when the AddToHomescreenDataFetcher has retrieved all of the
   // data needed to install a WebAPK.
-  void CreateInfoBarForWebApk(const ShortcutInfo& info, const SkBitmap& icon);
+  void CreateInfoBarForWebApk(const ShortcutInfo& info,
+                              const SkBitmap& primary_icon,
+                              const SkBitmap& badge_icon);
 
   void RecordAddToHomescreen();
 
diff --git a/chrome/browser/apps/guest_view/web_view_browsertest.cc b/chrome/browser/apps/guest_view/web_view_browsertest.cc
index e95d61e..3d5c9ff 100644
--- a/chrome/browser/apps/guest_view/web_view_browsertest.cc
+++ b/chrome/browser/apps/guest_view/web_view_browsertest.cc
@@ -1433,17 +1433,10 @@
   TestHelper("testExecuteScript", "web_view/shim", NO_TEST_SERVER);
 }
 
-// Flaky on Linux. See https://crbug.com/703727.
-#if defined(OS_LINUX)
-#define MAYBE_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged \
-  DISABLED_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged
-#else
-#define MAYBE_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged \
-  Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged
-#endif
+// Flaky and likely not testing the right assertion. https://crbug.com/703727
 IN_PROC_BROWSER_TEST_P(
     WebViewTest,
-    MAYBE_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) {
+    DISABLED_Shim_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) {
   TestHelper("testExecuteScriptIsAbortedWhenWebViewSourceIsChanged",
              "web_view/shim",
              NO_TEST_SERVER);
diff --git a/chrome/browser/autofill/autofill_interactive_uitest.cc b/chrome/browser/autofill/autofill_interactive_uitest.cc
index 06cc3734..bfbde45 100644
--- a/chrome/browser/autofill/autofill_interactive_uitest.cc
+++ b/chrome/browser/autofill/autofill_interactive_uitest.cc
@@ -1275,8 +1275,14 @@
   TryBasicFormFill();
 }
 
+// https://crbug.com/708861 tracks test flakiness.
+#if defined(OS_CHROMEOS)
+#define MAYBE_AutofillAfterReload DISABLED_AutofillAfterReload
+#else
+#define MAYBE_AutofillAfterReload AutofillAfterReload
+#endif
 // Test that form filling works after reloading the current page.
-IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, AutofillAfterReload) {
+IN_PROC_BROWSER_TEST_F(AutofillInteractiveTest, MAYBE_AutofillAfterReload) {
   CreateTestProfile();
 
   // Load the test page.
diff --git a/chrome/browser/banners/app_banner_manager.cc b/chrome/browser/banners/app_banner_manager.cc
index 7016833..3c2bf84 100644
--- a/chrome/browser/banners/app_banner_manager.cc
+++ b/chrome/browser/banners/app_banner_manager.cc
@@ -267,7 +267,7 @@
   DCHECK(data.primary_icon);
 
   primary_icon_url_ = data.primary_icon_url;
-  primary_icon_.reset(new SkBitmap(*data.primary_icon));
+  primary_icon_ = *data.primary_icon;
 
   // If we triggered the installability check on page load, then it's possible
   // we don't have enough engagement yet. If that's the case, return here but
@@ -540,7 +540,7 @@
   DCHECK(!manifest_url_.is_empty());
   DCHECK(!manifest_.IsEmpty());
   DCHECK(!primary_icon_url_.is_empty());
-  DCHECK(primary_icon_.get());
+  DCHECK(!primary_icon_.drawsNothing());
 
   TrackBeforeInstallEvent(BEFORE_INSTALL_EVENT_COMPLETE);
   ShowBanner();
diff --git a/chrome/browser/banners/app_banner_manager.h b/chrome/browser/banners/app_banner_manager.h
index 20d16a2..487592f 100644
--- a/chrome/browser/banners/app_banner_manager.h
+++ b/chrome/browser/banners/app_banner_manager.h
@@ -5,7 +5,6 @@
 #ifndef CHROME_BROWSER_BANNERS_APP_BANNER_MANAGER_H_
 #define CHROME_BROWSER_BANNERS_APP_BANNER_MANAGER_H_
 
-#include <memory>
 #include <vector>
 
 #include "base/macros.h"
@@ -243,7 +242,7 @@
   GURL primary_icon_url_;
 
   // The primary icon object.
-  std::unique_ptr<SkBitmap> primary_icon_;
+  SkBitmap primary_icon_;
 
   // The referrer string (if any) specified in the app URL. Used only for native
   // app banners.
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.cc b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
index df35212..46b5422 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.cc
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.cc
@@ -45,6 +45,7 @@
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/chromeos/system/input_device_settings.h"
 #include "chrome/browser/chromeos/ui/accessibility_focus_ring_controller.h"
 #include "chrome/browser/extensions/api/braille_display_private/stub_braille_controller.h"
 #include "chrome/browser/extensions/extension_service.h"
@@ -236,6 +237,7 @@
 AccessibilityManager::AccessibilityManager()
     : profile_(NULL),
       large_cursor_pref_handler_(prefs::kAccessibilityLargeCursorEnabled),
+      sticky_keys_pref_handler_(prefs::kAccessibilityStickyKeysEnabled),
       spoken_feedback_pref_handler_(prefs::kAccessibilitySpokenFeedbackEnabled),
       high_contrast_pref_handler_(prefs::kAccessibilityHighContrastEnabled),
       autoclick_pref_handler_(prefs::kAccessibilityAutoclickEnabled),
@@ -247,6 +249,7 @@
       cursor_highlight_pref_handler_(
           prefs::kAccessibilityCursorHighlightEnabled),
       focus_highlight_pref_handler_(prefs::kAccessibilityFocusHighlightEnabled),
+      tap_dragging_pref_handler_(prefs::kTapDraggingEnabled),
       select_to_speak_pref_handler_(prefs::kAccessibilitySelectToSpeakEnabled),
       switch_access_pref_handler_(prefs::kAccessibilitySwitchAccessEnabled),
       large_cursor_enabled_(false),
@@ -261,6 +264,7 @@
       caret_highlight_enabled_(false),
       cursor_highlight_enabled_(false),
       focus_highlight_enabled_(false),
+      tap_dragging_enabled_(false),
       select_to_speak_enabled_(false),
       switch_access_enabled_(false),
       spoken_feedback_notification_(ash::A11Y_NOTIFICATION_NONE),
@@ -359,7 +363,8 @@
         pref_service->GetBoolean(prefs::kAccessibilityMonoAudioEnabled) ||
         pref_service->GetBoolean(prefs::kAccessibilityCaretHighlightEnabled) ||
         pref_service->GetBoolean(prefs::kAccessibilityCursorHighlightEnabled) ||
-        pref_service->GetBoolean(prefs::kAccessibilityFocusHighlightEnabled))
+        pref_service->GetBoolean(prefs::kAccessibilityFocusHighlightEnabled) ||
+        pref_service->GetBoolean(prefs::kTapDraggingEnabled))
       return true;
   }
   return false;
@@ -457,6 +462,10 @@
     return;
 
   sticky_keys_enabled_ = enabled;
+
+  AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_STICKY_KEYS,
+                                          enabled, ash::A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged(details);
   ash::Shell::Get()->sticky_keys_controller()->Enable(enabled);
 }
 
@@ -902,6 +911,38 @@
   UpdateAccessibilityHighlightingFromPrefs();
 }
 
+void AccessibilityManager::EnableTapDragging(bool enabled) {
+  if (!profile_)
+    return;
+
+  PrefService* pref_service = profile_->GetPrefs();
+  pref_service->SetBoolean(prefs::kTapDraggingEnabled, enabled);
+  pref_service->CommitPendingWrite();
+}
+
+bool AccessibilityManager::IsTapDraggingEnabled() {
+  return tap_dragging_enabled_;
+}
+
+void AccessibilityManager::UpdateTapDraggingFromPref() {
+  if (!profile_)
+    return;
+
+  const bool enabled =
+      profile_->GetPrefs()->GetBoolean(prefs::kTapDraggingEnabled);
+
+  if (tap_dragging_enabled_ == enabled)
+    return;
+  tap_dragging_enabled_ = enabled;
+
+  AccessibilityStatusEventDetails details(ACCESSIBILITY_TOGGLE_TAP_DRAGGING,
+                                          enabled, ash::A11Y_NOTIFICATION_NONE);
+  NotifyAccessibilityStatusChanged(details);
+
+  system::TouchpadSettings touchpad_settings;
+  touchpad_settings.SetTapDragging(enabled);
+}
+
 void AccessibilityManager::SetSelectToSpeakEnabled(bool enabled) {
   if (!profile_)
     return;
@@ -1125,6 +1166,10 @@
         base::Bind(&AccessibilityManager::UpdateFocusHighlightFromPref,
                    base::Unretained(this)));
     pref_change_registrar_->Add(
+        prefs::kTapDraggingEnabled,
+        base::Bind(&AccessibilityManager::UpdateTapDraggingFromPref,
+                   base::Unretained(this)));
+    pref_change_registrar_->Add(
         prefs::kAccessibilitySelectToSpeakEnabled,
         base::Bind(&AccessibilityManager::UpdateSelectToSpeakFromPref,
                    base::Unretained(this)));
@@ -1154,6 +1199,7 @@
   large_cursor_pref_handler_.HandleProfileChanged(profile_, profile);
   spoken_feedback_pref_handler_.HandleProfileChanged(profile_, profile);
   high_contrast_pref_handler_.HandleProfileChanged(profile_, profile);
+  sticky_keys_pref_handler_.HandleProfileChanged(profile_, profile);
   autoclick_pref_handler_.HandleProfileChanged(profile_, profile);
   autoclick_delay_pref_handler_.HandleProfileChanged(profile_, profile);
   virtual_keyboard_pref_handler_.HandleProfileChanged(profile_, profile);
@@ -1161,6 +1207,7 @@
   caret_highlight_pref_handler_.HandleProfileChanged(profile_, profile);
   cursor_highlight_pref_handler_.HandleProfileChanged(profile_, profile);
   focus_highlight_pref_handler_.HandleProfileChanged(profile_, profile);
+  tap_dragging_pref_handler_.HandleProfileChanged(profile_, profile);
   select_to_speak_pref_handler_.HandleProfileChanged(profile_, profile);
   switch_access_pref_handler_.HandleProfileChanged(profile_, profile);
 
@@ -1182,6 +1229,7 @@
   UpdateCaretHighlightFromPref();
   UpdateCursorHighlightFromPref();
   UpdateFocusHighlightFromPref();
+  UpdateTapDraggingFromPref();
   UpdateSelectToSpeakFromPref();
   UpdateSwitchAccessFromPref();
 
diff --git a/chrome/browser/chromeos/accessibility/accessibility_manager.h b/chrome/browser/chromeos/accessibility/accessibility_manager.h
index 6371945c..0c7aedd 100644
--- a/chrome/browser/chromeos/accessibility/accessibility_manager.h
+++ b/chrome/browser/chromeos/accessibility/accessibility_manager.h
@@ -44,6 +44,7 @@
   ACCESSIBILITY_MANAGER_SHUTDOWN,
   ACCESSIBILITY_TOGGLE_HIGH_CONTRAST_MODE,
   ACCESSIBILITY_TOGGLE_LARGE_CURSOR,
+  ACCESSIBILITY_TOGGLE_STICKY_KEYS,
   ACCESSIBILITY_TOGGLE_SCREEN_MAGNIFIER,
   ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK,
   ACCESSIBILITY_TOGGLE_VIRTUAL_KEYBOARD,
@@ -51,6 +52,7 @@
   ACCESSIBILITY_TOGGLE_CARET_HIGHLIGHT,
   ACCESSIBILITY_TOGGLE_CURSOR_HIGHLIGHT,
   ACCESSIBILITY_TOGGLE_FOCUS_HIGHLIGHT,
+  ACCESSIBILITY_TOGGLE_TAP_DRAGGING,
   ACCESSIBILITY_BRAILLE_DISPLAY_CONNECTION_STATE_CHANGED
 };
 
@@ -206,6 +208,12 @@
   // Returns if focus highlighting is enabled.
   bool IsFocusHighlightEnabled() const;
 
+  // Enables or disables tap dragging.
+  void EnableTapDragging(bool enabled);
+
+  // Returns true if the tap dragging is enabled, or false if not.
+  bool IsTapDraggingEnabled();
+
   // Invoked to enable or disable select-to-speak.
   void SetSelectToSpeakEnabled(bool enabled);
 
@@ -316,6 +324,7 @@
   void UpdateCaretHighlightFromPref();
   void UpdateCursorHighlightFromPref();
   void UpdateFocusHighlightFromPref();
+  void UpdateTapDraggingFromPref();
   void UpdateSelectToSpeakFromPref();
   void UpdateSwitchAccessFromPref();
   void UpdateAccessibilityHighlightingFromPrefs();
@@ -368,6 +377,7 @@
       session_state_observer_;
 
   PrefHandler large_cursor_pref_handler_;
+  PrefHandler sticky_keys_pref_handler_;
   PrefHandler spoken_feedback_pref_handler_;
   PrefHandler high_contrast_pref_handler_;
   PrefHandler autoclick_pref_handler_;
@@ -377,6 +387,7 @@
   PrefHandler caret_highlight_pref_handler_;
   PrefHandler cursor_highlight_pref_handler_;
   PrefHandler focus_highlight_pref_handler_;
+  PrefHandler tap_dragging_pref_handler_;
   PrefHandler select_to_speak_pref_handler_;
   PrefHandler switch_access_pref_handler_;
 
@@ -392,6 +403,7 @@
   bool caret_highlight_enabled_;
   bool cursor_highlight_enabled_;
   bool focus_highlight_enabled_;
+  bool tap_dragging_enabled_;
   bool select_to_speak_enabled_;
   bool switch_access_enabled_;
 
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index 1ef51b2..6e74700 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -581,7 +581,7 @@
 
   // Enable/disable native CUPS integration
   printing::PrintBackend::SetNativeCupsEnabled(
-      parsed_command_line().HasSwitch(::switches::kEnableNativeCups));
+      !parsed_command_line().HasSwitch(::switches::kDisableNativeCups));
 
   power_prefs_.reset(new PowerPrefs(PowerPolicyController::Get()));
 
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc
index c21807d..cddc9ce 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.cc
@@ -13,6 +13,9 @@
 #include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
 #include "chrome/browser/chromeos/policy/server_backed_state_keys_broker.h"
 #include "chromeos/chromeos_switches.h"
+#include "chromeos/dbus/cryptohome/rpc.pb.h"
+#include "chromeos/dbus/cryptohome_client.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/system/statistics_provider.h"
 #include "components/policy/core/common/cloud/device_management_service.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -132,10 +135,7 @@
   return MODE_NONE;
 }
 
-AutoEnrollmentController::AutoEnrollmentController()
-    : state_(policy::AUTO_ENROLLMENT_STATE_IDLE),
-      safeguard_timer_(false, false),
-      client_start_weak_factory_(this) {}
+AutoEnrollmentController::AutoEnrollmentController() {}
 
 AutoEnrollmentController::~AutoEnrollmentController() {}
 
@@ -178,9 +178,10 @@
   }
 
   // Arm the belts-and-suspenders timer to avoid hangs.
-  safeguard_timer_.Start(
-      FROM_HERE, base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
-      base::Bind(&AutoEnrollmentController::Timeout, base::Unretained(this)));
+  safeguard_timer_.Start(FROM_HERE,
+                         base::TimeDelta::FromSeconds(kSafeguardTimeoutSeconds),
+                         base::Bind(&AutoEnrollmentController::Timeout,
+                                    weak_ptr_factory_.GetWeakPtr()));
 
   // Start by checking if the device has already been owned.
   UpdateState(policy::AUTO_ENROLLMENT_STATE_PENDING);
@@ -269,15 +270,12 @@
     power_initial = power_limit;
   }
 
-  client_.reset(new policy::AutoEnrollmentClient(
+  client_ = base::MakeUnique<policy::AutoEnrollmentClient>(
       base::Bind(&AutoEnrollmentController::UpdateState,
-                 base::Unretained(this)),
-      service,
-      g_browser_process->local_state(),
-      g_browser_process->system_request_context(),
-      state_keys.front(),
-      power_initial,
-      power_limit));
+                 weak_ptr_factory_.GetWeakPtr()),
+      service, g_browser_process->local_state(),
+      g_browser_process->system_request_context(), state_keys.front(),
+      power_initial, power_limit);
 
   VLOG(1) << "Starting auto-enrollment client.";
   client_->Start();
@@ -301,6 +299,35 @@
       break;
   }
 
+  if (state_ == policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT) {
+    StartRemoveFirmwareManagementParameters();
+  } else {
+    progress_callbacks_.Notify(state_);
+  }
+}
+
+void AutoEnrollmentController::StartRemoveFirmwareManagementParameters() {
+  DCHECK_EQ(policy::AUTO_ENROLLMENT_STATE_NO_ENROLLMENT, state_);
+
+  cryptohome::RemoveFirmwareManagementParametersRequest request;
+  chromeos::DBusThreadManager::Get()
+      ->GetCryptohomeClient()
+      ->RemoveFirmwareManagementParametersFromTpm(
+          request,
+          base::Bind(
+              &AutoEnrollmentController::OnFirmwareManagementParametersRemoved,
+              weak_ptr_factory_.GetWeakPtr()));
+}
+
+void AutoEnrollmentController::OnFirmwareManagementParametersRemoved(
+    chromeos::DBusMethodCallStatus call_status,
+    bool result,
+    const cryptohome::BaseReply& reply) {
+  if (!result) {
+    LOG(ERROR) << "Failed to remove firmware management parameters, error: "
+               << reply.error();
+  }
+
   progress_callbacks_.Notify(state_);
 }
 
diff --git a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h
index 5dfada5..1da39ec 100644
--- a/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h
+++ b/chrome/browser/chromeos/login/enrollment/auto_enrollment_controller.h
@@ -16,6 +16,10 @@
 #include "chrome/browser/chromeos/policy/auto_enrollment_client.h"
 #include "chrome/browser/chromeos/settings/device_settings_service.h"
 
+namespace cryptohome {
+class BaseReply;
+}  // namespace cryptohome
+
 namespace chromeos {
 
 // Drives the forced re-enrollment check (for historical reasons called
@@ -86,10 +90,28 @@
   // Sets |state_| and notifies |progress_callbacks_|.
   void UpdateState(policy::AutoEnrollmentState state);
 
+  // Makes a D-Bus call to cryptohome to remove the firmware management
+  // parameters (FWMP) from TPM. Stops the |safeguard_timer_| and notifies the
+  // |progress_callbacks_| after update is done if the timer is still running.
+  // The notifications have to be sent only after the FWMP is cleared, because
+  // the user might try to switch to devmode. In this case, if block_devmode is
+  // in FWMP and the clear operation didn't finish, the switch would be denied.
+  // Also the safeguard timer has to be active until the FWMP is cleared to
+  // avoid the risk of blocked flow.
+  void StartRemoveFirmwareManagementParameters();
+
+  // Callback for RemoveFirmwareManagementParameters(). If an error is received
+  // here, it is logged only, without changing the flow after that, because
+  // the FWMP is used only for newer devices.
+  void OnFirmwareManagementParametersRemoved(
+      chromeos::DBusMethodCallStatus call_status,
+      bool result,
+      const cryptohome::BaseReply& reply);
+
   // Handles timeout of the safeguard timer and stops waiting for a result.
   void Timeout();
 
-  policy::AutoEnrollmentState state_;
+  policy::AutoEnrollmentState state_ = policy::AUTO_ENROLLMENT_STATE_IDLE;
   ProgressCallbackList progress_callbacks_;
 
   std::unique_ptr<policy::AutoEnrollmentClient> client_;
@@ -102,12 +124,15 @@
   // something goes wrong, the timer will ensure that a decision gets made
   // eventually, which is crucial to not block OOBE forever. See
   // http://crbug.com/433634 for background.
-  base::Timer safeguard_timer_;
+  base::Timer safeguard_timer_{false, false};
 
   // Whether the forced re-enrollment check has to be applied.
   FRERequirement fre_requirement_ = REQUIRED;
 
-  base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_;
+  // TODO(igorcov): Merge the two weak_ptr factories in one.
+  base::WeakPtrFactory<AutoEnrollmentController> client_start_weak_factory_{
+      this};
+  base::WeakPtrFactory<AutoEnrollmentController> weak_ptr_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(AutoEnrollmentController);
 };
diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
index c4bcf11..a626d90f 100644
--- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
+++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.cc
@@ -29,6 +29,7 @@
 #include "chromeos/attestation/attestation_flow.h"
 #include "chromeos/chromeos_switches.h"
 #include "chromeos/dbus/auth_policy_client.h"
+#include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "chromeos/dbus/upstart_client.h"
 #include "components/version_info/version_info.h"
@@ -76,6 +77,33 @@
   return em::DeviceRegisterRequest::FLAVOR_ENROLLMENT_MANUAL;
 }
 
+// Returns whether block_devmode is set.
+bool GetBlockdevmodeFromPolicy(
+    const enterprise_management::PolicyFetchResponse* policy) {
+  DCHECK(policy);
+  em::PolicyData policy_data;
+  if (!policy_data.ParseFromString(policy->policy_data())) {
+    LOG(ERROR) << "Failed to parse policy data";
+    return false;
+  }
+
+  em::ChromeDeviceSettingsProto payload;
+  if (!payload.ParseFromString(policy_data.policy_value())) {
+    LOG(ERROR) << "Failed to parse policy value";
+    return false;
+  }
+
+  bool block_devmode = false;
+  if (payload.has_system_settings()) {
+    const em::SystemSettingsProto& container = payload.system_settings();
+    if (container.has_block_devmode()) {
+      block_devmode = container.block_devmode();
+    }
+  }
+
+  return block_devmode;
+}
+
 }  // namespace
 
 EnrollmentHandlerChromeOS::EnrollmentHandlerChromeOS(
@@ -397,6 +425,40 @@
   StartJoinAdDomain();
 }
 
+void EnrollmentHandlerChromeOS::SetFirmwareManagementParametersData() {
+  DCHECK_EQ(STEP_SET_FWMP_DATA, enrollment_step_);
+
+  // In case of reenrollment, the device has the TPM locked and nothing has to
+  // change in install attributes. No need to update firmware parameters in this
+  // case.
+  if (install_attributes_->IsDeviceLocked()) {
+    SetStep(STEP_LOCK_DEVICE);
+    StartLockDevice();
+    return;
+  }
+
+  install_attributes_->SetBlockDevmodeInTpm(
+      GetBlockdevmodeFromPolicy(policy_.get()),
+      base::Bind(
+          &EnrollmentHandlerChromeOS::OnFirmwareManagementParametersDataSet,
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void EnrollmentHandlerChromeOS::OnFirmwareManagementParametersDataSet(
+    chromeos::DBusMethodCallStatus call_status,
+    bool result,
+    const cryptohome::BaseReply& reply) {
+  DCHECK_EQ(STEP_SET_FWMP_DATA, enrollment_step_);
+  if (!result) {
+    LOG(ERROR)
+        << "Failed to update firmware management parameters in TPM, error: "
+        << reply.error();
+  }
+
+  SetStep(STEP_LOCK_DEVICE);
+  StartLockDevice();
+}
+
 // GaiaOAuthClient::Delegate
 void EnrollmentHandlerChromeOS::OnRefreshTokenResponse(
     const std::string& access_token,
@@ -427,8 +489,8 @@
 void EnrollmentHandlerChromeOS::StartJoinAdDomain() {
   DCHECK_EQ(STEP_AD_DOMAIN_JOIN, enrollment_step_);
   if (device_mode_ != DEVICE_MODE_ENTERPRISE_AD) {
-    SetStep(STEP_LOCK_DEVICE);
-    StartLockDevice();
+    SetStep(STEP_SET_FWMP_DATA);
+    SetFirmwareManagementParametersData();
     return;
   }
   DCHECK(ad_join_delegate_);
@@ -441,8 +503,8 @@
   DCHECK_EQ(STEP_AD_DOMAIN_JOIN, enrollment_step_);
   CHECK(!realm.empty());
   realm_ = realm;
-  SetStep(STEP_LOCK_DEVICE);
-  StartLockDevice();
+  SetStep(STEP_SET_FWMP_DATA);
+  SetFirmwareManagementParametersData();
 }
 
 void EnrollmentHandlerChromeOS::StartLockDevice() {
diff --git a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h
index a216297..73b5b62 100644
--- a/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h
+++ b/chrome/browser/chromeos/policy/enrollment_handler_chromeos.h
@@ -116,12 +116,13 @@
     STEP_ROBOT_AUTH_FETCH = 6,    // Fetching device API auth code.
     STEP_ROBOT_AUTH_REFRESH = 7,  // Fetching device API refresh token.
     STEP_AD_DOMAIN_JOIN = 8,      // Joining Active Directory domain.
-    STEP_LOCK_DEVICE = 9,         // Writing installation-time attributes.
-    STEP_STORE_TOKEN = 10,        // Encrypting and storing DM token.
-    STEP_STORE_ROBOT_AUTH = 11,   // Encrypting & writing robot refresh token.
-    STEP_STORE_POLICY = 12,       // Storing policy and API refresh token. For
+    STEP_SET_FWMP_DATA = 9,       // Setting the firmware management parameters.
+    STEP_LOCK_DEVICE = 10,        // Writing installation-time attributes.
+    STEP_STORE_TOKEN = 11,        // Encrypting and storing DM token.
+    STEP_STORE_ROBOT_AUTH = 12,   // Encrypting & writing robot refresh token.
+    STEP_STORE_POLICY = 13,       // Storing policy and API refresh token. For
                                   // AD, includes policy fetch via authpolicyd.
-    STEP_FINISHED = 13,           // Enrollment process done, no further action.
+    STEP_FINISHED = 14,           // Enrollment process done, no further action.
   };
 
   // Handles the response to a request for server-backed state keys.
@@ -149,6 +150,17 @@
   // Handles successful Active Directory domain join.
   void OnAdDomainJoined(const std::string& realm);
 
+  // Updates the firmware management partition from TPM, setting the flags
+  // according to enum FirmwareManagementParametersFlags from rpc.proto if
+  // devmode is blocked.
+  void SetFirmwareManagementParametersData();
+
+  // Invoked after the firmware management partition in TPM is updated.
+  void OnFirmwareManagementParametersDataSet(
+      chromeos::DBusMethodCallStatus call_status,
+      bool result,
+      const cryptohome::BaseReply& reply);
+
   // Calls InstallAttributes::LockDevice() for enterprise enrollment and
   // DeviceSettingsService::SetManagementSettings() for consumer
   // enrollment.
diff --git a/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc b/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc
index 4008a07e..49c02a6 100644
--- a/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc
+++ b/chrome/browser/chromeos/printer_detector/printer_detector_factory.cc
@@ -46,13 +46,13 @@
 KeyedService* PrinterDetectorFactory::BuildServiceInstanceFor(
     content::BrowserContext* context) const {
   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kEnableNativeCups)) {
-    return PrinterDetector::CreateCups(Profile::FromBrowserContext(context))
-        .release();
-  } else {
+          ::switches::kDisableNativeCups)) {
     return PrinterDetector::CreateLegacy(Profile::FromBrowserContext(context))
         .release();
   }
+
+  return PrinterDetector::CreateCups(Profile::FromBrowserContext(context))
+      .release();
 }
 
 bool PrinterDetectorFactory::ServiceIsCreatedWithBrowserContext() const {
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc b/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
index de550210..15331c7 100644
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
+++ b/chrome/browser/chromeos/resource_reporter/resource_reporter.cc
@@ -72,14 +72,7 @@
 constexpr base::TimeDelta kMinimumTimeBetweenReports =
     base::TimeDelta::FromDays(1);
 
-// Gets the memory usage threshold of a process beyond which the process is
-// considered memory-intensive on the current device it's running on.
-int64_t GetMemoryThresholdForDeviceInBytes() {
-  const int64_t bytes_per_cpu = base::SysInfo::AmountOfPhysicalMemory() /
-                                base::SysInfo::NumberOfProcessors();
-
-  return bytes_per_cpu * 0.6;
-}
+constexpr double kTaskCpuThresholdForReporting = 70.0;
 
 }  // namespace
 
@@ -175,8 +168,8 @@
 
       default:
         // Other tasks types will be reported using Rappor.
-        if (memory_usage < kTaskMemoryThresholdForReporting &&
-            cpu_usage < kTaskCpuThresholdForReporting) {
+        if (memory_usage < GetTaskMemoryThresholdForReporting() &&
+            cpu_usage < GetTaskCpuThresholdForReporting()) {
           // We only care about CPU and memory intensive tasks.
           break;
         }
@@ -205,13 +198,6 @@
       base::Bind(&ResourceReporter::ReportSamples, base::Unretained(this)));
 }
 
-// static
-const double ResourceReporter::kTaskCpuThresholdForReporting = 70.0;
-
-// static
-const int64_t ResourceReporter::kTaskMemoryThresholdForReporting =
-    GetMemoryThresholdForDeviceInBytes();
-
 ResourceReporter::ResourceReporter()
     : TaskManagerObserver(base::TimeDelta::FromSeconds(kRefreshIntervalSeconds),
                           task_manager::REFRESH_TYPE_CPU |
@@ -226,6 +212,19 @@
       is_monitoring_(false) {}
 
 // static
+double ResourceReporter::GetTaskCpuThresholdForReporting() {
+  return kTaskCpuThresholdForReporting;
+}
+
+// static
+int64_t ResourceReporter::GetTaskMemoryThresholdForReporting() {
+  static const int64_t threshold = 0.6 *
+                                   base::SysInfo::AmountOfPhysicalMemory() /
+                                   base::SysInfo::NumberOfProcessors();
+  return threshold;
+}
+
+// static
 std::unique_ptr<rappor::Sample> ResourceReporter::CreateRapporSample(
     rappor::RapporServiceImpl* rappor_service,
     const ResourceReporter::TaskRecord& task_record) {
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter.h b/chrome/browser/chromeos/resource_reporter/resource_reporter.h
index cfa2bc8..e33992a 100644
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter.h
+++ b/chrome/browser/chromeos/resource_reporter/resource_reporter.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -132,12 +133,12 @@
     NUM_RANGES            = 7,
   };
 
-  // The CPU and memory thresholds beyond which the tasks will be reported.
-  static const double kTaskCpuThresholdForReporting;
-  static const int64_t kTaskMemoryThresholdForReporting;
-
   ResourceReporter();
 
+  // The CPU and memory thresholds beyond which the tasks will be reported.
+  static double GetTaskCpuThresholdForReporting();
+  static int64_t GetTaskMemoryThresholdForReporting();
+
   // Creates a Rappor sample for the given |task_record|.
   static std::unique_ptr<rappor::Sample> CreateRapporSample(
       rappor::RapporServiceImpl* rappor_service,
diff --git a/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc b/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc
index 82c6954f..5711382 100644
--- a/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc
+++ b/chrome/browser/chromeos/resource_reporter/resource_reporter_unittest.cc
@@ -282,9 +282,9 @@
   ASSERT_FALSE(resource_reporter()->task_records_.empty());
   for (const auto& task_record : resource_reporter()->task_records_) {
     EXPECT_TRUE(task_record.cpu_percent >=
-                    ResourceReporter::kTaskCpuThresholdForReporting ||
+                    ResourceReporter::GetTaskCpuThresholdForReporting() ||
                 task_record.memory_bytes >=
-                    ResourceReporter::kTaskMemoryThresholdForReporting);
+                    ResourceReporter::GetTaskMemoryThresholdForReporting());
   }
 
   // Make sure you have the right info about the Browser and GPU process.
diff --git a/chrome/browser/chromeos/settings/install_attributes.cc b/chrome/browser/chromeos/settings/install_attributes.cc
index 710cb2e..f893661 100644
--- a/chrome/browser/chromeos/settings/install_attributes.cc
+++ b/chrome/browser/chromeos/settings/install_attributes.cc
@@ -20,8 +20,10 @@
 #include "base/time/time.h"
 #include "chrome/browser/chromeos/policy/proto/install_attributes.pb.h"
 #include "chromeos/cryptohome/cryptohome_util.h"
+#include "chromeos/dbus/cryptohome/rpc.pb.h"
 #include "chromeos/dbus/dbus_thread_manager.h"
 #include "google_apis/gaia/gaia_auth_util.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
 
 namespace chromeos {
 
@@ -186,6 +188,24 @@
   callback.Run();
 }
 
+void InstallAttributes::SetBlockDevmodeInTpm(
+    bool block_devmode,
+    const CryptohomeClient::ProtobufMethodCallback& callback) {
+  DCHECK(!callback.is_null());
+  DCHECK(!device_locked_);
+
+  cryptohome::SetFirmwareManagementParametersRequest request;
+  // Set the flags, according to enum FirmwareManagementParametersFlags from
+  // rpc.proto if devmode is blocked.
+  if (block_devmode) {
+    request.set_flags(
+        cryptohome::DEVELOPER_DISABLE_BOOT |
+        cryptohome::DEVELOPER_DISABLE_CASE_CLOSED_DEBUGGING_UNLOCK);
+  }
+
+  cryptohome_client_->SetFirmwareManagementParametersInTpm(request, callback);
+}
+
 void InstallAttributes::LockDevice(policy::DeviceMode device_mode,
                                    const std::string& domain,
                                    const std::string& realm,
diff --git a/chrome/browser/chromeos/settings/install_attributes.h b/chrome/browser/chromeos/settings/install_attributes.h
index 53615df..618c718d1 100644
--- a/chrome/browser/chromeos/settings/install_attributes.h
+++ b/chrome/browser/chromeos/settings/install_attributes.h
@@ -70,6 +70,13 @@
   // ReadAttributesIfReady().
   void ReadImmutableAttributes(const base::Closure& callback);
 
+  // Updates the firmware management parameters from TPM, storing the devmode
+  // flag according to |block_devmode|. Invokes |callback| when done. Must be
+  // called before LockDevice is done. Used to update TPM on enrollment.
+  void SetBlockDevmodeInTpm(
+      bool block_devmode,
+      const CryptohomeClient::ProtobufMethodCallback& callback);
+
   // Locks the device into |device_mode|.  Depending on |device_mode|, a
   // specific subset of |domain|, |realm| and |device_id| must be set.  Can also
   // be called after the lock has already been taken, in which case it checks
@@ -111,6 +118,9 @@
   // device id was not stored in the lockbox (prior to R19).
   std::string GetDeviceId() const { return registration_device_id_; }
 
+  // Return whether TPM is locked.
+  bool IsDeviceLocked() const { return device_locked_; }
+
  protected:
   // True if install attributes have been read successfully.  False if read
   // failed or no read attempt was made.
diff --git a/chrome/browser/chromeos/settings/install_attributes_unittest.cc b/chrome/browser/chromeos/settings/install_attributes_unittest.cc
index 2b5e452..9b61b32 100644
--- a/chrome/browser/chromeos/settings/install_attributes_unittest.cc
+++ b/chrome/browser/chromeos/settings/install_attributes_unittest.cc
@@ -32,6 +32,13 @@
   loop->Quit();
 }
 
+void OnSetBlockDevmode(chromeos::DBusMethodCallStatus* out_status,
+                       chromeos::DBusMethodCallStatus call_status,
+                       bool result,
+                       const cryptohome::BaseReply& reply) {
+  *out_status = call_status;
+}
+
 }  // namespace
 
 static const char kTestDomain[] = "example.com";
@@ -295,4 +302,14 @@
   EXPECT_EQ(std::string(), install_attributes_->GetDeviceId());
 }
 
+TEST_F(InstallAttributesTest, CheckSetBlockDevmodeInTpm) {
+  chromeos::DBusMethodCallStatus status =
+      chromeos::DBusMethodCallStatus::DBUS_METHOD_CALL_FAILURE;
+  install_attributes_->SetBlockDevmodeInTpm(
+      true, base::Bind(&OnSetBlockDevmode, &status));
+  base::RunLoop().RunUntilIdle();
+
+  EXPECT_EQ(chromeos::DBusMethodCallStatus::DBUS_METHOD_CALL_SUCCESS, status);
+}
+
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
index a00fd5f..32876c5 100644
--- a/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
+++ b/chrome/browser/chromeos/system/tray_accessibility_browsertest.cc
@@ -222,6 +222,18 @@
     tray()->detailed_menu_->OnViewClicked(button);
   }
 
+  void ClickStickyKeysOnDetailMenu() {
+    views::View* button = tray()->detailed_menu_->sticky_keys_view_;
+    ASSERT_TRUE(button);
+    tray()->detailed_menu_->OnViewClicked(button);
+  }
+
+  void ClickTapDraggingOnDetailMenu() {
+    views::View* button = tray()->detailed_menu_->tap_dragging_view_;
+    ASSERT_TRUE(button);
+    tray()->detailed_menu_->OnViewClicked(button);
+  }
+
   bool IsSpokenFeedbackEnabledOnDetailMenu() const {
     return tray()->detailed_menu_->spoken_feedback_enabled_;
   }
@@ -262,6 +274,14 @@
     return tray()->detailed_menu_->highlight_keyboard_focus_enabled_;
   }
 
+  bool IsStickyKeysEnabledOnDetailMenu() const {
+    return tray()->detailed_menu_->sticky_keys_enabled_;
+  }
+
+  bool IsTapDraggingEnabledOnDetailMenu() const {
+    return tray()->detailed_menu_->tap_dragging_enabled_;
+  }
+
   bool IsSpokenFeedbackMenuShownOnDetailMenu() const {
     return tray()->detailed_menu_->spoken_feedback_view_;
   }
@@ -302,6 +322,14 @@
     return tray()->detailed_menu_->highlight_keyboard_focus_view_;
   }
 
+  bool IsStickyKeysMenuShownOnDetailMenu() const {
+    return tray()->detailed_menu_->sticky_keys_view_;
+  }
+
+  bool IsTapDraggingMenuShownOnDetailMenu() const {
+    return tray()->detailed_menu_->tap_dragging_view_;
+  }
+
   // In material design we show the help button but theme it as disabled if
   // it is not possible to load the help page.
   bool IsHelpAvailableOnDetailMenu() const {
@@ -426,6 +454,18 @@
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_FALSE(IsTrayIconVisible());
 
+  // Toggling the sticky keys changes the visibility of the icon.
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_FALSE(IsTrayIconVisible());
+
+  // Toggling the tap dragging changes the visibility of the icon.
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_FALSE(IsTrayIconVisible());
+
   // Enabling all accessibility features.
   SetMagnifierEnabled(true);
   EXPECT_TRUE(IsTrayIconVisible());
@@ -446,6 +486,10 @@
   EXPECT_TRUE(IsTrayIconVisible());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(IsTrayIconVisible());
   AccessibilityManager::Get()->EnableSpokenFeedback(
       false, ash::A11Y_NOTIFICATION_NONE);
   EXPECT_TRUE(IsTrayIconVisible());
@@ -464,6 +508,10 @@
   AccessibilityManager::Get()->SetCursorHighlightEnabled(false);
   EXPECT_TRUE(IsTrayIconVisible());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
+  EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(IsTrayIconVisible());
+  AccessibilityManager::Get()->EnableTapDragging(false);
   EXPECT_FALSE(IsTrayIconVisible());
 
   // Confirms that prefs::kShouldAlwaysShowAccessibilityMenu doesn't affect
@@ -550,6 +598,18 @@
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_FALSE(CanCreateMenuItem());
 
+  // Toggling sticky keys changes the visibility of the menu.
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_FALSE(CanCreateMenuItem());
+
+  // Toggling tap dragging changes the visibility of the menu.
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_FALSE(CanCreateMenuItem());
+
   // Enabling all accessibility features.
   SetMagnifierEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
@@ -572,6 +632,10 @@
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableVirtualKeyboard(false);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableAutoclick(false);
@@ -592,6 +656,10 @@
   AccessibilityManager::Get()->SetCursorHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
   EXPECT_FALSE(CanCreateMenuItem());
 }
 
@@ -640,36 +708,48 @@
   AccessibilityManager::Get()->EnableVirtualKeyboard(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling large mouse cursor.
+  // The menu remains visible regardless of toggling large mouse cursor.
   AccessibilityManager::Get()->EnableLargeCursor(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableLargeCursor(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling mono audio.
+  // The menu remains visible regardless of toggling mono audio.
   AccessibilityManager::Get()->EnableMonoAudio(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableMonoAudio(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling caret highlight.
+  // The menu remains visible regardless of toggling caret highlight.
   AccessibilityManager::Get()->SetCaretHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetCaretHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling highlight mouse cursor.
+  // The menu remains visible regardless of toggling highlight mouse cursor.
   AccessibilityManager::Get()->SetCursorHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetCursorHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling highlight keyboard focus.
+  // The menu remains visible regardless of toggling highlight keyboard focus.
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
+  // The menu remains visible regardless of the toggling sticky keys.
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+
+  // The menu remains visible regardless of the toggling tap dragging.
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+
   // Enabling all accessibility features.
   SetMagnifierEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
@@ -692,6 +772,10 @@
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableVirtualKeyboard(false);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableAutoclick(false);
@@ -713,6 +797,10 @@
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_TRUE(CanCreateMenuItem());
 
   SetShowAccessibilityOptionsInSystemTrayMenu(false);
 
@@ -752,36 +840,48 @@
   AccessibilityManager::Get()->EnableVirtualKeyboard(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling large mouse cursor.
+  // The menu remains visible regardless of toggling large mouse cursor.
   AccessibilityManager::Get()->EnableLargeCursor(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableLargeCursor(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling mono audio.
+  // The menu remains visible regardless of toggling mono audio.
   AccessibilityManager::Get()->EnableMonoAudio(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableMonoAudio(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling caret highlight.
+  // The menu remains visible regardless of toggling caret highlight.
   AccessibilityManager::Get()->SetCaretHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetCaretHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling highlight mouse cursor.
+  // The menu remains visible regardless of toggling highlight mouse cursor.
   AccessibilityManager::Get()->SetCursorHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetCursorHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
-  // The menu remains visibile regardless of toggling highlight keyboard focus.
+  // The menu remains visible regardless of toggling highlight keyboard focus.
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
 
+  // The menu remains visible regardless of toggling sticky keys.
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+
+  // The menu remains visible regardless of toggling tap dragging.
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+
   // Enabling all accessibility features.
   SetMagnifierEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
@@ -802,6 +902,10 @@
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
   EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableVirtualKeyboard(false);
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->EnableSpokenFeedback(
@@ -821,6 +925,10 @@
   EXPECT_TRUE(CanCreateMenuItem());
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
   EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CanCreateMenuItem());
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_TRUE(CanCreateMenuItem());
 
   SetShowAccessibilityOptionsInSystemTrayMenu(true);
 
@@ -999,6 +1107,28 @@
   EXPECT_TRUE(CreateDetailedMenu());
   ClickHighlishtKeyboardFocusOnDetailMenu();
   EXPECT_FALSE(AccessibilityManager::Get()->IsFocusHighlightEnabled());
+
+  // Confirms that the check item toggles sticky keys.
+  EXPECT_FALSE(AccessibilityManager::Get()->IsStickyKeysEnabled());
+
+  EXPECT_TRUE(CreateDetailedMenu());
+  ClickStickyKeysOnDetailMenu();
+  EXPECT_TRUE(AccessibilityManager::Get()->IsStickyKeysEnabled());
+
+  EXPECT_TRUE(CreateDetailedMenu());
+  ClickStickyKeysOnDetailMenu();
+  EXPECT_FALSE(AccessibilityManager::Get()->IsStickyKeysEnabled());
+
+  // Confirms that the check item toggles tap dragging.
+  EXPECT_FALSE(AccessibilityManager::Get()->IsTapDraggingEnabled());
+
+  EXPECT_TRUE(CreateDetailedMenu());
+  ClickTapDraggingOnDetailMenu();
+  EXPECT_TRUE(AccessibilityManager::Get()->IsTapDraggingEnabled());
+
+  EXPECT_TRUE(CreateDetailedMenu());
+  ClickTapDraggingOnDetailMenu();
+  EXPECT_FALSE(AccessibilityManager::Get()->IsTapDraggingEnabled());
 }
 
 IN_PROC_BROWSER_TEST_P(TrayAccessibilityTest, CheckMarksOnDetailMenu) {
@@ -1016,6 +1146,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling spoken feedback.
@@ -1032,6 +1164,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling spoken feedback.
@@ -1048,6 +1182,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling high contrast.
@@ -1063,6 +1199,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling high contrast.
@@ -1078,6 +1216,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling full screen magnifier.
@@ -1093,6 +1233,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling screen magnifier.
@@ -1108,6 +1250,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling large cursor.
@@ -1123,6 +1267,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling large cursor.
@@ -1138,6 +1284,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enable on-screen keyboard.
@@ -1153,6 +1301,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disable on-screen keyboard.
@@ -1168,6 +1318,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling mono audio.
@@ -1183,6 +1335,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling mono audio.
@@ -1198,6 +1352,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling caret highlight.
@@ -1213,6 +1369,8 @@
   EXPECT_TRUE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling caret highlight.
@@ -1228,6 +1386,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling highlight mouse cursor.
@@ -1243,6 +1403,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_TRUE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling highlight mouse cursor.
@@ -1258,6 +1420,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling highlight keyboard focus.
@@ -1273,6 +1437,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_TRUE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling highlight keyboard focus.
@@ -1288,6 +1454,76 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
+  CloseDetailMenu();
+
+  // Enabling sticky keys.
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  EXPECT_TRUE(CreateDetailedMenu());
+  EXPECT_FALSE(IsSpokenFeedbackEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighContrastEnabledOnDetailMenu());
+  EXPECT_FALSE(IsScreenMagnifierEnabledOnDetailMenu());
+  EXPECT_FALSE(IsLargeCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsAutoclickEnabledOnDetailMenu());
+  EXPECT_FALSE(IsVirtualKeyboardEnabledOnDetailMenu());
+  EXPECT_FALSE(IsMonoAudioEnabledOnDetailMenu());
+  EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
+  CloseDetailMenu();
+
+  // Disabling sticky keys.
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  EXPECT_TRUE(CreateDetailedMenu());
+  EXPECT_FALSE(IsSpokenFeedbackEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighContrastEnabledOnDetailMenu());
+  EXPECT_FALSE(IsScreenMagnifierEnabledOnDetailMenu());
+  EXPECT_FALSE(IsLargeCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsAutoclickEnabledOnDetailMenu());
+  EXPECT_FALSE(IsVirtualKeyboardEnabledOnDetailMenu());
+  EXPECT_FALSE(IsMonoAudioEnabledOnDetailMenu());
+  EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
+  CloseDetailMenu();
+
+  // Enabling tap dragging.
+  AccessibilityManager::Get()->EnableTapDragging(true);
+  EXPECT_TRUE(CreateDetailedMenu());
+  EXPECT_FALSE(IsSpokenFeedbackEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighContrastEnabledOnDetailMenu());
+  EXPECT_FALSE(IsScreenMagnifierEnabledOnDetailMenu());
+  EXPECT_FALSE(IsLargeCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsAutoclickEnabledOnDetailMenu());
+  EXPECT_FALSE(IsVirtualKeyboardEnabledOnDetailMenu());
+  EXPECT_FALSE(IsMonoAudioEnabledOnDetailMenu());
+  EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingEnabledOnDetailMenu());
+  CloseDetailMenu();
+
+  // Disabling tap dragging.
+  AccessibilityManager::Get()->EnableTapDragging(false);
+  EXPECT_TRUE(CreateDetailedMenu());
+  EXPECT_FALSE(IsSpokenFeedbackEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighContrastEnabledOnDetailMenu());
+  EXPECT_FALSE(IsScreenMagnifierEnabledOnDetailMenu());
+  EXPECT_FALSE(IsLargeCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsAutoclickEnabledOnDetailMenu());
+  EXPECT_FALSE(IsVirtualKeyboardEnabledOnDetailMenu());
+  EXPECT_FALSE(IsMonoAudioEnabledOnDetailMenu());
+  EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
+  EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling all of the a11y features.
@@ -1302,6 +1538,8 @@
   AccessibilityManager::Get()->SetCaretHighlightEnabled(true);
   AccessibilityManager::Get()->SetCursorHighlightEnabled(true);
   AccessibilityManager::Get()->SetFocusHighlightEnabled(true);
+  AccessibilityManager::Get()->EnableStickyKeys(true);
+  AccessibilityManager::Get()->EnableTapDragging(true);
   EXPECT_TRUE(CreateDetailedMenu());
   EXPECT_TRUE(IsSpokenFeedbackEnabledOnDetailMenu());
   EXPECT_TRUE(IsHighContrastEnabledOnDetailMenu());
@@ -1314,6 +1552,8 @@
   EXPECT_TRUE(IsHighlightMouseCursorEnabledOnDetailMenu());
   // Focus highlighting can't be on when spoken feedback is on
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling all of the a11y features.
@@ -1328,6 +1568,8 @@
   AccessibilityManager::Get()->SetCaretHighlightEnabled(false);
   AccessibilityManager::Get()->SetCursorHighlightEnabled(false);
   AccessibilityManager::Get()->SetFocusHighlightEnabled(false);
+  AccessibilityManager::Get()->EnableStickyKeys(false);
+  AccessibilityManager::Get()->EnableTapDragging(false);
   EXPECT_TRUE(CreateDetailedMenu());
   EXPECT_FALSE(IsSpokenFeedbackEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighContrastEnabledOnDetailMenu());
@@ -1339,6 +1581,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Enabling autoclick.
@@ -1354,6 +1598,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 
   // Disabling autoclick.
@@ -1369,6 +1615,8 @@
   EXPECT_FALSE(IsCaretHighlightEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightMouseCursorEnabledOnDetailMenu());
   EXPECT_FALSE(IsHighlightKeyboardFocusEnabledOnDetailMenu());
+  EXPECT_FALSE(IsStickyKeysEnabledOnDetailMenu());
+  EXPECT_FALSE(IsTapDraggingEnabledOnDetailMenu());
   CloseDetailMenu();
 }
 
@@ -1389,6 +1637,8 @@
   EXPECT_TRUE(IsCaretHighlightMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightMouseCursorMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightKeyboardFocusMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingMenuShownOnDetailMenu());
   CloseDetailMenu();
 
   SetLoginStatus(ash::LoginStatus::USER);
@@ -1405,6 +1655,8 @@
   EXPECT_TRUE(IsCaretHighlightMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightMouseCursorMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightKeyboardFocusMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingMenuShownOnDetailMenu());
   CloseDetailMenu();
 
   SetLoginStatus(ash::LoginStatus::LOCKED);
@@ -1421,6 +1673,8 @@
   EXPECT_TRUE(IsCaretHighlightMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightMouseCursorMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightKeyboardFocusMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingMenuShownOnDetailMenu());
   CloseDetailMenu();
 
   session_manager::SessionManager::Get()->SetSessionState(
@@ -1441,6 +1695,8 @@
   EXPECT_TRUE(IsCaretHighlightMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightMouseCursorMenuShownOnDetailMenu());
   EXPECT_TRUE(IsHighlightKeyboardFocusMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsStickyKeysMenuShownOnDetailMenu());
+  EXPECT_TRUE(IsTapDraggingMenuShownOnDetailMenu());
   CloseDetailMenu();
 }
 
diff --git a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
index e653439d..9c9dae1d 100644
--- a/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
+++ b/chrome/browser/component_updater/subresource_filter_component_installer_unittest.cc
@@ -211,7 +211,9 @@
                             subresource_filter::kActivationScopeNoSites);
   std::unique_ptr<SubresourceFilterMockComponentUpdateService>
       component_updater(new SubresourceFilterMockComponentUpdateService());
-  EXPECT_CALL(*component_updater, RegisterComponent(testing::_)).Times(1);
+  EXPECT_CALL(*component_updater, RegisterComponent(testing::_))
+      .Times(1)
+      .WillOnce(testing::Return(true));
   RegisterSubresourceFilterComponent(component_updater.get());
   base::RunLoop().RunUntilIdle();
 }
diff --git a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
index be8d445..618ec4e6 100644
--- a/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
+++ b/chrome/browser/custom_handlers/protocol_handler_registry_unittest.cc
@@ -28,6 +28,7 @@
 #include "content/public/test/test_browser_thread.h"
 #include "content/public/test/test_renderer_host.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -41,8 +42,8 @@
     net::URLRequestJobFactory* interceptor) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   net::URLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(
-      context.CreateRequest(url, net::DEFAULT_PRIORITY, nullptr));
+  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
+      url, net::DEFAULT_PRIORITY, nullptr, TRAFFIC_ANNOTATION_FOR_TESTS));
   std::unique_ptr<net::URLRequestJob> job(
       interceptor->MaybeCreateJobWithProtocolHandler(
           url.scheme(), request.get(), context.network_delegate()));
diff --git a/chrome/browser/data_usage/tab_id_annotator_unittest.cc b/chrome/browser/data_usage/tab_id_annotator_unittest.cc
index 822213b..347fae0 100644
--- a/chrome/browser/data_usage/tab_id_annotator_unittest.cc
+++ b/chrome/browser/data_usage/tab_id_annotator_unittest.cc
@@ -27,6 +27,7 @@
 #include "content/public/common/previews_state.h"
 #include "net/base/network_change_notifier.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -99,7 +100,8 @@
   net::TestURLRequestContext context;
   net::TestDelegate test_delegate;
   std::unique_ptr<net::URLRequest> request =
-      context.CreateRequest(GURL("http://foo.com"), net::IDLE, &test_delegate);
+      context.CreateRequest(GURL("http://foo.com"), net::IDLE, &test_delegate,
+                            TRAFFIC_ANNOTATION_FOR_TESTS);
 
   if (render_process_id != -1 && render_frame_id != -1) {
     // The only args that matter here for the ResourceRequestInfo are the
diff --git a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
index 4274579..d034b76 100644
--- a/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
+++ b/chrome/browser/data_use_measurement/chrome_data_use_ascriber_unittest.cc
@@ -16,6 +16,7 @@
 #include "content/public/common/process_type.h"
 #include "content/public/test/mock_resource_context.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -54,8 +55,8 @@
                                                     int request_id,
                                                     int render_process_id,
                                                     int render_frame_id) {
-    std::unique_ptr<net::URLRequest> request =
-        context()->CreateRequest(GURL(url), net::IDLE, nullptr);
+    std::unique_ptr<net::URLRequest> request = context()->CreateRequest(
+        GURL(url), net::IDLE, nullptr, TRAFFIC_ANNOTATION_FOR_TESTS);
     // TODO(kundaji): Allow request_id to be specified in AllocateForTesting.
     content::ResourceRequestInfo::AllocateForTesting(
         request.get(), content::RESOURCE_TYPE_MAIN_FRAME, resource_context(),
diff --git a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
index 101d544..d2008b54 100644
--- a/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
+++ b/chrome/browser/devtools/chrome_devtools_manager_delegate.cc
@@ -34,10 +34,45 @@
 char ChromeDevToolsManagerDelegate::kTypeBackgroundPage[] = "background_page";
 char ChromeDevToolsManagerDelegate::kTypeWebView[] = "webview";
 
+namespace {
+
 char kLocationsParam[] = "locations";
 char kHostParam[] = "host";
 char kPortParam[] = "port";
 
+bool GetExtensionInfo(content::RenderFrameHost* host,
+                      std::string* name,
+                      std::string* type) {
+  content::WebContents* wc = content::WebContents::FromRenderFrameHost(host);
+  if (!wc)
+    return false;
+  Profile* profile = Profile::FromBrowserContext(wc->GetBrowserContext());
+  if (!profile)
+    return false;
+  const extensions::Extension* extension =
+      extensions::ProcessManager::Get(profile)->GetExtensionForRenderFrameHost(
+          host);
+  if (!extension)
+    return false;
+  extensions::ExtensionHost* extension_host =
+      extensions::ProcessManager::Get(profile)->GetBackgroundHostForExtension(
+          extension->id());
+  if (extension_host && extension_host->host_contents() == wc) {
+    *name = extension->name();
+    *type = ChromeDevToolsManagerDelegate::kTypeBackgroundPage;
+    return true;
+  } else if (extension->is_hosted_app() ||
+             extension->is_legacy_packaged_app() ||
+             extension->is_platform_app()) {
+    *name = extension->name();
+    *type = ChromeDevToolsManagerDelegate::kTypeApp;
+    return true;
+  }
+  return false;
+}
+
+}  // namespace
+
 class ChromeDevToolsManagerDelegate::HostData {
  public:
   HostData() {}
@@ -103,47 +138,20 @@
       return DevToolsAgentHost::kTypePage;
   }
 
-  const extensions::Extension* extension = extensions::ExtensionRegistry::Get(
-      web_contents->GetBrowserContext())->enabled_extensions().GetByID(
-          host->GetLastCommittedURL().host());
-  if (!extension)
+  std::string extension_name;
+  std::string extension_type;
+  if (!GetExtensionInfo(host, &extension_name, &extension_type))
     return DevToolsAgentHost::kTypeOther;
-
-  Profile* profile =
-      Profile::FromBrowserContext(web_contents->GetBrowserContext());
-  if (!profile)
-    return DevToolsAgentHost::kTypeOther;
-
-  extensions::ExtensionHost* extension_host =
-      extensions::ProcessManager::Get(profile)
-          ->GetBackgroundHostForExtension(extension->id());
-  if (extension_host &&
-      extension_host->host_contents() == web_contents) {
-    return kTypeBackgroundPage;
-  } else if (extension->is_hosted_app()
-             || extension->is_legacy_packaged_app()
-             || extension->is_platform_app()) {
-    return kTypeApp;
-  }
-  return DevToolsAgentHost::kTypeOther;
+  return extension_type;
 }
 
 std::string ChromeDevToolsManagerDelegate::GetTargetTitle(
     content::RenderFrameHost* host) {
-  content::WebContents* web_contents =
-      content::WebContents::FromRenderFrameHost(host);
-  if (host->GetParent())
-    return host->GetLastCommittedURL().spec();
-  for (TabContentsIterator it; !it.done(); it.Next()) {
-    if (*it == web_contents)
-      return base::UTF16ToUTF8(web_contents->GetTitle());
-  }
-  const extensions::Extension* extension = extensions::ExtensionRegistry::Get(
-    web_contents->GetBrowserContext())->enabled_extensions().GetByID(
-          host->GetLastCommittedURL().host());
-  if (extension)
-    return extension->name();
-  return "";
+  std::string extension_name;
+  std::string extension_type;
+  if (!GetExtensionInfo(host, &extension_name, &extension_type))
+    return std::string();
+  return extension_name;
 }
 
 scoped_refptr<DevToolsAgentHost>
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
index 348d2c8..396d22bc 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_action_unittest.cc
@@ -28,6 +28,7 @@
 #include "extensions/common/extension.h"
 #include "net/base/request_priority.h"
 #include "net/http/http_response_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -146,7 +147,8 @@
     const WebRequestActionSet* action_set,
     RequestStage stage) {
   std::unique_ptr<net::URLRequest> regular_request(
-      context_.CreateRequest(GURL(url_string), net::DEFAULT_PRIORITY, NULL));
+      context_.CreateRequest(GURL(url_string), net::DEFAULT_PRIORITY, NULL,
+                             TRAFFIC_ANNOTATION_FOR_TESTS));
   std::list<LinkedPtrEventResponseDelta> deltas;
   scoped_refptr<net::HttpResponseHeaders> headers(
       new net::HttpResponseHeaders(""));
diff --git a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
index 474367a..88dbe55f 100644
--- a/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
+++ b/chrome/browser/extensions/api/declarative_webrequest/webrequest_rules_registry_unittest.cc
@@ -25,6 +25,7 @@
 #include "extensions/browser/api/declarative_webrequest/webrequest_constants.h"
 #include "extensions/browser/api/web_request/web_request_api_helpers.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -279,8 +280,8 @@
 
   GURL http_url("http://www.example.com");
   net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> http_request(
-      context.CreateRequest(http_url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
+      http_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(2u, matches.size());
@@ -295,8 +296,8 @@
       base::ContainsKey(matches_ids, std::make_pair(kExtensionId, kRuleId2)));
 
   GURL foobar_url("http://www.foobar.com");
-  std::unique_ptr<net::URLRequest> foobar_request(
-      context.CreateRequest(foobar_url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> foobar_request(context.CreateRequest(
+      foobar_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   request_data.request = foobar_request.get();
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(1u, matches.size());
@@ -422,8 +423,8 @@
 
   GURL url("http://www.google.com");
   net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(
-      context.CreateRequest(url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
+      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(NULL, request_data, false);
@@ -471,8 +472,8 @@
 
   GURL url("http://www.google.com/index.html");
   net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(
-      context.CreateRequest(url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
+      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(NULL, request_data, false);
@@ -545,8 +546,8 @@
 
   GURL url("http://www.foo.com/test");
   net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> request(
-      context.CreateRequest(url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
+      url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data(request.get(), ON_BEFORE_REQUEST);
   std::list<LinkedPtrEventResponseDelta> deltas =
       registry->CreateDeltas(NULL, request_data, false);
@@ -595,8 +596,8 @@
 
   GURL http_url("http://www.example.com");
   net::TestURLRequestContext context;
-  std::unique_ptr<net::URLRequest> http_request(
-      context.CreateRequest(http_url, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
+      http_url, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
   matches = registry->GetMatches(request_data);
   EXPECT_EQ(1u, matches.size());
@@ -654,8 +655,8 @@
 
   for (size_t i = 0; i < arraysize(matchingRuleIds); ++i) {
     // Construct the inputs.
-    std::unique_ptr<net::URLRequest> http_request(
-        context.CreateRequest(urls[i], net::DEFAULT_PRIORITY, NULL));
+    std::unique_ptr<net::URLRequest> http_request(context.CreateRequest(
+        urls[i], net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
     WebRequestData request_data(http_request.get(), ON_BEFORE_REQUEST);
     http_request->set_first_party_for_cookies(firstPartyUrls[i]);
     // Now run both rules on the input.
@@ -803,16 +804,16 @@
 
   // No match because match is in the query parameter.
   GURL url1("http://bar.com/index.html?foo.com");
-  std::unique_ptr<net::URLRequest> request1(
-      context.CreateRequest(url1, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request1(context.CreateRequest(
+      url1, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data1(request1.get(), ON_BEFORE_REQUEST);
   deltas = registry->CreateDeltas(NULL, request_data1, false);
   EXPECT_EQ(0u, deltas.size());
 
   // This is a correct match.
   GURL url2("http://foo.com/index.html");
-  std::unique_ptr<net::URLRequest> request2(
-      context.CreateRequest(url2, net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request2(context.CreateRequest(
+      url2, net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
   WebRequestData request_data2(request2.get(), ON_BEFORE_REQUEST);
   deltas = registry->CreateDeltas(NULL, request_data2, false);
   EXPECT_EQ(1u, deltas.size());
diff --git a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
index 044a7e5f..ba11fc85 100644
--- a/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_api_unittest.cc
@@ -56,6 +56,7 @@
 #include "net/dns/mock_host_resolver.h"
 #include "net/log/net_log_with_source.h"
 #include "net/log/test_net_log.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest-message.h"
@@ -249,8 +250,9 @@
   GURL redirect_url("about:redirected");
   GURL not_chosen_redirect_url("about:not_chosen");
 
-  std::unique_ptr<net::URLRequest> request(context_->CreateRequest(
-      GURL("about:blank"), net::DEFAULT_PRIORITY, &delegate_));
+  std::unique_ptr<net::URLRequest> request(
+      context_->CreateRequest(GURL("about:blank"), net::DEFAULT_PRIORITY,
+                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
   {
     // onBeforeRequest will be dispatched twice initially. The second response -
     // the redirect - should win, since it has a later |install_time|. The
@@ -303,8 +305,9 @@
   }
 
   // Now test the same thing but the extensions answer in reverse order.
-  std::unique_ptr<net::URLRequest> request2(context_->CreateRequest(
-      GURL("about:blank"), net::DEFAULT_PRIORITY, &delegate_));
+  std::unique_ptr<net::URLRequest> request2(
+      context_->CreateRequest(GURL("about:blank"), net::DEFAULT_PRIORITY,
+                              &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
   {
     ExtensionWebRequestEventRouter::EventResponse* response = NULL;
 
@@ -381,7 +384,8 @@
 
   GURL request_url("about:blank");
   std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_));
+      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
 
   // onBeforeRequest will be dispatched twice. The second response -
   // the redirect - would win, since it has a later |install_time|, but
@@ -452,7 +456,8 @@
 
   GURL request_url("about:blank");
   std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_));
+      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
 
   ExtensionWebRequestEventRouter::EventResponse* response = NULL;
 
@@ -520,7 +525,8 @@
   // The request URL can be arbitrary but must have an HTTP or HTTPS scheme.
   GURL request_url("http://www.example.com");
   std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_));
+      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
   request->set_method(method);
   if (content_type != NULL) {
     request->SetExtraRequestHeaderByName(net::HttpRequestHeaders::kContentType,
@@ -823,8 +829,9 @@
   const GURL request_url("http://www.example.com");
 
   for (size_t i = 0; i < arraysize(kMethods); ++i) {
-    std::unique_ptr<net::URLRequest> request(context_->CreateRequest(
-        request_url, net::DEFAULT_PRIORITY, &delegate_));
+    std::unique_ptr<net::URLRequest> request(
+        context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                                TRAFFIC_ANNOTATION_FOR_TESTS));
     request->set_method(kMethods[i]);
     ipc_sender_.PushTask(base::Bind(&base::DoNothing));
     request->Start();
@@ -920,7 +927,8 @@
   // Send a request. It should block. Wait for the run loop to become idle.
   GURL request_url("about:blank");
   std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_));
+      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
   // Extension response for OnErrorOccurred: Terminate the message loop.
   {
     base::RunLoop run_loop;
@@ -1053,7 +1061,8 @@
 
   GURL request_url("http://doesnotexist/does_not_exist.html");
   std::unique_ptr<net::URLRequest> request(
-      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_));
+      context_->CreateRequest(request_url, net::DEFAULT_PRIORITY, &delegate_,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
 
   // Initialize headers available before extensions are notified of the
   // onBeforeSendHeaders event.
diff --git a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
index 3682c62..af3259e 100644
--- a/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
+++ b/chrome/browser/extensions/api/web_request/web_request_permissions_unittest.cc
@@ -19,6 +19,7 @@
 #include "extensions/common/permissions/permissions_data.h"
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -122,7 +123,8 @@
   for (size_t i = 0; i < arraysize(sensitive_urls); ++i) {
     GURL sensitive_url(sensitive_urls[i]);
     std::unique_ptr<net::URLRequest> request(
-        context.CreateRequest(sensitive_url, net::DEFAULT_PRIORITY, NULL));
+        context.CreateRequest(sensitive_url, net::DEFAULT_PRIORITY, NULL,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
     EXPECT_TRUE(WebRequestPermissions::HideRequest(
         extension_info_map_.get(), request.get(), nullptr)) <<
         sensitive_urls[i];
@@ -131,7 +133,8 @@
   for (size_t i = 0; i < arraysize(non_sensitive_urls); ++i) {
     GURL non_sensitive_url(non_sensitive_urls[i]);
     std::unique_ptr<net::URLRequest> request(
-        context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL));
+        context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
     EXPECT_FALSE(WebRequestPermissions::HideRequest(
         extension_info_map_.get(), request.get(), nullptr)) <<
         non_sensitive_urls[i];
@@ -142,7 +145,8 @@
   // Normally this request is not protected:
   GURL non_sensitive_url("http://www.google.com/test.js");
   std::unique_ptr<net::URLRequest> non_sensitive_request(
-      context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL));
+      context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
+                            TRAFFIC_ANNOTATION_FOR_TESTS));
   EXPECT_FALSE(WebRequestPermissions::HideRequest(
       extension_info_map_.get(), non_sensitive_request.get(), nullptr));
   // If the origin is labeled by the WebStoreAppId, it becomes protected.
@@ -151,7 +155,8 @@
     int site_instance_id = 23;
     int view_id = 17;
     std::unique_ptr<net::URLRequest> sensitive_request(
-        context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL));
+        context.CreateRequest(non_sensitive_url, net::DEFAULT_PRIORITY, NULL,
+                              TRAFFIC_ANNOTATION_FOR_TESTS));
     ResourceRequestInfo::AllocateForTesting(
         sensitive_request.get(), content::RESOURCE_TYPE_SCRIPT, NULL,
         process_id, view_id, MSG_ROUTING_NONE,
@@ -168,8 +173,9 @@
 
 TEST_F(ExtensionWebRequestHelpersTestWithThreadsTest,
        TestCanExtensionAccessURL_HostPermissions) {
-  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
-      GURL("http://example.com"), net::DEFAULT_PRIORITY, NULL));
+  std::unique_ptr<net::URLRequest> request(
+      context.CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
+                            NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   EXPECT_EQ(PermissionsData::ACCESS_ALLOWED,
             WebRequestPermissions::CanExtensionAccessURL(
@@ -237,8 +243,9 @@
                 WebRequestPermissions::REQUIRE_ALL_URLS));
 
   // Make sure that chrome:// URLs cannot be accessed.
-  std::unique_ptr<net::URLRequest> chrome_request(context.CreateRequest(
-    GURL("chrome://version/"), net::DEFAULT_PRIORITY, nullptr));
+  std::unique_ptr<net::URLRequest> chrome_request(
+      context.CreateRequest(GURL("chrome://version/"), net::DEFAULT_PRIORITY,
+                            nullptr, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   EXPECT_EQ(PermissionsData::ACCESS_DENIED,
             WebRequestPermissions::CanExtensionAccessURL(
diff --git a/chrome/browser/extensions/extension_protocols_unittest.cc b/chrome/browser/extensions/extension_protocols_unittest.cc
index 34b5cfd5..ae5afc90 100644
--- a/chrome/browser/extensions/extension_protocols_unittest.cc
+++ b/chrome/browser/extensions/extension_protocols_unittest.cc
@@ -34,6 +34,7 @@
 #include "extensions/common/extension_builder.h"
 #include "extensions/common/file_util.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_job_factory_impl.h"
 #include "net/url_request/url_request_status.h"
@@ -246,7 +247,7 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension.GetResourceURL(relative_path), net::DEFAULT_PRIORITY,
-            &test_delegate_));
+            &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MAIN_FRAME);
     return test_delegate_.request_status();
   }
@@ -301,7 +302,7 @@
       std::unique_ptr<net::URLRequest> request(
           resource_context_.GetRequestContext()->CreateRequest(
               extension->GetResourceURL("404.html"), net::DEFAULT_PRIORITY,
-              &test_delegate_));
+              &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
       StartRequest(request.get(), content::RESOURCE_TYPE_MAIN_FRAME);
 
       if (cases[i].should_allow_main_frame_load) {
@@ -323,7 +324,7 @@
         std::unique_ptr<net::URLRequest> request(
             resource_context_.GetRequestContext()->CreateRequest(
                 extension->GetResourceURL("404.html"), net::DEFAULT_PRIORITY,
-                &test_delegate_));
+                &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
         StartRequest(request.get(), content::RESOURCE_TYPE_SUB_FRAME);
 
         if (cases[i].should_allow_sub_frame_load) {
@@ -365,7 +366,8 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension->GetResourceURL("webstore_icon_16.png"),
-            net::DEFAULT_PRIORITY, &test_delegate_));
+            net::DEFAULT_PRIORITY, &test_delegate_,
+            TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MEDIA);
     EXPECT_EQ(net::OK, test_delegate_.request_status());
     CheckForContentLengthHeader(request.get());
@@ -378,7 +380,8 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension->GetResourceURL("webstore_icon_16.png"),
-            net::DEFAULT_PRIORITY, &test_delegate_));
+            net::DEFAULT_PRIORITY, &test_delegate_,
+            TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MEDIA);
     EXPECT_EQ(net::OK, test_delegate_.request_status());
     CheckForContentLengthHeader(request.get());
@@ -401,7 +404,7 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension->GetResourceURL("test.dat"), net::DEFAULT_PRIORITY,
-            &test_delegate_));
+            &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MEDIA);
     EXPECT_EQ(net::OK, test_delegate_.request_status());
 
@@ -442,7 +445,7 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension->GetResourceURL("test.dat"), net::DEFAULT_PRIORITY,
-            &test_delegate_));
+            &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MAIN_FRAME);
     EXPECT_EQ(net::OK, test_delegate_.request_status());
   }
@@ -455,7 +458,7 @@
       std::unique_ptr<net::URLRequest> request(
           resource_context_.GetRequestContext()->CreateRequest(
               extension->GetResourceURL("test.dat"), net::DEFAULT_PRIORITY,
-              &test_delegate_));
+              &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
       StartRequest(request.get(), content::RESOURCE_TYPE_SUB_FRAME);
       EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, test_delegate_.request_status());
     }
@@ -466,7 +469,7 @@
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
             extension->GetResourceURL("test.dat"), net::DEFAULT_PRIORITY,
-            &test_delegate_));
+            &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
     StartRequest(request.get(), content::RESOURCE_TYPE_MEDIA);
     EXPECT_EQ(net::ERR_BLOCKED_BY_CLIENT, test_delegate_.request_status());
   }
diff --git a/chrome/browser/extensions/user_script_listener_unittest.cc b/chrome/browser/extensions/user_script_listener_unittest.cc
index 3614c119..ec0f8111 100644
--- a/chrome/browser/extensions/user_script_listener_unittest.cc
+++ b/chrome/browser/extensions/user_script_listener_unittest.cc
@@ -25,6 +25,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "extensions/browser/extension_registry.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_interceptor.h"
@@ -175,8 +176,8 @@
       const std::string& url_string,
       net::TestURLRequestContext* context) {
     GURL url(url_string);
-    std::unique_ptr<net::URLRequest> request(
-        context->CreateRequest(url, net::DEFAULT_PRIORITY, delegate));
+    std::unique_ptr<net::URLRequest> request(context->CreateRequest(
+        url, net::DEFAULT_PRIORITY, delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
 
     ResourceThrottle* throttle = listener_->CreateResourceThrottle(
         url, content::RESOURCE_TYPE_MAIN_FRAME);
@@ -351,8 +352,8 @@
   net::TestDelegate delegate;
   net::TestURLRequestContext context;
   GURL url(kMatchingUrl);
-  std::unique_ptr<net::URLRequest> request(
-      context.CreateRequest(url, net::DEFAULT_PRIORITY, &delegate));
+  std::unique_ptr<net::URLRequest> request(context.CreateRequest(
+      url, net::DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   ResourceThrottle* throttle =
       listener_->CreateResourceThrottle(url, content::RESOURCE_TYPE_MAIN_FRAME);
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 2980249f..d3e7af50 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -2262,10 +2262,10 @@
 
 #if defined(OS_CHROMEOS)
 
-const char kEnableNativeCupsName[] = "Native CUPS";
+const char kDisableNativeCupsName[] = "Native CUPS";
 
-const char kEnableNativeCupsDescription[] =
-    "Enables the use of the native CUPS printing backend.";
+const char kDisableNativeCupsDescription[] =
+    "Disable the use of the native CUPS printing backend.";
 
 const char kEnableAndroidWallpapersAppName[] = "Android Wallpapers App";
 
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index e3b32b4..b83249b 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -2461,10 +2461,10 @@
 #if defined(OS_CHROMEOS)
 
 // Name of the native cups flag.
-extern const char kEnableNativeCupsName[];
+extern const char kDisableNativeCupsName[];
 
 // Description of the native CUPS flag
-extern const char kEnableNativeCupsDescription[];
+extern const char kDisableNativeCupsDescription[];
 
 // Name of the Android Wallpapers App flag.
 extern const char kEnableAndroidWallpapersAppName[];
diff --git a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_unittest.cc b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_unittest.cc
index 313f5f4c..b43dc63 100644
--- a/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_unittest.cc
+++ b/chrome/browser/loader/chrome_resource_dispatcher_host_delegate_unittest.cc
@@ -14,6 +14,7 @@
 #include "content/public/browser/navigation_data.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -37,8 +38,9 @@
 TEST_F(ChromeResourceDispatcherHostDelegateTest,
        GetNavigationDataWithDataReductionProxyData) {
   std::unique_ptr<net::URLRequestContext> context(new net::URLRequestContext());
-  std::unique_ptr<net::URLRequest> fake_request(context->CreateRequest(
-      GURL("google.com"), net::RequestPriority::IDLE, nullptr));
+  std::unique_ptr<net::URLRequest> fake_request(
+      context->CreateRequest(GURL("google.com"), net::RequestPriority::IDLE,
+                             nullptr, TRAFFIC_ANNOTATION_FOR_TESTS));
   // Add DataReductionProxyData to URLRequest
   data_reduction_proxy::DataReductionProxyData* data_reduction_proxy_data =
       data_reduction_proxy::DataReductionProxyData::GetDataAndCreateIfNecessary(
@@ -63,8 +65,9 @@
 TEST_F(ChromeResourceDispatcherHostDelegateTest,
        GetNavigationDataWithoutDataReductionProxyData) {
   std::unique_ptr<net::URLRequestContext> context(new net::URLRequestContext());
-  std::unique_ptr<net::URLRequest> fake_request(context->CreateRequest(
-      GURL("google.com"), net::RequestPriority::IDLE, nullptr));
+  std::unique_ptr<net::URLRequest> fake_request(
+      context->CreateRequest(GURL("google.com"), net::RequestPriority::IDLE,
+                             nullptr, TRAFFIC_ANNOTATION_FOR_TESTS));
   std::unique_ptr<ChromeResourceDispatcherHostDelegate> delegate(
       new ChromeResourceDispatcherHostDelegate());
   ChromeNavigationData* chrome_navigation_data =
diff --git a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
index cfd8ee04..394ec70 100644
--- a/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
+++ b/chrome/browser/media/android/router/media_router_dialog_controller_android.cc
@@ -87,6 +87,18 @@
   CancelPresentationRequest();
 }
 
+void MediaRouterDialogControllerAndroid::OnMediaSourceNotSupported(
+    JNIEnv* env,
+    const JavaParamRef<jobject>& obj) {
+  std::unique_ptr<CreatePresentationConnectionRequest> request =
+      TakeCreateConnectionRequest();
+  if (!request)
+    return;
+
+  request->InvokeErrorCallback(content::PresentationError(
+      content::PRESENTATION_ERROR_NO_AVAILABLE_SCREENS, "No screens found."));
+}
+
 void MediaRouterDialogControllerAndroid::CancelPresentationRequest() {
   std::unique_ptr<CreatePresentationConnectionRequest> request =
       TakeCreateConnectionRequest();
diff --git a/chrome/browser/media/android/router/media_router_dialog_controller_android.h b/chrome/browser/media/android/router/media_router_dialog_controller_android.h
index 1d74b1e9..ffb9c4c 100644
--- a/chrome/browser/media/android/router/media_router_dialog_controller_android.h
+++ b/chrome/browser/media/android/router/media_router_dialog_controller_android.h
@@ -43,6 +43,11 @@
   // taking any action (e.g. closing the route or selecting a sink).
   void OnDialogCancelled(JNIEnv* env,
                          const base::android::JavaParamRef<jobject>& obj);
+  // Notifies the controller the media source URN is not supported so it could
+  // properly reject the request.
+  void OnMediaSourceNotSupported(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
 
  private:
   friend class content::WebContentsUserData<MediaRouterDialogControllerAndroid>;
diff --git a/chrome/browser/net/chrome_network_delegate_unittest.cc b/chrome/browser/net/chrome_network_delegate_unittest.cc
index 8a08c72..5020876 100644
--- a/chrome/browser/net/chrome_network_delegate_unittest.cc
+++ b/chrome/browser/net/chrome_network_delegate_unittest.cc
@@ -39,6 +39,7 @@
 #include "net/base/request_priority.h"
 #include "net/http/http_request_headers.h"
 #include "net/socket/socket_test_util.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -73,8 +74,9 @@
   socket_factory->AddSocketDataProvider(&response_socket_data_provider);
   net::TestDelegate test_delegate;
   test_delegate.set_quit_on_complete(true);
-  std::unique_ptr<net::URLRequest> request(context->CreateRequest(
-      GURL("http://example.com"), net::DEFAULT_PRIORITY, &test_delegate));
+  std::unique_ptr<net::URLRequest> request(
+      context->CreateRequest(GURL("http://example.com"), net::DEFAULT_PRIORITY,
+                             &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
 
   content::ResourceRequestInfo::AllocateForTesting(
       request.get(), content::RESOURCE_TYPE_MAIN_FRAME, nullptr, -2, -2, -2,
@@ -262,8 +264,9 @@
     base::HistogramTester histograms;
 
     net::TestDelegate test_delegate;
-    std::unique_ptr<net::URLRequest> request(context()->CreateRequest(
-        test.url, net::DEFAULT_PRIORITY, &test_delegate));
+    std::unique_ptr<net::URLRequest> request(
+        context()->CreateRequest(test.url, net::DEFAULT_PRIORITY,
+                                 &test_delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     if (test.is_main_frame) {
       request->SetLoadFlags(request->load_flags() |
                             net::LOAD_MAIN_FRAME_DEPRECATED);
@@ -350,8 +353,9 @@
     safe_search_util::ClearForceGoogleSafeSearchCountForTesting();
     safe_search_util::ClearForceYouTubeRestrictCountForTesting();
 
-    std::unique_ptr<net::URLRequest> request(context_.CreateRequest(
-        GURL("http://anyurl.com"), net::DEFAULT_PRIORITY, &delegate_));
+    std::unique_ptr<net::URLRequest> request(
+        context_.CreateRequest(GURL("http://anyurl.com"), net::DEFAULT_PRIORITY,
+                               &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
 
     request->Start();
     base::RunLoop().RunUntilIdle();
@@ -421,7 +425,7 @@
     allowed_domains_for_apps_.SetValue(allowed);
 
     std::unique_ptr<net::URLRequest> request(context_.CreateRequest(
-        url, net::DEFAULT_PRIORITY, &delegate_));
+        url, net::DEFAULT_PRIORITY, &delegate_, TRAFFIC_ANNOTATION_FOR_TESTS));
 
     request->Start();
     base::RunLoop().RunUntilIdle();
diff --git a/chrome/browser/net/safe_search_util_unittest.cc b/chrome/browser/net/safe_search_util_unittest.cc
index b0d9722..d52f14d 100644
--- a/chrome/browser/net/safe_search_util_unittest.cc
+++ b/chrome/browser/net/safe_search_util_unittest.cc
@@ -8,6 +8,7 @@
 #include "base/strings/string_piece.h"
 #include "chrome/common/url_constants.h"
 #include "net/http/http_request_headers.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -18,7 +19,8 @@
   ~SafeSearchUtilTest() override {}
 
   std::unique_ptr<net::URLRequest> CreateRequest(const std::string& url) {
-    return context_.CreateRequest(GURL(url), net::DEFAULT_PRIORITY, NULL);
+    return context_.CreateRequest(GURL(url), net::DEFAULT_PRIORITY, NULL,
+                                  TRAFFIC_ANNOTATION_FOR_TESTS);
   }
 
   std::unique_ptr<net::URLRequest> CreateYoutubeRequest() {
diff --git a/chrome/browser/net/spdyproxy/chrome_data_use_group_provider_unittest.cc b/chrome/browser/net/spdyproxy/chrome_data_use_group_provider_unittest.cc
index 22a7a7a..8a36b1f 100644
--- a/chrome/browser/net/spdyproxy/chrome_data_use_group_provider_unittest.cc
+++ b/chrome/browser/net/spdyproxy/chrome_data_use_group_provider_unittest.cc
@@ -11,6 +11,7 @@
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -24,8 +25,9 @@
 
   std::unique_ptr<net::URLRequest> CreateRequestForFrame(int render_process_id,
                                                          int render_frame_id) {
-    std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
-        GURL("http://foo.com/"), net::IDLE, &test_delegate_);
+    std::unique_ptr<net::URLRequest> request =
+        context_.CreateRequest(GURL("http://foo.com/"), net::IDLE,
+                               &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
 
     content::ResourceRequestInfo::AllocateForTesting(
         request.get(), content::RESOURCE_TYPE_MAIN_FRAME,
diff --git a/chrome/browser/net/spdyproxy/chrome_data_use_group_unittest.cc b/chrome/browser/net/spdyproxy/chrome_data_use_group_unittest.cc
index b215f61..af86885 100644
--- a/chrome/browser/net/spdyproxy/chrome_data_use_group_unittest.cc
+++ b/chrome/browser/net/spdyproxy/chrome_data_use_group_unittest.cc
@@ -10,6 +10,7 @@
 #include "content/public/browser/resource_request_info.h"
 #include "content/public/common/previews_state.h"
 #include "content/public/test/test_browser_thread_bundle.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -18,8 +19,9 @@
 class ChromeDataUseGroupTest : public testing::Test {
  protected:
   void SetUp() override {
-    std::unique_ptr<net::URLRequest> request = context_.CreateRequest(
-        GURL("http://foo.com/index.html"), net::IDLE, &test_delegate_);
+    std::unique_ptr<net::URLRequest> request =
+        context_.CreateRequest(GURL("http://foo.com/index.html"), net::IDLE,
+                               &test_delegate_, TRAFFIC_ANNOTATION_FOR_TESTS);
 
     content::ResourceRequestInfo::AllocateForTesting(
         request.get(), content::RESOURCE_TYPE_MAIN_FRAME, nullptr,
diff --git a/chrome/browser/notifications/notification_common.h b/chrome/browser/notifications/notification_common.h
index 280c84d..9a6e678c 100644
--- a/chrome/browser/notifications/notification_common.h
+++ b/chrome/browser/notifications/notification_common.h
@@ -24,7 +24,8 @@
   enum Type {
     PERSISTENT = 0,
     NON_PERSISTENT = 1,
-    TYPE_MAX = NON_PERSISTENT
+    EXTENSION = 2,
+    TYPE_MAX = EXTENSION
   };
 
   // Open the Notification settings screen when clicking the right button.
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac.mm b/chrome/browser/notifications/notification_platform_bridge_mac.mm
index 50a54b6..16220a75 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac.mm
@@ -172,8 +172,12 @@
   [builder setTitle:base::SysUTF16ToNSString(notification.title())];
   [builder setContextMessage:base::SysUTF16ToNSString(notification.message())];
 
+  bool requires_attribution =
+      notification.context_message().empty() &&
+      notification_type != NotificationCommon::EXTENSION;
+
   base::string16 subtitle =
-      notification.context_message().empty()
+      requires_attribution
           ? url_formatter::FormatOriginForSecurityDisplay(
                 url::Origin(notification.origin_url()),
                 url_formatter::SchemeDisplay::OMIT_HTTP_AND_HTTPS)
@@ -184,6 +188,8 @@
     [builder setIcon:notification.icon().ToNSImage()];
   }
 
+  [builder setShowSettingsButton:(notification_type !=
+                                  NotificationCommon::EXTENSION)];
   std::vector<message_center::ButtonInfo> buttons = notification.buttons();
   if (!buttons.empty()) {
     DCHECK_LE(buttons.size(), blink::kWebNotificationMaxActions);
diff --git a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
index e633c457..e1a819f 100644
--- a/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
+++ b/chrome/browser/notifications/notification_platform_bridge_mac_unittest.mm
@@ -54,6 +54,7 @@
     [builder setProfileId:@"profile_id"];
     [builder setIncognito:false];
     [builder setNotificationType:@(NotificationCommon::PERSISTENT)];
+    [builder setShowSettingsButton:true];
 
     return [builder buildUserNotification];
   }
diff --git a/chrome/browser/prefs/active_profile_pref_service.cc b/chrome/browser/prefs/active_profile_pref_service.cc
index bafda8e..b508429 100644
--- a/chrome/browser/prefs/active_profile_pref_service.cc
+++ b/chrome/browser/prefs/active_profile_pref_service.cc
@@ -19,7 +19,7 @@
     const std::vector<PrefValueStore::PrefStoreType>& already_connected_types,
     const ConnectCallback& callback) {
   auto* connector = content::BrowserContext::GetConnectorFor(
-      ProfileManager::GetActiveUserProfile());
+      ProfileManager::GetActiveUserProfile()->GetOriginalProfile());
   connector->BindInterface(prefs::mojom::kServiceName, &connector_ptr_);
   connector_ptr_.set_connection_error_handler(base::Bind(
       &ActiveProfilePrefService::OnConnectError, base::Unretained(this)));
diff --git a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
index e9b5872d..7726902 100644
--- a/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
+++ b/chrome/browser/prerender/prerender_resource_throttle_unittest.cc
@@ -23,6 +23,7 @@
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
@@ -211,7 +212,7 @@
   DeferredRedirectDelegate delegate;
   std::unique_ptr<net::URLRequest> request(url_request_context.CreateRequest(
       net::URLRequestMockHTTPJob::GetMockUrl("prerender/image-deferred.png"),
-      net::DEFAULT_PRIORITY, &delegate));
+      net::DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
   content::ResourceRequestInfo::AllocateForTesting(
       request.get(), content::RESOURCE_TYPE_IMAGE, NULL, kDefaultChildId,
       kDefaultRouteId, MSG_ROUTING_NONE,
diff --git a/chrome/browser/profile_resetter/profile_resetter.cc b/chrome/browser/profile_resetter/profile_resetter.cc
index d9ef5a1..b23f2d4 100644
--- a/chrome/browser/profile_resetter/profile_resetter.cc
+++ b/chrome/browser/profile_resetter/profile_resetter.cc
@@ -18,7 +18,6 @@
 #include "chrome/browser/browsing_data/chrome_browsing_data_remover_delegate.h"
 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
 #include "chrome/browser/extensions/extension_service.h"
-#include "chrome/browser/google/google_url_tracker_factory.h"
 #include "chrome/browser/profile_resetter/brandcoded_default_settings.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -32,7 +31,6 @@
 #include "components/content_settings/core/browser/host_content_settings_map.h"
 #include "components/content_settings/core/browser/website_settings_info.h"
 #include "components/content_settings/core/browser/website_settings_registry.h"
-#include "components/google/core/browser/google_url_tracker.h"
 #include "components/prefs/pref_service.h"
 #include "components/prefs/scoped_user_pref_update.h"
 #include "components/search_engines/search_engines_pref_names.h"
@@ -183,18 +181,6 @@
 
     template_url_service_->RepairPrepopulatedSearchEngines();
 
-    // Reset Google search URL.
-    const TemplateURL* default_search_provider =
-        template_url_service_->GetDefaultSearchProvider();
-    if (default_search_provider &&
-        default_search_provider->HasGoogleBaseURLs(
-            template_url_service_->search_terms_data())) {
-      GoogleURLTracker* tracker =
-          GoogleURLTrackerFactory::GetForProfile(profile_);
-      if (tracker)
-        tracker->RequestServerCheck(true);
-    }
-
     MarkAsDone(DEFAULT_SEARCH_ENGINE);
   } else {
     template_url_service_sub_ =
diff --git a/chrome/browser/resources/options/browser_options.js b/chrome/browser/resources/options/browser_options.js
index 92d5a88..2266bdbe 100644
--- a/chrome/browser/resources/options/browser_options.js
+++ b/chrome/browser/resources/options/browser_options.js
@@ -724,7 +724,7 @@
 
       // CUPS Print section (CrOS only).
       if (cr.isChromeOS) {
-        if (loadTimeData.getBoolean('cupsPrintEnabled')) {
+        if (!loadTimeData.getBoolean('cupsPrintDisabled')) {
           $('cups-printers-section').hidden = false;
           $('cupsPrintersManageButton').onclick = function() {
             chrome.send('showCupsPrintDevicesPage');
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.html b/chrome/browser/resources/settings/settings_ui/settings_ui.html
index 40ceb14..7160876 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.html
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.html
@@ -105,11 +105,10 @@
       </div>
     </dialog>
     <div id="dropShadow"></div>
-    <div id="container">
+    <div id="container" class="no-outline" tabindex="-1">
       <!-- Used by IntersectionObserver, has a 0px height intentionally -->
       <div id="intersectionProbe"></div>
-      <settings-main id="main" prefs="{{prefs}}" tabindex="-1"
-          class="no-outline"
+      <settings-main id="main" prefs="{{prefs}}"
           toolbar-spinner-active="{{toolbarSpinnerActive_}}"
           page-visibility="[[pageVisibility_]]"
           show-android-apps="[[showAndroidApps_]]"
diff --git a/chrome/browser/resources/settings/settings_ui/settings_ui.js b/chrome/browser/resources/settings/settings_ui/settings_ui.js
index 9a6240e..f0d6c15 100644
--- a/chrome/browser/resources/settings/settings_ui/settings_ui.js
+++ b/chrome/browser/resources/settings/settings_ui/settings_ui.js
@@ -268,7 +268,7 @@
 
   /** @private */
   onMenuClosed_: function() {
-    this.$$('settings-main').focus();
+    this.$.container.focus();
   },
 
   /** @private */
diff --git a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
index 90802044..0232f03 100644
--- a/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
+++ b/chrome/browser/safe_browsing/incident_reporting/resource_request_detector_unittest.cc
@@ -21,6 +21,7 @@
 #include "crypto/sha2.h"
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_test_util.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -86,8 +87,8 @@
   std::unique_ptr<net::URLRequest> GetTestURLRequest(
       const std::string& url,
       content::ResourceType resource_type) const {
-    std::unique_ptr<net::URLRequest> url_request(
-        context_.CreateRequest(GURL(url), net::DEFAULT_PRIORITY, NULL));
+    std::unique_ptr<net::URLRequest> url_request(context_.CreateRequest(
+        GURL(url), net::DEFAULT_PRIORITY, NULL, TRAFFIC_ANNOTATION_FOR_TESTS));
 
     content::ResourceRequestInfo::AllocateForTesting(
         url_request.get(), resource_type,
diff --git a/chrome/browser/search/iframe_source_unittest.cc b/chrome/browser/search/iframe_source_unittest.cc
index c2d14270..909d300 100644
--- a/chrome/browser/search/iframe_source_unittest.cc
+++ b/chrome/browser/search/iframe_source_unittest.cc
@@ -18,6 +18,7 @@
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "ipc/ipc_message.h"
 #include "net/base/request_priority.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_test_util.h"
@@ -92,7 +93,8 @@
                                                int render_process_id) {
     std::unique_ptr<net::URLRequest> request(
         resource_context_.GetRequestContext()->CreateRequest(
-            GURL(url), net::DEFAULT_PRIORITY, NULL));
+            GURL(url), net::DEFAULT_PRIORITY, NULL,
+            TRAFFIC_ANNOTATION_FOR_TESTS));
     content::ResourceRequestInfo::AllocateForTesting(
         request.get(), content::RESOURCE_TYPE_SUB_FRAME, &resource_context_,
         render_process_id, MSG_ROUTING_NONE, MSG_ROUTING_NONE,
diff --git a/chrome/browser/sessions/better_session_restore_browsertest.cc b/chrome/browser/sessions/better_session_restore_browsertest.cc
index 381333c..d1bb334 100644
--- a/chrome/browser/sessions/better_session_restore_browsertest.cc
+++ b/chrome/browser/sessions/better_session_restore_browsertest.cc
@@ -594,9 +594,16 @@
   CheckFormRestored(new_browser, false, false);
 }
 
+// Flaky on Mac: https://crbug.com/709504
+#if defined(OS_MACOSX)
+#define MAYBE_SessionCookiesCloseAllBrowsers \
+  DISABLED_SessionCookiesCloseAllBrowsers
+#else
+#define MAYBE_SessionCookiesCloseAllBrowsers SessionCookiesCloseAllBrowsers
+#endif
 // Check that session cookies are cleared on a wrench menu quit.
 IN_PROC_BROWSER_TEST_F(ContinueWhereILeftOffTest,
-                       SessionCookiesCloseAllBrowsers) {
+                       MAYBE_SessionCookiesCloseAllBrowsers) {
   // Set the startup preference to "continue where I left off" and visit a page
   // which stores a session cookie.
   StoreDataWithPage("session_cookies.html");
diff --git a/chrome/browser/site_per_process_interactive_browsertest.cc b/chrome/browser/site_per_process_interactive_browsertest.cc
index a709572..37d6ab4a 100644
--- a/chrome/browser/site_per_process_interactive_browsertest.cc
+++ b/chrome/browser/site_per_process_interactive_browsertest.cc
@@ -302,11 +302,12 @@
   EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
 }
 
-// TODO(https://crbug.com/702330): Enable this test.
+#if (defined(OS_LINUX) && !defined(USE_OZONE)) || defined(OS_WIN)
 // Ensures that renderers know to advance focus to sibling frames and parent
 // frames in the presence of mouse click initiated focus changes.
+// Verifies against regression of https://crbug.com/702330
 IN_PROC_BROWSER_TEST_F(SitePerProcessInteractiveBrowserTest,
-                       DISABLED_TabAndMouseFocusNavigation) {
+                       TabAndMouseFocusNavigation) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b,c)"));
   ui_test_utils::NavigateToURL(browser(), main_url);
@@ -336,7 +337,6 @@
   // iframe: 55,18;55,67
   std::string script =
       "function onFocus(e) {"
-      "  console.log(window.name+'-focused-'+ e.target.id);"
       "  domAutomationController.setAutomationId(0);"
       "  domAutomationController.send(window.name + '-focused-' + e.target.id);"
       "}"
@@ -433,47 +433,59 @@
   EXPECT_EQ("\"root-focused-input1\"",
             click_element_and_wait_for_message(main_frame_input_coords[0]));
   EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
+  auto frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child1);
   EXPECT_EQ("\"child1-focused-input1\"",
             click_element_and_wait_for_message(child1_input_coords[0]));
-  EXPECT_EQ(child1, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(main_frame);
   EXPECT_EQ("\"root-focused-input1\"", press_tab_and_wait_for_message(true));
-  EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
 
   // Tab from child2 forward to root.
   EXPECT_EQ("\"root-focused-input2\"",
             click_element_and_wait_for_message(main_frame_input_coords[1]));
   EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child2);
   EXPECT_EQ("\"child2-focused-input2\"",
             click_element_and_wait_for_message(child2_input_coords[1]));
-  EXPECT_EQ(child2, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(main_frame);
   EXPECT_EQ("\"root-focused-input2\"", press_tab_and_wait_for_message(false));
-  EXPECT_EQ(main_frame, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
 
   // Tab forward from child1 to child2.
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child2);
   EXPECT_EQ("\"child2-focused-input1\"",
             click_element_and_wait_for_message(child2_input_coords[0]));
-  EXPECT_EQ(child2, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child1);
   EXPECT_EQ("\"child1-focused-input2\"",
             click_element_and_wait_for_message(child1_input_coords[1]));
-  EXPECT_EQ(child1, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child2);
   EXPECT_EQ("\"child2-focused-input1\"", press_tab_and_wait_for_message(false));
-  EXPECT_EQ(child2, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
 
   // Tab backward from child2 to child1.
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child1);
   EXPECT_EQ("\"child1-focused-input2\"",
             click_element_and_wait_for_message(child1_input_coords[1]));
-  EXPECT_EQ(child1, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child2);
   EXPECT_EQ("\"child2-focused-input1\"",
             click_element_and_wait_for_message(child2_input_coords[0]));
-  EXPECT_EQ(child2, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
+  frame_focused = base::MakeUnique<content::FrameFocusedObserver>(child1);
   EXPECT_EQ("\"child1-focused-input2\"", press_tab_and_wait_for_message(true));
-  EXPECT_EQ(child1, web_contents->GetFocusedFrame());
+  // EXPECT_EQ(child1, web_contents->GetFocusedFrame());
+  frame_focused->Wait();
 
   // Ensure there are no pending focus events after tabbing.
   EXPECT_EQ("\"root-focused-input1\"",
             click_element_and_wait_for_message(main_frame_input_coords[0]))
       << "Unexpected extra focus events.";
 }
+#endif
 
 namespace {
 
diff --git a/chrome/browser/ssl/ssl_client_certificate_selector_test.cc b/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
index b069cc0f..7e247e7 100644
--- a/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
+++ b/chrome/browser/ssl/ssl_client_certificate_selector_test.cc
@@ -18,6 +18,7 @@
 #include "net/ssl/ssl_cert_request_info.h"
 #include "net/test/cert_test_util.h"
 #include "net/test/test_data_directory.h"
+#include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
 #include "net/url_request/url_request.h"
 #include "net/url_request/url_request_context.h"
 #include "net/url_request/url_request_context_getter.h"
@@ -99,6 +100,7 @@
     net::URLRequestContextGetter* context_getter) {
   std::unique_ptr<net::URLRequest> request =
       context_getter->GetURLRequestContext()->CreateRequest(
-          GURL("https://example"), net::DEFAULT_PRIORITY, NULL);
+          GURL("https://example"), net::DEFAULT_PRIORITY, NULL,
+          TRAFFIC_ANNOTATION_FOR_TESTS);
   return request;
 }
diff --git a/chrome/browser/sync/test/integration/two_client_app_list_sync_test.cc b/chrome/browser/sync/test/integration/two_client_app_list_sync_test.cc
index 7ddd650465..2a49412 100644
--- a/chrome/browser/sync/test/integration/two_client_app_list_sync_test.cc
+++ b/chrome/browser/sync/test/integration/two_client_app_list_sync_test.cc
@@ -86,6 +86,12 @@
     return true;
   }
 
+  void AwaitQuiescenceAndInstallAppsPendingForSync() {
+    ASSERT_TRUE(AwaitQuiescence());
+    InstallAppsPendingForSync(GetProfile(0));
+    InstallAppsPendingForSync(GetProfile(1));
+  }
+
  private:
   void WaitForExtensionServicesToLoad() {
     for (int i = 0; i < num_clients(); ++i)
@@ -156,10 +162,7 @@
 
   ASSERT_TRUE(SetupSync());
 
-  ASSERT_TRUE(AwaitQuiescence());
-
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
   // Verify the app lists, but ignore absolute position values, checking only
   // relative positions (see note in app_list_syncable_service.h).
@@ -194,10 +197,7 @@
     std::string id = InstallApp(GetProfile(1), i);
   }
 
-  ASSERT_TRUE(AwaitQuiescence());
-
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
   // Verify the app lists, but ignore absolute position values, checking only
   // relative positions (see note in app_list_syncable_service.h).
@@ -209,10 +209,8 @@
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
@@ -221,10 +219,8 @@
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   UninstallApp(GetProfile(0), 0);
@@ -241,10 +237,8 @@
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   InstallApp(GetProfile(0), 0);
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   UninstallApp(GetProfile(0), 0);
@@ -252,9 +246,8 @@
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   InstallApp(GetProfile(0), 1);
-  ASSERT_TRUE(AwaitQuiescence());
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
+  AwaitQuiescenceAndInstallAppsPendingForSync();
+
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
@@ -273,10 +266,8 @@
   InstallApp(GetProfile(1), 2);
 
   InstallApp(GetProfile(1), 3);
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  ASSERT_TRUE(AwaitQuiescence());
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
@@ -344,10 +335,8 @@
 
   // Enable APP_LIST by enabling APPS since APP_LIST is in APPS groups.
   ASSERT_TRUE(GetClient(1)->EnableSyncForDatatype(syncer::APPS));
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
@@ -364,22 +353,14 @@
   ASSERT_FALSE(AllProfilesHaveSameAppList());
 
   ASSERT_TRUE(GetClient(1)->EnableSyncForAllDatatypes());
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
 // Install some apps on both clients, then sync. Move an app on one client
 // and sync. Both clients should have the updated position for the app.
-// crbug.com/689662
-#if defined(OS_CHROMEOS)
-#define MAYBE_Move DISABLED_Move
-#else
-#define MAYBE_Move Move
-#endif
-IN_PROC_BROWSER_TEST_F(TwoClientAppListSyncTest, MAYBE_Move) {
+IN_PROC_BROWSER_TEST_F(TwoClientAppListSyncTest, Move) {
   ASSERT_TRUE(SetupSync());
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
@@ -387,7 +368,8 @@
   for (int i = 0; i < kNumApps; ++i)
     InstallApp(GetProfile(1), i);
 
-  ASSERT_TRUE(AwaitQuiescence());
+  AwaitQuiescenceAndInstallAppsPendingForSync();
+
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   size_t first = kNumDefaultApps;
@@ -395,6 +377,7 @@
       GetProfile(0), first + 1, first + 2);
 
   ASSERT_TRUE(AwaitQuiescence());
+
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 }
 
@@ -412,10 +395,8 @@
   // Install a default app in Profile 0 only.
   const int default_app_index = 1;
   std::string default_app_id = InstallApp(GetProfile(0), default_app_index);
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  ASSERT_TRUE(AwaitQuiescence());
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   // Flag Default app in Profile 1.
@@ -441,10 +422,8 @@
   EXPECT_EQ(default_app_id, app_id2);
   sync_item = GetSyncItem(GetProfile(0), app_id2);
   EXPECT_EQ(sync_pb::AppListSpecifics::TYPE_APP, sync_item->item_type);
+  AwaitQuiescenceAndInstallAppsPendingForSync();
 
-  ASSERT_TRUE(AwaitQuiescence());
-  InstallAppsPendingForSync(GetProfile(0));
-  InstallAppsPendingForSync(GetProfile(1));
   ASSERT_TRUE(AllProfilesHaveSameAppList());
 
   // Ensure that the REMOVE_DEFAULT_APP SyncItem entry in Profile 1 is replaced
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index c37e7c1..81f24e8 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -1518,8 +1518,8 @@
       "views/page_info/chosen_object_row_observer.h",
       "views/page_info/non_accessible_image_view.cc",
       "views/page_info/non_accessible_image_view.h",
-      "views/page_info/page_info_popup_view.cc",
-      "views/page_info/page_info_popup_view.h",
+      "views/page_info/page_info_bubble_view.cc",
+      "views/page_info/page_info_bubble_view.h",
       "views/page_info/permission_selector_row.cc",
       "views/page_info/permission_selector_row.h",
       "views/page_info/permission_selector_row_observer.h",
@@ -2113,7 +2113,7 @@
       "views/desktop_capture/desktop_media_source_view.cc",
       "views/desktop_capture/desktop_media_source_view.h",
       "views/dropdown_bar_host_aura.cc",
-      "views/frame/browser_non_client_frame_view_factory_views.cc",
+      "views/frame/browser_non_client_frame_view_factory_chromeos.cc",
       "views/ime/ime_window_frame_view.cc",
       "views/ime/ime_window_frame_view.h",
       "views/ime/ime_window_view.cc",
@@ -2137,6 +2137,7 @@
     ]
     if (!is_chromeos) {
       sources += [
+        "views/frame/browser_non_client_frame_view_factory_views.cc",
         "views/frame/desktop_browser_frame_aura.cc",
         "views/frame/desktop_browser_frame_aura.h",
         "views/message_center/message_center_frame_view.cc",
diff --git a/chrome/browser/ui/android/context_menu_helper.cc b/chrome/browser/ui/android/context_menu_helper.cc
index 3f237be..8c74369 100644
--- a/chrome/browser/ui/android/context_menu_helper.cc
+++ b/chrome/browser/ui/android/context_menu_helper.cc
@@ -8,10 +8,10 @@
 
 #include <vector>
 
-#include "base/android/jni_android.h"
-#include "base/android/jni_array.h"
+#include "base/android/callback_android.h"
 #include "base/android/jni_string.h"
 #include "base/bind_helpers.h"
+#include "base/callback.h"
 #include "chrome/browser/android/download/download_controller_base.h"
 #include "chrome/browser/ui/tab_contents/core_tab_helper.h"
 #include "chrome/common/thumbnail_capturer.mojom.h"
@@ -24,8 +24,8 @@
 #include "jni/ContextMenuParams_jni.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "third_party/WebKit/public/web/WebContextMenuData.h"
-#include "ui/android/window_android.h"
 #include "ui/gfx/geometry/point.h"
+#include "ui/gfx/geometry/size.h"
 
 using base::android::ConvertJavaStringToUTF8;
 using base::android::ConvertUTF8ToJavaString;
@@ -34,14 +34,22 @@
 
 DEFINE_WEB_CONTENTS_USER_DATA_KEY(ContextMenuHelper);
 
-const int kShareImageMaxWidth = 2048;
-const int kShareImageMaxHeight = 2048;
-
 const char kDataReductionProxyPassthroughHeader[] =
     "Chrome-Proxy: pass-through\r\n";
 
+namespace {
+
+void OnRetrieveImage(chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer,
+                     const base::android::JavaRef<jobject>& jcallback,
+                     const std::vector<uint8_t>& thumbnail_data,
+                     const gfx::Size& original_size) {
+  base::android::RunCallbackAndroid(jcallback, thumbnail_data);
+}
+
+}  // namespace
+
 ContextMenuHelper::ContextMenuHelper(content::WebContents* web_contents)
-    : web_contents_(web_contents), weak_factory_(this) {
+    : web_contents_(web_contents) {
   JNIEnv* env = base::android::AttachCurrentThread();
   java_obj_.Reset(
       env,
@@ -120,6 +128,12 @@
   return jmenu_info;
 }
 
+base::android::ScopedJavaLocalRef<jobject>
+ContextMenuHelper::GetJavaWebContents(JNIEnv* env,
+                                      const JavaParamRef<jobject>& obj) {
+  return web_contents_->GetJavaWebContents();
+}
+
 void ContextMenuHelper::OnStartDownload(
     JNIEnv* env,
     const JavaParamRef<jobject>& obj,
@@ -147,8 +161,10 @@
       render_frame_host, context_menu_params_.src_url);
 }
 
-void ContextMenuHelper::ShareImage(JNIEnv* env,
-                                   const JavaParamRef<jobject>& obj) {
+void ContextMenuHelper::RetrieveImage(JNIEnv* env,
+                                      const JavaParamRef<jobject>& obj,
+                                      const JavaParamRef<jobject>& jcallback,
+                                      jint max_dimen_px) {
   content::RenderFrameHost* render_frame_host =
       content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
   if (!render_frame_host)
@@ -160,78 +176,9 @@
   // there's either a connection error or a response.
   auto* thumbnail_capturer_proxy = thumbnail_capturer.get();
   thumbnail_capturer_proxy->RequestThumbnailForContextNode(
-      0, gfx::Size(kShareImageMaxWidth, kShareImageMaxHeight),
-      base::Bind(&ContextMenuHelper::OnShareImage, weak_factory_.GetWeakPtr(),
-                 base::Passed(&thumbnail_capturer)));
-}
-
-void ContextMenuHelper::OnShareImage(
-    chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer,
-    const std::vector<uint8_t>& thumbnail_data,
-    const gfx::Size& original_size) {
-  content::ContentViewCore* content_view_core =
-      content::ContentViewCore::FromWebContents(web_contents_);
-  if (!content_view_core)
-    return;
-
-  base::android::ScopedJavaLocalRef<jobject> jwindow_android(
-      content_view_core->GetWindowAndroid()->GetJavaObject());
-
-  if (jwindow_android.is_null())
-    return;
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jbyteArray> j_bytes =
-      base::android::ToJavaByteArray(env, thumbnail_data);
-
-  Java_ContextMenuHelper_onShareImageReceived(env, java_obj_, jwindow_android,
-                                              j_bytes);
-}
-
-// TODO(tedchoc): Unify RetrieveHeaderThumbnail and ShareImage.
-void ContextMenuHelper::RetrieveHeaderThumbnail(
-    JNIEnv* env,
-    const JavaParamRef<jobject>& obj,
-    jint j_max_size_px) {
-  content::RenderFrameHost* render_frame_host =
-      content::RenderFrameHost::FromID(render_process_id_, render_frame_id_);
-
-  if (!render_frame_host)
-    return;
-
-  chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer;
-  render_frame_host->GetRemoteInterfaces()->GetInterface(&thumbnail_capturer);
-  // Bind the InterfacePtr into the callback so that it's kept alive until
-  // there's either a connection error or a response.
-  auto* thumbnail_capturer_proxy = thumbnail_capturer.get();
-  thumbnail_capturer_proxy->RequestThumbnailForContextNode(
-      0, gfx::Size(j_max_size_px, j_max_size_px),
-      base::Bind(&ContextMenuHelper::OnHeaderThumbnailReceived,
-                 weak_factory_.GetWeakPtr(),
-                 base::Passed(&thumbnail_capturer)));
-}
-
-void ContextMenuHelper::OnHeaderThumbnailReceived(
-    chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer,
-    const std::vector<uint8_t>& thumbnail_data,
-    const gfx::Size& original_size) {
-  content::ContentViewCore* content_view_core =
-      content::ContentViewCore::FromWebContents(web_contents_);
-  if (!content_view_core)
-    return;
-
-  base::android::ScopedJavaLocalRef<jobject> jwindow_android(
-      content_view_core->GetWindowAndroid()->GetJavaObject());
-
-  if (jwindow_android.is_null())
-    return;
-
-  JNIEnv* env = base::android::AttachCurrentThread();
-  base::android::ScopedJavaLocalRef<jbyteArray> j_bytes =
-      base::android::ToJavaByteArray(env, thumbnail_data);
-
-  Java_ContextMenuHelper_onHeaderThumbnailReceived(env, java_obj_,
-                                                   jwindow_android, j_bytes);
+      0, gfx::Size(max_dimen_px, max_dimen_px),
+      base::Bind(&OnRetrieveImage, base::Passed(&thumbnail_capturer),
+                 base::android::ScopedJavaGlobalRef<jobject>(env, jcallback)));
 }
 
 bool RegisterContextMenuHelper(JNIEnv* env) {
diff --git a/chrome/browser/ui/android/context_menu_helper.h b/chrome/browser/ui/android/context_menu_helper.h
index 70c15b3..040e5bd 100644
--- a/chrome/browser/ui/android/context_menu_helper.h
+++ b/chrome/browser/ui/android/context_menu_helper.h
@@ -10,12 +10,10 @@
 #include <vector>
 
 #include "base/android/jni_android.h"
-#include "base/callback.h"
+#include "base/android/scoped_java_ref.h"
 #include "base/macros.h"
-#include "chrome/common/thumbnail_capturer.mojom.h"
 #include "content/public/browser/web_contents_user_data.h"
 #include "content/public/common/context_menu_params.h"
-#include "ui/gfx/geometry/size.h"
 
 namespace content {
 struct ContextMenuParams;
@@ -37,17 +35,19 @@
   void SetPopulator(jobject jpopulator);
 
   // Methods called from Java via JNI ------------------------------------------
+  base::android::ScopedJavaLocalRef<jobject> GetJavaWebContents(
+      JNIEnv* env,
+      const base::android::JavaParamRef<jobject>& obj);
   void OnStartDownload(JNIEnv* env,
                        const base::android::JavaParamRef<jobject>& obj,
                        jboolean jis_link,
                        jboolean jis_data_reduction_proxy_enabled);
+  void RetrieveImage(JNIEnv* env,
+                     const base::android::JavaParamRef<jobject>& obj,
+                     const base::android::JavaParamRef<jobject>& jcallback,
+                     jint max_dimen_px);
   void SearchForImage(JNIEnv* env,
                       const base::android::JavaParamRef<jobject>& obj);
-  void ShareImage(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj);
-
-  void RetrieveHeaderThumbnail(JNIEnv* env,
-                               const base::android::JavaParamRef<jobject>& obj,
-                               jint j_max_size_px);
 
  private:
   explicit ContextMenuHelper(content::WebContents* web_contents);
@@ -56,15 +56,6 @@
   static base::android::ScopedJavaLocalRef<jobject> CreateJavaContextMenuParams(
       const content::ContextMenuParams& params);
 
-  void OnShareImage(chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer,
-                    const std::vector<uint8_t>& thumbnail_data,
-                    const gfx::Size& original_size);
-
-  void OnHeaderThumbnailReceived(
-      chrome::mojom::ThumbnailCapturerPtr thumbnail_capturer,
-      const std::vector<uint8_t>& thumbnail_data,
-      const gfx::Size& original_size);
-
   base::android::ScopedJavaGlobalRef<jobject> java_obj_;
   content::WebContents* web_contents_;
 
@@ -72,8 +63,6 @@
   int render_frame_id_;
   int render_process_id_;
 
-  base::WeakPtrFactory<ContextMenuHelper> weak_factory_;
-
   DISALLOW_COPY_AND_ASSIGN(ContextMenuHelper);
 };
 
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 210e48a..d189ae6 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -221,6 +221,26 @@
     return AccessibilityManager::Get()->IsFocusHighlightEnabled();
   }
 
+  void SetStickyKeysEnabled(bool enabled) override {
+    DCHECK(AccessibilityManager::Get());
+    return AccessibilityManager::Get()->EnableStickyKeys(enabled);
+  }
+
+  bool IsStickyKeysEnabled() const override {
+    DCHECK(AccessibilityManager::Get());
+    return AccessibilityManager::Get()->IsStickyKeysEnabled();
+  }
+
+  void SetTapDraggingEnabled(bool enabled) override {
+    DCHECK(AccessibilityManager::Get());
+    return AccessibilityManager::Get()->EnableTapDragging(enabled);
+  }
+
+  bool IsTapDraggingEnabled() const override {
+    DCHECK(AccessibilityManager::Get());
+    return AccessibilityManager::Get()->IsTapDraggingEnabled();
+  }
+
   void SetSelectToSpeakEnabled(bool enabled) override {
     DCHECK(AccessibilityManager::Get());
     AccessibilityManager::Get()->SetSelectToSpeakEnabled(enabled);
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
index 1984510f..cc29bfd 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_impl_unittest.cc
@@ -277,48 +277,6 @@
   DISALLOW_COPY_AND_ASSIGN(TestV2AppLauncherItemController);
 };
 
-// Proxies to ShelfDelegate invocation to the given
-// ChromeLauncherControllerImpl instance. Because of ownership management,
-// ChromeLauncherControllerImpl instance cannot be injected to WmShell.
-// This wraps the instance, so that it can be injected.
-class ProxyShelfDelegate : public ash::ShelfDelegate {
- public:
-  explicit ProxyShelfDelegate(ChromeLauncherControllerImpl* controller)
-      : controller_(controller) {}
-  ~ProxyShelfDelegate() override = default;
-
-  ash::ShelfID GetShelfIDForAppID(const std::string& app_id) override {
-    return controller_->GetShelfIDForAppID(app_id);
-  };
-
-  ash::ShelfID GetShelfIDForAppIDAndLaunchID(
-      const std::string& app_id,
-      const std::string& launch_id) override {
-    return controller_->GetShelfIDForAppIDAndLaunchID(app_id, launch_id);
-  }
-
-  const std::string& GetAppIDForShelfID(ash::ShelfID id) override {
-    return controller_->GetAppIDForShelfID(id);
-  }
-
-  void PinAppWithID(const std::string& app_id) override {
-    return controller_->PinAppWithID(app_id);
-  }
-
-  bool IsAppPinned(const std::string& app_id) override {
-    return controller_->IsAppPinned(app_id);
-  }
-
-  void UnpinAppWithID(const std::string& app_id) override {
-    return controller_->UnpinAppWithID(app_id);
-  }
-
- private:
-  ChromeLauncherControllerImpl* const controller_;
-
-  DISALLOW_COPY_AND_ASSIGN(ProxyShelfDelegate);
-};
-
 // A callback that does nothing after shelf item selection handling.
 void NoopCallback(ash::ShelfAction action, base::Optional<ash::MenuItemList>) {}
 
@@ -508,7 +466,7 @@
     arc_test_.TearDown();
     model_->RemoveObserver(model_observer_.get());
     model_observer_.reset();
-    launcher_controller_.reset();
+    launcher_controller_ = nullptr;
     BrowserWithTestWindowTest::TearDown();
   }
 
@@ -530,34 +488,47 @@
     model_->Add(app_list);
   }
 
-  void InitLauncherController() {
-    launcher_controller_.reset(
-        new ChromeLauncherControllerImpl(profile(), model_));
-    launcher_controller_->Init();
+  // Create a launcher controller instance and register it as the ShelfDelegate.
+  // Returns a pointer to the uninitialized controller, which is owned by Shell.
+  ChromeLauncherControllerImpl* CreateLauncherController() {
+    // Shell owns ChromeLauncherController as its ShelfDelegate. The lifetime
+    // of this instance should match production behavior as closely as possible.
+    DCHECK(!ChromeLauncherController::instance());
+    std::unique_ptr<ChromeLauncherControllerImpl> launcher_controller =
+        base::MakeUnique<ChromeLauncherControllerImpl>(profile(), model_);
+    launcher_controller_ = launcher_controller.get();
+    ash::test::ShellTestApi().SetShelfDelegate(std::move(launcher_controller));
+    return launcher_controller_;
   }
 
+  // Create and initialize the controller.
+  // Returns a pointer to the initialized controller, which is owned by Shell.
+  void InitLauncherController() { CreateLauncherController()->Init(); }
+
+  // Create and initialize the controller; create a tab and show the browser.
   void InitLauncherControllerWithBrowser() {
     InitLauncherController();
     chrome::NewTab(browser());
     browser()->window()->Show();
   }
 
-  void RecreateChromeLauncher() {
-    // Destroy controller first if it exists.
-    launcher_controller_.reset();
+  // Destroy Shell's controller instance and clear the local pointer.
+  void ResetLauncherController() {
+    launcher_controller_ = nullptr;
+    ash::test::ShellTestApi().SetShelfDelegate(nullptr);
+  }
+
+  // Destroy and recreate the controller; clear and reinitialize the ShelfModel.
+  // Returns a pointer to the uninitialized controller, which is owned by Shell.
+  // TODO(msw): This does not accurately represent ChromeLauncherControllerImpl
+  // lifetime or usage in production, and does not accurately simulate restarts.
+  ChromeLauncherControllerImpl* RecreateLauncherController() {
+    // Destroy any existing controller first; only one may exist at a time.
+    ResetLauncherController();
     while (model_->item_count() > 0)
       model_->RemoveItemAt(0);
     AddAppListLauncherItem();
-    launcher_controller_ =
-        base::MakeUnique<ChromeLauncherControllerImpl>(profile(), model_);
-    launcher_controller_->Init();
-  }
-
-  // This needs to be called after InitLaunchController(), or its family.
-  // It is not supported to recreate the instance.
-  void SetShelfDelegate() {
-    ash::test::ShellTestApi().SetShelfDelegate(
-        base::MakeUnique<ProxyShelfDelegate>(launcher_controller_.get()));
+    return CreateLauncherController();
   }
 
   void StartAppSyncService(const syncer::SyncDataList& init_sync_list) {
@@ -975,7 +946,7 @@
 
   ArcAppTest arc_test_;
   bool auto_start_arc_test_ = false;
-  std::unique_ptr<ChromeLauncherControllerImpl> launcher_controller_;
+  ChromeLauncherControllerImpl* launcher_controller_ = nullptr;
   std::unique_ptr<TestShelfModelObserver> model_observer_;
   ash::ShelfModel* model_ = nullptr;
   std::unique_ptr<TestingProfileManager> profile_manager_;
@@ -1345,14 +1316,14 @@
   syncer::SyncDataList copy_sync_list =
       app_service_->GetAllSyncData(syncer::APP_LIST);
 
-  launcher_controller_.reset();
+  ResetLauncherController();
   SendPinChanges(syncer::SyncChangeList(), true);
   StopAppSyncService();
   EXPECT_EQ(0U, app_service_->sync_items().size());
 
   // Move to ARC enabled platform, restart syncing with stored data.
   StartAppSyncService(copy_sync_list);
-  RecreateChromeLauncher();
+  RecreateLauncherController()->Init();
 
   // Pins must be automatically updated.
   SendListOfArcApps();
@@ -1376,7 +1347,7 @@
 
   copy_sync_list = app_service_->GetAllSyncData(syncer::APP_LIST);
 
-  launcher_controller_.reset();
+  ResetLauncherController();
   ResetPinModel();
 
   SendPinChanges(syncer::SyncChangeList(), true);
@@ -1389,7 +1360,7 @@
     return;
   EnablePlayStore(false);
   StartAppSyncService(copy_sync_list);
-  RecreateChromeLauncher();
+  RecreateLauncherController()->Init();
 
   EXPECT_TRUE(launcher_controller_->IsAppPinned(extension1_->id()));
   EXPECT_FALSE(launcher_controller_->IsAppPinned(arc_app_id1));
@@ -1856,7 +1827,7 @@
 }
 
 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcDeferredLaunch) {
-  RecreateChromeLauncher();
+  InitLauncherController();
 
   const arc::mojom::AppInfo& app1 = arc_test_.fake_apps()[0];
   const arc::mojom::AppInfo& app2 = arc_test_.fake_apps()[1];
@@ -1946,7 +1917,7 @@
 // Ensure the deferred controller does not override the active app controller
 // (crbug.com/701152).
 TEST_P(ChromeLauncherControllerImplWithArcTest, ArcDeferredLaunchForActiveApp) {
-  RecreateChromeLauncher();
+  InitLauncherController();
   SendListOfArcApps();
   arc_test_.StopArcInstance();
 
@@ -2725,7 +2696,7 @@
 
   std::vector<std::string> expected_launchers;
   std::vector<std::string> actual_launchers;
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Unavailable extensions don't create launcher items.
@@ -2737,14 +2708,14 @@
 
   expected_launchers.push_back(extension2_->id());
   expected_launchers.push_back(extension4_->id());
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertAddPinChange(&sync_list, 2, extension3_->id());
   SendPinChanges(sync_list, false);
   expected_launchers.insert(expected_launchers.begin() + 1, extension3_->id());
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
@@ -2753,21 +2724,21 @@
   InsertUpdatePinChange(&sync_list, 2, extension2_->id());
   SendPinChanges(sync_list, false);
   std::reverse(expected_launchers.begin(), expected_launchers.end());
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Sending legacy sync change without pin info should not affect pin model.
   sync_list.clear();
   InsertLegacyPinChange(&sync_list, extension4_->id());
   SendPinChanges(sync_list, false);
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
   InsertRemovePinChange(&sync_list, extension4_->id());
   SendPinChanges(sync_list, false);
   expected_launchers.erase(expected_launchers.begin());
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   sync_list.clear();
@@ -2775,7 +2746,7 @@
   InsertRemovePinChange(&sync_list, extension2_->id());
   SendPinChanges(sync_list, false);
   expected_launchers.clear();
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 }
 
@@ -2820,7 +2791,7 @@
   EXPECT_EQ("AppList, Chrome, App4, App2, App5", GetPinnedAppStatus());
 
   // Next Chrome start should preserve pins.
-  RecreateChromeLauncher();
+  RecreateLauncherController()->Init();
   StopPrefSyncService();
   StartPrefSyncService(syncer::SyncDataList());
   EXPECT_EQ("AppList, Chrome, App4, App2, App5", GetPinnedAppStatus());
@@ -2843,13 +2814,13 @@
   expected_launchers.push_back(extension3_->id());
   std::vector<std::string> actual_launchers;
 
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 
   // Install |extension2| and verify it shows up between the other two.
   extension_service_->AddExtension(extension2_.get());
   expected_launchers.insert(expected_launchers.begin() + 1, extension2_->id());
-  GetAppLaunchers(launcher_controller_.get(), &actual_launchers);
+  GetAppLaunchers(launcher_controller_, &actual_launchers);
   EXPECT_EQ(expected_launchers, actual_launchers);
 }
 
@@ -2876,7 +2847,7 @@
   item_browser.type = ash::TYPE_BROWSER_SHORTCUT;
   item_browser.id =
       launcher_controller_->GetShelfIDForAppID(extension_misc::kChromeAppId);
-  CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_browser, 0, nullptr);
 
   // Now make the created browser() visible by showing its browser window.
   browser()->window()->Show();
@@ -2884,7 +2855,7 @@
   NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1);
   base::string16 one_menu_item[] = { title1 };
 
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item);
+  CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item);
 
   // Create one more browser/window and check that one more was added.
   std::unique_ptr<Browser> browser2(
@@ -2898,7 +2869,7 @@
   // Check that the list contains now two entries - make furthermore sure that
   // the active item is the first entry.
   base::string16 two_menu_items[] = {title1, title2};
-  CheckAppMenu(launcher_controller_.get(), item_browser, 2, two_menu_items);
+  CheckAppMenu(launcher_controller_, item_browser, 2, two_menu_items);
 
   // Apparently we have to close all tabs we have.
   chrome::CloseTab(browser2.get());
@@ -2917,14 +2888,14 @@
 
   // Check that the menu is empty.
   chrome::NewTab(browser());
-  CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_browser, 0, nullptr);
 
   // Show the created |browser()| by showing its window.
   browser()->window()->Show();
   base::string16 title1 = ASCIIToUTF16("Test1");
   NavigateAndCommitActiveTabWithTitle(browser(), GURL("http://test1"), title1);
   base::string16 one_menu_item1[] = { title1 };
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item1);
+  CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item1);
 
   // Create a browser for another user and check that it is not included in the
   // users running browser list.
@@ -2935,17 +2906,17 @@
   std::unique_ptr<Browser> browser2(
       CreateBrowserAndTabWithProfile(profile2, user2, "http://test2"));
   base::string16 one_menu_item2[] = { ASCIIToUTF16(user2) };
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item1);
+  CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item1);
 
   // Switch to the other user and make sure that only that browser window gets
   // shown.
   SwitchActiveUser(account_id2);
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item2);
+  CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item2);
 
   // Transferred browsers of other users should not show up in the list.
   chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser(
       browser()->window()->GetNativeWindow(), account_id2);
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, one_menu_item2);
+  CheckAppMenu(launcher_controller_, item_browser, 1, one_menu_item2);
 
   chrome::CloseTab(browser2.get());
 }
@@ -2982,14 +2953,14 @@
   ash::ShelfItem item_gmail;
   item_gmail.type = ash::TYPE_PINNED_APP;
   item_gmail.id = gmail_id;
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr);
 
   // Set the gmail URL to a new tab.
   base::string16 title1 = ASCIIToUTF16("Test1");
   NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1);
 
   base::string16 one_menu_item[] = { title1 };
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 1, one_menu_item);
+  CheckAppMenu(launcher_controller_, item_gmail, 1, one_menu_item);
 
   // Create one empty tab.
   chrome::NewTab(browser());
@@ -3004,20 +2975,20 @@
   base::string16 title3 = ASCIIToUTF16("Test3");
   NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title3);
   base::string16 two_menu_items[] = {title1, title3};
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
+  CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items);
 
   // Even though the item is in the V1 app list, it should also be in the
   // browser list.
   base::string16 browser_menu_item[] = {title3};
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, browser_menu_item);
+  CheckAppMenu(launcher_controller_, item_browser, 1, browser_menu_item);
 
   // Test that closing of (all) the item(s) does work (and all menus get
   // updated properly).
   launcher_controller_->Close(item_gmail.id);
 
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr);
   base::string16 browser_menu_item2[] = { title2 };
-  CheckAppMenu(launcher_controller_.get(), item_browser, 1, browser_menu_item2);
+  CheckAppMenu(launcher_controller_, item_browser, 1, browser_menu_item2);
 }
 
 // Check the multi profile case where only user related apps should show up.
@@ -3045,14 +3016,14 @@
   ash::ShelfItem item_gmail;
   item_gmail.type = ash::TYPE_PINNED_APP;
   item_gmail.id = gmail_id;
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr);
 
   // Set the gmail URL to a new tab.
   base::string16 title1 = ASCIIToUTF16("Test1");
   NavigateAndCommitActiveTabWithTitle(browser(), GURL(gmail_url), title1);
 
   base::string16 one_menu_item[] = { title1 };
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 1, one_menu_item);
+  CheckAppMenu(launcher_controller_, item_gmail, 1, one_menu_item);
 
   // Create a second profile and switch to that user.
   std::string user2 = "user2";
@@ -3062,15 +3033,15 @@
   SwitchActiveUser(account_id2);
 
   // No item should have content yet.
-  CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_browser, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr);
 
   // Transfer the browser of the first user - it should still not show up.
   chrome::MultiUserWindowManager::GetInstance()->ShowWindowForUser(
       browser()->window()->GetNativeWindow(), account_id2);
 
-  CheckAppMenu(launcher_controller_.get(), item_browser, 0, nullptr);
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_browser, 0, nullptr);
+  CheckAppMenu(launcher_controller_, item_gmail, 0, nullptr);
 }
 
 // Check that V2 applications are creating items properly in the launcher when
@@ -3308,7 +3279,7 @@
   item_gmail.type = ash::TYPE_PINNED_APP;
   item_gmail.id = gmail_id;
   base::string16 two_menu_items[] = {title1, title2};
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
+  CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items);
   ash::ShelfItemDelegate* item_delegate =
       launcher_controller_->GetShelfItemDelegate(gmail_id);
   ASSERT_TRUE(item_delegate);
@@ -3356,7 +3327,7 @@
   item_gmail.type = ash::TYPE_PINNED_APP;
   item_gmail.id = gmail_id;
   base::string16 two_menu_items[] = {title1, title2};
-  CheckAppMenu(launcher_controller_.get(), item_gmail, 2, two_menu_items);
+  CheckAppMenu(launcher_controller_, item_gmail, 2, two_menu_items);
 
   ash::ShelfItemDelegate* item_delegate =
       launcher_controller_->GetShelfItemDelegate(gmail_id);
@@ -3516,13 +3487,7 @@
   EXPECT_EQ(ash::TYPE_PINNED_APP, model_->items()[2].type);
   EXPECT_EQ(ash::TYPE_BROWSER_SHORTCUT, model_->items()[3].type);
 
-  launcher_controller_.reset();
-  while (!model_->items().empty())
-    model_->RemoveItemAt(0);
-
-  AddAppListLauncherItem();
-  launcher_controller_ =
-      base::MakeUnique<ChromeLauncherControllerImpl>(profile(), model_);
+  RecreateLauncherController();
   helper = new TestLauncherControllerHelper(profile());
   helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
   helper->SetAppID(tab_strip_model->GetWebContentsAt(1), "2");
@@ -3563,13 +3528,7 @@
   EXPECT_FALSE(launcher_controller_->IsAppPinned("0"));
   EXPECT_EQ(initial_size + 1, model_->items().size());
 
-  launcher_controller_.reset();
-  while (!model_->items().empty())
-    model_->RemoveItemAt(0);
-
-  AddAppListLauncherItem();
-  launcher_controller_ =
-      base::MakeUnique<ChromeLauncherControllerImpl>(profile(), model_);
+  RecreateLauncherController();
   helper = new TestLauncherControllerHelper(profile());
   helper->SetAppID(tab_strip_model->GetWebContentsAt(0), "1");
   SetLauncherControllerHelper(helper);
@@ -3684,10 +3643,6 @@
   // Initially pins are imported from legacy pref based model.
   StartPrefSyncService(syncer::SyncDataList());
 
-  // Inject |launcher_controller_| as ShelfDelegate to verify the behavior
-  // of removing pinned icon in ArcSessionManager::OnOptInPreferenceChanged().
-  SetShelfDelegate();
-
   // Initial run, ARC is not managed and disabled, Play Store pin should be
   // available.
   ValidateArcState(false, false, arc::ArcSessionManager::State::STOPPED,
@@ -4048,7 +4003,9 @@
 TEST_P(ChromeLauncherControllerArcDefaultAppsTest, DefaultApps) {
   arc_test_.SetUp(profile());
   InitLauncherController();
-  ChromeLauncherController::set_instance_for_test(launcher_controller_.get());
+  // TODO(crbug.com/709297): Fix this workaround to prevent a TearDown crash.
+  std::vector<std::unique_ptr<AppIconLoader>> no_loaders;
+  launcher_controller_->SetAppIconLoadersForTest(no_loaders);
 
   ArcAppListPrefs* const prefs = arc_test_.arc_app_list_prefs();
   EnablePlayStore(false);
@@ -4184,7 +4141,7 @@
       app_service_->GetAllSyncData(syncer::APP_LIST);
 
   app_service_->StopSyncing(syncer::APP_LIST);
-  RecreateChromeLauncher();
+  RecreateLauncherController()->Init();
 
   // Pinned state should not change.
   EXPECT_EQ("AppList, Chrome, App1, App2", GetPinnedAppStatus());
diff --git a/chrome/browser/ui/browser_command_controller.cc b/chrome/browser/ui/browser_command_controller.cc
index 38d517a..ae5774e2 100644
--- a/chrome/browser/ui/browser_command_controller.cc
+++ b/chrome/browser/ui/browser_command_controller.cc
@@ -187,8 +187,27 @@
   }
 #endif
 
-  if (window()->IsFullscreen() && command_id == IDC_FULLSCREEN)
-    return true;
+  if (window()->IsFullscreen()) {
+    // In fullscreen, all commands except for IDC_FULLSCREEN and IDC_EXIT should
+    // be delivered to the web page. The intent to implement and ship can be
+    // found in http://crbug.com/680809.
+    const bool is_exit_fullscreen =
+        (command_id == IDC_EXIT || command_id == IDC_FULLSCREEN);
+#if defined(OS_MACOSX)
+    // This behavior is different on Mac OS, which has a unique user-initiated
+    // full-screen mode. According to the discussion in http://crbug.com/702251,
+    // the commands should be reserved for browser-side handling if the browser
+    // window's toolbar is visible.
+    if (window()->IsToolbarShowing()) {
+      if (command_id == IDC_FULLSCREEN)
+        return true;
+    } else {
+      return is_exit_fullscreen;
+    }
+#else
+    return is_exit_fullscreen;
+#endif
+  }
 
 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
   // If this key was registered by the user as a content editing hotkey, then
diff --git a/chrome/browser/ui/browser_command_controller_unittest.cc b/chrome/browser/ui/browser_command_controller_unittest.cc
index 8fa33d20..5dd6fc5 100644
--- a/chrome/browser/ui/browser_command_controller_unittest.cc
+++ b/chrome/browser/ui/browser_command_controller_unittest.cc
@@ -252,7 +252,9 @@
  public:
   FullscreenTestBrowserWindow(
       BrowserCommandControllerFullscreenTest* test_browser)
-      : fullscreen_(false), test_browser_(test_browser) {}
+      : fullscreen_(false),
+        toolbar_showing_(false),
+        test_browser_(test_browser) {}
 
   ~FullscreenTestBrowserWindow() override {}
 
@@ -264,6 +266,7 @@
     fullscreen_ = true;
   }
   void ExitFullscreen() override { fullscreen_ = false; }
+  bool IsToolbarShowing() const override { return toolbar_showing_; }
 
   ExclusiveAccessContext* GetExclusiveAccessContext() override { return this; }
 
@@ -277,8 +280,11 @@
       ExclusiveAccessBubbleType bubble_type) override {}
   void OnExclusiveAccessUserInput() override {}
 
+  void set_toolbar_showing(bool showing) { toolbar_showing_ = showing; }
+
  private:
   bool fullscreen_;
+  bool toolbar_showing_;
   BrowserCommandControllerFullscreenTest* test_browser_;
 
   DISALLOW_COPY_AND_ASSIGN(FullscreenTestBrowserWindow);
@@ -312,129 +318,115 @@
 
 TEST_F(BrowserCommandControllerFullscreenTest,
        UpdateCommandsForFullscreenMode) {
-  // Defaults for a tabbed browser.
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_SHOW_AS_TAB));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_TOOLBAR));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_LOCATION));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_SEARCH));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_MENU_BAR));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_NEXT_PANE));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_PREVIOUS_PANE));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_BOOKMARKS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEVELOPER_MENU));
+  struct {
+    int command_id;
+    // Whether the command is enabled in tab mode.
+    bool enabled_in_tab;
+    // Whether the keyboard shortcut is reserved in tab mode.
+    bool reserved_in_tab;
+    // Whether the command is enabled in fullscreen mode.
+    bool enabled_in_fullscreen;
+    // Whether the keyboard shortcut is reserved in fullscreen mode.
+    bool reserved_in_fullscreen;
+  } commands[] = {
+    // 1. Most commands are disabled in fullscreen.
+    // 2. In fullscreen, only the exit fullscreen commands are reserved. All
+    // other shortcuts should be delivered to the web page. See
+    // http://crbug.com/680809.
+
+    //         Command ID        |      tab mode      |      fullscreen     |
+    //                           | enabled | reserved | enabled  | reserved |
+    { IDC_OPEN_CURRENT_URL,        true,     false,     false,     false    },
+    { IDC_FOCUS_TOOLBAR,           true,     false,     false,     false    },
+    { IDC_FOCUS_LOCATION,          true,     false,     false,     false    },
+    { IDC_FOCUS_SEARCH,            true,     false,     false,     false    },
+    { IDC_FOCUS_MENU_BAR,          true,     false,     false,     false    },
+    { IDC_FOCUS_NEXT_PANE,         true,     false,     false,     false    },
+    { IDC_FOCUS_PREVIOUS_PANE,     true,     false,     false,     false    },
+    { IDC_FOCUS_BOOKMARKS,         true,     false,     false,     false    },
+    { IDC_DEVELOPER_MENU,          true,     false,     false,     false    },
 #if defined(GOOGLE_CHROME_BUILD)
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FEEDBACK));
+    { IDC_FEEDBACK,                true,     false,     false,     false    },
 #endif
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPTIONS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_IMPORT_SETTINGS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_EDIT_SEARCH_ENGINES));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_VIEW_PASSWORDS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_SHOW_APP_MENU));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FULLSCREEN));
+    { IDC_OPTIONS,                 true,     false,     false,     false    },
+    { IDC_IMPORT_SETTINGS,         true,     false,     false,     false    },
+    { IDC_EDIT_SEARCH_ENGINES,     true,     false,     false,     false    },
+    { IDC_VIEW_PASSWORDS,          true,     false,     false,     false    },
+    { IDC_ABOUT,                   true,     false,     false,     false    },
+    { IDC_SHOW_APP_MENU,           true,     false,     false,     false    },
+    { IDC_FULLSCREEN,              true,     false,     true,      true     },
+    { IDC_CLOSE_TAB,               true,     true,      true,      false    },
+    { IDC_CLOSE_WINDOW,            true,     true,      true,      false    },
+    { IDC_NEW_INCOGNITO_WINDOW,    true,     true,      true,      false    },
+    { IDC_NEW_TAB,                 true,     true,      true,      false    },
+    { IDC_NEW_WINDOW,              true,     true,      true,      false    },
+    { IDC_SELECT_NEXT_TAB,         true,     true,      true,      false    },
+    { IDC_SELECT_PREVIOUS_TAB,     true,     true,      true,      false    },
+    { IDC_EXIT,                    true,     true,      true,      true     },
+    { IDC_SHOW_AS_TAB,             false,    false,     false,     false    },
+  };
+  const content::NativeWebKeyboardEvent key_event(
+      blink::WebInputEvent::TypeFirst, 0, 0);
+  // Defaults for a tabbed browser.
+  for (size_t i = 0; i < arraysize(commands); i++) {
+    SCOPED_TRACE(commands[i].command_id);
+    EXPECT_EQ(chrome::IsCommandEnabled(browser(), commands[i].command_id),
+              commands[i].enabled_in_tab);
+    EXPECT_EQ(browser()->command_controller()->IsReservedCommandOrKey(
+                  commands[i].command_id, key_event),
+              commands[i].reserved_in_tab);
+  }
 
   // Simulate going fullscreen.
   chrome::ToggleFullscreenMode(browser());
   ASSERT_TRUE(browser()->window()->IsFullscreen());
   browser()->command_controller()->FullscreenStateChanged();
 
-  // Most commands are disabled in fullscreen.
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_SHOW_AS_TAB));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_TOOLBAR));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_LOCATION));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_SEARCH));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_MENU_BAR));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_NEXT_PANE));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_PREVIOUS_PANE));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_BOOKMARKS));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_DEVELOPER_MENU));
-#if defined(GOOGLE_CHROME_BUILD)
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_FEEDBACK));
-#endif
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_OPTIONS));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_IMPORT_SETTINGS));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_EDIT_SEARCH_ENGINES));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_VIEW_PASSWORDS));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_ABOUT));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_SHOW_APP_MENU));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FULLSCREEN));
+  // By default, in fullscreen mode, the toolbar should be hidden; and all
+  // platforms behave similarly.
+  EXPECT_FALSE(window()->IsToolbarShowing());
+  for (size_t i = 0; i < arraysize(commands); i++) {
+    SCOPED_TRACE(commands[i].command_id);
+    EXPECT_EQ(chrome::IsCommandEnabled(browser(), commands[i].command_id),
+              commands[i].enabled_in_fullscreen);
+    EXPECT_EQ(browser()->command_controller()->IsReservedCommandOrKey(
+                  commands[i].command_id, key_event),
+              commands[i].reserved_in_fullscreen);
+  }
 
+#if defined(OS_MACOSX)
+  // When the toolbar is showing, commands should be reserved as if the content
+  // were in a tab; IDC_FULLSCREEN should also be reserved.
+  static_cast<FullscreenTestBrowserWindow*>(window())->set_toolbar_showing(
+      true);
   EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_INCOGNITO_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_SELECT_NEXT_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_SELECT_PREVIOUS_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_EXIT,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
+      IDC_FULLSCREEN, key_event));
+  for (size_t i = 0; i < arraysize(commands); i++) {
+    if (commands[i].command_id != IDC_FULLSCREEN) {
+      SCOPED_TRACE(commands[i].command_id);
+      EXPECT_EQ(browser()->command_controller()->IsReservedCommandOrKey(
+                    commands[i].command_id, key_event),
+                commands[i].reserved_in_tab);
+    }
+  }
+  // Return to default state.
+  static_cast<FullscreenTestBrowserWindow*>(window())->set_toolbar_showing(
+      false);
+#endif
 
   // Exit fullscreen.
   chrome::ToggleFullscreenMode(browser());
   ASSERT_FALSE(browser()->window()->IsFullscreen());
   browser()->command_controller()->FullscreenStateChanged();
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPEN_CURRENT_URL));
-  EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_SHOW_AS_TAB));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_TOOLBAR));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_LOCATION));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_SEARCH));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_MENU_BAR));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_NEXT_PANE));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_PREVIOUS_PANE));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FOCUS_BOOKMARKS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_DEVELOPER_MENU));
-#if defined(GOOGLE_CHROME_BUILD)
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FEEDBACK));
-#endif
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_OPTIONS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_IMPORT_SETTINGS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_EDIT_SEARCH_ENGINES));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_VIEW_PASSWORDS));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ABOUT));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_SHOW_APP_MENU));
-  EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_FULLSCREEN));
 
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_CLOSE_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_INCOGNITO_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_NEW_WINDOW,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_SELECT_NEXT_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_SELECT_PREVIOUS_TAB,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
-  EXPECT_TRUE(browser()->command_controller()->IsReservedCommandOrKey(
-      IDC_EXIT,
-      content::NativeWebKeyboardEvent(blink::WebInputEvent::TypeFirst, 0, 0)));
+  for (size_t i = 0; i < arraysize(commands); i++) {
+    SCOPED_TRACE(commands[i].command_id);
+    EXPECT_EQ(chrome::IsCommandEnabled(browser(), commands[i].command_id),
+              commands[i].enabled_in_tab);
+    EXPECT_EQ(browser()->command_controller()->IsReservedCommandOrKey(
+                  commands[i].command_id, key_event),
+              commands[i].reserved_in_tab);
+  }
 
   // Guest Profiles disallow some options.
   TestingProfile* testprofile = browser()->profile()->AsTestingProfile();
diff --git a/chrome/browser/ui/browser_window.h b/chrome/browser/ui/browser_window.h
index 25ded91..d01dd7e1 100644
--- a/chrome/browser/ui/browser_window.h
+++ b/chrome/browser/ui/browser_window.h
@@ -220,9 +220,19 @@
   // Returns whether the tab strip is editable (for extensions).
   virtual bool IsTabStripEditable() const = 0;
 
-  // Returns whether the tool bar is visible or not.
+  // Returns whether the toolbar is available or not. It's called "Visible()"
+  // to follow the name convention. But it does not indicate the visibility of
+  // the toolbar, i.e. toolbar may be hidden, and only visible when the mouse
+  // cursor is at a certain place.
+  // TODO(zijiehe): Rename Visible() functions into Available() to match their
+  // original meaning.
   virtual bool IsToolbarVisible() const = 0;
 
+  // Returns whether the toolbar is showing up on the screen.
+  // TODO(zijiehe): Rename this function into IsToolbarVisible() once other
+  // Visible() functions are renamed to Available().
+  virtual bool IsToolbarShowing() const = 0;
+
   // Shows the Update Recommended dialog box.
   virtual void ShowUpdateChromeDialog() = 0;
 
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.h b/chrome/browser/ui/cocoa/browser_window_cocoa.h
index f92d625..b9df782 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.h
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.h
@@ -102,6 +102,7 @@
   bool IsBookmarkBarAnimating() const override;
   bool IsTabStripEditable() const override;
   bool IsToolbarVisible() const override;
+  bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override;
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
   void ShowBookmarkAppBubble(
diff --git a/chrome/browser/ui/cocoa/browser_window_cocoa.mm b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
index 2838580..219721c 100644
--- a/chrome/browser/ui/cocoa/browser_window_cocoa.mm
+++ b/chrome/browser/ui/cocoa/browser_window_cocoa.mm
@@ -322,6 +322,16 @@
       browser_->GetWindowTitleForCurrentTab(include_app_name));
 }
 
+bool BrowserWindowCocoa::IsToolbarShowing() const {
+  if (!IsFullscreen())
+    return true;
+
+  // TODO(zijiehe): Retrieve the visibility of toolbar from
+  // FullscreenToolbarController. See http://crbug.com/702251 and
+  // http://crbug.com/680809.
+  return true;
+}
+
 void BrowserWindowCocoa::BookmarkBarStateChanged(
     BookmarkBar::AnimateChangeType change_type) {
   [[controller_ bookmarkBarController]
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.h b/chrome/browser/ui/cocoa/browser_window_controller.h
index 5281cbf..cdbe2d1 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.h
+++ b/chrome/browser/ui/cocoa/browser_window_controller.h
@@ -519,9 +519,6 @@
 // |bubbleType|.
 - (void)updateFullscreenExitBubble;
 
-// Returns YES if the browser window is in or entering any fullscreen mode.
-- (BOOL)isInAnyFullscreenMode;
-
 // Returns YES if the browser window is currently in or entering fullscreen via
 // the built-in immersive mechanism.
 - (BOOL)isInImmersiveFullscreen;
diff --git a/chrome/browser/ui/cocoa/browser_window_controller.mm b/chrome/browser/ui/cocoa/browser_window_controller.mm
index 4d7ac82..2fd4f20f 100644
--- a/chrome/browser/ui/cocoa/browser_window_controller.mm
+++ b/chrome/browser/ui/cocoa/browser_window_controller.mm
@@ -1899,10 +1899,6 @@
   return NO;
 }
 
-- (BOOL)isInAnyFullscreenMode {
-  return [self isInImmersiveFullscreen] || [self isInAppKitFullscreen];
-}
-
 - (BOOL)isInImmersiveFullscreen {
   return fullscreenWindow_.get() != nil || enteringImmersiveFullscreen_;
 }
@@ -1914,6 +1910,10 @@
           enteringAppKitFullscreen_);
 }
 
+- (BOOL)isInAnyFullscreenMode {
+  return [self isInImmersiveFullscreen] || [self isInAppKitFullscreen];
+}
+
 - (NSView*)avatarView {
   return [avatarButtonController_ view];
 }
diff --git a/chrome/browser/ui/cocoa/browser_window_touch_bar.mm b/chrome/browser/ui/cocoa/browser_window_touch_bar.mm
index 06d7c2e1..a840011 100644
--- a/chrome/browser/ui/cocoa/browser_window_touch_bar.mm
+++ b/chrome/browser/ui/cocoa/browser_window_touch_bar.mm
@@ -25,8 +25,10 @@
 #include "components/omnibox/browser/vector_icons.h"
 #include "components/prefs/pref_member.h"
 #include "components/search_engines/util.h"
+#include "components/strings/grit/components_strings.h"
 #include "components/toolbar/vector_icons.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/base/l10n/l10n_util_mac.h"
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/color_utils.h"
 #include "ui/gfx/image/image.h"
@@ -89,12 +91,14 @@
 NSButton* CreateTouchBarButton(const gfx::VectorIcon& icon,
                                BrowserWindowTouchBar* owner,
                                int command,
+                               int tooltip_id,
                                SkColor color = kTouchBarDefaultIconColor) {
   NSButton* button =
       [NSButton buttonWithImage:CreateNSImageFromIcon(icon, color)
                          target:owner
                          action:@selector(executeCommand:)];
   button.tag = command;
+  [button setAccessibilityLabel:l10n_util::GetNSString(tooltip_id)];
   return button;
 }
 
@@ -234,21 +238,25 @@
   } else if ([identifier isEqualTo:kReloadOrStopTouchId]) {
     const gfx::VectorIcon& icon =
         isPageLoading_ ? kNavigateStopIcon : kNavigateReloadIcon;
-    int command_id = isPageLoading_ ? IDC_STOP : IDC_RELOAD;
-    [touchBarItem setView:CreateTouchBarButton(icon, self, command_id)];
-  } else if ([identifier isEqualTo:kHomeTouchId]) {
+    int commandId = isPageLoading_ ? IDC_STOP : IDC_RELOAD;
+    int tooltipId = isPageLoading_ ? IDS_TOOLTIP_STOP : IDS_TOOLTIP_RELOAD;
     [touchBarItem
-        setView:CreateTouchBarButton(kNavigateHomeIcon, self, IDC_HOME)];
+        setView:CreateTouchBarButton(icon, self, commandId, tooltipId)];
+  } else if ([identifier isEqualTo:kHomeTouchId]) {
+    [touchBarItem setView:CreateTouchBarButton(kNavigateHomeIcon, self,
+                                               IDC_HOME, IDS_TOOLTIP_HOME)];
   } else if ([identifier isEqualTo:kNewTabTouchId]) {
-    [touchBarItem setView:CreateTouchBarButton(kNewTabMacTouchbarIcon, self,
-                                               IDC_NEW_TAB)];
+    [touchBarItem
+        setView:CreateTouchBarButton(kNewTabMacTouchbarIcon, self, IDC_NEW_TAB,
+                                     IDS_TOOLTIP_NEW_TAB)];
   } else if ([identifier isEqualTo:kStarTouchId]) {
     const gfx::VectorIcon& icon =
         isStarred_ ? toolbar::kStarActiveIcon : toolbar::kStarIcon;
     SkColor iconColor =
         isStarred_ ? kTouchBarStarActiveColor : kTouchBarDefaultIconColor;
-    [touchBarItem
-        setView:CreateTouchBarButton(icon, self, IDC_BOOKMARK_PAGE, iconColor)];
+    int tooltipId = isStarred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR;
+    [touchBarItem setView:CreateTouchBarButton(icon, self, IDC_BOOKMARK_PAGE,
+                                               tooltipId, iconColor)];
   } else if ([identifier isEqualTo:kSearchTouchId]) {
     [touchBarItem setView:[self searchTouchBarView]];
   }
@@ -272,6 +280,21 @@
            forSegment:kBackSegmentIndex];
   [control setEnabled:commandUpdater_->IsCommandEnabled(IDC_FORWARD)
            forSegment:kForwardSegmentIndex];
+
+  // Use the accessibility protocol to get the children.
+  // Use NSAccessibilityUnignoredDescendant to be sure we start with the correct
+  // object.
+  id segmentElement = NSAccessibilityUnignoredDescendant(control);
+  NSArray* segments = [segmentElement
+      accessibilityAttributeValue:NSAccessibilityChildrenAttribute];
+  NSEnumerator* e = [segments objectEnumerator];
+  [[e nextObject] accessibilitySetOverrideValue:l10n_util::GetNSString(
+                                                    IDS_TOOLTIP_TOUCH_BAR_BACK)
+                                   forAttribute:NSAccessibilityTitleAttribute];
+  [[e nextObject]
+      accessibilitySetOverrideValue:l10n_util::GetNSString(
+                                        IDS_TOOLTIP_TOUCH_BAR_FORWARD)
+                       forAttribute:NSAccessibilityTitleAttribute];
   return control;
 }
 
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
index 3f9291f8..ec55064 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.h
@@ -59,6 +59,7 @@
 - (void)setProfileId:(NSString*)profileId;
 - (void)setIncognito:(BOOL)incognito;
 - (void)setNotificationType:(NSNumber*)notificationType;
+- (void)setShowSettingsButton:(BOOL)showSettingsButton;
 
 // Returns a notification ready to be displayed out of the provided
 // |notificationData|.
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
index e1c8a258..ae04950 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_mac.mm
@@ -26,6 +26,7 @@
 NSString* const kNotificationCloseButtonTag = @"closeButton";
 NSString* const kNotificationOptionsButtonTag = @"optionsButton";
 NSString* const kNotificationSettingsButtonTag = @"settingsButton";
+
 }  // namespace
 
 @implementation NotificationBuilder {
@@ -122,6 +123,12 @@
                         forKey:notification_constants::kNotificationType];
 }
 
+- (void)setShowSettingsButton:(BOOL)showSettingsButton {
+  [notificationData_
+      setObject:[NSNumber numberWithBool:showSettingsButton]
+         forKey:notification_constants::kNotificationHasSettingsButton];
+}
+
 - (NSUserNotification*)buildUserNotification {
   base::scoped_nsobject<NSUserNotification> toast(
       [[NSUserNotification alloc] init]);
@@ -142,11 +149,23 @@
     }
   }
 
+  // Type (needed to define the buttons)
+  NSNumber* type = [notificationData_
+      objectForKey:notification_constants::kNotificationType];
+
+  // Extensions don't have a settings button.
+  NSNumber* showSettingsButton = [notificationData_
+      objectForKey:notification_constants::kNotificationHasSettingsButton];
+
   // Buttons
   if ([toast respondsToSelector:@selector(_showsButtons)]) {
     DCHECK([notificationData_ objectForKey:kNotificationCloseButtonTag]);
     DCHECK([notificationData_ objectForKey:kNotificationSettingsButtonTag]);
     DCHECK([notificationData_ objectForKey:kNotificationOptionsButtonTag]);
+    DCHECK([notificationData_
+        objectForKey:notification_constants::kNotificationHasSettingsButton]);
+
+    BOOL settingsButton = [showSettingsButton boolValue];
 
     [toast setValue:@YES forKey:@"_showsButtons"];
     // A default close button label is provided by the platform but we
@@ -158,15 +177,19 @@
     // Display the Settings button as the action button if there are either no
     // developer-provided action buttons, or the alternate action menu is not
     // available on this Mac version. This avoids needlessly showing the menu.
-    // TODO(miguelg): Extensions should not have a settings button.
     if (![notificationData_ objectForKey:kNotificationButtonOne] ||
         ![toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]) {
-      [toast
-          setActionButtonTitle:
-              [notificationData_ objectForKey:kNotificationSettingsButtonTag]];
+      if (settingsButton) {
+        [toast setActionButtonTitle:
+                   [notificationData_
+                       objectForKey:kNotificationSettingsButtonTag]];
+      } else {
+        [toast setHasActionButton:NO];
+      }
+
     } else {
       // Otherwise show the alternate menu, then show the developer actions and
-      // finally the settings one.
+      // finally the settings one if needed.
       DCHECK(
           [toast respondsToSelector:@selector(_alwaysShowAlternateActionMenu)]);
       DCHECK(
@@ -183,8 +206,11 @@
         [buttons
             addObject:[notificationData_ objectForKey:kNotificationButtonTwo]];
       }
-      [buttons addObject:[notificationData_
-                             objectForKey:kNotificationSettingsButtonTag]];
+      if (settingsButton) {
+        [buttons addObject:[notificationData_
+                               objectForKey:kNotificationSettingsButtonTag]];
+      }
+
       [toast setValue:buttons forKey:@"_alternateActionButtonTitles"];
     }
   }
@@ -216,8 +242,6 @@
       objectForKey:notification_constants::kNotificationIncognito]);
   NSNumber* incognito = [notificationData_
       objectForKey:notification_constants::kNotificationIncognito];
-  NSNumber* type = [notificationData_
-      objectForKey:notification_constants::kNotificationType];
 
   toast.get().userInfo = @{
     notification_constants::kNotificationOrigin : origin,
@@ -225,6 +249,7 @@
     notification_constants::kNotificationProfileId : profileId,
     notification_constants::kNotificationIncognito : incognito,
     notification_constants::kNotificationType : type,
+    notification_constants::kNotificationHasSettingsButton : showSettingsButton,
   };
 
   return toast.autorelease();
diff --git a/chrome/browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm
index ee12b3dc..0c4babb 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_builder_mac_unittest.mm
@@ -27,6 +27,7 @@
   [builder setIncognito:false];
   [builder setNotificationType:
                [NSNumber numberWithInt:NotificationCommon::NON_PERSISTENT]];
+  [builder setShowSettingsButton:true];
 
   NSUserNotification* notification = [builder buildUserNotification];
   EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
@@ -57,6 +58,7 @@
   [builder
       setNotificationType:[NSNumber
                               numberWithInt:NotificationCommon::PERSISTENT]];
+  [builder setShowSettingsButton:true];
 
   NSUserNotification* notification = [builder buildUserNotification];
 
@@ -93,6 +95,7 @@
   [builder
       setNotificationType:[NSNumber
                               numberWithInt:NotificationCommon::PERSISTENT]];
+  [builder setShowSettingsButton:true];
 
   NSUserNotification* notification = [builder buildUserNotification];
 
@@ -115,6 +118,55 @@
   EXPECT_EQ("Settings", base::SysNSStringToUTF8([buttons objectAtIndex:2]));
 }
 
+TEST(NotificationBuilderMacTest, TestNotificationExtensionNoButtons) {
+  base::scoped_nsobject<NotificationBuilder> builder(
+      [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
+                                         optionsLabel:@"Options"
+                                        settingsLabel:@"Settings"]);
+  [builder setTitle:@"Title"];
+  [builder setSubTitle:@"https://www.miguel.com"];
+  [builder setContextMessage:@"SubTitle"];
+  [builder setNotificationId:@"notificationId"];
+  [builder setProfileId:@"profileId"];
+  [builder setIncognito:false];
+  [builder
+      setNotificationType:[NSNumber
+                              numberWithInt:NotificationCommon::EXTENSION]];
+  [builder setShowSettingsButton:false];
+
+  NSUserNotification* notification = [builder buildUserNotification];
+
+  EXPECT_FALSE(notification.hasActionButton);
+  EXPECT_EQ("Close", base::SysNSStringToUTF8([notification otherButtonTitle]));
+}
+
+TEST(NotificationBuilderMacTest, TestNotificationExtensionButtons) {
+  base::scoped_nsobject<NotificationBuilder> builder(
+      [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
+                                         optionsLabel:@"Options"
+                                        settingsLabel:@"Settings"]);
+  [builder setTitle:@"Title"];
+  [builder setSubTitle:@"https://www.miguel.com"];
+  [builder setContextMessage:@"SubTitle"];
+  [builder setButtons:@"Button1" secondaryButton:@"Button2"];
+  [builder setNotificationId:@"notificationId"];
+  [builder setProfileId:@"profileId"];
+  [builder setIncognito:false];
+  [builder
+      setNotificationType:[NSNumber
+                              numberWithInt:NotificationCommon::EXTENSION]];
+  [builder setShowSettingsButton:false];
+
+  NSUserNotification* notification = [builder buildUserNotification];
+
+  NSArray* buttons = [notification valueForKey:@"_alternateActionButtonTitles"];
+
+  // No settings button
+  ASSERT_EQ(2u, buttons.count);
+  EXPECT_EQ("Button1", base::SysNSStringToUTF8([buttons objectAtIndex:0]));
+  EXPECT_EQ("Button2", base::SysNSStringToUTF8([buttons objectAtIndex:1]));
+}
+
 TEST(NotificationBuilderMacTest, TestUserInfo) {
   base::scoped_nsobject<NotificationBuilder> builder(
       [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
@@ -128,6 +180,7 @@
   [builder
       setNotificationType:[NSNumber
                               numberWithInt:NotificationCommon::PERSISTENT]];
+  [builder setShowSettingsButton:true];
 
   NSUserNotification* notification = [builder buildUserNotification];
   EXPECT_EQ("Title", base::SysNSStringToUTF8([notification title]));
@@ -163,6 +216,7 @@
     [sourceBuilder
         setNotificationType:
             [NSNumber numberWithInt:NotificationCommon::NON_PERSISTENT]];
+    [sourceBuilder setShowSettingsButton:true];
 
     notificationData = [sourceBuilder buildDictionary];
   }
diff --git a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
index c3d2d78..349115f 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
+++ b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.h
@@ -16,6 +16,7 @@
 extern NSString* const kNotificationType;
 extern NSString* const kNotificationOperation;
 extern NSString* const kNotificationButtonIndex;
+extern NSString* const kNotificationHasSettingsButton;
 
 extern NSString* const kAlertXPCServiceName;
 
diff --git a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
index f4b007c..5820264 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_constants_mac.mm
@@ -12,6 +12,8 @@
 NSString* const kNotificationProfileId = @"notificationProfileId";
 NSString* const kNotificationIncognito = @"notificationIncognito";
 NSString* const kNotificationType = @"notificationType";
+NSString* const kNotificationHasSettingsButton =
+    @"notificationHasSettingsButton";
 
 // Only applicable to the NotificationResponseBuilder
 NSString* const kNotificationOperation = @"notificationOperation";
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
index fd582a9..4564d11 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac.mm
@@ -45,6 +45,8 @@
       objectForKey:notification_constants::kNotificationIncognito];
   NSNumber* notificationType = [[notification userInfo]
       objectForKey:notification_constants::kNotificationType];
+  NSNumber* hasSettingsButton = [[notification userInfo]
+      objectForKey:notification_constants::kNotificationHasSettingsButton];
 
   // Closed notifications are not activated.
   NotificationOperation operation =
@@ -54,7 +56,7 @@
   int buttonIndex = -1;
 
   // Determine whether the user clicked on a button, and if they did, whether it
-  // was a developer-provided button or the mandatory Settings button.
+  // was a developer-provided button or the  Settings button.
   if (notification.activationType ==
       NSUserNotificationActivationTypeActionButtonClicked) {
     NSArray* alternateButtons = @[];
@@ -64,26 +66,29 @@
           [notification valueForKey:@"_alternateActionButtonTitles"];
     }
 
-    bool multipleButtons = (alternateButtons.count > 0);
+    BOOL settingsButtonRequired = [hasSettingsButton boolValue];
+    BOOL multipleButtons = (alternateButtons.count > 0);
 
     // No developer actions, just the settings button.
     if (!multipleButtons) {
+      DCHECK(settingsButtonRequired);
       operation = NOTIFICATION_SETTINGS;
       buttonIndex = -1;
     } else {
       // 0 based array containing.
       // Button 1
       // Button 2 (optional)
-      // Settings
+      // Settings (if required)
       NSNumber* actionIndex =
           [notification valueForKey:@"_alternateActionIndex"];
-      operation = (actionIndex.unsignedLongValue == alternateButtons.count - 1)
+      operation = settingsButtonRequired && (actionIndex.unsignedLongValue ==
+                                             alternateButtons.count - 1)
                       ? NOTIFICATION_SETTINGS
                       : NOTIFICATION_CLICK;
-      buttonIndex =
-          (actionIndex.unsignedLongValue == alternateButtons.count - 1)
-              ? -1
-              : actionIndex.intValue;
+      buttonIndex = settingsButtonRequired && (actionIndex.unsignedLongValue ==
+                                               alternateButtons.count - 1)
+                        ? -1
+                        : actionIndex.intValue;
     }
   }
 
diff --git a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
index 14be1ae..9c0db2a 100644
--- a/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
+++ b/chrome/browser/ui/cocoa/notifications/notification_response_builder_mac_unittest.mm
@@ -13,7 +13,8 @@
 
 class NotificationResponseBuilderMacTest : public testing::Test {
  protected:
-  base::scoped_nsobject<NotificationBuilder> NewTestBuilder() {
+  base::scoped_nsobject<NotificationBuilder> NewTestBuilder(
+      NotificationCommon::Type type) {
     base::scoped_nsobject<NotificationBuilder> builder(
         [[NotificationBuilder alloc] initWithCloseLabel:@"Close"
                                            optionsLabel:@"Options"
@@ -26,13 +27,15 @@
     [builder setNotificationId:@"notificationId"];
     [builder setProfileId:@"profileId"];
     [builder setIncognito:false];
-    [builder setNotificationType:@(NotificationCommon::PERSISTENT)];
+    [builder setNotificationType:@(type)];
+    [builder setShowSettingsButton:(type != NotificationCommon::EXTENSION)];
     return builder;
   }
 };
 
 TEST_F(NotificationResponseBuilderMacTest, TestNotificationClick) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   NSUserNotification* notification = [builder buildUserNotification];
   // This will be set by the notification center to indicate the notification
   // was clicked.
@@ -46,12 +49,14 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
+
   EXPECT_EQ(0 /* NOTIFICATION_CLICK */, operation.intValue);
   EXPECT_EQ(-1, buttonIndex.intValue);
 }
 
 TEST_F(NotificationResponseBuilderMacTest, TestNotificationSettingsClick) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   NSUserNotification* notification = [builder buildUserNotification];
 
   // This will be set by the notification center to indicate the only available
@@ -65,12 +70,14 @@
       [response objectForKey:notification_constants::kNotificationOperation];
   NSNumber* buttonIndex =
       [response objectForKey:notification_constants::kNotificationButtonIndex];
+
   EXPECT_EQ(2 /* NOTIFICATION_SETTINGS */, operation.intValue);
   EXPECT_EQ(-1, buttonIndex.intValue);
 }
 
 TEST_F(NotificationResponseBuilderMacTest, TestNotificationOneActionClick) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   [builder setButtons:@"Button1" secondaryButton:@""];
 
   NSUserNotification* notification = [builder buildUserNotification];
@@ -93,7 +100,8 @@
 }
 
 TEST_F(NotificationResponseBuilderMacTest, TestNotificationTwoActionClick) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   [builder setButtons:@"Button1" secondaryButton:@"Button2"];
 
   NSUserNotification* notification = [builder buildUserNotification];
@@ -118,7 +126,8 @@
 
 TEST_F(NotificationResponseBuilderMacTest,
        TestNotificationTwoActionSettingsClick) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   [builder setButtons:@"Button1" secondaryButton:@"Button2"];
   NSUserNotification* notification = [builder buildUserNotification];
 
@@ -144,7 +153,8 @@
 }
 
 TEST_F(NotificationResponseBuilderMacTest, TestNotificationClose) {
-  base::scoped_nsobject<NotificationBuilder> builder = NewTestBuilder();
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::PERSISTENT);
   NSUserNotification* notification = [builder buildUserNotification];
 
   // None is what the NSUserNotification center emits when closing since it
@@ -162,3 +172,29 @@
   EXPECT_EQ(1 /* NOTIFICATION_CLOSE */, operation.intValue);
   EXPECT_EQ(-1, buttonIndex.intValue);
 }
+
+TEST_F(NotificationResponseBuilderMacTest, TestNotificationExtension) {
+  base::scoped_nsobject<NotificationBuilder> builder =
+      NewTestBuilder(NotificationCommon::EXTENSION);
+  [builder setButtons:@"Button1" secondaryButton:@"Button2"];
+  NSUserNotification* notification = [builder buildUserNotification];
+  // These values will be set by the notification center to indicate that button
+  // 1 was clicked.
+  [notification
+      setValue:
+          [NSNumber
+              numberWithInt:NSUserNotificationActivationTypeActionButtonClicked]
+        forKey:@"_activationType"];
+  [notification setValue:[NSNumber numberWithInt:1]
+                  forKey:@"_alternateActionIndex"];
+
+  NSDictionary* response =
+      [NotificationResponseBuilder buildDictionary:notification];
+
+  NSNumber* operation =
+      [response objectForKey:notification_constants::kNotificationOperation];
+  NSNumber* buttonIndex =
+      [response objectForKey:notification_constants::kNotificationButtonIndex];
+  EXPECT_EQ(0 /* NOTIFICATION_CLICK */, operation.intValue);
+  EXPECT_EQ(1, buttonIndex.intValue);
+}
diff --git a/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm b/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
index 6a34ae10..405e6d4f 100644
--- a/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
+++ b/chrome/browser/ui/cocoa/page_info/page_info_bubble_controller.mm
@@ -115,9 +115,9 @@
 // -----------------------------------------------------------------------------
 
 // NOTE: This assumes that there will never be more than one page info
-// popup shown, and that the one that is shown is associated with the current
-// window. This matches the behaviour in views: see PageInfoPopupView.
-bool g_is_popup_showing = false;
+// bubble shown, and that the one that is shown is associated with the current
+// window. This matches the behaviour in Views: see PageInfoBubbleView.
+bool g_is_bubble_showing = false;
 
 // Takes in the parent window, which should be a BrowserWindow, and gets the
 // proper anchor point for the bubble. The returned point is in screen
@@ -1145,13 +1145,13 @@
     : content::WebContentsObserver(web_contents),
       web_contents_(web_contents),
       bubble_controller_(nil) {
-  DCHECK(!g_is_popup_showing);
-  g_is_popup_showing = true;
+  DCHECK(!g_is_bubble_showing);
+  g_is_bubble_showing = true;
 }
 
 PageInfoUIBridge::~PageInfoUIBridge() {
-  DCHECK(g_is_popup_showing);
-  g_is_popup_showing = false;
+  DCHECK(g_is_bubble_showing);
+  g_is_bubble_showing = false;
 }
 
 void PageInfoUIBridge::set_bubble_controller(
@@ -1171,17 +1171,17 @@
     return;
   }
 
-  // Don't show the popup if it's already being shown. Since this method is
-  // called each time the location icon is clicked, each click toggles the popup
-  // in and out.
-  if (g_is_popup_showing)
+  // Don't show the bubble if it's already being shown. Since this method is
+  // called each time the location icon is clicked, each click toggles the
+  // bubble in and out.
+  if (g_is_bubble_showing)
     return;
 
   // Create the bridge. This will be owned by the bubble controller.
   PageInfoUIBridge* bridge = new PageInfoUIBridge(web_contents);
 
   // Create the bubble controller. It will dealloc itself when it closes,
-  // resetting |g_is_popup_showing|.
+  // resetting |g_is_bubble_showing|.
   PageInfoBubbleController* bubble_controller =
       [[PageInfoBubbleController alloc] initWithParentWindow:parent
                                             pageInfoUIBridge:bridge
diff --git a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
index 2e6e3378..a46fe9f4 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_strip_drag_controller.mm
@@ -352,9 +352,11 @@
   // See http://crbug.com/687647.
   NSPoint origin = sourceWindowFrame_.origin;
   origin.x += (thisPoint.x - dragOrigin_.x);
-  origin.y +=
-      (thisPoint.y - dragOrigin_.y) +
-      ([sourceController_ menubarOffset] + [sourceController_ menubarHeight]);
+  origin.y += (thisPoint.y - dragOrigin_.y);
+  if ([sourceController_ isInAnyFullscreenMode]) {
+    origin.y +=
+        [sourceController_ menubarOffset] + [sourceController_ menubarHeight];
+  }
 
   if (tearProgress < 1) {
     // If the tear animation is not complete, call back to ourself with the
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
index 23f0fcc..afa1e44 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.h
@@ -166,6 +166,9 @@
 // The height of the menubar.
 - (CGFloat)menubarHeight;
 
+// Returns YES if the browser window is in or entering any fullscreen mode.
+- (BOOL)isInAnyFullscreenMode;
+
 // Returns the view of the avatar button.
 - (NSView*)avatarView;
 
diff --git a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
index 716cb70c..d244942 100644
--- a/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
+++ b/chrome/browser/ui/cocoa/tabs/tab_window_controller.mm
@@ -378,6 +378,12 @@
   return kMenubarHeight;
 }
 
+- (BOOL)isInAnyFullscreenMode {
+  // Subclass must implement.
+  NOTIMPLEMENTED();
+  return NO;
+}
+
 - (NSView*)avatarView {
   return nil;
 }
diff --git a/chrome/browser/ui/navigation_correction_tab_observer.cc b/chrome/browser/ui/navigation_correction_tab_observer.cc
index ad27f82..96adf845 100644
--- a/chrome/browser/ui/navigation_correction_tab_observer.cc
+++ b/chrome/browser/ui/navigation_correction_tab_observer.cc
@@ -42,7 +42,7 @@
     if (google_util::IsGoogleDomainUrl(GetNavigationCorrectionURL(),
                                        google_util::ALLOW_SUBDOMAIN,
                                        google_util::ALLOW_NON_STANDARD_PORTS))
-      google_url_tracker->RequestServerCheck(false);
+      google_url_tracker->RequestServerCheck();
     google_url_updated_subscription_ = google_url_tracker->RegisterCallback(
         base::Bind(&NavigationCorrectionTabObserver::OnGoogleURLUpdated,
                    base::Unretained(this)));
diff --git a/chrome/browser/ui/page_info/page_info.cc b/chrome/browser/ui/page_info/page_info.cc
index 05f6f8e..26b3aa4c 100644
--- a/chrome/browser/ui/page_info/page_info.cc
+++ b/chrome/browser/ui/page_info/page_info.cc
@@ -337,7 +337,7 @@
 
   // This is technically redundant given the histogram above, but putting the
   // total count of permission changes in another histogram makes it easier to
-  // compare it against other kinds of actions in PageInfo[PopupView].
+  // compare it against other kinds of actions in Page Info.
   RecordPageInfoAction(PAGE_INFO_CHANGED_PERMISSION);
 
   PermissionUtil::ScopedRevocationReporter scoped_revocation_reporter(
@@ -407,7 +407,7 @@
                     const security_state::SecurityInfo& security_info) {
 #if !defined(OS_ANDROID) && !defined(OS_IOS)
   // On desktop, internal URLs aren't handled by this class. Instead, a
-  // custom and simpler popup is shown.
+  // custom and simpler bubble is shown.
   DCHECK(!url.SchemeIs(content::kChromeUIScheme) &&
          !url.SchemeIs(content::kChromeDevToolsScheme) &&
          !url.SchemeIs(content::kViewSourceScheme) &&
diff --git a/chrome/browser/ui/page_info/page_info_unittest.cc b/chrome/browser/ui/page_info/page_info_unittest.cc
index 6946c287..3eb25dd 100644
--- a/chrome/browser/ui/page_info/page_info_unittest.cc
+++ b/chrome/browser/ui/page_info/page_info_unittest.cc
@@ -677,7 +677,7 @@
 }
 
 // On desktop, internal URLs aren't handled by PageInfo class. Instead, a
-// custom and simpler popup is shown, so no need to test.
+// custom and simpler bubble is shown, so no need to test.
 #if defined(OS_ANDROID) || defined(OS_IOS)
 TEST_F(PageInfoTest, InternalPage) {
   SetURL("chrome://bookmarks");
diff --git a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
index 5a3731c..af9e915 100644
--- a/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
+++ b/chrome/browser/ui/toolbar/app_menu_model_unittest.cc
@@ -78,7 +78,6 @@
     BrowserWithTestWindowTest::TearDown();
     testing_io_thread_state_.reset();
     TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
-    DestroyBrowserAndProfile();
   }
 
  private:
diff --git a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
index 0a158f95..11380bc 100644
--- a/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
+++ b/chrome/browser/ui/views/apps/app_info_dialog/app_info_dialog_views_unittest.cc
@@ -196,13 +196,21 @@
 // Tests that the dialog closes when the current profile is destroyed.
 TEST_F(AppInfoDialogViewsTest, DestroyedProfileClosesDialog) {
   ShowAppInfo(kTestExtensionId);
+
   // First delete the test browser window. This ensures the test harness isn't
   // surprised by it being closed in response to the profile deletion below.
-  // Note the base class doesn't own the profile, so that part is skipped.
-  DestroyBrowserAndProfile();
+  std::unique_ptr<Browser> browser(release_browser());
+  browser->tab_strip_model()->CloseAllTabs();
+  browser.reset();
+  std::unique_ptr<BrowserWindow> browser_window(release_browser_window());
+  browser_window->Close();
+  browser_window.reset();
+
+  // This does not actually destroy the profile; see DestroyProfile above.
+  DestroyProfile(profile());
 
   // The following does nothing: it just ensures the Widget close is being
-  // triggered by the DeleteProfile() call rather than the line above.
+  // triggered by the DeleteProfile() call rather than the code above.
   base::RunLoop().RunUntilIdle();
 
   ASSERT_TRUE(widget_);
diff --git a/chrome/browser/ui/views/browser_dialogs_views_mac.cc b/chrome/browser/ui/views/browser_dialogs_views_mac.cc
index ea4ab31..b86f9855 100644
--- a/chrome/browser/ui/views/browser_dialogs_views_mac.cc
+++ b/chrome/browser/ui/views/browser_dialogs_views_mac.cc
@@ -9,7 +9,7 @@
 #include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bubble_view.h"
 #include "chrome/browser/ui/views/content_setting_bubble_contents.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/browser/ui/views/task_manager_view.h"
 #include "chrome/browser/ui/views/update_recommended_message_box.h"
 
@@ -30,17 +30,18 @@
   // Don't show the bubble again if it's already showing. A second click on the
   // location icon in the omnibox will dismiss an open bubble. This behaviour is
   // consistent with the non-Mac views implementation.
-  // Note that when the browser is toolkit-views, IsPopupShowing() is checked
-  // earlier because the popup is shown on mouse release (but dismissed on
+  // Note that when the browser is toolkit-views, IsBubbleShowing() is checked
+  // earlier because the bubble is shown on mouse release (but dismissed on
   // mouse pressed). A Cocoa browser does both on mouse pressed, so a check
   // when showing is sufficient.
-  if (PageInfoPopupView::GetShownPopupType() != PageInfoPopupView::POPUP_NONE) {
+  if (PageInfoBubbleView::GetShownBubbleType() !=
+      PageInfoBubbleView::BUBBLE_NONE) {
     return;
   }
 
-  PageInfoPopupView::ShowPopup(nullptr, gfx::Rect(anchor_point, gfx::Size()),
-                               profile, web_contents, virtual_url,
-                               security_info);
+  PageInfoBubbleView::ShowBubble(nullptr, gfx::Rect(anchor_point, gfx::Size()),
+                                 profile, web_contents, virtual_url,
+                                 security_info);
 }
 
 void ShowBookmarkBubbleViewsAtPoint(const gfx::Point& anchor_point,
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc
new file mode 100644
index 0000000..cbbb0cbf
--- /dev/null
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_chromeos.cc
@@ -0,0 +1,29 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/public/cpp/config.h"
+#include "build/build_config.h"
+#include "chrome/browser/chromeos/ash_config.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
+#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+
+namespace chrome {
+
+BrowserNonClientFrameView* CreateBrowserNonClientFrameView(
+    BrowserFrame* frame,
+    BrowserView* browser_view) {
+  if (chromeos::GetAshConfig() == ash::Config::MASH) {
+    BrowserNonClientFrameViewMus* frame_view =
+        new BrowserNonClientFrameViewMus(frame, browser_view);
+    frame_view->Init();
+    return frame_view;
+  }
+  BrowserNonClientFrameViewAsh* frame_view =
+      new BrowserNonClientFrameViewAsh(frame, browser_view);
+  frame_view->Init();
+  return frame_view;
+}
+
+}  // namespace chrome
diff --git a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
index 0a2535e4..3a41eac 100644
--- a/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
+++ b/chrome/browser/ui/views/frame/browser_non_client_frame_view_factory_views.cc
@@ -4,24 +4,17 @@
 
 #include "build/build_config.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
 
 #if defined(USE_AURA)
 #include "chrome/browser/ui/views/frame/browser_non_client_frame_view_mus.h"
 #include "services/service_manager/runner/common/client_util.h"
 #endif
 
-#if !defined(OS_CHROMEOS)
-#include "chrome/browser/ui/views/frame/opaque_browser_frame_view.h"
-#endif
-
 #if defined(OS_WIN)
 #include "chrome/browser/ui/views/frame/glass_browser_frame_view.h"
 #endif
 
-#if defined(USE_ASH)
-#include "chrome/browser/ui/views/frame/browser_non_client_frame_view_ash.h"
-#endif
-
 namespace chrome {
 
 BrowserNonClientFrameView* CreateBrowserNonClientFrameView(
@@ -35,18 +28,11 @@
     return frame_view;
   }
 #endif
-#if defined(USE_ASH)
-  BrowserNonClientFrameViewAsh* frame_view =
-      new BrowserNonClientFrameViewAsh(frame, browser_view);
-  frame_view->Init();
-  return frame_view;
-#else
 #if defined(OS_WIN)
   if (frame->ShouldUseNativeFrame())
     return new GlassBrowserFrameView(frame, browser_view);
 #endif
   return new OpaqueBrowserFrameView(frame, browser_view);
-#endif  // USE_ASH
 }
 
 }  // namespace chrome
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index b3eaf6b..488eda49 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -81,7 +81,7 @@
 #include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h"
 #include "chrome/browser/ui/views/new_back_shortcut_bubble.h"
 #include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/browser/ui/views/profiles/profile_indicator_icon.h"
 #include "chrome/browser/ui/views/status_bubble_views.h"
 #include "chrome/browser/ui/views/tabs/browser_tab_strip_controller.h"
@@ -1191,6 +1191,10 @@
          toolbar_;
 }
 
+bool BrowserView::IsToolbarShowing() const {
+  return IsToolbarVisible();
+}
+
 void BrowserView::ShowUpdateChromeDialog() {
   UpdateRecommendedMessageBox::Show(GetNativeWindow());
 }
@@ -1291,7 +1295,7 @@
     content::WebContents* web_contents,
     const GURL& virtual_url,
     const security_state::SecurityInfo& security_info) {
-  PageInfoPopupView::ShowPopup(
+  PageInfoBubbleView::ShowBubble(
       GetLocationBarView()->GetSecurityBubbleAnchorView(), gfx::Rect(), profile,
       web_contents, virtual_url, security_info);
 }
diff --git a/chrome/browser/ui/views/frame/browser_view.h b/chrome/browser/ui/views/frame/browser_view.h
index 5605d075..e0f70e8 100644
--- a/chrome/browser/ui/views/frame/browser_view.h
+++ b/chrome/browser/ui/views/frame/browser_view.h
@@ -311,6 +311,7 @@
   bool IsBookmarkBarAnimating() const override;
   bool IsTabStripEditable() const override;
   bool IsToolbarVisible() const override;
+  bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override;
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override;
   void ShowBookmarkAppBubble(
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view.cc b/chrome/browser/ui/views/location_bar/location_icon_view.cc
index 5eec8bc..d54f57d 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "chrome/grit/theme_resources.h"
 #include "components/grit/components_scaled_resources.h"
@@ -54,8 +54,8 @@
       model->PasteAndGo(text);
   }
 
-  suppress_mouse_released_action_ =
-      PageInfoPopupView::GetShownPopupType() != PageInfoPopupView::POPUP_NONE;
+  suppress_mouse_released_action_ = PageInfoBubbleView::GetShownBubbleType() !=
+                                    PageInfoBubbleView::BUBBLE_NONE;
   return true;
 }
 
diff --git a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc
index 2ab9073..5207f82 100644
--- a/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc
@@ -6,7 +6,7 @@
 
 #include "chrome/browser/ui/views/frame/browser_view.h"
 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
 #include "chrome/test/base/in_process_browser_test.h"
 #include "chrome/test/base/interactive_test_utils.h"
@@ -32,8 +32,8 @@
       runner1->QuitClosure());
   runner1->Run();
 
-  EXPECT_EQ(PageInfoPopupView::POPUP_PAGE_INFO,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_PAGE_INFO,
+            PageInfoBubbleView::GetShownBubbleType());
 
   // Verify that clicking again doesn't reshow it.
   scoped_refptr<content::MessageLoopRunner> runner2 =
@@ -45,8 +45,8 @@
       runner2->QuitClosure());
   runner2->Run();
 
-  EXPECT_EQ(PageInfoPopupView::POPUP_NONE,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_NONE,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/page_info/chosen_object_row.cc b/chrome/browser/ui/views/page_info/chosen_object_row.cc
index 21bd16c..e3cc3c6c 100644
--- a/chrome/browser/ui/views/page_info/chosen_object_row.cc
+++ b/chrome/browser/ui/views/page_info/chosen_object_row.cc
@@ -5,7 +5,7 @@
 #include "chrome/browser/ui/views/page_info/chosen_object_row.h"
 
 #include "chrome/browser/ui/views/page_info/chosen_object_row_observer.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "ui/resources/grit/ui_resources.h"
diff --git a/chrome/browser/ui/views/page_info/page_info_popup_view.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
similarity index 84%
rename from chrome/browser/ui/views/page_info/page_info_popup_view.cc
rename to chrome/browser/ui/views/page_info/page_info_bubble_view.cc
index c1b73ff..193734a 100644
--- a/chrome/browser/ui/views/page_info/page_info_popup_view.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 
 #include <stddef.h>
 
@@ -63,22 +63,24 @@
 namespace {
 
 // NOTE(jdonnelly): This use of this process-wide variable assumes that there's
-// never more than one page info popup shown and that it's associated
+// never more than one page info bubble shown and that it's associated
 // with the current window. If this assumption fails in the future, we'll need
-// to return a weak pointer from ShowPopup so callers can associate it with the
-// current window (or other context) and check if the popup they care about is
+// to return a weak pointer from ShowBubble so callers can associate it with the
+// current window (or other context) and check if the bubble they care about is
 // showing.
-PageInfoPopupView::PopupType g_shown_popup_type = PageInfoPopupView::POPUP_NONE;
+PageInfoBubbleView::BubbleType g_shown_bubble_type =
+    PageInfoBubbleView::BUBBLE_NONE;
 
 // General constants -----------------------------------------------------------
 
-// Popup width constraints.
-const int kMinPopupWidth = 320;
-const int kMaxPopupWidth = 1000;
+// Bubble width constraints.
+const int kMinBubbleWidth = 320;
+const int kMaxBubbleWidth = 1000;
 
-// Security Section (PopupHeaderView) ------------------------------------------
+// Security Section (BubbleHeaderView)
+// ------------------------------------------
 
-// Margin and padding values for the |PopupHeaderView|.
+// Margin and padding values for the |BubbleHeaderView|.
 const int kHeaderMarginBottom = 10;
 const int kHeaderPaddingBottom = 13;
 
@@ -122,15 +124,15 @@
 
 }  // namespace
 
-// |PopupHeaderView| is the UI element (view) that represents the header of the
-// |PageInfoPopupView|. The header shows the status of the site's
+// |BubbleHeaderView| is the UI element (view) that represents the header of the
+// |PageInfoBubbleView|. The header shows the status of the site's
 // identity check and the name of the site's identity.
-class PopupHeaderView : public views::View {
+class BubbleHeaderView : public views::View {
  public:
-  PopupHeaderView(views::ButtonListener* button_listener,
-                  views::StyledLabelListener* styled_label_listener,
-                  int side_margin);
-  ~PopupHeaderView() override;
+  BubbleHeaderView(views::ButtonListener* button_listener,
+                   views::StyledLabelListener* styled_label_listener,
+                   int side_margin);
+  ~BubbleHeaderView() override;
 
   // Sets the security summary for the current page.
   void SetSummary(const base::string16& summary_text);
@@ -155,39 +157,39 @@
   views::View* reset_decisions_label_container_;
   views::StyledLabel* reset_decisions_label_;
 
-  DISALLOW_COPY_AND_ASSIGN(PopupHeaderView);
+  DISALLOW_COPY_AND_ASSIGN(BubbleHeaderView);
 };
 
-// The regular PageInfoPopupView is not supported for internal Chrome pages and
-// extension pages. Instead of the |PageInfoPopupView|, the
-// |InternalPageInfoPopupView| is displayed.
-class InternalPageInfoPopupView : public views::BubbleDialogDelegateView {
+// The regular PageInfoBubbleView is not supported for internal Chrome pages and
+// extension pages. Instead of the |PageInfoBubbleView|, the
+// |InternalPageInfoBubbleView| is displayed.
+class InternalPageInfoBubbleView : public views::BubbleDialogDelegateView {
  public:
   // If |anchor_view| is nullptr, or has no Widget, |parent_window| may be
   // provided to ensure this bubble is closed when the parent closes.
-  InternalPageInfoPopupView(views::View* anchor_view,
-                            gfx::NativeView parent_window,
-                            const GURL& url);
-  ~InternalPageInfoPopupView() override;
+  InternalPageInfoBubbleView(views::View* anchor_view,
+                             gfx::NativeView parent_window,
+                             const GURL& url);
+  ~InternalPageInfoBubbleView() override;
 
   // views::BubbleDialogDelegateView:
   void OnWidgetDestroying(views::Widget* widget) override;
   int GetDialogButtons() const override;
 
  private:
-  friend class PageInfoPopupView;
+  friend class PageInfoBubbleView;
 
   // Used around icon and inside bubble border.
   static constexpr int kSpacing = 12;
 
-  DISALLOW_COPY_AND_ASSIGN(InternalPageInfoPopupView);
+  DISALLOW_COPY_AND_ASSIGN(InternalPageInfoBubbleView);
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-// Popup Header
+// Bubble Header
 ////////////////////////////////////////////////////////////////////////////////
 
-PopupHeaderView::PopupHeaderView(
+BubbleHeaderView::BubbleHeaderView(
     views::ButtonListener* button_listener,
     views::StyledLabelListener* styled_label_listener,
     int side_margin)
@@ -219,9 +221,9 @@
   layout->AddPaddingRow(1, kHeaderPaddingBottom);
 }
 
-PopupHeaderView::~PopupHeaderView() {}
+BubbleHeaderView::~BubbleHeaderView() {}
 
-void PopupHeaderView::SetDetails(const base::string16& details_text) {
+void BubbleHeaderView::SetDetails(const base::string16& details_text) {
   std::vector<base::string16> subst;
   subst.push_back(details_text);
   subst.push_back(l10n_util::GetStringUTF16(IDS_LEARN_MORE));
@@ -242,7 +244,7 @@
   details_label_->AddStyleRange(details_range, link_style);
 }
 
-void PopupHeaderView::AddResetDecisionsLabel() {
+void BubbleHeaderView::AddResetDecisionsLabel() {
   std::vector<base::string16> subst;
   subst.push_back(
       l10n_util::GetStringUTF16(IDS_PAGEINFO_INVALID_CERTIFICATE_DESCRIPTION));
@@ -276,15 +278,15 @@
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// InternalPageInfoPopupView
+// InternalPageInfoBubbleView
 ////////////////////////////////////////////////////////////////////////////////
 
-InternalPageInfoPopupView::InternalPageInfoPopupView(
+InternalPageInfoBubbleView::InternalPageInfoBubbleView(
     views::View* anchor_view,
     gfx::NativeView parent_window,
     const GURL& url)
     : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_LEFT) {
-  g_shown_popup_type = PageInfoPopupView::POPUP_INTERNAL_PAGE;
+  g_shown_bubble_type = PageInfoBubbleView::BUBBLE_INTERNAL_PAGE;
   set_parent_window(parent_window);
 
   int text = IDS_PAGE_INFO_INTERNAL_PAGE;
@@ -324,24 +326,24 @@
   views::BubbleDialogDelegateView::CreateBubble(this);
 }
 
-InternalPageInfoPopupView::~InternalPageInfoPopupView() {}
+InternalPageInfoBubbleView::~InternalPageInfoBubbleView() {}
 
-void InternalPageInfoPopupView::OnWidgetDestroying(views::Widget* widget) {
-  g_shown_popup_type = PageInfoPopupView::POPUP_NONE;
+void InternalPageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
+  g_shown_bubble_type = PageInfoBubbleView::BUBBLE_NONE;
 }
 
-int InternalPageInfoPopupView::GetDialogButtons() const {
+int InternalPageInfoBubbleView::GetDialogButtons() const {
   return ui::DIALOG_BUTTON_NONE;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// PageInfoPopupView
+// PageInfoBubbleView
 ////////////////////////////////////////////////////////////////////////////////
 
-PageInfoPopupView::~PageInfoPopupView() {}
+PageInfoBubbleView::~PageInfoBubbleView() {}
 
 // static
-void PageInfoPopupView::ShowPopup(
+void PageInfoBubbleView::ShowBubble(
     views::View* anchor_view,
     const gfx::Rect& anchor_rect,
     Profile* profile,
@@ -355,26 +357,26 @@
       url.SchemeIs(extensions::kExtensionScheme) ||
       url.SchemeIs(content::kViewSourceScheme)) {
     // Use the concrete type so that |SetAnchorRect| can be called as a friend.
-    InternalPageInfoPopupView* popup =
-        new InternalPageInfoPopupView(anchor_view, parent_window, url);
+    InternalPageInfoBubbleView* bubble =
+        new InternalPageInfoBubbleView(anchor_view, parent_window, url);
     if (!anchor_view)
-      popup->SetAnchorRect(anchor_rect);
-    popup->GetWidget()->Show();
+      bubble->SetAnchorRect(anchor_rect);
+    bubble->GetWidget()->Show();
     return;
   }
-  PageInfoPopupView* popup = new PageInfoPopupView(
+  PageInfoBubbleView* bubble = new PageInfoBubbleView(
       anchor_view, parent_window, profile, web_contents, url, security_info);
   if (!anchor_view)
-    popup->SetAnchorRect(anchor_rect);
-  popup->GetWidget()->Show();
+    bubble->SetAnchorRect(anchor_rect);
+  bubble->GetWidget()->Show();
 }
 
 // static
-PageInfoPopupView::PopupType PageInfoPopupView::GetShownPopupType() {
-  return g_shown_popup_type;
+PageInfoBubbleView::BubbleType PageInfoBubbleView::GetShownBubbleType() {
+  return g_shown_bubble_type;
 }
 
-PageInfoPopupView::PageInfoPopupView(
+PageInfoBubbleView::PageInfoBubbleView(
     views::View* anchor_view,
     gfx::NativeView parent_window,
     Profile* profile,
@@ -391,7 +393,7 @@
       cookie_dialog_link_(nullptr),
       permissions_view_(nullptr),
       weak_factory_(this) {
-  g_shown_popup_type = POPUP_PAGE_INFO;
+  g_shown_bubble_type = BUBBLE_PAGE_INFO;
   set_parent_window(parent_window);
 
   // Compensate for built-in vertical padding in the anchor view's image.
@@ -418,7 +420,7 @@
   column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
                         views::GridLayout::USE_PREF, 0, 0);
 
-  header_ = new PopupHeaderView(this, this, side_margin);
+  header_ = new BubbleHeaderView(this, this, side_margin);
   layout->StartRow(1, content_column);
   layout->AddView(header_);
 
@@ -447,17 +449,17 @@
       web_contents, url, security_info));
 }
 
-void PageInfoPopupView::RenderFrameDeleted(
+void PageInfoBubbleView::RenderFrameDeleted(
     content::RenderFrameHost* render_frame_host) {
   if (render_frame_host == web_contents()->GetMainFrame())
     GetWidget()->Close();
 }
 
-void PageInfoPopupView::WebContentsDestroyed() {
+void PageInfoBubbleView::WebContentsDestroyed() {
   weak_factory_.InvalidateWeakPtrs();
 }
 
-void PageInfoPopupView::OnPermissionChanged(
+void PageInfoBubbleView::OnPermissionChanged(
     const PageInfoUI::PermissionInfo& permission) {
   presenter_->OnSitePermissionChanged(permission.type, permission.setting);
   // The menu buttons for the permissions might have longer strings now, so we
@@ -466,50 +468,50 @@
   SizeToContents();
 }
 
-void PageInfoPopupView::OnChosenObjectDeleted(
+void PageInfoBubbleView::OnChosenObjectDeleted(
     const PageInfoUI::ChosenObjectInfo& info) {
   presenter_->OnSiteChosenObjectDeleted(info.ui_info, *info.object);
 }
 
-base::string16 PageInfoPopupView::GetWindowTitle() const {
+base::string16 PageInfoBubbleView::GetWindowTitle() const {
   return summary_text_;
 }
 
-bool PageInfoPopupView::ShouldShowCloseButton() const {
+bool PageInfoBubbleView::ShouldShowCloseButton() const {
   return true;
 }
 
-void PageInfoPopupView::OnWidgetDestroying(views::Widget* widget) {
-  g_shown_popup_type = POPUP_NONE;
+void PageInfoBubbleView::OnWidgetDestroying(views::Widget* widget) {
+  g_shown_bubble_type = BUBBLE_NONE;
   presenter_->OnUIClosing();
 }
 
-int PageInfoPopupView::GetDialogButtons() const {
+int PageInfoBubbleView::GetDialogButtons() const {
   return ui::DIALOG_BUTTON_NONE;
 }
 
-const gfx::FontList& PageInfoPopupView::GetTitleFontList() const {
+const gfx::FontList& PageInfoBubbleView::GetTitleFontList() const {
   return ui::ResourceBundle::GetSharedInstance().GetFontListWithDelta(
       kSummaryFontSizeDelta);
 }
 
-void PageInfoPopupView::ButtonPressed(views::Button* button,
-                                      const ui::Event& event) {
+void PageInfoBubbleView::ButtonPressed(views::Button* button,
+                                       const ui::Event& event) {
   DCHECK_EQ(BUTTON_CLOSE, button->id());
   GetWidget()->Close();
 }
 
-void PageInfoPopupView::LinkClicked(views::Link* source, int event_flags) {
-  // The popup closes automatically when the collected cookies dialog or the
+void PageInfoBubbleView::LinkClicked(views::Link* source, int event_flags) {
+  // The bubble closes automatically when the collected cookies dialog or the
   // certificate viewer opens. So delay handling of the link clicked to avoid
   // a crash in the base class which needs to complete the mouse event handling.
   content::BrowserThread::PostTask(
       content::BrowserThread::UI, FROM_HERE,
-      base::Bind(&PageInfoPopupView::HandleLinkClickedAsync,
+      base::Bind(&PageInfoBubbleView::HandleLinkClickedAsync,
                  weak_factory_.GetWeakPtr(), source));
 }
 
-gfx::Size PageInfoPopupView::GetPreferredSize() const {
+gfx::Size PageInfoBubbleView::GetPreferredSize() const {
   if (header_ == nullptr && site_settings_view_ == nullptr)
     return views::View::GetPreferredSize();
 
@@ -522,14 +524,14 @@
   if (site_settings_view_)
     height += site_settings_view_->GetPreferredSize().height();
 
-  int width = kMinPopupWidth;
+  int width = kMinBubbleWidth;
   if (site_settings_view_)
     width = std::max(width, site_settings_view_->GetPreferredSize().width());
-  width = std::min(width, kMaxPopupWidth);
+  width = std::min(width, kMaxBubbleWidth);
   return gfx::Size(width, height);
 }
 
-void PageInfoPopupView::SetCookieInfo(const CookieInfoList& cookie_info_list) {
+void PageInfoBubbleView::SetCookieInfo(const CookieInfoList& cookie_info_list) {
   // |cookie_info_list| should only ever have 2 items: first- and third-party
   // cookies.
   DCHECK_EQ(cookie_info_list.size(), 2u);
@@ -596,7 +598,7 @@
   SizeToContents();
 }
 
-void PageInfoPopupView::SetPermissionInfo(
+void PageInfoBubbleView::SetPermissionInfo(
     const PermissionInfoList& permission_info_list,
     ChosenObjectInfoList chosen_object_info_list) {
   // When a permission is changed, PageInfo::OnSitePermissionChanged()
@@ -669,7 +671,7 @@
   SizeToContents();
 }
 
-void PageInfoPopupView::SetIdentityInfo(const IdentityInfo& identity_info) {
+void PageInfoBubbleView::SetIdentityInfo(const IdentityInfo& identity_info) {
   std::unique_ptr<PageInfoUI::SecurityDescription> security_description =
       identity_info.GetSecurityDescription();
 
@@ -689,7 +691,7 @@
   SizeToContents();
 }
 
-views::View* PageInfoPopupView::CreateSiteSettingsView(int side_margin) {
+views::View* PageInfoBubbleView::CreateSiteSettingsView(int side_margin) {
   views::View* site_settings_view = new views::View();
   views::BoxLayout* box_layout =
       new views::BoxLayout(views::BoxLayout::kVertical, side_margin, 0, 0);
@@ -704,7 +706,7 @@
   return site_settings_view;
 }
 
-void PageInfoPopupView::HandleLinkClickedAsync(views::Link* source) {
+void PageInfoBubbleView::HandleLinkClickedAsync(views::Link* source) {
   // Both switch cases require accessing web_contents(), so we check it here.
   if (web_contents() == nullptr || web_contents()->IsBeingDestroyed())
     return;
@@ -732,9 +734,9 @@
   }
 }
 
-void PageInfoPopupView::StyledLabelLinkClicked(views::StyledLabel* label,
-                                               const gfx::Range& range,
-                                               int event_flags) {
+void PageInfoBubbleView::StyledLabelLinkClicked(views::StyledLabel* label,
+                                                const gfx::Range& range,
+                                                int event_flags) {
   switch (label->id()) {
     case STYLED_LABEL_SECURITY_DETAILS:
       web_contents()->OpenURL(content::OpenURLParams(
diff --git a/chrome/browser/ui/views/page_info/page_info_popup_view.h b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
similarity index 72%
rename from chrome/browser/ui/views/page_info/page_info_popup_view.h
rename to chrome/browser/ui/views/page_info/page_info_bubble_view.h
index c884362..2074631 100644
--- a/chrome/browser/ui/views/page_info/page_info_popup_view.h
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_POPUP_VIEW_H_
-#define CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_POPUP_VIEW_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_BUBBLE_VIEW_H_
+#define CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_BUBBLE_VIEW_H_
 
 #include <memory>
 
@@ -22,7 +22,7 @@
 #include "ui/views/controls/styled_label_listener.h"
 
 class GURL;
-class PopupHeaderView;
+class BubbleHeaderView;
 class Profile;
 
 namespace content {
@@ -38,7 +38,7 @@
 }  // namespace security_state
 
 namespace test {
-class PageInfoPopupViewTestApi;
+class PageInfoBubbleViewTestApi;
 }
 
 namespace views {
@@ -54,46 +54,46 @@
 };
 
 // The views implementation of the page info UI.
-class PageInfoPopupView : public content::WebContentsObserver,
-                          public PermissionSelectorRowObserver,
-                          public ChosenObjectRowObserver,
-                          public views::BubbleDialogDelegateView,
-                          public views::ButtonListener,
-                          public views::LinkListener,
-                          public views::StyledLabelListener,
-                          public PageInfoUI {
+class PageInfoBubbleView : public content::WebContentsObserver,
+                           public PermissionSelectorRowObserver,
+                           public ChosenObjectRowObserver,
+                           public views::BubbleDialogDelegateView,
+                           public views::ButtonListener,
+                           public views::LinkListener,
+                           public views::StyledLabelListener,
+                           public PageInfoUI {
  public:
-  ~PageInfoPopupView() override;
+  ~PageInfoBubbleView() override;
 
-  // Type of the popup being displayed.
-  enum PopupType {
-    POPUP_NONE,
+  // Type of the bubble being displayed.
+  enum BubbleType {
+    BUBBLE_NONE,
     // Usual page info bubble for websites.
-    POPUP_PAGE_INFO,
+    BUBBLE_PAGE_INFO,
     // Custom bubble for internal pages like chrome:// and chrome-extensions://.
-    POPUP_INTERNAL_PAGE
+    BUBBLE_INTERNAL_PAGE
   };
 
   // If |anchor_view| is null, |anchor_rect| is used to anchor the bubble.
-  static void ShowPopup(views::View* anchor_view,
-                        const gfx::Rect& anchor_rect,
-                        Profile* profile,
-                        content::WebContents* web_contents,
-                        const GURL& url,
-                        const security_state::SecurityInfo& security_info);
+  static void ShowBubble(views::View* anchor_view,
+                         const gfx::Rect& anchor_rect,
+                         Profile* profile,
+                         content::WebContents* web_contents,
+                         const GURL& url,
+                         const security_state::SecurityInfo& security_info);
 
-  // Returns the type of the popup bubble being shown.
-  static PopupType GetShownPopupType();
+  // Returns the type of the bubble being shown.
+  static BubbleType GetShownBubbleType();
 
  private:
-  friend class test::PageInfoPopupViewTestApi;
+  friend class test::PageInfoBubbleViewTestApi;
 
-  PageInfoPopupView(views::View* anchor_view,
-                    gfx::NativeView parent_window,
-                    Profile* profile,
-                    content::WebContents* web_contents,
-                    const GURL& url,
-                    const security_state::SecurityInfo& security_info);
+  PageInfoBubbleView(views::View* anchor_view,
+                     gfx::NativeView parent_window,
+                     Profile* profile,
+                     content::WebContents* web_contents,
+                     const GURL& url,
+                     const security_state::SecurityInfo& security_info);
 
   // WebContentsObserver implementation.
   void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
@@ -151,7 +151,7 @@
   Profile* profile_;
 
   // The header section (containing security-related information).
-  PopupHeaderView* header_;
+  BubbleHeaderView* header_;
 
   // The security summary for the current page.
   base::string16 summary_text_;
@@ -177,9 +177,9 @@
   // |Permission| changes.
   std::vector<std::unique_ptr<PermissionSelectorRow>> selector_rows_;
 
-  base::WeakPtrFactory<PageInfoPopupView> weak_factory_;
+  base::WeakPtrFactory<PageInfoBubbleView> weak_factory_;
 
-  DISALLOW_COPY_AND_ASSIGN(PageInfoPopupView);
+  DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleView);
 };
 
-#endif  // CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_POPUP_VIEW_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_PAGE_INFO_PAGE_INFO_BUBBLE_VIEW_H_
diff --git a/chrome/browser/ui/views/page_info/page_info_popup_view_interactive_uitest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc
similarity index 62%
rename from chrome/browser/ui/views/page_info/page_info_popup_view_interactive_uitest.cc
rename to chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc
index a743665..ee22800 100644
--- a/chrome/browser/ui/views/page_info/page_info_popup_view_interactive_uitest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 
 #include "chrome/browser/ui/browser_commands.h"
 #include "chrome/browser/ui/views/frame/browser_view.h"
@@ -14,7 +14,7 @@
 
 namespace {
 
-typedef InProcessBrowserTest PageInfoPopupViewBrowserTest;
+typedef InProcessBrowserTest PageInfoBubbleViewBrowserTest;
 
 // Clicks the location icon to open the page info bubble.
 void ClickAndWait(Browser* browser) {
@@ -31,41 +31,41 @@
   runner->Run();
 }
 
-IN_PROC_BROWSER_TEST_F(PageInfoPopupViewBrowserTest, ShowPopup) {
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ShowBubble) {
   ClickAndWait(browser());
-  EXPECT_EQ(PageInfoPopupView::POPUP_PAGE_INFO,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_PAGE_INFO,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
-IN_PROC_BROWSER_TEST_F(PageInfoPopupViewBrowserTest, ChromeURL) {
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeURL) {
   ui_test_utils::NavigateToURL(browser(), GURL("chrome://settings"));
   ClickAndWait(browser());
-  EXPECT_EQ(PageInfoPopupView::POPUP_INTERNAL_PAGE,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
-IN_PROC_BROWSER_TEST_F(PageInfoPopupViewBrowserTest, ChromeExtensionURL) {
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeExtensionURL) {
   ui_test_utils::NavigateToURL(
       browser(), GURL("chrome-extension://extension-id/options.html"));
   ClickAndWait(browser());
-  EXPECT_EQ(PageInfoPopupView::POPUP_INTERNAL_PAGE,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
-IN_PROC_BROWSER_TEST_F(PageInfoPopupViewBrowserTest, ChromeDevtoolsURL) {
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ChromeDevtoolsURL) {
   ui_test_utils::NavigateToURL(
       browser(), GURL("chrome-devtools://devtools/bundled/inspector.html"));
   ClickAndWait(browser());
-  EXPECT_EQ(PageInfoPopupView::POPUP_INTERNAL_PAGE,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
-IN_PROC_BROWSER_TEST_F(PageInfoPopupViewBrowserTest, ViewSourceURL) {
+IN_PROC_BROWSER_TEST_F(PageInfoBubbleViewBrowserTest, ViewSourceURL) {
   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
   chrome::ViewSelectedSource(browser());
   ClickAndWait(browser());
-  EXPECT_EQ(PageInfoPopupView::POPUP_INTERNAL_PAGE,
-            PageInfoPopupView::GetShownPopupType());
+  EXPECT_EQ(PageInfoBubbleView::BUBBLE_INTERNAL_PAGE,
+            PageInfoBubbleView::GetShownBubbleType());
 }
 
 }  // namespace
diff --git a/chrome/browser/ui/views/page_info/page_info_popup_view_unittest.cc b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
similarity index 89%
rename from chrome/browser/ui/views/page_info/page_info_popup_view_unittest.cc
rename to chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
index 5a65aa3..0f73de7168 100644
--- a/chrome/browser/ui/views/page_info/page_info_popup_view_unittest.cc
+++ b/chrome/browser/ui/views/page_info/page_info_bubble_view_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 
 #include "base/macros.h"
 #include "base/strings/utf_string_conversions.h"
@@ -29,11 +29,11 @@
 
 namespace test {
 
-class PageInfoPopupViewTestApi {
+class PageInfoBubbleViewTestApi {
  public:
-  PageInfoPopupViewTestApi(gfx::NativeView parent,
-                           Profile* profile,
-                           content::WebContents* web_contents)
+  PageInfoBubbleViewTestApi(gfx::NativeView parent,
+                            Profile* profile,
+                            content::WebContents* web_contents)
       : view_(nullptr),
         parent_(parent),
         profile_(profile),
@@ -47,11 +47,11 @@
 
     security_state::SecurityInfo security_info;
     views::View* anchor_view = nullptr;
-    view_ = new PageInfoPopupView(anchor_view, parent_, profile_, web_contents_,
-                                  GURL(kUrl), security_info);
+    view_ = new PageInfoBubbleView(anchor_view, parent_, profile_,
+                                   web_contents_, GURL(kUrl), security_info);
   }
 
-  PageInfoPopupView* view() { return view_; }
+  PageInfoBubbleView* view() { return view_; }
   views::View* permissions_view() { return view_->permissions_view_; }
 
   PermissionSelectorRow* GetPermissionSelectorAt(int index) {
@@ -83,20 +83,20 @@
 
   // Simulates recreating the dialog with a new PermissionInfoList.
   void SetPermissionInfo(const PermissionInfoList& list) {
-    for (const PageInfoPopupView::PermissionInfo& info : list)
+    for (const PageInfoBubbleView::PermissionInfo& info : list)
       view_->presenter_->OnSitePermissionChanged(info.type, info.setting);
     CreateView();
   }
 
  private:
-  PageInfoPopupView* view_;  // Weak. Owned by its Widget.
+  PageInfoBubbleView* view_;  // Weak. Owned by its Widget.
 
   // For recreating the view.
   gfx::NativeView parent_;
   Profile* profile_;
   content::WebContents* web_contents_;
 
-  DISALLOW_COPY_AND_ASSIGN(PageInfoPopupViewTestApi);
+  DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleViewTestApi);
 };
 
 }  // namespace test
@@ -124,9 +124,9 @@
   DISALLOW_COPY_AND_ASSIGN(ScopedWebContentsTestHelper);
 };
 
-class PageInfoPopupViewTest : public testing::Test {
+class PageInfoBubbleViewTest : public testing::Test {
  public:
-  PageInfoPopupViewTest() {}
+  PageInfoBubbleViewTest() {}
 
   // testing::Test:
   void SetUp() override {
@@ -137,7 +137,7 @@
 
     content::WebContents* web_contents = web_contents_helper_.web_contents();
     TabSpecificContentSettings::CreateForWebContents(web_contents);
-    api_.reset(new test::PageInfoPopupViewTestApi(
+    api_.reset(new test::PageInfoBubbleViewTestApi(
         parent_window_->GetNativeView(), web_contents_helper_.profile(),
         web_contents));
   }
@@ -150,10 +150,10 @@
   views::ScopedViewsTestHelper views_helper_;
 
   views::Widget* parent_window_ = nullptr;  // Weak. Owned by the NativeWidget.
-  std::unique_ptr<test::PageInfoPopupViewTestApi> api_;
+  std::unique_ptr<test::PageInfoBubbleViewTestApi> api_;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(PageInfoPopupViewTest);
+  DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleViewTest);
 };
 
 }  // namespace
@@ -176,8 +176,8 @@
 constexpr int kViewsPerPermissionRow = 3;
 
 // Test UI construction and reconstruction via
-// PageInfoPopupView::SetPermissionInfo().
-TEST_F(PageInfoPopupViewTest, MAYBE_SetPermissionInfo) {
+// PageInfoBubbleView::SetPermissionInfo().
+TEST_F(PageInfoBubbleViewTest, MAYBE_SetPermissionInfo) {
   PermissionInfoList list(1);
   list.back().type = CONTENT_SETTINGS_TYPE_GEOLOCATION;
   list.back().source = content_settings::SETTING_SOURCE_USER;
@@ -225,7 +225,7 @@
 }
 
 // Test UI construction and reconstruction with USB devices.
-TEST_F(PageInfoPopupViewTest, SetPermissionInfoWithUsbDevice) {
+TEST_F(PageInfoBubbleViewTest, SetPermissionInfoWithUsbDevice) {
   const int kExpectedChildren =
       kViewsPerPermissionRow *
       (ExclusiveAccessManager::IsSimplifiedFullscreenUIEnabled() ? 11 : 13);
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.cc b/chrome/browser/ui/views/page_info/permission_selector_row.cc
index 5f56e1e..8a30dca 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.cc
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.cc
@@ -10,7 +10,7 @@
 #include "chrome/browser/ui/page_info/page_info_ui.h"
 #include "chrome/browser/ui/page_info/permission_menu_model.h"
 #include "chrome/browser/ui/views/page_info/non_accessible_image_view.h"
-#include "chrome/browser/ui/views/page_info/page_info_popup_view.h"
+#include "chrome/browser/ui/views/page_info/page_info_bubble_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/material_design/material_design_controller.h"
@@ -347,7 +347,7 @@
         profile_, permission.type, permission.setting,
         permission.default_setting, content_settings::SETTING_SOURCE_USER));
     menu_button_->SizeToPreferredSize();
-    // Re-layout will be done at the |PageInfoPopupView| level, since
+    // Re-layout will be done at the |PageInfoBubbleView| level, since
     // that view may need to resize itself to accomodate the new sizes of its
     // contents.
     menu_button_->InvalidateLayout();
diff --git a/chrome/browser/ui/views/page_info/permission_selector_row.h b/chrome/browser/ui/views/page_info/permission_selector_row.h
index af592a60..cf25398a 100644
--- a/chrome/browser/ui/views/page_info/permission_selector_row.h
+++ b/chrome/browser/ui/views/page_info/permission_selector_row.h
@@ -26,7 +26,7 @@
 }
 
 namespace test {
-class PageInfoPopupViewTestApi;
+class PageInfoBubbleViewTestApi;
 }
 
 namespace views {
@@ -55,7 +55,7 @@
   void PermissionChanged(const PageInfoUI::PermissionInfo& permission);
 
  private:
-  friend class test::PageInfoPopupViewTestApi;
+  friend class test::PageInfoBubbleViewTestApi;
 
   void InitializeMenuButtonView(views::GridLayout* layout,
                                 const PageInfoUI::PermissionInfo& permission);
diff --git a/chrome/browser/ui/webui/options/browser_options_handler.cc b/chrome/browser/ui/webui/options/browser_options_handler.cc
index 8ee742d..3fa0d8e 100644
--- a/chrome/browser/ui/webui/options/browser_options_handler.cc
+++ b/chrome/browser/ui/webui/options/browser_options_handler.cc
@@ -753,9 +753,9 @@
 #endif
 
 #if defined(OS_CHROMEOS)
-  values->SetBoolean("cupsPrintEnabled",
+  values->SetBoolean("cupsPrintDisabled",
                      base::CommandLine::ForCurrentProcess()->HasSwitch(
-                         ::switches::kEnableNativeCups));
+                         ::switches::kDisableNativeCups));
   values->SetString("cupsPrintLearnMoreURL",
                     chrome::kChromeUIMdCupsSettingsURL);
 #endif  // defined(OS_CHROMEOS)
diff --git a/chrome/browser/ui/webui/options/options_browsertest.js b/chrome/browser/ui/webui/options/options_browsertest.js
index 5e91447..05dfd8c96b 100644
--- a/chrome/browser/ui/webui/options/options_browsertest.js
+++ b/chrome/browser/ui/webui/options/options_browsertest.js
@@ -95,6 +95,7 @@
       '#privacy-explanation > A',
       '#languages-section > .settings-row > A',
       '#cloudprint-options-mdns > .settings-row > A',
+      '#cups-printers-section > .settings-row > A',
       '#do-not-track-confirm-overlay > .action-area > .hbox.stretch > A',
     ];
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index 264f1af..8eb35ccfd 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -425,8 +425,8 @@
 
 #if defined(OS_CHROMEOS)
   bool cups_and_md_settings_enabled =
-      base::CommandLine::ForCurrentProcess()->HasSwitch(
-          ::switches::kEnableNativeCups);
+      !base::CommandLine::ForCurrentProcess()->HasSwitch(
+          ::switches::kDisableNativeCups);
   source->AddBoolean("showLocalManageButton", cups_and_md_settings_enabled);
 #else
   source->AddBoolean("showLocalManageButton", true);
diff --git a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
index 99da78a..0739673 100644
--- a/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
+++ b/chrome/browser/ui/webui/print_preview/printer_backend_proxy_chromeos.cc
@@ -134,8 +134,8 @@
 
     PrinterList printer_list;
 
-    if (base::CommandLine::ForCurrentProcess()->HasSwitch(
-            switches::kEnableNativeCups)) {
+    if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kDisableNativeCups)) {
       AddPrintersToList(prefs_->GetPrinters(), &printer_list);
       AddPrintersToList(prefs_->GetRecommendedPrinters(), &printer_list);
     }
diff --git a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
index 180c2fa..6544cb1 100644
--- a/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
+++ b/chrome/browser/ui/webui/settings/md_settings_localized_strings_provider.cc
@@ -1466,8 +1466,8 @@
 
 #if defined(OS_CHROMEOS)
   html_source->AddBoolean("showCupsPrintingFeatures",
-                          base::CommandLine::ForCurrentProcess()->HasSwitch(
-                              ::switches::kEnableNativeCups));
+                          !base::CommandLine::ForCurrentProcess()->HasSwitch(
+                              ::switches::kDisableNativeCups));
 #endif
 }
 
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 664dbb8..dee1a0b 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -934,14 +934,14 @@
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_CHROMEOS)
-// Enables apps on the login screen.
-const char kEnableLoginScreenApps[] = "enable-login-screen-apps";
-
-// Enables native cups integration
-const char kEnableNativeCups[] = "enable-native-cups";
-
 // Custom crosh command.
 const char kCroshCommand[] = "crosh-command";
+
+// Disables native cups integration
+const char kDisableNativeCups[] = "disable-native-cups";
+
+// Enables apps on the login screen.
+const char kEnableLoginScreenApps[] = "enable-login-screen-apps";
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(USE_ASH)
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 6b2a9f5..2a94775 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -274,9 +274,9 @@
 #endif  // defined(OS_ANDROID)
 
 #if defined(OS_CHROMEOS)
-extern const char kEnableLoginScreenApps[];
-extern const char kEnableNativeCups[];
 extern const char kCroshCommand[];
+extern const char kDisableNativeCups[];
+extern const char kEnableLoginScreenApps[];
 #endif  // defined(OS_CHROMEOS)
 
 #if defined(USE_ASH)
diff --git a/chrome/common/ppapi_utils.cc b/chrome/common/ppapi_utils.cc
index 58fe135..3e0563a 100644
--- a/chrome/common/ppapi_utils.cc
+++ b/chrome/common/ppapi_utils.cc
@@ -7,6 +7,7 @@
 
 #include "build/build_config.h"
 #include "ppapi/c/dev/ppb_audio_input_dev.h"
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
 #include "ppapi/c/dev/ppb_buffer_dev.h"
 #include "ppapi/c/dev/ppb_char_set_dev.h"
 #include "ppapi/c/dev/ppb_crypto_dev.h"
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 4572d20..7f706803 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -564,7 +564,7 @@
           "../browser/ui/views/location_bar/location_icon_view_interactive_uitest.cc",
           "../browser/ui/views/location_bar/star_view_browsertest.cc",
           "../browser/ui/views/omnibox/omnibox_view_views_browsertest.cc",
-          "../browser/ui/views/page_info/page_info_popup_view_interactive_uitest.cc",
+          "../browser/ui/views/page_info/page_info_bubble_view_interactive_uitest.cc",
           "../browser/ui/views/passwords/manage_passwords_bubble_view_interactive_uitest.cc",
           "../browser/ui/views/passwords/manage_passwords_icon_view_interactive_uitest.cc",
           "../browser/ui/views/ssl_client_certificate_selector_browsertest.cc",
@@ -4860,7 +4860,7 @@
       "../browser/ui/views/confirm_bubble_views_unittest.cc",
       "../browser/ui/views/global_error_bubble_view_unittest.cc",
       "../browser/ui/views/harmony/layout_delegate_unittest.cc",
-      "../browser/ui/views/page_info/page_info_popup_view_unittest.cc",
+      "../browser/ui/views/page_info/page_info_bubble_view_unittest.cc",
       "../browser/ui/views/payments/credit_card_editor_view_controller_unittest.cc",
       "../browser/ui/views/payments/payment_request_item_list_unittest.cc",
       "../browser/ui/views/payments/validating_textfield_unittest.cc",
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc
index 08a9dd7..955e36b 100644
--- a/chrome/test/base/browser_with_test_window_test.cc
+++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -87,9 +87,11 @@
   // before the profile can be destroyed and the test safely shut down.
   base::RunLoop().RunUntilIdle();
 
-  // Reset the profile here because some profile keyed services (like the
-  // audio service) depend on test stubs that the helpers below will remove.
-  DestroyBrowserAndProfile();
+  // Close the browser tabs and destroy the browser and window instances.
+  if (browser_)
+    browser_->tab_strip_model()->CloseAllTabs();
+  browser_.reset();
+  window_.reset();
 
   if (content::IsBrowserSideNavigationEnabled())
     content::BrowserSideNavigationTearDown();
@@ -99,15 +101,23 @@
 #endif
 
 #if defined(OS_CHROMEOS)
+  // Destroy the shell before the profile to match production shutdown ordering.
   ash_test_helper_->TearDown();
 #elif defined(TOOLKIT_VIEWS)
   views_test_helper_.reset();
 #endif
 
+  // Destroy the profile here - otherwise, if the profile is freed in the
+  // destructor, and a test subclass owns a resource that the profile depends
+  // on (such as g_browser_process()->local_state()) there's no way for the
+  // subclass to free it after the profile.
+  if (profile_)
+    DestroyProfile(profile_);
+  profile_ = nullptr;
+
   testing::Test::TearDown();
 
-  // A Task is leaked if we don't destroy everything, then run the message
-  // loop.
+  // A Task is leaked if we don't destroy everything, then run the message loop.
   base::ThreadTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::MessageLoop::QuitWhenIdleClosure());
   base::RunLoop().Run();
@@ -164,23 +174,6 @@
   contents->UpdateTitleForEntry(controller->GetActiveEntry(), title);
 }
 
-void BrowserWithTestWindowTest::DestroyBrowserAndProfile() {
-  if (browser_.get()) {
-    // Make sure we close all tabs, otherwise Browser isn't happy in its
-    // destructor.
-    browser()->tab_strip_model()->CloseAllTabs();
-    browser_.reset(NULL);
-  }
-  window_.reset(NULL);
-  // Destroy the profile here - otherwise, if the profile is freed in the
-  // destructor, and a test subclass owns a resource that the profile depends
-  // on (such as g_browser_process()->local_state()) there's no way for the
-  // subclass to free it after the profile.
-  if (profile_)
-    DestroyProfile(profile_);
-  profile_ = NULL;
-}
-
 TestingProfile* BrowserWithTestWindowTest::CreateProfile() {
   return new TestingProfile();
 }
diff --git a/chrome/test/base/browser_with_test_window_test.h b/chrome/test/base/browser_with_test_window_test.h
index a6d3123..18c5e06 100644
--- a/chrome/test/base/browser_with_test_window_test.h
+++ b/chrome/test/base/browser_with_test_window_test.h
@@ -127,10 +127,6 @@
                                            const GURL& url,
                                            const base::string16& title);
 
-  // Destroys the browser, window, and profile created by this class. This is
-  // invoked from the destructor.
-  void DestroyBrowserAndProfile();
-
   // Creates the profile used by this test. The caller owns the return value.
   virtual TestingProfile* CreateProfile();
 
diff --git a/chrome/test/base/test_browser_window.cc b/chrome/test/base/test_browser_window.cc
index c84cbe9..5d9d730 100644
--- a/chrome/test/base/test_browser_window.cc
+++ b/chrome/test/base/test_browser_window.cc
@@ -143,6 +143,10 @@
   return false;
 }
 
+bool TestBrowserWindow::IsToolbarShowing() const {
+  return false;
+}
+
 ShowTranslateBubbleResult TestBrowserWindow::ShowTranslateBubble(
     content::WebContents* contents,
     translate::TranslateStep step,
diff --git a/chrome/test/base/test_browser_window.h b/chrome/test/base/test_browser_window.h
index b9884db..73e5723 100644
--- a/chrome/test/base/test_browser_window.h
+++ b/chrome/test/base/test_browser_window.h
@@ -88,6 +88,7 @@
   bool IsBookmarkBarAnimating() const override;
   bool IsTabStripEditable() const override;
   bool IsToolbarVisible() const override;
+  bool IsToolbarShowing() const override;
   void ShowUpdateChromeDialog() override {}
   void ShowBookmarkBubble(const GURL& url, bool already_bookmarked) override {}
   void ShowBookmarkAppBubble(
diff --git a/chrome/test/data/android/webvr_instrumentation/html/test_device_capabilities_match_expectations.html b/chrome/test/data/android/webvr_instrumentation/html/test_device_capabilities_match_expectations.html
new file mode 100644
index 0000000..772de82
--- /dev/null
+++ b/chrome/test/data/android/webvr_instrumentation/html/test_device_capabilities_match_expectations.html
@@ -0,0 +1,53 @@
+<!doctype html>
+<!--
+Tests that the reported device capabilities match expectations.
+-->
+<html>
+  <head>
+    <link rel="stylesheet" type="text/css" href="../resources/webvr_e2e.css">
+  </head>
+  <body>
+    <canvas id="webgl-canvas"></canvas>
+    <script src="../../../../../../third_party/WebKit/LayoutTests/resources/testharness.js"></script>
+    <script src="../resources/webvr_e2e.js"></script>
+    <script src="../resources/webvr_boilerplate.js"></script>
+    <script>
+      // All the current test devices have the same expectations, but that will
+      // change with additional device support, especially desktop.
+      var android_expectation = {
+        "isPresenting": false,
+        "capabilities": {
+          "hasPosition": false,
+          "hasExternalDisplay": false,
+          "canPresent": true,
+          "maxLayers": 1,
+        },
+      }
+      var expectations = {
+        "angler": android_expectation,  // Nexus 6P
+        "bullhead": android_expectation,  // Nexus 5X
+        "hammerhead": android_expectation,  // Nexus 5
+        "marlin": android_expectation,  // Pixel XL
+        "sailfish": android_expectation,  // Pixel
+      }
+      var t = async_test("Device capabilities match expectations");
+      function stepCheckDeviceCapabilities(device) {
+        if (!(device in expectations)) {
+          t.step_func_done( () => {
+            assert_unreached("Given device " + device + " not in expectations");
+          })();
+          return;
+        }
+        let expectation = expectations[device];
+        t.step_func_done( () => {
+          assert_equals(vrDisplay["isPresenting"], expectation["isPresenting"]);
+          for (var capability in expectation["capabilities"]) {
+            assert_equals(vrDisplay["capabilities"][capability],
+                          expectation["capabilities"][capability],
+                          capability);
+          }
+        })();
+      }
+    </script>
+  </body>
+</html>
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index b77aff0..5b26a8b 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -1136,7 +1136,13 @@
   runAccessibilityChecks: false,
 };
 
-TEST_F('CrSettingsNonExistentRouteTest', 'All', function() {
+// Failing on ChromiumOS dbg. https://crbug.com/709442
+GEN('#if defined(OS_CHROMEOS) && !defined(NDEBUG)');
+GEN('#define MAYBE_All DISABLED_All');
+GEN('#else');
+GEN('#define MAYBE_All All');
+GEN('#endif');
+TEST_F('CrSettingsNonExistentRouteTest', 'MAYBE_All', function() {
   suite('NonExistentRoutes', function() {
     test('redirect to basic', function() {
       assertEquals(settings.Route.BASIC, settings.getCurrentRoute());
diff --git a/chrome/test/data/webui/settings/settings_subpage_browsertest.js b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
index bbc22ec..5b90649 100644
--- a/chrome/test/data/webui/settings/settings_subpage_browsertest.js
+++ b/chrome/test/data/webui/settings/settings_subpage_browsertest.js
@@ -96,7 +96,13 @@
   __proto__: SettingsSubPageBrowserTest.prototype,
 };
 
-TEST_F('SettingsBasicSubPageBrowserTest', 'SubPages', function() {
+// Failing on ChromiumOS dbg. https://crbug.com/709442
+GEN('#if defined(OS_CHROMEOS) && !defined(NDEBUG)');
+GEN('#define MAYBE_SubPages DISABLED_SubPages');
+GEN('#else');
+GEN('#define MAYBE_SubPages SubPages');
+GEN('#endif');
+TEST_F('SettingsBasicSubPageBrowserTest', 'MAYBE_SubPages', function() {
   suiteSetup(this.verifySubPagesHidden_.bind(this));
   suite('Basic', this.testSubPages.bind(this));
   mocha.run();
diff --git a/chrome/test/data/webui/settings/site_settings_page_browsertest.js b/chrome/test/data/webui/settings/site_settings_page_browsertest.js
index 0b8256e2..0b9ade75 100644
--- a/chrome/test/data/webui/settings/site_settings_page_browsertest.js
+++ b/chrome/test/data/webui/settings/site_settings_page_browsertest.js
@@ -15,7 +15,13 @@
   __proto__: SettingsPageBrowserTest.prototype,
 };
 
-TEST_F('SettingsSiteSettingsPageBrowserTest', 'labels', function() {
+// Failing on ChromiumOS dbg. https://crbug.com/709442
+GEN('#if defined(OS_CHROMEOS) && !defined(NDEBUG)');
+GEN('#define MAYBE_labels DISABLED_labels');
+GEN('#else');
+GEN('#define MAYBE_labels labels');
+GEN('#endif');
+TEST_F('SettingsSiteSettingsPageBrowserTest', 'MAYBE_labels', function() {
   suite('Site settings page', function() {
     var ui;
 
diff --git a/chromecast/common/media/cast_media_client.cc b/chromecast/common/media/cast_media_client.cc
index 4a0b696e..4103d59 100644
--- a/chromecast/common/media/cast_media_client.cc
+++ b/chromecast/common/media/cast_media_client.cc
@@ -75,6 +75,15 @@
 bool CastMediaClient::IsSupportedAudioConfig(
     const ::media::AudioConfig& config) {
 #if defined(OS_ANDROID)
+  AudioCodec codec = ToCastAudioCodec(config.codec);
+
+  // No ATV device we know of has (E)AC3 decoder, so it relies on the audio sink
+  // device.
+  if (codec == kCodecEAC3)
+    return MediaCapabilities::HdmiSinkSupportsEAC3();
+  if (codec == kCodecAC3)
+    return MediaCapabilities::HdmiSinkSupportsAC3();
+
   // TODO(sanfin): Implement this for Android.
   return true;
 #else
diff --git a/chromecast/media/cma/base/decoder_config_adapter.cc b/chromecast/media/cma/base/decoder_config_adapter.cc
index cf4fdee..c69341c5 100644
--- a/chromecast/media/cma/base/decoder_config_adapter.cc
+++ b/chromecast/media/cma/base/decoder_config_adapter.cc
@@ -307,19 +307,21 @@
   base::Optional<::media::HDRMetadata> hdr_metadata = config.hdr_metadata();
   if (hdr_metadata) {
     video_config.have_hdr_metadata = true;
-    video_config.hdr_metadata.max_cll = hdr_metadata->max_cll;
-    video_config.hdr_metadata.max_fall = hdr_metadata->max_fall;
+    video_config.hdr_metadata.max_content_light_level =
+        hdr_metadata->max_content_light_level;
+    video_config.hdr_metadata.max_frame_average_light_level =
+        hdr_metadata->max_frame_average_light_level;
 
     const auto& mm1 = hdr_metadata->mastering_metadata;
     auto& mm2 = video_config.hdr_metadata.mastering_metadata;
-    mm2.primary_r_chromaticity_x = mm1.primary_r_chromaticity_x;
-    mm2.primary_r_chromaticity_y = mm1.primary_r_chromaticity_y;
-    mm2.primary_g_chromaticity_x = mm1.primary_g_chromaticity_x;
-    mm2.primary_g_chromaticity_y = mm1.primary_g_chromaticity_y;
-    mm2.primary_b_chromaticity_x = mm1.primary_b_chromaticity_x;
-    mm2.primary_b_chromaticity_y = mm1.primary_b_chromaticity_y;
-    mm2.white_point_chromaticity_x = mm1.white_point_chromaticity_x;
-    mm2.white_point_chromaticity_y = mm1.white_point_chromaticity_y;
+    mm2.primary_r_chromaticity_x = mm1.primary_r.x();
+    mm2.primary_r_chromaticity_y = mm1.primary_r.y();
+    mm2.primary_g_chromaticity_x = mm1.primary_g.x();
+    mm2.primary_g_chromaticity_y = mm1.primary_g.y();
+    mm2.primary_b_chromaticity_x = mm1.primary_b.x();
+    mm2.primary_b_chromaticity_y = mm1.primary_b.y();
+    mm2.white_point_chromaticity_x = mm1.white_point.x();
+    mm2.white_point_chromaticity_y = mm1.white_point.y();
     mm2.luminance_max = mm1.luminance_max;
     mm2.luminance_min = mm1.luminance_min;
   }
diff --git a/chromecast/public/media/decoder_config.h b/chromecast/public/media/decoder_config.h
index 5b3b9db..d0fe46d 100644
--- a/chromecast/public/media/decoder_config.h
+++ b/chromecast/public/media/decoder_config.h
@@ -212,7 +212,7 @@
 };
 
 // Table 4
-enum class MatrixID : int8_t {
+enum class MatrixID : uint8_t {
   RGB = 0,
   BT709 = 1,
   UNSPECIFIED = 2,
@@ -265,8 +265,8 @@
 // HDR metadata common for HDR10 and WebM/VP9-based HDR formats.
 struct HDRMetadata {
   MasteringMetadata mastering_metadata;
-  unsigned max_cll = 0;
-  unsigned max_fall = 0;
+  unsigned max_content_light_level = 0;
+  unsigned max_frame_average_light_level = 0;
 
   HDRMetadata();
   HDRMetadata(const HDRMetadata& rhs);
diff --git a/chromeos/dbus/cryptohome_client.cc b/chromeos/dbus/cryptohome_client.cc
index 19a7728..23e6f31 100644
--- a/chromeos/dbus/cryptohome_client.cc
+++ b/chromeos/dbus/cryptohome_client.cc
@@ -889,48 +889,36 @@
 
   void GetBootAttribute(const cryptohome::GetBootAttributeRequest& request,
                         const ProtobufMethodCallback& callback) override {
-    const char* method_name = cryptohome::kCryptohomeGetBootAttribute;
-    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
-
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendProtoAsArrayOfBytes(request);
-
-    proxy_->CallMethod(&method_call,
-                       kTpmDBusTimeoutMs ,
-                       base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  callback));
+    CallCryptohomeMethod(cryptohome::kCryptohomeGetBootAttribute, request,
+                         callback);
   }
 
   void SetBootAttribute(const cryptohome::SetBootAttributeRequest& request,
                         const ProtobufMethodCallback& callback) override {
-    const char* method_name = cryptohome::kCryptohomeSetBootAttribute;
-    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
-
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendProtoAsArrayOfBytes(request);
-
-    proxy_->CallMethod(&method_call,
-                       kTpmDBusTimeoutMs ,
-                       base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  callback));
+    CallCryptohomeMethod(cryptohome::kCryptohomeSetBootAttribute, request,
+                         callback);
   }
 
   void FlushAndSignBootAttributes(
       const cryptohome::FlushAndSignBootAttributesRequest& request,
       const ProtobufMethodCallback& callback) override {
-    const char* method_name = cryptohome::kCryptohomeFlushAndSignBootAttributes;
-    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
+    CallCryptohomeMethod(cryptohome::kCryptohomeFlushAndSignBootAttributes,
+                         request, callback);
+  }
 
-    dbus::MessageWriter writer(&method_call);
-    writer.AppendProtoAsArrayOfBytes(request);
+  void RemoveFirmwareManagementParametersFromTpm(
+      const cryptohome::RemoveFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) override {
+    CallCryptohomeMethod(
+        cryptohome::kCryptohomeRemoveFirmwareManagementParameters, request,
+        callback);
+  }
 
-    proxy_->CallMethod(&method_call,
-                       kTpmDBusTimeoutMs ,
-                       base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
-                                  weak_ptr_factory_.GetWeakPtr(),
-                                  callback));
+  void SetFirmwareManagementParametersInTpm(
+      const cryptohome::SetFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) override {
+    CallCryptohomeMethod(cryptohome::kCryptohomeSetFirmwareManagementParameters,
+                         request, callback);
   }
 
   void MigrateToDircrypto(const cryptohome::Identification& cryptohome_id,
@@ -1246,6 +1234,23 @@
         signal << " failed.";
   }
 
+  // Makes an asynchronous D-Bus call, using cryptohome interface. |method_name|
+  // is the name of the method to be called. |request| is the specific request
+  // for the method, including the data required to be sent. |callback| is
+  // invoked when the response is received.
+  void CallCryptohomeMethod(const std::string& method_name,
+                            const google::protobuf::MessageLite& request,
+                            const ProtobufMethodCallback& callback) {
+    dbus::MethodCall method_call(cryptohome::kCryptohomeInterface, method_name);
+
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendProtoAsArrayOfBytes(request);
+
+    proxy_->CallMethod(&method_call, kTpmDBusTimeoutMs,
+                       base::Bind(&CryptohomeClientImpl::OnBaseReplyMethod,
+                                  weak_ptr_factory_.GetWeakPtr(), callback));
+  }
+
   dbus::ObjectProxy* proxy_;
   std::unique_ptr<BlockingMethodCaller> blocking_method_caller_;
   AsyncCallStatusHandler async_call_status_handler_;
diff --git a/chromeos/dbus/cryptohome_client.h b/chromeos/dbus/cryptohome_client.h
index 94fe05b2b..3d0480a9 100644
--- a/chromeos/dbus/cryptohome_client.h
+++ b/chromeos/dbus/cryptohome_client.h
@@ -28,8 +28,10 @@
 class GetBootAttributeRequest;
 class GetKeyDataRequest;
 class MountRequest;
+class RemoveFirmwareManagementParametersRequest;
 class RemoveKeyRequest;
 class SetBootAttributeRequest;
+class SetFirmwareManagementParametersRequest;
 class UpdateKeyRequest;
 
 class Identification;
@@ -578,6 +580,20 @@
       const cryptohome::AuthorizationRequest& auth,
       const VoidDBusMethodCallback& callback) = 0;
 
+  // Asynchronously calls RemoveFirmwareManagementParameters method. |callback|
+  // is called after method call, and with reply protobuf.
+  virtual void RemoveFirmwareManagementParametersFromTpm(
+      const cryptohome::RemoveFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) = 0;
+
+  // Asynchronously calls SetFirmwareManagementParameters method. |callback|
+  // is called after method call, and with reply protobuf. |request| contains
+  // the flags to be set. SetFirmwareManagementParameters creates the firmware
+  // management parameters in TPM and sets flags included in the request.
+  virtual void SetFirmwareManagementParametersInTpm(
+      const cryptohome::SetFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) = 0;
+
  protected:
   // Create() should be used instead.
   CryptohomeClient();
diff --git a/chromeos/dbus/fake_cryptohome_client.cc b/chromeos/dbus/fake_cryptohome_client.cc
index e145cca..7da79896 100644
--- a/chromeos/dbus/fake_cryptohome_client.cc
+++ b/chromeos/dbus/fake_cryptohome_client.cc
@@ -603,6 +603,18 @@
       this, &FakeCryptohomeClient::OnDircryptoMigrationProgressUpdated);
 }
 
+void FakeCryptohomeClient::RemoveFirmwareManagementParametersFromTpm(
+    const cryptohome::RemoveFirmwareManagementParametersRequest& request,
+    const ProtobufMethodCallback& callback) {
+  ReturnProtobufMethodCallback(cryptohome::BaseReply(), callback);
+}
+
+void FakeCryptohomeClient::SetFirmwareManagementParametersInTpm(
+    const cryptohome::SetFirmwareManagementParametersRequest& request,
+    const ProtobufMethodCallback& callback) {
+  ReturnProtobufMethodCallback(cryptohome::BaseReply(), callback);
+}
+
 void FakeCryptohomeClient::SetServiceIsAvailable(bool is_available) {
   service_is_available_ = is_available;
   if (is_available) {
diff --git a/chromeos/dbus/fake_cryptohome_client.h b/chromeos/dbus/fake_cryptohome_client.h
index 70f6ae3..0cf31de 100644
--- a/chromeos/dbus/fake_cryptohome_client.h
+++ b/chromeos/dbus/fake_cryptohome_client.h
@@ -201,6 +201,12 @@
                           const VoidDBusMethodCallback& callback) override;
   void SetDircryptoMigrationProgressHandler(
       const DircryptoMigrationProgessHandler& handler) override;
+  void RemoveFirmwareManagementParametersFromTpm(
+      const cryptohome::RemoveFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) override;
+  void SetFirmwareManagementParametersInTpm(
+      const cryptohome::SetFirmwareManagementParametersRequest& request,
+      const ProtobufMethodCallback& callback) override;
 
   // Changes the behavior of WaitForServiceToBeAvailable(). This method runs
   // pending callbacks if is_available is true.
diff --git a/chromeos/dbus/mock_cryptohome_client.h b/chromeos/dbus/mock_cryptohome_client.h
index 5c5bfb0..e6248794 100644
--- a/chromeos/dbus/mock_cryptohome_client.h
+++ b/chromeos/dbus/mock_cryptohome_client.h
@@ -228,6 +228,14 @@
                     const VoidDBusMethodCallback& callback));
   MOCK_METHOD1(SetDircryptoMigrationProgressHandler,
                void(const DircryptoMigrationProgessHandler& handler));
+  MOCK_METHOD2(
+      RemoveFirmwareManagementParametersFromTpm,
+      void(const cryptohome::RemoveFirmwareManagementParametersRequest& request,
+           const ProtobufMethodCallback& callback));
+  MOCK_METHOD2(
+      SetFirmwareManagementParametersInTpm,
+      void(const cryptohome::SetFirmwareManagementParametersRequest& request,
+           const ProtobufMethodCallback& callback));
 };
 
 }  // namespace chromeos
diff --git a/components/cronet/android/test/javaperftests/android_rndis_forwarder.py b/components/cronet/android/test/javaperftests/android_rndis_forwarder.py
index f4f5364e..4c5a3b0 100644
--- a/components/cronet/android/test/javaperftests/android_rndis_forwarder.py
+++ b/components/cronet/android/test/javaperftests/android_rndis_forwarder.py
@@ -28,7 +28,9 @@
     self._host_ip = rndis_configurator.host_ip
     self._original_dns = None, None, None
     self._RedirectPorts()
-    self._OverrideDns()
+    # The netd commands fail on Lollipop and newer releases, but aren't
+    # necessary as DNS isn't used.
+    # self._OverrideDns()
     self._OverrideDefaultGateway()
     # Need to override routing policy again since call to setifdns
     # sometimes resets policy table
diff --git a/components/cronet/android/test/javaperftests/run.py b/components/cronet/android/test/javaperftests/run.py
index fc6203b..1e815e2 100755
--- a/components/cronet/android/test/javaperftests/run.py
+++ b/components/cronet/android/test/javaperftests/run.py
@@ -21,6 +21,15 @@
 3. cronet_perf_test_apk has been built for the Android device, e.g. via:
      ./components/cronet/tools/cr_cronet.py gn -r
      ninja -C out/Release cronet_perf_test_apk
+4. If "sudo ufw status" doesn't say "Status: inactive", run "sudo ufw disable".
+5. sudo apt-get install lighttpd
+6. If the usb0 interface on the host keeps losing it's IPv4 address
+   (WaitFor(HasHostAddress) will keep failing), NetworkManager may need to be
+   told to leave usb0 alone with these commands:
+     sudo bash -c "printf \"\\n[keyfile]\
+         \\nunmanaged-devices=interface-name:usb0\\n\" \
+         >> /etc/NetworkManager/NetworkManager.conf"
+     sudo service network-manager restart
 
 Invocation:
 ./run.py
@@ -224,7 +233,7 @@
     # the redirect done in build/android/pylib/pexpect.py.
     # pylint: disable=no-member
     self._process = pexpect.spawn(QUIC_SERVER,
-                                  ['--quic_in_memory_cache_dir=%s' %
+                                  ['--quic_response_cache_dir=%s' %
                                       self._quic_server_doc_root,
                                    '--certificate_file=%s' % QUIC_CERT,
                                    '--key_file=%s' % QUIC_KEY,
diff --git a/components/data_use_measurement/content/content_url_request_classifier.cc b/components/data_use_measurement/content/content_url_request_classifier.cc
index d40af5d..d3354c45 100644
--- a/components/data_use_measurement/content/content_url_request_classifier.cc
+++ b/components/data_use_measurement/content/content_url_request_classifier.cc
@@ -156,4 +156,12 @@
           base::HistogramBase::kUmaTargetedHistogramFlag));
 }
 
+bool ContentURLRequestClassifier::IsFavIconRequest(
+    const net::URLRequest& request) const {
+  const content::ResourceRequestInfo* request_info =
+      content::ResourceRequestInfo::ForRequest(&request);
+  return request_info && request_info->GetResourceType() ==
+                             content::ResourceType::RESOURCE_TYPE_FAVICON;
+}
+
 }  // namespace data_use_measurement
diff --git a/components/data_use_measurement/content/content_url_request_classifier.h b/components/data_use_measurement/content/content_url_request_classifier.h
index 1b7bbb0..0726bb967 100644
--- a/components/data_use_measurement/content/content_url_request_classifier.h
+++ b/components/data_use_measurement/content/content_url_request_classifier.h
@@ -20,13 +20,12 @@
  private:
   // UrlRequestClassifier:
   bool IsUserRequest(const net::URLRequest& request) const override;
-
   DataUseUserData::DataUseContentType GetContentType(
       const net::URLRequest& request,
       const net::HttpResponseHeaders& response_headers) const override;
-
   void RecordPageTransitionUMA(uint64_t page_transition,
                                int64_t received_bytes) const override;
+  bool IsFavIconRequest(const net::URLRequest& request) const override;
 };
 
 }  // namespace data_use_measurement
diff --git a/components/data_use_measurement/core/data_use_measurement.cc b/components/data_use_measurement/core/data_use_measurement.cc
index 0e2794c..fcebeb8 100644
--- a/components/data_use_measurement/core/data_use_measurement.cc
+++ b/components/data_use_measurement/core/data_use_measurement.cc
@@ -17,6 +17,7 @@
 #include "net/base/network_change_notifier.h"
 #include "net/base/upload_data_stream.h"
 #include "net/http/http_response_headers.h"
+#include "net/http/http_status_code.h"
 #include "net/url_request/url_request.h"
 
 #if defined(OS_ANDROID)
@@ -65,6 +66,17 @@
 }
 #endif
 
+void RecordFavIconDataUse(const net::URLRequest& request) {
+  UMA_HISTOGRAM_COUNTS_100000(
+      "DataUse.FavIcon.Downstream",
+      request.was_cached() ? 0 : request.GetTotalReceivedBytes());
+  if (request.status().is_success() &&
+      request.GetResponseCode() != net::HTTP_OK) {
+    UMA_HISTOGRAM_COUNTS_100000("DataUse.FavIcon.Downstream.Non200Response",
+                                request.GetTotalReceivedBytes());
+  }
+}
+
 }  // namespace
 
 DataUseMeasurement::DataUseMeasurement(
@@ -132,6 +144,8 @@
   // TODO(rajendrant): May not be needed when http://crbug/651957 is fixed.
   UpdateDataUsePrefs(request);
   ReportServicesMessageSizeUMA(request);
+  if (url_request_classifier_->IsFavIconRequest(request))
+    RecordFavIconDataUse(request);
 }
 
 void DataUseMeasurement::OnHeadersReceived(
@@ -173,6 +187,8 @@
 #if defined(OS_ANDROID)
   MaybeRecordNetworkBytesOS();
 #endif
+  if (url_request_classifier_->IsFavIconRequest(request))
+    RecordFavIconDataUse(request);
 }
 
 void DataUseMeasurement::ReportDataUseUMA(const net::URLRequest& request,
diff --git a/components/data_use_measurement/core/data_use_measurement_unittest.cc b/components/data_use_measurement/core/data_use_measurement_unittest.cc
index 52887d9..63cc5e1 100644
--- a/components/data_use_measurement/core/data_use_measurement_unittest.cc
+++ b/components/data_use_measurement/core/data_use_measurement_unittest.cc
@@ -58,6 +58,10 @@
   void RecordPageTransitionUMA(uint64_t page_transition,
                                int64_t received_bytes) const override {}
 
+  bool IsFavIconRequest(const net::URLRequest& request) const override {
+    return false;
+  }
+
  private:
   DataUseUserData::DataUseContentType content_type_;
 };
diff --git a/components/data_use_measurement/core/data_use_network_delegate_unittest.cc b/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
index b5f3fd9..ef5b810 100644
--- a/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
+++ b/components/data_use_measurement/core/data_use_network_delegate_unittest.cc
@@ -42,6 +42,10 @@
 
   void RecordPageTransitionUMA(uint64_t page_transition,
                                int64_t received_bytes) const override {}
+
+  bool IsFavIconRequest(const net::URLRequest& request) const override {
+    return false;
+  }
 };
 
 class TestDataUseAscriber : public DataUseAscriber {
diff --git a/components/data_use_measurement/core/url_request_classifier.h b/components/data_use_measurement/core/url_request_classifier.h
index 27b3d72..d8239b3f 100644
--- a/components/data_use_measurement/core/url_request_classifier.h
+++ b/components/data_use_measurement/core/url_request_classifier.h
@@ -34,6 +34,9 @@
   // Records the page transition histograms.
   virtual void RecordPageTransitionUMA(uint64_t page_transition,
                                        int64_t received_bytes) const = 0;
+
+  // Returns true if |request| is fetching a favicon.
+  virtual bool IsFavIconRequest(const net::URLRequest& request) const = 0;
 };
 
 }  // namespace data_use_measurement
diff --git a/components/display_compositor/BUILD.gn b/components/display_compositor/BUILD.gn
index 57b7cdd..41cc049 100644
--- a/components/display_compositor/BUILD.gn
+++ b/components/display_compositor/BUILD.gn
@@ -17,6 +17,8 @@
     "gl_helper_readback_support.h",
     "gl_helper_scaling.cc",
     "gl_helper_scaling.h",
+    "host_shared_bitmap_manager.cc",
+    "host_shared_bitmap_manager.h",
   ]
 
   configs += [ "//build/config/compiler:no_size_t_to_int_warning" ]
@@ -73,6 +75,7 @@
   testonly = true
   sources = [
     "buffer_queue_unittest.cc",
+    "host_shared_bitmap_manager_unittest.cc",
   ]
 
   if (!use_aura && !is_mac) {
diff --git a/components/display_compositor/DEPS b/components/display_compositor/DEPS
index eca1d9b8..5f7809b 100644
--- a/components/display_compositor/DEPS
+++ b/components/display_compositor/DEPS
@@ -2,6 +2,7 @@
   "+cc/base",
   "+cc/ipc",
   "+cc/output",
+  "+cc/resources",
   "+cc/surfaces",
   "+cc/test",
   "+gpu/GLES2",
@@ -10,6 +11,7 @@
   "+gpu/ipc",
   "+gpu/ipc/common",
   "+mojo/public/cpp/bindings",
+  "+mojo/public/cpp/system",
   "+third_party/khronos/GLES2",
   "+third_party/skia",
   "+ui/display",
diff --git a/content/common/host_shared_bitmap_manager.cc b/components/display_compositor/host_shared_bitmap_manager.cc
similarity index 88%
rename from content/common/host_shared_bitmap_manager.cc
rename to components/display_compositor/host_shared_bitmap_manager.cc
index f424ac3..cd914f5 100644
--- a/content/common/host_shared_bitmap_manager.cc
+++ b/components/display_compositor/host_shared_bitmap_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/common/host_shared_bitmap_manager.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 
 #include <stdint.h>
 
@@ -13,12 +13,13 @@
 #include "base/memory/ptr_util.h"
 #include "base/memory/ref_counted.h"
 #include "base/strings/string_number_conversions.h"
+#include "base/strings/stringprintf.h"
 #include "base/trace_event/process_memory_dump.h"
 #include "build/build_config.h"
-#include "content/common/view_messages.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace content {
+namespace display_compositor {
 
 class BitmapData : public base::RefCountedThreadSafe<BitmapData> {
  public:
@@ -62,14 +63,29 @@
 
 HostSharedBitmapManagerClient::HostSharedBitmapManagerClient(
     HostSharedBitmapManager* manager)
-    : manager_(manager) {
-}
+    : manager_(manager), binding_(this) {}
 
 HostSharedBitmapManagerClient::~HostSharedBitmapManagerClient() {
   for (const auto& id : owned_bitmaps_)
     manager_->ChildDeletedSharedBitmap(id);
 }
 
+void HostSharedBitmapManagerClient::Bind(
+    cc::mojom::SharedBitmapManagerAssociatedRequest request) {
+  binding_.Bind(std::move(request));
+}
+
+void HostSharedBitmapManagerClient::DidAllocateSharedBitmap(
+    mojo::ScopedSharedBufferHandle buffer,
+    const cc::SharedBitmapId& id) {
+  base::SharedMemoryHandle memory_handle;
+  size_t size;
+  MojoResult result = mojo::UnwrapSharedMemoryHandle(
+      std::move(buffer), &memory_handle, &size, NULL);
+  DCHECK_EQ(result, MOJO_RESULT_OK);
+  this->ChildAllocatedSharedBitmap(size, memory_handle, id);
+}
+
 void HostSharedBitmapManagerClient::AllocateSharedBitmapForChild(
     base::ProcessHandle process_handle,
     size_t buffer_size,
@@ -93,7 +109,7 @@
   }
 }
 
-void HostSharedBitmapManagerClient::ChildDeletedSharedBitmap(
+void HostSharedBitmapManagerClient::DidDeleteSharedBitmap(
     const cc::SharedBitmapId& id) {
   manager_->ChildDeletedSharedBitmap(id);
   {
@@ -225,7 +241,7 @@
     *shared_memory_handle = base::SharedMemory::NULLHandle();
     return;
   }
- data->memory->Close();
+  data->memory->Close();
 }
 
 void HostSharedBitmapManager::ChildDeletedSharedBitmap(
@@ -245,4 +261,4 @@
   handle_map_.erase(id);
 }
 
-}  // namespace content
+}  // namespace display_compositor
diff --git a/content/common/host_shared_bitmap_manager.h b/components/display_compositor/host_shared_bitmap_manager.h
similarity index 73%
rename from content/common/host_shared_bitmap_manager.h
rename to components/display_compositor/host_shared_bitmap_manager.h
index d2abcd4..05706c3 100644
--- a/content/common/host_shared_bitmap_manager.h
+++ b/components/display_compositor/host_shared_bitmap_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_COMMON_HOST_SHARED_BITMAP_MANAGER_H_
-#define CONTENT_COMMON_HOST_SHARED_BITMAP_MANAGER_H_
+#ifndef COMPONENTS_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
+#define COMPONENTS_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
 
 #include <stddef.h>
 
@@ -18,8 +18,10 @@
 #include "base/memory/shared_memory.h"
 #include "base/synchronization/lock.h"
 #include "base/trace_event/memory_dump_provider.h"
+#include "cc/ipc/shared_bitmap_manager.mojom.h"
 #include "cc/resources/shared_bitmap_manager.h"
-#include "content/common/content_export.h"
+#include "components/display_compositor/display_compositor_export.h"
+#include "mojo/public/cpp/bindings/associated_binding.h"
 
 namespace BASE_HASH_NAMESPACE {
 template <>
@@ -30,15 +32,23 @@
 };
 }  // namespace BASE_HASH_NAMESPACE
 
-namespace content {
+namespace display_compositor {
 class BitmapData;
 class HostSharedBitmapManager;
 
-class CONTENT_EXPORT HostSharedBitmapManagerClient {
+class DISPLAY_COMPOSITOR_EXPORT HostSharedBitmapManagerClient
+    : NON_EXPORTED_BASE(public cc::mojom::SharedBitmapManager) {
  public:
   explicit HostSharedBitmapManagerClient(HostSharedBitmapManager* manager);
 
-  ~HostSharedBitmapManagerClient();
+  ~HostSharedBitmapManagerClient() override;
+
+  void Bind(cc::mojom::SharedBitmapManagerAssociatedRequest request);
+
+  // cc::mojom::SharedBitmapManager overrides:
+  void DidAllocateSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
+                               const cc::SharedBitmapId& id) override;
+  void DidDeleteSharedBitmap(const cc::SharedBitmapId& id) override;
 
   void AllocateSharedBitmapForChild(
       base::ProcessHandle process_handle,
@@ -48,10 +58,10 @@
   void ChildAllocatedSharedBitmap(size_t buffer_size,
                                   const base::SharedMemoryHandle& handle,
                                   const cc::SharedBitmapId& id);
-  void ChildDeletedSharedBitmap(const cc::SharedBitmapId& id);
 
  private:
   HostSharedBitmapManager* manager_;
+  mojo::AssociatedBinding<cc::mojom::SharedBitmapManager> binding_;
 
   // Lock must be held around access to owned_bitmaps_.
   base::Lock lock_;
@@ -60,7 +70,7 @@
   DISALLOW_COPY_AND_ASSIGN(HostSharedBitmapManagerClient);
 };
 
-class CONTENT_EXPORT HostSharedBitmapManager
+class DISPLAY_COMPOSITOR_EXPORT HostSharedBitmapManager
     : public cc::SharedBitmapManager,
       public base::trace_event::MemoryDumpProvider {
  public:
@@ -99,13 +109,13 @@
 
   mutable base::Lock lock_;
 
-  typedef base::hash_map<cc::SharedBitmapId, scoped_refptr<BitmapData> >
+  typedef base::hash_map<cc::SharedBitmapId, scoped_refptr<BitmapData>>
       BitmapMap;
   BitmapMap handle_map_;
 
   DISALLOW_COPY_AND_ASSIGN(HostSharedBitmapManager);
 };
 
-}  // namespace content
+}  // namespace display_compositor
 
-#endif  // CONTENT_COMMON_HOST_SHARED_BITMAP_MANAGER_H_
+#endif  // COMPONENTS_DISPLAY_COMPOSITOR_HOST_SHARED_BITMAP_MANAGER_H_
diff --git a/content/common/host_shared_bitmap_manager_unittest.cc b/components/display_compositor/host_shared_bitmap_manager_unittest.cc
similarity index 95%
rename from content/common/host_shared_bitmap_manager_unittest.cc
rename to components/display_compositor/host_shared_bitmap_manager_unittest.cc
index e1f5feeb..e3ea524 100644
--- a/content/common/host_shared_bitmap_manager_unittest.cc
+++ b/components/display_compositor/host_shared_bitmap_manager_unittest.cc
@@ -5,10 +5,10 @@
 #include <stddef.h>
 #include <string.h>
 
-#include "content/common/host_shared_bitmap_manager.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
+namespace display_compositor {
 namespace {
 
 class HostSharedBitmapManagerTest : public testing::Test {
@@ -66,7 +66,7 @@
   EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
             0);
 
-  client.ChildDeletedSharedBitmap(id);
+  client.DidDeleteSharedBitmap(id);
 
   memset(bitmap->memory(), 0, size_in_bytes);
 
@@ -98,7 +98,7 @@
   EXPECT_TRUE(
       memcmp(bitmap->memory(), shared_bitmap->pixels(), size_in_bytes) == 0);
 
-  client.ChildDeletedSharedBitmap(id);
+  client.DidDeleteSharedBitmap(id);
 }
 
 TEST_F(HostSharedBitmapManagerTest, RemoveProcess) {
@@ -158,8 +158,8 @@
   ASSERT_TRUE(shared_bitmap.get() != NULL);
   EXPECT_EQ(memcmp(shared_bitmap->pixels(), bitmap->memory(), size_in_bytes),
             0);
-  client.ChildDeletedSharedBitmap(id);
+  client.DidDeleteSharedBitmap(id);
 }
 
 }  // namespace
-}  // namespace content
+}  // namespace display_compositor
diff --git a/components/favicon/core/large_icon_service.cc b/components/favicon/core/large_icon_service.cc
index e44e5c7..0153ddf 100644
--- a/components/favicon/core/large_icon_service.cc
+++ b/components/favicon/core/large_icon_service.cc
@@ -32,7 +32,7 @@
 const char kGoogleServerV2RequestFormat[] =
     "https://t0.gstatic.com/faviconV2?"
     "client=chrome&drop_404_icon=true&size=%d&min_size=%d&max_size=%d&"
-    "fallback_opts=TYPE,SIZE&url=%s";
+    "fallback_opts=TYPE,SIZE,URL&url=%s";
 const int kGoogleServerV2MaxSizeInPixel = 128;
 const int kGoogleServerV2DesiredSizeInPixel = 64;
 
diff --git a/components/favicon/core/large_icon_service_unittest.cc b/components/favicon/core/large_icon_service_unittest.cc
index 849a185..e7f17ff 100644
--- a/components/favicon/core/large_icon_service_unittest.cc
+++ b/components/favicon/core/large_icon_service_unittest.cc
@@ -131,7 +131,7 @@
 TEST_F(LargeIconServiceTest, ShouldGetFromGoogleServer) {
   const GURL kExpectedServerUrl(
       "https://t0.gstatic.com/faviconV2?client=chrome&drop_404_icon=true"
-      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE"
+      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE,URL"
       "&url=http://www.example.com/");
 
   EXPECT_CALL(mock_favicon_service_, UnableToDownloadFavicon(_)).Times(0);
@@ -158,7 +158,7 @@
   const GURL kDummyUrlWithQuery("http://www.example.com?foo=1");
   const GURL kExpectedServerUrl(
       "https://t0.gstatic.com/faviconV2?client=chrome&drop_404_icon=true"
-      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE"
+      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE,URL"
       "&url=http://www.example.com/");
 
   EXPECT_CALL(*mock_image_fetcher_,
@@ -197,7 +197,7 @@
   const GURL kDummyUrlWithQuery("http://www.example.com?foo=1");
   const GURL kExpectedServerUrl(
       "https://t0.gstatic.com/faviconV2?client=chrome&drop_404_icon=true"
-      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE"
+      "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE,URL"
       "&url=http://www.example.com/");
 
   EXPECT_CALL(mock_favicon_service_, SetLastResortFavicons(_, _, _, _, _))
@@ -223,7 +223,7 @@
       mock_favicon_service_,
       WasUnableToDownloadFavicon(GURL(
           "https://t0.gstatic.com/faviconV2?client=chrome&drop_404_icon=true"
-          "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE"
+          "&size=64&min_size=42&max_size=128&fallback_opts=TYPE,SIZE,URL"
           "&url=http://www.example.com/")))
       .WillByDefault(Return(true));
 
diff --git a/components/google/core/browser/google_url_tracker.cc b/components/google/core/browser/google_url_tracker.cc
index 4f4a9a4..e16f32776 100644
--- a/components/google/core/browser/google_url_tracker.cc
+++ b/components/google/core/browser/google_url_tracker.cc
@@ -74,14 +74,9 @@
   registry->RegisterStringPref(prefs::kLastPromptedGoogleURL, std::string());
 }
 
-void GoogleURLTracker::RequestServerCheck(bool force) {
-  // If this instance already has a fetcher, SetNeedToFetch() is unnecessary,
-  // and changing |already_fetched_| is wrong.
-  if (!fetcher_) {
-    if (force)
-      already_fetched_ = false;
+void GoogleURLTracker::RequestServerCheck() {
+  if (!fetcher_)
     SetNeedToFetch();
-  }
 }
 
 std::unique_ptr<GoogleURLTracker::Subscription>
diff --git a/components/google/core/browser/google_url_tracker.h b/components/google/core/browser/google_url_tracker.h
index 27496a6a..5d0ba62a 100644
--- a/components/google/core/browser/google_url_tracker.h
+++ b/components/google/core/browser/google_url_tracker.h
@@ -65,13 +65,11 @@
   const GURL& google_url() const { return google_url_; }
 
   // Requests that the tracker perform a server check to update the Google URL
-  // as necessary.  If |force| is false, this will happen at most once per
-  // network change, not sooner than five seconds after startup (checks
-  // requested before that time will occur then; checks requested afterwards
-  // will occur immediately, if no other checks have been made during this run).
-  // If |force| is true, and the tracker has already performed any requested
-  // check, it will check again.
-  void RequestServerCheck(bool force);
+  // as necessary.  This will happen at most once per network change, not sooner
+  // than five seconds after startup (checks requested before that time will
+  // occur then; checks requested afterwards will occur immediately, if no other
+  // checks have been made during this run).
+  void RequestServerCheck();
 
   std::unique_ptr<Subscription> RegisterCallback(
       const OnGoogleURLUpdatedCallback& cb);
diff --git a/components/image_fetcher/core/image_data_fetcher.cc b/components/image_fetcher/core/image_data_fetcher.cc
index 56078f7..dd88027b 100644
--- a/components/image_fetcher/core/image_data_fetcher.cc
+++ b/components/image_fetcher/core/image_data_fetcher.cc
@@ -89,6 +89,7 @@
 
   RequestMetadata metadata;
   if (success && source->GetResponseHeaders()) {
+    metadata.http_response_headers = source->GetResponseHeaders();
     source->GetResponseHeaders()->GetMimeType(&metadata.mime_type);
     metadata.http_response_code = source->GetResponseHeaders()->response_code();
     success &= (metadata.http_response_code == net::HTTP_OK);
diff --git a/components/image_fetcher/core/image_data_fetcher_unittest.cc b/components/image_fetcher/core/image_data_fetcher_unittest.cc
index 2ece1b0..90887dd 100644
--- a/components/image_fetcher/core/image_data_fetcher_unittest.cc
+++ b/components/image_fetcher/core/image_data_fetcher_unittest.cc
@@ -63,9 +63,17 @@
       GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
                                   base::Unretained(this)));
 
+  std::string raw_header =
+      "HTTP/1.1 200 OK\n"
+      "Content-type: image/png\n\n";
+  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(raw_header));
+
   RequestMetadata expected_metadata;
   expected_metadata.mime_type = std::string("image/png");
   expected_metadata.http_response_code = net::HTTP_OK;
+  expected_metadata.http_response_headers = headers.get();
   EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData),
                                         expected_metadata));
 
@@ -80,14 +88,6 @@
       net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
   test_url_fetcher->SetResponseString(kURLResponseData);
   test_url_fetcher->set_response_code(net::HTTP_OK);
-
-  std::string raw_header =
-      "HTTP/1.1 200 OK\n"
-      "Content-type: image/png\n\n";
-  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
-  scoped_refptr<net::HttpResponseHeaders> headers(
-      new net::HttpResponseHeaders(raw_header));
-
   test_url_fetcher->set_response_headers(headers);
 
   // Call the URLFetcher delegate to continue the test.
@@ -99,10 +99,18 @@
       GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
                                   base::Unretained(this)));
 
+  std::string raw_header =
+      "HTTP/1.1 200 OK\n"
+      "Content-type: image/png\n\n";
+  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(raw_header));
+
   RequestMetadata expected_metadata;
   expected_metadata.mime_type = std::string("image/png");
   expected_metadata.http_response_code = net::HTTP_OK;
   expected_metadata.from_http_cache = true;
+  expected_metadata.http_response_headers = headers.get();
   EXPECT_CALL(*this, OnImageDataFetched(std::string(kURLResponseData),
                                         expected_metadata));
 
@@ -114,14 +122,6 @@
   test_url_fetcher->SetResponseString(kURLResponseData);
   test_url_fetcher->set_response_code(net::HTTP_OK);
   test_url_fetcher->set_was_cached(true);
-
-  std::string raw_header =
-      "HTTP/1.1 200 OK\n"
-      "Content-type: image/png\n\n";
-  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
-  scoped_refptr<net::HttpResponseHeaders> headers(
-      new net::HttpResponseHeaders(raw_header));
-
   test_url_fetcher->set_response_headers(headers);
 
   // Call the URLFetcher delegate to continue the test.
@@ -133,9 +133,17 @@
       GURL(kImageURL), base::Bind(&ImageDataFetcherTest::OnImageDataFetched,
                                   base::Unretained(this)));
 
+  std::string raw_header =
+      "HTTP/1.1 404 Not Found\n"
+      "Content-type: image/png\n\n";
+  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
+  scoped_refptr<net::HttpResponseHeaders> headers(
+      new net::HttpResponseHeaders(raw_header));
+
   RequestMetadata expected_metadata;
   expected_metadata.mime_type = std::string("image/png");
   expected_metadata.http_response_code = net::HTTP_NOT_FOUND;
+  expected_metadata.http_response_headers = headers.get();
   // For 404, expect an empty result even though correct image data is sent.
   EXPECT_CALL(*this, OnImageDataFetched(std::string(), expected_metadata));
 
@@ -145,14 +153,6 @@
   test_url_fetcher->set_status(
       net::URLRequestStatus(net::URLRequestStatus::SUCCESS, net::OK));
   test_url_fetcher->SetResponseString(kURLResponseData);
-
-  std::string raw_header =
-      "HTTP/1.1 404 Not Found\n"
-      "Content-type: image/png\n\n";
-  std::replace(raw_header.begin(), raw_header.end(), '\n', '\0');
-  scoped_refptr<net::HttpResponseHeaders> headers(
-      new net::HttpResponseHeaders(raw_header));
-
   test_url_fetcher->set_response_headers(headers);
 
   // Call the URLFetcher delegate to continue the test.
diff --git a/components/image_fetcher/core/request_metadata.cc b/components/image_fetcher/core/request_metadata.cc
index b9b31d5..2fa6e54 100644
--- a/components/image_fetcher/core/request_metadata.cc
+++ b/components/image_fetcher/core/request_metadata.cc
@@ -4,15 +4,35 @@
 
 #include "components/image_fetcher/core/request_metadata.h"
 
+#include "net/http/http_response_headers.h"
+
 namespace image_fetcher {
 
+namespace {
+
+bool HttpHeadersEqual(const net::HttpResponseHeaders* lhs,
+                      const net::HttpResponseHeaders* rhs) {
+  if (!lhs && !rhs) {
+    return true;  // Equal if both nullptr.
+  }
+  if (!lhs || !rhs) {
+    return false;  // We cannot compare the content if one of them is nullptr.
+  }
+  return lhs->raw_headers() == rhs->raw_headers();
+}
+
+}  // namespace
+
 RequestMetadata::RequestMetadata()
-    : http_response_code(RESPONSE_CODE_INVALID), from_http_cache(false) {}
+    : http_response_code(RESPONSE_CODE_INVALID),
+      from_http_cache(false),
+      http_response_headers(nullptr) {}
 
 bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs) {
   return lhs.mime_type == rhs.mime_type &&
          lhs.http_response_code == rhs.http_response_code &&
-         lhs.from_http_cache == rhs.from_http_cache;
+         lhs.from_http_cache == rhs.from_http_cache &&
+         HttpHeadersEqual(lhs.http_response_headers, rhs.http_response_headers);
 }
 
 bool operator!=(const RequestMetadata& lhs, const RequestMetadata& rhs) {
diff --git a/components/image_fetcher/core/request_metadata.h b/components/image_fetcher/core/request_metadata.h
index fbff28c1..f625c82 100644
--- a/components/image_fetcher/core/request_metadata.h
+++ b/components/image_fetcher/core/request_metadata.h
@@ -24,6 +24,12 @@
   std::string mime_type;
   int http_response_code;
   bool from_http_cache;
+
+  // Is guaranteed to be a valid pointer only when the callback is called. The
+  // headers may get deleted right after the callback finishes.
+  // TODO(jkrcal): Remove |mime_type| and |http_response_code| as it all can be
+  // read from HttpResponseHeaders.
+  net::HttpResponseHeaders* http_response_headers;
 };
 
 bool operator==(const RequestMetadata& lhs, const RequestMetadata& rhs);
diff --git a/components/image_fetcher/core/request_metadata_unittest.cc b/components/image_fetcher/core/request_metadata_unittest.cc
index 5337340..dcbe60c 100644
--- a/components/image_fetcher/core/request_metadata_unittest.cc
+++ b/components/image_fetcher/core/request_metadata_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "components/image_fetcher/core/request_metadata.h"
 
+#include "base/memory/ref_counted.h"
+#include "net/http/http_response_headers.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace image_fetcher {
diff --git a/components/ntp_snippets/pref_names.cc b/components/ntp_snippets/pref_names.cc
index 271c8900..fb5582a 100644
--- a/components/ntp_snippets/pref_names.cc
+++ b/components/ntp_snippets/pref_names.cc
@@ -13,11 +13,11 @@
 
 const char kSnippetLastFetchAttempt[] = "ntp_snippets.last_fetch_attempt";
 
-const char kSnippetSoftFetchingIntervalOnUsageEvent[] =
-    "ntp_snippets.soft_fetching_interval_on_usage_event";
+const char kSnippetSoftFetchingIntervalWifi[] =
+    "ntp_snippets.soft_fetching_interval_wifi";
 
-const char kSnippetSoftFetchingIntervalOnNtpOpened[] =
-    "ntp_snippets.soft_fetching_interval_on_ntp_opened";
+const char kSnippetSoftFetchingIntervalFallback[] =
+    "ntp_snippets.soft_fetching_interval_fallback";
 
 const char kSnippetPersistentFetchingIntervalWifi[] =
     "ntp_snippets.fetching_interval_wifi";
diff --git a/components/ntp_snippets/pref_names.h b/components/ntp_snippets/pref_names.h
index ee60486..9826eb9e 100644
--- a/components/ntp_snippets/pref_names.h
+++ b/components/ntp_snippets/pref_names.h
@@ -20,11 +20,12 @@
 extern const char kSnippetLastFetchAttempt[];
 // The pref name for the currently applied minimal interval between two
 // successive soft background fetches that react to user activity (such as
-// opening Chrome).
-extern const char kSnippetSoftFetchingIntervalOnUsageEvent[];
+// opening Chrome) when there is a WiFi connectivity.
+extern const char kSnippetSoftFetchingIntervalWifi[];
 // The pref name for the currently applied minimal interval between two
-// successive soft brackground fetches when the New Tab Page is opened.
-extern const char kSnippetSoftFetchingIntervalOnNtpOpened[];
+// successive soft background fetches that react to user activity (such as
+// opening Chrome) when there is no WiFi connectivity.
+extern const char kSnippetSoftFetchingIntervalFallback[];
 
 // The pref name for the currently-scheduled background fetching interval when
 // there is WiFi connectivity.
diff --git a/components/ntp_snippets/remote/persistent_scheduler.h b/components/ntp_snippets/remote/persistent_scheduler.h
index 70a27e7..080ceb0 100644
--- a/components/ntp_snippets/remote/persistent_scheduler.h
+++ b/components/ntp_snippets/remote/persistent_scheduler.h
@@ -35,6 +35,10 @@
   // the scheduling was successful.
   virtual bool Unschedule() = 0;
 
+  // TODO(jkrcal): Get this information exposed in the platform-independent
+  // net::NetworkChangeNotifier and remove this function.
+  virtual bool IsOnUnmeteredConnection() = 0;
+
  protected:
   PersistentScheduler() = default;
 
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
index 253d415..699c3b5a 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -22,6 +22,7 @@
 #include "components/ntp_snippets/user_classifier.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
+#include "net/base/network_change_notifier.h"
 
 namespace ntp_snippets {
 
@@ -30,16 +31,23 @@
 // The FetchingInterval enum specifies overlapping time intervals that are used
 // for scheduling the next remote suggestion fetch. Therefore a timer is created
 // for each interval. Initially all the timers are started at the same time.
-// Fetches are
-// only performed when certain conditions associated with the intervals are
-// met. If a fetch failed, then only the corresponding timer is reset. The
-// other timers are not touched.
-// TODO(markusheintz): Describe the individual intervals.
+// Fetches are only performed when conditions associated with the intervals are
+// met:
+//   - A "soft" fetch is performed only at a moment when the user actively uses
+//  Chrome after the interval has elapsed and causes a trigger that is currently
+//  enabled while
+//   - a "persistent" fetch is initiated automatically by the OS after the
+//  interval elapses.
+//   - A "wifi" fetch is performed only when the user is on a connection that
+//   the OS classifies as unmetered while
+//   - a "fallback" fetch happens on any connection.
+// If a fetch failed, then only the corresponding timer is reset. The other
+// timers are not touched.
 enum class FetchingInterval {
   PERSISTENT_FALLBACK,
   PERSISTENT_WIFI,
-  SOFT_ON_USAGE_EVENT,
-  SOFT_ON_NTP_OPENED,
+  SOFT_FALLBACK,
+  SOFT_WIFI,
   COUNT
 };
 
@@ -51,7 +59,7 @@
 // the arrays can be overridden using different variation parameters.
 const double kDefaultFetchingIntervalHoursRareNtpUser[] = {48.0, 24.0, 12.0,
                                                            6.0};
-const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 6.0, 2.0,
+const double kDefaultFetchingIntervalHoursActiveNtpUser[] = {24.0, 6.0, 4.0,
                                                              2.0};
 const double kDefaultFetchingIntervalHoursActiveSuggestionsConsumer[] = {
     24.0, 6.0, 2.0, 1.0};
@@ -61,18 +69,18 @@
 const char* kFetchingIntervalParamNameRareNtpUser[] = {
     "fetching_interval_hours-fallback-rare_ntp_user",
     "fetching_interval_hours-wifi-rare_ntp_user",
-    "soft_fetching_interval_hours-active-rare_ntp_user",
-    "soft_on_ntp_opened_interval_hours-rare_ntp_user"};
+    "soft_fetching_interval_hours-fallback-rare_ntp_user",
+    "soft_fetching_interval_hours-wifi-rare_ntp_user"};
 const char* kFetchingIntervalParamNameActiveNtpUser[] = {
     "fetching_interval_hours-fallback-active_ntp_user",
     "fetching_interval_hours-wifi-active_ntp_user",
-    "soft_fetching_interval_hours-active-active_ntp_user",
-    "soft_on_ntp_opened_interval_hours-active_ntp_user"};
+    "soft_fetching_interval_hours-fallback-active_ntp_user",
+    "soft_fetching_interval_hours-wifi-active_ntp_user"};
 const char* kFetchingIntervalParamNameActiveSuggestionsConsumer[] = {
     "fetching_interval_hours-fallback-active_suggestions_consumer",
     "fetching_interval_hours-wifi-active_suggestions_consumer",
-    "soft_fetching_interval_hours-active-active_suggestions_consumer",
-    "soft_on_ntp_opened_interval_hours-active_suggestions_consumer"};
+    "soft_fetching_interval_hours-fallback-active_suggestions_consumer",
+    "soft_fetching_interval_hours-wifi-active_suggestions_consumer"};
 
 static_assert(
     static_cast<unsigned int>(FetchingInterval::COUNT) ==
@@ -98,6 +106,11 @@
 
 const int kBlockBackgroundFetchesMinutesAfterClearingHistory = 30;
 
+const char kSnippetSoftFetchingIntervalOnUsageEventDeprecated[] =
+    "ntp_snippets.soft_fetching_interval_on_usage_event";
+const char kSnippetSoftFetchingIntervalOnNtpOpenedDeprecated[] =
+    "ntp_snippets.soft_fetching_interval_on_ntp_opened";
+
 // Returns the time interval to use for scheduling remote suggestion fetches for
 // the given interval and user_class.
 base::TimeDelta GetDesiredFetchingInterval(
@@ -183,8 +196,8 @@
     const FetchingSchedule& other) const {
   return interval_persistent_wifi == other.interval_persistent_wifi &&
          interval_persistent_fallback == other.interval_persistent_fallback &&
-         interval_soft_on_usage_event == other.interval_soft_on_usage_event &&
-         interval_soft_on_ntp_opened == other.interval_soft_on_ntp_opened;
+         interval_soft_wifi == other.interval_soft_wifi &&
+         interval_soft_fallback == other.interval_soft_fallback;
 }
 
 bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::operator!=(
@@ -195,8 +208,7 @@
 bool RemoteSuggestionsSchedulerImpl::FetchingSchedule::is_empty() const {
   return interval_persistent_wifi.is_zero() &&
          interval_persistent_fallback.is_zero() &&
-         interval_soft_on_usage_event.is_zero() &&
-         interval_soft_on_ntp_opened.is_zero();
+         interval_soft_wifi.is_zero() && interval_soft_fallback.is_zero();
 }
 
 // The TriggerType enum specifies values for the events that can trigger
@@ -241,6 +253,10 @@
   DCHECK(user_classifier);
   DCHECK(profile_prefs);
 
+  // Cleanup procedure in M59. Remove for M62.
+  profile_prefs_->ClearPref(kSnippetSoftFetchingIntervalOnUsageEventDeprecated);
+  profile_prefs_->ClearPref(kSnippetSoftFetchingIntervalOnNtpOpenedDeprecated);
+
   LoadLastFetchingSchedule();
 }
 
@@ -252,10 +268,14 @@
   registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalWifi, 0);
   registry->RegisterInt64Pref(prefs::kSnippetPersistentFetchingIntervalFallback,
                               0);
-  registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
-                              0);
+  registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalWifi, 0);
+  registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalFallback, 0);
   registry->RegisterInt64Pref(prefs::kSnippetLastFetchAttempt, 0);
-  registry->RegisterInt64Pref(prefs::kSnippetSoftFetchingIntervalOnNtpOpened,
+
+  // Cleanup procedure in M59. Remove for M62.
+  registry->RegisterInt64Pref(
+      kSnippetSoftFetchingIntervalOnUsageEventDeprecated, 0);
+  registry->RegisterInt64Pref(kSnippetSoftFetchingIntervalOnNtpOpenedDeprecated,
                               0);
 }
 
@@ -308,35 +328,26 @@
 }
 
 void RemoteSuggestionsSchedulerImpl::OnPersistentSchedulerWakeUp() {
-  RefetchInTheBackgroundIfEnabled(TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
+  RefetchInTheBackgroundIfAppropriate(
+      TriggerType::PERSISTENT_SCHEDULER_WAKE_UP);
 }
 
 void RemoteSuggestionsSchedulerImpl::OnBrowserForegrounded() {
   // TODO(jkrcal): Consider that this is called whenever we open or return to an
   // Activity. Therefore, keep work light for fast start up calls.
-  if (!ShouldRefetchInTheBackgroundNow(TriggerType::BROWSER_FOREGROUNDED)) {
-    return;
-  }
-
-  RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_FOREGROUNDED);
+  RefetchInTheBackgroundIfAppropriate(TriggerType::BROWSER_FOREGROUNDED);
 }
 
 void RemoteSuggestionsSchedulerImpl::OnBrowserColdStart() {
-  // TODO(fhorschig|jkrcal): Consider that work here must be kept light for fast
+  // TODO(jkrcal): Consider that work here must be kept light for fast
   // cold start ups.
-  if (!ShouldRefetchInTheBackgroundNow(TriggerType::BROWSER_COLD_START)) {
-    return;
-  }
-
-  RefetchInTheBackgroundIfEnabled(TriggerType::BROWSER_COLD_START);
+  RefetchInTheBackgroundIfAppropriate(TriggerType::BROWSER_COLD_START);
 }
 
 void RemoteSuggestionsSchedulerImpl::OnNTPOpened() {
-  if (!ShouldRefetchInTheBackgroundNow(TriggerType::NTP_OPENED)) {
-    return;
-  }
-
-  RefetchInTheBackgroundIfEnabled(TriggerType::NTP_OPENED);
+  // TODO(jkrcal): Consider that this is called whenever we open an NTP.
+  // Therefore, keep work light for fast start up calls.
+  RefetchInTheBackgroundIfAppropriate(TriggerType::NTP_OPENED);
 }
 
 void RemoteSuggestionsSchedulerImpl::StartScheduling() {
@@ -384,10 +395,10 @@
       GetDesiredFetchingInterval(FetchingInterval::PERSISTENT_WIFI, user_class);
   schedule.interval_persistent_fallback = GetDesiredFetchingInterval(
       FetchingInterval::PERSISTENT_FALLBACK, user_class);
-  schedule.interval_soft_on_usage_event = GetDesiredFetchingInterval(
-      FetchingInterval::SOFT_ON_USAGE_EVENT, user_class);
-  schedule.interval_soft_on_ntp_opened = GetDesiredFetchingInterval(
-      FetchingInterval::SOFT_ON_NTP_OPENED, user_class);
+  schedule.interval_soft_wifi =
+      GetDesiredFetchingInterval(FetchingInterval::SOFT_WIFI, user_class);
+  schedule.interval_soft_fallback =
+      GetDesiredFetchingInterval(FetchingInterval::SOFT_FALLBACK, user_class);
 
   return schedule;
 }
@@ -398,11 +409,10 @@
   schedule_.interval_persistent_fallback =
       base::TimeDelta::FromInternalValue(profile_prefs_->GetInt64(
           prefs::kSnippetPersistentFetchingIntervalFallback));
-  schedule_.interval_soft_on_usage_event =
-      base::TimeDelta::FromInternalValue(profile_prefs_->GetInt64(
-          prefs::kSnippetSoftFetchingIntervalOnUsageEvent));
-  schedule_.interval_soft_on_ntp_opened = base::TimeDelta::FromInternalValue(
-      profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalOnNtpOpened));
+  schedule_.interval_soft_wifi = base::TimeDelta::FromInternalValue(
+      profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalWifi));
+  schedule_.interval_soft_fallback = base::TimeDelta::FromInternalValue(
+      profile_prefs_->GetInt64(prefs::kSnippetSoftFetchingIntervalFallback));
 }
 
 void RemoteSuggestionsSchedulerImpl::StoreFetchingSchedule() {
@@ -412,29 +422,24 @@
   profile_prefs_->SetInt64(
       prefs::kSnippetPersistentFetchingIntervalFallback,
       schedule_.interval_persistent_fallback.ToInternalValue());
-  profile_prefs_->SetInt64(
-      prefs::kSnippetSoftFetchingIntervalOnUsageEvent,
-      schedule_.interval_soft_on_usage_event.ToInternalValue());
-  profile_prefs_->SetInt64(
-      prefs::kSnippetSoftFetchingIntervalOnNtpOpened,
-      schedule_.interval_soft_on_ntp_opened.ToInternalValue());
+  profile_prefs_->SetInt64(prefs::kSnippetSoftFetchingIntervalWifi,
+                           schedule_.interval_soft_wifi.ToInternalValue());
+  profile_prefs_->SetInt64(prefs::kSnippetSoftFetchingIntervalFallback,
+                           schedule_.interval_soft_fallback.ToInternalValue());
 }
 
-void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfEnabled(
+void RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundIfAppropriate(
     TriggerType trigger) {
+  if (background_fetch_in_progress_) {
+    return;
+  }
+
   if (BackgroundFetchesDisabled(trigger)) {
     return;
   }
 
-  UMA_HISTOGRAM_ENUMERATION(
-      "NewTabPage.ContentSuggestions.BackgroundFetchTrigger",
-      static_cast<int>(trigger), static_cast<int>(TriggerType::COUNT));
-
-  RefetchInTheBackground();
-}
-
-void RemoteSuggestionsSchedulerImpl::RefetchInTheBackground() {
-  if (background_fetch_in_progress_) {
+  if (trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP &&
+      !ShouldRefetchInTheBackgroundNow()) {
     return;
   }
 
@@ -442,34 +447,30 @@
     return;
   }
 
+  UMA_HISTOGRAM_ENUMERATION(
+      "NewTabPage.ContentSuggestions.BackgroundFetchTrigger",
+      static_cast<int>(trigger), static_cast<int>(TriggerType::COUNT));
+
   background_fetch_in_progress_ = true;
   provider_->RefetchInTheBackground(base::Bind(
       &RemoteSuggestionsSchedulerImpl::RefetchInTheBackgroundFinished,
       base::Unretained(this)));
 }
 
-bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow(
-    TriggerType trigger) {
+bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow() {
   const base::Time last_fetch_attempt_time = base::Time::FromInternalValue(
       profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt));
-  base::Time first_allowed_fetch_time;
-  switch (trigger) {
-    case TriggerType::NTP_OPENED:
-      first_allowed_fetch_time =
-          last_fetch_attempt_time + schedule_.interval_soft_on_ntp_opened;
-      break;
-    case TriggerType::BROWSER_FOREGROUNDED:
-    case TriggerType::BROWSER_COLD_START:
-      first_allowed_fetch_time =
-          last_fetch_attempt_time + schedule_.interval_soft_on_usage_event;
-      break;
-    case TriggerType::PERSISTENT_SCHEDULER_WAKE_UP:
-    case TriggerType::COUNT:
-      NOTREACHED();
-      break;
-  }
-  base::Time now = clock_->Now();
 
+  // If we have no persistent scheduler to ask, err on the side of caution.
+  bool wifi = false;
+  if (persistent_scheduler_) {
+    wifi = persistent_scheduler_->IsOnUnmeteredConnection();
+  }
+
+  base::Time first_allowed_fetch_time =
+      last_fetch_attempt_time +
+      (wifi ? schedule_.interval_soft_wifi : schedule_.interval_soft_fallback);
+  base::Time now = clock_->Now();
   return background_fetches_allowed_after_ <= now &&
          first_allowed_fetch_time <= now;
 }
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
index a8baaf0..7fe94d6 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
@@ -61,7 +61,8 @@
   void OnNTPOpened() override;
 
  private:
-  // Abstract description of the fetching schedule.
+  // Abstract description of the fetching schedule. See the enum
+  // FetchingInterval for more documentation.
   struct FetchingSchedule {
     static FetchingSchedule Empty();
     bool operator==(const FetchingSchedule& other) const;
@@ -70,8 +71,8 @@
 
     base::TimeDelta interval_persistent_wifi;
     base::TimeDelta interval_persistent_fallback;
-    base::TimeDelta interval_soft_on_usage_event;
-    base::TimeDelta interval_soft_on_ntp_opened;
+    base::TimeDelta interval_soft_wifi;
+    base::TimeDelta interval_soft_fallback;
   };
 
   enum class TriggerType;
@@ -86,15 +87,13 @@
   // schedule.
   void StopScheduling();
 
-  // Trigger a background refetch for the given |trigger| if enabled.
-  void RefetchInTheBackgroundIfEnabled(TriggerType trigger);
-
-  // Trigger the background refetch.
-  void RefetchInTheBackground();
+  // Trigger a background refetch for the given |trigger| if enabled and if the
+  // timing is appropriate for another fetch.
+  void RefetchInTheBackgroundIfAppropriate(TriggerType trigger);
 
   // Checks whether it is time to perform a soft background fetch, according to
   // |schedule|.
-  bool ShouldRefetchInTheBackgroundNow(TriggerType trigger);
+  bool ShouldRefetchInTheBackgroundNow();
 
   // Returns whether background fetching (for the given |trigger|) is disabled.
   bool BackgroundFetchesDisabled(TriggerType trigger) const;
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
index 3ad4455..220863e1 100644
--- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
+++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl_unittest.cc
@@ -32,6 +32,7 @@
 #include "components/prefs/testing_pref_service.h"
 #include "components/variations/variations_params_manager.h"
 #include "components/web_resource/web_resource_pref_names.h"
+#include "net/base/network_change_notifier.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -65,6 +66,7 @@
                bool(base::TimeDelta period_wifi,
                     base::TimeDelta period_fallback));
   MOCK_METHOD0(Unschedule, bool());
+  MOCK_METHOD0(IsOnUnmeteredConnection, bool());
 };
 
 // TODO(jkrcal): Move into its own library to reuse in other unit-tests?
@@ -118,6 +120,9 @@
     // registers this pref and replace the call in browser_process_impl.cc & in
     // eula_accepted_notifier_unittest.cc with the new static function.
     local_state_.registry()->RegisterBooleanPref(::prefs::kEulaAccepted, false);
+    // By default pretend we are on WiFi.
+    EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
+        .WillRepeatedly(Return(true));
     ResetProvider();
   }
 
@@ -473,7 +478,7 @@
         .WillOnce(SaveArg<0>(&signal_fetch_done));
     // Rescheduling after a succesful fetch.
     EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
-    // The second call to NTPOpened 2hrs later again results in a fetch.
+    // The second call to NTPOpened 4hrs later again results in a fetch.
     EXPECT_CALL(*provider(), RefetchInTheBackground(_));
   }
 
@@ -482,8 +487,8 @@
   // Make the first soft fetch successful.
   scheduler()->OnBrowserForegrounded();
   signal_fetch_done.Run(Status::Success());
-  // Open NTP again after 2hrs.
-  test_clock()->Advance(base::TimeDelta::FromHours(2));
+  // Open NTP again after 4hrs.
+  test_clock()->Advance(base::TimeDelta::FromHours(4));
   scheduler()->OnBrowserForegrounded();
 }
 
@@ -568,7 +573,8 @@
   DeactivateProvider();
 }
 
-TEST_F(RemoteSuggestionsSchedulerImplTest, ReschedulesWhenWifiParamChanges) {
+TEST_F(RemoteSuggestionsSchedulerImplTest,
+       ReschedulesWhenPersistentWifiParamChanges) {
   EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
   ActivateProvider();
 
@@ -581,7 +587,7 @@
 }
 
 TEST_F(RemoteSuggestionsSchedulerImplTest,
-       ReschedulesWhenFallbackParamChanges) {
+       ReschedulesWhenPersistentFallbackParamChanges) {
   EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
   ActivateProvider();
 
@@ -595,13 +601,13 @@
 }
 
 TEST_F(RemoteSuggestionsSchedulerImplTest,
-       ReschedulesWhenOnUsageEventParamChanges) {
+       ReschedulesWhenSoftWifiParamChanges) {
   EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
   ActivateProvider();
 
   // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
   // null. Change the on usage interval for this class.
-  SetVariationParameter("soft_fetching_interval_hours-active-active_ntp_user",
+  SetVariationParameter("soft_fetching_interval_hours-wifi-active_ntp_user",
                         "1.5");
 
   // Schedule() should get called for the second time after params have changed.
@@ -609,85 +615,143 @@
 }
 
 TEST_F(RemoteSuggestionsSchedulerImplTest,
-       ReschedulesWhenOnNtpOpenedParamChanges) {
+       ReschedulesWhenSoftFallbackParamChanges) {
   EXPECT_CALL(*persistent_scheduler(), Schedule(_, _)).Times(2);
   ActivateProvider();
 
   // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
   // null. Change the fallback interval for this class.
-  SetVariationParameter("soft_on_ntp_opened_interval_hours-active_ntp_user",
+  SetVariationParameter("soft_fetching_interval_hours-fallback-active_ntp_user",
                         "1.5");
 
   // Schedule() should get called for the second time after params have changed.
   ActivateProvider();
 }
 
-TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForNtpOpenedTrigger) {
-  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
-  {
-    InSequence s;
-    // Initial scheduling after being enabled.
-    EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
-    // The first call to NTPOpened results in a fetch.
-    EXPECT_CALL(*provider(), RefetchInTheBackground(_))
-        .WillOnce(SaveArg<0>(&signal_fetch_done));
-    // Rescheduling after a succesful fetch.
-    EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
-    // The third call to NTPOpened 35min later again results in a fetch.
-    EXPECT_CALL(*provider(), RefetchInTheBackground(_));
-  }
+TEST_F(RemoteSuggestionsSchedulerImplTest, FetchIntervalForSoftTriggerOnWifi) {
+  // Pretend we are on WiFi (already done in ctor, we make it explicit here).
+  EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
+      .WillRepeatedly(Return(true));
+  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 2h time
+  // interval by default for soft background fetches on WiFi.
 
+  // Initial scheduling after being enabled.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
   ActivateProvider();
 
+  // The first call to NTPOpened results in a fetch.
+  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_))
+      .WillOnce(SaveArg<0>(&signal_fetch_done));
   scheduler()->OnNTPOpened();
+  // Rescheduling after a succesful fetch.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
   signal_fetch_done.Run(Status::Success());
 
-  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 2h time
-  // interval by default for soft backgroudn fetches on ntp open events.
-
-  // Open NTP again after 20min. This time no fetch is executed.
+  // Open NTP again after too short delay. This time no fetch is executed.
   test_clock()->Advance(base::TimeDelta::FromMinutes(20));
   scheduler()->OnNTPOpened();
 
-  // Open NTP again after 101min (121min since first opened). Since the default
-  // time interval has passed refetch again.
-  test_clock()->Advance(base::TimeDelta::FromMinutes(101));
+  // Open NTP after another delay, now together long enough to issue a fetch.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(100));
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_));
   scheduler()->OnNTPOpened();
 }
 
 TEST_F(RemoteSuggestionsSchedulerImplTest,
-       OverrideFetchIntervalForNtpOpenedTrigger) {
+       OverrideFetchIntervalForSoftTriggerOnWifi) {
+  // Pretend we are on WiFi (already done in ctor, we make it explicit here).
+  EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
+      .WillRepeatedly(Return(true));
   // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
   // null. Change the on usage interval for this class from 2h to 30min.
-  SetVariationParameter("soft_on_ntp_opened_interval_hours-active_ntp_user",
+  SetVariationParameter("soft_fetching_interval_hours-wifi-active_ntp_user",
                         "0.5");
 
-  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
-  {
-    InSequence s;
-    // Initial scheduling after being enabled.
-    EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
-    // The first call to NTPOpened results in a fetch.
-    EXPECT_CALL(*provider(), RefetchInTheBackground(_))
-        .WillOnce(SaveArg<0>(&signal_fetch_done));
-    // Rescheduling after a succesful fetch.
-    EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
-    // The third call to NTPOpened 35min later again results in a fetch.
-    EXPECT_CALL(*provider(), RefetchInTheBackground(_));
-  }
-
+  // Initial scheduling after being enabled.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
   ActivateProvider();
 
+  // The first call to NTPOpened results in a fetch.
+  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_))
+      .WillOnce(SaveArg<0>(&signal_fetch_done));
   scheduler()->OnNTPOpened();
+  // Rescheduling after a succesful fetch.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
   signal_fetch_done.Run(Status::Success());
 
-  // Open NTP again after 20min. No fetch request is issues since the 30 min
-  // time interval has not passed yet.
+  // Open NTP again after too short delay. This time no fetch is executed.
   test_clock()->Advance(base::TimeDelta::FromMinutes(20));
   scheduler()->OnNTPOpened();
 
-  // Open NTP again after 15min (35min since first opened)
-  test_clock()->Advance(base::TimeDelta::FromMinutes(15));
+  // Open NTP after another delay, now together long enough to issue a fetch.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(10));
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_));
+  scheduler()->OnNTPOpened();
+}
+
+TEST_F(RemoteSuggestionsSchedulerImplTest,
+       FetchIntervalForSoftTriggerOnFallback) {
+  // Pretend we are not on wifi -> fallback connection.
+  EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
+      .WillRepeatedly(Return(false));
+  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER which uses a 4h time
+  // interval by default for soft background fetches not on WiFi.
+
+  // Initial scheduling after being enabled.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+  ActivateProvider();
+
+  // The first call to NTPOpened results in a fetch.
+  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_))
+      .WillOnce(SaveArg<0>(&signal_fetch_done));
+  scheduler()->OnNTPOpened();
+  // Rescheduling after a succesful fetch.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+  signal_fetch_done.Run(Status::Success());
+
+  // Open NTP again after too short delay. This time no fetch is executed.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(180));
+  scheduler()->OnNTPOpened();
+
+  // Open NTP after another delay, now together long enough to issue a fetch.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(60));
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_));
+  scheduler()->OnNTPOpened();
+}
+
+TEST_F(RemoteSuggestionsSchedulerImplTest,
+       OverrideFetchIntervalForSoftTriggerOnFallback) {
+  // Pretend we are not on wifi -> fallback connection.
+  EXPECT_CALL(*persistent_scheduler(), IsOnUnmeteredConnection())
+      .WillRepeatedly(Return(false));
+  // UserClassifier defaults to UserClass::ACTIVE_NTP_USER if PrefService is
+  // null. Change the on usage interval for this class from 4h to 30min.
+  SetVariationParameter("soft_fetching_interval_hours-fallback-active_ntp_user",
+                        "0.5");
+
+  // Initial scheduling after being enabled.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+  ActivateProvider();
+
+  // The first call to NTPOpened results in a fetch.
+  RemoteSuggestionsProvider::FetchStatusCallback signal_fetch_done;
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_))
+      .WillOnce(SaveArg<0>(&signal_fetch_done));
+  scheduler()->OnNTPOpened();
+  // Rescheduling after a succesful fetch.
+  EXPECT_CALL(*persistent_scheduler(), Schedule(_, _));
+  signal_fetch_done.Run(Status::Success());
+
+  // Open NTP again after too short delay. This time no fetch is executed.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(20));
+  scheduler()->OnNTPOpened();
+
+  // Open NTP after another delay, now together long enough to issue a fetch.
+  test_clock()->Advance(base::TimeDelta::FromMinutes(10));
+  EXPECT_CALL(*provider(), RefetchInTheBackground(_));
   scheduler()->OnNTPOpened();
 }
 
diff --git a/components/payments/core/BUILD.gn b/components/payments/core/BUILD.gn
index e330013..1eecb8e7 100644
--- a/components/payments/core/BUILD.gn
+++ b/components/payments/core/BUILD.gn
@@ -49,6 +49,7 @@
     "address_normalizer_unittest.cc",
     "basic_card_response_unittest.cc",
     "currency_formatter_unittest.cc",
+    "journey_logger_unittest.cc",
     "payment_address_unittest.cc",
     "payment_method_data_unittest.cc",
     "payment_request_data_util_unittest.cc",
@@ -61,6 +62,7 @@
     "//base/test:test_support",
     "//components/autofill/core/browser",
     "//components/autofill/core/browser:test_support",
+    "//testing/gmock",
     "//testing/gtest",
     "//third_party/libaddressinput:test_support",
   ]
diff --git a/components/payments/core/journey_logger.cc b/components/payments/core/journey_logger.cc
index a62e2e2..d8731c0 100644
--- a/components/payments/core/journey_logger.cc
+++ b/components/payments/core/journey_logger.cc
@@ -175,7 +175,7 @@
   if (was_show_called_)
     effect_on_show |= CMP_SHOW_DID_SHOW;
   if (could_make_payment_)
-    effect_on_show |= CMP_SHOW_COULD_MAKE_PAYMENT_;
+    effect_on_show |= CMP_SHOW_COULD_MAKE_PAYMENT;
 
   UMA_HISTOGRAM_ENUMERATION("PaymentRequest.CanMakePayment.Used.EffectOnShow",
                             effect_on_show, CMP_SHOW_MAX);
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h
index 326704e..67401c1 100644
--- a/components/payments/core/journey_logger.h
+++ b/components/payments/core/journey_logger.h
@@ -50,7 +50,7 @@
   // Payment Request is shown to the user.
   static const int CMP_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW = 0;
   static const int CMP_SHOW_DID_SHOW = 1 << 0;
-  static const int CMP_SHOW_COULD_MAKE_PAYMENT_ = 1 << 1;
+  static const int CMP_SHOW_COULD_MAKE_PAYMENT = 1 << 1;
   static const int CMP_SHOW_MAX = 4;
 
   explicit JourneyLogger(bool is_incognito);
diff --git a/components/payments/core/journey_logger_unittest.cc b/components/payments/core/journey_logger_unittest.cc
new file mode 100644
index 0000000..75372e9
--- /dev/null
+++ b/components/payments/core/journey_logger_unittest.cc
@@ -0,0 +1,661 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/payments/core/journey_logger.h"
+
+#include "base/test/histogram_tester.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using testing::ContainerEq;
+
+namespace payments {
+
+// Tests the canMakePayment stats for the case where the merchant does not use
+// it and does not show the PaymentRequest to the user.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentNotCalled_NoShow) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
+                                     1);
+
+  // There should be no completion stats since PR was not shown to the user
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix(
+          "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests the canMakePayment stats for the case where the merchant does not use
+// it and the transaction is aborted.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndUserAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The merchant does not query CanMakePayment, show the PaymentRequest and the
+  // user aborts it.
+  logger.SetShowCalled();
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
+                                     1);
+
+  // There should be a record for an abort when CanMakePayment is not used but
+  // the PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant does not use
+// it and the transaction is aborted.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndOtherAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The merchant does not query CanMakePayment, show the PaymentRequest and
+  // there is an abort not initiated by the user.
+  logger.SetShowCalled();
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
+                                     1);
+
+  // There should be a record for an abort when CanMakePayment is not used but
+  // the PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant does not use
+// it and the transaction is completed.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentNotCalled_ShowAndComplete) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The merchant does not query CanMakePayment, show the PaymentRequest and the
+  // user completes it.
+  logger.SetShowCalled();
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
+                                     1);
+
+  // There should be a record for an abort when CanMakePayment is not used but
+  // the PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns false and show is not called.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseAndNoShow) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetCanMakePaymentValue(false);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being false and not
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_COULD_NOT_MAKE_PAYMENT_AND_DID_NOT_SHOW, 1);
+
+  // There should be no completion stats since PR was not shown to the user.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix(
+          "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns true and show is not called.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueAndNoShow) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetCanMakePaymentValue(true);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being true and not
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT, 1);
+
+  // There should be no completion stats since PR was not shown to the user.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix(
+          "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns false, show is called but the transaction is aborted by the user.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndUserAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(false);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being false and
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+  // There should be a record for an abort when CanMakePayment is false but the
+  // PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns false, show is called but the transaction is aborted.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndOtherAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(false);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being false and
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+  // There should be a record for an abort when CanMakePayment is false but the
+  // PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns false, show is called and the transaction is completed.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_FalseShowAndComplete) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(false);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being false and
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW, 1);
+
+  // There should be a record for an completion when CanMakePayment is false but
+  // the PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.FalseWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns true, show is called but the transaction is aborted by the user.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndUserAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(true);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being true and not
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW |
+          JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+      1);
+  // There should be a record for an abort when CanMakePayment is true and the
+  // PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns true, show is called but the transaction is aborted.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndOtherAbort) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(true);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being true and not
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW |
+          JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+      1);
+  // There should be a record for an abort when CanMakePayment is true and the
+  // PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+}
+
+// Tests the canMakePayment stats for the case where the merchant uses it,
+// returns true, show is called and the transaction is completed.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePaymentCalled_TrueShowAndComplete) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(true);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+
+  // The CanMakePayment effect on show should be recorded as being true and not
+  // shown.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.EffectOnShow",
+      JourneyLogger::CMP_SHOW_DID_SHOW |
+          JourneyLogger::CMP_SHOW_COULD_MAKE_PAYMENT,
+      1);
+  // There should be a record for a completion when CanMakePayment is true and
+  // the PR is shown to the user.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+}
+
+// Tests the canMakePayment metrics are not logged if the Payment Request was
+// done in an incognito tab.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_CanMakePayment_IncognitoTab) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/true);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+
+  // The user cannot make payment and the PaymentRequest is not shown.
+  logger.SetShowCalled();
+  logger.SetCanMakePaymentValue(true);
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  // Expect no log for CanMakePayment.
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix("PaymentRequest.CanMakePayment"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_SuggestionsForEverything_Completed) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+
+  // Simulate that the user completes the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_SuggestionsForEverything_UserAborted) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+
+  // Simulate that the user aborts the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_SuggestionsForEverything_OtherAborted) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+
+  // Simulate that the checkout is aborted.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly, even in
+// incognito mode.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_SuggestionsForEverything_Incognito) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/true);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+
+  // Simulate that the user completes the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_NoSuggestionsForEverything_Completed) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+
+  // Simulate that the user completes the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+      "EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+
+  EXPECT_THAT(
+      histogram_tester.GetTotalCountsForPrefix(
+          "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion"),
+      testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_NoSuggestionsForEverything_UserAborted) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+
+  // Simulate that the user aborts the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+      "EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserHadSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_NoSuggestionsForEverything_OtherAborted) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/false);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+
+  // Simulate that the user aborts the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+      "EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_OTHER_ABORTED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserHadSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the completion status metrics based on whether the user had
+// suggestions for all the requested sections are logged as correctly, even in
+// incognito mode.
+TEST(JourneyLoggerTest,
+     RecordJourneyStatsHistograms_NoSuggestionsForEverything_Incognito) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger(/*is_incognito=*/true);
+
+  // Simulate that the user had suggestions for all the requested sections.
+  logger.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+
+  // Simulate that the user aborts the checkout.
+  logger.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+      "EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+
+  EXPECT_THAT(histogram_tester.GetTotalCountsForPrefix(
+                  "PaymentRequest.UserHadSuggestionsForEverything."
+                  "EffectOnCompletion"),
+              testing::ContainerEq(base::HistogramTester::CountsMap()));
+}
+
+// Tests that the metrics are logged correctly for two simultaneous Payment
+// Requests.
+TEST(JourneyLoggerTest, RecordJourneyStatsHistograms_TwoPaymentRequests) {
+  base::HistogramTester histogram_tester;
+  JourneyLogger logger1(/*is_incognito=*/false);
+  JourneyLogger logger2(/*is_incognito=*/false);
+
+  // Make the two loggers have different data.
+  logger1.SetShowCalled();
+  logger2.SetShowCalled();
+
+  logger1.SetCanMakePaymentValue(true);
+
+  logger1.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 1);
+  logger2.SetNumberOfSuggestionsShown(JourneyLogger::SECTION_CREDIT_CARDS, 0);
+
+  // Simulate that the user completes one checkout and aborts the other.
+  logger1.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_COMPLETED);
+  logger2.RecordJourneyStatsHistograms(
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED);
+
+  // Make sure the appropriate metrics were logged for logger1.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserHadSuggestionsForEverything.EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_USED, 1);
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.Used.TrueWithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_COMPLETED, 1);
+
+  // Make sure the appropriate metrics were logged for logger2.
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.UserDidNotHaveSuggestionsForEverything."
+      "EffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+  histogram_tester.ExpectBucketCount("PaymentRequest.CanMakePayment.Usage",
+                                     JourneyLogger::CAN_MAKE_PAYMENT_NOT_USED,
+                                     1);
+  histogram_tester.ExpectBucketCount(
+      "PaymentRequest.CanMakePayment.NotUsed.WithShowEffectOnCompletion",
+      JourneyLogger::COMPLETION_STATUS_USER_ABORTED, 1);
+}
+
+}  // namespace payments
\ No newline at end of file
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc
index 0a8eced..02bfb5e 100644
--- a/components/search_engines/template_url_service.cc
+++ b/components/search_engines/template_url_service.cc
@@ -671,18 +671,7 @@
 
   default_search_manager_.ClearUserSelectedDefaultSearchEngine();
 
-  if (!default_search_provider_) {
-    // If the default search provider came from a user pref we would have been
-    // notified of the new (fallback-provided) value in
-    // ClearUserSelectedDefaultSearchEngine() above. Since we are here, the
-    // value was presumably originally a fallback value (which may have been
-    // repaired).
-    DefaultSearchManager::Source source;
-    const TemplateURLData* new_dse =
-        default_search_manager_.GetDefaultSearchEngine(&source);
-    // ApplyDefaultSearchChange will notify observers once it is done.
-    ApplyDefaultSearchChange(new_dse, source);
-  } else {
+  if (default_search_provider_) {
     // Set fallback engine as user selected, because repair is considered a user
     // action and we are expected to sync new fallback engine to other devices.
     const TemplateURLData* fallback_engine_data =
@@ -701,6 +690,18 @@
                         fallback_engine->sync_guid());
     }
     NotifyObservers();
+    RequestGoogleURLTrackerServerCheckIfNecessary();
+  } else {
+    // If the default search provider came from a user pref we would have been
+    // notified of the new (fallback-provided) value in
+    // ClearUserSelectedDefaultSearchEngine() above. Since we are here, the
+    // value was presumably originally a fallback value (which may have been
+    // repaired).
+    DefaultSearchManager::Source source;
+    const TemplateURLData* new_dse =
+        default_search_manager_.GetDefaultSearchEngine(&source);
+    // ApplyDefaultSearchChange will notify observers once it is done.
+    ApplyDefaultSearchChange(new_dse, source);
   }
 }
 
@@ -1829,7 +1830,7 @@
   if (default_search_provider_ &&
       default_search_provider_->HasGoogleBaseURLs(search_terms_data()) &&
       google_url_tracker_)
-    google_url_tracker_->RequestServerCheck(false);
+    google_url_tracker_->RequestServerCheck();
 }
 
 void TemplateURLService::GoogleBaseURLChanged() {
diff --git a/components/update_client/ping_manager_unittest.cc b/components/update_client/ping_manager_unittest.cc
index 1d201887..9795d676 100644
--- a/components/update_client/ping_manager_unittest.cc
+++ b/components/update_client/ping_manager_unittest.cc
@@ -175,6 +175,9 @@
           "download_time_ms=\"9870\"/></app>"))
       << interceptor->GetRequestsAsString();
   interceptor->Reset();
+
+  interceptor_factory.reset();
+  base::RunLoop().RunUntilIdle();
 }
 
 // Tests that sending the ping fails when the component requires encryption but
diff --git a/content/browser/DEPS b/content/browser/DEPS
index 7b93f69c..c11c2b1 100644
--- a/content/browser/DEPS
+++ b/content/browser/DEPS
@@ -3,6 +3,7 @@
   # See comment in content/DEPS for which components are allowed.
   "+components/discardable_memory/common",
   "+components/discardable_memory/service",
+  "+components/display_compositor",
   "+components/filesystem",
   "+components/leveldb",
   "+components/link_header_util",
diff --git a/content/browser/accessibility/browser_accessibility.cc b/content/browser/accessibility/browser_accessibility.cc
index 2aff1e7..1b2bf05 100644
--- a/content/browser/accessibility/browser_accessibility.cc
+++ b/content/browser/accessibility/browser_accessibility.cc
@@ -375,16 +375,6 @@
   return RelativeToAbsoluteBounds(bounds, false);
 }
 
-gfx::Rect BrowserAccessibility::GetScreenBoundsRect() const {
-  gfx::Rect bounds = GetPageBoundsRect();
-
-  // Adjust the bounds by the top left corner of the containing view's bounds
-  // in screen coordinates.
-  bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
-
-  return bounds;
-}
-
 gfx::Rect BrowserAccessibility::GetPageBoundsForRange(int start, int len)
     const {
   DCHECK_GE(start, 0);
@@ -1363,9 +1353,14 @@
   return nullptr;
 }
 
-gfx::Vector2d BrowserAccessibility::GetGlobalCoordinateOffset() {
-  NOTREACHED();
-  return gfx::Vector2d();
+gfx::Rect BrowserAccessibility::GetScreenBoundsRect() const {
+  gfx::Rect bounds = GetPageBoundsRect();
+
+  // Adjust the bounds by the top left corner of the containing view's bounds
+  // in screen coordinates.
+  bounds.Offset(manager_->GetViewBounds().OffsetFromOrigin());
+
+  return bounds;
 }
 
 gfx::NativeViewAccessible BrowserAccessibility::HitTestSync(int x, int y) {
diff --git a/content/browser/accessibility/browser_accessibility.h b/content/browser/accessibility/browser_accessibility.h
index c8b8738e..871d6b1d 100644
--- a/content/browser/accessibility/browser_accessibility.h
+++ b/content/browser/accessibility/browser_accessibility.h
@@ -151,9 +151,6 @@
   // page (specifically, the top-left corner of the topmost web contents).
   gfx::Rect GetPageBoundsRect() const;
 
-  // Returns the bounds of this object in screen coordinates.
-  gfx::Rect GetScreenBoundsRect() const;
-
   // Returns the bounds of the given range in coordinates relative to the
   // top-left corner of the overall web area. Only valid when the
   // role is WebAXRoleStaticText.
@@ -419,7 +416,7 @@
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
-  gfx::Vector2d GetGlobalCoordinateOffset() override;
+  gfx::Rect GetScreenBoundsRect() const override;
   gfx::NativeViewAccessible HitTestSync(int x, int y) override;
   gfx::NativeViewAccessible GetFocus() override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
diff --git a/content/browser/accessibility/browser_accessibility_win.cc b/content/browser/accessibility/browser_accessibility_win.cc
index 920eab33..d4a8d1f 100644
--- a/content/browser/accessibility/browser_accessibility_win.cc
+++ b/content/browser/accessibility/browser_accessibility_win.cc
@@ -483,20 +483,8 @@
   if (!instance_active())
     return E_FAIL;
 
-  if (!x_left || !y_top || !width || !height)
-    return E_INVALIDARG;
-
-  BrowserAccessibilityWin* target = GetTargetFromChildID(var_id);
-  if (!target)
-    return E_INVALIDARG;
-
-  gfx::Rect bounds = target->GetScreenBoundsRect();
-  *x_left = bounds.x();
-  *y_top  = bounds.y();
-  *width  = bounds.width();
-  *height = bounds.height();
-
-  return S_OK;
+  return GetPlatformNodeWin()->accLocation(x_left, y_top, width, height,
+                                           var_id);
 }
 
 STDMETHODIMP BrowserAccessibilityWin::accNavigate(LONG nav_dir,
@@ -5037,6 +5025,11 @@
       this))->Fire();
 }
 
+ui::AXPlatformNodeWin* BrowserAccessibilityWin::GetPlatformNodeWin() const {
+  DCHECK(platform_node_);
+  return static_cast<ui::AXPlatformNodeWin*>(platform_node_);
+}
+
 void BrowserAccessibilityWin::InitRoleAndState() {
   int32_t ia_role = 0;
   int32_t ia_state = 0;
diff --git a/content/browser/accessibility/browser_accessibility_win.h b/content/browser/accessibility/browser_accessibility_win.h
index 7b454d6..d563382b 100644
--- a/content/browser/accessibility/browser_accessibility_win.h
+++ b/content/browser/accessibility/browser_accessibility_win.h
@@ -23,6 +23,7 @@
 #include "third_party/isimpledom/ISimpleDOMDocument.h"
 #include "third_party/isimpledom/ISimpleDOMNode.h"
 #include "third_party/isimpledom/ISimpleDOMText.h"
+#include "ui/accessibility/platform/ax_platform_node_win.h"
 
 namespace ui {
 enum TextBoundaryDirection;
@@ -917,6 +918,8 @@
   // Fire a Windows-specific accessibility event notification on this node.
   void FireNativeEvent(LONG win_event_type) const;
 
+  ui::AXPlatformNodeWin* GetPlatformNodeWin() const;
+
   struct WinAttributes {
     WinAttributes();
     ~WinAttributes();
diff --git a/content/browser/background_fetch/background_fetch_data_manager.cc b/content/browser/background_fetch/background_fetch_data_manager.cc
index 64d62d7..24909e37 100644
--- a/content/browser/background_fetch/background_fetch_data_manager.cc
+++ b/content/browser/background_fetch/background_fetch_data_manager.cc
@@ -207,11 +207,21 @@
     settled_fetch.request = request->fetch_request();
 
     settled_fetch.response.url_list = request->GetURLChain();
-    // TODO: settled_fetch.response.status_code
+
+    // TODO(peter): The |status_code| should match what the download manager ran
+    // in to in the BackgroundFetchResponseInfo.
+    settled_fetch.response.status_code = 200;
     // TODO: settled_fetch.response.status_text
+
     settled_fetch.response.response_type =
         blink::WebServiceWorkerResponseTypeDefault;
-    // TODO: settled_fetch.response.headers
+
+    // TODO(peter): The |headers| should be set to the real response headers,
+    // but the download manager does not relay those to us yet.
+    if (!request->GetResponseType().empty()) {
+      settled_fetch.response.headers["Content-Type"] =
+          request->GetResponseType();
+    }
 
     if (request->GetFileSize() > 0) {
       DCHECK(!request->GetFilePath().empty());
@@ -226,6 +236,10 @@
         settled_fetch.response.blob_uuid = blob_handle->GetUUID();
         settled_fetch.response.blob_size = request->GetFileSize();
 
+        // TODO(peter): Remove when we relay the real response headers.
+        settled_fetch.response.headers["Content-Length"] =
+            std::to_string(request->GetFileSize());
+
         blob_handles.push_back(std::move(blob_handle));
       }
     }
diff --git a/content/browser/background_fetch/background_fetch_request_info.cc b/content/browser/background_fetch/background_fetch_request_info.cc
index 00476061..0beb3c9 100644
--- a/content/browser/background_fetch/background_fetch_request_info.cc
+++ b/content/browser/background_fetch/background_fetch_request_info.cc
@@ -36,6 +36,7 @@
   file_path_ = download_item->GetTargetFilePath();
   file_size_ = download_item->GetReceivedBytes();
   response_time_ = download_item->GetEndTime();
+  response_type_ = download_item->GetMimeType();
 
   response_data_populated_ = true;
 }
@@ -60,4 +61,9 @@
   return response_time_;
 }
 
+const std::string& BackgroundFetchRequestInfo::GetResponseType() const {
+  DCHECK(response_data_populated_);
+  return response_type_;
+}
+
 }  // namespace content
diff --git a/content/browser/background_fetch/background_fetch_request_info.h b/content/browser/background_fetch/background_fetch_request_info.h
index d5939be9..6c611012 100644
--- a/content/browser/background_fetch/background_fetch_request_info.h
+++ b/content/browser/background_fetch/background_fetch_request_info.h
@@ -60,6 +60,9 @@
   // Returns the time at which the response was completed.
   const base::Time& GetResponseTime() const;
 
+  // Returns the mime type describing the response, as indicated by the server.
+  const std::string& GetResponseType() const;
+
  private:
   friend class base::RefCountedThreadSafe<BackgroundFetchRequestInfo>;
 
@@ -87,6 +90,7 @@
   base::FilePath file_path_;
   int64_t file_size_ = 0;
   base::Time response_time_;
+  std::string response_type_;
 
   DISALLOW_COPY_AND_ASSIGN(BackgroundFetchRequestInfo);
 };
diff --git a/content/browser/background_fetch/background_fetch_service_unittest.cc b/content/browser/background_fetch/background_fetch_service_unittest.cc
index 8e47edd..a76ac7a 100644
--- a/content/browser/background_fetch/background_fetch_service_unittest.cc
+++ b/content/browser/background_fetch/background_fetch_service_unittest.cc
@@ -369,11 +369,11 @@
     EXPECT_EQ(fetches[i].request.url, fetches[i].response.url_list[0]);
 
     // TODO(peter): change-detector tests for unsupported properties.
-    EXPECT_EQ(fetches[i].response.status_code, 0);
+    EXPECT_EQ(fetches[i].response.status_code, 200);
     EXPECT_TRUE(fetches[i].response.status_text.empty());
     EXPECT_EQ(fetches[i].response.response_type,
               blink::WebServiceWorkerResponseTypeDefault);
-    EXPECT_TRUE(fetches[i].response.headers.empty());
+    EXPECT_FALSE(fetches[i].response.headers.empty());
     EXPECT_EQ(fetches[i].response.error,
               blink::WebServiceWorkerResponseErrorUnknown);
 
diff --git a/content/browser/background_fetch/background_fetch_test_base.cc b/content/browser/background_fetch/background_fetch_test_base.cc
index 161e670..ec4ca73f 100644
--- a/content/browser/background_fetch/background_fetch_test_base.cc
+++ b/content/browser/background_fetch/background_fetch_test_base.cc
@@ -133,6 +133,7 @@
 
     download_item->SetTargetFilePath(response_path);
     download_item->SetReceivedBytes(response_info.second.size());
+    download_item->SetMimeType("text/plain");
 
     // Notify the Job Controller about the download having been updated.
     download_item->NotifyDownloadUpdated();
diff --git a/content/browser/browser_main_loop.cc b/content/browser/browser_main_loop.cc
index 337fc893..9b6275d 100644
--- a/content/browser/browser_main_loop.cc
+++ b/content/browser/browser_main_loop.cc
@@ -46,6 +46,7 @@
 #include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
 #include "components/discardable_memory/service/discardable_shared_memory_manager.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "components/tracing/common/process_metrics_memory_dump_provider.h"
 #include "components/tracing/common/trace_config_file.h"
 #include "components/tracing/common/trace_to_console.h"
@@ -78,7 +79,6 @@
 #include "content/browser/webui/content_web_ui_controller_factory.h"
 #include "content/browser/webui/url_data_manager.h"
 #include "content/common/content_switches_internal.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/common/service_manager/service_manager_connection_impl.h"
 #include "content/public/browser/browser_main_parts.h"
 #include "content/public/browser/content_browser_client.h"
@@ -804,7 +804,8 @@
   tracing::ProcessMetricsMemoryDumpProvider::RegisterForProcess(
       base::kNullProcessId);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
-      HostSharedBitmapManager::current(), "HostSharedBitmapManager", nullptr);
+      display_compositor::HostSharedBitmapManager::current(),
+      "display_compositor::HostSharedBitmapManager", nullptr);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
       skia::SkiaMemoryDumpProvider::GetInstance(), "Skia", nullptr);
   base::trace_event::MemoryDumpManager::GetInstance()->RegisterDumpProvider(
diff --git a/content/browser/browser_plugin/browser_plugin_guest.cc b/content/browser/browser_plugin/browser_plugin_guest.cc
index 437744a..4d67e25b 100644
--- a/content/browser/browser_plugin/browser_plugin_guest.cc
+++ b/content/browser/browser_plugin/browser_plugin_guest.cc
@@ -36,7 +36,6 @@
 #include "content/common/browser_plugin/browser_plugin_messages.h"
 #include "content/common/content_constants_internal.h"
 #include "content/common/drag_messages.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/common/input_messages.h"
 #include "content/common/site_isolation_policy.h"
 #include "content/common/text_input_state.h"
diff --git a/content/browser/compositor/gpu_process_transport_factory.cc b/content/browser/compositor/gpu_process_transport_factory.cc
index 54b7119..b1de4f5 100644
--- a/content/browser/compositor/gpu_process_transport_factory.cc
+++ b/content/browser/compositor/gpu_process_transport_factory.cc
@@ -31,6 +31,7 @@
 #include "cc/surfaces/surface_manager.h"
 #include "components/display_compositor/compositor_overlay_candidate_validator.h"
 #include "components/display_compositor/gl_helper.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/browser/compositor/browser_compositor_output_surface.h"
 #include "content/browser/compositor/gpu_browser_compositor_output_surface.h"
 #include "content/browser/compositor/gpu_surfaceless_browser_compositor_output_surface.h"
@@ -40,7 +41,6 @@
 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/public/common/content_switches.h"
 #include "gpu/GLES2/gl2extchromium.h"
 #include "gpu/command_buffer/client/gles2_interface.h"
@@ -581,11 +581,12 @@
 
   // The Display owns and uses the |display_output_surface| created above.
   data->display = base::MakeUnique<cc::Display>(
-      HostSharedBitmapManager::current(), GetGpuMemoryBufferManager(),
-      compositor->GetRendererSettings(), compositor->frame_sink_id(),
-      begin_frame_source, std::move(display_output_surface),
-      std::move(scheduler), base::MakeUnique<cc::TextureMailboxDeleter>(
-                                compositor->task_runner().get()));
+      display_compositor::HostSharedBitmapManager::current(),
+      GetGpuMemoryBufferManager(), compositor->GetRendererSettings(),
+      compositor->frame_sink_id(), begin_frame_source,
+      std::move(display_output_surface), std::move(scheduler),
+      base::MakeUnique<cc::TextureMailboxDeleter>(
+          compositor->task_runner().get()));
   // Note that we are careful not to destroy prior BeginFrameSource objects
   // until we have reset |data->display|.
   data->synthetic_begin_frame_source = std::move(synthetic_begin_frame_source);
@@ -605,7 +606,7 @@
                 compositor->frame_sink_id(), surface_manager_.get(),
                 data->display.get(), context_provider,
                 shared_worker_context_provider_, GetGpuMemoryBufferManager(),
-                HostSharedBitmapManager::current());
+                display_compositor::HostSharedBitmapManager::current());
   data->display->Resize(compositor->size());
   data->display->SetOutputIsSecure(data->output_is_secure);
   compositor->SetCompositorFrameSink(std::move(compositor_frame_sink));
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.cc b/content/browser/devtools/render_frame_devtools_agent_host.cc
index 8823e3a..204903c3 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.cc
+++ b/content/browser/devtools/render_frame_devtools_agent_host.cc
@@ -462,6 +462,13 @@
 
   g_instances.Get().push_back(this);
   AddRef();  // Balanced in RenderFrameHostDestroyed.
+
+  DevToolsManager* manager = DevToolsManager::GetInstance();
+  if (manager->delegate()) {
+    type_ = manager->delegate()->GetTargetType(host);
+    title_ = manager->delegate()->GetTargetTitle(host);
+  }
+
   NotifyCreated();
 }
 
@@ -1015,24 +1022,18 @@
 }
 
 std::string RenderFrameDevToolsAgentHost::GetType() {
-  DevToolsManager* manager = DevToolsManager::GetInstance();
-  if (manager->delegate() && current_) {
-    std::string result = manager->delegate()->GetTargetType(current_->host());
-    if (!result.empty())
-      return result;
-  }
+  if (!type_.empty())
+    return type_;
   if (IsChildFrame())
     return kTypeFrame;
   return kTypePage;
 }
 
 std::string RenderFrameDevToolsAgentHost::GetTitle() {
-  DevToolsManager* manager = DevToolsManager::GetInstance();
-  if (manager->delegate() && current_) {
-    std::string result = manager->delegate()->GetTargetTitle(current_->host());
-    if (!result.empty())
-      return result;
-  }
+  if (!title_.empty())
+    return title_;
+  if (current_ && current_->host()->GetParent())
+    return current_->host()->GetLastCommittedURL().spec();
   content::WebContents* web_contents = GetWebContents();
   if (web_contents)
     return base::UTF16ToUTF8(web_contents->GetTitle());
diff --git a/content/browser/devtools/render_frame_devtools_agent_host.h b/content/browser/devtools/render_frame_devtools_agent_host.h
index ff6bbd8..7e600114 100644
--- a/content/browser/devtools/render_frame_devtools_agent_host.h
+++ b/content/browser/devtools/render_frame_devtools_agent_host.h
@@ -171,6 +171,8 @@
 #endif
   RenderFrameHostImpl* handlers_frame_host_;
   bool current_frame_crashed_;
+  std::string title_;
+  std::string type_;
 
   // PlzNavigate
 
diff --git a/content/browser/renderer_host/compositor_impl_android.cc b/content/browser/renderer_host/compositor_impl_android.cc
index 8b9dd49..bfc265f 100644
--- a/content/browser/renderer_host/compositor_impl_android.cc
+++ b/content/browser/renderer_host/compositor_impl_android.cc
@@ -48,11 +48,11 @@
 #include "cc/trees/layer_tree_settings.h"
 #include "components/display_compositor/compositor_overlay_candidate_validator_android.h"
 #include "components/display_compositor/gl_helper.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/browser/gpu/browser_gpu_channel_host_factory.h"
 #include "content/browser/gpu/browser_gpu_memory_buffer_manager.h"
 #include "content/browser/gpu/compositor_util.h"
 #include "content/browser/renderer_host/render_widget_host_impl.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/public/browser/android/compositor.h"
 #include "content/public/browser/android/compositor_client.h"
 #include "content/public/common/content_switches.h"
@@ -759,7 +759,7 @@
       task_runner, display_output_surface->capabilities().max_frames_pending));
 
   display_.reset(new cc::Display(
-      HostSharedBitmapManager::current(),
+      display_compositor::HostSharedBitmapManager::current(),
       BrowserGpuMemoryBufferManager::current(),
       host_->GetSettings().renderer_settings, frame_sink_id_,
       root_window_->GetBeginFrameSource(), std::move(display_output_surface),
@@ -774,7 +774,7 @@
           : base::MakeUnique<cc::DirectCompositorFrameSink>(
                 frame_sink_id_, manager, display_.get(), context_provider,
                 nullptr, BrowserGpuMemoryBufferManager::current(),
-                HostSharedBitmapManager::current());
+                display_compositor::HostSharedBitmapManager::current());
 
   display_->SetVisible(true);
   display_->Resize(size_);
diff --git a/content/browser/renderer_host/media/buildable_video_capture_device.h b/content/browser/renderer_host/media/buildable_video_capture_device.h
index 3f41490a..e358435 100644
--- a/content/browser/renderer_host/media/buildable_video_capture_device.h
+++ b/content/browser/renderer_host/media/buildable_video_capture_device.h
@@ -30,11 +30,7 @@
   class CONTENT_EXPORT Callbacks {
    public:
     virtual ~Callbacks() {}
-    // Returns nullptr if no descriptor was found.
-    virtual const media::VideoCaptureDeviceDescriptor* LookupDeviceDescriptor(
-        const std::string& id) = 0;
-    virtual void WillStartDevice(media::VideoFacingMode facing_mode) = 0;
-    virtual void DidStartDevice(VideoCaptureController* controller) = 0;
+    virtual void OnDeviceStarted(VideoCaptureController* controller) = 0;
     virtual void OnDeviceStartFailed(VideoCaptureController* controller) = 0;
     virtual void OnDeviceStartAborted() = 0;
   };
diff --git a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.cc b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.cc
index 3016f5f..f562158b 100644
--- a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.cc
+++ b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.cc
@@ -83,9 +83,9 @@
 
 InProcessBuildableVideoCaptureDevice::InProcessBuildableVideoCaptureDevice(
     scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
-    media::VideoCaptureDeviceFactory* device_factory)
+    media::VideoCaptureSystem* video_capture_system)
     : device_task_runner_(std::move(device_task_runner)),
-      device_factory_(device_factory) {}
+      video_capture_system_(video_capture_system) {}
 
 InProcessBuildableVideoCaptureDevice::~InProcessBuildableVideoCaptureDevice() {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -108,45 +108,24 @@
       CreateDeviceClient(max_buffers, controller->GetWeakPtrForIOThread());
 
   base::Closure start_capture_closure;
+  // Use of Unretained() is safe, because |done_cb| guarantees that
+  // |this| stays alive.
+  ReceiveDeviceCallback after_start_capture_callback = media::BindToCurrentLoop(
+      base::Bind(&InProcessBuildableVideoCaptureDevice::OnDeviceStarted,
+                 base::Unretained(this), controller, callbacks,
+                 base::Passed(&done_cb)));
+
   switch (controller->stream_type()) {
     case MEDIA_DEVICE_VIDEO_CAPTURE: {
-      const media::VideoCaptureDeviceDescriptor* descriptor =
-          callbacks->LookupDeviceDescriptor(controller->device_id());
-      if (!descriptor) {
-        callbacks->OnDeviceStartFailed(controller);
-        base::ResetAndReturn(&done_cb).Run();
-        return;
-      }
-      controller->OnLog(base::StringPrintf(
-          "Starting device: id: %s, name: %s, api: %s",
-          descriptor->device_id.c_str(), descriptor->GetNameAndModel().c_str(),
-          descriptor->GetCaptureApiTypeString()));
-
-      callbacks->WillStartDevice(descriptor->facing);
-
-      // Use of Unretained() is safe, because |done_cb| guarantees that |this|
-      // stays alive.
-      ReceiveDeviceCallback after_start_capture_callback =
-          media::BindToCurrentLoop(
-              base::Bind(&InProcessBuildableVideoCaptureDevice::OnDeviceStarted,
-                         base::Unretained(this), controller, callbacks,
-                         base::Passed(&done_cb)));
       start_capture_closure =
           base::Bind(&InProcessBuildableVideoCaptureDevice::
                          DoStartDeviceCaptureOnDeviceThread,
-                     base::Unretained(this), *descriptor, params,
+                     base::Unretained(this), controller->device_id(), params,
                      base::Passed(std::move(device_client)),
                      std::move(after_start_capture_callback));
       break;
     }
-    case MEDIA_TAB_VIDEO_CAPTURE: {
-      // Use of Unretained() is safe, because |done_cb| guarantees that |this|
-      // stays alive.
-      ReceiveDeviceCallback after_start_capture_callback =
-          media::BindToCurrentLoop(
-              base::Bind(&InProcessBuildableVideoCaptureDevice::OnDeviceStarted,
-                         base::Unretained(this), controller, callbacks,
-                         base::Passed(&done_cb)));
+    case MEDIA_TAB_VIDEO_CAPTURE:
       start_capture_closure =
           base::Bind(&InProcessBuildableVideoCaptureDevice::
                          DoStartTabCaptureOnDeviceThread,
@@ -154,15 +133,8 @@
                      base::Passed(std::move(device_client)),
                      std::move(after_start_capture_callback));
       break;
-    }
-    case MEDIA_DESKTOP_VIDEO_CAPTURE: {
-      // Use of Unretained() is safe, because |done_cb| guarantees that |this|
-      // stays alive.
-      ReceiveDeviceCallback after_start_capture_callback =
-          media::BindToCurrentLoop(
-              base::Bind(&InProcessBuildableVideoCaptureDevice::OnDeviceStarted,
-                         base::Unretained(this), controller, callbacks,
-                         base::Passed(&done_cb)));
+
+    case MEDIA_DESKTOP_VIDEO_CAPTURE:
       start_capture_closure =
           base::Bind(&InProcessBuildableVideoCaptureDevice::
                          DoStartDesktopCaptureOnDeviceThread,
@@ -170,7 +142,7 @@
                      base::Passed(std::move(device_client)),
                      std::move(after_start_capture_callback));
       break;
-    }
+
     default: {
       NOTIMPLEMENTED();
       return;
@@ -338,7 +310,7 @@
               device.get(), device_task_runner_));
       device_ = std::move(device);
       state_ = State::DEVICE_STARTED;
-      callbacks->DidStartDevice(controller);
+      callbacks->OnDeviceStarted(controller);
       base::ResetAndReturn(&done_cb).Run();
       return;
     case State::DEVICE_START_ABORTING:
@@ -363,7 +335,7 @@
 }
 
 void InProcessBuildableVideoCaptureDevice::DoStartDeviceCaptureOnDeviceThread(
-    const media::VideoCaptureDeviceDescriptor& descriptor,
+    const std::string& device_id,
     const media::VideoCaptureParams& params,
     std::unique_ptr<media::VideoCaptureDeviceClient> device_client,
     ReceiveDeviceCallback result_callback) {
@@ -371,7 +343,7 @@
   DCHECK(device_task_runner_->BelongsToCurrentThread());
 
   std::unique_ptr<media::VideoCaptureDevice> video_capture_device =
-      device_factory_->CreateDevice(descriptor);
+      video_capture_system_->CreateDevice(device_id);
 
   if (!video_capture_device) {
     result_callback.Run(nullptr);
diff --git a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
index b7c5639..a4cdfed 100644
--- a/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
+++ b/content/browser/renderer_host/media/in_process_buildable_video_capture_device.h
@@ -10,7 +10,7 @@
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_device_client.h"
 #include "media/capture/video/video_capture_device_descriptor.h"
-#include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/video_capture_system.h"
 
 namespace content {
 
@@ -23,7 +23,7 @@
  public:
   InProcessBuildableVideoCaptureDevice(
       scoped_refptr<base::SingleThreadTaskRunner> device_task_runner,
-      media::VideoCaptureDeviceFactory* device_factory);
+      media::VideoCaptureSystem* video_capture_system);
   ~InProcessBuildableVideoCaptureDevice() override;
 
   // BuildableVideoCaptureDevice implementation:
@@ -70,7 +70,7 @@
                        std::unique_ptr<media::VideoCaptureDevice> device);
 
   void DoStartDeviceCaptureOnDeviceThread(
-      const media::VideoCaptureDeviceDescriptor& descriptor,
+      const std::string& device_id,
       const media::VideoCaptureParams& params,
       std::unique_ptr<media::VideoCaptureDeviceClient> client,
       ReceiveDeviceCallback result_callback);
@@ -93,7 +93,7 @@
       base::OnceClosure done_cb);
 
   const scoped_refptr<base::SingleThreadTaskRunner> device_task_runner_;
-  media::VideoCaptureDeviceFactory* const device_factory_;
+  media::VideoCaptureSystem* const video_capture_system_;
   std::unique_ptr<media::VideoCaptureDevice> device_;
   State state_ = State::NO_DEVICE;
 };
diff --git a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
index c907d5f..5c96457 100644
--- a/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_dispatcher_host_unittest.cc
@@ -36,6 +36,7 @@
 
 using testing::_;
 using testing::SaveArg;
+using testing::InvokeWithoutArgs;
 
 namespace content {
 
@@ -354,38 +355,26 @@
   base::RunLoop().RunUntilIdle();
 
   // Verify that the callback for a valid origin does get called.
-  EXPECT_CALL(*this, ValidOriginCallback(testing::_));
+  base::RunLoop run_loop;
+  EXPECT_CALL(*this, ValidOriginCallback(testing::_))
+      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
   host_->EnumerateDevices(
       true, true, true, url::Origin(GURL("http://localhost")),
       base::Bind(&MediaDevicesDispatcherHostTest::ValidOriginCallback,
                  base::Unretained(this)));
-  base::RunLoop().RunUntilIdle();
-#if defined(OS_WIN)
-  // On Windows, the underlying MediaStreamManager uses a separate thread for
-  // video capture which must be flushed to guarantee that the callback bound to
-  // EnumerateDevices above is invoked before the end of this test's body.
-  media_stream_manager_->FlushVideoCaptureThreadForTesting();
-  base::RunLoop().RunUntilIdle();
-#endif
+  run_loop.Run();
 }
 
 TEST_F(MediaDevicesDispatcherHostTest, GetVideoInputCapabilities) {
-  EXPECT_CALL(*this, MockVideoInputCapabilitiesCallback());
+  base::RunLoop run_loop;
+  EXPECT_CALL(*this, MockVideoInputCapabilitiesCallback())
+      .WillOnce(InvokeWithoutArgs([&run_loop]() { run_loop.Quit(); }));
   host_->GetVideoInputCapabilities(
       origin_,
       base::Bind(
           &MediaDevicesDispatcherHostTest::VideoInputCapabilitiesCallback,
           base::Unretained(this)));
-  base::RunLoop().RunUntilIdle();
-
-#if defined(OS_WIN)
-  // On Windows, the underlying MediaStreamManager uses a separate thread for
-  // video capture which must be flushed to guarantee that the callback bound to
-  // GetVIdeoInputCapabilities above is invoked before the end of this test's
-  // body.
-  media_stream_manager_->FlushVideoCaptureThreadForTesting();
-  base::RunLoop().RunUntilIdle();
-#endif
+  run_loop.Run();
 }
 
 };  // namespace content
diff --git a/content/browser/renderer_host/media/media_devices_manager_unittest.cc b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
index 5ad37f9..cc3356d 100644
--- a/content/browser/renderer_host/media/media_devices_manager_unittest.cc
+++ b/content/browser/renderer_host/media/media_devices_manager_unittest.cc
@@ -143,8 +143,9 @@
     audio_manager_.reset(new MockAudioManager());
     audio_system_ = media::AudioSystemImpl::Create(audio_manager_.get());
     video_capture_manager_ = new VideoCaptureManager(
-        std::unique_ptr<media::VideoCaptureDeviceFactory>(
-            new MockVideoCaptureDeviceFactory()),
+        base::MakeUnique<media::VideoCaptureSystem>(
+            std::unique_ptr<media::VideoCaptureDeviceFactory>(
+                new MockVideoCaptureDeviceFactory())),
         base::ThreadTaskRunnerHandle::Get());
     video_capture_device_factory_ = static_cast<MockVideoCaptureDeviceFactory*>(
         video_capture_manager_->video_capture_device_factory());
diff --git a/content/browser/renderer_host/media/media_stream_manager.cc b/content/browser/renderer_host/media/media_stream_manager.cc
index 963c9c7..156faa9 100644
--- a/content/browser/renderer_host/media/media_stream_manager.cc
+++ b/content/browser/renderer_host/media/media_stream_manager.cc
@@ -49,7 +49,7 @@
 #include "media/base/audio_parameters.h"
 #include "media/base/channel_layout.h"
 #include "media/base/media_switches.h"
-#include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/video_capture_system.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
@@ -1253,20 +1253,19 @@
   tracked_objects::ScopedTracker tracking_profile4(
       FROM_HERE_WITH_EXPLICIT_FUNCTION(
           "457525 MediaStreamManager::InitializeDeviceManagersOnIOThread 4"));
+  auto video_capture_system = base::MakeUnique<media::VideoCaptureSystem>(
+      media::VideoCaptureDeviceFactory::CreateFactory(
+          BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)));
 #if defined(OS_WIN)
   // Use an STA Video Capture Thread to try to avoid crashes on enumeration of
   // buggy third party Direct Show modules, http://crbug.com/428958.
   video_capture_thread_.init_com_with_mta(false);
   CHECK(video_capture_thread_.Start());
   video_capture_manager_ = new VideoCaptureManager(
-      media::VideoCaptureDeviceFactory::CreateFactory(
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)),
-      video_capture_thread_.task_runner());
+      std::move(video_capture_system), video_capture_thread_.task_runner());
 #else
   video_capture_manager_ = new VideoCaptureManager(
-      media::VideoCaptureDeviceFactory::CreateFactory(
-          BrowserThread::GetTaskRunnerForThread(BrowserThread::UI)),
-      audio_system_->GetTaskRunner());
+      std::move(video_capture_system), audio_system_->GetTaskRunner());
 #endif
 
   video_capture_manager_->RegisterListener(this);
@@ -1735,12 +1734,6 @@
   generate_stream_test_callback_ = test_callback;
 }
 
-#if defined(OS_WIN)
-void MediaStreamManager::FlushVideoCaptureThreadForTesting() {
-  video_capture_thread_.FlushForTesting();
-}
-#endif
-
 MediaStreamDevices MediaStreamManager::ConvertToMediaStreamDevices(
     MediaStreamType stream_type,
     const MediaDeviceInfoArray& device_infos) {
diff --git a/content/browser/renderer_host/media/media_stream_manager.h b/content/browser/renderer_host/media/media_stream_manager.h
index 630addd..edc0cb6 100644
--- a/content/browser/renderer_host/media/media_stream_manager.h
+++ b/content/browser/renderer_host/media/media_stream_manager.h
@@ -260,10 +260,6 @@
   void SetGenerateStreamCallbackForTesting(
       GenerateStreamTestCallback test_callback);
 
-#if defined(OS_WIN)
-  void FlushVideoCaptureThreadForTesting();
-#endif
-
   // This method is called when all tracks are started.
   void OnStreamStarted(const std::string& label);
 
diff --git a/content/browser/renderer_host/media/video_capture_manager.cc b/content/browser/renderer_host/media/video_capture_manager.cc
index c5b56ce..9c58c0f 100644
--- a/content/browser/renderer_host/media/video_capture_manager.cc
+++ b/content/browser/renderer_host/media/video_capture_manager.cc
@@ -58,51 +58,6 @@
 
 namespace {
 
-// Compares two VideoCaptureFormat by checking smallest frame_size area, then
-// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
-// the first entry for a given resolution has the largest frame rate, as needed
-// by the ConsolidateCaptureFormats() method.
-bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
-                            const media::VideoCaptureFormat& format2) {
-  DCHECK(format1.frame_size.GetCheckedArea().IsValid());
-  DCHECK(format2.frame_size.GetCheckedArea().IsValid());
-  if (format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
-      format2.frame_size.GetCheckedArea().ValueOrDefault(0)) {
-    return format1.frame_rate > format2.frame_rate;
-  }
-  return format1.frame_size.GetCheckedArea().ValueOrDefault(0) <
-         format2.frame_size.GetCheckedArea().ValueOrDefault(0);
-}
-
-bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
-                              const media::VideoCaptureFormat& format2) {
-  DCHECK(format1.frame_size.GetCheckedArea().IsValid());
-  DCHECK(format2.frame_size.GetCheckedArea().IsValid());
-  return format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
-         format2.frame_size.GetCheckedArea().ValueOrDefault(0);
-}
-
-// This function receives a list of capture formats, removes duplicated
-// resolutions while keeping the highest frame rate for each, and forcing I420
-// pixel format.
-void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
-  if (formats->empty())
-    return;
-  std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
-  // Due to the ordering imposed, the largest frame_rate is kept while removing
-  // duplicated resolutions.
-  media::VideoCaptureFormats::iterator last =
-      std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
-  formats->erase(last, formats->end());
-  // Mark all formats as I420, since this is what the renderer side will get
-  // anyhow: the actual pixel format is decided at the device level.
-  // Don't do this for Y16 format as it is handled separatelly.
-  for (auto& format : *formats) {
-    if (format.pixel_format != media::PIXEL_FORMAT_Y16)
-      format.pixel_format = media::PIXEL_FORMAT_I420;
-  }
-}
-
 // Used for logging capture events.
 // Elements in this enum should not be deleted or rearranged; the only
 // permitted operation is to add new elements before NUM_VIDEO_CAPTURE_EVENT.
@@ -126,19 +81,6 @@
 
 namespace content {
 
-// Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported
-// video formats.
-struct VideoCaptureManager::DeviceInfo {
-  DeviceInfo();
-  DeviceInfo(media::VideoCaptureDeviceDescriptor descriptor);
-  DeviceInfo(const DeviceInfo& other);
-  ~DeviceInfo();
-  DeviceInfo& operator=(const DeviceInfo& other);
-
-  media::VideoCaptureDeviceDescriptor descriptor;
-  media::VideoCaptureFormats supported_formats;
-};
-
 // Class used for queuing request for starting a device.
 class VideoCaptureManager::CaptureDeviceStartRequest {
  public:
@@ -155,20 +97,6 @@
   const media::VideoCaptureParams params_;
 };
 
-VideoCaptureManager::DeviceInfo::DeviceInfo() = default;
-
-VideoCaptureManager::DeviceInfo::DeviceInfo(
-    media::VideoCaptureDeviceDescriptor descriptor)
-    : descriptor(descriptor) {}
-
-VideoCaptureManager::DeviceInfo::DeviceInfo(
-    const VideoCaptureManager::DeviceInfo& other) = default;
-
-VideoCaptureManager::DeviceInfo::~DeviceInfo() = default;
-
-VideoCaptureManager::DeviceInfo& VideoCaptureManager::DeviceInfo::operator=(
-    const VideoCaptureManager::DeviceInfo& other) = default;
-
 VideoCaptureManager::CaptureDeviceStartRequest::CaptureDeviceStartRequest(
     VideoCaptureController* controller,
     media::VideoCaptureSessionId session_id,
@@ -176,11 +104,11 @@
     : controller_(controller), session_id_(session_id), params_(params) {}
 
 VideoCaptureManager::VideoCaptureManager(
-    std::unique_ptr<media::VideoCaptureDeviceFactory> factory,
+    std::unique_ptr<media::VideoCaptureSystem> video_capture_system,
     scoped_refptr<base::SingleThreadTaskRunner> device_task_runner)
     : device_task_runner_(std::move(device_task_runner)),
       new_capture_session_id_(1),
-      video_capture_device_factory_(std::move(factory)) {}
+      video_capture_system_(std::move(video_capture_system)) {}
 
 VideoCaptureManager::~VideoCaptureManager() {
   DCHECK(controllers_.empty());
@@ -224,29 +152,17 @@
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DVLOG(1) << "VideoCaptureManager::EnumerateDevices";
 
-  // Bind a callback to ConsolidateDevicesInfoOnDeviceThread() with an argument
-  // for another callback to OnDevicesInfoEnumerated() to be run in the current
-  // loop, i.e. IO loop. Pass a timer for UMA histogram collection.
-  base::Callback<void(std::unique_ptr<VideoCaptureDeviceDescriptors>)>
-      devices_enumerated_callback = base::Bind(
-          &VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread, this,
-          media::BindToCurrentLoop(base::Bind(
-              &VideoCaptureManager::OnDevicesInfoEnumerated, this,
-              base::Owned(new base::ElapsedTimer()), client_callback)),
-          devices_info_cache_);
-  // OK to use base::Unretained() since we own the VCDFactory and |this| is
-  // bound in |devices_enumerated_callback|.
+  // OK to use base::Unretained(video_capture_system_) since we own the
+  // |video_capture_system_| and |this| is bound in
+  // |devices_enumerated_callback|.
   device_task_runner_->PostTask(
       FROM_HERE,
-      base::Bind(&media::VideoCaptureDeviceFactory::EnumerateDeviceDescriptors,
-                 base::Unretained(video_capture_device_factory_.get()),
-                 devices_enumerated_callback));
-}
-
-const media::VideoCaptureDeviceDescriptor*
-VideoCaptureManager::LookupDeviceDescriptor(const std::string& id) {
-  const DeviceInfo* info = GetDeviceInfoById(id);
-  return info ? (&info->descriptor) : nullptr;
+      base::Bind(&media::VideoCaptureSystem::GetDeviceInfosAsync,
+                 base::Unretained(video_capture_system_.get()),
+                 // Pass a timer for UMA histogram collection.
+                 media::BindToCurrentLoop(base::Bind(
+                     &VideoCaptureManager::OnDeviceInfosReceived, this,
+                     base::Owned(new base::ElapsedTimer()), client_callback))));
 }
 
 int VideoCaptureManager::Open(const MediaStreamDevice& device) {
@@ -337,7 +253,8 @@
     }
   }
 
-  const DeviceInfo* device_info = GetDeviceInfoById(controller->device_id());
+  const media::VideoCaptureDeviceInfo* device_info =
+      GetDeviceInfoById(controller->device_id());
   if (device_info != nullptr) {
     for (auto& observer : capture_observers_)
       observer.OnVideoCaptureStopped(device_info->descriptor.facing);
@@ -368,6 +285,25 @@
   DVLOG(3) << "HandleQueuedStartRequest, Post start to device thread, device = "
            << controller->device_id()
            << " start id = " << controller->serial_id();
+  // The unit test VideoCaptureManagerTest.OpenNotExisting requires us to fail
+  // synchronously if the stream_type is MEDIA_DEVICE_VIDEO_CAPTURE and no
+  // DeviceInfo matching the requested id is present (which is the case when
+  // requesting a device with a bogus id). Note, that since other types of
+  // failure during startup of the device are allowed to be reported
+  // asynchronously, this requirement is questionable.
+  // TODO(chfremer): Check if any production code actually depends on this
+  // requirement. If not, relax the requirement in the test and remove the below
+  // if block. See crbug.com/708251
+  if (controller->stream_type() == MEDIA_DEVICE_VIDEO_CAPTURE) {
+    const media::VideoCaptureDeviceInfo* device_info =
+        GetDeviceInfoById(controller->device_id());
+    if (!device_info) {
+      OnDeviceStartFailed(controller);
+      return;
+    }
+    for (auto& observer : capture_observers_)
+      observer.OnVideoCaptureStarted(device_info->descriptor.facing);
+  }
 
   // The method CreateAndStartDeviceAsync() is going to run asynchronously.
   // Since we may be removing the controller while it is executing, we need to
@@ -385,17 +321,13 @@
                  GetControllerSharedRef(controller)));
 }
 
-void VideoCaptureManager::WillStartDevice(media::VideoFacingMode facing_mode) {
-  for (auto& observer : capture_observers_)
-    observer.OnVideoCaptureStarted(facing_mode);
-}
-
-void VideoCaptureManager::DidStartDevice(VideoCaptureController* controller) {
+void VideoCaptureManager::OnDeviceStarted(VideoCaptureController* controller) {
   DVLOG(3) << __func__;
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   DCHECK(!device_start_request_queue_.empty());
   DCHECK_EQ(controller, device_start_request_queue_.begin()->controller());
   DCHECK(controller);
+
   if (controller->stream_type() == MEDIA_DESKTOP_VIDEO_CAPTURE) {
     const media::VideoCaptureSessionId session_id =
         device_start_request_queue_.front().session_id();
@@ -586,7 +518,7 @@
   DCHECK(supported_formats->empty());
 
   // Return all available formats of the device, regardless its started state.
-  DeviceInfo* existing_device = GetDeviceInfoById(device_id);
+  media::VideoCaptureDeviceInfo* existing_device = GetDeviceInfoById(device_id);
   if (existing_device)
     *supported_formats = existing_device->supported_formats;
   return true;
@@ -744,18 +676,18 @@
     listener.Closed(stream_type, capture_session_id);
 }
 
-void VideoCaptureManager::OnDevicesInfoEnumerated(
+void VideoCaptureManager::OnDeviceInfosReceived(
     base::ElapsedTimer* timer,
     const EnumerationCallback& client_callback,
-    const VideoCaptureManager::DeviceInfos& new_devices_info_cache) {
+    const std::vector<media::VideoCaptureDeviceInfo>& device_infos) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
   UMA_HISTOGRAM_TIMES(
       "Media.VideoCaptureManager.GetAvailableDevicesInfoOnDeviceThreadTime",
       timer->Elapsed());
-  devices_info_cache_ = new_devices_info_cache;
+  devices_info_cache_ = device_infos;
 
   // Walk the |devices_info_cache_| and produce a
-  // media::VideoCaptureDeviceDescriptors for return purposes.
+  // media::VideoCaptureDeviceDescriptors for |client_callback|.
   media::VideoCaptureDeviceDescriptors devices;
   std::vector<std::tuple<media::VideoCaptureDeviceDescriptor,
                          media::VideoCaptureFormats>>
@@ -770,40 +702,6 @@
   client_callback.Run(devices);
 }
 
-void VideoCaptureManager::ConsolidateDevicesInfoOnDeviceThread(
-    base::Callback<void(const VideoCaptureManager::DeviceInfos&)>
-        on_devices_enumerated_callback,
-    const VideoCaptureManager::DeviceInfos& old_device_info_cache,
-    std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors_snapshot) {
-  DCHECK(device_task_runner_->BelongsToCurrentThread());
-  // Construct |new_devices_info_cache| with the cached devices that are still
-  // present in the system, and remove their names from |names_snapshot|, so we
-  // keep there the truly new devices.
-  VideoCaptureManager::DeviceInfos new_devices_info_cache;
-  for (const auto& device_info : old_device_info_cache) {
-    for (VideoCaptureDeviceDescriptors::iterator it =
-             descriptors_snapshot->begin();
-         it != descriptors_snapshot->end(); ++it) {
-      if (device_info.descriptor.device_id == it->device_id) {
-        new_devices_info_cache.push_back(device_info);
-        descriptors_snapshot->erase(it);
-        break;
-      }
-    }
-  }
-
-  // Get the device info for the new devices in |names_snapshot|.
-  for (const auto& it : *descriptors_snapshot) {
-    DeviceInfo device_info(it);
-    video_capture_device_factory_->GetSupportedFormats(
-        it, &device_info.supported_formats);
-    ConsolidateCaptureFormats(&device_info.supported_formats);
-    new_devices_info_cache.push_back(device_info);
-  }
-
-  on_devices_enumerated_callback.Run(new_devices_info_cache);
-}
-
 void VideoCaptureManager::DestroyControllerIfNoClients(
     VideoCaptureController* controller) {
   DCHECK_CURRENTLY_ON(BrowserThread::IO);
@@ -872,7 +770,7 @@
   return nullptr;
 }
 
-VideoCaptureManager::DeviceInfo* VideoCaptureManager::GetDeviceInfoById(
+media::VideoCaptureDeviceInfo* VideoCaptureManager::GetDeviceInfoById(
     const std::string& id) {
   for (auto& it : devices_info_cache_) {
     if (it.descriptor.device_id == id)
@@ -903,14 +801,14 @@
   VideoCaptureController* new_controller = new VideoCaptureController(
       device_info.id, device_info.type, params,
       base::MakeUnique<InProcessBuildableVideoCaptureDevice>(
-          device_task_runner_, video_capture_device_factory_.get()));
+          device_task_runner_, video_capture_system_.get()));
   controllers_.emplace_back(new_controller);
   return new_controller;
 }
 
 base::Optional<CameraCalibration> VideoCaptureManager::GetCameraCalibration(
     const std::string& device_id) {
-  VideoCaptureManager::DeviceInfo* info = GetDeviceInfoById(device_id);
+  media::VideoCaptureDeviceInfo* info = GetDeviceInfoById(device_id);
   if (!info)
     return base::Optional<CameraCalibration>();
   return info->descriptor.camera_calibration;
diff --git a/content/browser/renderer_host/media/video_capture_manager.h b/content/browser/renderer_host/media/video_capture_manager.h
index 026af56..2788df3b 100644
--- a/content/browser/renderer_host/media/video_capture_manager.h
+++ b/content/browser/renderer_host/media/video_capture_manager.h
@@ -34,6 +34,8 @@
 #include "media/base/video_facing.h"
 #include "media/capture/video/video_capture_device.h"
 #include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/video_capture_device_info.h"
+#include "media/capture/video/video_capture_system.h"
 #include "media/capture/video_capture_types.h"
 
 #if defined(OS_ANDROID)
@@ -56,7 +58,7 @@
       base::Callback<void(const base::WeakPtr<VideoCaptureController>&)>;
 
   VideoCaptureManager(
-      std::unique_ptr<media::VideoCaptureDeviceFactory> factory,
+      std::unique_ptr<media::VideoCaptureSystem> capture_system,
       scoped_refptr<base::SingleThreadTaskRunner> device_task_runner);
 
   // AddVideoCaptureObserver() can be called only before any devices are opened.
@@ -163,7 +165,7 @@
 
   // Gets a weak reference to the device factory, used for tests.
   media::VideoCaptureDeviceFactory* video_capture_device_factory() const {
-    return video_capture_device_factory_.get();
+    return video_capture_system_->video_capture_device_factory();
   }
 
 #if defined(OS_WIN)
@@ -197,13 +199,12 @@
 
   using EnumerationCallback =
       base::Callback<void(const media::VideoCaptureDeviceDescriptors&)>;
+  // Asynchronously obtains descriptors for the available devices.
+  // As a side-effect, updates |devices_info_cache_|.
   void EnumerateDevices(const EnumerationCallback& client_callback);
 
   // Implementation of BuildableVideoCaptureDevice::Callbacks:
-  const media::VideoCaptureDeviceDescriptor* LookupDeviceDescriptor(
-      const std::string& id) override;
-  void WillStartDevice(media::VideoFacingMode facing_mode) override;
-  void DidStartDevice(VideoCaptureController* controller) override;
+  void OnDeviceStarted(VideoCaptureController* controller) override;
   void OnDeviceStartFailed(VideoCaptureController* controller) override;
   void OnDeviceStartAborted() override;
 
@@ -216,32 +217,24 @@
 
  private:
   class CaptureDeviceStartRequest;
-  struct DeviceInfo;
 
   using SessionMap = std::map<media::VideoCaptureSessionId, MediaStreamDevice>;
-  using DeviceInfos = std::vector<DeviceInfo>;
   using DeviceStartQueue = std::list<CaptureDeviceStartRequest>;
   using VideoCaptureDeviceDescriptor = media::VideoCaptureDeviceDescriptor;
   using VideoCaptureDeviceDescriptors = media::VideoCaptureDeviceDescriptors;
 
   ~VideoCaptureManager() override;
 
+  void OnDeviceInfosReceived(
+      base::ElapsedTimer* timer,
+      const EnumerationCallback& client_callback,
+      const std::vector<media::VideoCaptureDeviceInfo>& device_infos);
+
   // Helpers to report an event to our Listener.
   void OnOpened(MediaStreamType type,
                 media::VideoCaptureSessionId capture_session_id);
   void OnClosed(MediaStreamType type,
                 media::VideoCaptureSessionId capture_session_id);
-  void OnDevicesInfoEnumerated(base::ElapsedTimer* timer,
-                               const EnumerationCallback& client_callback,
-                               const DeviceInfos& new_devices_info_cache);
-
-  // Consolidates the cached devices list with the list of currently connected
-  // devices in the system |names_snapshot|. Retrieves the supported formats of
-  // the new devices and sends the new cache to OnDevicesInfoEnumerated().
-  void ConsolidateDevicesInfoOnDeviceThread(
-      base::Callback<void(const DeviceInfos&)> on_devices_enumerated_callback,
-      const DeviceInfos& old_device_info_cache,
-      std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors_snapshot);
 
   // Checks to see if |controller| has no clients left. If so, remove it from
   // the list of controllers, and delete it asynchronously. |controller| may be
@@ -260,7 +253,7 @@
       VideoCaptureController* controller) const;
 
   // Finds the device info by |id| in |devices_info_cache_|, or nullptr.
-  DeviceInfo* GetDeviceInfoById(const std::string& id);
+  media::VideoCaptureDeviceInfo* GetDeviceInfoById(const std::string& id);
 
   // Finds a VideoCaptureController for the indicated |capture_session_id|,
   // creating a fresh one if necessary. Returns nullptr if said
@@ -320,19 +313,14 @@
 
   // Device creation factory injected on construction from MediaStreamManager or
   // from the test harness.
-  std::unique_ptr<media::VideoCaptureDeviceFactory>
-      video_capture_device_factory_;
+  std::unique_ptr<media::VideoCaptureSystem> video_capture_system_;
 
   base::ObserverList<media::VideoCaptureObserver> capture_observers_;
 
-  // Local cache of the enumerated video capture devices' names and capture
-  // supported formats. A snapshot of the current devices and their capabilities
-  // is composed in VideoCaptureDeviceFactory::EnumerateDeviceNames() and
-  // ConsolidateDevicesInfoOnDeviceThread(), and this snapshot is used to update
-  // this list in OnDevicesInfoEnumerated(). GetDeviceSupportedFormats() will
+  // Local cache of the enumerated DeviceInfos. GetDeviceSupportedFormats() will
   // use this list if the device is not started, otherwise it will retrieve the
   // active device capture format from the VideoCaptureController associated.
-  DeviceInfos devices_info_cache_;
+  std::vector<media::VideoCaptureDeviceInfo> devices_info_cache_;
 
   // Map used by DesktopCapture.
   std::map<media::VideoCaptureSessionId, gfx::NativeViewId>
diff --git a/content/browser/renderer_host/media/video_capture_manager_unittest.cc b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
index 778b906..e4c9ff78 100644
--- a/content/browser/renderer_host/media/video_capture_manager_unittest.cc
+++ b/content/browser/renderer_host/media/video_capture_manager_unittest.cc
@@ -24,6 +24,7 @@
 #include "content/common/media/media_stream_options.h"
 #include "content/public/test/test_browser_thread_bundle.h"
 #include "media/capture/video/fake_video_capture_device_factory.h"
+#include "media/capture/video/video_capture_system.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -196,12 +197,13 @@
  protected:
   void SetUp() override {
     listener_.reset(new MockMediaStreamProviderListener());
-    vcm_ = new VideoCaptureManager(
-        std::unique_ptr<media::VideoCaptureDeviceFactory>(
-            new WrappedDeviceFactory()),
-        base::ThreadTaskRunnerHandle::Get());
-    video_capture_device_factory_ = static_cast<WrappedDeviceFactory*>(
-        vcm_->video_capture_device_factory());
+    auto video_capture_device_factory =
+        base::MakeUnique<WrappedDeviceFactory>();
+    video_capture_device_factory_ = video_capture_device_factory.get();
+    auto video_capture_system = base::MakeUnique<media::VideoCaptureSystem>(
+        std::move(video_capture_device_factory));
+    vcm_ = new VideoCaptureManager(std::move(video_capture_system),
+                                   base::ThreadTaskRunnerHandle::Get());
     const int32_t kNumberOfFakeDevices = 2;
     video_capture_device_factory_->SetToDefaultDevicesConfig(
         kNumberOfFakeDevices);
diff --git a/content/browser/renderer_host/render_message_filter.cc b/content/browser/renderer_host/render_message_filter.cc
index e1359fb..6276ca0 100644
--- a/content/browser/renderer_host/render_message_filter.cc
+++ b/content/browser/renderer_host/render_message_filter.cc
@@ -135,7 +135,8 @@
                            arraysize(kFilteredMessageClasses)),
       BrowserAssociatedInterface<mojom::RenderMessageFilter>(this, this),
       resource_dispatcher_host_(ResourceDispatcherHostImpl::Get()),
-      bitmap_manager_client_(HostSharedBitmapManager::current()),
+      bitmap_manager_client_(
+          display_compositor::HostSharedBitmapManager::current()),
       request_context_(request_context),
       resource_context_(browser_context->GetResourceContext()),
       render_widget_helper_(render_widget_helper),
@@ -260,6 +261,11 @@
   callback.Run(route_id);
 }
 
+void RenderMessageFilter::GetSharedBitmapManager(
+    cc::mojom::SharedBitmapManagerAssociatedRequest request) {
+  bitmap_manager_client_.Bind(std::move(request));
+}
+
 #if defined(OS_MACOSX)
 
 void RenderMessageFilter::OnLoadFont(const FontDescriptor& font,
@@ -290,21 +296,6 @@
 
 #endif  // defined(OS_MACOSX)
 
-void RenderMessageFilter::AllocatedSharedBitmap(
-    mojo::ScopedSharedBufferHandle buffer,
-    const cc::SharedBitmapId& id) {
-  base::SharedMemoryHandle memory_handle;
-  size_t size;
-  MojoResult result = mojo::UnwrapSharedMemoryHandle(
-      std::move(buffer), &memory_handle, &size, NULL);
-  DCHECK_EQ(result, MOJO_RESULT_OK);
-  bitmap_manager_client_.ChildAllocatedSharedBitmap(size, memory_handle, id);
-}
-
-void RenderMessageFilter::DeletedSharedBitmap(const cc::SharedBitmapId& id) {
-  bitmap_manager_client_.ChildDeletedSharedBitmap(id);
-}
-
 #if defined(OS_LINUX)
 void RenderMessageFilter::SetThreadPriorityOnFileThread(
     base::PlatformThreadId ns_tid,
diff --git a/content/browser/renderer_host/render_message_filter.h b/content/browser/renderer_host/render_message_filter.h
index d025771..55de0db1 100644
--- a/content/browser/renderer_host/render_message_filter.h
+++ b/content/browser/renderer_host/render_message_filter.h
@@ -20,8 +20,8 @@
 #include "base/strings/string16.h"
 #include "build/build_config.h"
 #include "cc/resources/shared_bitmap_manager.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/common/cache_storage/cache_storage_types.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/common/render_message_filter.mojom.h"
 #include "content/public/browser/browser_associated_interface.h"
 #include "content/public/browser/browser_message_filter.h"
@@ -120,9 +120,8 @@
   void CreateFullscreenWidget(
       int opener_id,
       const CreateFullscreenWidgetCallback& callback) override;
-  void AllocatedSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
-                             const cc::SharedBitmapId& id) override;
-  void DeletedSharedBitmap(const cc::SharedBitmapId& id) override;
+  void GetSharedBitmapManager(
+      cc::mojom::SharedBitmapManagerAssociatedRequest request) override;
 
   // Message handlers called on the browser IO thread:
   void OnHasGpuProcess(IPC::Message* reply);
@@ -165,7 +164,7 @@
   // than we do.
   ResourceDispatcherHostImpl* resource_dispatcher_host_;
 
-  HostSharedBitmapManagerClient bitmap_manager_client_;
+  display_compositor::HostSharedBitmapManagerClient bitmap_manager_client_;
 
   // Contextual information to be used for requests created here.
   scoped_refptr<net::URLRequestContextGetter> request_context_;
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index 09711bf..7f26dc84 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -30,6 +30,7 @@
 #include "build/build_config.h"
 #include "cc/base/switches.h"
 #include "cc/output/compositor_frame.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 #include "content/browser/bad_message.h"
 #include "content/browser/browser_plugin/browser_plugin_guest.h"
@@ -57,7 +58,6 @@
 #include "content/common/cursors/webcursor.h"
 #include "content/common/drag_messages.h"
 #include "content/common/frame_messages.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/common/input_messages.h"
 #include "content/common/resize_params.h"
 #include "content/common/text_input_state.h"
@@ -2104,7 +2104,8 @@
   DCHECK(!size.IsEmpty());
 
   std::unique_ptr<cc::SharedBitmap> bitmap =
-      HostSharedBitmapManager::current()->GetSharedBitmapFromId(size, id);
+      display_compositor::HostSharedBitmapManager::current()
+          ->GetSharedBitmapFromId(size, id);
   if (!bitmap) {
     bad_message::ReceivedBadMessage(GetProcess(),
                                     bad_message::RWH_SHARED_BITMAP);
diff --git a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
index 0f4ca71..2b7a0e2 100644
--- a/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
+++ b/content/browser/renderer_host/render_widget_host_view_aura_unittest.cc
@@ -32,6 +32,7 @@
 #include "cc/test/begin_frame_args_test.h"
 #include "cc/test/fake_external_begin_frame_source.h"
 #include "components/display_compositor/gl_helper.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/browser/browser_thread_impl.h"
 #include "content/browser/compositor/test/no_transport_image_transport_factory.h"
 #include "content/browser/frame_host/render_widget_host_view_guest.h"
@@ -49,7 +50,6 @@
 #include "content/browser/renderer_host/render_widget_host_view_frame_subscriber.h"
 #include "content/browser/renderer_host/text_input_manager.h"
 #include "content/browser/web_contents/web_contents_view_aura.h"
-#include "content/common/host_shared_bitmap_manager.h"
 #include "content/common/input/synthetic_web_input_event_builders.h"
 #include "content/common/input_messages.h"
 #include "content/common/text_input_state.h"
@@ -2671,7 +2671,8 @@
   size_t renderer_count = max_renderer_frames + 1;
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
-  DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
+  DCHECK_EQ(0u, display_compositor::HostSharedBitmapManager::current()
+                    ->AllocatedBitmapCount());
 
   std::unique_ptr<RenderWidgetHostImpl* []> hosts(
       new RenderWidgetHostImpl*[renderer_count]);
@@ -2810,8 +2811,8 @@
   int handles_per_frame = 5;
   RendererFrameManager::GetInstance()->set_max_handles(handles_per_frame * 2);
 
-  HostSharedBitmapManagerClient bitmap_client(
-      HostSharedBitmapManager::current());
+  display_compositor::HostSharedBitmapManagerClient bitmap_client(
+      display_compositor::HostSharedBitmapManager::current());
 
   for (size_t i = 0; i < (renderer_count - 1) * handles_per_frame; i++) {
     bitmap_client.ChildAllocatedSharedBitmap(
@@ -2844,7 +2845,8 @@
   size_t renderer_count = max_renderer_frames + 1;
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
-  DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
+  DCHECK_EQ(0u, display_compositor::HostSharedBitmapManager::current()
+                    ->AllocatedBitmapCount());
 
   std::unique_ptr<RenderWidgetHostImpl* []> hosts(
       new RenderWidgetHostImpl*[renderer_count]);
@@ -2916,7 +2918,8 @@
   size_t renderer_count = kMaxRendererFrames;
   gfx::Rect view_rect(100, 100);
   gfx::Size frame_size = view_rect.size();
-  DCHECK_EQ(0u, HostSharedBitmapManager::current()->AllocatedBitmapCount());
+  DCHECK_EQ(0u, display_compositor::HostSharedBitmapManager::current()
+                    ->AllocatedBitmapCount());
 
   std::unique_ptr<RenderWidgetHostImpl* []> hosts(
       new RenderWidgetHostImpl*[renderer_count]);
diff --git a/content/browser/renderer_host/renderer_frame_manager.cc b/content/browser/renderer_host/renderer_frame_manager.cc
index 7650e32..e8944e4 100644
--- a/content/browser/renderer_host/renderer_frame_manager.cc
+++ b/content/browser/renderer_host/renderer_frame_manager.cc
@@ -15,7 +15,7 @@
 #include "base/memory/shared_memory.h"
 #include "base/sys_info.h"
 #include "build/build_config.h"
-#include "content/common/host_shared_bitmap_manager.h"
+#include "components/display_compositor/host_shared_bitmap_manager.h"
 #include "content/public/common/content_features.h"
 
 namespace content {
@@ -135,8 +135,9 @@
 void RendererFrameManager::CullUnlockedFrames(size_t saved_frame_limit) {
   if (unlocked_frames_.size() + locked_frames_.size() > 0) {
     float handles_per_frame =
-        HostSharedBitmapManager::current()->AllocatedBitmapCount() * 1.0f /
-        (unlocked_frames_.size() + locked_frames_.size());
+        display_compositor::HostSharedBitmapManager::current()
+            ->AllocatedBitmapCount() *
+        1.0f / (unlocked_frames_.size() + locked_frames_.size());
 
     saved_frame_limit = std::max(
         1,
diff --git a/content/browser/tracing/memory_tracing_browsertest.cc b/content/browser/tracing/memory_tracing_browsertest.cc
index fe96666..a5f8b8a2 100644
--- a/content/browser/tracing/memory_tracing_browsertest.cc
+++ b/content/browser/tracing/memory_tracing_browsertest.cc
@@ -302,7 +302,8 @@
 #endif  // !defined(GOOGLE_CHROME_BUILD)
 
 // Non-deterministic races under TSan. crbug.com/529678
-#if defined(THREAD_SANITIZER)
+// Flaky on Linux. crbug.com/709524
+#if defined(THREAD_SANITIZER) || defined(OS_LINUX)
 #define MAYBE_BrowserInitiatedDump DISABLED_BrowserInitiatedDump
 #else
 #define MAYBE_BrowserInitiatedDump BrowserInitiatedDump
diff --git a/content/child/BUILD.gn b/content/child/BUILD.gn
index 38b71e5..a63855b1 100644
--- a/content/child/BUILD.gn
+++ b/content/child/BUILD.gn
@@ -66,8 +66,6 @@
     "child_process_sandbox_support_impl_shm_linux.cc",
     "child_resource_message_filter.cc",
     "child_resource_message_filter.h",
-    "child_shared_bitmap_manager.cc",
-    "child_shared_bitmap_manager.h",
     "child_thread_impl.cc",
     "child_thread_impl.h",
     "content_child_helpers.cc",
diff --git a/content/common/BUILD.gn b/content/common/BUILD.gn
index 4295af4..567121c 100644
--- a/content/common/BUILD.gn
+++ b/content/common/BUILD.gn
@@ -150,8 +150,6 @@
     "generic_shared_memory_id_generator.cc",
     "generic_shared_memory_id_generator.h",
     "gin_java_bridge_messages.h",
-    "host_shared_bitmap_manager.cc",
-    "host_shared_bitmap_manager.h",
     "in_process_child_thread_params.cc",
     "in_process_child_thread_params.h",
     "indexed_db/indexed_db_constants.h",
diff --git a/content/common/render_message_filter.mojom b/content/common/render_message_filter.mojom
index 78c93ef..0e6cfc3 100644
--- a/content/common/render_message_filter.mojom
+++ b/content/common/render_message_filter.mojom
@@ -4,6 +4,7 @@
 
 module content.mojom;
 
+import "cc/ipc/shared_bitmap_manager.mojom";
 import "content/common/native_types.mojom";
 import "content/public/common/window_container_type.mojom";
 import "gpu/ipc/common/mailbox.mojom";
@@ -94,14 +95,5 @@
   [Sync] CreateFullscreenWidget(int32 opener_id)
       => (int32 route_id);
 
-  // The 2 following methods belong to a future CC related mojom.
-  // For now they need to be part of this channel associated interface to
-  // prevent running into message ordering issues (CC trying to access a shared
-  // bitmap before the registration message below made it to the browser).
-  //
-  // Informs the browser that the child allocated a shared bitmap.
-  AllocatedSharedBitmap(handle<shared_buffer> buffer, gpu.mojom.Mailbox id);
-
-  // Informs the browser that the child deleted a shared bitmap.
-  DeletedSharedBitmap(gpu.mojom.Mailbox id);
+  GetSharedBitmapManager(associated cc.mojom.SharedBitmapManager& bitmap_manager);
 };
diff --git a/content/common/service_manager/service_manager_connection_impl.cc b/content/common/service_manager/service_manager_connection_impl.cc
index ea9b4f4..9e81238 100644
--- a/content/common/service_manager/service_manager_connection_impl.cc
+++ b/content/common/service_manager/service_manager_connection_impl.cc
@@ -139,11 +139,6 @@
  private:
   friend class base::RefCountedThreadSafe<IOThreadContext>;
 
-  struct PendingRequest {
-    std::string service_name;
-    service_manager::mojom::ServiceRequest request;
-  };
-
   class MessageLoopObserver : public base::MessageLoop::DestructionObserver {
    public:
     explicit MessageLoopObserver(base::WeakPtr<IOThreadContext> context)
@@ -261,16 +256,6 @@
     DCHECK(io_thread_checker_.CalledOnValidThread());
     auto result = request_handlers_.insert(std::make_pair(name, handler));
     DCHECK(result.second);
-    auto iter = pending_requests_.begin();
-    while (iter != pending_requests_.end()) {
-      if ((*iter)->service_name == name) {
-        std::unique_ptr<PendingRequest> pending_request = std::move(*iter);
-        iter = pending_requests_.erase(iter);
-        handler.Run(std::move(pending_request->request));
-      } else {
-        ++iter;
-      }
-    }
   }
 
   /////////////////////////////////////////////////////////////////////////////
@@ -344,14 +329,8 @@
                      const std::string& name) override {
     DCHECK(io_thread_checker_.CalledOnValidThread());
     auto it = request_handlers_.find(name);
-    if (it == request_handlers_.end()) {
-      std::unique_ptr<PendingRequest> pending_request =
-          base::MakeUnique<PendingRequest>();
-      pending_request->service_name = name;
-      pending_request->request = std::move(request);
-      pending_requests_.push_back(std::move(pending_request));
-      return;
-    }
+    DCHECK(it != request_handlers_.end())
+        << "Can't create service " << name << ". No handler found.";
     it->second.Run(std::move(request));
   }
 
@@ -413,10 +392,6 @@
       embedded_services_;
   std::unordered_map<std::string, ServiceRequestHandler> request_handlers_;
 
-  // Requests before the service have been registered are added here. Typically
-  // there are very few elements, so we use a vector.
-  std::vector<std::unique_ptr<PendingRequest>> pending_requests_;
-
   mojo::Binding<mojom::Child> child_binding_;
 
   base::WeakPtrFactory<IOThreadContext> weak_factory_;
diff --git a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
index 4499622f..2ad7b8a 100644
--- a/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
+++ b/content/public/android/java/src/org/chromium/content/browser/ChildProcessConnectionImpl.java
@@ -12,7 +12,6 @@
 import android.content.pm.ServiceInfo;
 import android.os.Build;
 import android.os.Bundle;
-import android.os.DeadObjectException;
 import android.os.IBinder;
 import android.os.RemoteException;
 
@@ -541,13 +540,8 @@
     }
 
     @VisibleForTesting
-    public boolean crashServiceForTesting() throws RemoteException {
-        try {
-            mService.crashIntentionallyForTesting();
-        } catch (DeadObjectException e) {
-            return true;
-        }
-        return false;
+    public void crashServiceForTesting() throws RemoteException {
+        mService.crashIntentionallyForTesting();
     }
 
     @VisibleForTesting
diff --git a/content/public/android/java/src/org/chromium/content/common/IGpuProcessCallback.aidl b/content/public/android/java/src/org/chromium/content/common/IGpuProcessCallback.aidl
index 5b2de4c..03e906d 100644
--- a/content/public/android/java/src/org/chromium/content/common/IGpuProcessCallback.aidl
+++ b/content/public/android/java/src/org/chromium/content/common/IGpuProcessCallback.aidl
@@ -9,7 +9,7 @@
 
 interface IGpuProcessCallback {
 
-  void forwardSurfaceForSurfaceRequest(
+  oneway void forwardSurfaceForSurfaceRequest(
       in UnguessableToken requestToken, in Surface surface);
 
   SurfaceWrapper getViewSurface(int surfaceId);
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
index 4de2bfa..7e08d1e 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/ChildProcessLauncherTest.java
@@ -103,7 +103,7 @@
         assertEquals(0, ChildProcessLauncher.connectedServicesCountForTesting());
 
         // Crash the service.
-        assertTrue(connection.crashServiceForTesting());
+        connection.crashServiceForTesting();
 
         // Verify that the connection gets cleaned-up.
         CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable<Integer>() {
@@ -153,7 +153,7 @@
                 });
 
         // Crash the service.
-        assertTrue(connection.crashServiceForTesting());
+        connection.crashServiceForTesting();
 
         // Verify that the connection gets cleaned-up.
         CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable<Integer>() {
@@ -217,7 +217,7 @@
                 });
 
         // Crash the service.
-        assertTrue(connection.crashServiceForTesting());
+        connection.crashServiceForTesting();
 
         // Verify that a new service is started for the pending spawn.
         CriteriaHelper.pollInstrumentationThread(Criteria.equals(0, new Callable<Integer>() {
diff --git a/content/public/test/mock_render_thread.cc b/content/public/test/mock_render_thread.cc
index 393c6fe..51ee9d4 100644
--- a/content/public/test/mock_render_thread.cc
+++ b/content/public/test/mock_render_thread.cc
@@ -77,12 +77,8 @@
     NOTREACHED();
   }
 
-  void AllocatedSharedBitmap(mojo::ScopedSharedBufferHandle buffer,
-                             const cc::SharedBitmapId& id) override {
-    NOTREACHED();
-  }
-
-  void DeletedSharedBitmap(const cc::SharedBitmapId& id) override {
+  void GetSharedBitmapManager(
+      cc::mojom::SharedBitmapManagerAssociatedRequest request) override {
     NOTREACHED();
   }
 
diff --git a/content/renderer/BUILD.gn b/content/renderer/BUILD.gn
index b00ed864..708387c 100644
--- a/content/renderer/BUILD.gn
+++ b/content/renderer/BUILD.gn
@@ -476,6 +476,7 @@
     "//services/device/public/interfaces:constants",
     "//services/service_manager/public/cpp",
     "//services/service_manager/public/interfaces",
+    "//services/ui/public/cpp/bitmap",
     "//services/ui/public/cpp/gpu",
     "//skia",
     "//storage/common",
@@ -792,6 +793,8 @@
       "pepper/pepper_audio_encoder_host.h",
       "pepper/pepper_audio_input_host.cc",
       "pepper/pepper_audio_input_host.h",
+      "pepper/pepper_audio_output_host.cc",
+      "pepper/pepper_audio_output_host.h",
       "pepper/pepper_broker.cc",
       "pepper/pepper_broker.h",
       "pepper/pepper_browser_connection.cc",
@@ -822,6 +825,8 @@
       "pepper/pepper_platform_audio_input.h",
       "pepper/pepper_platform_audio_output.cc",
       "pepper/pepper_platform_audio_output.h",
+      "pepper/pepper_platform_audio_output_dev.cc",
+      "pepper/pepper_platform_audio_output_dev.h",
       "pepper/pepper_platform_camera_device.cc",
       "pepper/pepper_platform_camera_device.h",
       "pepper/pepper_platform_video_capture.cc",
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.cc b/content/renderer/media/media_stream_constraints_util_video_content.cc
index 7e879e0..9f21ec5 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_content.cc
@@ -16,10 +16,10 @@
 
 namespace content {
 
-// TODO(guidou): Change default width and height to larger values. See
-// http://crbug.com/257097.
-const int kDefaultScreenCastWidth = MediaStreamVideoSource::kDefaultWidth;
-const int kDefaultScreenCastHeight = MediaStreamVideoSource::kDefaultHeight;
+const int kDefaultScreenCastWidth = 2880;
+const int kDefaultScreenCastHeight = 1800;
+const double kDefaultScreenCastAspectRatio =
+    static_cast<double>(kDefaultScreenCastWidth) / kDefaultScreenCastHeight;
 const double kDefaultScreenCastFrameRate =
     MediaStreamVideoSource::kDefaultFrameRate;
 const int kMinScreenCastDimension = 1;
@@ -224,12 +224,33 @@
     const blink::WebMediaTrackConstraintSet& basic_constraint_set) {
   std::string device_id = SelectDeviceIDFromCandidates(candidates.device_id_set,
                                                        basic_constraint_set);
+  // If a maximum width or height is explicitly given, use them as default.
+  // If only one of them is given, use the default aspect ratio to determine the
+  // other default value.
   // TODO(guidou): Use native screen-capture resolution as default.
   // http://crbug.com/257097
+  int default_height = kDefaultScreenCastHeight;
+  int default_width = kDefaultScreenCastWidth;
+  bool has_explicit_max_height =
+      candidates.resolution_set.max_height() < kMaxScreenCastDimension;
+  bool has_explicit_max_width =
+      candidates.resolution_set.max_width() < kMaxScreenCastDimension;
+  if (has_explicit_max_height && has_explicit_max_width) {
+    default_height = candidates.resolution_set.max_height();
+    default_width = candidates.resolution_set.max_width();
+  } else if (has_explicit_max_height) {
+    default_height = candidates.resolution_set.max_height();
+    default_width = static_cast<int>(
+        std::round(default_height * kDefaultScreenCastAspectRatio));
+  } else if (has_explicit_max_width) {
+    default_width = candidates.resolution_set.max_width();
+    default_height = static_cast<int>(
+        std::round(default_width / kDefaultScreenCastAspectRatio));
+  }
   media::VideoCaptureParams capture_params =
-      SelectVideoCaptureParamsFromCandidates(
-          candidates, basic_constraint_set, kDefaultScreenCastHeight,
-          kDefaultScreenCastWidth, kDefaultScreenCastFrameRate);
+      SelectVideoCaptureParamsFromCandidates(candidates, basic_constraint_set,
+                                             default_height, default_width,
+                                             kDefaultScreenCastFrameRate);
 
   base::Optional<bool> noise_reduction = SelectNoiseReductionFromCandidates(
       candidates.noise_reduction_set, basic_constraint_set);
diff --git a/content/renderer/media/media_stream_constraints_util_video_content.h b/content/renderer/media/media_stream_constraints_util_video_content.h
index fb7a2105..28f5b739 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content.h
+++ b/content/renderer/media/media_stream_constraints_util_video_content.h
@@ -19,6 +19,7 @@
 
 CONTENT_EXPORT extern const int kDefaultScreenCastWidth;
 CONTENT_EXPORT extern const int kDefaultScreenCastHeight;
+CONTENT_EXPORT extern const double kDefaultScreenCastAspectRatio;
 CONTENT_EXPORT extern const double kDefaultScreenCastFrameRate;
 CONTENT_EXPORT extern const int kMinScreenCastDimension;
 CONTENT_EXPORT extern const int kMaxScreenCastDimension;
diff --git a/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
index 79d69b1..4d9fb32 100644
--- a/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
+++ b/content/renderer/media/media_stream_constraints_util_video_content_unittest.cc
@@ -16,10 +16,6 @@
 
 namespace {
 
-const double kDefaultScreenCastAspectRatio =
-    static_cast<double>(kDefaultScreenCastWidth) /
-    static_cast<double>(kDefaultScreenCastHeight);
-
 void CheckNonResolutionDefaults(const VideoCaptureSettings& result) {
   EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
   EXPECT_EQ(base::Optional<bool>(), result.noise_reduction());
@@ -268,7 +264,7 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinHeight) {
   constraint_factory_.Reset();
-  const int kHeight = 1000;
+  const int kHeight = 2000;
   constraint_factory_.basic().height.setMin(kHeight);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
@@ -299,66 +295,73 @@
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxHeight) {
-  constraint_factory_.Reset();
-  const int kHeight = 1000;
-  constraint_factory_.basic().height.setMax(kHeight);
-  auto result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kHeight is greater that the default, so expect the default.
-  EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
-  EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
-  CheckNonResolutionDefaults(result);
-  EXPECT_EQ(1.0 / kHeight, result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(kMaxScreenCastDimension,
-            result.track_adapter_settings().max_aspect_ratio);
-  CheckTrackAdapterSettingsEqualsFormat(result);
-
-  const int kSmallHeight = 100;
-  constraint_factory_.basic().height.setMax(kSmallHeight);
-  result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kSmallHeight is less that the default, so expect kSmallHeight.
-  EXPECT_EQ(kSmallHeight, result.Height());
-  EXPECT_EQ(std::round(kSmallHeight * kDefaultScreenCastAspectRatio),
-            result.Width());
-  CheckNonResolutionDefaults(result);
-  EXPECT_EQ(1.0 / kSmallHeight,
-            result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(kMaxScreenCastDimension,
-            result.track_adapter_settings().max_aspect_ratio);
-  CheckTrackAdapterSettingsEqualsFormat(result);
-}
-
-TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryHeightRange) {
-  constraint_factory_.Reset();
+  // kMaxHeight smaller than the default.
   {
-    const int kMinHeight = 300;
-    const int kMaxHeight = 1000;
-    constraint_factory_.basic().height.setMin(kMinHeight);
+    constraint_factory_.Reset();
+    const int kMaxHeight = kDefaultScreenCastHeight - 100;
     constraint_factory_.basic().height.setMax(kMaxHeight);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The range includes the default, so expect the default.
-    EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
-    EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
+              result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(1.0 / kMaxHeight,
               result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(static_cast<double>(kMaxScreenCastDimension) / kMinHeight,
+    EXPECT_EQ(kMaxScreenCastDimension,
               result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
+  // kMaxHeight greater than the default.
   {
-    const int kMinHeight = 900;
-    const int kMaxHeight = 1000;
+    constraint_factory_.Reset();
+    const int kMaxHeight = kDefaultScreenCastHeight + 100;
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kMaxHeight, result.Height());
+    EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
+              result.Width());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxHeight,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(kMaxScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+
+  // kMaxHeight greater than the maximum allowed.
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().height.setMax(kMaxScreenCastDimension + 100);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
+    EXPECT_EQ(
+        std::round(kDefaultScreenCastHeight * kDefaultScreenCastAspectRatio),
+        result.Width());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(kMaxScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryHeightRange) {
+  // Range includes the default.
+  {
+    constraint_factory_.Reset();
+    const int kMinHeight = kDefaultScreenCastHeight - 100;
+    const int kMaxHeight = kDefaultScreenCastHeight + 100;
     constraint_factory_.basic().height.setMin(kMinHeight);
     constraint_factory_.basic().height.setMax(kMaxHeight);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is greater than the default, so expect the range minimum.
-    EXPECT_EQ(kMinHeight, result.Height());
-    EXPECT_EQ(std::round(kMinHeight * kDefaultScreenCastAspectRatio),
+    EXPECT_EQ(kMaxHeight, result.Height());
+    EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
               result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(1.0 / kMaxHeight,
@@ -368,14 +371,35 @@
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
+  // The whole range is greater than the default.
   {
-    const int kMinHeight = 300;
-    const int kMaxHeight = 400;
+    constraint_factory_.Reset();
+    const int kMinHeight = kDefaultScreenCastHeight + 100;
+    const int kMaxHeight = kDefaultScreenCastHeight + 200;
     constraint_factory_.basic().height.setMin(kMinHeight);
     constraint_factory_.basic().height.setMax(kMaxHeight);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is less than the default, so expect the range maximum.
+    EXPECT_EQ(kMaxHeight, result.Height());
+    EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
+              result.Width());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxHeight,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(static_cast<double>(kMaxScreenCastDimension) / kMinHeight,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+
+  // The whole range is less than the default.
+  {
+    constraint_factory_.Reset();
+    const int kMinHeight = kDefaultScreenCastHeight - 200;
+    const int kMaxHeight = kDefaultScreenCastHeight - 100;
+    constraint_factory_.basic().height.setMin(kMinHeight);
+    constraint_factory_.basic().height.setMax(kMaxHeight);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMaxHeight, result.Height());
     EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
               result.Width());
@@ -398,7 +422,7 @@
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kIdealHeight, result.Height());
     // When ideal height is given, the algorithm returns a width that is closest
-    // to height * kDefaultAspectRatio.
+    // to height * kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kIdealHeight * kDefaultScreenCastAspectRatio),
               result.Width());
     CheckNonResolutionDefaults(result);
@@ -416,7 +440,7 @@
     EXPECT_TRUE(result.HasValue());
     // Ideal height is greater than the maximum, expect maximum.
     EXPECT_EQ(kMaxHeight, result.Height());
-    // Expect closest to kMaxHeight * kDefaultAspectRatio.
+    // Expect closest to kMaxHeight * kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
               result.Width());
     CheckNonResolutionDefaults(result);
@@ -438,7 +462,7 @@
     EXPECT_TRUE(result.HasValue());
     // Ideal height is less than the minimum, expect minimum.
     EXPECT_EQ(kMinHeight, result.Height());
-    // Expect closest to kMinHeight * kDefaultAspectRatio.
+    // Expect closest to kMinHeight * kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kMinHeight * kDefaultScreenCastAspectRatio),
               result.Width());
     CheckNonResolutionDefaults(result);
@@ -462,36 +486,11 @@
     EXPECT_TRUE(result.HasValue());
     // Ideal height is included in the bounding box.
     EXPECT_EQ(kIdealHeight, result.Height());
-    // Expect width closest to kIdealHeight * kDefaultAspectRatio, which is
-    // outside the box. Closest is max width.
-    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(100.0 / 1000.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().width.setMin(1200);
-    constraint_factory_.basic().width.setMax(2000);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kIdealHeight, result.Height());
-    // kIdealHeight * kDefaultAspectRatio is outside the box. Closest is
-    // min width.
-    EXPECT_EQ(constraint_factory_.basic().width.min(), result.Width());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(1200.0 / 1000.0,
-              result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(2000.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().width.setMin(100);
-    constraint_factory_.basic().width.setMax(500);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kIdealHeight, result.Height());
-    // kIdealHeight * kDefaultAspectRatio is outside the box. Closest is
-    // max width.
-    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
+    double default_aspect_ratio =
+        static_cast<double>(constraint_factory_.basic().width.max()) /
+        constraint_factory_.basic().height.max();
+    // Expect width closest to kIdealHeight * default aspect ratio.
+    EXPECT_EQ(std::round(kIdealHeight * default_aspect_ratio), result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(100.0 / 1000.0, result.track_adapter_settings().min_aspect_ratio);
     EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
@@ -510,7 +509,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMaxHeight, result.Height());
-    // Expect width closest to kMaxHeight * kDefaultAspectRatio, which is
+    // Expect width closest to kMaxHeight * default aspect ratio, which is
     // outside the box. Closest it max width.
     EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
     CheckNonResolutionDefaults(result);
@@ -518,33 +517,6 @@
               result.track_adapter_settings().min_aspect_ratio);
     EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().width.setMin(1500);
-    constraint_factory_.basic().width.setMax(2000);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kMaxHeight, result.Height());
-    // kMaxHeight * kDefaultAspectRatio is outside the box. Closest is min
-    // width.
-    EXPECT_EQ(constraint_factory_.basic().width.min(), result.Width());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(1500.0 / kMaxHeight,
-              result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(2000.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().width.setMin(100);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kMaxHeight, result.Height());
-    // kMaxHeight * kDefaultAspectRatio is within the width limits.
-    EXPECT_EQ(std::round(kMaxHeight * kDefaultScreenCastAspectRatio),
-              result.Width());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(100.0 / kMaxHeight,
-              result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(2000.0 / 500.0, result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
   // Ideal outside the constrained set, closest to a single point.
@@ -587,7 +559,7 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMinWidth) {
   constraint_factory_.Reset();
-  const int kWidth = 1000;
+  const int kWidth = 3000;
   constraint_factory_.basic().width.setMin(kWidth);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
@@ -620,67 +592,77 @@
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryMaxWidth) {
-  constraint_factory_.Reset();
-  const int kWidth = 1000;
-  constraint_factory_.basic().width.setMax(kWidth);
-  auto result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kWidth is greater that the default, so expect the default.
-  EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
-  EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
-  CheckNonResolutionDefaults(result);
-  EXPECT_EQ(1.0 / kMaxScreenCastDimension,
-            result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(static_cast<double>(kWidth) / kMinScreenCastDimension,
-            result.track_adapter_settings().max_aspect_ratio);
-  CheckTrackAdapterSettingsEqualsFormat(result);
-
-  const int kSmallWidth = 100;
-  constraint_factory_.basic().width.setMax(kSmallWidth);
-  result = SelectSettings();
-  EXPECT_TRUE(result.HasValue());
-  // kSmallWidth is less that the default, so expect kSmallWidth.
-  EXPECT_EQ(kSmallWidth, result.Width());
-  EXPECT_EQ(std::round(kSmallWidth / kDefaultScreenCastAspectRatio),
-            result.Height());
-  CheckNonResolutionDefaults(result);
-  EXPECT_EQ(1.0 / kMaxScreenCastDimension,
-            result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(static_cast<double>(kSmallWidth) / kMinScreenCastDimension,
-            result.track_adapter_settings().max_aspect_ratio);
-  CheckTrackAdapterSettingsEqualsFormat(result);
-}
-
-TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryWidthRange) {
-  constraint_factory_.Reset();
+  // kMaxWidth less than the default.
   {
-    const int kMinWidth = 300;
-    const int kMaxWidth = 1000;
-    constraint_factory_.basic().width.setMin(kMinWidth);
+    constraint_factory_.Reset();
+    const int kMaxWidth = kDefaultScreenCastWidth - 100;
     constraint_factory_.basic().width.setMax(kMaxWidth);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The range includes the default, so expect the default.
-    EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
-    EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
+    // kSmallWidth is less that the default, so expect kSmallWidth.
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
+              result.Height());
     CheckNonResolutionDefaults(result);
-    EXPECT_EQ(static_cast<double>(kMinWidth) / kMaxScreenCastDimension,
+    EXPECT_EQ(1.0 / kMaxScreenCastDimension,
               result.track_adapter_settings().min_aspect_ratio);
     EXPECT_EQ(static_cast<double>(kMaxWidth) / kMinScreenCastDimension,
               result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
+  // kMaxWidth greater than the default.
   {
-    const int kMinWidth = 900;
-    const int kMaxWidth = 1000;
+    constraint_factory_.Reset();
+    const int kMaxWidth = kDefaultScreenCastWidth + 100;
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // kSmallWidth is less that the default, so expect kSmallWidth.
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
+              result.Height());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(static_cast<double>(kMaxWidth) / kMinScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+
+  // kMaxWidth greater than the maximum allowed (gets ignored).
+  {
+    constraint_factory_.Reset();
+    constraint_factory_.basic().width.setMax(kMaxScreenCastDimension + 100);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
+    // kSmallWidth is less that the default, so expect kSmallWidth.
+    EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
+    EXPECT_EQ(
+        std::round(kDefaultScreenCastWidth / kDefaultScreenCastAspectRatio),
+        result.Height());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(1.0 / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(
+        static_cast<double>(kMaxScreenCastDimension) / kMinScreenCastDimension,
+        result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+}
+
+TEST_F(MediaStreamConstraintsUtilVideoContentTest, MandatoryWidthRange) {
+  // The whole range is less than the default.
+  {
+    constraint_factory_.Reset();
+    const int kMinWidth = kDefaultScreenCastWidth - 200;
+    const int kMaxWidth = kDefaultScreenCastWidth - 100;
     constraint_factory_.basic().width.setMin(kMinWidth);
     constraint_factory_.basic().width.setMax(kMaxWidth);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is greater than the default, so expect the range minimum.
-    EXPECT_EQ(kMinWidth, result.Width());
-    EXPECT_EQ(std::round(kMinWidth / kDefaultScreenCastAspectRatio),
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
               result.Height());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(static_cast<double>(kMinWidth) / kMaxScreenCastDimension,
@@ -690,14 +672,35 @@
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
+  // The range includes the default.
   {
-    const int kMinWidth = 300;
-    const int kMaxWidth = 400;
+    constraint_factory_.Reset();
+    const int kMinWidth = kDefaultScreenCastWidth - 100;
+    const int kMaxWidth = kDefaultScreenCastWidth + 100;
     constraint_factory_.basic().width.setMin(kMinWidth);
     constraint_factory_.basic().width.setMax(kMaxWidth);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // The whole range is less than the default, so expect the range maximum.
+    EXPECT_EQ(kMaxWidth, result.Width());
+    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
+              result.Height());
+    CheckNonResolutionDefaults(result);
+    EXPECT_EQ(static_cast<double>(kMinWidth) / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(static_cast<double>(kMaxWidth) / kMinScreenCastDimension,
+              result.track_adapter_settings().max_aspect_ratio);
+    CheckTrackAdapterSettingsEqualsFormat(result);
+  }
+
+  // The whole range is greater than the default.
+  {
+    constraint_factory_.Reset();
+    const int kMinWidth = kDefaultScreenCastWidth + 100;
+    const int kMaxWidth = kDefaultScreenCastWidth + 200;
+    constraint_factory_.basic().width.setMin(kMinWidth);
+    constraint_factory_.basic().width.setMax(kMaxWidth);
+    auto result = SelectSettings();
+    EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMaxWidth, result.Width());
     EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
               result.Height());
@@ -720,7 +723,7 @@
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kIdealWidth, result.Width());
     // When ideal width is given, the algorithm returns a height that is closest
-    // to width / kDefaultAspectRatio.
+    // to width / kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kIdealWidth / kDefaultScreenCastAspectRatio),
               result.Height());
     CheckNonResolutionDefaults(result);
@@ -737,7 +740,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMaxWidth, result.Width());
-    // Expect closest to kMaxWidth / kDefaultAspectRatio.
+    // Expect closest to kMaxWidth / kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
               result.Height());
     CheckNonResolutionDefaults(result);
@@ -758,7 +761,7 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMinWidth, result.Width());
-    // Expect closest to kMinWidth / kDefaultAspectRatio.
+    // Expect closest to kMinWidth / kDefaultScreenCastAspectRatio.
     EXPECT_EQ(std::round(kMinWidth / kDefaultScreenCastAspectRatio),
               result.Height());
     CheckNonResolutionDefaults(result);
@@ -783,36 +786,11 @@
     EXPECT_TRUE(result.HasValue());
     // Ideal width is included in the bounding box.
     EXPECT_EQ(kIdealWidth, result.Width());
-    // Expect height closest to kIdealWidth / kDefaultAspectRatio, which is
-    // outside the box. Closest is max height.
-    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(1000.0 / 100.0, result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().height.setMin(1200);
-    constraint_factory_.basic().height.setMax(2000);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kIdealWidth, result.Width());
-    // kIdealWidth / kDefaultAspectRatio outside the box. Closest is
-    // min height.
-    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(500.0 / 2000.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(1000.0 / 1200.0,
-              result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().height.setMin(100);
-    constraint_factory_.basic().height.setMax(500);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kIdealWidth, result.Width());
-    // kIdealWidth / kDefaultAspectRatio is outside the box. Closest is max
-    // height.
-    EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
+    // Expect height closest to kIdealWidth / default aspect ratio.
+    double default_aspect_ratio =
+        static_cast<double>(constraint_factory_.basic().width.max()) /
+        constraint_factory_.basic().height.max();
+    EXPECT_EQ(std::round(kIdealWidth / default_aspect_ratio), result.Height());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().min_aspect_ratio);
     EXPECT_EQ(1000.0 / 100.0, result.track_adapter_settings().max_aspect_ratio);
@@ -831,41 +809,14 @@
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
     EXPECT_EQ(kMaxWidth, result.Width());
-    // kMaxWidth / kDefaultAspectRatio is outside the box. Closest is max
-    // height.
+    // kMaxWidth / kDefaultScreenCastAspectRatio is outside the box. Closest is
+    // max height.
     EXPECT_EQ(constraint_factory_.basic().height.max(), result.Height());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(500.0 / 500.0, result.track_adapter_settings().min_aspect_ratio);
     EXPECT_EQ(static_cast<double>(kMaxWidth) / 100.0,
               result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().height.setMin(1500);
-    constraint_factory_.basic().height.setMax(2000);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kMaxWidth, result.Width());
-    // kMaxWidth / kDefaultAspectRatio is outside the box. Closest is
-    // min height.
-    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(500.0 / 2000.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(static_cast<double>(kMaxWidth) / 1500.0,
-              result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
-
-    constraint_factory_.basic().height.setMin(100);
-    result = SelectSettings();
-    EXPECT_TRUE(result.HasValue());
-    EXPECT_EQ(kMaxWidth, result.Width());
-    // kMaxWidth / kDefaultAspectRatio is within the height limits.
-    EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
-              result.Height());
-    CheckNonResolutionDefaults(result);
-    EXPECT_EQ(500.0 / 2000.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(static_cast<double>(kMaxWidth) / 100.0,
-              result.track_adapter_settings().max_aspect_ratio);
-    CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
   // Ideal outside the constrained set, closest to a single point.
@@ -1058,10 +1009,10 @@
     constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // Ideal height is greater than the maximum, expect maximum.
-    EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
-    EXPECT_EQ(std::round(kDefaultScreenCastHeight * kMaxAspectRatio),
-              result.Width());
+    // Ideal aspect ratio is greater than the maximum, expect maximum.
+    EXPECT_EQ(std::round(kDefaultScreenCastWidth / kMaxAspectRatio),
+              result.Height());
+    EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(
         static_cast<double>(kMinScreenCastDimension) / kMaxScreenCastDimension,
@@ -1080,10 +1031,10 @@
     constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
     auto result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // Ideal height is greater than the maximum, expect maximum.
-    EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
-    EXPECT_EQ(std::round(kDefaultScreenCastHeight * kMinAspectRatio),
-              result.Width());
+    // Ideal aspect ratio is less than the maximum, expect minimum.
+    EXPECT_EQ(std::round(kDefaultScreenCastWidth / kMinAspectRatio),
+              result.Height());
+    EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(kMinAspectRatio,
               result.track_adapter_settings().min_aspect_ratio);
@@ -1122,13 +1073,11 @@
     constraint_factory_.basic().width.setMax(5000);
     result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // Ideal aspect-ratio is included in the bounding box, with the value
-    // closest to a standard width or height and largest area being the cut with
-    // the minimum height.
-    EXPECT_EQ(constraint_factory_.basic().height.min(), result.Height());
-    EXPECT_EQ(std::round(constraint_factory_.basic().height.min() *
-                         kIdealAspectRatio),
-              result.Width());
+    // Ideal aspect-ratio is included in the bounding box.
+    EXPECT_EQ(
+        std::round(constraint_factory_.basic().width.max() / kIdealAspectRatio),
+        result.Height());
+    EXPECT_EQ(constraint_factory_.basic().width.max(), result.Width());
     CheckNonResolutionDefaults(result);
     EXPECT_EQ(1000.0 / 5000.0,
               result.track_adapter_settings().min_aspect_ratio);
@@ -1136,20 +1085,21 @@
               result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
 
+    constraint_factory_.Reset();
+    constraint_factory_.basic().aspectRatio.setIdeal(kIdealAspectRatio);
     constraint_factory_.basic().height.setMin(250);
-    constraint_factory_.basic().height.setMax(5000);
     constraint_factory_.basic().width.setMin(250);
-    constraint_factory_.basic().width.setMax(5000);
     result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
-    // Ideal aspect-ratio and default width and height are included in the
-    // bounding box. Preserving default height leads to larger area than
-    // preserving default width.
+    // Ideal aspect-ratio is included in the bounding box. Preserving default
+    // height leads to larger area than preserving default width.
     EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
     EXPECT_EQ(kDefaultScreenCastHeight * kIdealAspectRatio, result.Width());
     CheckNonResolutionDefaults(result);
-    EXPECT_EQ(250.0 / 5000.0, result.track_adapter_settings().min_aspect_ratio);
-    EXPECT_EQ(5000.0 / 250.0, result.track_adapter_settings().max_aspect_ratio);
+    EXPECT_EQ(250.0 / kMaxScreenCastDimension,
+              result.track_adapter_settings().min_aspect_ratio);
+    EXPECT_EQ(kMaxScreenCastDimension / 250.0,
+              result.track_adapter_settings().max_aspect_ratio);
     CheckTrackAdapterSettingsEqualsFormat(result);
   }
 
@@ -1196,10 +1146,11 @@
 
     // Use a box that is bigger and further from the origin to force closeness
     // to a different default dimension.
-    constraint_factory_.basic().height.setMin(1000);
-    constraint_factory_.basic().height.setMax(5000);
-    constraint_factory_.basic().width.setMin(1000);
-    constraint_factory_.basic().width.setMax(5000);
+    constraint_factory_.Reset();
+    constraint_factory_.basic().aspectRatio.setMin(kMinAspectRatio);
+    constraint_factory_.basic().aspectRatio.setMax(kMaxAspectRatio);
+    constraint_factory_.basic().height.setMin(3000);
+    constraint_factory_.basic().width.setMin(3000);
     constraint_factory_.basic().aspectRatio.setIdeal(3.0);
     result = SelectSettings();
     EXPECT_TRUE(result.HasValue());
@@ -1600,20 +1551,22 @@
   advanced1.height.setMin(480);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
-  advanced2.width.setMin(1920);
-  advanced2.height.setMin(1080);
+  const int kMinWidth = 4000;
+  const int kMinHeight = 2000;
+  advanced2.width.setMin(kMinWidth);
+  advanced2.height.setMin(kMinHeight);
   advanced2.googNoiseReduction.setExact(false);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(1920, result.Width());
+  EXPECT_EQ(kMinWidth, result.Width());
   // Preserves default aspect ratio.
   EXPECT_EQ(static_cast<int>(
                 std::round(result.Width() / kDefaultScreenCastAspectRatio)),
             result.Height());
   EXPECT_TRUE(result.noise_reduction() && !*result.noise_reduction());
-  EXPECT_EQ(1920.0 / static_cast<double>(kMaxScreenCastDimension),
+  EXPECT_EQ(kMinWidth / static_cast<double>(kMaxScreenCastDimension),
             result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(static_cast<double>(kMaxScreenCastDimension) / 1080.0,
+  EXPECT_EQ(static_cast<double>(kMaxScreenCastDimension) / kMinHeight,
             result.track_adapter_settings().max_aspect_ratio);
   CheckTrackAdapterSettingsEqualsFormat(result);
 }
@@ -1691,11 +1644,13 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest,
        AdvancedContradictoryMinMaxResolutionFrameRate) {
+  const int kMinHeight = 2600;
+  const int kMinWidth = 2800;
   constraint_factory_.Reset();
   blink::WebMediaTrackConstraintSet& advanced1 =
       constraint_factory_.AddAdvanced();
-  advanced1.width.setMin(800);
-  advanced1.height.setMin(600);
+  advanced1.width.setMin(kMinWidth);
+  advanced1.height.setMin(kMinHeight);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
   advanced2.width.setMax(640);
@@ -1703,12 +1658,13 @@
   advanced2.frameRate.setExact(60.0);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(800, result.Width());
-  EXPECT_EQ(600, result.Height());
+  EXPECT_EQ(std::round(kMinHeight * kDefaultScreenCastAspectRatio),
+            result.Width());
+  EXPECT_EQ(kMinHeight, result.Height());
   EXPECT_EQ(kDefaultScreenCastFrameRate, result.FrameRate());
-  EXPECT_EQ(800.0 / kMaxScreenCastDimension,
+  EXPECT_EQ(static_cast<double>(kMinWidth) / kMaxScreenCastDimension,
             result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(kMaxScreenCastDimension / 600.0,
+  EXPECT_EQ(static_cast<double>(kMaxScreenCastDimension) / kMinHeight,
             result.track_adapter_settings().max_aspect_ratio);
   CheckTrackAdapterSettingsEqualsFormat(result);
 }
@@ -1787,10 +1743,11 @@
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest,
        AdvancedContradictoryWidthFrameRate) {
+  const int kMaxWidth = 1920;
   constraint_factory_.Reset();
   blink::WebMediaTrackConstraintSet& advanced1 =
       constraint_factory_.AddAdvanced();
-  advanced1.width.setMax(1920);
+  advanced1.width.setMax(kMaxWidth);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
   advanced2.width.setMin(2000);
@@ -1800,36 +1757,39 @@
   advanced3.frameRate.setExact(90.0);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
-  EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
+  EXPECT_EQ(kMaxWidth, result.Width());
+  EXPECT_EQ(std::round(kMaxWidth / kDefaultScreenCastAspectRatio),
+            result.Height());
   EXPECT_EQ(90.0, result.FrameRate());
   EXPECT_EQ(
       static_cast<double>(kMinScreenCastDimension) / kMaxScreenCastDimension,
       result.track_adapter_settings().min_aspect_ratio);
-  EXPECT_EQ(1920.0 / kMinScreenCastDimension,
+  EXPECT_EQ(static_cast<double>(kMaxWidth) / kMinScreenCastDimension,
             result.track_adapter_settings().max_aspect_ratio);
   CheckTrackAdapterSettingsEqualsFormat(result);
 }
 
 TEST_F(MediaStreamConstraintsUtilVideoContentTest,
        AdvancedContradictoryHeightFrameRate) {
+  const int kMaxHeight = 2000;
   constraint_factory_.Reset();
   blink::WebMediaTrackConstraintSet& advanced1 =
       constraint_factory_.AddAdvanced();
-  advanced1.height.setMax(1080);
+  advanced1.height.setMax(kMaxHeight);
   blink::WebMediaTrackConstraintSet& advanced2 =
       constraint_factory_.AddAdvanced();
-  advanced2.height.setMin(1500);
+  advanced2.height.setMin(4500);
   advanced2.frameRate.setExact(10.0);
   blink::WebMediaTrackConstraintSet& advanced3 =
       constraint_factory_.AddAdvanced();
   advanced3.frameRate.setExact(60.0);
   auto result = SelectSettings();
   EXPECT_TRUE(result.HasValue());
-  EXPECT_EQ(kDefaultScreenCastWidth, result.Width());
-  EXPECT_EQ(kDefaultScreenCastHeight, result.Height());
+  EXPECT_EQ(kMaxHeight * kDefaultScreenCastAspectRatio, result.Width());
+  // Height defaults to explicitly given max constraint.
+  EXPECT_EQ(kMaxHeight, result.Height());
   EXPECT_EQ(60.0, result.FrameRate());
-  EXPECT_EQ(static_cast<double>(kMinScreenCastDimension) / 1080.0,
+  EXPECT_EQ(static_cast<double>(kMinScreenCastDimension) / kMaxHeight,
             result.track_adapter_settings().min_aspect_ratio);
   EXPECT_EQ(
       static_cast<double>(kMaxScreenCastDimension) / kMinScreenCastDimension,
diff --git a/content/renderer/media/media_stream_video_capturer_source.h b/content/renderer/media/media_stream_video_capturer_source.h
index 8161949..df70468 100644
--- a/content/renderer/media/media_stream_video_capturer_source.h
+++ b/content/renderer/media/media_stream_video_capturer_source.h
@@ -46,6 +46,7 @@
  private:
   friend class CanvasCaptureHandlerTest;
   friend class MediaStreamVideoCapturerSourceTest;
+  friend class MediaStreamVideoCapturerSourceOldConstraintsTest;
 
   // MediaStreamVideoSource overrides.
   void RequestRefreshFrame() override;
diff --git a/content/renderer/media/media_stream_video_capturer_source_unittest.cc b/content/renderer/media/media_stream_video_capturer_source_unittest.cc
index bdae348..b33d5e2 100644
--- a/content/renderer/media/media_stream_video_capturer_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_capturer_source_unittest.cc
@@ -31,6 +31,8 @@
 
 namespace content {
 
+namespace {
+
 class MockVideoCapturerSource : public media::VideoCapturerSource {
  public:
   MockVideoCapturerSource() {
@@ -64,13 +66,51 @@
   }
 };
 
+class FakeMediaStreamVideoSink : public MediaStreamVideoSink {
+ public:
+  FakeMediaStreamVideoSink(base::TimeTicks* capture_time,
+                           media::VideoFrameMetadata* metadata,
+                           base::Closure got_frame_cb)
+      : capture_time_(capture_time),
+        metadata_(metadata),
+        got_frame_cb_(got_frame_cb) {}
+
+  void ConnectToTrack(const blink::WebMediaStreamTrack& track) {
+    MediaStreamVideoSink::ConnectToTrack(
+        track,
+        base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame,
+                   base::Unretained(this)),
+        true);
+  }
+
+  void DisconnectFromTrack() { MediaStreamVideoSink::DisconnectFromTrack(); }
+
+  void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
+                    base::TimeTicks capture_time) {
+    *capture_time_ = capture_time;
+    metadata_->Clear();
+    metadata_->MergeMetadataFrom(frame->metadata());
+    base::ResetAndReturn(&got_frame_cb_).Run();
+  }
+
+ private:
+  base::TimeTicks* const capture_time_;
+  media::VideoFrameMetadata* const metadata_;
+  base::Closure got_frame_cb_;
+};
+
+}  // namespace
+
 class MediaStreamVideoCapturerSourceTest : public testing::Test {
  public:
   MediaStreamVideoCapturerSourceTest()
       : child_process_(new ChildProcess()),
         source_(nullptr),
         delegate_(nullptr),
-        source_stopped_(false) {}
+        source_stopped_(false) {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
 
   void TearDown() override {
     webkit_source_.reset();
@@ -150,15 +190,197 @@
   blink::WebString webkit_source_id_;
   bool source_stopped_;
   MockConstraintFactory constraint_factory_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
-// This test does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       TabCaptureFixedResolutionByDefaultOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) {
+  std::unique_ptr<MockVideoCapturerSource> delegate(
+      new MockVideoCapturerSource());
+  delegate_ = delegate.get();
+  EXPECT_CALL(*delegate_, GetPreferredFormats());
+  source_ = new MediaStreamVideoCapturerSource(
+      base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
+                 base::Unretained(this)),
+      std::move(delegate));
+  webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                            blink::WebMediaStreamSource::TypeVideo,
+                            blink::WebString::fromASCII("dummy_source_name"),
+                            false /* remote */);
+  webkit_source_.setExtraData(source_);
+  webkit_source_id_ = webkit_source_.id();
+
+  InSequence s;
+  EXPECT_CALL(mock_delegate(), StartCapture(_, _, _));
+  blink::WebMediaStreamTrack track = StartSource(
+      VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
+  base::RunLoop().RunUntilIdle();
+
+  OnStarted(true);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
+            webkit_source_.getReadyState());
+
+  EXPECT_FALSE(source_stopped_);
+
+  EXPECT_CALL(mock_delegate(), StopCapture());
+  OnStarted(false);
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded,
+            webkit_source_.getReadyState());
+  // Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
+  EXPECT_TRUE(source_stopped_);
+}
+
+TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
+  std::unique_ptr<MockVideoCapturerSource> delegate(
+      new MockVideoCapturerSource());
+  delegate_ = delegate.get();
+  EXPECT_CALL(*delegate_, GetPreferredFormats());
+  source_ = new MediaStreamVideoCapturerSource(
+      base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
+                 base::Unretained(this)),
+      std::move(delegate));
+  webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                            blink::WebMediaStreamSource::TypeVideo,
+                            blink::WebString::fromASCII("dummy_source_name"),
+                            false /* remote */);
+  webkit_source_.setExtraData(source_);
+  webkit_source_id_ = webkit_source_.id();
+
+  VideoCaptureDeliverFrameCB deliver_frame_cb;
+  media::VideoCapturerSource::RunningCallback running_cb;
+
+  InSequence s;
+  //  EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_, _, _, _));
+  EXPECT_CALL(mock_delegate(), StartCapture(_, _, _))
+      .WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb),
+                               testing::SaveArg<2>(&running_cb)));
+  EXPECT_CALL(mock_delegate(), RequestRefreshFrame());
+  EXPECT_CALL(mock_delegate(), StopCapture());
+  blink::WebMediaStreamTrack track = StartSource(
+      VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
+  running_cb.Run(true);
+
+  base::RunLoop run_loop;
+  base::TimeTicks reference_capture_time =
+      base::TimeTicks::FromInternalValue(60013);
+  base::TimeTicks capture_time;
+  media::VideoFrameMetadata metadata;
+  FakeMediaStreamVideoSink fake_sink(
+      &capture_time, &metadata,
+      media::BindToCurrentLoop(run_loop.QuitClosure()));
+  fake_sink.ConnectToTrack(track);
+  const scoped_refptr<media::VideoFrame> frame =
+      media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
+  frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, 30.0);
+  child_process_->io_task_runner()->PostTask(
+      FROM_HERE, base::Bind(deliver_frame_cb, frame, reference_capture_time));
+  run_loop.Run();
+  fake_sink.DisconnectFromTrack();
+  EXPECT_EQ(reference_capture_time, capture_time);
+  double metadata_value;
+  EXPECT_TRUE(metadata.GetDouble(media::VideoFrameMetadata::FRAME_RATE,
+                                 &metadata_value));
+  EXPECT_EQ(30.0, metadata_value);
+}
+
+class MediaStreamVideoCapturerSourceOldConstraintsTest : public testing::Test {
+ public:
+  MediaStreamVideoCapturerSourceOldConstraintsTest()
+      : child_process_(new ChildProcess()),
+        source_(nullptr),
+        delegate_(nullptr),
+        source_stopped_(false) {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
+
+  void TearDown() override {
+    webkit_source_.reset();
+    blink::WebHeap::collectAllGarbageForTesting();
+  }
+
+  void InitWithDeviceInfo(const StreamDeviceInfo& device_info) {
+    std::unique_ptr<MockVideoCapturerSource> delegate(
+        new MockVideoCapturerSource());
+    delegate_ = delegate.get();
+    source_ = new MediaStreamVideoCapturerSource(
+        base::Bind(
+            &MediaStreamVideoCapturerSourceOldConstraintsTest::OnSourceStopped,
+            base::Unretained(this)),
+        std::move(delegate));
+    source_->SetDeviceInfo(device_info);
+
+    webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                              blink::WebMediaStreamSource::TypeVideo,
+                              blink::WebString::fromASCII("dummy_source_name"),
+                              false /* remote */);
+    webkit_source_.setExtraData(source_);
+    webkit_source_id_ = webkit_source_.id();
+  }
+
+  MockConstraintFactory* constraint_factory() { return &constraint_factory_; }
+
+  blink::WebMediaStreamTrack StartSource() {
+    bool enabled = true;
+    // CreateVideoTrack will trigger OnConstraintsApplied.
+    return MediaStreamVideoTrack::CreateVideoTrack(
+        source_, constraint_factory_.CreateWebMediaConstraints(),
+        base::Bind(&MediaStreamVideoCapturerSourceOldConstraintsTest::
+                       OnConstraintsApplied,
+                   base::Unretained(this)),
+        enabled);
+  }
+
+  blink::WebMediaStreamTrack StartSource(
+      const VideoTrackAdapterSettings& adapter_settings,
+      const base::Optional<bool>& noise_reduction,
+      bool is_screencast,
+      double min_frame_rate) {
+    bool enabled = true;
+    // CreateVideoTrack will trigger OnConstraintsApplied.
+    return MediaStreamVideoTrack::CreateVideoTrack(
+        source_, adapter_settings, noise_reduction, is_screencast,
+        min_frame_rate,
+        base::Bind(&MediaStreamVideoCapturerSourceOldConstraintsTest::
+                       OnConstraintsApplied,
+                   base::Unretained(this)),
+        enabled);
+  }
+
+  MockVideoCapturerSource& mock_delegate() { return *delegate_; }
+
+  const char* GetPowerLineFrequencyForTesting() const {
+    return source_->GetPowerLineFrequencyForTesting();
+  }
+
+  void OnSourceStopped(const blink::WebMediaStreamSource& source) {
+    source_stopped_ = true;
+    EXPECT_EQ(source.id(), webkit_source_id_);
+  }
+  void OnStarted(bool result) { source_->OnRunStateChanged(result); }
+
+ protected:
+  void OnConstraintsApplied(MediaStreamSource* source,
+                            MediaStreamRequestResult result,
+                            const blink::WebString& result_name) {}
+
+  // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks
+  // and Sources below into believing they are on the right threads.
+  base::MessageLoopForUI message_loop_;
+  std::unique_ptr<ChildProcess> child_process_;
+
+  blink::WebMediaStreamSource webkit_source_;
+  MediaStreamVideoCapturerSource* source_;  // owned by |webkit_source_|.
+  MockVideoCapturerSource* delegate_;       // owned by |source|.
+  blink::WebString webkit_source_id_;
+  bool source_stopped_;
+  MockConstraintFactory constraint_factory_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       TabCaptureFixedResolutionByDefault) {
   StreamDeviceInfo device_info;
   device_info.device.type = MEDIA_TAB_VIDEO_CAPTURE;
   InitWithDeviceInfo(device_info);
@@ -183,13 +405,8 @@
   EXPECT_CALL(mock_delegate(), StopCapture());
 }
 
-// This test does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       DesktopCaptureAllowAnyResolutionChangeByDefaultOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       DesktopCaptureAllowAnyResolutionChangeByDefault) {
   StreamDeviceInfo device_info;
   device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE;
   InitWithDeviceInfo(device_info);
@@ -214,13 +431,8 @@
   EXPECT_CALL(mock_delegate(), StopCapture());
 }
 
-// This test does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       TabCaptureConstraintsImplyFixedAspectRatioOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       TabCaptureConstraintsImplyFixedAspectRatio) {
   StreamDeviceInfo device_info;
   device_info.device.type = MEDIA_TAB_VIDEO_CAPTURE;
   InitWithDeviceInfo(device_info);
@@ -252,13 +464,8 @@
   EXPECT_CALL(mock_delegate(), StopCapture());
 }
 
-// This test does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       TabCaptureConstraintsImplyAllowingAnyResolutionChangeOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       TabCaptureConstraintsImplyAllowingAnyResolutionChange) {
   StreamDeviceInfo device_info;
   device_info.device.type = MEDIA_TAB_VIDEO_CAPTURE;
   InitWithDeviceInfo(device_info);
@@ -290,13 +497,8 @@
   EXPECT_CALL(mock_delegate(), StopCapture());
 }
 
-// This test does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       DeviceCaptureConstraintsSupportPowerLineFrequencyOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       DeviceCaptureConstraintsSupportPowerLineFrequency) {
   for (int frequency = -100; frequency < 100; ++frequency) {
     StreamDeviceInfo device_info;
     device_info.device.type = MEDIA_DEVICE_VIDEO_CAPTURE;
@@ -335,17 +537,14 @@
   }
 }
 
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoCapturerSourceTest, EndedOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest, Ended) {
   std::unique_ptr<MockVideoCapturerSource> delegate(
       new MockVideoCapturerSource());
   delegate_ = delegate.get();
   source_ = new MediaStreamVideoCapturerSource(
-      base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
-                 base::Unretained(this)),
+      base::Bind(
+          &MediaStreamVideoCapturerSourceOldConstraintsTest::OnSourceStopped,
+          base::Unretained(this)),
       std::move(delegate));
   webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
                             blink::WebMediaStreamSource::TypeVideo,
@@ -376,86 +575,8 @@
   EXPECT_TRUE(source_stopped_);
 }
 
-TEST_F(MediaStreamVideoCapturerSourceTest, StartAndStop) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  std::unique_ptr<MockVideoCapturerSource> delegate(
-      new MockVideoCapturerSource());
-  delegate_ = delegate.get();
-  EXPECT_CALL(*delegate_, GetPreferredFormats());
-  source_ = new MediaStreamVideoCapturerSource(
-      base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
-                 base::Unretained(this)),
-      std::move(delegate));
-  webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
-                            blink::WebMediaStreamSource::TypeVideo,
-                            blink::WebString::fromASCII("dummy_source_name"),
-                            false /* remote */);
-  webkit_source_.setExtraData(source_);
-  webkit_source_id_ = webkit_source_.id();
-
-  InSequence s;
-  EXPECT_CALL(mock_delegate(), StartCapture(_, _, _));
-  blink::WebMediaStreamTrack track = StartSource(
-      VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
-  base::RunLoop().RunUntilIdle();
-
-  OnStarted(true);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
-            webkit_source_.getReadyState());
-
-  EXPECT_FALSE(source_stopped_);
-
-  EXPECT_CALL(mock_delegate(), StopCapture());
-  OnStarted(false);
-  base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded,
-            webkit_source_.getReadyState());
-  // Verify that MediaStreamSource::SourceStoppedCallback has been triggered.
-  EXPECT_TRUE(source_stopped_);
-}
-
-class FakeMediaStreamVideoSink : public MediaStreamVideoSink {
- public:
-  FakeMediaStreamVideoSink(base::TimeTicks* capture_time,
-                           media::VideoFrameMetadata* metadata,
-                           base::Closure got_frame_cb)
-      : capture_time_(capture_time),
-        metadata_(metadata),
-        got_frame_cb_(got_frame_cb) {}
-
-  void ConnectToTrack(const blink::WebMediaStreamTrack& track) {
-    MediaStreamVideoSink::ConnectToTrack(
-        track, base::Bind(&FakeMediaStreamVideoSink::OnVideoFrame,
-                          base::Unretained(this)),
-        true);
-  }
-
-  void DisconnectFromTrack() {
-    MediaStreamVideoSink::DisconnectFromTrack();
-  }
-
-  void OnVideoFrame(const scoped_refptr<media::VideoFrame>& frame,
-                    base::TimeTicks capture_time) {
-    *capture_time_ = capture_time;
-    metadata_->Clear();
-    metadata_->MergeMetadataFrom(frame->metadata());
-    base::ResetAndReturn(&got_frame_cb_).Run();
-  }
-
- private:
-  base::TimeTicks* const capture_time_;
-  media::VideoFrameMetadata* const metadata_;
-  base::Closure got_frame_cb_;
-};
-
-TEST_F(MediaStreamVideoCapturerSourceTest,
-       CaptureTimeAndMetadataPlumbingOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoCapturerSourceOldConstraintsTest,
+       CaptureTimeAndMetadataPlumbing) {
   StreamDeviceInfo device_info;
   device_info.device.type = MEDIA_DESKTOP_VIDEO_CAPTURE;
   InitWithDeviceInfo(device_info);
@@ -496,60 +617,4 @@
   EXPECT_EQ(30.0, metadata_value);
 }
 
-TEST_F(MediaStreamVideoCapturerSourceTest, CaptureTimeAndMetadataPlumbing) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  std::unique_ptr<MockVideoCapturerSource> delegate(
-      new MockVideoCapturerSource());
-  delegate_ = delegate.get();
-  EXPECT_CALL(*delegate_, GetPreferredFormats());
-  source_ = new MediaStreamVideoCapturerSource(
-      base::Bind(&MediaStreamVideoCapturerSourceTest::OnSourceStopped,
-                 base::Unretained(this)),
-      std::move(delegate));
-  webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
-                            blink::WebMediaStreamSource::TypeVideo,
-                            blink::WebString::fromASCII("dummy_source_name"),
-                            false /* remote */);
-  webkit_source_.setExtraData(source_);
-  webkit_source_id_ = webkit_source_.id();
-
-  VideoCaptureDeliverFrameCB deliver_frame_cb;
-  media::VideoCapturerSource::RunningCallback running_cb;
-
-  InSequence s;
-  //  EXPECT_CALL(mock_delegate(), GetCurrentSupportedFormats(_, _, _, _));
-  EXPECT_CALL(mock_delegate(), StartCapture(_, _, _))
-      .WillOnce(testing::DoAll(testing::SaveArg<1>(&deliver_frame_cb),
-                               testing::SaveArg<2>(&running_cb)));
-  EXPECT_CALL(mock_delegate(), RequestRefreshFrame());
-  EXPECT_CALL(mock_delegate(), StopCapture());
-  blink::WebMediaStreamTrack track = StartSource(
-      VideoTrackAdapterSettings(), base::Optional<bool>(), false, 0.0);
-  running_cb.Run(true);
-
-  base::RunLoop run_loop;
-  base::TimeTicks reference_capture_time =
-      base::TimeTicks::FromInternalValue(60013);
-  base::TimeTicks capture_time;
-  media::VideoFrameMetadata metadata;
-  FakeMediaStreamVideoSink fake_sink(
-      &capture_time, &metadata,
-      media::BindToCurrentLoop(run_loop.QuitClosure()));
-  fake_sink.ConnectToTrack(track);
-  const scoped_refptr<media::VideoFrame> frame =
-      media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2));
-  frame->metadata()->SetDouble(media::VideoFrameMetadata::FRAME_RATE, 30.0);
-  child_process_->io_task_runner()->PostTask(
-      FROM_HERE, base::Bind(deliver_frame_cb, frame, reference_capture_time));
-  run_loop.Run();
-  fake_sink.DisconnectFromTrack();
-  EXPECT_EQ(reference_capture_time, capture_time);
-  double metadata_value;
-  EXPECT_TRUE(metadata.GetDouble(media::VideoFrameMetadata::FRAME_RATE,
-                                 &metadata_value));
-  EXPECT_EQ(30.0, metadata_value);
-}
-
 }  // namespace content
diff --git a/content/renderer/media/media_stream_video_source_unittest.cc b/content/renderer/media/media_stream_video_source_unittest.cc
index 59c416f..7e684b61 100644
--- a/content/renderer/media/media_stream_video_source_unittest.cc
+++ b/content/renderer/media/media_stream_video_source_unittest.cc
@@ -42,12 +42,353 @@
         number_of_failed_constraints_applied_(0),
         result_(MEDIA_DEVICE_OK),
         result_name_(""),
-        mock_source_(new MockMediaStreamVideoSource(true)),
-        mock_source2_(new MockMediaStreamVideoSource(
+        mock_source_(new MockMediaStreamVideoSource(
             media::VideoCaptureFormat(gfx::Size(1280, 720),
                                       1000.0,
                                       media::PIXEL_FORMAT_I420),
             false)) {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kMediaStreamOldVideoConstraints);
+
+    media::VideoCaptureFormats formats;
+    formats.push_back(media::VideoCaptureFormat(gfx::Size(1280, 720), 30,
+                                                media::PIXEL_FORMAT_I420));
+    formats.push_back(media::VideoCaptureFormat(gfx::Size(640, 480), 30,
+                                                media::PIXEL_FORMAT_I420));
+    formats.push_back(media::VideoCaptureFormat(gfx::Size(352, 288), 30,
+                                                media::PIXEL_FORMAT_I420));
+    formats.push_back(media::VideoCaptureFormat(gfx::Size(320, 240), 30,
+                                                media::PIXEL_FORMAT_I420));
+    webkit_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                              blink::WebMediaStreamSource::TypeVideo,
+                              blink::WebString::fromASCII("dummy_source_name"),
+                              false /* remote */);
+    webkit_source_.setExtraData(mock_source_);
+  }
+
+  void TearDown() override {
+    webkit_source_.reset();
+    blink::WebHeap::collectAllGarbageForTesting();
+  }
+
+ protected:
+  // Create a track that's associated with |webkit_source_|.
+  blink::WebMediaStreamTrack CreateTrack(const std::string& id) {
+    bool enabled = true;
+    return MediaStreamVideoTrack::CreateVideoTrack(
+        mock_source_,
+        base::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
+                   base::Unretained(this)),
+        enabled);
+  }
+
+  blink::WebMediaStreamTrack CreateTrack(
+      const std::string& id,
+      const VideoTrackAdapterSettings& adapter_settings,
+      const base::Optional<bool>& noise_reduction,
+      bool is_screencast,
+      double min_frame_rate) {
+    bool enabled = true;
+    return MediaStreamVideoTrack::CreateVideoTrack(
+        mock_source_, adapter_settings, noise_reduction, is_screencast,
+        min_frame_rate,
+        base::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
+                   base::Unretained(this)),
+        enabled);
+  }
+
+  blink::WebMediaStreamTrack CreateTrackAndStartSource(int width,
+                                                       int height,
+                                                       double frame_rate) {
+    DCHECK(!IsOldVideoConstraints());
+    blink::WebMediaStreamTrack track = CreateTrack(
+        "123",
+        VideoTrackAdapterSettings(width, height, 0.0, HUGE_VAL, frame_rate),
+        base::Optional<bool>(), false, 0.0);
+
+    EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
+    mock_source_->StartMockedSource();
+    // Once the source has started successfully we expect that the
+    // ConstraintsCallback in MediaStreamSource::AddTrack completes.
+    EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
+    return track;
+  }
+
+  int NumberOfSuccessConstraintsCallbacks() const {
+    return number_of_successful_constraints_applied_;
+  }
+
+  int NumberOfFailedConstraintsCallbacks() const {
+    return number_of_failed_constraints_applied_;
+  }
+
+  content::MediaStreamRequestResult error_type() const { return result_; }
+  blink::WebString error_name() const { return result_name_; }
+
+  MockMediaStreamVideoSource* mock_source() { return mock_source_; }
+
+  const blink::WebMediaStreamSource& webkit_source() { return webkit_source_; }
+
+  void TestSourceCropFrame(int capture_width,
+                           int capture_height,
+                           int expected_width,
+                           int expected_height) {
+    DCHECK(!IsOldVideoConstraints());
+    // Configure the track to crop to the expected resolution.
+    blink::WebMediaStreamTrack track =
+        CreateTrackAndStartSource(expected_width, expected_height, 30.0);
+
+    // Produce frames at the capture resolution.
+    MockMediaStreamVideoSink sink;
+    sink.ConnectToTrack(track);
+    DeliverVideoFrameAndWaitForRenderer(capture_width, capture_height, &sink);
+    EXPECT_EQ(1, sink.number_of_frames());
+
+    // Expect the delivered frame to be cropped.
+    EXPECT_EQ(expected_height, sink.frame_size().height());
+    EXPECT_EQ(expected_width, sink.frame_size().width());
+    sink.DisconnectFromTrack();
+  }
+
+  void DeliverVideoFrameAndWaitForRenderer(int width,
+                                           int height,
+                                           MockMediaStreamVideoSink* sink) {
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    EXPECT_CALL(*sink, OnVideoFrame()).WillOnce(RunClosure(quit_closure));
+    scoped_refptr<media::VideoFrame> frame =
+        media::VideoFrame::CreateBlackFrame(gfx::Size(width, height));
+    mock_source()->DeliverVideoFrame(frame);
+    run_loop.Run();
+  }
+
+  void DeliverVideoFrameAndWaitForTwoRenderers(
+      int width,
+      int height,
+      MockMediaStreamVideoSink* sink1,
+      MockMediaStreamVideoSink* sink2) {
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    EXPECT_CALL(*sink1, OnVideoFrame());
+    EXPECT_CALL(*sink2, OnVideoFrame()).WillOnce(RunClosure(quit_closure));
+    scoped_refptr<media::VideoFrame> frame =
+        media::VideoFrame::CreateBlackFrame(gfx::Size(width, height));
+    mock_source()->DeliverVideoFrame(frame);
+    run_loop.Run();
+  }
+
+  void TestTwoTracksWithDifferentSettings(int capture_width,
+                                          int capture_height,
+                                          int expected_width1,
+                                          int expected_height1,
+                                          int expected_width2,
+                                          int expected_height2) {
+    blink::WebMediaStreamTrack track1 =
+        CreateTrackAndStartSource(expected_width1, expected_height1,
+                                  MediaStreamVideoSource::kDefaultFrameRate);
+
+    blink::WebMediaStreamTrack track2 =
+        CreateTrack("dummy",
+                    VideoTrackAdapterSettings(
+                        expected_width2, expected_height2, 0.0, HUGE_VAL,
+                        MediaStreamVideoSource::kDefaultFrameRate),
+                    base::Optional<bool>(), false, 0.0);
+
+    MockMediaStreamVideoSink sink1;
+    sink1.ConnectToTrack(track1);
+    EXPECT_EQ(0, sink1.number_of_frames());
+
+    MockMediaStreamVideoSink sink2;
+    sink2.ConnectToTrack(track2);
+    EXPECT_EQ(0, sink2.number_of_frames());
+
+    DeliverVideoFrameAndWaitForTwoRenderers(capture_width, capture_height,
+                                            &sink1, &sink2);
+
+    EXPECT_EQ(1, sink1.number_of_frames());
+    EXPECT_EQ(expected_width1, sink1.frame_size().width());
+    EXPECT_EQ(expected_height1, sink1.frame_size().height());
+
+    EXPECT_EQ(1, sink2.number_of_frames());
+    EXPECT_EQ(expected_width2, sink2.frame_size().width());
+    EXPECT_EQ(expected_height2, sink2.frame_size().height());
+
+    sink1.DisconnectFromTrack();
+    sink2.DisconnectFromTrack();
+  }
+
+  void ReleaseTrackAndSourceOnAddTrackCallback(
+      const blink::WebMediaStreamTrack& track_to_release) {
+    track_to_release_ = track_to_release;
+  }
+
+ private:
+  void OnConstraintsApplied(MediaStreamSource* source,
+                            MediaStreamRequestResult result,
+                            const blink::WebString& result_name) {
+    ASSERT_EQ(source, webkit_source().getExtraData());
+
+    if (result == MEDIA_DEVICE_OK) {
+      ++number_of_successful_constraints_applied_;
+    } else {
+      result_ = result;
+      result_name_ = result_name;
+      ++number_of_failed_constraints_applied_;
+    }
+
+    if (!track_to_release_.isNull()) {
+      mock_source_ = nullptr;
+      webkit_source_.reset();
+      track_to_release_.reset();
+    }
+  }
+  const base::MessageLoopForUI message_loop_;
+  const std::unique_ptr<ChildProcess> child_process_;
+  blink::WebMediaStreamTrack track_to_release_;
+  int number_of_successful_constraints_applied_;
+  int number_of_failed_constraints_applied_;
+  content::MediaStreamRequestResult result_;
+  blink::WebString result_name_;
+  blink::WebMediaStreamSource webkit_source_;
+  // |mock_source_| is owned by |webkit_source_|.
+  MockMediaStreamVideoSource* mock_source_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSource) {
+  blink::WebMediaStreamTrack track = CreateTrack("123");
+  mock_source()->StartMockedSource();
+  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
+}
+
+TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeSourceStarts) {
+  blink::WebMediaStreamTrack track1 = CreateTrack("123");
+  blink::WebMediaStreamTrack track2 = CreateTrack("123");
+  EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
+  mock_source()->StartMockedSource();
+  EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
+}
+
+TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStarts) {
+  blink::WebMediaStreamTrack track1 = CreateTrack("123");
+  mock_source()->StartMockedSource();
+  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
+  blink::WebMediaStreamTrack track2 = CreateTrack("123");
+  EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
+}
+
+TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSource) {
+  blink::WebMediaStreamTrack track = CreateTrack("123");
+  mock_source()->FailToStartMockedSource();
+  EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
+}
+
+TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3) {
+  TestSourceCropFrame(1280, 720, 960, 720);
+}
+
+TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnSuccessCallBack) {
+  blink::WebMediaStreamTrack track = CreateTrack("123");
+  ReleaseTrackAndSourceOnAddTrackCallback(track);
+  mock_source()->StartMockedSource();
+  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
+}
+
+TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVGAAndWVGA) {
+  TestTwoTracksWithDifferentSettings(640, 480, 640, 480, 640, 360);
+}
+
+TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndWVGA) {
+  TestTwoTracksWithDifferentSettings(1280, 720, 1280, 720, 640, 360);
+}
+
+TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
+  MockConstraintFactory factory;
+  factory.AddAdvanced().width.setMax(800);
+  factory.AddAdvanced().height.setMax(700);
+
+  // Expect the source to start capture with the supported resolution.
+  // Disable frame-rate adjustment in spec-compliant mode to ensure no frames
+  // are dropped.
+  blink::WebMediaStreamTrack track = CreateTrackAndStartSource(800, 700, 0.0);
+
+  MockMediaStreamVideoSink sink;
+  sink.ConnectToTrack(track);
+  EXPECT_EQ(0, sink.number_of_frames());
+  DeliverVideoFrameAndWaitForRenderer(320, 240, &sink);
+  EXPECT_EQ(1, sink.number_of_frames());
+  // Expect the delivered frame to be passed unchanged since its smaller than
+  // max requested.
+  EXPECT_EQ(320, sink.frame_size().width());
+  EXPECT_EQ(240, sink.frame_size().height());
+
+  DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
+  EXPECT_EQ(2, sink.number_of_frames());
+  // Expect the delivered frame to be passed unchanged since its smaller than
+  // max requested.
+  EXPECT_EQ(640, sink.frame_size().width());
+  EXPECT_EQ(480, sink.frame_size().height());
+
+  DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
+  EXPECT_EQ(3, sink.number_of_frames());
+  // Expect a frame to be cropped since its larger than max requested.
+  EXPECT_EQ(800, sink.frame_size().width());
+  EXPECT_EQ(700, sink.frame_size().height());
+
+  sink.DisconnectFromTrack();
+}
+
+// Test that a source producing no frames change the source ReadyState to muted.
+// that in a reasonable time frame the muted state turns to false.
+TEST_F(MediaStreamVideoSourceTest, MutedSource) {
+  // Setup the source for support a frame rate of 999 fps in order to test
+  // the muted event faster. This is since the frame monitoring uses
+  // PostDelayedTask that is dependent on the source frame rate.
+  // Note that media::limits::kMaxFramesPerSecond is 1000.
+  blink::WebMediaStreamTrack track = CreateTrackAndStartSource(
+      640, 480, media::limits::kMaxFramesPerSecond - 2);
+  MockMediaStreamVideoSink sink;
+  sink.ConnectToTrack(track);
+  EXPECT_EQ(track.source().getReadyState(),
+            blink::WebMediaStreamSource::ReadyStateLive);
+
+  base::RunLoop run_loop;
+  base::Closure quit_closure = run_loop.QuitClosure();
+  bool muted_state = false;
+  EXPECT_CALL(*mock_source(), DoSetMutedState(_))
+      .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure)));
+  run_loop.Run();
+  EXPECT_EQ(muted_state, true);
+
+  EXPECT_EQ(track.source().getReadyState(),
+            blink::WebMediaStreamSource::ReadyStateMuted);
+
+  base::RunLoop run_loop2;
+  base::Closure quit_closure2 = run_loop2.QuitClosure();
+  EXPECT_CALL(*mock_source(), DoSetMutedState(_))
+      .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure2)));
+  DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
+  run_loop2.Run();
+
+  EXPECT_EQ(muted_state, false);
+  EXPECT_EQ(track.source().getReadyState(),
+            blink::WebMediaStreamSource::ReadyStateLive);
+
+  sink.DisconnectFromTrack();
+}
+
+class MediaStreamVideoSourceOldConstraintsTest : public ::testing::Test {
+ public:
+  MediaStreamVideoSourceOldConstraintsTest()
+      : child_process_(new ChildProcess()),
+        number_of_successful_constraints_applied_(0),
+        number_of_failed_constraints_applied_(0),
+        result_(MEDIA_DEVICE_OK),
+        result_name_(""),
+        mock_source_(new MockMediaStreamVideoSource(true)) {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kMediaStreamOldVideoConstraints);
+
     media::VideoCaptureFormats formats;
     formats.push_back(media::VideoCaptureFormat(
         gfx::Size(1280, 720), 30, media::PIXEL_FORMAT_I420));
@@ -63,16 +404,10 @@
                               blink::WebString::fromASCII("dummy_source_name"),
                               false /* remote */);
     webkit_source_.setExtraData(mock_source_);
-    webkit_source2_.initialize(blink::WebString::fromASCII("dummy_source_id"),
-                               blink::WebMediaStreamSource::TypeVideo,
-                               blink::WebString::fromASCII("dummy_source_name"),
-                               false /* remote */);
-    webkit_source2_.setExtraData(mock_source2_);
   }
 
   void TearDown() override {
     webkit_source_.reset();
-    webkit_source2_.reset();
     blink::WebHeap::collectAllGarbageForTesting();
   }
 
@@ -84,32 +419,9 @@
     bool enabled = true;
     return MediaStreamVideoTrack::CreateVideoTrack(
         mock_source_, constraints,
-        base::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
-                   base::Unretained(this)),
-        enabled);
-  }
-
-  blink::WebMediaStreamTrack CreateTrack(const std::string& id) {
-    bool enabled = true;
-    return MediaStreamVideoTrack::CreateVideoTrack(
-        mock_source2_,
-        base::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
-                   base::Unretained(this)),
-        enabled);
-  }
-
-  blink::WebMediaStreamTrack CreateTrack(
-      const std::string& id,
-      const VideoTrackAdapterSettings& adapter_settings,
-      const base::Optional<bool>& noise_reduction,
-      bool is_screencast,
-      double min_frame_rate) {
-    bool enabled = true;
-    return MediaStreamVideoTrack::CreateVideoTrack(
-        mock_source2_, adapter_settings, noise_reduction, is_screencast,
-        min_frame_rate,
-        base::Bind(&MediaStreamVideoSourceTest::OnConstraintsApplied,
-                   base::Unretained(this)),
+        base::Bind(
+            &MediaStreamVideoSourceOldConstraintsTest::OnConstraintsApplied,
+            base::Unretained(this)),
         enabled);
   }
 
@@ -135,23 +447,6 @@
     return track;
   }
 
-  blink::WebMediaStreamTrack CreateTrackAndStartSource(int width,
-                                                       int height,
-                                                       double frame_rate) {
-    DCHECK(!IsOldVideoConstraints());
-    blink::WebMediaStreamTrack track = CreateTrack(
-        "123",
-        VideoTrackAdapterSettings(width, height, 0.0, HUGE_VAL, frame_rate),
-        base::Optional<bool>(), false, 0.0);
-
-    EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
-    mock_source2_->StartMockedSource();
-    // Once the source has started successfully we expect that the
-    // ConstraintsCallback in MediaStreamSource::AddTrack completes.
-    EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
-    return track;
-  }
-
   int NumberOfSuccessConstraintsCallbacks() const {
     return number_of_successful_constraints_applied_;
   }
@@ -163,13 +458,9 @@
   content::MediaStreamRequestResult error_type() const { return result_; }
   blink::WebString error_name() const { return result_name_; }
 
-  MockMediaStreamVideoSource* mock_source() {
-    return IsOldVideoConstraints() ? mock_source_ : mock_source2_;
-  }
+  MockMediaStreamVideoSource* mock_source() { return mock_source_; }
 
-  const blink::WebMediaStreamSource& webkit_source() {
-    return IsOldVideoConstraints() ? webkit_source_ : webkit_source2_;
-  }
+  const blink::WebMediaStreamSource& webkit_source() { return webkit_source_; }
 
   // Test that the source crops/scales to the requested width and
   // height even though the camera delivers a larger frame.
@@ -194,27 +485,6 @@
     sink.DisconnectFromTrack();
   }
 
-  void TestSourceCropFrame(int capture_width,
-                           int capture_height,
-                           int expected_width,
-                           int expected_height) {
-    DCHECK(!IsOldVideoConstraints());
-    // Configure the track to crop to the expected resolution.
-    blink::WebMediaStreamTrack track =
-        CreateTrackAndStartSource(expected_width, expected_height, 30.0);
-
-    // Produce frames at the capture resolution.
-    MockMediaStreamVideoSink sink;
-    sink.ConnectToTrack(track);
-    DeliverVideoFrameAndWaitForRenderer(capture_width, capture_height, &sink);
-    EXPECT_EQ(1, sink.number_of_frames());
-
-    // Expect the delivered frame to be cropped.
-    EXPECT_EQ(expected_height, sink.frame_size().height());
-    EXPECT_EQ(expected_width, sink.frame_size().width());
-    sink.DisconnectFromTrack();
-  }
-
   void DeliverVideoFrameAndWaitForRenderer(int width, int height,
                                            MockMediaStreamVideoSink* sink) {
     base::RunLoop run_loop;
@@ -283,45 +553,6 @@
     sink2.DisconnectFromTrack();
   }
 
-  void TestTwoTracksWithDifferentSettings(int capture_width,
-                                          int capture_height,
-                                          int expected_width1,
-                                          int expected_height1,
-                                          int expected_width2,
-                                          int expected_height2) {
-    blink::WebMediaStreamTrack track1 =
-        CreateTrackAndStartSource(expected_width1, expected_height1,
-                                  MediaStreamVideoSource::kDefaultFrameRate);
-
-    blink::WebMediaStreamTrack track2 =
-        CreateTrack("dummy",
-                    VideoTrackAdapterSettings(
-                        expected_width2, expected_height2, 0.0, HUGE_VAL,
-                        MediaStreamVideoSource::kDefaultFrameRate),
-                    base::Optional<bool>(), false, 0.0);
-
-    MockMediaStreamVideoSink sink1;
-    sink1.ConnectToTrack(track1);
-    EXPECT_EQ(0, sink1.number_of_frames());
-
-    MockMediaStreamVideoSink sink2;
-    sink2.ConnectToTrack(track2);
-    EXPECT_EQ(0, sink2.number_of_frames());
-
-    DeliverVideoFrameAndWaitForTwoRenderers(capture_width, capture_height,
-                                            &sink1, &sink2);
-
-    EXPECT_EQ(1, sink1.number_of_frames());
-    EXPECT_EQ(expected_width1, sink1.frame_size().width());
-    EXPECT_EQ(expected_height1, sink1.frame_size().height());
-
-    EXPECT_EQ(1, sink2.number_of_frames());
-    EXPECT_EQ(expected_width2, sink2.frame_size().width());
-    EXPECT_EQ(expected_height2, sink2.frame_size().height());
-
-    sink1.DisconnectFromTrack();
-    sink2.DisconnectFromTrack();
-  }
   void SetSourceSupportedFormats(const media::VideoCaptureFormats& formats) {
     mock_source_->SetSupportedFormats(formats);
   }
@@ -348,8 +579,6 @@
     if (!track_to_release_.isNull()) {
       mock_source_ = nullptr;
       webkit_source_.reset();
-      mock_source2_ = nullptr;
-      webkit_source2_.reset();
       track_to_release_.reset();
     }
   }
@@ -363,16 +592,11 @@
   blink::WebMediaStreamSource webkit_source_;
   // |mock_source_| is owned by |webkit_source_|.
   MockMediaStreamVideoSource* mock_source_;
-  blink::WebMediaStreamSource webkit_source2_;
-  // |mock_source2_| is owned by |webkit_source2_|.
-  MockMediaStreamVideoSource* mock_source2_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSourceOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, AddTrackAndStartSource) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   blink::WebMediaStreamTrack track = CreateTrack("123", constraints);
@@ -381,21 +605,9 @@
   EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
 }
 
-TEST_F(MediaStreamVideoSourceTest, AddTrackAndStartSource) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  blink::WebMediaStreamTrack track = CreateTrack("123");
-  mock_source()->StartMockedSource();
-  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
-}
-
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       AddTwoTracksBeforeSourceStartsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       AddTwoTracksBeforeSourceStarts) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
@@ -406,22 +618,8 @@
   EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
 }
 
-TEST_F(MediaStreamVideoSourceTest, AddTwoTracksBeforeSourceStarts) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  blink::WebMediaStreamTrack track1 = CreateTrack("123");
-  blink::WebMediaStreamTrack track2 = CreateTrack("123");
-  EXPECT_EQ(0, NumberOfSuccessConstraintsCallbacks());
-  mock_source()->StartMockedSource();
-  EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
-}
-
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStartsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, AddTrackAfterSourceStarts) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
@@ -432,22 +630,8 @@
   EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
 }
 
-TEST_F(MediaStreamVideoSourceTest, AddTrackAfterSourceStarts) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  blink::WebMediaStreamTrack track1 = CreateTrack("123");
-  mock_source()->StartMockedSource();
-  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
-  blink::WebMediaStreamTrack track2 = CreateTrack("123");
-  EXPECT_EQ(2, NumberOfSuccessConstraintsCallbacks());
-}
-
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSourceOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, AddTrackAndFailToStartSource) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   blink::WebMediaStreamTrack track = CreateTrack("123", constraints);
@@ -456,22 +640,10 @@
   EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
 }
 
-TEST_F(MediaStreamVideoSourceTest, AddTrackAndFailToStartSource) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  blink::WebMediaStreamTrack track = CreateTrack("123");
-  mock_source()->FailToStartMockedSource();
-  EXPECT_EQ(1, NumberOfFailedConstraintsCallbacks());
-}
-
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       AddTwoTracksBeforeGetSupportedFormatsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       AddTwoTracksBeforeGetSupportedFormats) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   blink::WebMediaStreamTrack track1 = CreateTrack("123", constraints);
@@ -485,10 +657,7 @@
 // and the capture device support CIF.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MandatoryConstraintCif5FpsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MandatoryConstraintCif5Fps) {
   MockConstraintFactory factory;
   factory.basic().width.setMax(352);
   factory.basic().height.setMax(288);
@@ -501,10 +670,7 @@
 // optional constraint is set to 720P.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MandatoryMinVgaOptional720POldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MandatoryMinVgaOptional720P) {
   MockConstraintFactory factory;
   factory.basic().width.setMin(640);
   factory.basic().height.setMin(480);
@@ -518,10 +684,7 @@
 // mandatory constraint is exactly width 1280.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MandatoryExact720POldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MandatoryExact720P) {
   MockConstraintFactory factory;
   factory.basic().width.setExact(1280);
   CreateTrackAndStartSource(factory.CreateWebMediaConstraints(), 1280, 720, 30);
@@ -531,10 +694,7 @@
 // require it even if an optional constraint request a higher resolution
 // that don't have this aspect ratio.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MandatoryAspectRatio4To3) {
   MockConstraintFactory factory;
   factory.basic().width.setMin(640);
   factory.basic().height.setMin(480);
@@ -544,18 +704,8 @@
   TestSourceCropFrame(1280, 720, factory.CreateWebMediaConstraints(), 960, 720);
 }
 
-TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio4To3) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  TestSourceCropFrame(1280, 720, 960, 720);
-}
-
 // Test that AddTrack succeeds if the mandatory min aspect ratio it set to 2.
-TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio2OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MandatoryAspectRatio2) {
   MockConstraintFactory factory;
   factory.basic().aspectRatio.setMin(2.0);
 
@@ -564,20 +714,10 @@
                       factory.CreateWebMediaConstraints(), 640, 320);
 }
 
-TEST_F(MediaStreamVideoSourceTest, MandatoryAspectRatio2) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  TestSourceCropFrame(1280, 720, 960, 720);
-}
-
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       MinAspectRatioLargerThanMaxAspectRatioOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       MinAspectRatioLargerThanMaxAspectRatio) {
   MockConstraintFactory factory;
   factory.basic().aspectRatio.setMin(2.0);
   factory.basic().aspectRatio.setMax(1.0);
@@ -589,10 +729,7 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MinWidthLargerThanMaxWidthOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MinWidthLargerThanMaxWidth) {
   MockConstraintFactory factory;
   factory.basic().width.setMin(640);
   factory.basic().width.setMax(320);
@@ -604,10 +741,7 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MinHeightLargerThanMaxHeightOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MinHeightLargerThanMaxHeight) {
   MockConstraintFactory factory;
   factory.basic().height.setMin(480);
   factory.basic().height.setMax(360);
@@ -620,11 +754,8 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       MinFrameRateLargerThanMaxFrameRateOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       MinFrameRateLargerThanMaxFrameRate) {
   MockConstraintFactory factory;
   factory.basic().frameRate.setMin(25);
   factory.basic().frameRate.setMax(15);
@@ -636,10 +767,7 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, ExactWidthNotSupportedOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, ExactWidthNotSupported) {
   MockConstraintFactory factory;
   factory.basic().width.setExact(12000);
   blink::WebMediaStreamTrack track =
@@ -650,10 +778,7 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MinWidthNotSupportedOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MinWidthNotSupported) {
   MockConstraintFactory factory;
   factory.basic().width.setMin(12000);
   blink::WebMediaStreamTrack track =
@@ -665,11 +790,8 @@
 // Test that its safe to release the last reference of a blink track and the
 // source during the callback if adding a track succeeds.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       ReleaseTrackAndSourceOnSuccessCallBackOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       ReleaseTrackAndSourceOnSuccessCallBack) {
   MockConstraintFactory factory;
   blink::WebMediaStreamTrack track =
       CreateTrack("123", factory.CreateWebMediaConstraints());
@@ -679,25 +801,12 @@
   EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
 }
 
-TEST_F(MediaStreamVideoSourceTest, ReleaseTrackAndSourceOnSuccessCallBack) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  blink::WebMediaStreamTrack track = CreateTrack("123");
-  ReleaseTrackAndSourceOnAddTrackCallback(track);
-  mock_source()->StartMockedSource();
-  EXPECT_EQ(1, NumberOfSuccessConstraintsCallbacks());
-}
-
 // Test that its safe to release the last reference of a blink track and the
 // source during the callback if adding a track fails.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       ReleaseTrackAndSourceOnFailureCallBackOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       ReleaseTrackAndSourceOnFailureCallBack) {
   MockConstraintFactory factory;
   factory.basic().width.setMin(99999);
   {
@@ -713,10 +822,7 @@
 // supported.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, OptionalAspectRatioTooHighOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, OptionalAspectRatioTooHigh) {
   MockConstraintFactory factory;
   factory.AddAdvanced().aspectRatio.setMin(2.0);
   blink::WebMediaStreamTrack track =
@@ -733,10 +839,7 @@
 // that is the only supported.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, DefaultCapabilityOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, DefaultCapability) {
   media::VideoCaptureFormats formats;
   formats.push_back(media::VideoCaptureFormat(
       gfx::Size(MediaStreamVideoSource::kDefaultWidth,
@@ -752,10 +855,7 @@
 
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, InvalidMandatoryConstraintOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, InvalidMandatoryConstraint) {
   MockConstraintFactory factory;
   // Use a constraint that is only known for audio.
   factory.basic().echoCancellation.setExact(true);
@@ -770,10 +870,7 @@
 // Test that the source ignores an unknown optional constraint.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, InvalidOptionalConstraintOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, InvalidOptionalConstraint) {
   MockConstraintFactory factory;
   factory.AddAdvanced().echoCancellation.setExact(true);
 
@@ -786,11 +883,8 @@
 // constraints for screencast.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       ScreencastResolutionWithConstraintOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       ScreencastResolutionWithConstraint) {
   media::VideoCaptureFormats formats;
   formats.push_back(media::VideoCaptureFormat(gfx::Size(480, 270), 30,
                                               media::PIXEL_FORMAT_I420));
@@ -808,10 +902,7 @@
 // Test that optional constraints are applied in order.
 // Does not apply with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, OptionalConstraintsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, OptionalConstraints) {
   MockConstraintFactory factory;
   // Min width of 2056 pixels can not be fulfilled.
   factory.AddAdvanced().width.setMin(2056);
@@ -825,11 +916,8 @@
 // height even though the camera delivers a larger frame.
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverCroppedVideoFrameOptional640360OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverCroppedVideoFrameOptional640360) {
   MockConstraintFactory factory;
   factory.AddAdvanced().width.setMax(640);
   factory.AddAdvanced().height.setMax(360);
@@ -838,11 +926,8 @@
 
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverCroppedVideoFrameMandatory640360OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverCroppedVideoFrameMandatory640360) {
   MockConstraintFactory factory;
   factory.basic().width.setMax(640);
   factory.basic().height.setMax(360);
@@ -851,11 +936,8 @@
 
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverCroppedVideoFrameMandatory732489OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverCroppedVideoFrameMandatory732489) {
   MockConstraintFactory factory;
   factory.basic().width.setMax(732);
   factory.basic().height.setMax(489);
@@ -868,11 +950,8 @@
 // height even though the requested frame has odd size.
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverCroppedVideoFrame637359OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverCroppedVideoFrame637359) {
   MockConstraintFactory factory;
   factory.AddAdvanced().width.setMax(637);
   factory.AddAdvanced().height.setMax(359);
@@ -881,11 +960,8 @@
 
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverCroppedVideoFrame320320OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverCroppedVideoFrame320320) {
   MockConstraintFactory factory;
   factory.basic().width.setMax(320);
   factory.basic().height.setMax(320);
@@ -896,11 +972,8 @@
 
 // Redundant with spec-compliant constraints.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       DeliverSmallerSizeWhenTooLargeMaxOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       DeliverSmallerSizeWhenTooLargeMax) {
   MockConstraintFactory factory;
   factory.AddAdvanced().width.setMax(1920);
   factory.AddAdvanced().height.setMax(1080);
@@ -911,10 +984,7 @@
 }
 
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVGAAndWVGAOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, TwoTracksWithVGAAndWVGA) {
   MockConstraintFactory factory1;
   factory1.AddAdvanced().width.setMax(640);
   factory1.AddAdvanced().height.setMax(480);
@@ -927,19 +997,9 @@
                                         640, 480, 640, 480, 640, 360);
 }
 
-TEST_F(MediaStreamVideoSourceTest, TwoTracksWithVGAAndWVGA) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  TestTwoTracksWithDifferentSettings(640, 480, 640, 480, 640, 360);
-}
-
 // Redundant with spec-compliant constraints
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndWVGAOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, TwoTracksWith720AndWVGA) {
   MockConstraintFactory factory1;
   factory1.AddAdvanced().width.setMin(1280);
   factory1.AddAdvanced().height.setMin(720);
@@ -953,19 +1013,9 @@
                                         1280, 720, 1280, 720, 640, 360);
 }
 
-TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndWVGA) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  TestTwoTracksWithDifferentSettings(1280, 720, 1280, 720, 640, 360);
-}
-
 // Redundant with spec-compliant constraints
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, TwoTracksWith720AndW700H700OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, TwoTracksWith720AndW700H700) {
   MockConstraintFactory factory1;
   factory1.AddAdvanced().width.setMin(1280);
   factory1.AddAdvanced().height.setMin(720);
@@ -981,11 +1031,8 @@
 
 // Redundant with spec-compliant constraints
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       TwoTracksWith720AndMaxAspectRatio4To3OldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       TwoTracksWith720AndMaxAspectRatio4To3) {
   MockConstraintFactory factory1;
   factory1.AddAdvanced().width.setMin(1280);
   factory1.AddAdvanced().height.setMin(720);
@@ -1000,11 +1047,8 @@
 
 // Redundant with spec-compliant constraints
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       TwoTracksWithVgaAndMinAspectRatioOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       TwoTracksWithVgaAndMinAspectRatio) {
   MockConstraintFactory factory1;
   factory1.AddAdvanced().width.setMax(640);
   factory1.AddAdvanced().height.setMax(480);
@@ -1019,11 +1063,8 @@
 
 // Does not apply with spec-compliant constraints
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       TwoTracksWithSecondTrackFrameRateHigherThanFirstOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       TwoTracksWithSecondTrackFrameRateHigherThanFirst) {
   MockConstraintFactory factory1;
   factory1.basic().frameRate.setMin(15);
   factory1.basic().frameRate.setMax(15);
@@ -1044,10 +1085,7 @@
 // tracks sinks get the new frame size unless constraints force the frame to be
 // cropped.
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSizeOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, SourceChangeFrameSize) {
   MockConstraintFactory factory;
   factory.AddAdvanced().width.setMax(800);
   factory.AddAdvanced().height.setMax(700);
@@ -1084,52 +1122,8 @@
   sink.DisconnectFromTrack();
 }
 
-TEST_F(MediaStreamVideoSourceTest, SourceChangeFrameSize) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  MockConstraintFactory factory;
-  factory.AddAdvanced().width.setMax(800);
-  factory.AddAdvanced().height.setMax(700);
-
-  // Expect the source to start capture with the supported resolution.
-  // Disable frame-rate adjustment in spec-compliant mode to ensure no frames
-  // are dropped.
-  blink::WebMediaStreamTrack track = CreateTrackAndStartSource(800, 700, 0.0);
-
-  MockMediaStreamVideoSink sink;
-  sink.ConnectToTrack(track);
-  EXPECT_EQ(0, sink.number_of_frames());
-  DeliverVideoFrameAndWaitForRenderer(320, 240, &sink);
-  EXPECT_EQ(1, sink.number_of_frames());
-  // Expect the delivered frame to be passed unchanged since its smaller than
-  // max requested.
-  EXPECT_EQ(320, sink.frame_size().width());
-  EXPECT_EQ(240, sink.frame_size().height());
-
-  DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
-  EXPECT_EQ(2, sink.number_of_frames());
-  // Expect the delivered frame to be passed unchanged since its smaller than
-  // max requested.
-  EXPECT_EQ(640, sink.frame_size().width());
-  EXPECT_EQ(480, sink.frame_size().height());
-
-  DeliverVideoFrameAndWaitForRenderer(1280, 720, &sink);
-  EXPECT_EQ(3, sink.number_of_frames());
-  // Expect a frame to be cropped since its larger than max requested.
-  EXPECT_EQ(800, sink.frame_size().width());
-  EXPECT_EQ(700, sink.frame_size().height());
-
-  sink.DisconnectFromTrack();
-}
-
 // Test that the constraint negotiation can handle 0.0 fps as frame rate.
-// Does not apply to spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, Use0FpsSupportedFormatOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, Use0FpsSupportedFormat) {
   media::VideoCaptureFormats formats;
   formats.push_back(media::VideoCaptureFormat(gfx::Size(640, 480), 0.0f,
                                               media::PIXEL_FORMAT_I420));
@@ -1158,11 +1152,7 @@
 
 // Test that a source producing no frames change the source ReadyState to muted.
 // that in a reasonable time frame the muted state turns to false.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest, MutedSourceOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest, MutedSource) {
   // Setup the source for support a frame rate of 999 fps in order to test
   // the muted event faster. This is since the frame monitoring uses
   // PostDelayedTask that is dependent on the source frame rate.
@@ -1208,56 +1198,9 @@
   sink.DisconnectFromTrack();
 }
 
-// Test that a source producing no frames change the source ReadyState to muted.
-// that in a reasonable time frame the muted state turns to false.
-TEST_F(MediaStreamVideoSourceTest, MutedSource) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  // Setup the source for support a frame rate of 999 fps in order to test
-  // the muted event faster. This is since the frame monitoring uses
-  // PostDelayedTask that is dependent on the source frame rate.
-  // Note that media::limits::kMaxFramesPerSecond is 1000.
-  blink::WebMediaStreamTrack track = CreateTrackAndStartSource(
-      640, 480, media::limits::kMaxFramesPerSecond - 2);
-  MockMediaStreamVideoSink sink;
-  sink.ConnectToTrack(track);
-  EXPECT_EQ(track.source().getReadyState(),
-            blink::WebMediaStreamSource::ReadyStateLive);
-
-  base::RunLoop run_loop;
-  base::Closure quit_closure = run_loop.QuitClosure();
-  bool muted_state = false;
-  EXPECT_CALL(*mock_source(), DoSetMutedState(_))
-      .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure)));
-  run_loop.Run();
-  EXPECT_EQ(muted_state, true);
-
-  EXPECT_EQ(track.source().getReadyState(),
-            blink::WebMediaStreamSource::ReadyStateMuted);
-
-  base::RunLoop run_loop2;
-  base::Closure quit_closure2 = run_loop2.QuitClosure();
-  EXPECT_CALL(*mock_source(), DoSetMutedState(_))
-      .WillOnce(DoAll(SaveArg<0>(&muted_state), RunClosure(quit_closure2)));
-  DeliverVideoFrameAndWaitForRenderer(640, 480, &sink);
-  run_loop2.Run();
-
-  EXPECT_EQ(muted_state, false);
-  EXPECT_EQ(track.source().getReadyState(),
-            blink::WebMediaStreamSource::ReadyStateLive);
-
-  sink.DisconnectFromTrack();
-}
-
 // Test that an optional constraint with an invalid aspect ratio is ignored.
-// Does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       InvalidOptionalAspectRatioIgnoredOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       InvalidOptionalAspectRatioIgnored) {
   MockConstraintFactory factory;
   factory.AddAdvanced().aspectRatio.setMax(0.0);
   blink::WebMediaStreamTrack track =
@@ -1268,12 +1211,8 @@
 
 // Test that setting an invalid mandatory aspect ratio fails.
 // Does not apply with spec-compliant constraints.
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoSourceTest,
-       InvalidMandatoryAspectRatioFailsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoSourceOldConstraintsTest,
+       InvalidMandatoryAspectRatioFails) {
   MockConstraintFactory factory;
   factory.basic().aspectRatio.setMax(0.0);
   blink::WebMediaStreamTrack track =
diff --git a/content/renderer/media/media_stream_video_track_unittest.cc b/content/renderer/media/media_stream_video_track_unittest.cc
index 7e6bb3b..410207c8a 100644
--- a/content/renderer/media/media_stream_video_track_unittest.cc
+++ b/content/renderer/media/media_stream_video_track_unittest.cc
@@ -37,7 +37,10 @@
   MediaStreamVideoTrackTest()
       : child_process_(new ChildProcess()),
         mock_source_(nullptr),
-        source_started_(false) {}
+        source_started_(false) {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
 
   ~MediaStreamVideoTrackTest() override {}
 
@@ -120,6 +123,7 @@
   // |mock_source_| is owned by |webkit_source_|.
   MockMediaStreamVideoSource* mock_source_;
   bool source_started_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
 TEST_F(MediaStreamVideoTrackTest, AddAndRemoveSink) {
@@ -260,31 +264,7 @@
   sink2.DisconnectFromTrack();
 }
 
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoTrackTest,
-       CheckTrackRequestsFrameOldConstraintsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  UpdateVideoSourceToRespondToRequestRefreshFrame();
-  blink::WebMediaStreamTrack track = CreateTrack();
-
-  // Add sink and expect to get a frame.
-  MockMediaStreamVideoSink sink;
-  base::RunLoop run_loop;
-  base::Closure quit_closure = run_loop.QuitClosure();
-  EXPECT_CALL(sink, OnVideoFrame()).WillOnce(RunClosure(quit_closure));
-  sink.ConnectToTrack(track);
-  run_loop.Run();
-  EXPECT_EQ(1, sink.number_of_frames());
-
-  sink.DisconnectFromTrack();
-}
-
 TEST_F(MediaStreamVideoTrackTest, CheckTrackRequestsFrame) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
   InitializeSource();
   UpdateVideoSourceToRespondToRequestRefreshFrame();
   blink::WebMediaStreamTrack track = CreateTrack();
@@ -301,11 +281,7 @@
   sink.DisconnectFromTrack();
 }
 
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoTrackTest, GetSettingsOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoTrackTest, GetSettings) {
   InitializeSource();
   blink::WebMediaStreamTrack track = CreateTrack();
   MediaStreamVideoTrack* const native_track =
@@ -319,10 +295,230 @@
   EXPECT_EQ(blink::WebMediaStreamTrack::FacingMode::None, settings.facingMode);
 }
 
-TEST_F(MediaStreamVideoTrackTest, GetSettings) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
+// TODO(guidou): Remove this test. http://crbug.com/706408
+class MediaStreamVideoTrackOldConstraintsTest : public ::testing::Test {
+ public:
+  MediaStreamVideoTrackOldConstraintsTest()
+      : child_process_(new ChildProcess()),
+        mock_source_(nullptr),
+        source_started_(false) {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
+
+  ~MediaStreamVideoTrackOldConstraintsTest() override {}
+
+  void TearDown() override {
+    blink_source_.reset();
+    blink::WebHeap::collectAllGarbageForTesting();
+  }
+
+  void DeliverVideoFrameAndWaitForRenderer(MockMediaStreamVideoSink* sink) {
+    base::RunLoop run_loop;
+    base::Closure quit_closure = run_loop.QuitClosure();
+    EXPECT_CALL(*sink, OnVideoFrame()).WillOnce(RunClosure(quit_closure));
+    const scoped_refptr<media::VideoFrame> frame =
+        media::VideoFrame::CreateColorFrame(
+            gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+                      MediaStreamVideoSource::kDefaultHeight),
+            kColorValue, kColorValue, kColorValue, base::TimeDelta());
+    mock_source()->DeliverVideoFrame(frame);
+    run_loop.Run();
+  }
+
+ protected:
+  base::MessageLoop* io_message_loop() const {
+    return child_process_->io_message_loop();
+  }
+
+  void InitializeSource() {
+    blink_source_.reset();
+    mock_source_ = IsOldVideoConstraints()
+                       ? new MockMediaStreamVideoSource(false)
+                       : new MockMediaStreamVideoSource(
+                             media::VideoCaptureFormat(
+                                 gfx::Size(kMockSourceWidth, kMockSourceHeight),
+                                 30.0, media::PIXEL_FORMAT_I420),
+                             false);
+    blink_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                             blink::WebMediaStreamSource::TypeVideo,
+                             blink::WebString::fromASCII("dummy_source_name"),
+                             false /* remote */);
+    blink_source_.setExtraData(mock_source_);
+  }
+
+  // Create a track that's associated with |mock_source_|.
+  blink::WebMediaStreamTrack CreateTrack() {
+    const bool enabled = true;
+    blink::WebMediaStreamTrack track = MediaStreamVideoTrack::CreateVideoTrack(
+        mock_source_, MediaStreamSource::ConstraintsCallback(), enabled);
+    if (!source_started_) {
+      mock_source_->StartMockedSource();
+      source_started_ = true;
+    }
+    return track;
+  }
+
+  void UpdateVideoSourceToRespondToRequestRefreshFrame() {
+    blink_source_.reset();
+    mock_source_ = IsOldVideoConstraints()
+                       ? new MockMediaStreamVideoSource(false, true)
+                       : new MockMediaStreamVideoSource(
+                             media::VideoCaptureFormat(
+                                 gfx::Size(kMockSourceWidth, kMockSourceHeight),
+                                 30.0, media::PIXEL_FORMAT_I420),
+                             true);
+    blink_source_.initialize(blink::WebString::fromASCII("dummy_source_id"),
+                             blink::WebMediaStreamSource::TypeVideo,
+                             blink::WebString::fromASCII("dummy_source_name"),
+                             false /* remote */);
+    blink_source_.setExtraData(mock_source_);
+  }
+
+  MockMediaStreamVideoSource* mock_source() { return mock_source_; }
+  const blink::WebMediaStreamSource& blink_source() const {
+    return blink_source_;
+  }
+
+ private:
+  const base::MessageLoopForUI message_loop_;
+  const std::unique_ptr<ChildProcess> child_process_;
+  blink::WebMediaStreamSource blink_source_;
+  // |mock_source_| is owned by |webkit_source_|.
+  MockMediaStreamVideoSource* mock_source_;
+  bool source_started_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, AddAndRemoveSink) {
+  InitializeSource();
+  MockMediaStreamVideoSink sink;
+  blink::WebMediaStreamTrack track = CreateTrack();
+  sink.ConnectToTrack(track);
+
+  DeliverVideoFrameAndWaitForRenderer(&sink);
+  EXPECT_EQ(1, sink.number_of_frames());
+
+  DeliverVideoFrameAndWaitForRenderer(&sink);
+
+  sink.DisconnectFromTrack();
+
+  scoped_refptr<media::VideoFrame> frame = media::VideoFrame::CreateBlackFrame(
+      gfx::Size(MediaStreamVideoSource::kDefaultWidth,
+                MediaStreamVideoSource::kDefaultHeight));
+  mock_source()->DeliverVideoFrame(frame);
+  // Wait for the IO thread to complete delivering frames.
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(2, sink.number_of_frames());
+}
+
+// Checks that the callback given to the track is reset on the right thread.
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, ResetCallbackOnThread) {
+  InitializeSource();
+  MockMediaStreamVideoSink sink;
+  blink::WebMediaStreamTrack track = CreateTrack();
+
+  base::RunLoop run_loop;
+  bool correct = false;
+  sink.ConnectToTrackWithCallback(
+      track, base::Bind(&CheckThreadVideoFrameReceiver,
+                        base::Owned(new CheckThreadHelper(
+                            run_loop.QuitClosure(), &correct))));
+  sink.DisconnectFromTrack();
+  run_loop.Run();
+  EXPECT_TRUE(correct) << "Not called on correct thread.";
+}
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, SetEnabled) {
+  InitializeSource();
+  MockMediaStreamVideoSink sink;
+  blink::WebMediaStreamTrack track = CreateTrack();
+  sink.ConnectToTrack(track);
+
+  MediaStreamVideoTrack* video_track =
+      MediaStreamVideoTrack::GetVideoTrack(track);
+
+  DeliverVideoFrameAndWaitForRenderer(&sink);
+  EXPECT_EQ(1, sink.number_of_frames());
+  EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
+
+  video_track->SetEnabled(false);
+  EXPECT_FALSE(sink.enabled());
+
+  DeliverVideoFrameAndWaitForRenderer(&sink);
+  EXPECT_EQ(2, sink.number_of_frames());
+  EXPECT_EQ(kBlackValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
+
+  video_track->SetEnabled(true);
+  EXPECT_TRUE(sink.enabled());
+  DeliverVideoFrameAndWaitForRenderer(&sink);
+  EXPECT_EQ(3, sink.number_of_frames());
+  EXPECT_EQ(kColorValue, *sink.last_frame()->data(media::VideoFrame::kYPlane));
+  sink.DisconnectFromTrack();
+}
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, SourceStopped) {
+  InitializeSource();
+  MockMediaStreamVideoSink sink;
+  blink::WebMediaStreamTrack track = CreateTrack();
+  sink.ConnectToTrack(track);
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink.state());
+
+  mock_source()->StopSource();
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink.state());
+  sink.DisconnectFromTrack();
+}
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, StopLastTrack) {
+  InitializeSource();
+  MockMediaStreamVideoSink sink1;
+  blink::WebMediaStreamTrack track1 = CreateTrack();
+  sink1.ConnectToTrack(track1);
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink1.state());
+
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
+            blink_source().getReadyState());
+
+  MockMediaStreamVideoSink sink2;
+  blink::WebMediaStreamTrack track2 = CreateTrack();
+  sink2.ConnectToTrack(track2);
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive, sink2.state());
+
+  MediaStreamVideoTrack* const native_track1 =
+      MediaStreamVideoTrack::GetVideoTrack(track1);
+  native_track1->Stop();
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink1.state());
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateLive,
+            blink_source().getReadyState());
+  sink1.DisconnectFromTrack();
+
+  MediaStreamVideoTrack* const native_track2 =
+      MediaStreamVideoTrack::GetVideoTrack(track2);
+  native_track2->Stop();
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded, sink2.state());
+  EXPECT_EQ(blink::WebMediaStreamSource::ReadyStateEnded,
+            blink_source().getReadyState());
+  sink2.DisconnectFromTrack();
+}
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest,
+       CheckTrackRequestsFrameOldConstraintsOldConstraints) {
+  UpdateVideoSourceToRespondToRequestRefreshFrame();
+  blink::WebMediaStreamTrack track = CreateTrack();
+
+  // Add sink and expect to get a frame.
+  MockMediaStreamVideoSink sink;
+  base::RunLoop run_loop;
+  base::Closure quit_closure = run_loop.QuitClosure();
+  EXPECT_CALL(sink, OnVideoFrame()).WillOnce(RunClosure(quit_closure));
+  sink.ConnectToTrack(track);
+  run_loop.Run();
+  EXPECT_EQ(1, sink.number_of_frames());
+
+  sink.DisconnectFromTrack();
+}
+
+TEST_F(MediaStreamVideoTrackOldConstraintsTest, GetSettingsOldConstraints) {
   InitializeSource();
   blink::WebMediaStreamTrack track = CreateTrack();
   MediaStreamVideoTrack* const native_track =
diff --git a/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc b/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
index a5a9874..9f2d82a 100644
--- a/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
@@ -18,7 +18,10 @@
 
 class MediaStreamVideoWebRtcSinkTest : public ::testing::Test {
  public:
-  MediaStreamVideoWebRtcSinkTest() {}
+  MediaStreamVideoWebRtcSinkTest() {
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
 
   void SetVideoTrack() {
     registry_.Init("stream URL");
@@ -61,14 +64,78 @@
   // and Sources in |registry_| into believing they are on the right threads.
   base::MessageLoopForUI message_loop_;
   const ChildProcess child_process_;
+  base::test::ScopedFeatureList scoped_feature_list_;
 };
 
+TEST_F(MediaStreamVideoWebRtcSinkTest, NoiseReductionDefaultsToNotSet) {
+  SetVideoTrack();
+  MediaStreamVideoWebRtcSink my_sink(track_, &dependency_factory_);
+  EXPECT_TRUE(my_sink.webrtc_video_track());
+  EXPECT_FALSE(my_sink.SourceNeedsDenoisingForTesting());
+}
+
 // TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoWebRtcSinkTest,
-       NoiseReductionDefaultsToNotSetOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoWebRtcSinkTest, NoiseReductionConstraintPassThrough) {
+  SetVideoTrack(base::Optional<bool>(true));
+  MediaStreamVideoWebRtcSink my_sink(track_, &dependency_factory_);
+  EXPECT_TRUE(my_sink.SourceNeedsDenoisingForTesting());
+  EXPECT_TRUE(*(my_sink.SourceNeedsDenoisingForTesting()));
+}
+
+// TODO(guidou): Remove this test. http://crbug.com/706408
+class MediaStreamVideoWebRtcSinkOldConstraintsTest : public ::testing::Test {
+ public:
+  MediaStreamVideoWebRtcSinkOldConstraintsTest() {
+    scoped_feature_list_.InitAndEnableFeature(
+        features::kMediaStreamOldVideoConstraints);
+  }
+
+  void SetVideoTrack() {
+    registry_.Init("stream URL");
+    registry_.AddVideoTrack("test video track");
+    blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+    registry_.test_stream().videoTracks(video_tracks);
+    track_ = video_tracks[0];
+    // TODO(hta): Verify that track_ is valid. When constraints produce
+    // no valid format, using the track will cause a crash.
+  }
+
+  void SetVideoTrack(blink::WebMediaConstraints constraints) {
+    registry_.Init("stream URL");
+    registry_.AddVideoTrack("test video track", constraints);
+    blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+    registry_.test_stream().videoTracks(video_tracks);
+    track_ = video_tracks[0];
+    // TODO(hta): Verify that track_ is valid. When constraints produce
+    // no valid format, using the track will cause a crash.
+  }
+
+  void SetVideoTrack(const base::Optional<bool>& noise_reduction) {
+    registry_.Init("stream URL");
+    registry_.AddVideoTrack("test video track", VideoTrackAdapterSettings(),
+                            noise_reduction, false, 0.0);
+    blink::WebVector<blink::WebMediaStreamTrack> video_tracks;
+    registry_.test_stream().videoTracks(video_tracks);
+    track_ = video_tracks[0];
+    // TODO(hta): Verify that track_ is valid. When constraints produce
+    // no valid format, using the track will cause a crash.
+  }
+
+ protected:
+  blink::WebMediaStreamTrack track_;
+  MockPeerConnectionDependencyFactory dependency_factory_;
+
+ private:
+  MockMediaStreamRegistry registry_;
+  // A ChildProcess and a MessageLoopForUI are both needed to fool the Tracks
+  // and Sources in |registry_| into believing they are on the right threads.
+  base::MessageLoopForUI message_loop_;
+  const ChildProcess child_process_;
+  base::test::ScopedFeatureList scoped_feature_list_;
+};
+
+TEST_F(MediaStreamVideoWebRtcSinkOldConstraintsTest,
+       NoiseReductionDefaultsToNotSet) {
   blink::WebMediaConstraints constraints;
   constraints.initialize();
   SetVideoTrack(constraints);
@@ -77,23 +144,8 @@
   EXPECT_FALSE(my_sink.SourceNeedsDenoisingForTesting());
 }
 
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoWebRtcSinkTest, NoiseReductionDefaultsToNotSet) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  SetVideoTrack();
-  MediaStreamVideoWebRtcSink my_sink(track_, &dependency_factory_);
-  EXPECT_TRUE(my_sink.webrtc_video_track());
-  EXPECT_FALSE(my_sink.SourceNeedsDenoisingForTesting());
-}
-
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoWebRtcSinkTest,
-       NoiseReductionConstraintPassThroughOldConstraints) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeature(
-      features::kMediaStreamOldVideoConstraints);
+TEST_F(MediaStreamVideoWebRtcSinkOldConstraintsTest,
+       NoiseReductionConstraintPassThrough) {
   MockConstraintFactory factory;
   factory.basic().googNoiseReduction.setExact(true);
   SetVideoTrack(factory.CreateWebMediaConstraints());
@@ -102,16 +154,5 @@
   EXPECT_TRUE(*(my_sink.SourceNeedsDenoisingForTesting()));
 }
 
-// TODO(guidou): Remove this test. http://crbug.com/706408
-TEST_F(MediaStreamVideoWebRtcSinkTest, NoiseReductionConstraintPassThrough) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kMediaStreamOldVideoConstraints);
-  SetVideoTrack(base::Optional<bool>(true));
-  MediaStreamVideoWebRtcSink my_sink(track_, &dependency_factory_);
-  EXPECT_TRUE(my_sink.SourceNeedsDenoisingForTesting());
-  EXPECT_TRUE(*(my_sink.SourceNeedsDenoisingForTesting()));
-}
-
 }  // namespace
 }  // namespace content
diff --git a/content/renderer/pepper/content_renderer_pepper_host_factory.cc b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
index 16eff2bcc..3423b01e 100644
--- a/content/renderer/pepper/content_renderer_pepper_host_factory.cc
+++ b/content/renderer/pepper/content_renderer_pepper_host_factory.cc
@@ -15,6 +15,7 @@
 #include "content/public/renderer/content_renderer_client.h"
 #include "content/renderer/pepper/pepper_audio_encoder_host.h"
 #include "content/renderer/pepper/pepper_audio_input_host.h"
+#include "content/renderer/pepper/pepper_audio_output_host.h"
 #include "content/renderer/pepper/pepper_camera_device_host.h"
 #include "content/renderer/pepper/pepper_compositor_host.h"
 #include "content/renderer/pepper/pepper_file_chooser_host.h"
@@ -211,6 +212,9 @@
       case PpapiHostMsg_AudioInput_Create::ID:
         return base::MakeUnique<PepperAudioInputHost>(host_, instance,
                                                       resource);
+      case PpapiHostMsg_AudioOutput_Create::ID:
+        return base::MakeUnique<PepperAudioOutputHost>(host_, instance,
+                                                       resource);
       case PpapiHostMsg_FileChooser_Create::ID:
         return base::MakeUnique<PepperFileChooserHost>(host_, instance,
                                                        resource);
diff --git a/content/renderer/pepper/pepper_audio_controller.cc b/content/renderer/pepper/pepper_audio_controller.cc
index 88d80f1..759666e 100644
--- a/content/renderer/pepper/pepper_audio_controller.cc
+++ b/content/renderer/pepper/pepper_audio_controller.cc
@@ -4,6 +4,7 @@
 
 #include "content/renderer/pepper/pepper_audio_controller.h"
 
+#include "content/renderer/pepper/pepper_audio_output_host.h"
 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
 #include "content/renderer/pepper/ppb_audio_impl.h"
 #include "content/renderer/render_frame_impl.h"
@@ -27,15 +28,22 @@
   if (ppb_audios_.count(audio))
     return;
 
-  if (ppb_audios_.empty()) {
-    RenderFrameImpl* render_frame = instance_->render_frame();
-    if (render_frame)
-      render_frame->PepperStartsPlayback(instance_);
-  }
+  StartPlaybackIfFirstInstance();
 
   ppb_audios_.insert(audio);
 }
 
+void PepperAudioController::AddInstance(PepperAudioOutputHost* audio_output) {
+  if (!instance_)
+    return;
+  if (audio_output_hosts_.count(audio_output))
+    return;
+
+  StartPlaybackIfFirstInstance();
+
+  audio_output_hosts_.insert(audio_output);
+}
+
 void PepperAudioController::RemoveInstance(PPB_Audio_Impl* audio) {
   if (!instance_)
     return;
@@ -44,8 +52,19 @@
 
   ppb_audios_.erase(audio);
 
-  if (ppb_audios_.empty())
-    NotifyPlaybackStopsOnEmpty();
+  StopPlaybackIfLastInstance();
+}
+
+void PepperAudioController::RemoveInstance(
+    PepperAudioOutputHost* audio_output) {
+  if (!instance_)
+    return;
+  if (!audio_output_hosts_.count(audio_output))
+    return;
+
+  audio_output_hosts_.erase(audio_output);
+
+  StopPlaybackIfLastInstance();
 }
 
 void PepperAudioController::SetVolume(double volume) {
@@ -54,15 +73,19 @@
 
   for (auto* ppb_audio : ppb_audios_)
     ppb_audio->SetVolume(volume);
+
+  for (auto* audio_output_host : audio_output_hosts_)
+    audio_output_host->SetVolume(volume);
 }
 
 void PepperAudioController::OnPepperInstanceDeleted() {
   DCHECK(instance_);
 
-  if (!ppb_audios_.empty())
+  if (!audio_output_hosts_.empty() || !ppb_audios_.empty())
     NotifyPlaybackStopsOnEmpty();
 
   ppb_audios_.clear();
+  audio_output_hosts_.clear();
   instance_ = nullptr;
 }
 
@@ -74,4 +97,21 @@
     render_frame->PepperStopsPlayback(instance_);
 }
 
+void PepperAudioController::StartPlaybackIfFirstInstance() {
+  DCHECK(instance_);
+
+  if (audio_output_hosts_.empty() && ppb_audios_.empty()) {
+    RenderFrameImpl* render_frame = instance_->render_frame();
+    if (render_frame)
+      render_frame->PepperStartsPlayback(instance_);
+  }
+}
+
+void PepperAudioController::StopPlaybackIfLastInstance() {
+  DCHECK(instance_);
+
+  if (audio_output_hosts_.empty() && ppb_audios_.empty())
+    NotifyPlaybackStopsOnEmpty();
+}
+
 }  // namespace content
diff --git a/content/renderer/pepper/pepper_audio_controller.h b/content/renderer/pepper/pepper_audio_controller.h
index 29241ab..8e8b8741 100644
--- a/content/renderer/pepper/pepper_audio_controller.h
+++ b/content/renderer/pepper/pepper_audio_controller.h
@@ -11,6 +11,7 @@
 
 namespace content {
 
+class PepperAudioOutputHost;
 class PepperPluginInstanceImpl;
 class PPB_Audio_Impl;
 
@@ -25,9 +26,11 @@
 
   // Adds an audio instance to the controller.
   void AddInstance(PPB_Audio_Impl* audio);
+  void AddInstance(PepperAudioOutputHost* audio_output);
 
   // Removes an audio instance from the controller.
   void RemoveInstance(PPB_Audio_Impl* audio);
+  void RemoveInstance(PepperAudioOutputHost* audio_output);
 
   // Sets the volume of all audio instances.
   void SetVolume(double volume);
@@ -42,9 +45,18 @@
   // only be called when |ppb_audios_| turns from non-empty to empty.
   void NotifyPlaybackStopsOnEmpty();
 
-  // All active audio instances.
+  // Helper functions to deal with the first and last audio instance.
+  void StartPlaybackIfFirstInstance();
+  void StopPlaybackIfLastInstance();
+
+  // All active audio instances that are using the old
+  // PPB_Audio interface.
   std::set<PPB_Audio_Impl*> ppb_audios_;
 
+  // All active audio output instances that are using the new
+  // PPB_AudioOutput interface.
+  std::set<PepperAudioOutputHost*> audio_output_hosts_;
+
   // The Pepper instance which this controller is for. Will be null after
   // OnPepperInstanceDeleted() is called.
   PepperPluginInstanceImpl* instance_;
diff --git a/content/renderer/pepper/pepper_audio_output_host.cc b/content/renderer/pepper/pepper_audio_output_host.cc
new file mode 100644
index 0000000..6f264b9
--- /dev/null
+++ b/content/renderer/pepper/pepper_audio_output_host.cc
@@ -0,0 +1,265 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/pepper/pepper_audio_output_host.h"
+
+#include "base/logging.h"
+#include "build/build_config.h"
+#include "content/common/pepper_file_util.h"
+#include "content/renderer/pepper/pepper_audio_controller.h"
+#include "content/renderer/pepper/pepper_media_device_manager.h"
+#include "content/renderer/pepper/pepper_platform_audio_output_dev.h"
+#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
+#include "content/renderer/pepper/plugin_instance_throttler_impl.h"
+#include "content/renderer/pepper/renderer_ppapi_host_impl.h"
+#include "content/renderer/render_frame_impl.h"
+#include "ipc/ipc_message.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/host/dispatch_host_message.h"
+#include "ppapi/host/ppapi_host.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/serialized_structs.h"
+
+namespace content {
+
+namespace {
+
+base::PlatformFile ConvertSyncSocketHandle(const base::SyncSocket& socket) {
+  return socket.handle();
+}
+
+}  // namespace
+
+PepperAudioOutputHost::PepperAudioOutputHost(RendererPpapiHostImpl* host,
+                                             PP_Instance instance,
+                                             PP_Resource resource)
+    : ResourceHost(host->GetPpapiHost(), instance, resource),
+      renderer_ppapi_host_(host),
+      audio_output_(NULL),
+      playback_throttled_(false),
+      enumeration_helper_(this,
+                          PepperMediaDeviceManager::GetForRenderFrame(
+                              host->GetRenderFrameForInstance(pp_instance())),
+                          PP_DEVICETYPE_DEV_AUDIOOUTPUT,
+                          host->GetDocumentURL(instance)) {
+  PepperPluginInstanceImpl* plugin_instance =
+      static_cast<PepperPluginInstanceImpl*>(
+          PepperPluginInstance::Get(pp_instance()));
+  if (plugin_instance && plugin_instance->throttler())
+    plugin_instance->throttler()->AddObserver(this);
+}
+
+PepperAudioOutputHost::~PepperAudioOutputHost() {
+  PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
+      PepperPluginInstance::Get(pp_instance()));
+  if (instance) {
+    if (instance->throttler()) {
+      instance->throttler()->RemoveObserver(this);
+    }
+    instance->audio_controller().RemoveInstance(this);
+  }
+  Close();
+}
+
+int32_t PepperAudioOutputHost::OnResourceMessageReceived(
+    const IPC::Message& msg,
+    ppapi::host::HostMessageContext* context) {
+  int32_t result = PP_ERROR_FAILED;
+  if (enumeration_helper_.HandleResourceMessage(msg, context, &result))
+    return result;
+
+  PPAPI_BEGIN_MESSAGE_MAP(PepperAudioOutputHost, msg)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioOutput_Open, OnOpen)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_AudioOutput_StartOrStop,
+                                      OnStartOrStop)
+    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_AudioOutput_Close, OnClose)
+  PPAPI_END_MESSAGE_MAP()
+  return PP_ERROR_FAILED;
+}
+
+void PepperAudioOutputHost::StreamCreated(
+    base::SharedMemoryHandle shared_memory_handle,
+    size_t shared_memory_size,
+    base::SyncSocket::Handle socket) {
+  OnOpenComplete(PP_OK, shared_memory_handle, shared_memory_size, socket);
+}
+
+void PepperAudioOutputHost::StreamCreationFailed() {
+  OnOpenComplete(PP_ERROR_FAILED, base::SharedMemory::NULLHandle(), 0,
+                 base::SyncSocket::kInvalidHandle);
+}
+
+void PepperAudioOutputHost::SetVolume(double volume) {
+  if (audio_output_)
+    audio_output_->SetVolume(volume);
+}
+
+int32_t PepperAudioOutputHost::OnOpen(ppapi::host::HostMessageContext* context,
+                                      const std::string& device_id,
+                                      PP_AudioSampleRate sample_rate,
+                                      uint32_t sample_frame_count) {
+  if (open_context_.is_valid())
+    return PP_ERROR_INPROGRESS;
+
+  if (audio_output_)
+    return PP_ERROR_FAILED;
+
+  GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance());
+  if (!document_url.is_valid())
+    return PP_ERROR_FAILED;
+
+  // When it is done, we'll get called back on StreamCreated() or
+  // StreamCreationFailed().
+  audio_output_ = PepperPlatformAudioOutputDev::Create(
+      renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())
+          ->GetRoutingID(),
+      device_id, document_url, static_cast<int>(sample_rate),
+      static_cast<int>(sample_frame_count), this);
+  if (audio_output_) {
+    open_context_ = context->MakeReplyMessageContext();
+    return PP_OK_COMPLETIONPENDING;
+  } else {
+    return PP_ERROR_FAILED;
+  }
+}
+
+int32_t PepperAudioOutputHost::OnStartOrStop(
+    ppapi::host::HostMessageContext* /* context */,
+    bool playback) {
+  if (!audio_output_)
+    return PP_ERROR_FAILED;
+
+  PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
+      PepperPluginInstance::Get(pp_instance()));
+
+  if (playback) {
+    // If plugin is in power saver mode, defer audio IPC communication.
+    if (instance && instance->throttler() &&
+        instance->throttler()->power_saver_enabled()) {
+      instance->throttler()->NotifyAudioThrottled();
+      playback_throttled_ = true;
+      return PP_TRUE;
+    }
+    if (instance)
+      instance->audio_controller().AddInstance(this);
+
+    audio_output_->StartPlayback();
+  } else {
+    if (instance)
+      instance->audio_controller().RemoveInstance(this);
+
+    audio_output_->StopPlayback();
+  }
+  return PP_OK;
+}
+
+int32_t PepperAudioOutputHost::OnClose(
+    ppapi::host::HostMessageContext* /* context */) {
+  Close();
+  return PP_OK;
+}
+
+void PepperAudioOutputHost::OnOpenComplete(
+    int32_t result,
+    base::SharedMemoryHandle shared_memory_handle,
+    size_t shared_memory_size,
+    base::SyncSocket::Handle socket_handle) {
+  // Make sure the handles are cleaned up.
+  base::SyncSocket scoped_socket(socket_handle);
+  base::SharedMemory scoped_shared_memory(shared_memory_handle, false);
+
+  if (!open_context_.is_valid()) {
+    NOTREACHED();
+    return;
+  }
+
+  ppapi::proxy::SerializedHandle serialized_socket_handle(
+      ppapi::proxy::SerializedHandle::SOCKET);
+  ppapi::proxy::SerializedHandle serialized_shared_memory_handle(
+      ppapi::proxy::SerializedHandle::SHARED_MEMORY);
+
+  if (result == PP_OK) {
+    IPC::PlatformFileForTransit temp_socket =
+        IPC::InvalidPlatformFileForTransit();
+    base::SharedMemoryHandle temp_shmem = base::SharedMemory::NULLHandle();
+    result = GetRemoteHandles(scoped_socket, scoped_shared_memory, &temp_socket,
+                              &temp_shmem);
+
+    serialized_socket_handle.set_socket(temp_socket);
+    serialized_shared_memory_handle.set_shmem(temp_shmem, shared_memory_size);
+  }
+
+  // Send all the values, even on error. This simplifies some of our cleanup
+  // code since the handles will be in the other process and could be
+  // inconvenient to clean up. Our IPC code will automatically handle this for
+  // us, as long as the remote side always closes the handles it receives, even
+  // in the failure case.
+  open_context_.params.AppendHandle(serialized_socket_handle);
+  open_context_.params.AppendHandle(serialized_shared_memory_handle);
+  SendOpenReply(result);
+}
+
+int32_t PepperAudioOutputHost::GetRemoteHandles(
+    const base::SyncSocket& socket,
+    const base::SharedMemory& shared_memory,
+    IPC::PlatformFileForTransit* remote_socket_handle,
+    base::SharedMemoryHandle* remote_shared_memory_handle) {
+  *remote_socket_handle = renderer_ppapi_host_->ShareHandleWithRemote(
+      ConvertSyncSocketHandle(socket), false);
+  if (*remote_socket_handle == IPC::InvalidPlatformFileForTransit())
+    return PP_ERROR_FAILED;
+
+  *remote_shared_memory_handle =
+      renderer_ppapi_host_->ShareSharedMemoryHandleWithRemote(
+          shared_memory.handle());
+  if (!base::SharedMemory::IsHandleValid(*remote_shared_memory_handle))
+    return PP_ERROR_FAILED;
+
+  return PP_OK;
+}
+
+void PepperAudioOutputHost::Close() {
+  if (!audio_output_)
+    return;
+
+  audio_output_->ShutDown();
+  audio_output_ = NULL;
+
+  if (open_context_.is_valid())
+    SendOpenReply(PP_ERROR_ABORTED);
+}
+
+void PepperAudioOutputHost::SendOpenReply(int32_t result) {
+  open_context_.params.set_result(result);
+  host()->SendReply(open_context_, PpapiPluginMsg_AudioOutput_OpenReply());
+  open_context_ = ppapi::host::ReplyMessageContext();
+}
+
+void PepperAudioOutputHost::OnThrottleStateChange() {
+  PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
+      PepperPluginInstance::Get(pp_instance()));
+  if (playback_throttled_ && instance && instance->throttler() &&
+      !instance->throttler()->power_saver_enabled()) {
+    // If we have become unthrottled, and we have a pending playback,
+    // start it.
+    StartDeferredPlayback();
+  }
+}
+
+void PepperAudioOutputHost::StartDeferredPlayback() {
+  if (!audio_output_)
+    return;
+
+  DCHECK(playback_throttled_);
+  playback_throttled_ = false;
+
+  PepperPluginInstanceImpl* instance = static_cast<PepperPluginInstanceImpl*>(
+      PepperPluginInstance::Get(pp_instance()));
+  if (instance)
+    instance->audio_controller().AddInstance(this);
+
+  audio_output_->StartPlayback();
+}
+
+}  // namespace content
diff --git a/content/renderer/pepper/pepper_audio_output_host.h b/content/renderer/pepper/pepper_audio_output_host.h
new file mode 100644
index 0000000..e8ea573
--- /dev/null
+++ b/content/renderer/pepper/pepper_audio_output_host.h
@@ -0,0 +1,97 @@
+// Copyright (c) 2017 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 CONTENT_RENDERER_PEPPER_PEPPER_AUDIO_OUTPUT_HOST_H_
+#define CONTENT_RENDERER_PEPPER_PEPPER_AUDIO_OUTPUT_HOST_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/shared_memory.h"
+#include "base/sync_socket.h"
+#include "content/public/renderer/plugin_instance_throttler.h"
+#include "content/renderer/pepper/pepper_device_enumeration_host_helper.h"
+#include "ipc/ipc_platform_file.h"
+#include "ppapi/c/ppb_audio_config.h"
+#include "ppapi/host/host_message_context.h"
+#include "ppapi/host/resource_host.h"
+
+namespace content {
+class PepperPlatformAudioOutputDev;
+class RendererPpapiHostImpl;
+
+class PepperAudioOutputHost : public ppapi::host::ResourceHost,
+                              public PluginInstanceThrottler::Observer {
+ public:
+  PepperAudioOutputHost(RendererPpapiHostImpl* host,
+                        PP_Instance instance,
+                        PP_Resource resource);
+  ~PepperAudioOutputHost() override;
+
+  int32_t OnResourceMessageReceived(
+      const IPC::Message& msg,
+      ppapi::host::HostMessageContext* context) override;
+
+  // Called when the stream is created.
+  void StreamCreated(base::SharedMemoryHandle shared_memory_handle,
+                     size_t shared_memory_size,
+                     base::SyncSocket::Handle socket);
+  void StreamCreationFailed();
+  void SetVolume(double volume);
+
+ private:
+  int32_t OnOpen(ppapi::host::HostMessageContext* context,
+                 const std::string& device_id,
+                 PP_AudioSampleRate sample_rate,
+                 uint32_t sample_frame_count);
+  int32_t OnStartOrStop(ppapi::host::HostMessageContext* context,
+                        bool playback);
+  int32_t OnClose(ppapi::host::HostMessageContext* context);
+
+  void OnOpenComplete(int32_t result,
+                      base::SharedMemoryHandle shared_memory_handle,
+                      size_t shared_memory_size,
+                      base::SyncSocket::Handle socket_handle);
+
+  int32_t GetRemoteHandles(
+      const base::SyncSocket& socket,
+      const base::SharedMemory& shared_memory,
+      IPC::PlatformFileForTransit* remote_socket_handle,
+      base::SharedMemoryHandle* remote_shared_memory_handle);
+
+  void Close();
+
+  void SendOpenReply(int32_t result);
+
+  // PluginInstanceThrottler::Observer implementation.
+  void OnThrottleStateChange() override;
+
+  // Starts the deferred playback and unsubscribes from the throttler.
+  void StartDeferredPlayback();
+
+  // Non-owning pointer.
+  RendererPpapiHostImpl* renderer_ppapi_host_;
+
+  ppapi::host::ReplyMessageContext open_context_;
+
+  // Audio output object that we delegate audio IPC through.
+  // We don't own this pointer but are responsible for calling Shutdown on it.
+  PepperPlatformAudioOutputDev* audio_output_;
+
+  // Stream is playing, but throttled due to Plugin Power Saver.
+  bool playback_throttled_;
+
+  PepperDeviceEnumerationHostHelper enumeration_helper_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperAudioOutputHost);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_PEPPER_PEPPER_AUDIO_OUTPUT_HOST_H_
diff --git a/content/renderer/pepper/pepper_compositor_host.cc b/content/renderer/pepper/pepper_compositor_host.cc
index c0038dc..5c08126 100644
--- a/content/renderer/pepper/pepper_compositor_host.cc
+++ b/content/renderer/pepper/pepper_compositor_host.cc
@@ -16,7 +16,6 @@
 #include "cc/layers/texture_layer.h"
 #include "cc/resources/texture_mailbox.h"
 #include "cc/trees/layer_tree_host.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/pepper/gfx_conversion.h"
 #include "content/renderer/pepper/host_globals.h"
@@ -29,6 +28,7 @@
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/thunk/enter.h"
 #include "ppapi/thunk/ppb_image_data_api.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 #include "third_party/khronos/GLES2/gl2.h"
 #include "ui/gfx/geometry/size_conversions.h"
 #include "ui/gfx/transform.h"
diff --git a/content/renderer/pepper/pepper_graphics_2d_host.cc b/content/renderer/pepper/pepper_graphics_2d_host.cc
index e8df984..c2f625f 100644
--- a/content/renderer/pepper/pepper_graphics_2d_host.cc
+++ b/content/renderer/pepper/pepper_graphics_2d_host.cc
@@ -17,7 +17,6 @@
 #include "cc/paint/paint_flags.h"
 #include "cc/resources/shared_bitmap.h"
 #include "cc/resources/texture_mailbox.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/public/renderer/render_thread.h"
 #include "content/public/renderer/renderer_ppapi_host.h"
 #include "content/renderer/pepper/gfx_conversion.h"
@@ -35,6 +34,7 @@
 #include "ppapi/proxy/ppapi_messages.h"
 #include "ppapi/shared_impl/ppb_view_shared.h"
 #include "ppapi/thunk/enter.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/skia/include/core/SkBitmap.h"
 #include "third_party/skia/include/core/SkSwizzle.h"
diff --git a/content/renderer/pepper/pepper_media_device_manager.cc b/content/renderer/pepper/pepper_media_device_manager.cc
index 8888bb7..55a2fd0 100644
--- a/content/renderer/pepper/pepper_media_device_manager.cc
+++ b/content/renderer/pepper/pepper_media_device_manager.cc
@@ -25,6 +25,8 @@
       return PP_DEVICETYPE_DEV_AUDIOCAPTURE;
     case MEDIA_DEVICE_TYPE_VIDEO_INPUT:
       return PP_DEVICETYPE_DEV_VIDEOCAPTURE;
+    case MEDIA_DEVICE_TYPE_AUDIO_OUTPUT:
+      return PP_DEVICETYPE_DEV_AUDIOOUTPUT;
     default:
       NOTREACHED();
       return PP_DEVICETYPE_DEV_INVALID;
@@ -37,6 +39,8 @@
       return MEDIA_DEVICE_TYPE_AUDIO_INPUT;
     case PP_DEVICETYPE_DEV_VIDEOCAPTURE:
       return MEDIA_DEVICE_TYPE_VIDEO_INPUT;
+    case PP_DEVICETYPE_DEV_AUDIOOUTPUT:
+      return MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
     default:
       NOTREACHED();
       return MEDIA_DEVICE_TYPE_AUDIO_OUTPUT;
@@ -82,9 +86,10 @@
 #if BUILDFLAG(ENABLE_WEBRTC)
   bool request_audio_input = type == PP_DEVICETYPE_DEV_AUDIOCAPTURE;
   bool request_video_input = type == PP_DEVICETYPE_DEV_VIDEOCAPTURE;
-  CHECK(request_audio_input || request_video_input);
+  bool request_audio_output = type == PP_DEVICETYPE_DEV_AUDIOOUTPUT;
+  CHECK(request_audio_input || request_video_input || request_audio_output);
   GetMediaDevicesDispatcher()->EnumerateDevices(
-      request_audio_input, request_video_input, false /* audio_output */,
+      request_audio_input, request_video_input, request_audio_output,
       url::Origin(document_url.GetOrigin()),
       base::Bind(&PepperMediaDeviceManager::DevicesEnumerated, AsWeakPtr(),
                  callback, ToMediaDeviceType(type)));
diff --git a/content/renderer/pepper/pepper_platform_audio_output_dev.cc b/content/renderer/pepper/pepper_platform_audio_output_dev.cc
new file mode 100644
index 0000000..63bb2e4
--- /dev/null
+++ b/content/renderer/pepper/pepper_platform_audio_output_dev.cc
@@ -0,0 +1,407 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/renderer/pepper/pepper_platform_audio_output_dev.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/threading/thread_task_runner_handle.h"
+#include "base/time/time.h"
+#include "base/timer/timer.h"
+#include "build/build_config.h"
+#include "content/child/child_process.h"
+#include "content/common/content_constants_internal.h"
+#include "content/common/media/audio_messages.h"
+#include "content/renderer/media/audio_message_filter.h"
+#include "content/renderer/pepper/audio_helper.h"
+#include "content/renderer/pepper/pepper_audio_output_host.h"
+#include "content/renderer/pepper/pepper_media_device_manager.h"
+#include "content/renderer/render_frame_impl.h"
+#include "content/renderer/render_thread_impl.h"
+#include "media/audio/audio_device_description.h"
+#include "ppapi/shared_impl/ppb_audio_config_shared.h"
+
+namespace content {
+
+// static
+PepperPlatformAudioOutputDev* PepperPlatformAudioOutputDev::Create(
+    int render_frame_id,
+    const std::string& device_id,
+    const GURL& document_url,
+    int sample_rate,
+    int frames_per_buffer,
+    PepperAudioOutputHost* client) {
+  scoped_refptr<PepperPlatformAudioOutputDev> audio_output(
+      new PepperPlatformAudioOutputDev(
+          render_frame_id, device_id, document_url,
+          // Set authorization request timeout at 80% of renderer hung timeout,
+          // but no more than kMaxAuthorizationTimeout.
+          base::TimeDelta::FromMilliseconds(std::min(
+              kHungRendererDelayMs * 8 / 10, kMaxAuthorizationTimeoutMs))));
+
+  if (audio_output->Initialize(sample_rate, frames_per_buffer, client)) {
+    // Balanced by Release invoked in
+    // PepperPlatformAudioOutputDev::ShutDownOnIOThread().
+    audio_output->AddRef();
+    return audio_output.get();
+  }
+  return NULL;
+}
+
+void PepperPlatformAudioOutputDev::RequestDeviceAuthorization() {
+  if (ipc_) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(
+            &PepperPlatformAudioOutputDev::RequestDeviceAuthorizationOnIOThread,
+            this));
+  }
+}
+
+bool PepperPlatformAudioOutputDev::StartPlayback() {
+  if (ipc_) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&PepperPlatformAudioOutputDev::StartPlaybackOnIOThread,
+                   this));
+    return true;
+  }
+  return false;
+}
+
+bool PepperPlatformAudioOutputDev::StopPlayback() {
+  if (ipc_) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&PepperPlatformAudioOutputDev::StopPlaybackOnIOThread,
+                   this));
+    return true;
+  }
+  return false;
+}
+
+bool PepperPlatformAudioOutputDev::SetVolume(double volume) {
+  if (ipc_) {
+    io_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&PepperPlatformAudioOutputDev::SetVolumeOnIOThread, this,
+                   volume));
+    return true;
+  }
+  return false;
+}
+
+void PepperPlatformAudioOutputDev::ShutDown() {
+  // Called on the main thread to stop all audio callbacks. We must only change
+  // the client on the main thread, and the delegates from the I/O thread.
+  client_ = NULL;
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&PepperPlatformAudioOutputDev::ShutDownOnIOThread, this));
+}
+
+void PepperPlatformAudioOutputDev::OnError() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  // Do nothing if the stream has been closed.
+  if (state_ < CREATING_STREAM)
+    return;
+
+  DLOG(WARNING) << "PepperPlatformAudioOutputDev::OnError()";
+}
+
+void PepperPlatformAudioOutputDev::OnDeviceAuthorized(
+    media::OutputDeviceStatus device_status,
+    const media::AudioParameters& output_params,
+    const std::string& matched_device_id) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  auth_timeout_action_.reset();
+
+  // Do nothing if late authorization is received after timeout.
+  if (state_ == IPC_CLOSED)
+    return;
+
+  LOG_IF(WARNING, device_status == media::OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT)
+      << "Output device authorization timed out";
+
+  DCHECK_EQ(state_, AUTHORIZING);
+
+  // It may happen that a second authorization is received as a result to a
+  // call to StartPlayback() after Shutdown(). If the status for the second
+  // authorization differs from the first, it will not be reflected in
+  // |device_status_| to avoid a race.
+  // This scenario is unlikely. If it occurs, the new value will be
+  // different from OUTPUT_DEVICE_STATUS_OK, so the PepperPlatformAudioOutputDev
+  // will enter the IPC_CLOSED state anyway, which is the safe thing to do.
+  // This is preferable to holding a lock.
+  if (!did_receive_auth_.IsSignaled())
+    device_status_ = device_status;
+
+  if (device_status == media::OUTPUT_DEVICE_STATUS_OK) {
+    state_ = AUTHORIZED;
+    if (!did_receive_auth_.IsSignaled()) {
+      output_params_ = output_params;
+
+      // It's possible to not have a matched device obtained via session id. It
+      // means matching output device through |session_id_| failed and the
+      // default device is used.
+      DCHECK(media::AudioDeviceDescription::UseSessionIdToSelectDevice(
+                 session_id_, device_id_) ||
+             matched_device_id_.empty());
+      matched_device_id_ = matched_device_id;
+
+      DVLOG(1) << "PepperPlatformAudioOutputDev authorized, session_id: "
+               << session_id_ << ", device_id: " << device_id_
+               << ", matched_device_id: " << matched_device_id_;
+
+      did_receive_auth_.Signal();
+    }
+    if (start_on_authorized_)
+      CreateStreamOnIOThread(params_);
+  } else {
+    // Closing IPC forces a Signal(), so no clients are locked waiting
+    // indefinitely after this method returns.
+    ipc_->CloseStream();
+    OnIPCClosed();
+    main_task_runner_->PostTask(
+        FROM_HERE,
+        base::Bind(&PepperPlatformAudioOutputDev::NotifyStreamCreationFailed,
+                   this));
+  }
+}
+
+void PepperPlatformAudioOutputDev::OnStreamCreated(
+    base::SharedMemoryHandle handle,
+    base::SyncSocket::Handle socket_handle,
+    int length) {
+#if defined(OS_WIN)
+  DCHECK(handle.IsValid());
+  DCHECK(socket_handle);
+#else
+  DCHECK(base::SharedMemory::IsHandleValid(handle));
+  DCHECK_NE(-1, socket_handle);
+#endif
+  DCHECK(length);
+
+  if (base::ThreadTaskRunnerHandle::Get().get() == main_task_runner_.get()) {
+    // Must dereference the client only on the main thread. Shutdown may have
+    // occurred while the request was in-flight, so we need to NULL check.
+    if (client_)
+      client_->StreamCreated(handle, length, socket_handle);
+  } else {
+    DCHECK(io_task_runner_->BelongsToCurrentThread());
+    if (state_ != CREATING_STREAM)
+      return;
+
+    state_ = PAUSED;
+    if (play_on_start_)
+      StartPlaybackOnIOThread();
+
+    main_task_runner_->PostTask(
+        FROM_HERE, base::Bind(&PepperPlatformAudioOutputDev::OnStreamCreated,
+                              this, handle, socket_handle, length));
+  }
+}
+
+void PepperPlatformAudioOutputDev::OnIPCClosed() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  state_ = IPC_CLOSED;
+  ipc_.reset();
+
+  // Signal to unblock any blocked threads waiting for parameters
+  did_receive_auth_.Signal();
+}
+
+PepperPlatformAudioOutputDev::~PepperPlatformAudioOutputDev() {
+  // Make sure we have been shut down. Warning: this will usually happen on
+  // the I/O thread!
+  DCHECK(!ipc_);
+  DCHECK(!client_);
+}
+
+PepperPlatformAudioOutputDev::PepperPlatformAudioOutputDev(
+    int render_frame_id,
+    const std::string& device_id,
+    const GURL& document_url,
+    base::TimeDelta authorization_timeout)
+    : client_(NULL),
+      main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
+      io_task_runner_(ChildProcess::current()->io_task_runner()),
+      render_frame_id_(render_frame_id),
+      state_(IDLE),
+      start_on_authorized_(true),
+      play_on_start_(false),
+      session_id_(0),
+      device_id_(device_id),
+      security_origin_(document_url),
+      did_receive_auth_(base::WaitableEvent::ResetPolicy::MANUAL,
+                        base::WaitableEvent::InitialState::NOT_SIGNALED),
+      device_status_(media::OUTPUT_DEVICE_STATUS_ERROR_INTERNAL),
+      auth_timeout_(authorization_timeout) {}
+
+bool PepperPlatformAudioOutputDev::Initialize(int sample_rate,
+                                              int frames_per_buffer,
+                                              PepperAudioOutputHost* client) {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+  RenderFrameImpl* const render_frame =
+      RenderFrameImpl::FromRoutingID(render_frame_id_);
+  if (!render_frame || !client)
+    return false;
+
+  client_ = client;
+
+  RenderThreadImpl* const render_thread = RenderThreadImpl::current();
+  ipc_ = render_thread->audio_message_filter()->CreateAudioOutputIPC(
+      render_frame_id_);
+  CHECK(ipc_);
+
+  params_.Reset(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
+                media::CHANNEL_LAYOUT_STEREO, sample_rate,
+                ppapi::kBitsPerAudioOutputSample, frames_per_buffer);
+
+  io_task_runner_->PostTask(
+      FROM_HERE,
+      base::Bind(&PepperPlatformAudioOutputDev::CreateStreamOnIOThread, this,
+                 params_));
+
+  return true;
+}
+
+void PepperPlatformAudioOutputDev::RequestDeviceAuthorizationOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  DCHECK_EQ(state_, IDLE);
+
+  if (!ipc_)
+    return;
+
+  state_ = AUTHORIZING;
+  ipc_->RequestDeviceAuthorization(this, session_id_, device_id_,
+                                   security_origin_);
+
+  if (auth_timeout_ > base::TimeDelta()) {
+    // Create the timer on the thread it's used on. It's guaranteed to be
+    // deleted on the same thread since users must call ShutDown() before
+    // deleting PepperPlatformAudioOutputDev; see ShutDownOnIOThread().
+    auth_timeout_action_.reset(new base::OneShotTimer());
+    auth_timeout_action_->Start(
+        FROM_HERE, auth_timeout_,
+        base::Bind(&PepperPlatformAudioOutputDev::OnDeviceAuthorized, this,
+                   media::OUTPUT_DEVICE_STATUS_ERROR_TIMED_OUT,
+                   media::AudioParameters(), std::string()));
+  }
+}
+
+void PepperPlatformAudioOutputDev::CreateStreamOnIOThread(
+    const media::AudioParameters& params) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  switch (state_) {
+    case IPC_CLOSED:
+      main_task_runner_->PostTask(
+          FROM_HERE,
+          base::Bind(&PepperPlatformAudioOutputDev::NotifyStreamCreationFailed,
+                     this));
+      break;
+
+    case IDLE:
+      if (did_receive_auth_.IsSignaled() && device_id_.empty() &&
+          security_origin_.unique()) {
+        state_ = CREATING_STREAM;
+        ipc_->CreateStream(this, params);
+      } else {
+        RequestDeviceAuthorizationOnIOThread();
+        start_on_authorized_ = true;
+      }
+      break;
+
+    case AUTHORIZING:
+      start_on_authorized_ = true;
+      break;
+
+    case AUTHORIZED:
+      state_ = CREATING_STREAM;
+      ipc_->CreateStream(this, params);
+      start_on_authorized_ = false;
+      break;
+
+    case CREATING_STREAM:
+    case PAUSED:
+    case PLAYING:
+      NOTREACHED();
+      break;
+  }
+}
+
+void PepperPlatformAudioOutputDev::StartPlaybackOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (!ipc_)
+    return;
+
+  if (state_ == PAUSED) {
+    ipc_->PlayStream();
+    state_ = PLAYING;
+    play_on_start_ = false;
+  } else {
+    if (state_ < CREATING_STREAM)
+      CreateStreamOnIOThread(params_);
+
+    play_on_start_ = true;
+  }
+}
+
+void PepperPlatformAudioOutputDev::StopPlaybackOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (!ipc_)
+    return;
+
+  if (state_ == PLAYING) {
+    ipc_->PauseStream();
+    state_ = PAUSED;
+  }
+  play_on_start_ = false;
+}
+
+void PepperPlatformAudioOutputDev::SetVolumeOnIOThread(double volume) {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+  if (!ipc_)
+    return;
+
+  if (state_ >= CREATING_STREAM)
+    ipc_->SetVolume(volume);
+}
+
+void PepperPlatformAudioOutputDev::ShutDownOnIOThread() {
+  DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+  // Make sure we don't call shutdown more than once.
+  if (!ipc_)
+    return;
+
+  // Close the stream, if we haven't already.
+  if (state_ >= AUTHORIZING) {
+    ipc_->CloseStream();
+    ipc_.reset();
+    state_ = IDLE;
+  }
+  start_on_authorized_ = false;
+
+  // Destoy the timer on the thread it's used on.
+  auth_timeout_action_.reset();
+
+  // Release for the delegate, balances out the reference taken in
+  // PepperPlatformAudioOutputDev::Create.
+  Release();
+}
+
+void PepperPlatformAudioOutputDev::NotifyStreamCreationFailed() {
+  DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+  if (client_)
+    client_->StreamCreationFailed();
+}
+
+}  // namespace content
diff --git a/content/renderer/pepper/pepper_platform_audio_output_dev.h b/content/renderer/pepper/pepper_platform_audio_output_dev.h
new file mode 100644
index 0000000..50d4537a
--- /dev/null
+++ b/content/renderer/pepper/pepper_platform_audio_output_dev.h
@@ -0,0 +1,162 @@
+// Copyright (c) 2017 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 CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_AUDIO_OUTPUT_DEV_H_
+#define CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_AUDIO_OUTPUT_DEV_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "media/audio/audio_output_ipc.h"
+#include "media/base/audio_parameters.h"
+#include "media/base/output_device_info.h"
+
+namespace base {
+class OneShotTimer;
+class SingleThreadTaskRunner;
+}
+
+namespace {
+#if defined(OS_WIN) || defined(OS_MACOSX)
+const int64_t kMaxAuthorizationTimeoutMs = 4000;
+#else
+const int64_t kMaxAuthorizationTimeoutMs = 0;  // No timeout.
+#endif
+}
+
+namespace content {
+class PepperAudioOutputHost;
+
+// This class is used to support new PPAPI |PPB_AudioOutput_Dev|, while
+// |PepperPlatformAudioOutput| is to support old PPAPI |PPB_Audio|.
+class PepperPlatformAudioOutputDev
+    : public media::AudioOutputIPCDelegate,
+      public base::RefCountedThreadSafe<PepperPlatformAudioOutputDev> {
+ public:
+  // Factory function, returns NULL on failure. StreamCreated() will be called
+  // when the stream is created.
+  static PepperPlatformAudioOutputDev* Create(int render_frame_id,
+                                              const std::string& device_id,
+                                              const GURL& document_url,
+                                              int sample_rate,
+                                              int frames_per_buffer,
+                                              PepperAudioOutputHost* client);
+
+  // The following three methods are all called on main thread.
+
+  // Request authorization to use the device.
+  void RequestDeviceAuthorization();
+
+  // Starts the playback. Returns false on error or if called before the
+  // stream is created or after the stream is closed.
+  bool StartPlayback();
+
+  // Stops the playback. Returns false on error or if called before the stream
+  // is created or after the stream is closed.
+  bool StopPlayback();
+
+  // Sets the volume. Returns false on error or if called before the stream
+  // is created or after the stream is closed.
+  bool SetVolume(double volume);
+
+  // Closes the stream. Make sure to call this before the object is
+  // destructed.
+  void ShutDown();
+
+  // media::AudioOutputIPCDelegate implementation.
+  void OnError() override;
+  void OnDeviceAuthorized(media::OutputDeviceStatus device_status,
+                          const media::AudioParameters& output_params,
+                          const std::string& matched_device_id) override;
+  void OnStreamCreated(base::SharedMemoryHandle handle,
+                       base::SyncSocket::Handle socket_handle,
+                       int length) override;
+  void OnIPCClosed() override;
+
+ protected:
+  ~PepperPlatformAudioOutputDev() override;
+
+ private:
+  enum State {
+    IPC_CLOSED,       // No more IPCs can take place.
+    IDLE,             // Not started.
+    AUTHORIZING,      // Sent device authorization request, waiting for reply.
+    AUTHORIZED,       // Successful device authorization received.
+    CREATING_STREAM,  // Waiting for OnStreamCreated() to be called back.
+    PAUSED,   // Paused.  OnStreamCreated() has been called.  Can Play()/Stop().
+    PLAYING,  // Playing back.  Can Pause()/Stop().
+  };
+
+  friend class base::RefCountedThreadSafe<PepperPlatformAudioOutputDev>;
+
+  PepperPlatformAudioOutputDev();
+  PepperPlatformAudioOutputDev(int render_frame_id,
+                               const std::string& device_id,
+                               const GURL& document_url,
+                               base::TimeDelta authorization_timeout);
+
+  // Creates audio stream. Used for new Pepper audio output interface
+  // |PPB_AudioOutput_Dev|.
+  bool Initialize(int sample_rate,
+                  int frames_per_buffer,
+                  PepperAudioOutputHost* client);
+
+  void RequestDeviceAuthorizationOnIOThread();
+  void CreateStreamOnIOThread(const media::AudioParameters& params);
+  void StartPlaybackOnIOThread();
+  void StopPlaybackOnIOThread();
+  void SetVolumeOnIOThread(double volume);
+  void ShutDownOnIOThread();
+
+  void NotifyStreamCreationFailed();
+
+  PepperAudioOutputHost* client_;
+
+  // Used to send/receive IPC. THIS MUST ONLY BE ACCESSED ON THE
+  // I/O thread except to send messages and get the message loop.
+  std::unique_ptr<media::AudioOutputIPC> ipc_;
+
+  scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+  // The frame containing the Pepper widget.
+  int render_frame_id_;
+
+  // Initialized on the main thread and accessed on the I/O thread afterwards.
+  media::AudioParameters params_;
+
+  // Current state (must only be accessed from the IO thread).
+  State state_;
+
+  // State of Start() calls before OnDeviceAuthorized() is called.
+  bool start_on_authorized_;
+
+  // State of StartPlayback() calls before OnStreamCreated() is called.
+  bool play_on_start_;
+
+  // The media session ID used to identify which output device to be started.
+  int session_id_;
+
+  // ID of hardware output device to be used (provided session_id_ is zero)
+  const std::string device_id_;
+  const url::Origin security_origin_;
+
+  // If |device_id_| is empty and |session_id_| is not, |matched_device_id_| is
+  // received in OnDeviceAuthorized().
+  std::string matched_device_id_;
+
+  base::WaitableEvent did_receive_auth_;
+  media::AudioParameters output_params_;
+  media::OutputDeviceStatus device_status_;
+
+  const base::TimeDelta auth_timeout_;
+  std::unique_ptr<base::OneShotTimer> auth_timeout_action_;
+
+  DISALLOW_COPY_AND_ASSIGN(PepperPlatformAudioOutputDev);
+};
+
+}  // namespace content
+
+#endif  // CONTENT_RENDERER_PEPPER_PEPPER_PLATFORM_AUDIO_OUTPUT_DEV_H_
diff --git a/content/renderer/pepper/plugin_module.cc b/content/renderer/pepper/plugin_module.cc
index 7db5c1a8..839dc1b 100644
--- a/content/renderer/pepper/plugin_module.cc
+++ b/content/renderer/pepper/plugin_module.cc
@@ -35,6 +35,7 @@
 #include "content/renderer/pepper/renderer_ppapi_host_impl.h"
 #include "content/renderer/render_view_impl.h"
 #include "ppapi/c/dev/ppb_audio_input_dev.h"
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
 #include "ppapi/c/dev/ppb_buffer_dev.h"
 #include "ppapi/c/dev/ppb_char_set_dev.h"
 #include "ppapi/c/dev/ppb_crypto_dev.h"
diff --git a/content/renderer/pepper/resource_creation_impl.cc b/content/renderer/pepper/resource_creation_impl.cc
index 0101196..073a0b0 100644
--- a/content/renderer/pepper/resource_creation_impl.cc
+++ b/content/renderer/pepper/resource_creation_impl.cc
@@ -73,6 +73,10 @@
   return 0;  // Not supported in-process.
 }
 
+PP_Resource ResourceCreationImpl::CreateAudioOutput(PP_Instance instance) {
+  return 0;  // Not supported in-process.
+}
+
 PP_Resource ResourceCreationImpl::CreateCompositor(PP_Instance instance) {
   return 0;  // Not supported in-process.
 }
diff --git a/content/renderer/pepper/resource_creation_impl.h b/content/renderer/pepper/resource_creation_impl.h
index 5f48569..3e05ba3 100644
--- a/content/renderer/pepper/resource_creation_impl.h
+++ b/content/renderer/pepper/resource_creation_impl.h
@@ -39,6 +39,7 @@
                                 PP_AudioSampleRate sample_rate,
                                 uint32_t sample_frame_count) override;
   PP_Resource CreateAudioInput(PP_Instance instance) override;
+  PP_Resource CreateAudioOutput(PP_Instance instance) override;
   PP_Resource CreateCompositor(PP_Instance instance) override;
   PP_Resource CreateBroker(PP_Instance instance) override;
   PP_Resource CreateBuffer(PP_Instance instance, uint32_t size) override;
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 07a2abf..5de9361 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -56,7 +56,6 @@
 #include "content/child/blob_storage/blob_message_filter.h"
 #include "content/child/child_histogram_message_filter.h"
 #include "content/child/child_resource_message_filter.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/child/content_child_helpers.h"
 #include "content/child/db_message_filter.h"
 #include "content/child/indexed_db/indexed_db_dispatcher.h"
@@ -145,6 +144,7 @@
 #include "services/service_manager/public/cpp/connector.h"
 #include "services/service_manager/public/cpp/interface_provider.h"
 #include "services/service_manager/public/cpp/interface_registry.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 #include "services/ui/public/cpp/gpu/context_provider_command_buffer.h"
 #include "services/ui/public/interfaces/constants.mojom.h"
 #include "skia/ext/event_tracer_impl.h"
@@ -555,13 +555,6 @@
 }
 
 // static
-const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-RenderThreadImpl::current_thread_safe_render_message_filter() {
-  DCHECK(current());
-  return current()->thread_safe_render_message_filter();
-}
-
-// static
 void RenderThreadImpl::SetRenderMessageFilterForTesting(
     mojom::RenderMessageFilter* render_message_filter) {
   g_render_message_filter_for_testing = render_message_filter;
@@ -635,10 +628,13 @@
       IsRunningInMash() ? ui::mojom::kServiceName : mojom::kBrowserServiceName,
       GetIOTaskRunner());
 
-  channel()->GetThreadSafeRemoteAssociatedInterface(
-      &thread_safe_render_message_filter_);
-  shared_bitmap_manager_.reset(
-      new ChildSharedBitmapManager(thread_safe_render_message_filter_));
+  cc::mojom::SharedBitmapManagerAssociatedPtr shared_bitmap_manager_ptr;
+  render_message_filter()->GetSharedBitmapManager(
+      mojo::MakeRequest(&shared_bitmap_manager_ptr));
+  shared_bitmap_manager_.reset(new ui::ChildSharedBitmapManager(
+      cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr::Create(
+          shared_bitmap_manager_ptr.PassInterface(),
+          GetChannel()->ipc_task_runner_refptr())));
 
   InitializeWebKit(resource_task_queue);
 
@@ -2075,11 +2071,6 @@
   return render_message_filter_.get();
 }
 
-const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-RenderThreadImpl::thread_safe_render_message_filter() {
-  return thread_safe_render_message_filter_;
-}
-
 gpu::GpuChannelHost* RenderThreadImpl::GetGpuChannel() {
   if (!gpu_channel_)
     return nullptr;
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index ffecef1..83960ac 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -95,6 +95,7 @@
 }
 
 namespace ui {
+class ChildSharedBitmapManager;
 class ContextProviderCommandBuffer;
 class Gpu;
 }
@@ -113,7 +114,6 @@
 class BlobMessageFilter;
 class BrowserPluginManager;
 class CacheStorageDispatcher;
-class ChildSharedBitmapManager;
 class CompositorForwardingMessageFilter;
 class DBMessageFilter;
 class DevToolsAgentFilter;
@@ -170,8 +170,6 @@
       std::unique_ptr<blink::scheduler::RendererScheduler> renderer_scheduler);
   static RenderThreadImpl* current();
   static mojom::RenderMessageFilter* current_render_message_filter();
-  static const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-  current_thread_safe_render_message_filter();
 
   static void SetRenderMessageFilterForTesting(
       mojom::RenderMessageFilter* render_message_filter);
@@ -362,15 +360,13 @@
     return vc_manager_.get();
   }
 
-  ChildSharedBitmapManager* shared_bitmap_manager() const {
+  ui::ChildSharedBitmapManager* shared_bitmap_manager() const {
     DCHECK(shared_bitmap_manager_);
     return shared_bitmap_manager_.get();
   }
 
   mojom::RenderFrameMessageFilter* render_frame_message_filter();
   mojom::RenderMessageFilter* render_message_filter();
-  const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-  thread_safe_render_message_filter();
 
   // Get the GPU channel. Returns NULL if the channel is not established or
   // has been lost.
@@ -643,7 +639,7 @@
   // Used on the render thread.
   std::unique_ptr<VideoCaptureImplManager> vc_manager_;
 
-  std::unique_ptr<ChildSharedBitmapManager> shared_bitmap_manager_;
+  std::unique_ptr<ui::ChildSharedBitmapManager> shared_bitmap_manager_;
 
   // The count of RenderWidgets running through this thread.
   int widget_count_;
@@ -780,8 +776,6 @@
 
   mojom::RenderFrameMessageFilterAssociatedPtr render_frame_message_filter_;
   mojom::RenderMessageFilterAssociatedPtr render_message_filter_;
-  scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>
-      thread_safe_render_message_filter_;
 
   base::CancelableClosure record_purge_suspend_metric_closure_;
   RendererMemoryMetrics purge_and_suspend_memory_metrics_;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 3c12a24..7a26265 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -41,7 +41,6 @@
 #include "cc/paint/skia_paint_canvas.h"
 #include "content/child/appcache/appcache_dispatcher.h"
 #include "content/child/appcache/web_application_cache_host_impl.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/child/request_extra_data.h"
 #include "content/child/v8_value_converter_impl.h"
 #include "content/child/webmessageportchannel_impl.h"
@@ -109,6 +108,7 @@
 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
 #include "net/http/http_util.h"
 #include "ppapi/features/features.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 #include "skia/ext/platform_canvas.h"
 #include "third_party/WebKit/public/platform/FilePathConversion.h"
 #include "third_party/WebKit/public/platform/URLConversion.h"
diff --git a/content/renderer/renderer_blink_platform_impl.cc b/content/renderer/renderer_blink_platform_impl.cc
index 23379f46..839acd23 100644
--- a/content/renderer/renderer_blink_platform_impl.cc
+++ b/content/renderer/renderer_blink_platform_impl.cc
@@ -25,7 +25,6 @@
 #include "build/build_config.h"
 #include "components/url_formatter/url_formatter.h"
 #include "content/child/blob_storage/webblobregistry_impl.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/child/database_util.h"
 #include "content/child/file_info_util.h"
 #include "content/child/fileapi/webfilesystem_impl.h"
diff --git a/content/renderer/renderer_blink_platform_impl.h b/content/renderer/renderer_blink_platform_impl.h
index 0a46d4a..afce682 100644
--- a/content/renderer/renderer_blink_platform_impl.h
+++ b/content/renderer/renderer_blink_platform_impl.h
@@ -16,12 +16,12 @@
 #include "build/build_config.h"
 #include "cc/blink/web_compositor_support_impl.h"
 #include "content/child/blink_platform_impl.h"
-#include "content/child/child_shared_bitmap_manager.h"
 #include "content/common/content_export.h"
 #include "content/common/url_loader_factory.mojom.h"
 #include "content/renderer/origin_trials/web_trial_token_validator_impl.h"
 #include "content/renderer/top_level_blame_context.h"
 #include "content/renderer/webpublicsuffixlist_impl.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBFactory.h"
 #include "third_party/WebKit/public/platform/modules/screen_orientation/WebScreenOrientationType.h"
 
@@ -283,7 +283,7 @@
   scoped_refptr<IPC::SyncMessageFilter> sync_message_filter_;
   scoped_refptr<ThreadSafeSender> thread_safe_sender_;
   scoped_refptr<QuotaMessageFilter> quota_message_filter_;
-  ChildSharedBitmapManager* shared_bitmap_manager_;
+  ui::ChildSharedBitmapManager* shared_bitmap_manager_;
 
   std::unique_ptr<WebDatabaseObserverImpl> web_database_observer_impl_;
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 03c74fa..3c8b08c 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1359,7 +1359,6 @@
     "../common/dom_storage/dom_storage_map_unittest.cc",
     "../common/feature_policy/feature_policy_unittest.cc",
     "../common/fileapi/file_system_util_unittest.cc",
-    "../common/host_shared_bitmap_manager_unittest.cc",
     "../common/indexed_db/indexed_db_key_unittest.cc",
     "../common/input/event_with_latency_info_unittest.cc",
     "../common/input/gesture_event_stream_validator_unittest.cc",
diff --git a/content/test/gpu/gpu_tests/pixel_expectations.py b/content/test/gpu/gpu_tests/pixel_expectations.py
index aa3ae6ed..929e48f 100644
--- a/content/test/gpu/gpu_tests/pixel_expectations.py
+++ b/content/test/gpu/gpu_tests/pixel_expectations.py
@@ -28,12 +28,6 @@
     self.Fail('Pixel_ScissorTestWithPreserveDrawingBuffer',
         ['android'], bug=521588)
 
-    # TODO(junov) needs new baseline
-    self.Fail('Pixel_OffscreenCanvasTransferToImageBitmap',
-        bug=585607)
-    self.Fail('Pixel_OffscreenCanvasTransferToImageBitmapWorker',
-        bug=585607)
-
     # TODO(ccameron) fix these on Mac Retina
     self.Fail('Pixel_CSS3DBlueBox', ['mac'], bug=533690)
 
@@ -61,6 +55,9 @@
     # TODO(jbauman): Times out on Pixel C.
     self.Fail('Pixel_Video_VP9', ['android', 'nvidia'], bug=704389)
 
+    # TODO(jbauman): Re-enable when references images created.
+    self.Fail('Pixel_DirectComposition_Video_*', ['win'], bug=704389)
+
     # TODO(xlai): Remove this after test dimension is shrunk
     # Intended to skip only Nexus 5
     self.Skip('Pixel_OffscreenCanvasAccelerated2D',
diff --git a/content/test/gpu/gpu_tests/pixel_integration_test.py b/content/test/gpu/gpu_tests/pixel_integration_test.py
index 8690d69..85e31be 100644
--- a/content/test/gpu/gpu_tests/pixel_integration_test.py
+++ b/content/test/gpu/gpu_tests/pixel_integration_test.py
@@ -122,6 +122,8 @@
     pages += pixel_test_pages.ExperimentalCanvasFeaturesPages(name)
     if sys.platform.startswith('darwin'):
       pages += pixel_test_pages.MacSpecificPages(name)
+    if sys.platform.startswith('win'):
+      pages += pixel_test_pages.DirectCompositionPages(name)
     for p in pages:
       yield(p.name, gpu_relative_path + p.url, (p))
 
diff --git a/content/test/gpu/gpu_tests/pixel_test_pages.py b/content/test/gpu/gpu_tests/pixel_test_pages.py
index 4046f2b..acc40096 100644
--- a/content/test/gpu/gpu_tests/pixel_test_pages.py
+++ b/content/test/gpu/gpu_tests/pixel_test_pages.py
@@ -487,3 +487,21 @@
       tolerance=10,
       browser_args=['--disable-mac-overlays']),
   ]
+
+def DirectCompositionPages(base_name):
+  browser_args = ['--enable-direct-composition-layers']
+  return [
+    PixelTestPage(
+      'pixel_video_mp4.html',
+      base_name + '_DirectComposition_Video_MP4',
+      test_rect=[0, 0, 300, 300],
+      revision=1,
+      browser_args=browser_args),
+
+    PixelTestPage(
+      'pixel_video_vp9.html',
+      base_name + '_DirectComposition_Video_VP9',
+      test_rect=[0, 0, 300, 300],
+      revision=1,
+      browser_args=browser_args),
+  ]
diff --git a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
index 15e6564..5426567b 100644
--- a/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl2_conformance_expectations.py
@@ -44,6 +44,10 @@
     self.Flaky('conformance2/query/occlusion-query.html', bug=603168)
     self.Fail('conformance2/glsl3/tricky-loop-conditions.html', bug=483282)
 
+    # Temporary suppression; will be removed after bug fix.
+    self.Fail('conformance/textures/misc/texture-corner-case-videos.html',
+              bug=701060)
+
     self.Fail('conformance2/rendering/depth-stencil-feedback-loop.html',
         bug=660844) # WebGL 2.0.1
     self.Fail('conformance2/rendering/rendering-sampling-feedback-loop.html',
@@ -203,6 +207,10 @@
 
     # Mac only.
 
+    # Fails on all GPU types.
+    self.Fail('conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html',
+              ['mac'], bug=709351)
+
     self.Fail('conformance2/rendering/' +
         'framebuffer-completeness-unaffected.html',
         ['mac'], bug=630800)
@@ -694,9 +702,14 @@
         ['linux', 'nvidia'], bug=618447)
     self.Fail('conformance/glsl/bugs/unary-minus-operator-float-bug.html',
         ['linux', 'nvidia'], bug=672380)
+    self.Fail('conformance2/glsl3/vector-dynamic-indexing-swizzled-lvalue.html',
+        ['linux', 'nvidia'], bug=709351)
     self.Fail('conformance2/textures/canvas_sub_rectangle/' +
         'tex-2d-r11f_g11f_b10f-rgb-half_float.html',
         ['linux', 'nvidia'], bug=694359)
+    self.Fail('conformance2/textures/canvas_sub_rectangle/' +
+        'tex-2d-rgb16f-rgb-half_float.html',
+        ['linux', 'nvidia'], bug=694359)
     self.Fail('conformance2/textures/image_bitmap_from_canvas/' +
         'tex-3d-srgb8_alpha8-rgba-unsigned_byte.html',
         ['linux', 'nvidia'], bug=679677)
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
index 0463383..0d9b0871 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
+++ b/content/test/gpu/gpu_tests/webgl_conformance_expectations.py
@@ -101,6 +101,10 @@
     self.Fail('conformance/textures/misc/tex-sub-image-2d-bad-args.html',
         bug=570453)
 
+    # Temporary suppression; will be removed after bug fix.
+    self.Fail('conformance/textures/misc/texture-corner-case-videos.html',
+              bug=701060)
+
     # Passthrough command decoder
     self.Fail('conformance/extensions/ext-sRGB.html',
         ['passthrough'], bug=679696)
@@ -235,6 +239,10 @@
     self.Fail('conformance/uniforms/uniform-samplers-test.html',
         ['passthrough', 'd3d11'], bug=1639) # angle bug ID
 
+    # Win / AMD / Passthrough command decoder / D3D11
+    self.Flaky('conformance/textures/misc/copytexsubimage2d-subrects.html',
+        ['win', 'amd', 'passthrough', 'd3d11'], bug=685232)
+
     # Win failures
     # Note that the following test seems to pass, but it may still be flaky.
     self.Fail('conformance/glsl/constructors/' +
diff --git a/content/test/gpu/gpu_tests/webgl_conformance_revision.txt b/content/test/gpu/gpu_tests/webgl_conformance_revision.txt
index de866c1c..b820de1 100644
--- a/content/test/gpu/gpu_tests/webgl_conformance_revision.txt
+++ b/content/test/gpu/gpu_tests/webgl_conformance_revision.txt
@@ -1,3 +1,3 @@
 # AUTOGENERATED FILE - DO NOT EDIT
 # SEE roll_webgl_conformance.py
-Current webgl revision c02db7653f0a0af14fd3303044279b8e4234d80b
+Current webgl revision 32cfddc9e452c93bcbe443d6ddcc0c18ac556501
diff --git a/device/bluetooth/bluetooth_device_unittest.cc b/device/bluetooth/bluetooth_device_unittest.cc
index 7dd31ef8..b19d78e 100644
--- a/device/bluetooth/bluetooth_device_unittest.cc
+++ b/device/bluetooth/bluetooth_device_unittest.cc
@@ -1276,10 +1276,9 @@
   EXPECT_TRUE(device->IsConnected());
 
   // Discover services
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
   EXPECT_EQ(2u, device->GetGattServices().size());
@@ -1301,9 +1300,8 @@
   EXPECT_TRUE(device->IsConnected());
 
   // Verify that service discovery can be done again
-  std::vector<std::string> services2;
-  services2.push_back("00000002-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services2);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAttribute}));
   base::RunLoop().RunUntilIdle();
   EXPECT_TRUE(device->IsGattServicesDiscoveryComplete());
   EXPECT_EQ(1u, device->GetGattServices().size());
@@ -1361,10 +1359,9 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, gatt_discovery_attempts_);
 
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(1, observer.gatt_services_discovered_count());
@@ -1396,10 +1393,9 @@
   RememberDeviceForSubsequentAction(device);
   DeleteDevice(device);
 
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(nullptr /* use remembered device */, services);
+  SimulateGattServicesDiscovered(
+      nullptr /* use remembered device */,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
 }
 #endif  // defined(OS_ANDROID)
@@ -1454,10 +1450,9 @@
   SimulateGattDisconnection(device);
   base::RunLoop().RunUntilIdle();
 
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
 
   EXPECT_FALSE(device->IsGattServicesDiscoveryComplete());
@@ -1509,12 +1504,11 @@
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1, gatt_discovery_attempts_);
 
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
   // 2 duplicate UUIDs creating 2 instances.
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  services.push_back("00000001-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>(
+          {kTestUUIDGenericAccess, kTestUUIDHeartRate, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(3u, device->GetGattServices().size());
 
diff --git a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
index 0e779e7..89a9a73 100644
--- a/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_characteristic_unittest.cc
@@ -38,15 +38,13 @@
     SimulateGattConnection(device_);
     base::RunLoop().RunUntilIdle();
 
-    std::vector<std::string> services;
-    std::string uuid("00000000-0000-1000-8000-00805f9b34fb");
-    services.push_back(uuid);
-    SimulateGattServicesDiscovered(device_, services);
+    SimulateGattServicesDiscovered(
+        device_, std::vector<std::string>({kTestUUIDGenericAccess}));
     base::RunLoop().RunUntilIdle();
     ASSERT_EQ(1u, device_->GetGattServices().size());
     service_ = device_->GetGattServices()[0];
-    SimulateGattCharacteristic(service_, uuid, properties);
-    SimulateGattCharacteristic(service_, uuid, properties);
+    SimulateGattCharacteristic(service_, kTestUUIDDeviceName, properties);
+    SimulateGattCharacteristic(service_, kTestUUIDDeviceName, properties);
     ASSERT_EQ(2u, service_->GetCharacteristics().size());
     characteristic1_ = service_->GetCharacteristics()[0];
     characteristic2_ = service_->GetCharacteristics()[1];
@@ -159,24 +157,23 @@
   // 3 services (all with same UUID).
   //   1 on the first device (to test characteristic instances across devices).
   //   2 on the second device (to test same device, multiple service instances).
-  std::vector<std::string> services;
-  std::string uuid = "00000000-0000-1000-8000-00805f9b34fb";
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device1, services);
+  SimulateGattServicesDiscovered(
+      device1, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device2, services);
+  SimulateGattServicesDiscovered(
+      device2, std::vector<std::string>(
+                   {kTestUUIDGenericAccess, kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service1 = device1->GetGattServices()[0];
   BluetoothRemoteGattService* service2 = device2->GetGattServices()[0];
   BluetoothRemoteGattService* service3 = device2->GetGattServices()[1];
   // 6 characteristics (same UUID), 2 on each service.
-  SimulateGattCharacteristic(service1, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service1, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service2, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service2, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service3, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service3, uuid, /* properties */ 0);
+  SimulateGattCharacteristic(service1, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service1, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
   BluetoothRemoteGattCharacteristic* char1 = service1->GetCharacteristics()[0];
   BluetoothRemoteGattCharacteristic* char2 = service1->GetCharacteristics()[1];
   BluetoothRemoteGattCharacteristic* char3 = service2->GetCharacteristics()[0];
@@ -220,20 +217,17 @@
                                GetConnectErrorCallback(Call::NOT_EXPECTED));
   SimulateGattConnection(device);
   base::RunLoop().RunUntilIdle();
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
 
   // Create 3 characteristics. Two of them are duplicates.
-  std::string uuid_str1("11111111-0000-1000-8000-00805f9b34fb");
-  std::string uuid_str2("22222222-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID uuid1(uuid_str1);
-  BluetoothUUID uuid2(uuid_str2);
-  SimulateGattCharacteristic(service, uuid_str1, /* properties */ 0);
-  SimulateGattCharacteristic(service, uuid_str2, /* properties */ 0);
-  SimulateGattCharacteristic(service, uuid_str2, /* properties */ 0);
+  BluetoothUUID uuid1(kTestUUIDDeviceName);
+  BluetoothUUID uuid2(kTestUUIDAppearance);
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDAppearance, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDAppearance, /* properties */ 0);
   BluetoothRemoteGattCharacteristic* char1 = service->GetCharacteristics()[0];
   BluetoothRemoteGattCharacteristic* char2 = service->GetCharacteristics()[1];
   BluetoothRemoteGattCharacteristic* char3 = service->GetCharacteristics()[2];
@@ -264,16 +258,14 @@
                                GetConnectErrorCallback(Call::NOT_EXPECTED));
   SimulateGattConnection(device);
   base::RunLoop().RunUntilIdle();
-  std::vector<std::string> services;
-  std::string uuid("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
 
   // Create two characteristics with different properties:
-  SimulateGattCharacteristic(service, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service, uuid, /* properties */ 7);
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 7);
 
   // Read the properties. Because ordering is unknown swap as necessary.
   int properties1 = service->GetCharacteristics()[0]->GetProperties();
@@ -2208,10 +2200,10 @@
   ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
 
   // Add several Descriptors:
-  BluetoothUUID uuid1("11111111-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID uuid2("22222222-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID uuid3("33333333-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID uuid4("44444444-0000-1000-8000-00805f9b34fb");
+  BluetoothUUID uuid1(kTestUUIDCharacteristicUserDescription);
+  BluetoothUUID uuid2(kTestUUIDClientCharacteristicConfiguration);
+  BluetoothUUID uuid3(kTestUUIDServerCharacteristicConfiguration);
+  BluetoothUUID uuid4(kTestUUIDCharacteristicPresentationFormat);
   SimulateGattDescriptor(characteristic1_, uuid1.canonical_value());
   SimulateGattDescriptor(characteristic1_, uuid2.canonical_value());
   SimulateGattDescriptor(characteristic2_, uuid3.canonical_value());
@@ -2253,9 +2245,9 @@
   ASSERT_NO_FATAL_FAILURE(FakeCharacteristicBoilerplate());
 
   // Add several Descriptors:
-  BluetoothUUID id1("11111111-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID id2("22222222-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID id3("33333333-0000-1000-8000-00805f9b34fb");
+  BluetoothUUID id1(kTestUUIDCharacteristicUserDescription);
+  BluetoothUUID id2(kTestUUIDClientCharacteristicConfiguration);
+  BluetoothUUID id3(kTestUUIDServerCharacteristicConfiguration);
   SimulateGattDescriptor(characteristic1_, id1.canonical_value());
   SimulateGattDescriptor(characteristic1_, id2.canonical_value());
   SimulateGattDescriptor(characteristic2_, id3.canonical_value());
diff --git a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
index 1152797d..3b886bb 100644
--- a/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_descriptor_unittest.cc
@@ -28,20 +28,18 @@
                                   GetConnectErrorCallback(Call::NOT_EXPECTED));
     SimulateGattConnection(device_);
     base::RunLoop().RunUntilIdle();
-    std::vector<std::string> services;
-    std::string uuid("00000000-0000-1000-8000-00805f9b34fb");
-    services.push_back(uuid);
-    SimulateGattServicesDiscovered(device_, services);
+    SimulateGattServicesDiscovered(
+        device_, std::vector<std::string>({kTestUUIDGenericAccess}));
     base::RunLoop().RunUntilIdle();
     ASSERT_EQ(1u, device_->GetGattServices().size());
     service_ = device_->GetGattServices()[0];
-    SimulateGattCharacteristic(service_, uuid, 0);
+    SimulateGattCharacteristic(service_, kTestUUIDDeviceName, 0);
     ASSERT_EQ(1u, service_->GetCharacteristics().size());
     characteristic_ = service_->GetCharacteristics()[0];
     SimulateGattDescriptor(characteristic_,
-                           "00000001-0000-1000-8000-00805f9b34fb");
+                           kTestUUIDCharacteristicUserDescription);
     SimulateGattDescriptor(characteristic_,
-                           "00000002-0000-1000-8000-00805f9b34fb");
+                           kTestUUIDClientCharacteristicConfiguration);
     ASSERT_EQ(2u, characteristic_->GetDescriptors().size());
     descriptor1_ = characteristic_->GetDescriptors()[0];
     descriptor2_ = characteristic_->GetDescriptors()[1];
@@ -78,24 +76,23 @@
   // 3 services (all with same UUID).
   //   1 on the first device (to test characteristic instances across devices).
   //   2 on the second device (to test same device, multiple service instances).
-  std::vector<std::string> services;
-  std::string uuid = "00000000-0000-1000-8000-00805f9b34fb";
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device1, services);
+  SimulateGattServicesDiscovered(
+      device1, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device2, services);
+  SimulateGattServicesDiscovered(
+      device2, std::vector<std::string>(
+                   {kTestUUIDGenericAccess, kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service1 = device1->GetGattServices()[0];
   BluetoothRemoteGattService* service2 = device2->GetGattServices()[0];
   BluetoothRemoteGattService* service3 = device2->GetGattServices()[1];
   // 6 characteristics (same UUID), 2 on each service.
-  SimulateGattCharacteristic(service1, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service1, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service2, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service2, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service3, uuid, /* properties */ 0);
-  SimulateGattCharacteristic(service3, uuid, /* properties */ 0);
+  SimulateGattCharacteristic(service1, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service1, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service2, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service3, kTestUUIDDeviceName, /* properties */ 0);
   BluetoothRemoteGattCharacteristic* char1 = service1->GetCharacteristics()[0];
   BluetoothRemoteGattCharacteristic* char2 = service1->GetCharacteristics()[1];
   BluetoothRemoteGattCharacteristic* char3 = service2->GetCharacteristics()[0];
@@ -105,12 +102,12 @@
   // 6 descriptors (same UUID), 1 on each characteristic
   // TODO(576900) Test multiple descriptors with same UUID on one
   // characteristic.
-  SimulateGattDescriptor(char1, uuid);
-  SimulateGattDescriptor(char2, uuid);
-  SimulateGattDescriptor(char3, uuid);
-  SimulateGattDescriptor(char4, uuid);
-  SimulateGattDescriptor(char5, uuid);
-  SimulateGattDescriptor(char6, uuid);
+  SimulateGattDescriptor(char1, kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(char2, kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(char3, kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(char4, kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(char5, kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(char6, kTestUUIDCharacteristicUserDescription);
   BluetoothRemoteGattDescriptor* desc1 = char1->GetDescriptors()[0];
   BluetoothRemoteGattDescriptor* desc2 = char2->GetDescriptors()[0];
   BluetoothRemoteGattDescriptor* desc3 = char3->GetDescriptors()[0];
@@ -153,26 +150,25 @@
   device->CreateGattConnection(GetGattConnectionCallback(Call::EXPECTED),
                                GetConnectErrorCallback(Call::NOT_EXPECTED));
   SimulateGattConnection(device);
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   ASSERT_EQ(1u, device->GetGattServices().size());
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
 
-  SimulateGattCharacteristic(service, "00000000-0000-1000-8000-00805f9b34fb",
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName,
                              /* properties */ 0);
   ASSERT_EQ(1u, service->GetCharacteristics().size());
   BluetoothRemoteGattCharacteristic* characteristic =
       service->GetCharacteristics()[0];
 
   // Create 2 descriptors.
-  std::string uuid_str1("11111111-0000-1000-8000-00805f9b34fb");
-  std::string uuid_str2("22222222-0000-1000-8000-00805f9b34fb");
-  BluetoothUUID uuid1(uuid_str1);
-  BluetoothUUID uuid2(uuid_str2);
-  SimulateGattDescriptor(characteristic, uuid_str1);
-  SimulateGattDescriptor(characteristic, uuid_str2);
+  BluetoothUUID uuid1(kTestUUIDCharacteristicUserDescription);
+  BluetoothUUID uuid2(kTestUUIDClientCharacteristicConfiguration);
+  SimulateGattDescriptor(characteristic,
+                         kTestUUIDCharacteristicUserDescription);
+  SimulateGattDescriptor(characteristic,
+                         kTestUUIDClientCharacteristicConfiguration);
   ASSERT_EQ(2u, characteristic->GetDescriptors().size());
   BluetoothRemoteGattDescriptor* descriptor1 =
       characteristic->GetDescriptors()[0];
diff --git a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
index c5118645..2c86e62 100644
--- a/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
+++ b/device/bluetooth/bluetooth_remote_gatt_service_unittest.cc
@@ -43,12 +43,12 @@
   base::RunLoop().RunUntilIdle();
 
   // 2 duplicate UUIDs creating 2 service instances on each device.
-  std::vector<std::string> services;
-  std::string uuid = "00000000-0000-1000-8000-00805f9b34fb";
-  services.push_back(uuid);
-  services.push_back(uuid);
-  SimulateGattServicesDiscovered(device1, services);
-  SimulateGattServicesDiscovered(device2, services);
+  SimulateGattServicesDiscovered(
+      device1, std::vector<std::string>(
+                   {kTestUUIDGenericAccess, kTestUUIDGenericAccess}));
+  SimulateGattServicesDiscovered(
+      device2, std::vector<std::string>(
+                   {kTestUUIDGenericAccess, kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service1 = device1->GetGattServices()[0];
   BluetoothRemoteGattService* service2 = device1->GetGattServices()[1];
@@ -82,7 +82,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Create multiple instances with the same UUID.
-  BluetoothUUID uuid("00000000-0000-1000-8000-00805f9b34fb");
+  BluetoothUUID uuid(kTestUUIDGenericAccess);
   std::vector<std::string> services;
   services.push_back(uuid.canonical_value());
   services.push_back(uuid.canonical_value());
@@ -110,9 +110,8 @@
   base::RunLoop().RunUntilIdle();
 
   // Simulate a service, with no Characteristics:
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
 
@@ -136,19 +135,18 @@
   base::RunLoop().RunUntilIdle();
 
   // Simulate a service, with several Characteristics:
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
-  std::string characteristic_uuid1 = "11111111-0000-1000-8000-00805f9b34fb";
-  std::string characteristic_uuid2 = "22222222-0000-1000-8000-00805f9b34fb";
-  std::string characteristic_uuid3 = characteristic_uuid2;  // Duplicate UUID.
-  std::string characteristic_uuid4 = "33333333-0000-1000-8000-00805f9b34fb";
-  SimulateGattCharacteristic(service, characteristic_uuid1, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid2, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid3, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid4, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDAppearance,
+                             /* properties */ 0);
+  // Duplicate UUID.
+  SimulateGattCharacteristic(service, kTestUUIDAppearance,
+                             /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDReconnectionAddress,
+                             /* properties */ 0);
 
   // Verify that GetCharacteristic can retrieve characteristics again by ID,
   // and that the same Characteristics come back.
@@ -188,38 +186,36 @@
   base::RunLoop().RunUntilIdle();
 
   // Simulate two primary GATT services.
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("01010101-0101-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service1 = device->GetGattServices()[0];
   BluetoothRemoteGattService* service2 = device->GetGattServices()[1];
-  std::string characteristic_uuid1 = "11111111-0000-1000-8000-00805f9b34fb";
-  std::string characteristic_uuid2 = "22222222-0000-1000-8000-00805f9b34fb";
-  SimulateGattCharacteristic(service1, characteristic_uuid1,
+  SimulateGattCharacteristic(service1, kTestUUIDDeviceName,
                              /* properties */ 0);
   // 2 duplicate UUIDs creating 2 instances.
-  SimulateGattCharacteristic(service2, characteristic_uuid2,
+  SimulateGattCharacteristic(service2, kTestUUIDHeartRateMeasurement,
                              /* properties */ 0);
-  SimulateGattCharacteristic(service2, characteristic_uuid2,
+  SimulateGattCharacteristic(service2, kTestUUIDHeartRateMeasurement,
                              /* properties */ 0);
 
   {
     std::vector<BluetoothRemoteGattCharacteristic*> characteristics =
-        service1->GetCharacteristicsByUUID(BluetoothUUID(characteristic_uuid1));
+        service1->GetCharacteristicsByUUID(BluetoothUUID(kTestUUIDDeviceName));
     EXPECT_EQ(1u, characteristics.size());
-    EXPECT_EQ(characteristic_uuid1,
+    EXPECT_EQ(kTestUUIDDeviceName,
               characteristics[0]->GetUUID().canonical_value());
   }
 
   {
     std::vector<BluetoothRemoteGattCharacteristic*> characteristics =
-        service2->GetCharacteristicsByUUID(BluetoothUUID(characteristic_uuid2));
+        service2->GetCharacteristicsByUUID(
+            BluetoothUUID(kTestUUIDHeartRateMeasurement));
     EXPECT_EQ(2u, characteristics.size());
-    EXPECT_EQ(characteristic_uuid2,
+    EXPECT_EQ(kTestUUIDHeartRateMeasurement,
               characteristics[0]->GetUUID().canonical_value());
-    EXPECT_EQ(characteristic_uuid2,
+    EXPECT_EQ(kTestUUIDHeartRateMeasurement,
               characteristics[1]->GetUUID().canonical_value());
     EXPECT_NE(characteristics[0]->GetIdentifier(),
               characteristics[1]->GetIdentifier());
@@ -253,19 +249,18 @@
   TestBluetoothAdapterObserver observer(adapter_);
 
   // Simulate a service, with several Characteristics:
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device, std::vector<std::string>({kTestUUIDGenericAccess}));
   base::RunLoop().RunUntilIdle();
   BluetoothRemoteGattService* service = device->GetGattServices()[0];
-  std::string characteristic_uuid1 = "11111111-0000-1000-8000-00805f9b34fb";
-  std::string characteristic_uuid2 = "22222222-0000-1000-8000-00805f9b34fb";
-  std::string characteristic_uuid3 = characteristic_uuid2;  // Duplicate UUID.
-  std::string characteristic_uuid4 = "33333333-0000-1000-8000-00805f9b34fb";
-  SimulateGattCharacteristic(service, characteristic_uuid1, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid2, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid3, /* properties */ 0);
-  SimulateGattCharacteristic(service, characteristic_uuid4, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDDeviceName, /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDAppearance,
+                             /* properties */ 0);
+  // Duplicate UUID.
+  SimulateGattCharacteristic(service, kTestUUIDAppearance,
+                             /* properties */ 0);
+  SimulateGattCharacteristic(service, kTestUUIDReconnectionAddress,
+                             /* properties */ 0);
 #if !defined(OS_WIN)
   // TODO(620895) GattCharacteristicAdded has to be implemented for Windows.
   EXPECT_EQ(4, observer.gatt_characteristic_added_count());
@@ -326,10 +321,9 @@
   TestBluetoothAdapterObserver observer(adapter_);
 
   // Simulate two primary GATT services.
-  std::vector<std::string> services;
-  services.push_back("00000000-0000-1000-8000-00805f9b34fb");
-  services.push_back("01010101-0101-1000-8000-00805f9b34fb");
-  SimulateGattServicesDiscovered(device, services);
+  SimulateGattServicesDiscovered(
+      device,
+      std::vector<std::string>({kTestUUIDGenericAccess, kTestUUIDHeartRate}));
   EXPECT_EQ(2u, device->GetGattServices().size());
 
   // Simulate remove of a primary service.
diff --git a/device/bluetooth/test/bluetooth_test.cc b/device/bluetooth/test/bluetooth_test.cc
index 234b0486..82d2146 100644
--- a/device/bluetooth/test/bluetooth_test.cc
+++ b/device/bluetooth/test/bluetooth_test.cc
@@ -25,6 +25,7 @@
 const std::string BluetoothTestBase::kTestDeviceAddress2 = "02:00:00:8B:74:63";
 const std::string BluetoothTestBase::kTestDeviceAddress3 = "03:00:00:17:C0:57";
 
+// Service UUIDs
 const std::string BluetoothTestBase::kTestUUIDGenericAccess =
     "00001800-0000-1000-8000-00805f9b34fb";
 const std::string BluetoothTestBase::kTestUUIDGenericAttribute =
@@ -35,6 +36,26 @@
     "00001803-0000-1000-8000-00805f9b34fb";
 const std::string BluetoothTestBase::kTestUUIDHeartRate =
     "0000180d-0000-1000-8000-00805f9b34fb";
+// Characteristic UUIDs
+const std::string BluetoothTestBase::kTestUUIDDeviceName =
+    "00002a00-0000-1000-8000-00805f9b34fb";
+const std::string BluetoothTestBase::kTestUUIDAppearance =
+    "00002a01-0000-1000-8000-00805f9b34fb";
+const std::string BluetoothTestBase::kTestUUIDReconnectionAddress =
+    "00002a03-0000-1000-8000-00805f9b34fb";
+const std::string BluetoothTestBase::kTestUUIDHeartRateMeasurement =
+    "00002a37-0000-1000-8000-00805f9b34fb";
+// Descriptor UUIDs
+const std::string BluetoothTestBase::kTestUUIDCharacteristicUserDescription =
+    "00002901-0000-1000-8000-00805f9b34fb";
+const std::string
+    BluetoothTestBase::kTestUUIDClientCharacteristicConfiguration =
+        "00002902-0000-1000-8000-00805f9b34fb";
+const std::string
+    BluetoothTestBase::kTestUUIDServerCharacteristicConfiguration =
+        "00002903-0000-1000-8000-00805f9b34fb";
+const std::string BluetoothTestBase::kTestUUIDCharacteristicPresentationFormat =
+    "00002904-0000-1000-8000-00805f9b34fb";
 
 BluetoothTestBase::BluetoothTestBase() : weak_factory_(this) {}
 
diff --git a/device/bluetooth/test/bluetooth_test.h b/device/bluetooth/test/bluetooth_test.h
index b6610ea9..8b99e13 100644
--- a/device/bluetooth/test/bluetooth_test.h
+++ b/device/bluetooth/test/bluetooth_test.h
@@ -87,11 +87,24 @@
     LOWER = -20,
   };
 
+  // Services
   static const std::string kTestUUIDGenericAccess;
   static const std::string kTestUUIDGenericAttribute;
   static const std::string kTestUUIDImmediateAlert;
   static const std::string kTestUUIDLinkLoss;
   static const std::string kTestUUIDHeartRate;
+  // Characteristics
+  // The following three characteristics are for kTestUUIDGenericAccess.
+  static const std::string kTestUUIDDeviceName;
+  static const std::string kTestUUIDAppearance;
+  static const std::string kTestUUIDReconnectionAddress;
+  // This characteristic is for kTestUUIDHeartRate.
+  static const std::string kTestUUIDHeartRateMeasurement;
+  // Descriptors
+  static const std::string kTestUUIDCharacteristicUserDescription;
+  static const std::string kTestUUIDClientCharacteristicConfiguration;
+  static const std::string kTestUUIDServerCharacteristicConfiguration;
+  static const std::string kTestUUIDCharacteristicPresentationFormat;
 
   BluetoothTestBase();
   ~BluetoothTestBase() override;
diff --git a/extensions/browser/content_hash_fetcher.cc b/extensions/browser/content_hash_fetcher.cc
index 2c5f270..4ed26faf 100644
--- a/extensions/browser/content_hash_fetcher.cc
+++ b/extensions/browser/content_hash_fetcher.cc
@@ -18,8 +18,7 @@
 #include "base/memory/ref_counted.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/synchronization/lock.h"
-#include "base/task_runner_util.h"
-#include "base/threading/sequenced_worker_pool.h"
+#include "base/task_scheduler/post_task.h"
 #include "base/timer/elapsed_timer.h"
 #include "base/version.h"
 #include "content/public/browser/browser_thread.h"
@@ -197,11 +196,10 @@
 void ContentHashFetcherJob::Start() {
   base::FilePath verified_contents_path =
       file_util::GetVerifiedContentsPath(extension_path_);
-  base::PostTaskAndReplyWithResult(
-      content::BrowserThread::GetBlockingPool(),
-      FROM_HERE,
-      base::Bind(&ContentHashFetcherJob::LoadVerifiedContents,
-                 this,
+  base::PostTaskWithTraitsAndReplyWithResult(
+      FROM_HERE, base::TaskTraits().MayBlock().WithPriority(
+                     base::TaskPriority::USER_VISIBLE),
+      base::Bind(&ContentHashFetcherJob::LoadVerifiedContents, this,
                  verified_contents_path),
       base::Bind(&ContentHashFetcherJob::DoneCheckingForVerifiedContents,
                  this));
@@ -314,12 +312,12 @@
     base::FilePath destination =
         file_util::GetVerifiedContentsPath(extension_path_);
     size_t size = response->size();
-    base::PostTaskAndReplyWithResult(
-        content::BrowserThread::GetBlockingPool(),
-        FROM_HERE,
+    base::PostTaskWithTraitsAndReplyWithResult(
+        FROM_HERE, base::TaskTraits().MayBlock().WithPriority(
+                       base::TaskPriority::USER_VISIBLE),
         base::Bind(&WriteFileHelper, destination, base::Passed(&response)),
-        base::Bind(
-            &ContentHashFetcherJob::OnVerifiedContentsWritten, this, size));
+        base::Bind(&ContentHashFetcherJob::OnVerifiedContentsWritten, this,
+                   size));
   } else {
     DoneFetchingVerifiedContents(false);
   }
diff --git a/extensions/browser/content_hash_fetcher_unittest.cc b/extensions/browser/content_hash_fetcher_unittest.cc
index 2d01fb1..e54b4d6 100644
--- a/extensions/browser/content_hash_fetcher_unittest.cc
+++ b/extensions/browser/content_hash_fetcher_unittest.cc
@@ -186,9 +186,15 @@
   base::ScopedTempDir temp_dir_;
 };
 
+// Flaky on Linux and ChromeOS. https://crbug.com/702300
+#if defined(OS_LINUX) || defined(OS_CHROMEOS)
+#define MAYBE_MissingVerifiedContents DISABLED_MissingVerifiedContents
+#else
+#define MAYBE_MissingVerifiedContents MissingVerifiedContents
+#endif
 // This tests our ability to successfully fetch, parse, and validate a missing
 // verified_contents.json file for an extension.
-TEST_F(ContentHashFetcherTest, MissingVerifiedContents) {
+TEST_F(ContentHashFetcherTest, MAYBE_MissingVerifiedContents) {
   // We unzip the extension source to a temp directory to simulate it being
   // installed there, because the ContentHashFetcher will create the _metadata/
   // directory within the extension install dir and write the fetched
@@ -227,9 +233,7 @@
       base::PathExists(file_util::GetVerifiedContentsPath(extension->path())));
 }
 
-// Similar to MissingVerifiedContents, but tests the case where the extension
-// actually has corruption.
-// Flaky on Linux and ChromeOS. crbug.com/
+// Flaky on Linux and ChromeOS. https://crbug.com/702300
 #if defined(OS_LINUX) || defined(OS_CHROMEOS)
 #define MAYBE_MissingVerifiedContentsAndCorrupt \
   DISABLED_MissingVerifiedContentsAndCorrupt
@@ -237,6 +241,8 @@
 #define MAYBE_MissingVerifiedContentsAndCorrupt \
   MissingVerifiedContentsAndCorrupt
 #endif
+// Similar to MissingVerifiedContents, but tests the case where the extension
+// actually has corruption.
 TEST_F(ContentHashFetcherTest, MAYBE_MissingVerifiedContentsAndCorrupt) {
   base::FilePath test_dir_base =
       GetTestPath(base::FilePath()).AppendASCII("missing_verified_contents");
diff --git a/extensions/browser/guest_view/web_view/web_view_apitest.cc b/extensions/browser/guest_view/web_view/web_view_apitest.cc
index fe74232..745edfe 100644
--- a/extensions/browser/guest_view/web_view/web_view_apitest.cc
+++ b/extensions/browser/guest_view/web_view/web_view_apitest.cc
@@ -533,17 +533,10 @@
   RunTest("testExecuteScriptFail", "web_view/apitest");
 }
 
-// Failes on Linux/CrOS.  https://crbug.com/702918
-#if defined(OS_LINUX) || defined(OS_CHROMEOS)
-#define MAYBE_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged \
-  DISABLED_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged
-#else
-#define MAYBE_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged \
-  TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged
-#endif
+// Flaky and likely not testing the right assertion.  https://crbug.com/702918
 IN_PROC_BROWSER_TEST_F(
     WebViewAPITest,
-    MAYBE_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) {
+    DISABLED_TestExecuteScriptIsAbortedWhenWebViewSourceIsChanged) {
   RunTest("testExecuteScriptIsAbortedWhenWebViewSourceIsChanged",
           "web_view/apitest");
 }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
index b6412b6..5eefa17 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doer_prototypes.h
@@ -420,6 +420,8 @@
                           GLenum type,
                           GLsizei bufsize,
                           GLsizei* length,
+                          GLsizei* columns,
+                          GLsizei* rows,
                           void* pixels,
                           int32_t* success);
 error::Error DoReleaseShaderCompiler();
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
index f6e723d..d602360 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_doers.cc
@@ -1687,11 +1687,13 @@
                                                        GLenum type,
                                                        GLsizei bufsize,
                                                        GLsizei* length,
+                                                       GLsizei* columns,
+                                                       GLsizei* rows,
                                                        void* pixels,
                                                        int32_t* success) {
   FlushErrors();
   glReadPixelsRobustANGLE(x, y, width, height, format, type, bufsize, length,
-                          pixels);
+                          columns, rows, pixels);
   *success = FlushErrors() ? 0 : 1;
   return error::kNoError;
 }
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
index 2989ccd..a1398ed 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder_passthrough_handlers.cc
@@ -859,9 +859,11 @@
 
   GLsizei bufsize = buffer_size;
   GLsizei length = 0;
+  GLsizei columns = 0;
+  GLsizei rows = 0;
   int32_t success = 0;
   error::Error error = DoReadPixels(x, y, width, height, format, type, bufsize,
-                                    &length, pixels, &success);
+                                    &length, &columns, &rows, pixels, &success);
   if (error != error::kNoError) {
     return error;
   }
@@ -884,8 +886,8 @@
 
   if (result) {
     result->success = success;
-    result->row_length = static_cast<uint32_t>(width);
-    result->num_rows = static_cast<uint32_t>(height);
+    result->row_length = static_cast<uint32_t>(columns);
+    result->num_rows = static_cast<uint32_t>(rows);
   }
 
   return error::kNoError;
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 79d69d9..7e12be9 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1453,7 +1453,7 @@
       <message name="IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE" desc="The iOS menu item for requesting the desktop site [Length: 15em] [iOS only]">
         Request Desktop Site
       </message>
-      <message name="IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE" desc="The iOS menu item for requesting the mobile site [Length: 15em] [iOS only]">
+      <message name="IDS_IOS_TOOLS_MENU_REQUEST_MOBILE_SITE" desc="The iOS menu item for requesting the mobile site [Length: 25em] [iOS only]">
         Request Mobile Site
       </message>
       <message name="IDS_IOS_TOOLS_MENU_SETTINGS" desc="The iOS menu item for opening the settings [Length: 15em] [iOS only]">
diff --git a/ios/chrome/browser/payments/payment_request_view_controller.mm b/ios/chrome/browser/payments/payment_request_view_controller.mm
index cc90505..eb9e74a 100644
--- a/ios/chrome/browser/payments/payment_request_view_controller.mm
+++ b/ios/chrome/browser/payments/payment_request_view_controller.mm
@@ -60,6 +60,8 @@
 // as a link in the UI (see setLabelLinkURL: in CollectionViewFooterCell).
 const char kSettingsURL[] = "settings://card-and-address";
 
+const CGFloat kFooterCellHorizontalPadding = 16;
+
 }  // namespace
 
 NSString* const kPaymentRequestCollectionViewID =
@@ -519,6 +521,15 @@
           [[MDCPalette cr_bluePalette] tint700];
       break;
     }
+    case ItemTypeFooterText: {
+      CollectionViewFooterCell* footerCell =
+          base::mac::ObjCCastStrict<CollectionViewFooterCell>(cell);
+      footerCell.textLabel.font = [MDCTypography body2Font];
+      footerCell.textLabel.textColor = [[MDCPalette greyPalette] tint600];
+      footerCell.textLabel.shadowColor = nil;  // No shadow.
+      footerCell.horizontalPadding = kFooterCellHorizontalPadding;
+      break;
+    }
     default:
       break;
   }
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.cc b/ios/chrome/browser/reading_list/offline_url_utils.cc
index 2964725..9d41df72 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils.cc
+++ b/ios/chrome/browser/reading_list/offline_url_utils.cc
@@ -10,6 +10,8 @@
 #include "base/strings/stringprintf.h"
 #include "base/strings/utf_string_conversions.h"
 #include "components/reading_list/core/offline_url_utils.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model.h"
 #include "ios/chrome/browser/chrome_url_constants.h"
 #include "net/base/url_util.h"
 
@@ -23,21 +25,19 @@
 GURL OfflineURLForPath(const base::FilePath& distilled_path,
                        const GURL& entry_url,
                        const GURL& virtual_url) {
-  if (distilled_path.empty()) {
-    return GURL();
-  }
+  DCHECK(!distilled_path.empty());
+  DCHECK(entry_url.is_valid());
+  DCHECK(virtual_url.is_valid());
   GURL page_url(kChromeUIOfflineURL);
   GURL::Replacements replacements;
   replacements.SetPathStr(distilled_path.value());
   page_url = page_url.ReplaceComponents(replacements);
-  if (entry_url.is_valid()) {
-    page_url = net::AppendQueryParameter(page_url, kEntryURLQueryParam,
-                                         entry_url.spec());
-  }
-  if (virtual_url.is_valid()) {
-    page_url = net::AppendQueryParameter(page_url, kVirtualURLQueryParam,
-                                         virtual_url.spec());
-  }
+  page_url = net::AppendQueryParameter(page_url, kEntryURLQueryParam,
+                                       entry_url.spec());
+
+  page_url = net::AppendQueryParameter(page_url, kVirtualURLQueryParam,
+                                       virtual_url.spec());
+
   return page_url;
 }
 
@@ -50,7 +50,7 @@
       return entry_url;
     }
   }
-  return offline_url;
+  return GURL::EmptyGURL();
 }
 
 GURL VirtualURLForOfflineURL(const GURL& offline_url) {
@@ -62,7 +62,7 @@
       return virtual_url;
     }
   }
-  return EntryURLForOfflineURL(offline_url);
+  return GURL::EmptyGURL();
 }
 
 GURL FileURLForDistilledURL(const GURL& distilled_url,
@@ -84,4 +84,24 @@
 bool IsOfflineURL(const GURL& url) {
   return url.SchemeIs(kChromeUIScheme) && url.host() == kChromeUIOfflineHost;
 }
+
+bool IsOfflineURLValid(const GURL& url, ReadingListModel* model) {
+  if (!IsOfflineURL(url)) {
+    return false;
+  }
+  GURL entry_url = EntryURLForOfflineURL(url);
+  if (!entry_url.is_valid() || !model || !model->loaded()) {
+    return false;
+  }
+  const ReadingListEntry* entry = model->GetEntryByURL(entry_url);
+  if (!entry || entry->DistilledState() != ReadingListEntry::PROCESSED) {
+    return false;
+  }
+  // It is possible (unlikely) for a user to type directly a URL that passes all
+  // the tests above but still is not exactly the one returned by
+  // |OfflineURLForPath|. Make a final test to check it.
+  return url == reading_list::OfflineURLForPath(entry->DistilledPath(),
+                                                entry->URL(),
+                                                entry->DistilledURL());
+}
 }
diff --git a/ios/chrome/browser/reading_list/offline_url_utils.h b/ios/chrome/browser/reading_list/offline_url_utils.h
index d7af133..34caa5d 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils.h
+++ b/ios/chrome/browser/reading_list/offline_url_utils.h
@@ -11,23 +11,26 @@
 #include "base/strings/string16.h"
 #include "url/gurl.h"
 
+class ReadingListModel;
+
 namespace reading_list {
 
 // The distilled URL chrome://offline/... that will load the file at |path|.
 // |entry_url| is the URL of the ReadingListEntry.
 // |virtual_url| is the URL to display in the omnibox. This can be different
-// from |entry_url| is the distillation was done after a redirection.
-// |entry_url| and |virtual_url| are optionnal.
+// from |entry_url| if the distillation was done after a redirection.
+// |distilled_path|, |entry_url| and |virtual_url| are required and must not be
+// empty or invalid.
 GURL OfflineURLForPath(const base::FilePath& distilled_path,
                        const GURL& entry_url,
                        const GURL& virtual_url);
 
 // If |offline_url| has a "entryURL" query params that is a URL, returns it.
-// If not, return |offline_url|
+// If not, return GURL::EmptyURL().
 GURL EntryURLForOfflineURL(const GURL& offline_url);
 
 // If |offline_url| has a "virtualURL" query params that is a URL, returns it.
-// If not, return |EntryURLForOfflineURL(|offline_url|)|
+// If not, return GURL::EmptyURL().
 GURL VirtualURLForOfflineURL(const GURL& offline_url);
 
 // The file URL pointing to the local file to load to display |distilled_url|.
@@ -41,6 +44,12 @@
 // Returns whether the URL points to a chrome offline URL.
 bool IsOfflineURL(const GURL& url);
 
+// Returns whether the URL points to a valid chrome offline URL that can be
+// displayed by a |OfflinePageNativeContent|.
+// Returns false if |model| is null, not loaded or does not contain entry
+// pointed by |url|.
+bool IsOfflineURLValid(const GURL& url, ReadingListModel* model);
+
 }  // namespace reading_list
 
 #endif  // IOS_CHROME_BROWSER_READING_LIST_OFFLINE_URL_UTILS_H_
diff --git a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
index 8472ffad..0229ceb 100644
--- a/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
+++ b/ios/chrome/browser/reading_list/offline_url_utils_unittest.cc
@@ -7,19 +7,15 @@
 #include <string>
 
 #include "base/files/file_path.h"
+#include "base/memory/ptr_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/gtest_util.h"
+#include "base/time/default_clock.h"
+#include "components/reading_list/core/reading_list_entry.h"
+#include "components/reading_list/core/reading_list_model_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 
-// Checks the distilled URL for the page is chrome://offline/MD5/page.html;
-TEST(OfflineURLUtilsTest, DistilledURLForPathTest) {
-  base::FilePath page_path("MD5/page.html");
-  GURL distilled_url =
-      reading_list::OfflineURLForPath(page_path, GURL(), GURL());
-  EXPECT_EQ("chrome://offline/MD5/page.html", distilled_url.spec());
-}
-
 // Checks the distilled URL for the page with an onlineURL is
 // chrome://offline/MD5/page.html?entryURL=...&virtualURL=...
 TEST(OfflineURLUtilsTest, OfflineURLForPathWithEntryURLAndVirtualURLTest) {
@@ -36,26 +32,26 @@
 }
 
 // Checks the parsing of offline URL chrome://offline/MD5/page.html.
-// As entryURL and virtualURL are absent, they should return the offline URL.
+// As entryURL and virtualURL are absent, they should be invalid.
 TEST(OfflineURLUtilsTest, ParseOfflineURLTest) {
   GURL distilled_url("chrome://offline/MD5/page.html");
   GURL entry_url = reading_list::EntryURLForOfflineURL(distilled_url);
-  EXPECT_EQ("chrome://offline/MD5/page.html", entry_url.spec());
+  EXPECT_TRUE(entry_url.is_empty());
   GURL virtual_url = reading_list::VirtualURLForOfflineURL(distilled_url);
-  EXPECT_EQ("chrome://offline/MD5/page.html", virtual_url.spec());
+  EXPECT_TRUE(virtual_url.is_empty());
 }
 
 // Checks the parsing of offline URL
 // chrome://offline/MD5/page.html?entryURL=encorded%20URL
 // As entryURL is present, it should be returned correctly.
-// As virtualURL is absent, it should return the entryURL.
+// As virtualURL is absent, it should return GURL::EmptyGURL().
 TEST(OfflineURLUtilsTest, ParseOfflineURLWithEntryURLTest) {
   GURL offline_url(
       "chrome://offline/MD5/page.html?entryURL=http%3A%2F%2Ffoo.bar%2F");
   GURL entry_url = reading_list::EntryURLForOfflineURL(offline_url);
   EXPECT_EQ("http://foo.bar/", entry_url.spec());
   GURL virtual_url = reading_list::VirtualURLForOfflineURL(offline_url);
-  EXPECT_EQ("http://foo.bar/", virtual_url.spec());
+  EXPECT_TRUE(virtual_url.is_empty());
 }
 
 // Checks the parsing of offline URL
@@ -66,7 +62,7 @@
   GURL offline_url(
       "chrome://offline/MD5/page.html?virtualURL=http%3A%2F%2Ffoo.bar%2F");
   GURL entry_url = reading_list::EntryURLForOfflineURL(offline_url);
-  EXPECT_EQ(offline_url, entry_url);
+  EXPECT_TRUE(entry_url.is_empty());
   GURL virtual_url = reading_list::VirtualURLForOfflineURL(offline_url);
   EXPECT_EQ("http://foo.bar/", virtual_url.spec());
 }
@@ -125,3 +121,48 @@
       reading_list::IsOfflineURL(GURL("chrome://offline/foobar?foo=bar")));
 }
 
+// Checks that the offline URLs are correctly detected by |IsOfflineURL|.
+TEST(OfflineURLUtilsTest, IsOfflineURLValid) {
+  auto reading_list_model = base::MakeUnique<ReadingListModelImpl>(
+      nullptr, nullptr, base::MakeUnique<base::DefaultClock>());
+  GURL entry_url("http://entry_url.com");
+  base::FilePath distilled_path("distilled/page.html");
+  GURL distilled_url("http://distilled_url.com");
+  reading_list_model->AddEntry(entry_url, "title",
+                               reading_list::ADDED_VIA_CURRENT_APP);
+  reading_list_model->SetEntryDistilledInfo(
+      entry_url, distilled_path, distilled_url, 10, base::Time::Now());
+
+  EXPECT_FALSE(
+      reading_list::IsOfflineURLValid(GURL(), reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("chrome://"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("chrome://offline-foobar"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("http://offline/"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("http://chrome://offline/"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("chrome://offline"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("chrome://offline/"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(GURL("chrome://offline/foobar"),
+                                               reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(
+      GURL("chrome://offline/foobar?foo=bar"), reading_list_model.get()));
+  EXPECT_TRUE(reading_list::IsOfflineURLValid(
+      reading_list::OfflineURLForPath(distilled_path, entry_url, distilled_url),
+      reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(
+      reading_list::OfflineURLForPath(distilled_path, entry_url, entry_url),
+      reading_list_model.get()));
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(
+      reading_list::OfflineURLForPath(base::FilePath("not_distilled_path"),
+                                      entry_url, distilled_url),
+      reading_list_model.get()));
+  reading_list_model->RemoveEntryByURL(entry_url);
+  EXPECT_FALSE(reading_list::IsOfflineURLValid(
+      reading_list::OfflineURLForPath(distilled_path, entry_url, distilled_url),
+      reading_list_model.get()));
+}
diff --git a/ios/chrome/browser/ui/browser_view_controller.mm b/ios/chrome/browser/ui/browser_view_controller.mm
index e4b9c0f..4e535e1 100644
--- a/ios/chrome/browser/ui/browser_view_controller.mm
+++ b/ios/chrome/browser/ui/browser_view_controller.mm
@@ -2973,9 +2973,14 @@
 
 - (BOOL)hasControllerForURL:(const GURL&)url {
   std::string host(url.host());
+  if (host == kChromeUIOfflineHost) {
+    // Only allow offline URL that are fully specified.
+    return reading_list::IsOfflineURLValid(
+        url, ReadingListModelFactory::GetForBrowserState(_browserState));
+  }
 
   return host == kChromeUINewTabHost || host == kChromeUIBookmarksHost ||
-         host == kChromeUITermsHost || host == kChromeUIOfflineHost;
+         host == kChromeUITermsHost;
 }
 
 - (id<CRWNativeContent>)controllerForURL:(const GURL&)url
@@ -3028,7 +3033,8 @@
     [self setOverScrollActionControllerToStaticNativeContent:
               staticNativeController];
     nativeController = staticNativeController;
-  } else if (url_host == kChromeUIOfflineHost) {
+  } else if (url_host == kChromeUIOfflineHost &&
+             [self hasControllerForURL:url]) {
     StaticHtmlNativeContent* staticNativeController =
         [[[OfflinePageNativeContent alloc] initWithLoader:self
                                              browserState:_browserState
diff --git a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_article_item.mm b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_article_item.mm
index 569b9e1..8781014 100644
--- a/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_article_item.mm
+++ b/ios/chrome/browser/ui/content_suggestions/cells/content_suggestions_article_item.mm
@@ -173,8 +173,7 @@
     _titleLabel.font = [MDCTypography subheadFont];
     _subtitleLabel.font = [MDCTypography body1Font];
     _publisherLabel.font = [MDCTypography captionFont];
-    _faviconView.font =
-        [[MDCTypography fontLoader] regularFontOfSize:kFaviconSize / 2];
+    _faviconView.font = [[MDCTypography fontLoader] mediumFontOfSize:10];
 
     _subtitleLabel.textColor = [[MDCPalette greyPalette] tint700];
     _publisherLabel.textColor = [[MDCPalette greyPalette] tint700];
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
index 16e91f9..dd8d35b9 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_toolbar_controller.mm
@@ -206,10 +206,12 @@
   [super hideViewsForNewTabPage:hide];
   // Show the back/forward buttons if there is forward history.
   ToolbarModelIOS* toolbarModelIOS = [_delegate toolbarModelIOS];
-  BOOL forwardEnabled = toolbarModelIOS->CanGoForward();
-  [_backButton setHidden:!forwardEnabled && hide];
-  [_backButton setEnabled:toolbarModelIOS->CanGoBack()];
-  [_forwardButton setHidden:!forwardEnabled && hide];
+  if (toolbarModelIOS) {
+    BOOL forwardEnabled = toolbarModelIOS->CanGoForward();
+    [_backButton setHidden:!forwardEnabled && hide];
+    [_backButton setEnabled:toolbarModelIOS->CanGoBack()];
+    [_forwardButton setHidden:!forwardEnabled && hide];
+  }
 }
 
 - (void)focusOmnibox:(id)sender {
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view.mm
index 7f9f7ab8..5245ee5 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view.mm
@@ -80,11 +80,15 @@
     [self.scrollView setContentOffset:point animated:NO];
   }
 
-  // Trigger a layout.  The |-layoutIfNeeded| call is required because sometimes
-  // |-layoutSubviews| is not successfully triggered when |-setNeedsLayout| is
-  // called after frame changes due to autoresizing masks.
-  [self setNeedsLayout];
-  [self layoutIfNeeded];
+  // This should never be needed in autolayout.
+  if (self.translatesAutoresizingMaskIntoConstraints) {
+    // Trigger a layout.  The |-layoutIfNeeded| call is required because
+    // sometimes  |-layoutSubviews| is not successfully triggered when
+    // |-setNeedsLayout| is called after frame changes due to autoresizing
+    // masks.
+    [self setNeedsLayout];
+    [self layoutIfNeeded];
+  }
 }
 
 - (void)layoutSubviews {
@@ -102,6 +106,14 @@
         CGRectGetMinX(self.bounds), CGRectGetMinY(self.bounds),
         CGRectGetWidth(self.bounds), CGRectGetMinY(self.tabBar.frame));
   }
+
+  // When using a new_tab_page_view in autolayout -setFrame is never called,
+  // which means all the logic to keep the selected scroll index set is never
+  // called.  Rather than refactor away all of this to support ios/clean, just
+  // make sure -setFrame is called when loaded in autolayout.
+  if (!self.translatesAutoresizingMaskIntoConstraints) {
+    [self setFrame:self.frame];
+  }
 }
 
 - (void)updateScrollViewContentSize {
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new.h b/ios/chrome/browser/ui/ntp/notification_promo_whats_new.h
index 31a74f3..b1b0f48 100644
--- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new.h
+++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new.h
@@ -42,7 +42,6 @@
   bool valid() const { return valid_; }
   const std::string& promo_type() { return promo_type_; }
   const std::string& promo_text() { return promo_text_; }
-  const std::string& promo_name() { return promo_name_; }
   WhatsNewIcon icon() { return icon_; }
   bool IsURLPromo() const;
   const GURL& url() { return url_; }
@@ -78,9 +77,6 @@
   // Type of whats new promo.
   std::string promo_type_;
 
-  // Name of promo.
-  std::string promo_name_;
-
   // Icon of promo.
   WhatsNewIcon icon_;
 
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new.mm b/ios/chrome/browser/ui/ntp/notification_promo_whats_new.mm
index 2828c79..e47479fa 100644
--- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new.mm
+++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new.mm
@@ -42,13 +42,13 @@
 };
 
 // Returns a localized version of |promo_text| if it has an entry in the
-// |kPromoStringToIdsMap|. If there is no entry, |promo_text| is returned.
+// |kPromoStringToIdsMap|. If there is no entry, an empty string is returned.
 std::string GetLocalizedPromoText(const std::string& promo_text) {
   for (size_t i = 0; i < arraysize(kPromoStringToIdsMap); ++i) {
     if (kPromoStringToIdsMap[i].promo_text_str == promo_text)
       return l10n_util::GetStringUTF8(kPromoStringToIdsMap[i].message_id);
   }
-  return promo_text;
+  return std::string();
 }
 
 }  // namespace
@@ -141,12 +141,6 @@
     }
   }
 
-  if (promo_name_ == "WKWVGotFasterPromo") {
-    // Promo is not relevant anymore: It was shown during the migration to the
-    // WKWebview.
-    return false;
-  }
-
   return true;
 }
 
@@ -195,21 +189,23 @@
 bool NotificationPromoWhatsNew::InitFromNotificationPromo() {
   valid_ = false;
 
-  notification_promo_.promo_payload()->GetString("promo_type", &promo_type_);
-  notification_promo_.promo_payload()->GetString("metric_name", &metric_name_);
   promo_text_ = GetLocalizedPromoText(notification_promo_.promo_text());
+  if (promo_text_.empty())
+    return valid_;
 
+  notification_promo_.promo_payload()->GetString("metric_name", &metric_name_);
+  if (metric_name_.empty())
+    return valid_;
+
+  notification_promo_.promo_payload()->GetString("promo_type", &promo_type_);
   if (IsURLPromo()) {
     std::string url_text;
     notification_promo_.promo_payload()->GetString("url", &url_text);
     url_ = GURL(url_text);
     if (url_.is_empty() || !url_.is_valid()) {
-      valid_ = false;
       return valid_;
     }
-  }
-
-  if (IsChromeCommand()) {
+  } else if (IsChromeCommand()) {
     std::string command;
     notification_promo_.promo_payload()->GetString("command", &command);
     if (command == "bookmark") {
@@ -217,20 +213,19 @@
     } else if (command == "ratethisapp") {
       command_id_ = IDC_RATE_THIS_APP;
     } else {
-      valid_ = false;
       return valid_;
     }
+  } else {  // If |promo_type_| is not set to URL or Command, return early.
+    return valid_;
   }
 
-  valid_ =
-      !metric_name_.empty() && !promo_type_.empty() && !promo_text_.empty();
+  valid_ = true;
 
-  notification_promo_.promo_payload()->GetString("promo_name", &promo_name_);
+  // Optional values don't need validation.
   std::string icon_name;
   notification_promo_.promo_payload()->GetString("icon", &icon_name);
   icon_ = ParseIconName(icon_name);
 
-  // Optional values don't need validation.
   seconds_since_install_ = 0;
   notification_promo_.promo_payload()->GetInteger("seconds_since_install",
                                                   &seconds_since_install_);
diff --git a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
index 9a917a9..1ed5ebe 100644
--- a/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
+++ b/ios/chrome/browser/ui/ntp/notification_promo_whats_new_unittest.mm
@@ -137,17 +137,59 @@
 
 // Test that a url-based, valid promo is shown with the correct text and icon.
 TEST_F(NotificationPromoWhatsNewTest, NotificationPromoURLTest) {
-  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT", "Test URL", "0",
-       "url", "http://blog.chromium.org", "", "TestURLPromo", "", "0", "0");
-  RunTests("Test URL", "url", "http://blog.chromium.org/", 0, WHATS_NEW_INFO,
-           true);
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_MOVE_TO_DOCK_TIP", "0", "url", "http://blog.chromium.org", "",
+       "TestURLPromo", "", "0", "0");
+  RunTests(l10n_util::GetStringUTF8(IDS_IOS_MOVE_TO_DOCK_TIP), "url",
+           "http://blog.chromium.org/", 0, WHATS_NEW_INFO, true);
 }
 
-// Test that an invalid promo is not shown.
-TEST_F(NotificationPromoWhatsNewTest, NotificationPromoInvalidTest) {
-  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT", "Test URL", "0",
-       "url", "", "", "TestURLPromo", "", "0", "0");
-  RunTests("Test URL", "url", "", 0, WHATS_NEW_INFO, false);
+// Test that a promo without a valid promo type is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoNoTypeTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_MOVE_TO_DOCK_TIP", "0", "invalid type",
+       "http://blog.chromium.org", "", "TestPromo", "", "0", "0");
+  EXPECT_FALSE(promo_.CanShow());
+}
+
+// Test that a url promo with an empty url is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoEmptyURLTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_MOVE_TO_DOCK_TIP", "0", "url", "", "", "TestURLPromo", "", "0",
+       "0");
+  EXPECT_FALSE(promo_.CanShow());
+}
+
+// Test that a url promo with an invalid url is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoInvalidURLTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_MOVE_TO_DOCK_TIP", "0", "url", "INVALID URL", "",
+       "TestURLPromo", "", "0", "0");
+  EXPECT_FALSE(promo_.CanShow());
+}
+
+// Test that a command-based promo with an invalid command is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoInvalidCommandTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_APP_RATING_PROMO_STRING", "0", "chrome_command", "",
+       "INVALID COMMAND", "CommandPromo", "logo", "0", "0");
+  EXPECT_FALSE(promo_.CanShow());
+}
+
+// Test that a promo without a metric name is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoNoMetricTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT",
+       "IDS_IOS_MOVE_TO_DOCK_TIP", "0", "url", "http://blog.chromium.org", "",
+       "", "", "0", "0");
+  EXPECT_FALSE(promo_.CanShow());
+}
+
+// Test that if no localized text is found, the promo is not shown.
+TEST_F(NotificationPromoWhatsNewTest, NotificationPromoNoLocalizedTextTest) {
+  Init("3 Aug 1999 9:26:06 GMT", "3 Aug 2199 9:26:06 GMT", "TEST BAD STRING",
+       "0", "url", "http://blog.chromium.org", "", "TestURLPromo", "", "0",
+       "0");
+  EXPECT_FALSE(promo_.CanShow());
 }
 
 // Test that if max_seconds_since_install is set, and the current time is before
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h
index 26f4c72..aa4a580 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h
@@ -45,6 +45,10 @@
 // Sets the tab restore service to null.
 - (void)tabRestoreServiceDestroyed;
 
+// TODO(crbug.com/708319): Expose the view controller in preparation for a full
+// view controller conversion.
+- (UIViewController*)viewController;
+
 @end
 
 #endif  // IOS_CHROME_BROWSER_UI_NTP_RECENT_TABS_RECENT_TABS_PANEL_CONTROLLER_H_
diff --git a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.mm b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.mm
index 5c86b8b..b2d3c4e 100644
--- a/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.mm
+++ b/ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.mm
@@ -180,6 +180,10 @@
   return [_tableViewController view];
 }
 
+- (UIViewController*)viewController {
+  return _tableViewController;
+}
+
 #pragma mark - Private
 
 - (BOOL)isSignedIn {
diff --git a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
index 9b3a5cd..cd1c109a 100644
--- a/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
+++ b/ios/chrome/browser/ui/reading_list/offline_page_native_content.mm
@@ -86,6 +86,11 @@
 }
 
 - (void)reload {
+  if (!_entryURL.is_valid()) {
+    // If entryURL is not valid, the restoreOnlineURL will fail and the |reload|
+    // will be called in a loop. Early return here.
+    return;
+  }
   [self restoreOnlineURL];
   _webState->GetNavigationManager()->Reload(web::ReloadType::NORMAL,
                                             false /*check_for_repost*/);
diff --git a/ios/chrome/browser/ui/reading_list/offline_page_native_content_unittest.mm b/ios/chrome/browser/ui/reading_list/offline_page_native_content_unittest.mm
index a17566bc..abc5dd5 100644
--- a/ios/chrome/browser/ui/reading_list/offline_page_native_content_unittest.mm
+++ b/ios/chrome/browser/ui/reading_list/offline_page_native_content_unittest.mm
@@ -49,23 +49,6 @@
   ASSERT_OCMOCK_VERIFY((OCMockObject*)loader);
 }
 
-// Checks the OfflinePageNativeContent without virtual URL is correctly
-// initialized
-TEST_F(OfflinePageNativeContentTest, BasicOfflinePageTestWithoutVirtualURL) {
-  GURL url = reading_list::OfflineURLForPath(
-      base::FilePath("offline_id/page.html"), GURL(), GURL());
-  id<UrlLoader> loader = [OCMockObject mockForProtocol:@protocol(UrlLoader)];
-  base::scoped_nsobject<OfflinePageNativeContent> content(
-      [[OfflinePageNativeContent alloc]
-          initWithLoader:loader
-            browserState:chrome_browser_state_.get()
-                webState:web_state()
-                     URL:url]);
-  ASSERT_EQ(url, [content url]);
-  ASSERT_EQ(url, [content virtualURL]);
-  ASSERT_OCMOCK_VERIFY((OCMockObject*)loader);
-}
-
 // Checks that dismissing offline page restores EntryURL.
 TEST_F(OfflinePageNativeContentTest, DismissOfflineContent) {
   GURL offline_url("http://foo.bar/offline");
diff --git a/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn b/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn
new file mode 100644
index 0000000..dec4e7694
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/bookmarks/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2017 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.
+
+source_set("bookmarks") {
+  sources = [
+    "bookmarks_coordinator.h",
+    "bookmarks_coordinator.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/bookmarks",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/coordinators",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "bookmarks_coordinator_unittest.mm",
+  ]
+
+  deps = [
+    ":bookmarks",
+    "//testing/gtest",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h
new file mode 100644
index 0000000..1493cf9
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h
@@ -0,0 +1,17 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARKS_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARKS_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// A coordinator for the bookmarks UI, which can be presented modally on its
+// own or inside the NTP.
+@interface BookmarksCoordinator : BrowserCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_BOOKMARKS_BOOKMARKS_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
new file mode 100644
index 0000000..9bdeae9
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.mm
@@ -0,0 +1,47 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h"
+
+#import "ios/chrome/browser/ui/bookmarks/bookmark_controller_factory.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_handset_view_controller.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface BookmarksCoordinator ()
+@property(nonatomic, strong) UIViewController* viewController;
+@property(nonatomic, strong) BookmarkHomeTabletNTPController* wrapperController;
+@end
+
+@implementation BookmarksCoordinator
+@synthesize viewController = _viewController;
+@synthesize wrapperController = _wrapperController;
+
+- (void)start {
+  // HACK: Re-using old view controllers for now.
+  if (!IsIPadIdiom()) {
+    self.viewController = [[BookmarkHomeHandsetViewController alloc]
+        initWithLoader:nil
+          browserState:self.browser->browser_state()];
+    self.viewController.modalPresentationStyle = UIModalPresentationFormSheet;
+  } else {
+    BookmarkControllerFactory* factory =
+        [[BookmarkControllerFactory alloc] init];
+    self.wrapperController = [factory
+        bookmarkPanelControllerForBrowserState:self.browser->browser_state()
+                                        loader:nil
+                                    colorCache:nil];
+    self.viewController = [[UIViewController alloc] init];
+    self.viewController.view = [self.wrapperController view];
+  }
+  [super start];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator_unittest.mm
new file mode 100644
index 0000000..30ca2dd
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/commands/BUILD.gn b/ios/clean/chrome/browser/ui/commands/BUILD.gn
index 2f1c64c4f..3be6a02 100644
--- a/ios/clean/chrome/browser/ui/commands/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/commands/BUILD.gn
@@ -5,13 +5,16 @@
 source_set("commands") {
   sources = [
     "navigation_commands.h",
+    "ntp_commands.h",
     "settings_commands.h",
+    "tab_commands.h",
     "tab_grid_commands.h",
     "tab_strip_commands.h",
     "tools_menu_commands.h",
   ]
   deps = [
     "//base",
+    "//ios/web:web_arc",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
diff --git a/ios/clean/chrome/browser/ui/commands/ntp_commands.h b/ios/clean/chrome/browser/ui/commands/ntp_commands.h
new file mode 100644
index 0000000..6938943
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/ntp_commands.h
@@ -0,0 +1,21 @@
+// Copyright 2016 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 IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_NTP_COMMANDS_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_NTP_COMMANDS_H_
+
+// Command protocol for commands relating to the Commands UI.
+// (Commands are for communicating into or within the coordinator layer).
+@protocol NTPCommands
+// Display a Chrome Home panel inside the NTP.
+- (void)showNTPHomePanel;
+// Display a bookmarks panel inside the NTP on iPad or present a bookmarks panel
+// on iPhone.
+- (void)showNTPBookmarksPanel;
+// Display a recent tabs panel inside the NTP on iPad or present a bookmarks
+// panel on iPhone.
+- (void)showNTPRecentTabsPanel;
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_NTP_COMMANDS_H_
diff --git a/ios/clean/chrome/browser/ui/commands/tab_commands.h b/ios/clean/chrome/browser/ui/commands/tab_commands.h
new file mode 100644
index 0000000..d2f498c
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/commands/tab_commands.h
@@ -0,0 +1,16 @@
+// Copyright 2016 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 IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
+
+#import "ios/web/public/navigation_manager.h"
+
+// Commands relating to the Settings UI.
+@protocol TabCommands
+// Open a URL.
+- (void)loadURL:(web::NavigationManager::WebLoadParams)params;
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_COMMANDS_TAB_COMMANDS_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/BUILD.gn b/ios/clean/chrome/browser/ui/ntp/BUILD.gn
index 2def52c..c8a871d 100644
--- a/ios/clean/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/ntp/BUILD.gn
@@ -4,22 +4,41 @@
 
 source_set("ntp") {
   sources = [
-    "new_tab_page_coordinator.h",
-    "new_tab_page_coordinator.mm",
+    "ntp_coordinator.h",
+    "ntp_coordinator.mm",
+    "ntp_home_coordinator.h",
+    "ntp_home_coordinator.mm",
+    "ntp_home_mediator.h",
+    "ntp_home_mediator.mm",
+    "ntp_mediator.h",
+    "ntp_mediator.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
 
   deps = [
     ":ntp_ui",
+    "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/ntp:ntp_internal",
+    "//ios/chrome/browser/ui/toolbar",
+    "//ios/clean/chrome/browser/ui/bookmarks",
+    "//ios/clean/chrome/browser/ui/commands",
+    "//ios/clean/chrome/browser/ui/recent_tabs",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
+    "//ios/web:web_arc",
+    "//ui/base:base",
+    "//url:url",
   ]
 }
 
 source_set("ntp_ui") {
   sources = [
-    "new_tab_page_view_controller.h",
-    "new_tab_page_view_controller.mm",
+    "ntp_consumer.h",
+    "ntp_view_controller.h",
+    "ntp_view_controller.mm",
   ]
 
   configs += [ "//build/config/compiler:enable_arc" ]
@@ -28,8 +47,28 @@
     "//base",
     "//components/strings:components_strings_grit",
     "//ios/chrome/app/strings:ios_strings_grit",
+    "//ios/chrome/browser/ui:ui",
     "//ios/chrome/browser/ui/ntp:ntp_internal",
     "//ios/clean/chrome/browser/ui",
+    "//ios/clean/chrome/browser/ui/commands:commands",
     "//ui/base:base",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "ntp_coordinator_unittest.mm",
+    "ntp_home_coordinator_unittest.mm",
+    "ntp_home_mediator_unittest.mm",
+    "ntp_mediator_unittest.mm",
+    "ntp_view_controller_unittest.mm",
+  ]
+
+  deps = [
+    ":ntp",
+    ":ntp_ui",
+    "//testing/gtest",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
deleted file mode 100644
index 5dfd67c..0000000
--- a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.h"
-
-#import "ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@interface NTPCoordinator ()
-@property(nonatomic, strong) NTPViewController* viewController;
-@end
-
-@implementation NTPCoordinator
-@synthesize viewController = _viewController;
-
-- (void)start {
-  self.viewController = [[NTPViewController alloc] init];
-  [super start];
-}
-
-@end
diff --git a/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.h
deleted file mode 100644
index c2b5f34..0000000
--- a/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
-#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
-
-#import <UIKit/UIKit.h>
-
-// View controller that displays a new tab page.
-@interface NTPViewController : UIViewController
-@end
-
-#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
deleted file mode 100644
index 7e9eee5a..0000000
--- a/ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#import "ios/clean/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
-
-#import "base/ios/crb_protocol_observers.h"
-#include "components/strings/grit/components_strings.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
-#import "ios/chrome/browser/ui/ntp/new_tab_page_view.h"
-#include "ios/chrome/grit/ios_strings.h"
-#include "ui/base/l10n/l10n_util.h"
-
-#if !defined(__has_feature) || !__has_feature(objc_arc)
-#error "This file requires ARC support."
-#endif
-
-@implementation NTPViewController
-
-#pragma mark - UIViewController
-
-- (void)viewDidLoad {
-  self.title = l10n_util::GetNSString(IDS_NEW_TAB_TITLE);
-  self.view.backgroundColor = [UIColor whiteColor];
-
-  UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
-  [scrollView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
-                                   UIViewAutoresizingFlexibleHeight)];
-  scrollView.pagingEnabled = YES;
-  scrollView.showsHorizontalScrollIndicator = NO;
-  scrollView.showsVerticalScrollIndicator = NO;
-  scrollView.contentMode = UIViewContentModeScaleAspectFit;
-  scrollView.bounces = YES;
-  scrollView.scrollsToTop = NO;
-
-  NewTabPageBar* tabBar = [[NewTabPageBar alloc] initWithFrame:CGRectZero];
-  NewTabPageView* ntpView = [[NewTabPageView alloc] initWithFrame:CGRectZero
-                                                    andScrollView:scrollView
-                                                        andTabBar:tabBar];
-  ntpView.translatesAutoresizingMaskIntoConstraints = NO;
-  [self.view addSubview:ntpView];
-
-  [NSLayoutConstraint activateConstraints:@[
-    [ntpView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
-    [ntpView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
-    [ntpView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
-    [ntpView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
-  ]];
-
-  // PLACEHOLDER: this logic should move out of the UIVC.
-  NSString* mostVisited = l10n_util::GetNSString(IDS_IOS_NEW_TAB_MOST_VISITED);
-  NSString* bookmarks =
-      l10n_util::GetNSString(IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE);
-  NSString* openTabs = l10n_util::GetNSString(IDS_IOS_NEW_TAB_RECENT_TABS);
-
-  NSMutableArray* tabBarItems = [NSMutableArray array];
-
-  NewTabPageBarItem* mostVisitedItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:mostVisited
-                      identifier:NewTabPage::kMostVisitedPanel
-                           image:[UIImage imageNamed:@"ntp_mv_search"]];
-  NewTabPageBarItem* bookmarksItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:bookmarks
-                      identifier:NewTabPage::kBookmarksPanel
-                           image:[UIImage imageNamed:@"ntp_bookmarks"]];
-  [tabBarItems addObject:bookmarksItem];
-  [tabBarItems addObject:mostVisitedItem];
-
-  NewTabPageBarItem* openTabsItem = [NewTabPageBarItem
-      newTabPageBarItemWithTitle:openTabs
-                      identifier:NewTabPage::kOpenTabsPanel
-                           image:[UIImage imageNamed:@"ntp_opentabs"]];
-  [tabBarItems addObject:openTabsItem];
-  tabBar.items = tabBarItems;
-}
-
-@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_consumer.h b/ios/clean/chrome/browser/ui/ntp/ntp_consumer.h
new file mode 100644
index 0000000..f54ae11b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_consumer.h
@@ -0,0 +1,19 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_CONSUMER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_CONSUMER_H_
+
+#import <Foundation/Foundation.h>
+
+@class NewTabPageBarItem;
+
+@protocol NTPConsumer
+
+// Tells NTP what bar items to display.
+- (void)setBarItems:(NSArray<NewTabPageBarItem*>*)items;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_CONSUMER_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.h b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h
similarity index 67%
rename from ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.h
rename to ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h
index fa0ba00..b17e815 100644
--- a/ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.h
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_COORDINATOR_H_
-#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_COORDINATOR_H_
+#ifndef IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_COORDINATOR_H_
 
 #import <UIKit/UIKit.h>
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
@@ -13,4 +13,4 @@
 @interface NTPCoordinator : BrowserCoordinator
 @end
 
-#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NEW_TAB_PAGE_COORDINATOR_H_
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm
new file mode 100644
index 0000000..3ead5f6b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator.mm
@@ -0,0 +1,103 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h"
+
+#include "base/logging.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/clean/chrome/browser/ui/bookmarks/bookmarks_coordinator.h"
+#import "ios/clean/chrome/browser/ui/commands/ntp_commands.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_mediator.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h"
+#import "ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NTPCoordinator ()<NTPCommands>
+@property(nonatomic, strong) NTPMediator* mediator;
+@property(nonatomic, strong) NTPViewController* viewController;
+@end
+
+@implementation NTPCoordinator
+@synthesize mediator = _mediator;
+@synthesize viewController = _viewController;
+
+- (void)start {
+  self.viewController = [[NTPViewController alloc] init];
+  self.mediator = [[NTPMediator alloc] initWithConsumer:self.viewController];
+
+  CommandDispatcher* dispatcher = self.browser->dispatcher();
+  // NTPCommands
+  [dispatcher startDispatchingToTarget:self
+                           forSelector:@selector(showNTPHomePanel)];
+  [dispatcher startDispatchingToTarget:self
+                           forSelector:@selector(showNTPBookmarksPanel)];
+  [dispatcher startDispatchingToTarget:self
+                           forSelector:@selector(showNTPRecentTabsPanel)];
+  self.viewController.dispatcher = static_cast<id>(self.browser->dispatcher());
+  [super start];
+}
+
+- (void)stop {
+  [super stop];
+  // PLACEHOLDER: Stop child coordinators here for now. We might deal with this
+  // differently later on.
+  for (BrowserCoordinator* child in self.children) {
+    [child stop];
+  }
+  [self.browser->dispatcher() stopDispatchingToTarget:self];
+}
+
+- (void)childCoordinatorDidStart:(BrowserCoordinator*)coordinator {
+  if ([coordinator isKindOfClass:[NTPHomeCoordinator class]]) {
+    self.viewController.homeViewController = coordinator.viewController;
+
+  } else if ([coordinator isKindOfClass:[BookmarksCoordinator class]]) {
+    if (IsIPadIdiom()) {
+      self.viewController.bookmarksViewController = coordinator.viewController;
+    } else {
+      [self.viewController presentViewController:coordinator.viewController
+                                        animated:YES
+                                      completion:nil];
+    }
+
+  } else if ([coordinator isKindOfClass:[RecentTabsCoordinator class]]) {
+    if (IsIPadIdiom()) {
+      self.viewController.recentTabsViewController = coordinator.viewController;
+    } else {
+      [self.viewController presentViewController:coordinator.viewController
+                                        animated:YES
+                                      completion:nil];
+    }
+  }
+}
+
+#pragma mark - NTPCommands
+
+- (void)showNTPHomePanel {
+  NTPHomeCoordinator* panelCoordinator = [[NTPHomeCoordinator alloc] init];
+  [self addChildCoordinator:panelCoordinator];
+  [panelCoordinator start];
+}
+
+- (void)showNTPBookmarksPanel {
+  BookmarksCoordinator* panelCoordinator = [[BookmarksCoordinator alloc] init];
+  [self addChildCoordinator:panelCoordinator];
+  [panelCoordinator start];
+}
+
+- (void)showNTPRecentTabsPanel {
+  RecentTabsCoordinator* panelCoordinator =
+      [[RecentTabsCoordinator alloc] init];
+  [self addChildCoordinator:panelCoordinator];
+  [panelCoordinator start];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator_unittest.mm
new file mode 100644
index 0000000..4070689
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_coordinator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h
new file mode 100644
index 0000000..54f734cb
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h
@@ -0,0 +1,15 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// Coordinator that runs the chrome home panel of the NTP.
+@interface NTPHomeCoordinator : BrowserCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
new file mode 100644
index 0000000..47c09ee
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.mm
@@ -0,0 +1,42 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h"
+
+#import "ios/chrome/browser/ui/ntp/google_landing_controller.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NTPHomeCoordinator ()
+@property(nonatomic, strong) NTPHomeMediator* mediator;
+@property(nonatomic, strong) UIViewController* viewController;
+@property(nonatomic, strong) GoogleLandingController* wrapperController;
+@end
+
+@implementation NTPHomeCoordinator
+@synthesize mediator = _mediator;
+@synthesize viewController = _viewController;
+@synthesize wrapperController = _wrapperController;
+
+- (void)start {
+  // PLACEHOLDER: Re-using old view controllers for now.
+  self.mediator = [[NTPHomeMediator alloc] init];
+  self.mediator.dispatcher = static_cast<id>(self.browser->dispatcher());
+  self.wrapperController = [[GoogleLandingController alloc]
+          initWithLoader:self.mediator
+            browserState:self.browser->browser_state()
+                 focuser:self.mediator
+      webToolbarDelegate:nil
+                tabModel:nil];
+  self.viewController = [[UIViewController alloc] init];
+  self.viewController.view = [self.wrapperController view];
+  [super start];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator_unittest.mm
new file mode 100644
index 0000000..6014219
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h
new file mode 100644
index 0000000..e8688d6
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h
@@ -0,0 +1,24 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_MEDIATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_MEDIATOR_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/toolbar/web_toolbar_controller.h"
+#import "ios/chrome/browser/ui/url_loader.h"
+
+@protocol TabCommands;
+
+// A mediator object that sets an NTP home view controller's appeareance based
+// on various data sources.
+@interface NTPHomeMediator : NSObject<UrlLoader, OmniboxFocuser>
+
+// The dispatcher for this mediator.
+@property(nonatomic, weak) id<TabCommands> dispatcher;
+
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_HOME_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.mm
new file mode 100644
index 0000000..8aea01c2
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.mm
@@ -0,0 +1,69 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h"
+
+#import "ios/clean/chrome/browser/ui/commands/tab_commands.h"
+#import "ios/web/public/navigation_manager.h"
+#include "ui/base/page_transition_types.h"
+#include "url/gurl.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NTPHomeMediator ()
+@end
+
+@implementation NTPHomeMediator
+@synthesize dispatcher = _dispatcher;
+
+#pragma mark - UrlLoader
+
+- (void)loadURL:(const GURL&)url
+             referrer:(const web::Referrer&)referrer
+           transition:(ui::PageTransition)transition
+    rendererInitiated:(BOOL)rendererInitiated {
+  web::NavigationManager::WebLoadParams params(url);
+  params.transition_type = transition;
+  [self.dispatcher loadURL:params];
+}
+
+- (void)webPageOrderedOpen:(const GURL&)url
+                  referrer:(const web::Referrer&)referrer
+              inBackground:(BOOL)inBackground
+                  appendTo:(OpenPosition)appendTo {
+}
+
+- (void)webPageOrderedOpen:(const GURL&)url
+                  referrer:(const web::Referrer&)referrer
+               inIncognito:(BOOL)inIncognito
+              inBackground:(BOOL)inBackground
+                  appendTo:(OpenPosition)appendTo {
+}
+
+- (void)loadSessionTab:(const sessions::SessionTab*)sessionTab {
+}
+
+- (void)loadJavaScriptFromLocationBar:(NSString*)script {
+}
+
+#pragma mark - OmniboxFocuser
+
+- (void)focusOmnibox {
+}
+
+- (void)cancelOmniboxEdit {
+}
+
+- (void)focusFakebox {
+}
+
+- (void)onFakeboxBlur {
+}
+
+- (void)onFakeboxAnimationComplete {
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator_unittest.mm b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator_unittest.mm
new file mode 100644
index 0000000..567a667
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_home_mediator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_home_mediator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_mediator.h b/ios/clean/chrome/browser/ui/ntp/ntp_mediator.h
new file mode 100644
index 0000000..210f0a3b
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_mediator.h
@@ -0,0 +1,20 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_MEDIATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_MEDIATOR_H_
+
+#import <UIKit/UIKit.h>
+
+@protocol NTPConsumer;
+
+// A mediator object that sets an NTP view controller's appeareance based on
+// various data sources.
+@interface NTPMediator : NSObject
+- (instancetype)initWithConsumer:(id<NTPConsumer>)consumer
+    NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_MEDIATOR_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_mediator.mm b/ios/clean/chrome/browser/ui/ntp/ntp_mediator.mm
new file mode 100644
index 0000000..042540b2
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_mediator.mm
@@ -0,0 +1,66 @@
+
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_mediator.h"
+
+#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_consumer.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NTPMediator ()
+@property(nonatomic, strong) id<NTPConsumer> consumer;
+@end
+
+@implementation NTPMediator
+
+@synthesize consumer = _consumer;
+
+- (instancetype)initWithConsumer:(id<NTPConsumer>)consumer {
+  self = [super init];
+  if (self) {
+    _consumer = consumer;
+    [self setTabBarItems];
+  }
+  return self;
+}
+
+- (void)setTabBarItems {
+  NSString* mostVisited = l10n_util::GetNSString(IDS_IOS_NEW_TAB_MOST_VISITED);
+  NSString* bookmarks =
+      l10n_util::GetNSString(IDS_IOS_NEW_TAB_BOOKMARKS_PAGE_TITLE_MOBILE);
+  NSString* recentTabs = l10n_util::GetNSString(IDS_IOS_NEW_TAB_RECENT_TABS);
+
+  NSMutableArray* tabBarItems = [NSMutableArray array];
+
+  NewTabPageBarItem* mostVisitedItem = [NewTabPageBarItem
+      newTabPageBarItemWithTitle:mostVisited
+                      identifier:NewTabPage::kMostVisitedPanel
+                           image:[UIImage imageNamed:@"ntp_mv_search"]];
+  NewTabPageBarItem* bookmarksItem = [NewTabPageBarItem
+      newTabPageBarItemWithTitle:bookmarks
+                      identifier:NewTabPage::kBookmarksPanel
+                           image:[UIImage imageNamed:@"ntp_bookmarks"]];
+  [tabBarItems addObject:bookmarksItem];
+  if (IsIPadIdiom()) {
+    [tabBarItems addObject:mostVisitedItem];
+  }
+
+  NewTabPageBarItem* recentTabsItem = [NewTabPageBarItem
+      newTabPageBarItemWithTitle:recentTabs
+                      identifier:NewTabPage::kOpenTabsPanel
+                           image:[UIImage imageNamed:@"ntp_opentabs"]];
+  [tabBarItems addObject:recentTabsItem];
+
+  [self.consumer setBarItems:[tabBarItems copy]];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_mediator_unittest.mm b/ios/clean/chrome/browser/ui/ntp/ntp_mediator_unittest.mm
new file mode 100644
index 0000000..a80d88db
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_mediator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_mediator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h
new file mode 100644
index 0000000..a80885d4
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h
@@ -0,0 +1,32 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_VIEW_CONTROLLER_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_VIEW_CONTROLLER_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_consumer.h"
+
+@protocol NTPCommands;
+
+// View controller that displays a new tab page.
+@interface NTPViewController : UIViewController<NTPConsumer>
+
+// The dispatcher for this view controller
+@property(nonatomic, weak) id<NTPCommands> dispatcher;
+
+// Setting this property adds a Chrome Home panel.
+@property(nonatomic, strong) UIViewController* homeViewController;
+
+// Setting this property adds a bookmarks panel on iPad or present a bookmarks
+// panel on iPhone.
+@property(nonatomic, strong) UIViewController* bookmarksViewController;
+
+// Setting this property adds a recent tabs panel on iPad or present a bookmarks
+// panel on iPhone.
+@property(nonatomic, strong) UIViewController* recentTabsViewController;
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_NTP_NTP_VIEW_CONTROLLER_H_
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.mm b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.mm
new file mode 100644
index 0000000..8493d29
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller.mm
@@ -0,0 +1,194 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h"
+
+#import "base/ios/crb_protocol_observers.h"
+#include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_bar.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_bar_item.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_controller.h"
+#import "ios/chrome/browser/ui/ntp/new_tab_page_view.h"
+#import "ios/chrome/browser/ui/rtl_geometry.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/clean/chrome/browser/ui/commands/ntp_commands.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface NTPViewController ()<UIScrollViewDelegate, NewTabPageBarDelegate>
+@property(nonatomic, strong) NewTabPageView* NTPView;
+@property(nonatomic, strong) NSArray* tabBarItems;
+@end
+
+@implementation NTPViewController
+
+@synthesize bookmarksViewController = _bookmarksViewController;
+@synthesize dispatcher = _dispatcher;
+@synthesize homeViewController = _homeViewController;
+@synthesize NTPView = _NTPView;
+@synthesize recentTabsViewController = _recentTabsViewController;
+@synthesize tabBarItems = _tabBarItems;
+
+#pragma mark - UIViewController
+
+- (void)viewDidLoad {
+  [super viewDidLoad];
+  self.title = l10n_util::GetNSString(IDS_NEW_TAB_TITLE);
+  self.view.backgroundColor = [UIColor whiteColor];
+
+  UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
+  [scrollView setAutoresizingMask:(UIViewAutoresizingFlexibleWidth |
+                                   UIViewAutoresizingFlexibleHeight)];
+  scrollView.pagingEnabled = YES;
+  scrollView.showsHorizontalScrollIndicator = NO;
+  scrollView.showsVerticalScrollIndicator = NO;
+  scrollView.contentMode = UIViewContentModeScaleAspectFit;
+  scrollView.bounces = YES;
+  scrollView.scrollsToTop = NO;
+  scrollView.delegate = self;
+
+  NewTabPageBar* tabBar = [[NewTabPageBar alloc] initWithFrame:CGRectZero];
+  tabBar.delegate = self;
+  self.NTPView = [[NewTabPageView alloc] initWithFrame:CGRectZero
+                                         andScrollView:scrollView
+                                             andTabBar:tabBar];
+  self.NTPView.translatesAutoresizingMaskIntoConstraints = NO;
+  [self.view addSubview:self.NTPView];
+
+  [NSLayoutConstraint activateConstraints:@[
+    [self.NTPView.topAnchor constraintEqualToAnchor:self.view.topAnchor],
+    [self.NTPView.leadingAnchor
+        constraintEqualToAnchor:self.view.leadingAnchor],
+    [self.NTPView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
+    [self.NTPView.trailingAnchor
+        constraintEqualToAnchor:self.view.trailingAnchor],
+  ]];
+
+  DCHECK(self.tabBarItems);
+  self.NTPView.tabBar.items = self.tabBarItems;
+}
+
+- (void)viewDidLayoutSubviews {
+  [super viewDidLayoutSubviews];
+
+  if (!self.homeViewController) {
+    // Make sure scrollView is properly set up.
+    [self.NTPView layoutIfNeeded];
+    // PLACEHOLDER: This should come from the mediator.
+    if (IsIPadIdiom()) {
+      CGRect itemFrame = [self.NTPView panelFrameForItemAtIndex:1];
+      CGPoint point = CGPointMake(CGRectGetMinX(itemFrame), 0);
+      [self.NTPView.scrollView setContentOffset:point animated:NO];
+    } else {
+      [self.dispatcher showNTPHomePanel];
+    }
+  }
+}
+
+- (void)setHomeViewController:(UIViewController*)controller {
+  _homeViewController = controller;
+  if (IsIPadIdiom()) {
+    controller.view.frame = [self.NTPView panelFrameForItemAtIndex:1];
+  } else {
+    controller.view.frame = [self.NTPView panelFrameForItemAtIndex:0];
+  }
+  NewTabPageBarItem* item = self.NTPView.tabBar.items[1];
+  item.view = controller.view;
+  [self addControllerToScrollView:controller];
+}
+
+- (void)setBookmarksViewController:(UIViewController*)controller {
+  _bookmarksViewController = controller;
+  controller.view.frame = [self.NTPView panelFrameForItemAtIndex:0];
+  NewTabPageBarItem* item = self.NTPView.tabBar.items[0];
+  item.view = controller.view;
+  [self addControllerToScrollView:controller];
+}
+
+- (void)setRecentTabsViewController:(UIViewController*)controller {
+  _recentTabsViewController = controller;
+  controller.view.frame = [self.NTPView panelFrameForItemAtIndex:2];
+  NewTabPageBarItem* item = self.NTPView.tabBar.items[2];
+  item.view = controller.view;
+  [self addControllerToScrollView:_recentTabsViewController];
+}
+
+#pragma mark - Private
+
+// Add the view controller to vc hierarchy and add the view to the scrollview.
+- (void)addControllerToScrollView:(UIViewController*)controller {
+  [self addChildViewController:controller];
+  [self.NTPView.scrollView addSubview:controller.view];
+  [controller didMoveToParentViewController:self];
+}
+
+#pragma mark - NTPConsumer
+
+- (void)setBarItems:(NSArray*)items {
+  self.tabBarItems = items;
+}
+
+#pragma mark - UIScrollViewDelegate
+
+// TODO(crbug.com/709086) Move UIScrollViewDelegate to shared object.
+- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
+  // Position is used to track the exact X position of the scroll view, whereas
+  // index is rounded to the panel that is most visible.
+  CGFloat panelWidth =
+      scrollView.contentSize.width / self.NTPView.tabBar.items.count;
+  LayoutOffset position =
+      LeadingContentOffsetForScrollView(scrollView) / panelWidth;
+  NSUInteger index = round(position);
+
+  // |scrollView| can be out of range when the frame changes.
+  if (index >= self.NTPView.tabBar.items.count)
+    return;
+
+  NewTabPageBarItem* item = self.NTPView.tabBar.items[index];
+  if (item.identifier == NewTabPage::kBookmarksPanel &&
+      !self.bookmarksViewController)
+    [self.dispatcher showNTPBookmarksPanel];
+  else if (item.identifier == NewTabPage::kMostVisitedPanel &&
+           !self.homeViewController)
+    [self.dispatcher showNTPHomePanel];
+  else if (item.identifier == NewTabPage::kOpenTabsPanel &&
+           !self.recentTabsViewController)
+    [self.dispatcher showNTPRecentTabsPanel];
+
+  // If index changed, follow same path as if a tab bar item was pressed.  When
+  // |index| == |position|, the panel is completely in view.
+  if (index == position && self.NTPView.tabBar.selectedIndex != index) {
+    NewTabPageBarItem* item = [self.NTPView.tabBar.items objectAtIndex:index];
+    DCHECK(item);
+    self.NTPView.tabBar.selectedIndex = index;
+  }
+  [self.NTPView.tabBar updateColorsForScrollView:scrollView];
+
+  self.NTPView.tabBar.overlayPercentage =
+      scrollView.contentOffset.x / scrollView.contentSize.width;
+}
+
+#pragma mark - NewTabPageBarDelegate
+
+- (void)newTabBarItemDidChange:(NewTabPageBarItem*)selectedItem
+                   changePanel:(BOOL)changePanel {
+  if (IsIPadIdiom()) {
+    NSUInteger index = [self.NTPView.tabBar.items indexOfObject:selectedItem];
+    CGRect itemFrame = [self.NTPView panelFrameForItemAtIndex:index];
+    CGPoint point = CGPointMake(CGRectGetMinX(itemFrame), 0);
+    [self.NTPView.scrollView setContentOffset:point animated:YES];
+  } else {
+    if (selectedItem.identifier == NewTabPage::kBookmarksPanel) {
+      [self.dispatcher showNTPBookmarksPanel];
+    } else if (selectedItem.identifier == NewTabPage::kOpenTabsPanel) {
+      [self.dispatcher showNTPRecentTabsPanel];
+    }
+  }
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/ntp/ntp_view_controller_unittest.mm b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller_unittest.mm
new file mode 100644
index 0000000..3bcd2e93
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/ntp/ntp_view_controller_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/ntp/ntp_view_controller.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn b/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn
new file mode 100644
index 0000000..0ebc0e4
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/recent_tabs/BUILD.gn
@@ -0,0 +1,32 @@
+# Copyright 2017 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.
+
+source_set("recent_tabs") {
+  sources = [
+    "recent_tabs_coordinator.h",
+    "recent_tabs_coordinator.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    "//ios/chrome/browser/ui",
+    "//ios/chrome/browser/ui/ntp/recent_tabs",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/coordinators",
+  ]
+}
+
+source_set("unit_tests") {
+  testonly = true
+  sources = [
+    "recent_tabs_coordinator_unittest.mm",
+  ]
+
+  deps = [
+    ":recent_tabs",
+    "//testing/gtest",
+  ]
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h
new file mode 100644
index 0000000..cdf85ae4
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h
@@ -0,0 +1,16 @@
+// Copyright 2017 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 IOS_CLEAN_CHROME_BROWSER_UI_RECENT_TABS_RECENT_TABS_COORDINATOR_H_
+#define IOS_CLEAN_CHROME_BROWSER_UI_RECENT_TABS_RECENT_TABS_COORDINATOR_H_
+
+#import <UIKit/UIKit.h>
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator.h"
+
+// A coordinator for the recent tabs UI, which can be presented modally on its
+// own or inside the NTP.
+@interface RecentTabsCoordinator : BrowserCoordinator
+@end
+
+#endif  // IOS_CLEAN_CHROME_BROWSER_UI_RECENT_TABS_RECENT_TABS_COORDINATOR_H_
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
new file mode 100644
index 0000000..539bee2
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.mm
@@ -0,0 +1,43 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
+
+#import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_controller.h"
+#import "ios/chrome/browser/ui/ntp/recent_tabs/recent_tabs_panel_view_controller.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface RecentTabsCoordinator ()
+@property(nonatomic, strong) UIViewController* viewController;
+@property(nonatomic, strong) RecentTabsPanelController* wrapperController;
+@end
+
+@implementation RecentTabsCoordinator
+@synthesize viewController = _viewController;
+@synthesize wrapperController = _wrapperController;
+
+- (void)start {
+  // HACK: Re-using old view controllers for now.
+  if (!IsIPadIdiom()) {
+    self.viewController = [RecentTabsPanelViewController
+        controllerToPresentForBrowserState:self.browser->browser_state()
+                                    loader:nil];
+    self.viewController.modalPresentationStyle = UIModalPresentationFormSheet;
+    self.viewController.modalPresentationCapturesStatusBarAppearance = YES;
+  } else {
+    self.wrapperController = [[RecentTabsPanelController alloc]
+        initWithLoader:nil
+          browserState:self.browser->browser_state()];
+    self.viewController = [self.wrapperController viewController];
+  }
+  [super start];
+}
+
+@end
diff --git a/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm
new file mode 100644
index 0000000..c0c2ba7
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator_unittest.mm
@@ -0,0 +1,9 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/recent_tabs/recent_tabs_coordinator.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
diff --git a/ios/clean/chrome/browser/ui/tab/BUILD.gn b/ios/clean/chrome/browser/ui/tab/BUILD.gn
index d97375c..63ca3f1 100644
--- a/ios/clean/chrome/browser/ui/tab/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab/BUILD.gn
@@ -13,12 +13,16 @@
   deps = [
     ":tab_ui",
     "//base",
+    "//ios/chrome/browser",
     "//ios/clean/chrome/browser/ui/actions",
     "//ios/clean/chrome/browser/ui/animators",
+    "//ios/clean/chrome/browser/ui/commands",
     "//ios/clean/chrome/browser/ui/ntp",
     "//ios/clean/chrome/browser/ui/tab_strip",
     "//ios/clean/chrome/browser/ui/toolbar",
     "//ios/clean/chrome/browser/ui/web_contents",
+    "//ios/shared/chrome/browser/ui/browser_list",
+    "//ios/shared/chrome/browser/ui/commands",
     "//ios/shared/chrome/browser/ui/coordinators",
     "//ios/web",
   ]
diff --git a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
index faf15db3..8fe33ea 100644
--- a/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
+++ b/ios/clean/chrome/browser/ui/tab/tab_coordinator.mm
@@ -8,12 +8,16 @@
 
 #include "base/mac/foundation_util.h"
 #include "base/memory/ptr_util.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/clean/chrome/browser/ui/animators/zoom_transition_animator.h"
-#import "ios/clean/chrome/browser/ui/ntp/new_tab_page_coordinator.h"
+#import "ios/clean/chrome/browser/ui/commands/tab_commands.h"
+#import "ios/clean/chrome/browser/ui/ntp/ntp_coordinator.h"
 #import "ios/clean/chrome/browser/ui/tab/tab_container_view_controller.h"
 #import "ios/clean/chrome/browser/ui/tab_strip/tab_strip_coordinator.h"
 #import "ios/clean/chrome/browser/ui/toolbar/toolbar_coordinator.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_coordinator.h"
+#import "ios/shared/chrome/browser/ui/browser_list/browser.h"
+#import "ios/shared/chrome/browser/ui/commands/command_dispatcher.h"
 #import "ios/shared/chrome/browser/ui/coordinators/browser_coordinator+internal.h"
 #import "ios/web/public/web_state/web_state.h"
 #import "ios/web/public/web_state/web_state_observer_bridge.h"
@@ -29,8 +33,11 @@
 }  // namespace
 
 @interface TabCoordinator ()<CRWWebStateObserver,
+                             TabCommands,
                              UIViewControllerTransitioningDelegate>
 @property(nonatomic, strong) TabContainerViewController* viewController;
+@property(nonatomic, weak) NTPCoordinator* ntpCoordinator;
+@property(nonatomic, weak) WebCoordinator* webCoordinator;
 @end
 
 @implementation TabCoordinator {
@@ -40,6 +47,8 @@
 @synthesize presentationKey = _presentationKey;
 @synthesize viewController = _viewController;
 @synthesize webState = _webState;
+@synthesize webCoordinator = _webCoordinator;
+@synthesize ntpCoordinator = _ntpCoordinator;
 
 #pragma mark - BrowserCoordinator
 
@@ -50,10 +59,15 @@
   _webStateObserver =
       base::MakeUnique<web::WebStateObserverBridge>(self.webState, self);
 
+  CommandDispatcher* dispatcher = self.browser->dispatcher();
+  // TabCommands
+  [dispatcher startDispatchingToTarget:self forSelector:@selector(loadURL:)];
+
   WebCoordinator* webCoordinator = [[WebCoordinator alloc] init];
   webCoordinator.webState = self.webState;
   [self addChildCoordinator:webCoordinator];
   [webCoordinator start];
+  self.webCoordinator = webCoordinator;
 
   ToolbarCoordinator* toolbarCoordinator = [[ToolbarCoordinator alloc] init];
   toolbarCoordinator.webState = self.webState;
@@ -64,6 +78,15 @@
   [self addChildCoordinator:tabStripCoordinator];
   [tabStripCoordinator start];
 
+  // PLACEHOLDER: Fix the order of events here. The ntpCoordinator was already
+  // created above when |webCoordinator.webState = self.webState;| triggers
+  // a load event, but then the webCoordinator stomps on the
+  // contentViewController when it starts afterwards.
+  if (self.webState->GetLastCommittedURL() == GURL(kChromeUINewTabURL)) {
+    self.viewController.contentViewController =
+        self.ntpCoordinator.viewController;
+  }
+
   [super start];
 }
 
@@ -75,6 +98,7 @@
     [child stop];
   }
   _webStateObserver.reset();
+  [self.browser->dispatcher() stopDispatchingToTarget:self];
 }
 
 - (void)childCoordinatorDidStart:(BrowserCoordinator*)childCoordinator {
@@ -89,6 +113,10 @@
   }
 }
 
+- (void)childCoordinatorWillStop:(BrowserCoordinator*)childCoordinator {
+  self.viewController.contentViewController = nil;
+}
+
 - (BOOL)canAddOverlayCoordinator:(BrowserCoordinator*)overlayCoordinator {
   // This coordinator will always accept overlay coordinators.
   return YES;
@@ -112,10 +140,21 @@
 // optimization in some equivalent to loadURL.
 - (void)webState:(web::WebState*)webState
     didCommitNavigationWithDetails:(const web::LoadCommittedDetails&)details {
-  if (webState->GetLastCommittedURL() == GURL("chrome://newtab/")) {
+  if (webState->GetLastCommittedURL() == GURL(kChromeUINewTabURL)) {
     NTPCoordinator* ntpCoordinator = [[NTPCoordinator alloc] init];
     [self addChildCoordinator:ntpCoordinator];
     [ntpCoordinator start];
+    self.ntpCoordinator = ntpCoordinator;
+  }
+}
+
+- (void)webState:(web::WebState*)webState
+    didStartProvisionalNavigationForURL:(const GURL&)URL {
+  if (self.ntpCoordinator) {
+    [self.ntpCoordinator stop];
+    [self removeChildCoordinator:self.ntpCoordinator];
+    self.viewController.contentViewController =
+        self.webCoordinator.viewController;
   }
 }
 
@@ -141,4 +180,10 @@
   return animator;
 }
 
+#pragma mark - TabCommands
+
+- (void)loadURL:(web::NavigationManager::WebLoadParams)params {
+  self.webState->GetNavigationManager()->LoadURLWithParams(params);
+}
+
 @end
diff --git a/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn b/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
index ec4ad7d..ef9472e8 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/tab_grid/BUILD.gn
@@ -65,3 +65,26 @@
     "//ui/base",
   ]
 }
+
+source_set("unit_tests") {
+  testonly = true
+
+  sources = [
+    "tab_grid_mediator_unittest.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":tab_grid",
+    ":tab_grid_ui",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/test/base",
+    "//ios/shared/chrome/browser/tabs",
+    "//ios/shared/chrome/browser/tabs:test_support",
+    "//ios/web:test_support",
+    "//testing/gtest",
+    "//third_party/ocmock",
+  ]
+}
diff --git a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.mm b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
index 6d17f3f..87ec7f34 100644
--- a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
+++ b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.mm
@@ -4,9 +4,7 @@
 
 #import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.h"
 
-#include "base/memory/ptr_util.h"
-#include "base/strings/sys_string_conversions.h"
-#import "ios/clean/chrome/browser/ui/tab_collection/tab_collection_consumer.h"
+#import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_consumer.h"
 #import "ios/shared/chrome/browser/tabs/web_state_list.h"
 #import "ios/shared/chrome/browser/tabs/web_state_list_observer_bridge.h"
 #include "ios/web/public/web_state/web_state.h"
diff --git a/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
new file mode 100644
index 0000000..ef8e287
--- /dev/null
+++ b/ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator_unittest.mm
@@ -0,0 +1,74 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_mediator.h"
+
+#include "base/memory/ptr_util.h"
+#import "ios/clean/chrome/browser/ui/tab_grid/tab_grid_consumer.h"
+#include "ios/shared/chrome/browser/tabs/fake_web_state_list_delegate.h"
+#include "ios/shared/chrome/browser/tabs/web_state_list.h"
+#import "ios/shared/chrome/browser/tabs/web_state_list_observer_bridge.h"
+#import "ios/web/public/test/fakes/test_navigation_manager.h"
+#import "ios/web/public/test/fakes/test_web_state.h"
+#include "testing/platform_test.h"
+#import "third_party/ocmock/OCMock/OCMock.h"
+#include "third_party/ocmock/gtest_support.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+class TabGridMediatorTest : public PlatformTest {
+ public:
+  TabGridMediatorTest() {
+    SetUpEmptyWebStateList();
+    mediator_ = [[TabGridMediator alloc] init];
+    mediator_.webStateList = web_state_list_.get();
+  }
+  ~TabGridMediatorTest() override { [mediator_ disconnect]; }
+
+ protected:
+  void SetUpEmptyWebStateList() {
+    web_state_list_ = base::MakeUnique<WebStateList>(&web_state_list_delegate_);
+  }
+
+  void InsertWebStateAt(int index) {
+    auto web_state = base::MakeUnique<web::TestWebState>();
+    web_state_list_->InsertWebState(index, std::move(web_state));
+  }
+
+  void SetConsumer() {
+    consumer_ = [OCMockObject mockForProtocol:@protocol(TabGridConsumer)];
+    mediator_.consumer = consumer_;
+  }
+
+  TabGridMediator* mediator_;
+  std::unique_ptr<WebStateList> web_state_list_;
+  FakeWebStateListDelegate web_state_list_delegate_;
+  id consumer_;
+};
+
+// Tests that the noTabsOverlay is removed when a web state is inserted when
+// the list is empty.
+TEST_F(TabGridMediatorTest, TestRemoveNoTabsOverlay) {
+  SetConsumer();
+  [[consumer_ expect] insertItemAtIndex:0];
+  [[consumer_ expect] removeNoTabsOverlay];
+  InsertWebStateAt(0);
+  EXPECT_OCMOCK_VERIFY(consumer_);
+}
+
+// Tests that the noTabsOverlay is added when the web state list becomes empty.
+TEST_F(TabGridMediatorTest, TestAddNoTabsOverlay) {
+  InsertWebStateAt(0);
+  SetConsumer();
+  [[consumer_ expect] deleteItemAtIndex:0];
+  [[consumer_ expect] addNoTabsOverlay];
+  web_state_list_->CloseWebStateAt(0);
+  EXPECT_OCMOCK_VERIFY(consumer_);
+}
+
+}  // namespace
diff --git a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
index a8cfe05f..0eb10e7 100644
--- a/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
+++ b/ios/clean/chrome/browser/ui/web_contents/BUILD.gn
@@ -14,6 +14,7 @@
 
   deps = [
     ":web_contents_ui",
+    "//ios/chrome/browser",
     "//ios/clean/chrome/browser/ui/context_menu",
     "//ios/shared/chrome/browser/tabs",
     "//ios/shared/chrome/browser/ui/browser_list",
diff --git a/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.mm b/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.mm
index 9c2bc7fb..a4596fbc 100644
--- a/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.mm
+++ b/ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.mm
@@ -5,6 +5,7 @@
 #import "ios/clean/chrome/browser/ui/web_contents/web_contents_mediator.h"
 
 #include "base/memory/ptr_util.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
 #import "ios/clean/chrome/browser/ui/web_contents/web_contents_consumer.h"
 #import "ios/shared/chrome/browser/tabs/web_state_list.h"
 #import "ios/shared/chrome/browser/tabs/web_state_list_observer_bridge.h"
@@ -102,8 +103,7 @@
 // PLACEHOLDER: This navigates the page since the omnibox is not yet hooked up.
 - (void)navigateToDefaultPage:(web::WebState*)webState {
   if (!webState->GetNavigationManager()->GetItemCount()) {
-    web::NavigationManager::WebLoadParams params(
-        GURL("https://dev.chromium.org/"));
+    web::NavigationManager::WebLoadParams params((GURL(kChromeUINewTabURL)));
     params.transition_type = ui::PAGE_TRANSITION_TYPED;
     webState->GetNavigationManager()->LoadURLWithParams(params);
   }
diff --git a/ios/clean/chrome/test/BUILD.gn b/ios/clean/chrome/test/BUILD.gn
index 860986f..4c59abc 100644
--- a/ios/clean/chrome/test/BUILD.gn
+++ b/ios/clean/chrome/test/BUILD.gn
@@ -19,9 +19,13 @@
     ios_packed_resources_target,
 
     # Add unit_tests target here.
+    "//ios/clean/chrome/browser/ui/bookmarks:unit_tests",
     "//ios/clean/chrome/browser/ui/context_menu:unit_tests",
+    "//ios/clean/chrome/browser/ui/ntp:unit_tests",
     "//ios/clean/chrome/browser/ui/omnibox:unit_tests",
+    "//ios/clean/chrome/browser/ui/recent_tabs:unit_tests",
     "//ios/clean/chrome/browser/ui/tab_collection:unit_tests",
+    "//ios/clean/chrome/browser/ui/tab_grid:unit_tests",
     "//ios/clean/chrome/browser/ui/web_contents:unit_tests",
   ]
 }
diff --git a/ios/web/navigation/crw_session_controller.h b/ios/web/navigation/crw_session_controller.h
index 26fb183..4ed7a98 100644
--- a/ios/web/navigation/crw_session_controller.h
+++ b/ios/web/navigation/crw_session_controller.h
@@ -9,6 +9,7 @@
 #include <vector>
 
 #import "ios/web/navigation/navigation_item_impl_list.h"
+#import "ios/web/public/navigation_manager.h"
 #include "ui/base/page_transition_types.h"
 #include "url/gurl.h"
 
@@ -77,14 +78,17 @@
 // Sets the corresponding BrowserState.
 - (void)setBrowserState:(web::BrowserState*)browserState;
 
-// Add a new item with the given url, referrer, and navigation type, making it
-// the current item. If pending item is the same as current item, this does
-// nothing. |referrer| may be nil if there isn't one. The item starts out as
-// pending, and will be lost unless |-commitPendingItem| is called.
+// Add a new item with the given url, referrer, navigation type and user agent
+// override option, making it the current item. If pending item is the same as
+// current item, this does nothing. |referrer| may be nil if there isn't one.
+// The item starts out as pending, and will be lost unless |-commitPendingItem|
+// is called.
 - (void)addPendingItem:(const GURL&)url
-              referrer:(const web::Referrer&)referrer
-            transition:(ui::PageTransition)type
-        initiationType:(web::NavigationInitiationType)initiationType;
+                   referrer:(const web::Referrer&)referrer
+                 transition:(ui::PageTransition)type
+             initiationType:(web::NavigationInitiationType)initiationType
+    userAgentOverrideOption:(web::NavigationManager::UserAgentOverrideOption)
+                                userAgentOverrideOption;
 
 // Updates the URL of the yet to be committed pending item. Useful for page
 // redirects. Does nothing if there is no pending item.
diff --git a/ios/web/navigation/crw_session_controller.mm b/ios/web/navigation/crw_session_controller.mm
index c867f5e9..2c29cf11 100644
--- a/ios/web/navigation/crw_session_controller.mm
+++ b/ios/web/navigation/crw_session_controller.mm
@@ -80,6 +80,21 @@
 // |index| in |items| has ui::PAGE_TRANSITION_IS_REDIRECT_MASK.
 - (BOOL)isRedirectTransitionForItemAtIndex:(size_t)index;
 
+// Should create a new pending item if the new pending item is not a duplicate
+// of the last added or commited item. Returns YES if one of the following rules
+// apply:
+// 1. There is no last added or committed item.
+// 2. The new item has different url from the last added or commited item.
+// 3. Url is the same, but the new item is a form submission resulted from the
+//    last added or committed item.
+// 4. Url is the same, but new item is a reload with different user agent type
+//    resulted from last added or commited item.
+- (BOOL)shouldCreatePendingItemWithURL:(const GURL&)URL
+                            transition:(ui::PageTransition)transition
+               userAgentOverrideOption:
+                   (web::NavigationManager::UserAgentOverrideOption)
+                       userAgentOverrideOption;
+
 @end
 
 @implementation CRWSessionController
@@ -268,9 +283,11 @@
 }
 
 - (void)addPendingItem:(const GURL&)url
-              referrer:(const web::Referrer&)ref
-            transition:(ui::PageTransition)trans
-        initiationType:(web::NavigationInitiationType)initiationType {
+                   referrer:(const web::Referrer&)ref
+                 transition:(ui::PageTransition)trans
+             initiationType:(web::NavigationInitiationType)initiationType
+    userAgentOverrideOption:(web::NavigationManager::UserAgentOverrideOption)
+                                userAgentOverrideOption {
   // Server side redirects are handled by updating existing pending item instead
   // of adding a new item.
   DCHECK((trans & ui::PAGE_TRANSITION_SERVER_REDIRECT) == 0);
@@ -278,35 +295,16 @@
   [self discardTransientItem];
   self.pendingItemIndex = -1;
 
-  // Don't create a new item if it's already the same as the current item,
-  // allowing this routine to be called multiple times in a row without issue.
-  // Note: CRWSessionController currently has the responsibility to distinguish
-  // between new navigations and history stack navigation, hence the inclusion
-  // of specific transiton type logic here, in order to make it reliable with
-  // real-world observed behavior.
-  // TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere
-  // in the web layer so that this hack can be removed.
-  // Remove the workaround code from -presentSafeBrowsingWarningForResource:.
-  web::NavigationItemImpl* currentItem = self.currentItem;
-  if (currentItem) {
-    BOOL hasSameURL = currentItem->GetURL() == url;
-    BOOL isPendingTransitionFormSubmit =
-        PageTransitionCoreTypeIs(trans, ui::PAGE_TRANSITION_FORM_SUBMIT);
-    BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs(
-        currentItem->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT);
-    BOOL shouldCreatePendingItem =
-        !hasSameURL ||
-        (isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit);
-
-    if (!shouldCreatePendingItem) {
-      // Send the notification anyway, to preserve old behavior. It's unknown
-      // whether anything currently relies on this, but since both this whole
-      // hack and the content facade will both be going away, it's not worth
-      // trying to unwind.
-      if (_navigationManager && _navigationManager->GetFacadeDelegate())
-        _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
-      return;
-    }
+  if (![self shouldCreatePendingItemWithURL:url
+                                 transition:trans
+                    userAgentOverrideOption:userAgentOverrideOption]) {
+    // Send the notification anyway, to preserve old behavior. It's unknown
+    // whether anything currently relies on this, but since both this whole
+    // hack and the content facade will both be going away, it's not worth
+    // trying to unwind.
+    if (_navigationManager && _navigationManager->GetFacadeDelegate())
+      _navigationManager->GetFacadeDelegate()->OnNavigationItemPending();
+    return;
   }
 
   _pendingItem = [self itemWithURL:url
@@ -319,6 +317,64 @@
   DCHECK_EQ(-1, self.pendingItemIndex);
 }
 
+- (BOOL)shouldCreatePendingItemWithURL:(const GURL&)URL
+                            transition:(ui::PageTransition)transition
+               userAgentOverrideOption:
+                   (web::NavigationManager::UserAgentOverrideOption)
+                       userAgentOverrideOption {
+  // Note: CRWSessionController currently has the responsibility to distinguish
+  // between new navigations and history stack navigation, hence the inclusion
+  // of specific transiton type logic here, in order to make it reliable with
+  // real-world observed behavior.
+  // TODO(crbug.com/676129): Fix the way changes are detected/reported elsewhere
+  // in the web layer so that this hack can be removed.
+  // Remove the workaround code from -presentSafeBrowsingWarningForResource:.
+  web::NavigationItemImpl* currentItem = self.currentItem;
+  if (!currentItem)
+    return YES;
+
+  // User agent override option should always be different from the user agent
+  // type of the pending item, or the last committed item if pending doesn't
+  // exist.
+  DCHECK(userAgentOverrideOption !=
+             web::NavigationManager::UserAgentOverrideOption::DESKTOP ||
+         currentItem->GetUserAgentType() != web::UserAgentType::DESKTOP);
+  DCHECK(userAgentOverrideOption !=
+             web::NavigationManager::UserAgentOverrideOption::MOBILE ||
+         currentItem->GetUserAgentType() != web::UserAgentType::MOBILE);
+
+  BOOL hasSameURL = self.currentItem->GetURL() == URL;
+  if (!hasSameURL) {
+    // Different url indicates that it's not a duplicate item.
+    return YES;
+  }
+
+  BOOL isPendingTransitionFormSubmit =
+      PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_FORM_SUBMIT);
+  BOOL isCurrentTransitionFormSubmit = PageTransitionCoreTypeIs(
+      currentItem->GetTransitionType(), ui::PAGE_TRANSITION_FORM_SUBMIT);
+  if (isPendingTransitionFormSubmit && !isCurrentTransitionFormSubmit) {
+    // |isPendingTransitionFormSubmit| indicates that the new item is a form
+    // submission resulted from the last added or commited item, and
+    // |!isCurrentTransitionFormSubmit| shows that the form submission is not
+    // counted multiple times.
+    return YES;
+  }
+
+  BOOL isPendingTransitionReload =
+      PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_RELOAD);
+  BOOL isInheritingUserAgentType =
+      userAgentOverrideOption ==
+      web::NavigationManager::UserAgentOverrideOption::INHERIT;
+  if (isPendingTransitionReload && !isInheritingUserAgentType) {
+    // Overriding user agent type to MOBILE or DESKTOP indicates that the new
+    // new item is a reload with different user agent type.
+    return YES;
+  }
+
+  return NO;
+}
+
 - (void)updatePendingItem:(const GURL&)url {
   // If there is no pending item, navigation is probably happening within the
   // session history. Don't modify the item list.
diff --git a/ios/web/navigation/crw_session_controller_unittest.mm b/ios/web/navigation/crw_session_controller_unittest.mm
index 2079ced..6e7eb988 100644
--- a/ios/web/navigation/crw_session_controller_unittest.mm
+++ b/ios/web/navigation/crw_session_controller_unittest.mm
@@ -23,6 +23,8 @@
 #import "testing/gtest_mac.h"
 #include "testing/platform_test.h"
 
+using UserAgentOverrideOption = web::NavigationManager::UserAgentOverrideOption;
+
 @interface CRWSessionController (Testing)
 - (const GURL&)URLForItemAtIndex:(size_t)index;
 - (const GURL&)currentURL;
@@ -66,10 +68,11 @@
 // Tests session controller state after setting a pending index.
 TEST_F(CRWSessionControllerTest, SetPendingIndex) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
@@ -81,10 +84,11 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   EXPECT_TRUE([session_controller_ items].empty());
   EXPECT_EQ(
@@ -94,17 +98,19 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemWithCommittedItems) {
   [session_controller_
-      addPendingItem:GURL("http://www.committed.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.committed.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
   EXPECT_EQ(GURL("http://www.committed.url.com/"),
@@ -117,16 +123,18 @@
 // Tests that adding a pending item resets pending item index.
 TEST_F(CRWSessionControllerTest, addPendingItemWithExistingPendingItemIndex) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Set 0 as pending item index.
@@ -137,10 +145,11 @@
 
   // Add a pending item, which should drop pending navigation index.
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   EXPECT_EQ(GURL("http://www.example.com/1"),
             [session_controller_ pendingItem]->GetURL());
   EXPECT_EQ(-1, [session_controller_ pendingItemIndex]);
@@ -148,15 +157,17 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemOverriding) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_
-      addPendingItem:GURL("http://www.another.url.com")
-            referrer:MakeReferrer("http://www.another.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.another.url.com")
+                     referrer:MakeReferrer("http://www.another.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   EXPECT_TRUE([session_controller_ items].empty());
   EXPECT_EQ(
@@ -166,10 +177,11 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndCommit) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
@@ -181,15 +193,17 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemOverridingAndCommit) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_
-      addPendingItem:GURL("http://www.another.url.com")
-            referrer:MakeReferrer("http://www.another.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.another.url.com")
+                     referrer:MakeReferrer("http://www.another.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
@@ -201,17 +215,19 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndCommitMultiple) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   [session_controller_
-      addPendingItem:GURL("http://www.another.url.com")
-            referrer:MakeReferrer("http://www.another.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.another.url.com")
+                     referrer:MakeReferrer("http://www.another.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(2U, [session_controller_ items].size());
@@ -225,10 +241,11 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndDiscard) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ discardNonCommittedItems];
 
   EXPECT_TRUE([session_controller_ items].empty());
@@ -238,10 +255,11 @@
 // Tests discarding pending item added via |setPendingItemIndex:| call.
 TEST_F(CRWSessionControllerTest, setPendingItemIndexAndDiscard) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   [session_controller_ setPendingItemIndex:0];
@@ -255,17 +273,19 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndDiscardAndAddAndCommit) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ discardNonCommittedItems];
 
   [session_controller_
-      addPendingItem:GURL("http://www.another.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.another.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
@@ -277,17 +297,19 @@
 
 TEST_F(CRWSessionControllerTest, addPendingItemAndCommitAndAddAndDiscard) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   [session_controller_
-      addPendingItem:GURL("http://www.another.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.another.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ discardNonCommittedItems];
 
   EXPECT_EQ(1U, [session_controller_ items].size());
@@ -309,10 +331,11 @@
        commitPendingItemWithoutPendingItemWithCommittedItem) {
   // Setup committed item.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/")
-            referrer:MakeReferrer("http://www.referrer.com/")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/")
+                     referrer:MakeReferrer("http://www.referrer.com/")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Commit pending item when there is no such one
@@ -327,22 +350,25 @@
 TEST_F(CRWSessionControllerTest, commitPendingItemWithExistingForwardItems) {
   // Make 3 items.
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/1")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/1")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/2")
-            referrer:MakeReferrer("http://www.example.com/c")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/2")
+                     referrer:MakeReferrer("http://www.example.com/c")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Go back to the first item.
@@ -350,10 +376,11 @@
 
   // Create and commit a new pending item.
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/2")
-            referrer:MakeReferrer("http://www.example.com/c")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/2")
+                     referrer:MakeReferrer("http://www.example.com/c")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // All forward items should go away.
@@ -366,22 +393,25 @@
 // Tests committing pending item index from the middle.
 TEST_F(CRWSessionControllerTest, commitPendingItemIndex) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/2")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/2")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   ASSERT_EQ(3U, [session_controller_ items].size());
 
@@ -417,10 +447,11 @@
        DiscardPendingItemWithoutPendingItemWithCommittedItem) {
   // Setup committed item
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Discard noncommitted items when there is no such one
@@ -441,10 +472,11 @@
 
 TEST_F(CRWSessionControllerTest, updatePendingItemWithPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ updatePendingItem:GURL("http://www.another.url.com")];
 
   EXPECT_EQ(
@@ -455,10 +487,11 @@
 TEST_F(CRWSessionControllerTest,
        updatePendingItemWithPendingItemAlreadyCommited) {
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_ updatePendingItem:GURL("http://www.another.url.com")];
   [session_controller_ commitPendingItem];
@@ -474,31 +507,35 @@
 TEST_F(CRWSessionControllerTest, CopyState) {
   // Add 1 committed and 1 pending item to target controller.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/2")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/2")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/3")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/3")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   // Create source session controller with 1 committed item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_]);
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [other_session_controller commitPendingItem];
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   // Insert and verify the state of target session controller.
   EXPECT_TRUE([session_controller_ canPruneAllButLastCommittedItem]);
@@ -522,16 +559,18 @@
 TEST_F(CRWSessionControllerTest, CopyStateFromEmptySessionController) {
   // Add 2 committed items to target controller.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Create empty source session controller.
@@ -562,16 +601,18 @@
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_]);
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [other_session_controller commitPendingItem];
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   // Attempt to copy |other_session_controller|'s state and verify that
   // |session_controller_| is unchanged.
@@ -590,32 +631,36 @@
 TEST_F(CRWSessionControllerTest, CopyStateDuringPendingHistoryNavigation) {
   // Add 1 committed and 1 pending item to target controller.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/2")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/2")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   // Create source session controller with 1 committed item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_]);
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [other_session_controller commitPendingItem];
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   // Set the pending item index to the first item.
   [session_controller_ setPendingItemIndex:0];
@@ -639,33 +684,37 @@
 TEST_F(CRWSessionControllerTest, CopyStateWithTransientItem) {
   // Add 1 committed and 1 pending item to target controller.
   [session_controller_
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   GURL second_url = GURL("http://www.url.com/2");
   [session_controller_
-      addPendingItem:second_url
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:second_url
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ addTransientItemWithURL:second_url];
 
   // Create source session controller with 1 committed item.
   base::scoped_nsobject<CRWSessionController> other_session_controller(
       [[CRWSessionController alloc] initWithBrowserState:&browser_state_]);
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/0")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/0")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [other_session_controller commitPendingItem];
   [other_session_controller
-      addPendingItem:GURL("http://www.url.com/1")
-            referrer:web::Referrer()
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com/1")
+                     referrer:web::Referrer()
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
 
   // Attempt to copy |other_session_controller|'s state and verify that
   // |session_controller_| is unchanged.
@@ -750,24 +799,27 @@
 TEST_F(CRWSessionControllerTest, PreviousNavigationItem) {
   EXPECT_EQ(session_controller_.get().previousItemIndex, -1);
   [session_controller_
-      addPendingItem:GURL("http://www.url.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   EXPECT_EQ(session_controller_.get().previousItemIndex, -1);
   [session_controller_
-      addPendingItem:GURL("http://www.url1.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url1.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   EXPECT_EQ(session_controller_.get().previousItemIndex, 0);
   [session_controller_
-      addPendingItem:GURL("http://www.url2.com")
-            referrer:MakeReferrer("http://www.referer.com")
-          transition:ui::PAGE_TRANSITION_TYPED
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.url2.com")
+                     referrer:MakeReferrer("http://www.referer.com")
+                   transition:ui::PAGE_TRANSITION_TYPED
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(session_controller_.get().previousItemIndex, 1);
@@ -913,28 +965,32 @@
 
 TEST_F(CRWSessionControllerTest, TestBackwardForwardItems) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/1")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/1")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/redirect")
-            referrer:MakeReferrer("http://www.example.com/r")
-          transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/redirect")
+                     referrer:MakeReferrer("http://www.example.com/r")
+                   transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/2")
-            referrer:MakeReferrer("http://www.example.com/c")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/2")
+                     referrer:MakeReferrer("http://www.example.com/c")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   EXPECT_EQ(3, session_controller_.get().lastCommittedItemIndex);
@@ -957,34 +1013,39 @@
 // Tests going to items with existing and non-existing indices.
 TEST_F(CRWSessionControllerTest, GoToItemAtIndex) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/1")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/1")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/redirect")
-            referrer:MakeReferrer("http://www.example.com/r")
-          transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/redirect")
+                     referrer:MakeReferrer("http://www.example.com/r")
+                   transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/2")
-            referrer:MakeReferrer("http://www.example.com/c")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/2")
+                     referrer:MakeReferrer("http://www.example.com/c")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/3")
-            referrer:MakeReferrer("http://www.example.com/d")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/3")
+                     referrer:MakeReferrer("http://www.example.com/d")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ addTransientItemWithURL:GURL("http://www.example.com")];
   EXPECT_EQ(3, session_controller_.get().lastCommittedItemIndex);
   EXPECT_EQ(2, session_controller_.get().previousItemIndex);
@@ -1033,10 +1094,11 @@
 // item.
 TEST_F(CRWSessionControllerTest, VisibleItemWithCommittedAndTransientItems) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_ addTransientItemWithURL:GURL("http://www.example.com")];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
@@ -1048,10 +1110,11 @@
 TEST_F(CRWSessionControllerTest,
        VisibleItemWithSingleUserInitiatedPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
   ASSERT_TRUE(visible_item);
   EXPECT_EQ("http://www.example.com/0", visible_item->GetURL().spec());
@@ -1062,16 +1125,18 @@
 TEST_F(CRWSessionControllerTest,
        VisibleItemWithCommittedAndUserInitiatedPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
   ASSERT_TRUE(visible_item);
   EXPECT_EQ("http://www.example.com/0", visible_item->GetURL().spec());
@@ -1082,10 +1147,11 @@
 TEST_F(CRWSessionControllerTest,
        VisibleItemWithSingleRendererInitiatedPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
   ASSERT_FALSE(visible_item);
 }
@@ -1095,16 +1161,18 @@
 TEST_F(CRWSessionControllerTest,
        VisibleItemWithCommittedAndRendererInitiatedPendingItem) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   web::NavigationItem* visible_item = [session_controller_ visibleItem];
   ASSERT_TRUE(visible_item);
   EXPECT_EQ("http://www.example.com/", visible_item->GetURL().spec());
@@ -1114,16 +1182,18 @@
 // navigation index.
 TEST_F(CRWSessionControllerTest, VisibleItemWithPendingNavigationIndex) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_LINK
-      initiationType:web::NavigationInitiationType::USER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_LINK
+               initiationType:web::NavigationInitiationType::USER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
 
   [session_controller_ setPendingItemIndex:0];
@@ -1137,16 +1207,18 @@
 // redirects.
 TEST_F(CRWSessionControllerTest, BackwardItemsForAllRedirects) {
   [session_controller_
-      addPendingItem:GURL("http://www.example.com")
-            referrer:MakeReferrer("http://www.example.com/a")
-          transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com")
+                     referrer:MakeReferrer("http://www.example.com/a")
+                   transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   [session_controller_
-      addPendingItem:GURL("http://www.example.com/0")
-            referrer:MakeReferrer("http://www.example.com/b")
-          transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
-      initiationType:web::NavigationInitiationType::RENDERER_INITIATED];
+               addPendingItem:GURL("http://www.example.com/0")
+                     referrer:MakeReferrer("http://www.example.com/b")
+                   transition:ui::PAGE_TRANSITION_CLIENT_REDIRECT
+               initiationType:web::NavigationInitiationType::RENDERER_INITIATED
+      userAgentOverrideOption:UserAgentOverrideOption::INHERIT];
   [session_controller_ commitPendingItem];
   EXPECT_EQ(0U, [session_controller_ backwardItems].size());
 }
diff --git a/ios/web/navigation/navigation_manager_impl.mm b/ios/web/navigation/navigation_manager_impl.mm
index 1d20a08..be7070d 100644
--- a/ios/web/navigation/navigation_manager_impl.mm
+++ b/ios/web/navigation/navigation_manager_impl.mm
@@ -192,7 +192,8 @@
   [session_controller_ addPendingItem:url
                              referrer:referrer
                            transition:navigation_type
-                       initiationType:initiation_type];
+                       initiationType:initiation_type
+              userAgentOverrideOption:user_agent_override_option];
 
   // Set the user agent type for web URLs.
   NavigationItem* pending_item = GetPendingItem();
diff --git a/ios/web/navigation/navigation_manager_impl_unittest.mm b/ios/web/navigation/navigation_manager_impl_unittest.mm
index e76db99..3035b93 100644
--- a/ios/web/navigation/navigation_manager_impl_unittest.mm
+++ b/ios/web/navigation/navigation_manager_impl_unittest.mm
@@ -515,17 +515,355 @@
   EXPECT_EQ(0, navigation_manager()->GetIndexForOffset(-1));
 }
 
+// Tests that when given a pending item, adding a new pending item replaces the
+// existing pending item if their URLs are different.
+TEST_F(NavigationManagerTest, ReplacePendingItemIfDiffernetURL) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(existing_url, navigation_manager()->GetPendingItem()->GetURL());
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  GURL new_url = GURL("http://www.new.com");
+  navigation_manager()->AddPendingItem(
+      new_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(new_url, navigation_manager()->GetPendingItem()->GetURL());
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when given a pending item, adding a new pending item with the same
+// URL doesn't replace the existing pending item if new pending item is not a
+// form submission.
+TEST_F(NavigationManagerTest, NotReplaceSameUrlPendingItemIfNotFormSubmission) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_LINK,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when given a pending item, adding a new pending item with the same
+// URL replaces the existing pending item if new pending item is a form
+// submission while existing pending item is not.
+TEST_F(NavigationManagerTest, ReplaceSameUrlPendingItemIfFormSubmission) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_FORM_SUBMIT,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_FORM_SUBMIT));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when given a pending item, adding a new pending item with the same
+// URL doesn't replace the existing pending item if the user agent override
+// option is INHERIT.
+TEST_F(NavigationManagerTest, NotReplaceSameUrlPendingItemIfOverrideInherit) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when given a pending item, adding a new pending item with the same
+// URL replaces the existing pending item if the user agent override option is
+// DESKTOP.
+TEST_F(NavigationManagerTest, ReplaceSameUrlPendingItemIfOverrideDesktop) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::MOBILE);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(web::UserAgentType::MOBILE,
+            navigation_manager()->GetPendingItem()->GetUserAgentType());
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::DESKTOP);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_RELOAD));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when given a pending item, adding a new pending item with the same
+// URL replaces the existing pending item if the user agent override option is
+// MOBILE.
+TEST_F(NavigationManagerTest, ReplaceSameUrlPendingItemIfOverrideMobile) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::DESKTOP);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(web::UserAgentType::DESKTOP,
+            navigation_manager()->GetPendingItem()->GetUserAgentType());
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::MOBILE);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_RELOAD));
+  EXPECT_EQ(0, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item
+// succeeds if the new item's URL is different from the last committed item.
+TEST_F(NavigationManagerTest, AddPendingItemIfDiffernetURL) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_EQ(existing_url,
+            navigation_manager()->GetLastCommittedItem()->GetURL());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  GURL new_url = GURL("http://www.new.com");
+  navigation_manager()->AddPendingItem(
+      new_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(new_url, navigation_manager()->GetPendingItem()->GetURL());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL fails if the new item is not form submission.
+TEST_F(NavigationManagerTest, NotAddSameUrlPendingItemIfNotFormSubmission) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetLastCommittedItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_LINK,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL succeeds if the new item is a form submission while the last
+// committed item is not.
+TEST_F(NavigationManagerTest, AddSameUrlPendingItemIfFormSubmission) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetLastCommittedItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  // Add if new transition is a form submission.
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_FORM_SUBMIT,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_FORM_SUBMIT));
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL fails if both the new item and the last committed item are form
+// submissions.
+TEST_F(NavigationManagerTest,
+       NotAddSameUrlPendingItemIfDuplicateFormSubmission) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_FORM_SUBMIT,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_FORM_SUBMIT,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL fails if the user agent override option is INHERIT.
+TEST_F(NavigationManagerTest, NotAddSameUrlPendingItemIfOverrideInherit) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetLastCommittedItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+  EXPECT_FALSE(navigation_manager()->GetPendingItem());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL succeeds if the user agent override option is DESKTOP.
+TEST_F(NavigationManagerTest, AddSameUrlPendingItemIfOverrideDesktop) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::MOBILE);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetLastCommittedItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(web::UserAgentType::MOBILE,
+            navigation_manager()->GetLastCommittedItem()->GetUserAgentType());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::DESKTOP);
+
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_RELOAD));
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
+// Tests that when the last committed item exists, adding a pending item with
+// the same URL succeeds if the user agent override option is MOBILE.
+TEST_F(NavigationManagerTest, AddSameUrlPendingItemIfOverrideMobile) {
+  GURL existing_url = GURL("http://www.existing.com");
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_TYPED,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::DESKTOP);
+  [session_controller() commitPendingItem];
+  ASSERT_TRUE(navigation_manager()->GetLastCommittedItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetLastCommittedItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_TYPED));
+  EXPECT_EQ(web::UserAgentType::DESKTOP,
+            navigation_manager()->GetLastCommittedItem()->GetUserAgentType());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+
+  navigation_manager()->AddPendingItem(
+      existing_url, Referrer(), ui::PAGE_TRANSITION_RELOAD,
+      web::NavigationInitiationType::USER_INITIATED,
+      web::NavigationManager::UserAgentOverrideOption::MOBILE);
+  ASSERT_TRUE(navigation_manager()->GetPendingItem());
+  EXPECT_TRUE(ui::PageTransitionCoreTypeIs(
+      navigation_manager()->GetPendingItem()->GetTransitionType(),
+      ui::PAGE_TRANSITION_RELOAD));
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
+}
+
 // Tests that desktop user agent can be enforced to use for next pending item
 // when UserAgentOverrideOption is DESKTOP.
 TEST_F(NavigationManagerTest, OverrideUserAgentWithDesktop) {
   navigation_manager()->AddPendingItem(
       GURL("http://www.1.com"), Referrer(), ui::PAGE_TRANSITION_TYPED,
       web::NavigationInitiationType::USER_INITIATED,
-      web::NavigationManager::UserAgentOverrideOption::INHERIT);
+      web::NavigationManager::UserAgentOverrideOption::MOBILE);
   [session_controller() commitPendingItem];
   NavigationItem* last_committed_item =
       navigation_manager()->GetLastCommittedItem();
   EXPECT_EQ(UserAgentType::MOBILE, last_committed_item->GetUserAgentType());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
 
   navigation_manager()->AddPendingItem(
       GURL("http://www.2.com"), Referrer(), ui::PAGE_TRANSITION_TYPED,
@@ -534,6 +872,7 @@
   ASSERT_TRUE(navigation_manager()->GetPendingItem());
   EXPECT_EQ(UserAgentType::DESKTOP,
             navigation_manager()->GetPendingItem()->GetUserAgentType());
+  EXPECT_EQ(1, navigation_manager()->GetItemCount());
 }
 
 // Tests that mobile user agent can be enforced to use for next pending item
diff --git a/ios/web/net/crw_ssl_status_updater_unittest.mm b/ios/web/net/crw_ssl_status_updater_unittest.mm
index 964dbe4..96c7731 100644
--- a/ios/web/net/crw_ssl_status_updater_unittest.mm
+++ b/ios/web/net/crw_ssl_status_updater_unittest.mm
@@ -109,10 +109,12 @@
                                            navigationItems:std::move(nav_items)
                                     lastCommittedItemIndex:0]);
     [session_controller
-        addPendingItem:GURL(item_url_spec)
-              referrer:Referrer()
-            transition:ui::PAGE_TRANSITION_LINK
-        initiationType:web::NavigationInitiationType::USER_INITIATED];
+                 addPendingItem:GURL(item_url_spec)
+                       referrer:Referrer()
+                     transition:ui::PAGE_TRANSITION_LINK
+                 initiationType:web::NavigationInitiationType::USER_INITIATED
+        userAgentOverrideOption:NavigationManager::UserAgentOverrideOption::
+                                    INHERIT];
     [session_controller commitPendingItem];
 
     return session_controller.autorelease();
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 4b34cba3..6b548101 100644
--- a/ios/web/web_state/ui/crw_web_controller_unittest.mm
+++ b/ios/web/web_state/ui/crw_web_controller_unittest.mm
@@ -579,14 +579,6 @@
 // Real WKWebView is required for CRWWebControllerInvalidUrlTest.
 typedef web::WebTestWithWebState CRWWebControllerInvalidUrlTest;
 
-// Tests that web controller navigates to about:blank if invalid URL is loaded.
-TEST_F(CRWWebControllerInvalidUrlTest, LoadInvalidURL) {
-  GURL url(kInvalidURL);
-  ASSERT_FALSE(url.is_valid());
-  LoadHtml(@"<html><body></body></html>", url);
-  EXPECT_EQ(GURL(url::kAboutBlankURL), web_state()->GetLastCommittedURL());
-}
-
 // Tests that web controller does not navigate to about:blank if iframe src
 // has invalid url. Web controller loads about:blank if page navigates to
 // invalid url, but should do nothing if navigation is performed in iframe. This
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index 0bb7260c..37b6419 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -225,6 +225,11 @@
     return context_->ipc_task_runner();
   }
 
+  const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner_refptr()
+      const {
+    return context_->ipc_task_runner_refptr();
+  }
+
   // Called to clear the pointer to the IPC task runner when it's going away.
   void ClearIPCTaskRunner();
 
@@ -244,6 +249,11 @@
     base::SingleThreadTaskRunner* ipc_task_runner() const {
       return ipc_task_runner_.get();
     }
+    const scoped_refptr<base::SingleThreadTaskRunner>& ipc_task_runner_refptr()
+        const {
+      return ipc_task_runner_;
+    }
+
     // Dispatches a message on the listener thread.
     void OnDispatchMessage(const Message& message);
 
diff --git a/media/base/hdr_metadata.h b/media/base/hdr_metadata.h
index 2808e173..4846025e 100644
--- a/media/base/hdr_metadata.h
+++ b/media/base/hdr_metadata.h
@@ -6,19 +6,17 @@
 #define MEDIA_BASE_HDR_METADATA_H_
 
 #include "media/base/media_export.h"
+#include "ui/gfx/geometry/point_f.h"
 
 namespace media {
 
 // SMPTE ST 2086 mastering metadata.
 struct MEDIA_EXPORT MasteringMetadata {
-  float primary_r_chromaticity_x = 0;
-  float primary_r_chromaticity_y = 0;
-  float primary_g_chromaticity_x = 0;
-  float primary_g_chromaticity_y = 0;
-  float primary_b_chromaticity_x = 0;
-  float primary_b_chromaticity_y = 0;
-  float white_point_chromaticity_x = 0;
-  float white_point_chromaticity_y = 0;
+  using Chromaticity = gfx::PointF;
+  Chromaticity primary_r;
+  Chromaticity primary_g;
+  Chromaticity primary_b;
+  Chromaticity white_point;
   float luminance_max = 0;
   float luminance_min = 0;
 
@@ -26,14 +24,8 @@
   MasteringMetadata(const MasteringMetadata& rhs);
 
   bool operator==(const MasteringMetadata& rhs) const {
-    return ((primary_r_chromaticity_x == rhs.primary_r_chromaticity_x) &&
-            (primary_r_chromaticity_y == rhs.primary_r_chromaticity_y) &&
-            (primary_g_chromaticity_x == rhs.primary_g_chromaticity_x) &&
-            (primary_g_chromaticity_y == rhs.primary_g_chromaticity_y) &&
-            (primary_b_chromaticity_x == rhs.primary_b_chromaticity_x) &&
-            (primary_b_chromaticity_y == rhs.primary_b_chromaticity_y) &&
-            (white_point_chromaticity_x == rhs.white_point_chromaticity_x) &&
-            (white_point_chromaticity_y == rhs.white_point_chromaticity_y) &&
+    return ((primary_r == rhs.primary_r) && (primary_g == rhs.primary_g) &&
+            (primary_b == rhs.primary_b) && (white_point == rhs.white_point) &&
             (luminance_max == rhs.luminance_max) &&
             (luminance_min == rhs.luminance_min));
   }
@@ -42,15 +34,21 @@
 // HDR metadata common for HDR10 and WebM/VP9-based HDR formats.
 struct MEDIA_EXPORT HDRMetadata {
   MasteringMetadata mastering_metadata;
-  unsigned max_cll = 0;
-  unsigned max_fall = 0;
+  // Max content light level (CLL), i.e. maximum brightness level present in the
+  // stream), in nits.
+  unsigned max_content_light_level = 0;
+  // Max frame-average light level (FALL), i.e. maximum average brightness of
+  // the brightest frame in the stream), in nits.
+  unsigned max_frame_average_light_level = 0;
 
   HDRMetadata();
   HDRMetadata(const HDRMetadata& rhs);
 
   bool operator==(const HDRMetadata& rhs) const {
-    return ((max_cll == rhs.max_cll) && (max_fall == rhs.max_fall) &&
-            (mastering_metadata == rhs.mastering_metadata));
+    return (
+        (max_content_light_level == rhs.max_content_light_level) &&
+        (max_frame_average_light_level == rhs.max_frame_average_light_level) &&
+        (mastering_metadata == rhs.mastering_metadata));
   }
 };
 
diff --git a/media/capture/BUILD.gn b/media/capture/BUILD.gn
index 43feb7b..150f7b3 100644
--- a/media/capture/BUILD.gn
+++ b/media/capture/BUILD.gn
@@ -90,7 +90,11 @@
     "video/video_capture_device_descriptor.h",
     "video/video_capture_device_factory.cc",
     "video/video_capture_device_factory.h",
+    "video/video_capture_device_info.cc",
+    "video/video_capture_device_info.h",
     "video/video_capture_jpeg_decoder.h",
+    "video/video_capture_system.cc",
+    "video/video_capture_system.h",
     "video/video_frame_receiver.h",
     "video/video_frame_receiver_on_task_runner.cc",
     "video/video_frame_receiver_on_task_runner.h",
diff --git a/media/capture/video/video_capture_device_factory.h b/media/capture/video/video_capture_device_factory.h
index 49d9519f..f0b9b09 100644
--- a/media/capture/video/video_capture_device_factory.h
+++ b/media/capture/video/video_capture_device_factory.h
@@ -36,6 +36,8 @@
       const VideoCaptureDeviceDescriptor& device_descriptor) = 0;
 
   // Asynchronous version of GetDeviceDescriptors calling back to |callback|.
+  // TODO(chfremer): Consider removing this if none of the implementations
+  // overrides it. See crbug.com/708233.
   virtual void EnumerateDeviceDescriptors(
       const base::Callback<
           void(std::unique_ptr<VideoCaptureDeviceDescriptors>)>& callback);
diff --git a/media/capture/video/video_capture_device_info.cc b/media/capture/video/video_capture_device_info.cc
new file mode 100644
index 0000000..50f999b
--- /dev/null
+++ b/media/capture/video/video_capture_device_info.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/capture/video/video_capture_device_info.h"
+
+namespace media {
+
+VideoCaptureDeviceInfo::VideoCaptureDeviceInfo() = default;
+
+VideoCaptureDeviceInfo::VideoCaptureDeviceInfo(
+    media::VideoCaptureDeviceDescriptor descriptor)
+    : descriptor(descriptor) {}
+
+VideoCaptureDeviceInfo::VideoCaptureDeviceInfo(
+    const VideoCaptureDeviceInfo& other) = default;
+
+VideoCaptureDeviceInfo::~VideoCaptureDeviceInfo() = default;
+
+VideoCaptureDeviceInfo& VideoCaptureDeviceInfo::operator=(
+    const VideoCaptureDeviceInfo& other) = default;
+
+}  // namespace media
diff --git a/media/capture/video/video_capture_device_info.h b/media/capture/video/video_capture_device_info.h
new file mode 100644
index 0000000..32f61c6
--- /dev/null
+++ b/media/capture/video/video_capture_device_info.h
@@ -0,0 +1,28 @@
+// Copyright 2017 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 MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_INFO_H_
+#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_INFO_H_
+
+#include "media/capture/video/video_capture_device_descriptor.h"
+#include "media/capture/video_capture_types.h"
+
+namespace media {
+
+// Bundles a media::VideoCaptureDeviceDescriptor with corresponding supported
+// video formats.
+struct CAPTURE_EXPORT VideoCaptureDeviceInfo {
+  VideoCaptureDeviceInfo();
+  VideoCaptureDeviceInfo(media::VideoCaptureDeviceDescriptor descriptor);
+  VideoCaptureDeviceInfo(const VideoCaptureDeviceInfo& other);
+  ~VideoCaptureDeviceInfo();
+  VideoCaptureDeviceInfo& operator=(const VideoCaptureDeviceInfo& other);
+
+  media::VideoCaptureDeviceDescriptor descriptor;
+  media::VideoCaptureFormats supported_formats;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_INFO_H_
diff --git a/media/capture/video/video_capture_system.cc b/media/capture/video/video_capture_system.cc
new file mode 100644
index 0000000..7df5e54
--- /dev/null
+++ b/media/capture/video/video_capture_system.cc
@@ -0,0 +1,126 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/capture/video/video_capture_system.h"
+
+#include "media/base/bind_to_current_loop.h"
+
+namespace {
+
+// Compares two VideoCaptureFormat by checking smallest frame_size area, then
+// by _largest_ frame_rate. Used to order a VideoCaptureFormats vector so that
+// the first entry for a given resolution has the largest frame rate, as needed
+// by the ConsolidateCaptureFormats() method.
+bool IsCaptureFormatSmaller(const media::VideoCaptureFormat& format1,
+                            const media::VideoCaptureFormat& format2) {
+  DCHECK(format1.frame_size.GetCheckedArea().IsValid());
+  DCHECK(format2.frame_size.GetCheckedArea().IsValid());
+  if (format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
+      format2.frame_size.GetCheckedArea().ValueOrDefault(0)) {
+    return format1.frame_rate > format2.frame_rate;
+  }
+  return format1.frame_size.GetCheckedArea().ValueOrDefault(0) <
+         format2.frame_size.GetCheckedArea().ValueOrDefault(0);
+}
+
+bool IsCaptureFormatSizeEqual(const media::VideoCaptureFormat& format1,
+                              const media::VideoCaptureFormat& format2) {
+  DCHECK(format1.frame_size.GetCheckedArea().IsValid());
+  DCHECK(format2.frame_size.GetCheckedArea().IsValid());
+  return format1.frame_size.GetCheckedArea().ValueOrDefault(0) ==
+         format2.frame_size.GetCheckedArea().ValueOrDefault(0);
+}
+
+// This function receives a list of capture formats, removes duplicated
+// resolutions while keeping the highest frame rate for each, and forcing I420
+// pixel format.
+void ConsolidateCaptureFormats(media::VideoCaptureFormats* formats) {
+  if (formats->empty())
+    return;
+  std::sort(formats->begin(), formats->end(), IsCaptureFormatSmaller);
+  // Due to the ordering imposed, the largest frame_rate is kept while removing
+  // duplicated resolutions.
+  media::VideoCaptureFormats::iterator last =
+      std::unique(formats->begin(), formats->end(), IsCaptureFormatSizeEqual);
+  formats->erase(last, formats->end());
+  // Mark all formats as I420, since this is what the renderer side will get
+  // anyhow: the actual pixel format is decided at the device level.
+  // Don't do this for Y16 format as it is handled separatelly.
+  for (auto& format : *formats) {
+    if (format.pixel_format != media::PIXEL_FORMAT_Y16)
+      format.pixel_format = media::PIXEL_FORMAT_I420;
+  }
+}
+
+}  // anonymous namespace
+
+namespace media {
+
+VideoCaptureSystem::VideoCaptureSystem(
+    std::unique_ptr<VideoCaptureDeviceFactory> factory)
+    : factory_(std::move(factory)) {
+  thread_checker_.DetachFromThread();
+}
+
+VideoCaptureSystem::~VideoCaptureSystem() = default;
+
+void VideoCaptureSystem::GetDeviceInfosAsync(
+    const DeviceInfoCallback& result_callback) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // Use of Unretained() is safe assuming that |result_callback| has ownership
+  // of |this|.
+  factory_->EnumerateDeviceDescriptors(media::BindToCurrentLoop(
+      base::Bind(&VideoCaptureSystem::OnDescriptorsReceived,
+                 base::Unretained(this), result_callback)));
+}
+
+// Creates a VideoCaptureDevice object. Returns NULL if something goes wrong.
+std::unique_ptr<VideoCaptureDevice> VideoCaptureSystem::CreateDevice(
+    const std::string& device_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  const VideoCaptureDeviceInfo* device_info = LookupDeviceInfoFromId(device_id);
+  if (!device_info)
+    return nullptr;
+  return factory_->CreateDevice(device_info->descriptor);
+}
+
+void VideoCaptureSystem::OnDescriptorsReceived(
+    const DeviceInfoCallback& result_callback,
+    std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  // For devices for which we already have an entry in |devices_info_cache_|,
+  // we do not want to query the |factory_| for supported formats again. We
+  // simply copy them from |devices_info_cache_|.
+  std::vector<VideoCaptureDeviceInfo> new_devices_info_cache;
+  new_devices_info_cache.reserve(descriptors->size());
+  for (const auto& descriptor : *descriptors) {
+    if (auto* cached_info = LookupDeviceInfoFromId(descriptor.device_id)) {
+      new_devices_info_cache.push_back(*cached_info);
+    } else {
+      // Query for supported formats in order to create the entry.
+      VideoCaptureDeviceInfo device_info(descriptor);
+      factory_->GetSupportedFormats(descriptor, &device_info.supported_formats);
+      ConsolidateCaptureFormats(&device_info.supported_formats);
+      new_devices_info_cache.push_back(device_info);
+    }
+  }
+
+  devices_info_cache_.swap(new_devices_info_cache);
+  result_callback.Run(devices_info_cache_);
+}
+
+const VideoCaptureDeviceInfo* VideoCaptureSystem::LookupDeviceInfoFromId(
+    const std::string& device_id) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  auto iter = std::find_if(
+      devices_info_cache_.begin(), devices_info_cache_.end(),
+      [&device_id](const media::VideoCaptureDeviceInfo& device_info) {
+        return device_info.descriptor.device_id == device_id;
+      });
+  if (iter == devices_info_cache_.end())
+    return nullptr;
+  return &(*iter);
+}
+
+}  // namespace media
diff --git a/media/capture/video/video_capture_system.h b/media/capture/video/video_capture_system.h
new file mode 100644
index 0000000..f03e3a98
--- /dev/null
+++ b/media/capture/video/video_capture_system.h
@@ -0,0 +1,56 @@
+// Copyright 2017 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 MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_SYSTEM_H_
+#define MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_SYSTEM_H_
+
+#include "media/capture/video/video_capture_device_factory.h"
+#include "media/capture/video/video_capture_device_info.h"
+
+namespace media {
+
+// Layer on top of VideoCaptureDeviceFactory that translates device descriptors
+// to string identifiers and consolidates and caches device descriptors and
+// supported formats into VideoCaptureDeviceInfos.
+class CAPTURE_EXPORT VideoCaptureSystem {
+ public:
+  using DeviceInfoCallback =
+      base::Callback<void(const std::vector<VideoCaptureDeviceInfo>&)>;
+
+  explicit VideoCaptureSystem(
+      std::unique_ptr<VideoCaptureDeviceFactory> factory);
+  ~VideoCaptureSystem();
+
+  // The passed-in |result_callback| must have ownership of the called
+  // VideoCaptureSystem instance to guarantee that it stays alive during the
+  // asynchronous operation.
+  void GetDeviceInfosAsync(const DeviceInfoCallback& result_callback);
+
+  // Creates a VideoCaptureDevice object. Returns nullptr if something goes
+  // wrong.
+  std::unique_ptr<VideoCaptureDevice> CreateDevice(
+      const std::string& device_id);
+
+  media::VideoCaptureDeviceFactory* video_capture_device_factory() const {
+    return factory_.get();
+  }
+
+ private:
+  void OnDescriptorsReceived(
+      const DeviceInfoCallback& result_callback,
+      std::unique_ptr<VideoCaptureDeviceDescriptors> descriptors);
+
+  // Returns nullptr if no descriptor found.
+  const VideoCaptureDeviceInfo* LookupDeviceInfoFromId(
+      const std::string& device_id);
+
+  const std::unique_ptr<VideoCaptureDeviceFactory> factory_;
+  std::vector<VideoCaptureDeviceInfo> devices_info_cache_;
+
+  base::ThreadChecker thread_checker_;
+};
+
+}  // namespace media
+
+#endif  // MEDIA_CAPTURE_VIDEO_VIDEO_CAPTURE_DEVICE_FACTORY_H_
diff --git a/media/formats/webm/webm_colour_parser.cc b/media/formats/webm/webm_colour_parser.cc
index 7e3c217..e2f108c 100644
--- a/media/formats/webm/webm_colour_parser.cc
+++ b/media/formats/webm/webm_colour_parser.cc
@@ -263,28 +263,28 @@
 bool WebMMasteringMetadataParser::OnFloat(int id, double val) {
   switch (id) {
     case kWebMIdPrimaryRChromaticityX:
-      mastering_metadata_.primary_r_chromaticity_x = val;
+      mastering_metadata_.primary_r.set_x(val);
       break;
     case kWebMIdPrimaryRChromaticityY:
-      mastering_metadata_.primary_r_chromaticity_y = val;
+      mastering_metadata_.primary_r.set_y(val);
       break;
     case kWebMIdPrimaryGChromaticityX:
-      mastering_metadata_.primary_g_chromaticity_x = val;
+      mastering_metadata_.primary_g.set_x(val);
       break;
     case kWebMIdPrimaryGChromaticityY:
-      mastering_metadata_.primary_g_chromaticity_y = val;
+      mastering_metadata_.primary_g.set_y(val);
       break;
     case kWebMIdPrimaryBChromaticityX:
-      mastering_metadata_.primary_b_chromaticity_x = val;
+      mastering_metadata_.primary_b.set_x(val);
       break;
     case kWebMIdPrimaryBChromaticityY:
-      mastering_metadata_.primary_b_chromaticity_y = val;
+      mastering_metadata_.primary_b.set_y(val);
       break;
     case kWebMIdWhitePointChromaticityX:
-      mastering_metadata_.white_point_chromaticity_x = val;
+      mastering_metadata_.white_point.set_x(val);
       break;
     case kWebMIdWhitePointChromaticityY:
-      mastering_metadata_.white_point_chromaticity_y = val;
+      mastering_metadata_.white_point.set_y(val);
       break;
     case kWebMIdLuminanceMax:
       mastering_metadata_.luminance_max = val;
@@ -317,8 +317,8 @@
   range_ = -1;
   transfer_characteristics_ = -1;
   primaries_ = -1;
-  max_cll_ = -1;
-  max_fall_ = -1;
+  max_content_light_level_ = -1;
+  max_frame_average_light_level_ = -1;
 }
 
 WebMParserClient* WebMColourParser::OnListStart(int id) {
@@ -374,10 +374,10 @@
       dst = &primaries_;
       break;
     case kWebMIdMaxCLL:
-      dst = &max_cll_;
+      dst = &max_content_light_level_;
       break;
     case kWebMIdMaxFALL:
-      dst = &max_fall_;
+      dst = &max_frame_average_light_level_;
       break;
     default:
       return true;
@@ -431,11 +431,13 @@
   color_metadata.color_space = VideoColorSpace(
       primaries_, transfer_characteristics_, matrix_coefficients_, range_id);
 
-  if (max_cll_ != -1)
-    color_metadata.hdr_metadata.max_cll = max_cll_;
+  if (max_content_light_level_ != -1)
+    color_metadata.hdr_metadata.max_content_light_level =
+        max_content_light_level_;
 
-  if (max_fall_ != -1)
-    color_metadata.hdr_metadata.max_fall = max_fall_;
+  if (max_frame_average_light_level_ != -1)
+    color_metadata.hdr_metadata.max_frame_average_light_level =
+        max_frame_average_light_level_;
 
   if (mastering_metadata_parsed_)
     color_metadata.hdr_metadata.mastering_metadata =
diff --git a/media/formats/webm/webm_colour_parser.h b/media/formats/webm/webm_colour_parser.h
index 8304e65f..636bdc9 100644
--- a/media/formats/webm/webm_colour_parser.h
+++ b/media/formats/webm/webm_colour_parser.h
@@ -76,8 +76,8 @@
   int64_t range_;
   int64_t transfer_characteristics_;
   int64_t primaries_;
-  int64_t max_cll_;
-  int64_t max_fall_;
+  int64_t max_content_light_level_;
+  int64_t max_frame_average_light_level_;
 
   WebMMasteringMetadataParser mastering_metadata_parser_;
   bool mastering_metadata_parsed_ = false;
diff --git a/media/formats/webm/webm_stream_parser_unittest.cc b/media/formats/webm/webm_stream_parser_unittest.cc
index d7ef3b7..1251f2d8 100644
--- a/media/formats/webm/webm_stream_parser_unittest.cc
+++ b/media/formats/webm/webm_stream_parser_unittest.cc
@@ -177,18 +177,18 @@
 
   base::Optional<HDRMetadata> hdr_metadata = video_config.hdr_metadata();
   EXPECT_TRUE(hdr_metadata.has_value());
-  EXPECT_EQ(hdr_metadata->max_cll, 11u);
-  EXPECT_EQ(hdr_metadata->max_fall, 12u);
+  EXPECT_EQ(hdr_metadata->max_content_light_level, 11u);
+  EXPECT_EQ(hdr_metadata->max_frame_average_light_level, 12u);
 
   const MasteringMetadata& mmdata = hdr_metadata->mastering_metadata;
-  EXPECT_FLOAT_EQ(mmdata.primary_r_chromaticity_x, 0.1f);
-  EXPECT_FLOAT_EQ(mmdata.primary_r_chromaticity_y, 0.2f);
-  EXPECT_FLOAT_EQ(mmdata.primary_g_chromaticity_x, 0.1f);
-  EXPECT_FLOAT_EQ(mmdata.primary_g_chromaticity_y, 0.2f);
-  EXPECT_FLOAT_EQ(mmdata.primary_b_chromaticity_x, 0.1f);
-  EXPECT_FLOAT_EQ(mmdata.primary_b_chromaticity_y, 0.2f);
-  EXPECT_FLOAT_EQ(mmdata.white_point_chromaticity_x, 0.1f);
-  EXPECT_FLOAT_EQ(mmdata.white_point_chromaticity_y, 0.2f);
+  EXPECT_FLOAT_EQ(mmdata.primary_r.x(), 0.1f);
+  EXPECT_FLOAT_EQ(mmdata.primary_r.y(), 0.2f);
+  EXPECT_FLOAT_EQ(mmdata.primary_g.x(), 0.1f);
+  EXPECT_FLOAT_EQ(mmdata.primary_g.y(), 0.2f);
+  EXPECT_FLOAT_EQ(mmdata.primary_b.x(), 0.1f);
+  EXPECT_FLOAT_EQ(mmdata.primary_b.y(), 0.2f);
+  EXPECT_FLOAT_EQ(mmdata.white_point.x(), 0.1f);
+  EXPECT_FLOAT_EQ(mmdata.white_point.y(), 0.2f);
   EXPECT_EQ(mmdata.luminance_max, 40);
   EXPECT_EQ(mmdata.luminance_min, 30);
 }
diff --git a/media/mojo/common/media_type_converters.cc b/media/mojo/common/media_type_converters.cc
index 61b8922..50dd25f 100644
--- a/media/mojo/common/media_type_converters.cc
+++ b/media/mojo/common/media_type_converters.cc
@@ -219,6 +219,8 @@
   config->encryption_scheme =
       media::mojom::EncryptionScheme::From(input.encryption_scheme());
   config->color_space_info = input.color_space_info();
+  if (input.hdr_metadata())
+    config->hdr_metadata = *input.hdr_metadata();
   return config;
 }
 
@@ -232,6 +234,8 @@
                     input->natural_size, input->extra_data,
                     input->encryption_scheme.To<media::EncryptionScheme>());
   config.set_color_space_info(input->color_space_info);
+  if (input->hdr_metadata)
+    config.set_hdr_metadata(*input->hdr_metadata);
   return config;
 }
 
diff --git a/media/mojo/common/media_type_converters_unittest.cc b/media/mojo/common/media_type_converters_unittest.cc
index f9e0fac..54aa3717 100644
--- a/media/mojo/common/media_type_converters_unittest.cc
+++ b/media/mojo/common/media_type_converters_unittest.cc
@@ -360,6 +360,29 @@
   EXPECT_TRUE(result.Matches(config));
 }
 
+TEST(MediaTypeConvertersTest, ConvertVideoDecoderConfig_HDRMetadata) {
+  VideoDecoderConfig config(kCodecVP8, VP8PROFILE_ANY, PIXEL_FORMAT_YV12,
+                            COLOR_SPACE_UNSPECIFIED, kCodedSize, kVisibleRect,
+                            kNaturalSize, EmptyExtraData(), Unencrypted());
+  HDRMetadata hdr_metadata;
+  hdr_metadata.max_frame_average_light_level = 123;
+  hdr_metadata.max_content_light_level = 456;
+  hdr_metadata.mastering_metadata.primary_r.set_x(0.1f);
+  hdr_metadata.mastering_metadata.primary_r.set_y(0.2f);
+  hdr_metadata.mastering_metadata.primary_g.set_x(0.3f);
+  hdr_metadata.mastering_metadata.primary_g.set_y(0.4f);
+  hdr_metadata.mastering_metadata.primary_b.set_x(0.5f);
+  hdr_metadata.mastering_metadata.primary_b.set_y(0.6f);
+  hdr_metadata.mastering_metadata.white_point.set_x(0.7f);
+  hdr_metadata.mastering_metadata.white_point.set_y(0.8f);
+  hdr_metadata.mastering_metadata.luminance_max = 1000;
+  hdr_metadata.mastering_metadata.luminance_min = 0;
+  config.set_hdr_metadata(hdr_metadata);
+  mojom::VideoDecoderConfigPtr ptr(mojom::VideoDecoderConfig::From(config));
+  VideoDecoderConfig result(ptr.To<VideoDecoderConfig>());
+  EXPECT_TRUE(result.Matches(config));
+}
+
 TEST(MediaTypeConvertersTest, ConvertCdmConfig) {
   CdmConfig config;
   config.allow_distinctive_identifier = true;
diff --git a/media/mojo/interfaces/hdr_metadata.typemap b/media/mojo/interfaces/hdr_metadata.typemap
new file mode 100644
index 0000000..6557c4b7
--- /dev/null
+++ b/media/mojo/interfaces/hdr_metadata.typemap
@@ -0,0 +1,11 @@
+# Copyright 2017 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.
+
+mojom = "//media/mojo/interfaces/media_types.mojom"
+public_headers = [ "//media/base/hdr_metadata.h" ]
+traits_headers = [ "//media/mojo/interfaces/hdr_metadata_struct_traits.h" ]
+type_mappings = [
+  "media.mojom.MasteringMetadata=media::MasteringMetadata",
+  "media.mojom.HDRMetadata=media::HDRMetadata",
+]
diff --git a/media/mojo/interfaces/hdr_metadata_struct_traits.h b/media/mojo/interfaces/hdr_metadata_struct_traits.h
new file mode 100644
index 0000000..498f830
--- /dev/null
+++ b/media/mojo/interfaces/hdr_metadata_struct_traits.h
@@ -0,0 +1,68 @@
+// Copyright 2017 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 MEDIA_MOJO_INTERFACES_HDR_METADATA_STRUCT_TRAITS_H_
+#define MEDIA_MOJO_INTERFACES_HDR_METADATA_STRUCT_TRAITS_H_
+
+#include "media/base/hdr_metadata.h"
+#include "media/mojo/interfaces/media_types.mojom.h"
+
+namespace mojo {
+
+template <>
+struct StructTraits<media::mojom::MasteringMetadataDataView,
+                    media::MasteringMetadata> {
+  static gfx::PointF primary_r(const media::MasteringMetadata& input) {
+    return input.primary_r;
+  }
+  static gfx::PointF primary_g(const media::MasteringMetadata& input) {
+    return input.primary_g;
+  }
+  static gfx::PointF primary_b(const media::MasteringMetadata& input) {
+    return input.primary_b;
+  }
+  static gfx::PointF white_point(const media::MasteringMetadata& input) {
+    return input.white_point;
+  }
+  static float luminance_max(const media::MasteringMetadata& input) {
+    return input.luminance_max;
+  }
+  static float luminance_min(const media::MasteringMetadata& input) {
+    return input.luminance_min;
+  }
+
+  static bool Read(media::mojom::MasteringMetadataDataView data,
+                   media::MasteringMetadata* output) {
+    output->luminance_max = data.luminance_max();
+    output->luminance_min = data.luminance_min();
+    return true;
+  }
+};
+
+template <>
+struct StructTraits<media::mojom::HDRMetadataDataView, media::HDRMetadata> {
+  static unsigned max_content_light_level(const media::HDRMetadata& input) {
+    return input.max_content_light_level;
+  }
+  static unsigned max_frame_average_light_level(
+      const media::HDRMetadata& input) {
+    return input.max_frame_average_light_level;
+  }
+  static media::MasteringMetadata mastering_metadata(
+      const media::HDRMetadata& input) {
+    return input.mastering_metadata;
+  }
+
+  static bool Read(media::mojom::HDRMetadataDataView data,
+                   media::HDRMetadata* output) {
+    output->max_content_light_level = data.max_content_light_level();
+    output->max_frame_average_light_level =
+        data.max_frame_average_light_level();
+    return true;
+  }
+};
+
+}  // namespace mojo
+
+#endif  // MEDIA_MOJO_INTERFACES_HDR_METADATA_STRUCT_TRAITS_H_
diff --git a/media/mojo/interfaces/media_types.mojom b/media/mojo/interfaces/media_types.mojom
index ae20147..16612b5 100644
--- a/media/mojo/interfaces/media_types.mojom
+++ b/media/mojo/interfaces/media_types.mojom
@@ -86,6 +86,23 @@
   RangeID range;
 };
 
+// This defines a mojo transport format for media::HDRMetadata.
+// See media/base/hdr_metadata.h for description.
+struct MasteringMetadata {
+  gfx.mojom.PointF primary_r;
+  gfx.mojom.PointF primary_g;
+  gfx.mojom.PointF primary_b;
+  gfx.mojom.PointF white_point;
+  float luminance_max;
+  float luminance_min;
+};
+
+struct HDRMetadata {
+  MasteringMetadata mastering_metadata;
+  uint32 max_content_light_level;
+  uint32 max_frame_average_light_level;
+};
+
 // This defines a mojo transport format for media::AudioDecoderConfig.
 // See media/base/audio_decoder_config.h for descriptions.
 struct AudioDecoderConfig {
@@ -112,6 +129,7 @@
   array<uint8> extra_data;
   EncryptionScheme encryption_scheme;
   VideoColorSpace color_space_info;
+  HDRMetadata? hdr_metadata;
 };
 
 // Native struct media::SubsampleEntry;
diff --git a/media/mojo/interfaces/media_types.typemap b/media/mojo/interfaces/media_types.typemap
index a10cdf6..cc8b655 100644
--- a/media/mojo/interfaces/media_types.typemap
+++ b/media/mojo/interfaces/media_types.typemap
@@ -10,6 +10,7 @@
   "//media/base/channel_layout.h",
   "//media/base/decode_status.h",
   "//media/base/encryption_scheme.h",
+  "//media/base/hdr_metadata.h",
   "//media/base/output_device_info.h",
   "//media/base/sample_format.h",
   "//media/base/subsample_entry.h",
diff --git a/media/mojo/interfaces/typemaps.gni b/media/mojo/interfaces/typemaps.gni
index 1f0377b..a69837f2 100644
--- a/media/mojo/interfaces/typemaps.gni
+++ b/media/mojo/interfaces/typemaps.gni
@@ -7,6 +7,7 @@
   "//media/mojo/interfaces/content_decryption_module.typemap",
   "//media/mojo/interfaces/decryptor.typemap",
   "//media/mojo/interfaces/demuxer_stream.typemap",
+  "//media/mojo/interfaces/hdr_metadata.typemap",
   "//media/mojo/interfaces/media_types.typemap",
   "//media/mojo/interfaces/pipeline_statistics.typemap",
   "//media/mojo/interfaces/video_color_space.typemap",
diff --git a/media/renderers/skcanvas_video_renderer_unittest.cc b/media/renderers/skcanvas_video_renderer_unittest.cc
index aeed7454..7034b89 100644
--- a/media/renderers/skcanvas_video_renderer_unittest.cc
+++ b/media/renderers/skcanvas_video_renderer_unittest.cc
@@ -33,25 +33,6 @@
 static const int kHeight = 240;
 static const gfx::RectF kNaturalRect(kWidth, kHeight);
 
-// Helper for filling a |canvas| with a solid |color|.
-void FillCanvas(cc::PaintCanvas* canvas, SkColor color) {
-  canvas->clear(color);
-}
-
-// Helper for returning the color of a solid |canvas|.
-SkColor GetColorAt(cc::PaintCanvas* canvas, int x, int y) {
-  SkBitmap bitmap;
-  if (!bitmap.tryAllocN32Pixels(1, 1))
-    return 0;
-  if (!canvas->readPixels(&bitmap, x, y))
-    return 0;
-  return bitmap.getColor(0, 0);
-}
-
-SkColor GetColor(cc::PaintCanvas* canvas) {
-  return GetColorAt(canvas, 0, 0);
-}
-
 // Generate frame pixels to provided |external_memory| and wrap it as frame.
 scoped_refptr<VideoFrame> CreateTestY16Frame(const gfx::Size& coded_size,
                                              const gfx::Rect& visible_rect,
@@ -115,6 +96,7 @@
 
   // Standard canvas.
   cc::PaintCanvas* target_canvas() { return &target_canvas_; }
+  SkBitmap* bitmap() { return &bitmap_; }
 
  protected:
   SkCanvasVideoRenderer renderer_;
@@ -124,6 +106,7 @@
   scoped_refptr<VideoFrame> smaller_frame_;
   scoped_refptr<VideoFrame> cropped_frame_;
 
+  SkBitmap bitmap_;
   cc::SkiaPaintCanvas target_canvas_;
   base::MessageLoop message_loop_;
 
@@ -149,7 +132,8 @@
                                   gfx::Rect(6, 6, 8, 6),
                                   gfx::Size(8, 6),
                                   base::TimeDelta::FromMilliseconds(4))),
-      target_canvas_(AllocBitmap(kWidth, kHeight)) {
+      bitmap_(AllocBitmap(kWidth, kHeight)),
+      target_canvas_(bitmap_) {
   // Give each frame a unique timestamp.
   natural_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(1));
   larger_frame_->set_timestamp(base::TimeDelta::FromMilliseconds(2));
@@ -286,89 +270,89 @@
 
 TEST_F(SkCanvasVideoRendererTest, NoFrame) {
   // Test that black gets painted over canvas.
-  FillCanvas(target_canvas(), SK_ColorRED);
+  target_canvas()->clear(SK_ColorRED);
   PaintWithoutFrame(target_canvas());
-  EXPECT_EQ(SK_ColorBLACK, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, TransparentFrame) {
-  FillCanvas(target_canvas(), SK_ColorRED);
+  target_canvas()->clear(SK_ColorRED);
   PaintRotated(
       VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
       target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrcOver,
       VIDEO_ROTATION_0);
-  EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), GetColor(target_canvas()));
+  EXPECT_EQ(static_cast<SkColor>(SK_ColorRED), bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, TransparentFrameSrcMode) {
-  FillCanvas(target_canvas(), SK_ColorRED);
+  target_canvas()->clear(SK_ColorRED);
   // SRC mode completely overwrites the buffer.
   PaintRotated(
       VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
       target_canvas(), kNaturalRect, kNone, SkBlendMode::kSrc,
       VIDEO_ROTATION_0);
   EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
-            GetColor(target_canvas()));
+            bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, CopyTransparentFrame) {
-  FillCanvas(target_canvas(), SK_ColorRED);
+  target_canvas()->clear(SK_ColorRED);
   Copy(VideoFrame::CreateTransparentFrame(gfx::Size(kWidth, kHeight)).get(),
        target_canvas());
   EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT),
-            GetColor(target_canvas()));
+            bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Natural) {
   Paint(natural_frame(), target_canvas(), kRed);
-  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Larger) {
   Paint(natural_frame(), target_canvas(), kRed);
-  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
 
   Paint(larger_frame(), target_canvas(), kBlue);
-  EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Smaller) {
   Paint(natural_frame(), target_canvas(), kRed);
-  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
 
   Paint(smaller_frame(), target_canvas(), kBlue);
-  EXPECT_EQ(SK_ColorBLUE, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, NoTimestamp) {
   VideoFrame* video_frame = natural_frame().get();
   video_frame->set_timestamp(media::kNoTimestamp);
   Paint(video_frame, target_canvas(), kRed);
-  EXPECT_EQ(SK_ColorRED, GetColor(target_canvas()));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(0, 0));
 }
 
 TEST_F(SkCanvasVideoRendererTest, CroppedFrame) {
   Paint(cropped_frame(), target_canvas(), kNone);
   // Check the corners.
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), 0, 0));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth - 1, 0));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), 0, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
+  EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
   // Check the interior along the border between color regions.  Note that we're
   // bilinearly upscaling, so we'll need to take care to pick sample points that
   // are just outside the "zone of resampling".
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1,
-                                      kHeight * 1 / 6 - 1));
+  EXPECT_EQ(SK_ColorBLACK,
+            bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
   EXPECT_EQ(SK_ColorRED,
-            GetColorAt(target_canvas(), kWidth * 3 / 8, kHeight * 1 / 6 - 1));
+            bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
   EXPECT_EQ(SK_ColorGREEN,
-            GetColorAt(target_canvas(), kWidth * 1 / 8 - 1, kHeight * 3 / 6));
-  EXPECT_EQ(SK_ColorBLUE,
-            GetColorAt(target_canvas(), kWidth * 3 / 8, kHeight * 3 / 6));
+            bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
 }
 
 TEST_F(SkCanvasVideoRendererTest, CroppedFrame_NoScaling) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
   const gfx::Rect crop_rect = cropped_frame()->visible_rect();
 
   // Force painting to a non-zero position on the destination bitmap, to check
@@ -385,122 +369,129 @@
   Paint(cropped_frame(), &canvas, kNone);
 
   // Check the corners.
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, offset_x, offset_y));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(offset_x, offset_y));
   EXPECT_EQ(SK_ColorRED,
-            GetColorAt(&canvas, offset_x + crop_rect.width() - 1, offset_y));
+            bitmap.getColor(offset_x + crop_rect.width() - 1, offset_y));
   EXPECT_EQ(SK_ColorGREEN,
-            GetColorAt(&canvas, offset_x, offset_y + crop_rect.height() - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, offset_x + crop_rect.width() - 1,
-                                     offset_y + crop_rect.height() - 1));
+            bitmap.getColor(offset_x, offset_y + crop_rect.height() - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(offset_x + crop_rect.width() - 1,
+                                          offset_y + crop_rect.height() - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_90) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
   PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
                SkBlendMode::kSrcOver, VIDEO_ROTATION_90);
   // Check the corners.
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, 0));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, kHeight - 1));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, 0));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_180) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
   PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
                SkBlendMode::kSrcOver, VIDEO_ROTATION_180);
   // Check the corners.
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, 0));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, 0));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Rotation_270) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
   PaintRotated(cropped_frame(), &canvas, kNaturalRect, kNone,
                SkBlendMode::kSrcOver, VIDEO_ROTATION_270);
   // Check the corners.
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, 0));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, 0, kHeight - 1));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, 0));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(0, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Translate) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
-  FillCanvas(&canvas, SK_ColorMAGENTA);
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
+  canvas.clear(SK_ColorMAGENTA);
 
   PaintRotated(cropped_frame(), &canvas,
                gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
                kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_0);
   // Check the corners of quadrant 2 and 4.
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
   EXPECT_EQ(SK_ColorMAGENTA,
-            GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
+            bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight / 2));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight / 2));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_90) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
-  FillCanvas(&canvas, SK_ColorMAGENTA);
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
+  canvas.clear(SK_ColorMAGENTA);
 
   PaintRotated(cropped_frame(), &canvas,
                gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
                kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_90);
   // Check the corners of quadrant 2 and 4.
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
   EXPECT_EQ(SK_ColorMAGENTA,
-            GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
+            bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth / 2, kHeight / 2));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight / 2));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_180) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
-  FillCanvas(&canvas, SK_ColorMAGENTA);
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
+  canvas.clear(SK_ColorMAGENTA);
 
   PaintRotated(cropped_frame(), &canvas,
                gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
                kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_180);
   // Check the corners of quadrant 2 and 4.
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
   EXPECT_EQ(SK_ColorMAGENTA,
-            GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
+            bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth / 2, kHeight / 2));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight / 2));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Video_Translate_Rotation_270) {
-  cc::SkiaPaintCanvas canvas(AllocBitmap(kWidth, kHeight));
-  FillCanvas(&canvas, SK_ColorMAGENTA);
+  SkBitmap bitmap = AllocBitmap(kWidth, kHeight);
+  cc::SkiaPaintCanvas canvas(bitmap);
+  canvas.clear(SK_ColorMAGENTA);
 
   PaintRotated(cropped_frame(), &canvas,
                gfx::RectF(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
                kNone, SkBlendMode::kSrcOver, VIDEO_ROTATION_270);
   // Check the corners of quadrant 2 and 4.
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, 0));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, (kWidth / 2) - 1, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, 0));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor((kWidth / 2) - 1, 0));
   EXPECT_EQ(SK_ColorMAGENTA,
-            GetColorAt(&canvas, (kWidth / 2) - 1, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorMAGENTA, GetColorAt(&canvas, 0, (kHeight / 2) - 1));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(&canvas, kWidth / 2, kHeight / 2));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(&canvas, kWidth - 1, kHeight / 2));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(&canvas, kWidth - 1, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(&canvas, kWidth / 2, kHeight - 1));
+            bitmap.getColor((kWidth / 2) - 1, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorMAGENTA, bitmap.getColor(0, (kHeight / 2) - 1));
+  EXPECT_EQ(SK_ColorRED, bitmap.getColor(kWidth / 2, kHeight / 2));
+  EXPECT_EQ(SK_ColorBLUE, bitmap.getColor(kWidth - 1, kHeight / 2));
+  EXPECT_EQ(SK_ColorGREEN, bitmap.getColor(kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLACK, bitmap.getColor(kWidth / 2, kHeight - 1));
 }
 
 TEST_F(SkCanvasVideoRendererTest, HighBits) {
@@ -524,21 +515,20 @@
 
   Paint(frame, target_canvas(), kNone);
   // Check the corners.
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), 0, 0));
-  EXPECT_EQ(SK_ColorRED, GetColorAt(target_canvas(), kWidth - 1, 0));
-  EXPECT_EQ(SK_ColorGREEN, GetColorAt(target_canvas(), 0, kHeight - 1));
-  EXPECT_EQ(SK_ColorBLUE, GetColorAt(target_canvas(), kWidth - 1, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLACK, bitmap()->getColor(0, 0));
+  EXPECT_EQ(SK_ColorRED, bitmap()->getColor(kWidth - 1, 0));
+  EXPECT_EQ(SK_ColorGREEN, bitmap()->getColor(0, kHeight - 1));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth - 1, kHeight - 1));
   // Check the interior along the border between color regions.  Note that we're
   // bilinearly upscaling, so we'll need to take care to pick sample points that
   // are just outside the "zone of resampling".
-  EXPECT_EQ(SK_ColorBLACK, GetColorAt(target_canvas(), kWidth * 1 / 8 - 1,
-                                      kHeight * 1 / 6 - 1));
+  EXPECT_EQ(SK_ColorBLACK,
+            bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 1 / 6 - 1));
   EXPECT_EQ(SK_ColorRED,
-            GetColorAt(target_canvas(), kWidth * 3 / 8, kHeight * 1 / 6 - 1));
+            bitmap()->getColor(kWidth * 3 / 8, kHeight * 1 / 6 - 1));
   EXPECT_EQ(SK_ColorGREEN,
-            GetColorAt(target_canvas(), kWidth * 1 / 8 - 1, kHeight * 3 / 6));
-  EXPECT_EQ(SK_ColorBLUE,
-            GetColorAt(target_canvas(), kWidth * 3 / 8, kHeight * 3 / 6));
+            bitmap()->getColor(kWidth * 1 / 8 - 1, kHeight * 3 / 6));
+  EXPECT_EQ(SK_ColorBLUE, bitmap()->getColor(kWidth * 3 / 8, kHeight * 3 / 6));
 }
 
 TEST_F(SkCanvasVideoRendererTest, Y16) {
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f5f99c3..7c8a16b 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1401,14 +1401,24 @@
       "reporting/reporting_cache.h",
       "reporting/reporting_client.cc",
       "reporting/reporting_client.h",
+      "reporting/reporting_context.cc",
+      "reporting/reporting_context.h",
+      "reporting/reporting_delegate.cc",
+      "reporting/reporting_delegate.h",
       "reporting/reporting_delivery_agent.cc",
       "reporting/reporting_delivery_agent.h",
       "reporting/reporting_endpoint_manager.cc",
       "reporting/reporting_endpoint_manager.h",
       "reporting/reporting_header_parser.cc",
       "reporting/reporting_header_parser.h",
+      "reporting/reporting_observer.cc",
+      "reporting/reporting_observer.h",
+      "reporting/reporting_policy.cc",
+      "reporting/reporting_policy.h",
       "reporting/reporting_report.cc",
       "reporting/reporting_report.h",
+      "reporting/reporting_service.cc",
+      "reporting/reporting_service.h",
       "reporting/reporting_uploader.cc",
       "reporting/reporting_uploader.h",
       "sdch/sdch_owner.cc",
@@ -4628,6 +4638,7 @@
     "reporting/reporting_delivery_agent_unittest.cc",
     "reporting/reporting_endpoint_manager_unittest.cc",
     "reporting/reporting_header_parser_unittest.cc",
+    "reporting/reporting_service_unittest.cc",
     "reporting/reporting_test_util.cc",
     "reporting/reporting_test_util.h",
     "reporting/reporting_uploader_unittest.cc",
diff --git a/net/reporting/reporting_cache.cc b/net/reporting/reporting_cache.cc
index aaa3953..9e43153 100644
--- a/net/reporting/reporting_cache.cc
+++ b/net/reporting/reporting_cache.cc
@@ -14,12 +14,15 @@
 #include "base/stl_util.h"
 #include "base/time/time.h"
 #include "net/reporting/reporting_client.h"
+#include "net/reporting/reporting_context.h"
 #include "net/reporting/reporting_report.h"
 #include "url/gurl.h"
 
 namespace net {
 
-ReportingCache::ReportingCache() {}
+ReportingCache::ReportingCache(ReportingContext* context) : context_(context) {
+  DCHECK(context_);
+}
 
 ReportingCache::~ReportingCache() {}
 
@@ -35,6 +38,8 @@
   auto inserted =
       reports_.insert(std::make_pair(report.get(), std::move(report)));
   DCHECK(inserted.second);
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::GetReports(
@@ -76,6 +81,8 @@
     DCHECK(base::ContainsKey(reports_, report));
     reports_[report]->attempts++;
   }
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveReports(
@@ -89,6 +96,8 @@
       reports_.erase(report);
     }
   }
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveAllReports() {
@@ -105,6 +114,8 @@
 
   for (auto& it : reports_to_remove)
     reports_.erase(it);
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::GetClients(
@@ -140,6 +151,8 @@
 
   clients_[origin][endpoint] = base::MakeUnique<ReportingClient>(
       origin, endpoint, subdomains, group, expires);
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveClients(
@@ -149,6 +162,8 @@
     DCHECK(clients_[client->origin][client->endpoint].get() == client);
     clients_[client->origin].erase(client->endpoint);
   }
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveClientForOriginAndEndpoint(const url::Origin& origin,
@@ -156,15 +171,21 @@
   DCHECK(base::ContainsKey(clients_, origin));
   DCHECK(base::ContainsKey(clients_[origin], endpoint));
   clients_[origin].erase(endpoint);
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveClientsForEndpoint(const GURL& endpoint) {
   for (auto& it : clients_)
     it.second.erase(endpoint);
+
+  context_->NotifyCacheUpdated();
 }
 
 void ReportingCache::RemoveAllClients() {
   clients_.clear();
+
+  context_->NotifyCacheUpdated();
 }
 
 }  // namespace net
diff --git a/net/reporting/reporting_cache.h b/net/reporting/reporting_cache.h
index fdd19c2..8d04279 100644
--- a/net/reporting/reporting_cache.h
+++ b/net/reporting/reporting_cache.h
@@ -7,6 +7,7 @@
 
 #include <map>
 #include <memory>
+#include <set>
 #include <string>
 #include <unordered_map>
 #include <unordered_set>
@@ -23,6 +24,7 @@
 
 namespace net {
 
+class ReportingContext;
 struct ReportingReport;
 
 // The cache holds undelivered reports and clients (per-origin endpoint
@@ -38,7 +40,8 @@
 // "doomed", which will cause it to be deallocated once it is no longer pending.
 class NET_EXPORT ReportingCache {
  public:
-  ReportingCache();
+  // |context| must outlive the ReportingCache.
+  ReportingCache(ReportingContext* context);
 
   ~ReportingCache();
 
@@ -145,6 +148,8 @@
   }
 
  private:
+  ReportingContext* context_;
+
   // Owns all clients, keyed by origin, then endpoint URL.
   // (These would be unordered_map, but neither url::Origin nor GURL has a hash
   // function implemented.)
@@ -155,11 +160,11 @@
   std::unordered_map<const ReportingReport*, std::unique_ptr<ReportingReport>>
       reports_;
 
-  // Reports that have been marked "pending" (in use elsewhere and should not be
+  // Reports that have been marked pending (in use elsewhere and should not be
   // deleted until no longer pending).
   std::unordered_set<const ReportingReport*> pending_reports_;
 
-  // Reports that have been marked "doomed" (would have been deleted, but were
+  // Reports that have been marked doomed (would have been deleted, but were
   // pending when the deletion was requested).
   std::unordered_set<const ReportingReport*> doomed_reports_;
 
diff --git a/net/reporting/reporting_cache_unittest.cc b/net/reporting/reporting_cache_unittest.cc
index be55cbb..08cd273 100644
--- a/net/reporting/reporting_cache_unittest.cc
+++ b/net/reporting/reporting_cache_unittest.cc
@@ -10,6 +10,7 @@
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/reporting/reporting_client.h"
+#include "net/reporting/reporting_observer.h"
 #include "net/reporting/reporting_report.h"
 #include "net/reporting/reporting_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -19,32 +20,54 @@
 namespace net {
 namespace {
 
-class ReportingCacheTest : public ::testing::Test {
+class TestReportingObserver : public ReportingObserver {
+ public:
+  TestReportingObserver() : cache_update_count_(0) {}
+
+  void OnCacheUpdated() override { ++cache_update_count_; }
+
+  int cache_update_count() const { return cache_update_count_; }
+
+ private:
+  int cache_update_count_;
+};
+
+class ReportingCacheTest : public ReportingTestBase {
  protected:
+  ReportingCacheTest() : ReportingTestBase() {
+    context()->AddObserver(&observer_);
+  }
+
+  ~ReportingCacheTest() override { context()->RemoveObserver(&observer_); }
+
+  TestReportingObserver* observer() { return &observer_; }
+
   const GURL kUrl1_ = GURL("https://origin1/path");
   const url::Origin kOrigin1_ = url::Origin(GURL("https://origin1/"));
   const url::Origin kOrigin2_ = url::Origin(GURL("https://origin2/"));
   const GURL kEndpoint1_ = GURL("https://endpoint1/");
   const GURL kEndpoint2_ = GURL("https://endpoint2/");
   const std::string kGroup1_ = "group1";
-  const std::string kGroup2_ = "group2";
+  const std::string kGroup2 = "group2";
   const std::string kType_ = "default";
   const base::TimeTicks kNow_ = base::TimeTicks::Now();
   const base::TimeTicks kExpires1_ = kNow_ + base::TimeDelta::FromDays(7);
   const base::TimeTicks kExpires2_ = kExpires1_ + base::TimeDelta::FromDays(7);
 
-  ReportingCache cache_;
+ private:
+  TestReportingObserver observer_;
 };
 
 TEST_F(ReportingCacheTest, Reports) {
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
 
-  cache_.AddReport(kUrl1_, kGroup1_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  cache()->AddReport(kUrl1_, kGroup1_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  EXPECT_EQ(1, observer()->cache_update_count());
 
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
   const ReportingReport* report = reports[0];
   ASSERT_TRUE(report);
@@ -54,103 +77,113 @@
   // TODO(juliatuttle): Check body?
   EXPECT_EQ(kNow_, report->queued);
   EXPECT_EQ(0, report->attempts);
-  EXPECT_FALSE(cache_.IsReportPendingForTesting(report));
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(report));
+  EXPECT_FALSE(cache()->IsReportPendingForTesting(report));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(report));
 
-  cache_.IncrementReportsAttempts(reports);
+  cache()->IncrementReportsAttempts(reports);
+  EXPECT_EQ(2, observer()->cache_update_count());
 
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
   report = reports[0];
   ASSERT_TRUE(report);
   EXPECT_EQ(1, report->attempts);
 
-  cache_.RemoveReports(reports);
+  cache()->RemoveReports(reports);
+  EXPECT_EQ(3, observer()->cache_update_count());
 
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
 }
 
 TEST_F(ReportingCacheTest, RemoveAllReports) {
-  cache_.AddReport(kUrl1_, kGroup1_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
-  cache_.AddReport(kUrl1_, kGroup1_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  cache()->AddReport(kUrl1_, kGroup1_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  cache()->AddReport(kUrl1_, kGroup1_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  EXPECT_EQ(2, observer()->cache_update_count());
 
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_EQ(2u, reports.size());
 
-  cache_.RemoveAllReports();
+  cache()->RemoveAllReports();
+  EXPECT_EQ(3, observer()->cache_update_count());
 
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
 }
 
 TEST_F(ReportingCacheTest, RemovePendingReports) {
-  cache_.AddReport(kUrl1_, kGroup1_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  cache()->AddReport(kUrl1_, kGroup1_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  EXPECT_EQ(1, observer()->cache_update_count());
 
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
-  EXPECT_FALSE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(reports[0]));
 
-  cache_.SetReportsPending(reports);
-  EXPECT_TRUE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(reports[0]));
+  cache()->SetReportsPending(reports);
+  EXPECT_TRUE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(reports[0]));
 
-  cache_.RemoveReports(reports);
-  EXPECT_TRUE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_TRUE(cache_.IsReportDoomedForTesting(reports[0]));
+  cache()->RemoveReports(reports);
+  EXPECT_TRUE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_TRUE(cache()->IsReportDoomedForTesting(reports[0]));
+  EXPECT_EQ(2, observer()->cache_update_count());
 
   // After removing report, future calls to GetReports should not return it.
   std::vector<const ReportingReport*> visible_reports;
-  cache_.GetReports(&visible_reports);
+  cache()->GetReports(&visible_reports);
   EXPECT_TRUE(visible_reports.empty());
-  EXPECT_EQ(1u, cache_.GetFullReportCountForTesting());
+  EXPECT_EQ(1u, cache()->GetFullReportCountForTesting());
 
   // After clearing pending flag, report should be deleted.
-  cache_.ClearReportsPending(reports);
-  EXPECT_EQ(0u, cache_.GetFullReportCountForTesting());
+  cache()->ClearReportsPending(reports);
+  EXPECT_EQ(0u, cache()->GetFullReportCountForTesting());
 }
 
 TEST_F(ReportingCacheTest, RemoveAllPendingReports) {
-  cache_.AddReport(kUrl1_, kGroup1_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  cache()->AddReport(kUrl1_, kGroup1_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(), kNow_, 0);
+  EXPECT_EQ(1, observer()->cache_update_count());
 
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
-  EXPECT_FALSE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(reports[0]));
 
-  cache_.SetReportsPending(reports);
-  EXPECT_TRUE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(reports[0]));
+  cache()->SetReportsPending(reports);
+  EXPECT_TRUE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(reports[0]));
 
-  cache_.RemoveAllReports();
-  EXPECT_TRUE(cache_.IsReportPendingForTesting(reports[0]));
-  EXPECT_TRUE(cache_.IsReportDoomedForTesting(reports[0]));
+  cache()->RemoveAllReports();
+  EXPECT_TRUE(cache()->IsReportPendingForTesting(reports[0]));
+  EXPECT_TRUE(cache()->IsReportDoomedForTesting(reports[0]));
+  EXPECT_EQ(2, observer()->cache_update_count());
 
   // After removing report, future calls to GetReports should not return it.
   std::vector<const ReportingReport*> visible_reports;
-  cache_.GetReports(&visible_reports);
+  cache()->GetReports(&visible_reports);
   EXPECT_TRUE(visible_reports.empty());
-  EXPECT_EQ(1u, cache_.GetFullReportCountForTesting());
+  EXPECT_EQ(1u, cache()->GetFullReportCountForTesting());
 
   // After clearing pending flag, report should be deleted.
-  cache_.ClearReportsPending(reports);
-  EXPECT_EQ(0u, cache_.GetFullReportCountForTesting());
+  cache()->ClearReportsPending(reports);
+  EXPECT_EQ(0u, cache()->GetFullReportCountForTesting());
 }
 
 TEST_F(ReportingCacheTest, Endpoints) {
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  EXPECT_EQ(1, observer()->cache_update_count());
 
   const ReportingClient* client =
-      FindClientInCache(&cache_, kOrigin1_, kEndpoint1_);
+      FindClientInCache(cache(), kOrigin1_, kEndpoint1_);
   ASSERT_TRUE(client);
   EXPECT_EQ(kOrigin1_, client->origin);
   EXPECT_EQ(kEndpoint1_, client->endpoint);
@@ -158,35 +191,37 @@
   EXPECT_EQ(kGroup1_, client->group);
   EXPECT_EQ(kExpires1_, client->expires);
 
-  // Replaces original configuration with new Subdomains, group, and expires
-  // values.
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::INCLUDE,
-                   kGroup2_, kExpires2_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::INCLUDE, kGroup2, kExpires2_);
+  EXPECT_EQ(2, observer()->cache_update_count());
 
-  client = FindClientInCache(&cache_, kOrigin1_, kEndpoint1_);
+  client = FindClientInCache(cache(), kOrigin1_, kEndpoint1_);
   ASSERT_TRUE(client);
   EXPECT_EQ(kOrigin1_, client->origin);
   EXPECT_EQ(kEndpoint1_, client->endpoint);
   EXPECT_EQ(ReportingClient::Subdomains::INCLUDE, client->subdomains);
-  EXPECT_EQ(kGroup2_, client->group);
+  EXPECT_EQ(kGroup2, client->group);
   EXPECT_EQ(kExpires2_, client->expires);
 
-  cache_.RemoveClients(std::vector<const ReportingClient*>{client});
+  cache()->RemoveClients(std::vector<const ReportingClient*>{client});
+  EXPECT_EQ(3, observer()->cache_update_count());
 
-  client = FindClientInCache(&cache_, kOrigin1_, kEndpoint1_);
+  client = FindClientInCache(cache(), kOrigin1_, kEndpoint1_);
   EXPECT_FALSE(client);
 }
 
 TEST_F(ReportingCacheTest, GetClientsForOriginAndGroup) {
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
-  cache_.SetClient(kOrigin1_, kEndpoint2_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup2_, kExpires1_);
-  cache_.SetClient(kOrigin2_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint2_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup2, kExpires1_);
+  cache()->SetClient(kOrigin2_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
 
   std::vector<const ReportingClient*> clients;
-  cache_.GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
   ASSERT_EQ(1u, clients.size());
   const ReportingClient* client = clients[0];
   ASSERT_TRUE(client);
@@ -195,57 +230,69 @@
 }
 
 TEST_F(ReportingCacheTest, RemoveClientForOriginAndEndpoint) {
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
-  cache_.SetClient(kOrigin1_, kEndpoint2_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup2_, kExpires1_);
-  cache_.SetClient(kOrigin2_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint2_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup2, kExpires1_);
+  cache()->SetClient(kOrigin2_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  EXPECT_EQ(3, observer()->cache_update_count());
 
-  cache_.RemoveClientForOriginAndEndpoint(kOrigin1_, kEndpoint1_);
+  cache()->RemoveClientForOriginAndEndpoint(kOrigin1_, kEndpoint1_);
+  EXPECT_EQ(4, observer()->cache_update_count());
 
   std::vector<const ReportingClient*> clients;
-  cache_.GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
   EXPECT_TRUE(clients.empty());
 
-  cache_.GetClientsForOriginAndGroup(kOrigin1_, kGroup2_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin1_, kGroup2, &clients);
   EXPECT_EQ(1u, clients.size());
 
-  cache_.GetClientsForOriginAndGroup(kOrigin2_, kGroup1_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin2_, kGroup1_, &clients);
   EXPECT_EQ(1u, clients.size());
 }
 
 TEST_F(ReportingCacheTest, RemoveClientsForEndpoint) {
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
-  cache_.SetClient(kOrigin1_, kEndpoint2_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup2_, kExpires1_);
-  cache_.SetClient(kOrigin2_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint2_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup2, kExpires1_);
+  cache()->SetClient(kOrigin2_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  EXPECT_EQ(3, observer()->cache_update_count());
 
-  cache_.RemoveClientsForEndpoint(kEndpoint1_);
+  cache()->RemoveClientsForEndpoint(kEndpoint1_);
+  EXPECT_EQ(4, observer()->cache_update_count());
 
   std::vector<const ReportingClient*> clients;
-  cache_.GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin1_, kGroup1_, &clients);
   EXPECT_TRUE(clients.empty());
 
-  cache_.GetClientsForOriginAndGroup(kOrigin1_, kGroup2_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin1_, kGroup2, &clients);
   EXPECT_EQ(1u, clients.size());
 
-  cache_.GetClientsForOriginAndGroup(kOrigin2_, kGroup1_, &clients);
+  cache()->GetClientsForOriginAndGroup(kOrigin2_, kGroup1_, &clients);
   EXPECT_TRUE(clients.empty());
 }
 
 TEST_F(ReportingCacheTest, RemoveAllClients) {
-  cache_.SetClient(kOrigin1_, kEndpoint1_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
-  cache_.SetClient(kOrigin2_, kEndpoint2_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup1_, kExpires1_);
+  cache()->SetClient(kOrigin1_, kEndpoint1_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  cache()->SetClient(kOrigin2_, kEndpoint2_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup1_,
+                     kExpires1_);
+  EXPECT_EQ(2, observer()->cache_update_count());
 
-  cache_.RemoveAllClients();
+  cache()->RemoveAllClients();
+  EXPECT_EQ(3, observer()->cache_update_count());
 
   std::vector<const ReportingClient*> clients;
-  cache_.GetClients(&clients);
+  cache()->GetClients(&clients);
   EXPECT_TRUE(clients.empty());
 }
 
diff --git a/net/reporting/reporting_context.cc b/net/reporting/reporting_context.cc
new file mode 100644
index 0000000..cc50dc7
--- /dev/null
+++ b/net/reporting/reporting_context.cc
@@ -0,0 +1,84 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_context.h"
+
+#include <memory>
+
+#include "base/memory/ptr_util.h"
+#include "base/observer_list.h"
+#include "base/time/clock.h"
+#include "base/time/default_clock.h"
+#include "base/time/default_tick_clock.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "net/base/backoff_entry.h"
+#include "net/reporting/reporting_cache.h"
+#include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_delivery_agent.h"
+#include "net/reporting/reporting_endpoint_manager.h"
+#include "net/reporting/reporting_observer.h"
+#include "net/reporting/reporting_policy.h"
+
+namespace net {
+
+class URLRequestContext;
+
+namespace {
+
+class ReportingContextImpl : public ReportingContext {
+ public:
+  ReportingContextImpl(const ReportingPolicy& policy,
+                       std::unique_ptr<ReportingDelegate> delegate,
+                       URLRequestContext* request_context)
+      : ReportingContext(policy,
+                         std::move(delegate),
+                         base::MakeUnique<base::DefaultClock>(),
+                         base::MakeUnique<base::DefaultTickClock>(),
+                         ReportingUploader::Create(request_context)) {}
+};
+
+}  // namespace
+
+// static
+std::unique_ptr<ReportingContext> ReportingContext::Create(
+    const ReportingPolicy& policy,
+    std::unique_ptr<ReportingDelegate> delegate,
+    URLRequestContext* request_context) {
+  return base::MakeUnique<ReportingContextImpl>(policy, std::move(delegate),
+                                                request_context);
+}
+
+ReportingContext::~ReportingContext() {}
+
+void ReportingContext::AddObserver(ReportingObserver* observer) {
+  DCHECK(!observers_.HasObserver(observer));
+  observers_.AddObserver(observer);
+}
+
+void ReportingContext::RemoveObserver(ReportingObserver* observer) {
+  DCHECK(observers_.HasObserver(observer));
+  observers_.RemoveObserver(observer);
+}
+
+void ReportingContext::NotifyCacheUpdated() {
+  for (auto& observer : observers_)
+    observer.OnCacheUpdated();
+}
+
+ReportingContext::ReportingContext(const ReportingPolicy& policy,
+                                   std::unique_ptr<ReportingDelegate> delegate,
+                                   std::unique_ptr<base::Clock> clock,
+                                   std::unique_ptr<base::TickClock> tick_clock,
+                                   std::unique_ptr<ReportingUploader> uploader)
+    : policy_(policy),
+      delegate_(std::move(delegate)),
+      clock_(std::move(clock)),
+      tick_clock_(std::move(tick_clock)),
+      uploader_(std::move(uploader)),
+      cache_(base::MakeUnique<ReportingCache>(this)),
+      endpoint_manager_(base::MakeUnique<ReportingEndpointManager>(this)),
+      delivery_agent_(base::MakeUnique<ReportingDeliveryAgent>(this)) {}
+
+}  // namespace net
diff --git a/net/reporting/reporting_context.h b/net/reporting/reporting_context.h
new file mode 100644
index 0000000..0242561
--- /dev/null
+++ b/net/reporting/reporting_context.h
@@ -0,0 +1,91 @@
+// Copyright 2017 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 NET_REPORTING_REPORTING_CONTEXT_H_
+#define NET_REPORTING_REPORTING_CONTEXT_H_
+
+#include <memory>
+
+#include "base/observer_list.h"
+#include "base/time/time.h"
+#include "net/base/backoff_entry.h"
+#include "net/base/net_export.h"
+#include "net/reporting/reporting_policy.h"
+
+namespace base {
+class Clock;
+class TickClock;
+}  // namespace base
+
+namespace net {
+
+class ReportingCache;
+class ReportingDelegate;
+class ReportingDeliveryAgent;
+class ReportingEndpointManager;
+class ReportingObserver;
+class ReportingUploader;
+class URLRequestContext;
+
+// Contains the various internal classes that make up the Reporting system.
+// Wrapped by ReportingService, which provides the external interface.
+class NET_EXPORT ReportingContext {
+ public:
+  static std::unique_ptr<ReportingContext> Create(
+      const ReportingPolicy& policy,
+      std::unique_ptr<ReportingDelegate> delegate,
+      URLRequestContext* request_context);
+
+  ~ReportingContext();
+
+  const ReportingPolicy& policy() { return policy_; }
+  ReportingDelegate* delegate() { return delegate_.get(); }
+
+  base::Clock* clock() { return clock_.get(); }
+  base::TickClock* tick_clock() { return tick_clock_.get(); }
+  ReportingUploader* uploader() { return uploader_.get(); }
+
+  ReportingCache* cache() { return cache_.get(); }
+  ReportingEndpointManager* endpoint_manager() {
+    return endpoint_manager_.get();
+  }
+  ReportingDeliveryAgent* delivery_agent() { return delivery_agent_.get(); }
+
+  void AddObserver(ReportingObserver* observer);
+  void RemoveObserver(ReportingObserver* observer);
+
+  void NotifyCacheUpdated();
+
+ protected:
+  ReportingContext(const ReportingPolicy& policy,
+                   std::unique_ptr<ReportingDelegate> delegate,
+                   std::unique_ptr<base::Clock> clock,
+                   std::unique_ptr<base::TickClock> tick_clock,
+                   std::unique_ptr<ReportingUploader> uploader);
+
+ private:
+  ReportingPolicy policy_;
+  std::unique_ptr<ReportingDelegate> delegate_;
+
+  std::unique_ptr<base::Clock> clock_;
+  std::unique_ptr<base::TickClock> tick_clock_;
+  std::unique_ptr<ReportingUploader> uploader_;
+
+  base::ObserverList<ReportingObserver, /* check_empty= */ true> observers_;
+
+  std::unique_ptr<ReportingCache> cache_;
+
+  // |endpoint_manager_| must come after |tick_clock_| and |cache_|.
+  std::unique_ptr<ReportingEndpointManager> endpoint_manager_;
+
+  // |delivery_agent_| must come after |tick_clock_|, |uploader_|, |cache_|,
+  // and |endpoint_manager_|.
+  std::unique_ptr<ReportingDeliveryAgent> delivery_agent_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReportingContext);
+};
+
+}  // namespace net
+
+#endif  // NET_REPORTING_REPORTING_CONTEXT_H_
diff --git a/net/reporting/reporting_delegate.cc b/net/reporting/reporting_delegate.cc
new file mode 100644
index 0000000..cef5baf
--- /dev/null
+++ b/net/reporting/reporting_delegate.cc
@@ -0,0 +1,13 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_delegate.h"
+
+namespace net {
+
+ReportingDelegate::~ReportingDelegate() {}
+
+ReportingDelegate::ReportingDelegate() {}
+
+}  // namespace net
diff --git a/net/reporting/reporting_delegate.h b/net/reporting/reporting_delegate.h
new file mode 100644
index 0000000..57d01ab
--- /dev/null
+++ b/net/reporting/reporting_delegate.h
@@ -0,0 +1,45 @@
+// Copyright 2017 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 NET_REPORTING_REPORTING_DELEGATE_H_
+#define NET_REPORTING_REPORTING_DELEGATE_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "net/base/net_export.h"
+
+namespace base {
+class Value;
+}  // namespace base
+
+namespace net {
+
+// Delegate for things that the Reporting system can't do by itself, like
+// persisting data across embedder restarts.
+class NET_EXPORT ReportingDelegate {
+ public:
+  virtual ~ReportingDelegate();
+
+  // Gets previously persisted data, if any is available. Returns a null pointer
+  // if no data is available. Can be called any number of times.
+  virtual std::unique_ptr<const base::Value> GetPersistedData() = 0;
+
+  // Sets data to be persisted across embedder restarts. Ideally, this data will
+  // be returned by any future calls to GetPersistedData() in this or future
+  // sessions (until newer data is persisted), but no guarantee is made, since
+  // the underlying persistence mechanism may or may not be reliable.
+  virtual void PersistData(
+      std::unique_ptr<const base::Value> persisted_data) = 0;
+
+ protected:
+  ReportingDelegate();
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ReportingDelegate);
+};
+
+}  // namespace net
+
+#endif  // NET_REPORTING_REPORTING_DELEGATE_H_
diff --git a/net/reporting/reporting_delivery_agent.cc b/net/reporting/reporting_delivery_agent.cc
index b0d54dd..f39dde9 100644
--- a/net/reporting/reporting_delivery_agent.cc
+++ b/net/reporting/reporting_delivery_agent.cc
@@ -48,16 +48,8 @@
 
 }  // namespace
 
-ReportingDeliveryAgent::ReportingDeliveryAgent(
-    base::TickClock* clock,
-    ReportingCache* cache,
-    ReportingUploader* uploader,
-    const BackoffEntry::Policy* endpoint_backoff_policy)
-    : clock_(clock),
-      cache_(cache),
-      uploader_(uploader),
-      endpoint_manager_(clock, cache, endpoint_backoff_policy),
-      weak_factory_(this) {}
+ReportingDeliveryAgent::ReportingDeliveryAgent(ReportingContext* context)
+    : context_(context), weak_factory_(this) {}
 ReportingDeliveryAgent::~ReportingDeliveryAgent() {}
 
 class ReportingDeliveryAgent::Delivery {
@@ -74,7 +66,7 @@
 
 void ReportingDeliveryAgent::SendReports() {
   std::vector<const ReportingReport*> reports;
-  cache_->GetReports(&reports);
+  cache()->GetReports(&reports);
 
   // Sort reports into (origin, group) buckets.
   std::map<OriginGroup, std::vector<const ReportingReport*>>
@@ -95,7 +87,7 @@
       continue;
 
     GURL endpoint_url;
-    if (!endpoint_manager_.FindEndpointForOriginAndGroup(
+    if (!endpoint_manager()->FindEndpointForOriginAndGroup(
             origin_group.first, origin_group.second, &endpoint_url)) {
       continue;
     }
@@ -110,13 +102,13 @@
     const GURL& endpoint = it.first;
     const std::vector<const ReportingReport*>& reports = it.second;
 
-    endpoint_manager_.SetEndpointPending(endpoint);
-    cache_->SetReportsPending(reports);
+    endpoint_manager()->SetEndpointPending(endpoint);
+    cache()->SetReportsPending(reports);
 
     std::string json;
-    SerializeReports(reports, clock_->NowTicks(), &json);
+    SerializeReports(reports, tick_clock()->NowTicks(), &json);
 
-    uploader_->StartUpload(
+    uploader()->StartUpload(
         endpoint, json,
         base::Bind(&ReportingDeliveryAgent::OnUploadComplete,
                    weak_factory_.GetWeakPtr(),
@@ -128,23 +120,23 @@
     const std::unique_ptr<Delivery>& delivery,
     ReportingUploader::Outcome outcome) {
   if (outcome == ReportingUploader::Outcome::SUCCESS) {
-    cache_->RemoveReports(delivery->reports);
-    endpoint_manager_.InformOfEndpointRequest(delivery->endpoint, true);
+    cache()->RemoveReports(delivery->reports);
+    endpoint_manager()->InformOfEndpointRequest(delivery->endpoint, true);
   } else {
-    cache_->IncrementReportsAttempts(delivery->reports);
-    endpoint_manager_.InformOfEndpointRequest(delivery->endpoint, false);
+    cache()->IncrementReportsAttempts(delivery->reports);
+    endpoint_manager()->InformOfEndpointRequest(delivery->endpoint, false);
   }
 
   if (outcome == ReportingUploader::Outcome::REMOVE_ENDPOINT)
-    cache_->RemoveClientsForEndpoint(delivery->endpoint);
+    cache()->RemoveClientsForEndpoint(delivery->endpoint);
 
   for (const ReportingReport* report : delivery->reports) {
     pending_origin_groups_.erase(
         OriginGroup(url::Origin(report->url), report->group));
   }
 
-  cache_->ClearReportsPending(delivery->reports);
-  endpoint_manager_.ClearEndpointPending(delivery->endpoint);
+  endpoint_manager()->ClearEndpointPending(delivery->endpoint);
+  cache()->ClearReportsPending(delivery->reports);
 }
 
 }  // namespace net
diff --git a/net/reporting/reporting_delivery_agent.h b/net/reporting/reporting_delivery_agent.h
index f6cda81..eaae8a3 100644
--- a/net/reporting/reporting_delivery_agent.h
+++ b/net/reporting/reporting_delivery_agent.h
@@ -14,7 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "net/base/backoff_entry.h"
 #include "net/base/net_export.h"
-#include "net/reporting/reporting_endpoint_manager.h"
+#include "net/reporting/reporting_context.h"
 #include "net/reporting/reporting_uploader.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -26,6 +26,7 @@
 namespace net {
 
 class ReportingCache;
+class ReportingEndpointManager;
 
 // Takes reports from the ReportingCache, assembles reports into deliveries to
 // endpoints, and sends those deliveries using ReportingUploader.
@@ -58,12 +59,8 @@
 // delivery attempt.
 class NET_EXPORT ReportingDeliveryAgent {
  public:
-  // |clock|, |cache|, |uploader|, and |endpoint_backoff_policy| must all
-  // outlive the ReportingDeliveryAgent.
-  ReportingDeliveryAgent(base::TickClock* clock,
-                         ReportingCache* cache,
-                         ReportingUploader* uploader,
-                         const BackoffEntry::Policy* endpoint_backoff_policy);
+  // |context| must outlive the ReportingDeliveryAgent.
+  ReportingDeliveryAgent(ReportingContext* context);
   ~ReportingDeliveryAgent();
 
   // Tries to deliver all of the reports in the cache. Reports that are already
@@ -79,11 +76,14 @@
   void OnUploadComplete(const std::unique_ptr<Delivery>& delivery,
                         ReportingUploader::Outcome outcome);
 
-  base::TickClock* clock_;
-  ReportingCache* cache_;
-  ReportingUploader* uploader_;
+  base::TickClock* tick_clock() { return context_->tick_clock(); }
+  ReportingCache* cache() { return context_->cache(); }
+  ReportingUploader* uploader() { return context_->uploader(); }
+  ReportingEndpointManager* endpoint_manager() {
+    return context_->endpoint_manager();
+  }
 
-  ReportingEndpointManager endpoint_manager_;
+  ReportingContext* context_;
 
   // Tracks OriginGroup tuples for which there is a pending delivery running.
   // (Would be an unordered_set, but there's no hash on pair.)
diff --git a/net/reporting/reporting_delivery_agent_unittest.cc b/net/reporting/reporting_delivery_agent_unittest.cc
index b2d8fbfd..d62f97c 100644
--- a/net/reporting/reporting_delivery_agent_unittest.cc
+++ b/net/reporting/reporting_delivery_agent_unittest.cc
@@ -24,88 +24,27 @@
 namespace net {
 namespace {
 
-class MockUploader : public ReportingUploader {
- public:
-  class PendingUpload {
-   public:
-    PendingUpload(MockUploader* uploader,
-                  const GURL& url,
-                  const std::string& json,
-                  const Callback& callback)
-        : uploader_(uploader), url_(url), json_(json), callback_(callback) {
-      DCHECK(uploader_);
-    }
-
-    ~PendingUpload() {}
-
-    void Complete(Outcome outcome) {
-      callback_.Run(outcome);
-      // Deletes |this|.
-      uploader_->OnUploadComplete(this);
-    }
-
-    const GURL& url() const { return url_; }
-    const std::string& json() const { return json_; }
-
-    std::unique_ptr<base::Value> GetValue() const {
-      return base::JSONReader::Read(json_);
-    }
-
-   private:
-    MockUploader* uploader_;
-    GURL url_;
-    std::string json_;
-    Callback callback_;
-  };
-
-  MockUploader() {}
-  ~MockUploader() override {}
-
-  void StartUpload(const GURL& url,
-                   const std::string& json,
-                   const Callback& callback) override {
-    uploads_.push_back(
-        base::MakeUnique<PendingUpload>(this, url, json, callback));
-  }
-
-  const std::vector<std::unique_ptr<PendingUpload>>& pending_uploads() const {
-    return uploads_;
-  }
-
-  void OnUploadComplete(PendingUpload* upload) {
-    for (auto it = uploads_.begin(); it != uploads_.end(); ++it) {
-      if (it->get() == upload) {
-        uploads_.erase(it);
-        return;
-      }
-    }
-    NOTREACHED();
-  }
-
- private:
-  std::vector<std::unique_ptr<PendingUpload>> uploads_;
-};
-
-class ReportingDeliveryAgentTest : public ::testing::Test {
+class ReportingDeliveryAgentTest : public ReportingTestBase {
  protected:
-  ReportingDeliveryAgentTest()
-      : agent_(&clock_, &cache_, &uploader_, &backoff_policy_) {
-    backoff_policy_.num_errors_to_ignore = 0;
-    backoff_policy_.initial_delay_ms = 60000;
-    backoff_policy_.multiply_factor = 2.0;
-    backoff_policy_.jitter_factor = 0.0;
-    backoff_policy_.maximum_backoff_ms = -1;
-    backoff_policy_.entry_lifetime_ms = 0;
-    backoff_policy_.always_use_initial_delay = false;
+  ReportingDeliveryAgentTest() {
+    ReportingPolicy policy;
+    policy.endpoint_backoff_policy.num_errors_to_ignore = 0;
+    policy.endpoint_backoff_policy.initial_delay_ms = 60000;
+    policy.endpoint_backoff_policy.multiply_factor = 2.0;
+    policy.endpoint_backoff_policy.jitter_factor = 0.0;
+    policy.endpoint_backoff_policy.maximum_backoff_ms = -1;
+    policy.endpoint_backoff_policy.entry_lifetime_ms = 0;
+    policy.endpoint_backoff_policy.always_use_initial_delay = false;
+    UsePolicy(policy);
   }
 
   base::TimeTicks tomorrow() {
-    return clock_.NowTicks() + base::TimeDelta::FromDays(1);
+    return tick_clock()->NowTicks() + base::TimeDelta::FromDays(1);
   }
 
-  const std::vector<std::unique_ptr<MockUploader::PendingUpload>>&
-  pending_uploads() const {
-    return uploader_.pending_uploads();
+  const std::vector<std::unique_ptr<TestReportingUploader::PendingUpload>>&
+  pending_uploads() {
+    return uploader()->pending_uploads();
   }
 
   const GURL kUrl_ = GURL("https://origin/path");
@@ -113,12 +52,6 @@
   const GURL kEndpoint_ = GURL("https://endpoint/");
   const std::string kGroup_ = "group";
   const std::string kType_ = "type";
-
-  base::SimpleTestTickClock clock_;
-  ReportingCache cache_;
-  MockUploader uploader_;
-  BackoffEntry::Policy backoff_policy_;
-  ReportingDeliveryAgent agent_;
 };
 
 TEST_F(ReportingDeliveryAgentTest, SuccessfulUpload) {
@@ -127,14 +60,14 @@
   base::DictionaryValue body;
   body.SetString("key", "value");
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.AddReport(kUrl_, kGroup_, kType_, body.CreateDeepCopy(),
-                   clock_.NowTicks(), 0);
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->AddReport(kUrl_, kGroup_, kType_, body.CreateDeepCopy(),
+                     tick_clock()->NowTicks(), 0);
 
-  clock_.Advance(base::TimeDelta::FromMilliseconds(kAgeMillis));
+  tick_clock()->Advance(base::TimeDelta::FromMilliseconds(kAgeMillis));
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
 
   ASSERT_EQ(1u, pending_uploads().size());
   EXPECT_EQ(kEndpoint_, pending_uploads()[0]->url());
@@ -158,52 +91,52 @@
 
   // Successful upload should remove delivered reports.
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
 
   // TODO(juliatuttle): Check that BackoffEntry was informed of success.
 }
 
 TEST_F(ReportingDeliveryAgentTest, FailedUpload) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
 
   ASSERT_EQ(1u, pending_uploads().size());
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::FAILURE);
 
   // Failed upload should increment reports' attempts.
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
   EXPECT_EQ(1, reports[0]->attempts);
 
   // Since endpoint is now failing, an upload won't be started despite a pending
   // report.
   ASSERT_TRUE(pending_uploads().empty());
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   EXPECT_TRUE(pending_uploads().empty());
 }
 
 TEST_F(ReportingDeliveryAgentTest, RemoveEndpointUpload) {
   static const url::Origin kDifferentOrigin(GURL("https://origin2/"));
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kDifferentOrigin, kEndpoint_,
-                   ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
-  ASSERT_TRUE(FindClientInCache(&cache_, kOrigin_, kEndpoint_));
-  ASSERT_TRUE(FindClientInCache(&cache_, kDifferentOrigin, kEndpoint_));
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->SetClient(kDifferentOrigin, kEndpoint_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+  ASSERT_TRUE(FindClientInCache(cache(), kOrigin_, kEndpoint_));
+  ASSERT_TRUE(FindClientInCache(cache(), kDifferentOrigin, kEndpoint_));
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
 
   ASSERT_EQ(1u, pending_uploads().size());
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::REMOVE_ENDPOINT);
@@ -211,50 +144,50 @@
   // "Remove endpoint" upload should remove endpoint from *all* origins and
   // increment reports' attempts.
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   ASSERT_EQ(1u, reports.size());
   EXPECT_EQ(1, reports[0]->attempts);
 
-  EXPECT_FALSE(FindClientInCache(&cache_, kOrigin_, kEndpoint_));
-  EXPECT_FALSE(FindClientInCache(&cache_, kDifferentOrigin, kEndpoint_));
+  EXPECT_FALSE(FindClientInCache(cache(), kOrigin_, kEndpoint_));
+  EXPECT_FALSE(FindClientInCache(cache(), kDifferentOrigin, kEndpoint_));
 
   // Since endpoint is now failing, an upload won't be started despite a pending
   // report.
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   EXPECT_TRUE(pending_uploads().empty());
 }
 
 TEST_F(ReportingDeliveryAgentTest, ConcurrentRemove) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   ASSERT_EQ(1u, pending_uploads().size());
 
   // Remove the report while the upload is running.
   std::vector<const ReportingReport*> reports;
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_EQ(1u, reports.size());
 
   const ReportingReport* report = reports[0];
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(report));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(report));
 
   // Report should appear removed, even though the cache has doomed it.
-  cache_.RemoveReports(reports);
-  cache_.GetReports(&reports);
+  cache()->RemoveReports(reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
-  EXPECT_TRUE(cache_.IsReportDoomedForTesting(report));
+  EXPECT_TRUE(cache()->IsReportDoomedForTesting(report));
 
   // Completing upload shouldn't crash, and report should still be gone.
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
-  cache_.GetReports(&reports);
+  cache()->GetReports(&reports);
   EXPECT_TRUE(reports.empty());
   // This is slightly sketchy since |report| has been freed, but it nonetheless
   // should not be in the set of doomed reports.
-  EXPECT_FALSE(cache_.IsReportDoomedForTesting(report));
+  EXPECT_FALSE(cache()->IsReportDoomedForTesting(report));
 }
 
 // Test that the agent will combine reports destined for the same endpoint, even
@@ -264,19 +197,19 @@
   static const GURL kDifferentUrl("https://origin2/path");
   static const url::Origin kDifferentOrigin(kDifferentUrl);
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kDifferentOrigin, kEndpoint_,
-                   ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->SetClient(kDifferentOrigin, kEndpoint_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
-  cache_.AddReport(kDifferentUrl, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
+  cache()->AddReport(kDifferentUrl, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   ASSERT_EQ(1u, pending_uploads().size());
 
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
@@ -290,29 +223,29 @@
   static const GURL kDifferentUrl("https://origin2/path");
   static const url::Origin kDifferentOrigin(kDifferentUrl);
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kDifferentOrigin, kEndpoint_,
-                   ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->SetClient(kDifferentOrigin, kEndpoint_,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   EXPECT_EQ(1u, pending_uploads().size());
 
-  cache_.AddReport(kDifferentUrl, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kDifferentUrl, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   ASSERT_EQ(1u, pending_uploads().size());
 
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
   EXPECT_EQ(0u, pending_uploads().size());
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   ASSERT_EQ(1u, pending_uploads().size());
 
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
@@ -325,30 +258,33 @@
 TEST_F(ReportingDeliveryAgentTest, SerializeUploadsToGroup) {
   static const GURL kDifferentEndpoint("https://endpoint2/");
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kOrigin_, kDifferentEndpoint,
-                   ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kDifferentEndpoint,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
+  delivery_agent()->SendReports();
   EXPECT_EQ(1u, pending_uploads().size());
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
-  EXPECT_EQ(1u, pending_uploads().size());
+  delivery_agent()->SendReports();
+  ASSERT_EQ(1u, pending_uploads().size());
 
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
   EXPECT_EQ(0u, pending_uploads().size());
 
-  agent_.SendReports();
-  EXPECT_EQ(1u, pending_uploads().size());
+  delivery_agent()->SendReports();
+  ASSERT_EQ(1u, pending_uploads().size());
+
+  pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
+  EXPECT_EQ(0u, pending_uploads().size());
 }
 
 // Tests that the agent will start parallel uploads to different groups within
@@ -357,21 +293,21 @@
   static const GURL kDifferentEndpoint("https://endpoint2/");
   static const std::string kDifferentGroup("group2");
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kOrigin_, kDifferentEndpoint,
-                   ReportingClient::Subdomains::EXCLUDE, kDifferentGroup,
-                   tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kDifferentEndpoint,
+                     ReportingClient::Subdomains::EXCLUDE, kDifferentGroup,
+                     tomorrow());
 
-  cache_.AddReport(kUrl_, kGroup_, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
-  cache_.AddReport(kUrl_, kDifferentGroup, kType_,
-                   base::MakeUnique<base::DictionaryValue>(), clock_.NowTicks(),
-                   0);
+  cache()->AddReport(kUrl_, kGroup_, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
+  cache()->AddReport(kUrl_, kDifferentGroup, kType_,
+                     base::MakeUnique<base::DictionaryValue>(),
+                     tick_clock()->NowTicks(), 0);
 
-  agent_.SendReports();
-  EXPECT_EQ(2u, pending_uploads().size());
+  delivery_agent()->SendReports();
+  ASSERT_EQ(2u, pending_uploads().size());
 
   pending_uploads()[1]->Complete(ReportingUploader::Outcome::SUCCESS);
   pending_uploads()[0]->Complete(ReportingUploader::Outcome::SUCCESS);
diff --git a/net/reporting/reporting_endpoint_manager.cc b/net/reporting/reporting_endpoint_manager.cc
index 6333a9a..3d533da 100644
--- a/net/reporting/reporting_endpoint_manager.cc
+++ b/net/reporting/reporting_endpoint_manager.cc
@@ -15,16 +15,14 @@
 #include "net/base/backoff_entry.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_client.h"
+#include "net/reporting/reporting_policy.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 namespace net {
 
-ReportingEndpointManager::ReportingEndpointManager(
-    base::TickClock* clock,
-    const ReportingCache* cache,
-    const BackoffEntry::Policy* backoff_policy)
-    : clock_(clock), cache_(cache), backoff_policy_(backoff_policy) {}
+ReportingEndpointManager::ReportingEndpointManager(ReportingContext* context)
+    : context_(context) {}
 
 ReportingEndpointManager::~ReportingEndpointManager() {}
 
@@ -33,11 +31,11 @@
     const std::string& group,
     GURL* endpoint_url_out) {
   std::vector<const ReportingClient*> clients;
-  cache_->GetClientsForOriginAndGroup(origin, group, &clients);
+  cache()->GetClientsForOriginAndGroup(origin, group, &clients);
 
   // Filter out expired, pending, and backed-off endpoints.
   std::vector<const ReportingClient*> available_clients;
-  base::TimeTicks now = clock_->NowTicks();
+  base::TimeTicks now = tick_clock()->NowTicks();
   for (const ReportingClient* client : clients) {
     if (client->expires < now)
       continue;
@@ -73,8 +71,8 @@
 void ReportingEndpointManager::InformOfEndpointRequest(const GURL& endpoint,
                                                        bool succeeded) {
   if (!base::ContainsKey(endpoint_backoff_, endpoint)) {
-    endpoint_backoff_[endpoint] =
-        base::MakeUnique<BackoffEntry>(backoff_policy_, clock_);
+    endpoint_backoff_[endpoint] = base::MakeUnique<BackoffEntry>(
+        &policy().endpoint_backoff_policy, tick_clock());
   }
   endpoint_backoff_[endpoint]->InformOfRequest(succeeded);
 }
diff --git a/net/reporting/reporting_endpoint_manager.h b/net/reporting/reporting_endpoint_manager.h
index c550d5e..e3cc827 100644
--- a/net/reporting/reporting_endpoint_manager.h
+++ b/net/reporting/reporting_endpoint_manager.h
@@ -11,8 +11,10 @@
 #include <string>
 
 #include "base/macros.h"
+#include "base/time/tick_clock.h"
 #include "net/base/backoff_entry.h"
 #include "net/base/net_export.h"
+#include "net/reporting/reporting_context.h"
 
 class GURL;
 
@@ -27,16 +29,15 @@
 namespace net {
 
 class ReportingCache;
+struct ReportingPolicy;
 
 // Keeps track of which endpoints are pending (have active delivery attempts to
 // them) or in exponential backoff after one or more failures, and chooses an
 // endpoint from an endpoint group to receive reports for an origin.
 class NET_EXPORT ReportingEndpointManager {
  public:
-  // Note: All three parameters must outlive the endpoint manager.
-  ReportingEndpointManager(base::TickClock* clock,
-                           const ReportingCache* cache,
-                           const BackoffEntry::Policy* backoff_policy);
+  // |context| must outlive the ReportingEndpointManager.
+  ReportingEndpointManager(ReportingContext* context);
   ~ReportingEndpointManager();
 
   // Finds an endpoint configured by |origin| in group |group| that is not
@@ -64,9 +65,11 @@
   void InformOfEndpointRequest(const GURL& endpoint, bool succeeded);
 
  private:
-  base::TickClock* clock_;
-  const ReportingCache* cache_;
-  const BackoffEntry::Policy* backoff_policy_;
+  const ReportingPolicy& policy() { return context_->policy(); }
+  base::TickClock* tick_clock() { return context_->tick_clock(); }
+  ReportingCache* cache() { return context_->cache(); }
+
+  ReportingContext* context_;
 
   std::set<GURL> pending_endpoints_;
   std::map<GURL, std::unique_ptr<net::BackoffEntry>> endpoint_backoff_;
diff --git a/net/reporting/reporting_endpoint_manager_unittest.cc b/net/reporting/reporting_endpoint_manager_unittest.cc
index f1d5003..113128db 100644
--- a/net/reporting/reporting_endpoint_manager_unittest.cc
+++ b/net/reporting/reporting_endpoint_manager_unittest.cc
@@ -11,6 +11,7 @@
 #include "net/base/backoff_entry.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_client.h"
+#include "net/reporting/reporting_policy.h"
 #include "net/reporting/reporting_test_util.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -19,175 +20,150 @@
 namespace net {
 namespace {
 
-class ReportingEndpointManagerTest : public ::testing::Test {
+class ReportingEndpointManagerTest : public ReportingTestBase {
  protected:
-  ReportingEndpointManagerTest()
-      : manager_(&clock_, &cache_, &backoff_policy_) {
-    backoff_policy_.num_errors_to_ignore = 0;
-    backoff_policy_.initial_delay_ms = 60000;
-    backoff_policy_.multiply_factor = 2.0;
-    backoff_policy_.jitter_factor = 0.0;
-    backoff_policy_.maximum_backoff_ms = -1;
-    backoff_policy_.entry_lifetime_ms = 0;
-    backoff_policy_.always_use_initial_delay = false;
-  }
-
-  base::TimeTicks yesterday() {
-    return clock_.NowTicks() - base::TimeDelta::FromDays(1);
-  }
-
-  base::TimeTicks tomorrow() {
-    return clock_.NowTicks() + base::TimeDelta::FromDays(1);
-  }
-
   const url::Origin kOrigin_ = url::Origin(GURL("https://origin/"));
   const GURL kEndpoint_ = GURL("https://endpoint/");
   const std::string kGroup_ = "group";
-
-  base::SimpleTestTickClock clock_;
-  ReportingCache cache_;
-  BackoffEntry::Policy backoff_policy_;
-  ReportingEndpointManager manager_;
 };
 
 TEST_F(ReportingEndpointManagerTest, NoEndpoint) {
   GURL endpoint_url;
-  bool found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 }
 
 TEST_F(ReportingEndpointManagerTest, Endpoint) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
 
   GURL endpoint_url;
-  bool found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_TRUE(found_endpoint);
   EXPECT_EQ(kEndpoint_, endpoint_url);
 }
 
 TEST_F(ReportingEndpointManagerTest, ExpiredEndpoint) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, yesterday());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, yesterday());
 
   GURL endpoint_url;
-  bool found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 }
 
 TEST_F(ReportingEndpointManagerTest, PendingEndpoint) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
 
-  manager_.SetEndpointPending(kEndpoint_);
+  endpoint_manager()->SetEndpointPending(kEndpoint_);
 
   GURL endpoint_url;
-  bool found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 
-  manager_.ClearEndpointPending(kEndpoint_);
+  endpoint_manager()->ClearEndpointPending(kEndpoint_);
 
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_TRUE(found_endpoint);
   EXPECT_EQ(kEndpoint_, endpoint_url);
 }
 
 TEST_F(ReportingEndpointManagerTest, BackedOffEndpoint) {
-  ASSERT_EQ(2.0, backoff_policy_.multiply_factor);
+  ASSERT_EQ(2.0, policy().endpoint_backoff_policy.multiply_factor);
 
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
+  base::TimeDelta initial_delay = base::TimeDelta::FromMilliseconds(
+      policy().endpoint_backoff_policy.initial_delay_ms);
 
-  manager_.InformOfEndpointRequest(kEndpoint_, false);
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_, tomorrow());
+
+  endpoint_manager()->InformOfEndpointRequest(kEndpoint_, false);
 
   // After one failure, endpoint is in exponential backoff.
   GURL endpoint_url;
-  bool found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 
   // After initial delay, endpoint is usable again.
-  clock_.Advance(
-      base::TimeDelta::FromMilliseconds(backoff_policy_.initial_delay_ms));
+  tick_clock()->Advance(initial_delay);
 
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_TRUE(found_endpoint);
   EXPECT_EQ(kEndpoint_, endpoint_url);
 
-  manager_.InformOfEndpointRequest(kEndpoint_, false);
+  endpoint_manager()->InformOfEndpointRequest(kEndpoint_, false);
 
   // After a second failure, endpoint is backed off again.
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 
-  clock_.Advance(
-      base::TimeDelta::FromMilliseconds(backoff_policy_.initial_delay_ms));
+  tick_clock()->Advance(initial_delay);
 
   // Next backoff is longer -- 2x the first -- so endpoint isn't usable yet.
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 
-  clock_.Advance(
-      base::TimeDelta::FromMilliseconds(backoff_policy_.initial_delay_ms));
+  tick_clock()->Advance(initial_delay);
 
   // After 2x the initial delay, the endpoint is usable again.
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_TRUE(found_endpoint);
   EXPECT_EQ(kEndpoint_, endpoint_url);
 
-  manager_.InformOfEndpointRequest(kEndpoint_, true);
-  manager_.InformOfEndpointRequest(kEndpoint_, true);
+  endpoint_manager()->InformOfEndpointRequest(kEndpoint_, true);
+  endpoint_manager()->InformOfEndpointRequest(kEndpoint_, true);
 
   // Two more successful requests should reset the backoff to the initial delay
   // again.
-  manager_.InformOfEndpointRequest(kEndpoint_, false);
+  endpoint_manager()->InformOfEndpointRequest(kEndpoint_, false);
 
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_FALSE(found_endpoint);
 
-  clock_.Advance(
-      base::TimeDelta::FromMilliseconds(backoff_policy_.initial_delay_ms));
+  tick_clock()->Advance(initial_delay);
 
-  found_endpoint =
-      manager_.FindEndpointForOriginAndGroup(kOrigin_, kGroup_, &endpoint_url);
+  found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
+      kOrigin_, kGroup_, &endpoint_url);
   EXPECT_TRUE(found_endpoint);
 }
 
 // Make sure that multiple endpoints will all be returned at some point, to
 // avoid accidentally or intentionally implementing any priority ordering.
 TEST_F(ReportingEndpointManagerTest, RandomEndpoint) {
-  static const GURL kEndpoint1("https://endpoint1/");
-  static const GURL kEndpoint2("https://endpoint2/");
+  static const GURL kEndpoint_1("https://endpoint1/");
+  static const GURL kEndpoint_2("https://endpoint2/");
   static const int kMaxAttempts = 20;
 
-  cache_.SetClient(kOrigin_, kEndpoint1, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
-  cache_.SetClient(kOrigin_, kEndpoint2, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_1,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
+  cache()->SetClient(kOrigin_, kEndpoint_2,
+                     ReportingClient::Subdomains::EXCLUDE, kGroup_, tomorrow());
 
   bool endpoint1_seen = false;
   bool endpoint2_seen = false;
 
   for (int i = 0; i < kMaxAttempts; i++) {
     GURL endpoint_url;
-    bool found_endpoint = manager_.FindEndpointForOriginAndGroup(
+    bool found_endpoint = endpoint_manager()->FindEndpointForOriginAndGroup(
         kOrigin_, kGroup_, &endpoint_url);
     ASSERT_TRUE(found_endpoint);
-    ASSERT_TRUE(endpoint_url == kEndpoint1 || endpoint_url == kEndpoint2);
+    ASSERT_TRUE(endpoint_url == kEndpoint_1 || endpoint_url == kEndpoint_2);
 
-    if (endpoint_url == kEndpoint1)
+    if (endpoint_url == kEndpoint_1)
       endpoint1_seen = true;
-    else if (endpoint_url == kEndpoint2)
+    else if (endpoint_url == kEndpoint_2)
       endpoint2_seen = true;
 
     if (endpoint1_seen && endpoint2_seen)
diff --git a/net/reporting/reporting_header_parser.cc b/net/reporting/reporting_header_parser.cc
index 2bf6ddc3..8dbcb8e 100644
--- a/net/reporting/reporting_header_parser.cc
+++ b/net/reporting/reporting_header_parser.cc
@@ -8,11 +8,11 @@
 
 #include "base/json/json_reader.h"
 #include "base/logging.h"
+#include "base/time/tick_clock.h"
 #include "base/time/time.h"
 #include "base/values.h"
 #include "net/reporting/reporting_cache.h"
-#include "url/gurl.h"
-#include "url/origin.h"
+#include "net/reporting/reporting_context.h"
 
 namespace net {
 
@@ -27,8 +27,7 @@
 }  // namespace
 
 // static
-void ReportingHeaderParser::ParseHeader(ReportingCache* cache,
-                                        base::TimeTicks now,
+void ReportingHeaderParser::ParseHeader(ReportingContext* context,
                                         const GURL& url,
                                         const std::string& json_value) {
   DCHECK(url.SchemeIsCryptographic());
@@ -42,6 +41,8 @@
   bool is_list = value->GetAsList(&list);
   DCHECK(is_list);
 
+  ReportingCache* cache = context->cache();
+  base::TimeTicks now = context->tick_clock()->NowTicks();
   for (size_t i = 0; i < list->GetSize(); i++) {
     const base::Value* endpoint = nullptr;
     bool got_endpoint = list->Get(i, &endpoint);
diff --git a/net/reporting/reporting_header_parser.h b/net/reporting/reporting_header_parser.h
index 22fe4f3..2d7ac6ee 100644
--- a/net/reporting/reporting_header_parser.h
+++ b/net/reporting/reporting_header_parser.h
@@ -20,16 +20,11 @@
 namespace net {
 
 class ReportingCache;
+class ReportingContext;
 
-// Parses Report-To headers and writes the endpoint configurations to the
-// ReportingCache.
 class NET_EXPORT ReportingHeaderParser {
  public:
-  // Parses the normalized Report-To header value |json_value| sent in response
-  // to a request for |url| at |now|, and applies the endpoint configurations
-  // to |cache|.
-  static void ParseHeader(ReportingCache* cache,
-                          base::TimeTicks now,
+  static void ParseHeader(ReportingContext* context,
                           const GURL& url,
                           const std::string& json_value);
 
diff --git a/net/reporting/reporting_header_parser_unittest.cc b/net/reporting/reporting_header_parser_unittest.cc
index a4f70e2..69210f2 100644
--- a/net/reporting/reporting_header_parser_unittest.cc
+++ b/net/reporting/reporting_header_parser_unittest.cc
@@ -21,21 +21,13 @@
 namespace net {
 namespace {
 
-class ReportingHeaderParserTest : public ::testing::Test {
+class ReportingHeaderParserTest : public ReportingTestBase {
  protected:
-  void ParseHeader(const GURL& url, const std::string& header_value) {
-    ReportingHeaderParser::ParseHeader(&cache_, clock_.NowTicks(), url,
-                                       header_value);
-  }
-
   const GURL kUrl_ = GURL("https://origin/path");
   const url::Origin kOrigin_ = url::Origin(GURL("https://origin/"));
   const GURL kEndpoint_ = GURL("https://endpoint/");
   const std::string kGroup_ = "group";
   const std::string kType_ = "type";
-
-  base::SimpleTestTickClock clock_;
-  ReportingCache cache_;
 };
 
 TEST_F(ReportingHeaderParserTest, Invalid) {
@@ -64,10 +56,11 @@
 
   for (size_t i = 0; i < arraysize(kInvalidHeaderTestCases); ++i) {
     auto& test_case = kInvalidHeaderTestCases[i];
-    ParseHeader(kUrl_, test_case.header_value);
+    ReportingHeaderParser::ParseHeader(context(), kUrl_,
+                                       test_case.header_value);
 
     std::vector<const ReportingClient*> clients;
-    cache_.GetClients(&clients);
+    cache()->GetClients(&clients);
     EXPECT_TRUE(clients.empty())
         << "Invalid Report-To header (" << test_case.description << ": \""
         << test_case.header_value << "\") parsed as valid.";
@@ -75,36 +68,41 @@
 }
 
 TEST_F(ReportingHeaderParserTest, Valid) {
-  ParseHeader(kUrl_,
-              "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
+  ReportingHeaderParser::ParseHeader(
+      context(), kUrl_,
+      "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":86400}");
 
   const ReportingClient* client =
-      FindClientInCache(&cache_, kOrigin_, kEndpoint_);
+      FindClientInCache(cache(), kOrigin_, kEndpoint_);
   ASSERT_TRUE(client);
   EXPECT_EQ(kOrigin_, client->origin);
   EXPECT_EQ(kEndpoint_, client->endpoint);
   EXPECT_EQ(ReportingClient::Subdomains::EXCLUDE, client->subdomains);
-  EXPECT_EQ(86400, (client->expires - clock_.NowTicks()).InSeconds());
+  EXPECT_EQ(86400, (client->expires - tick_clock()->NowTicks()).InSeconds());
 }
 
 TEST_F(ReportingHeaderParserTest, Subdomains) {
-  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
-                         "\",\"max-age\":86400,"
-                         "\"includeSubdomains\":true}");
+  ReportingHeaderParser::ParseHeader(context(), kUrl_,
+                                     "{\"url\":\"" + kEndpoint_.spec() +
+                                         "\",\"max-age\":86400,"
+                                         "\"includeSubdomains\":true}");
 
   const ReportingClient* client =
-      FindClientInCache(&cache_, kOrigin_, kEndpoint_);
+      FindClientInCache(cache(), kOrigin_, kEndpoint_);
   ASSERT_TRUE(client);
   EXPECT_EQ(ReportingClient::Subdomains::INCLUDE, client->subdomains);
 }
 
 TEST_F(ReportingHeaderParserTest, ZeroMaxAge) {
-  cache_.SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
-                   kGroup_, clock_.NowTicks() + base::TimeDelta::FromDays(1));
+  cache()->SetClient(kOrigin_, kEndpoint_, ReportingClient::Subdomains::EXCLUDE,
+                     kGroup_,
+                     tick_clock()->NowTicks() + base::TimeDelta::FromDays(1));
 
-  ParseHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":0}");
+  ReportingHeaderParser::ParseHeader(
+      context(), kUrl_,
+      "{\"url\":\"" + kEndpoint_.spec() + "\",\"max-age\":0}");
 
-  EXPECT_EQ(nullptr, FindClientInCache(&cache_, kOrigin_, kEndpoint_));
+  EXPECT_EQ(nullptr, FindClientInCache(cache(), kOrigin_, kEndpoint_));
 }
 
 }  // namespace
diff --git a/net/reporting/reporting_observer.cc b/net/reporting/reporting_observer.cc
new file mode 100644
index 0000000..5e8d778
--- /dev/null
+++ b/net/reporting/reporting_observer.cc
@@ -0,0 +1,15 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_observer.h"
+
+namespace net {
+
+void ReportingObserver::OnCacheUpdated() {}
+
+ReportingObserver::ReportingObserver() {}
+
+ReportingObserver::~ReportingObserver() {}
+
+}  // namespace net
diff --git a/net/reporting/reporting_observer.h b/net/reporting/reporting_observer.h
new file mode 100644
index 0000000..fe2ab5c
--- /dev/null
+++ b/net/reporting/reporting_observer.h
@@ -0,0 +1,27 @@
+// Copyright 2017 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 NET_REPORTING_REPORTING_OBSERVER_H_
+#define NET_REPORTING_REPORTING_OBSERVER_H_
+
+#include "base/macros.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+class NET_EXPORT ReportingObserver {
+ public:
+  virtual void OnCacheUpdated();
+
+ protected:
+  ReportingObserver();
+
+  ~ReportingObserver();
+
+  DISALLOW_COPY_AND_ASSIGN(ReportingObserver);
+};
+
+}  // namespace net
+
+#endif  // NET_REPORTING_REPORTING_OBSERVER_H_
diff --git a/net/reporting/reporting_policy.cc b/net/reporting/reporting_policy.cc
new file mode 100644
index 0000000..6d36f84
--- /dev/null
+++ b/net/reporting/reporting_policy.cc
@@ -0,0 +1,24 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_policy.h"
+
+namespace net {
+
+ReportingPolicy::ReportingPolicy() {
+  endpoint_backoff_policy.num_errors_to_ignore = 0;
+  endpoint_backoff_policy.initial_delay_ms = 60 * 1000;  // 1 minute
+  endpoint_backoff_policy.multiply_factor = 2.0;
+  endpoint_backoff_policy.jitter_factor = 0.1;
+  endpoint_backoff_policy.maximum_backoff_ms = -1;  // 1 hour
+  endpoint_backoff_policy.entry_lifetime_ms = -1;   // infinite
+  endpoint_backoff_policy.always_use_initial_delay = false;
+}
+
+ReportingPolicy::ReportingPolicy(const ReportingPolicy& other)
+    : endpoint_backoff_policy(other.endpoint_backoff_policy) {}
+
+ReportingPolicy::~ReportingPolicy() {}
+
+}  // namespace net
diff --git a/net/reporting/reporting_policy.h b/net/reporting/reporting_policy.h
new file mode 100644
index 0000000..5f472d7f
--- /dev/null
+++ b/net/reporting/reporting_policy.h
@@ -0,0 +1,27 @@
+// Copyright 2017 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 NET_REPORTING_REPORTING_POLICY_H_
+#define NET_REPORTING_REPORTING_POLICY_H_
+
+#include "base/time/time.h"
+#include "net/base/backoff_entry.h"
+#include "net/base/net_export.h"
+
+namespace net {
+
+// Various policy knobs for the Reporting system.
+struct NET_EXPORT ReportingPolicy {
+  // Provides a reasonable default for use in a browser embedder.
+  ReportingPolicy();
+  ReportingPolicy(const ReportingPolicy& other);
+  ~ReportingPolicy();
+
+  // Backoff policy for failing endpoints.
+  BackoffEntry::Policy endpoint_backoff_policy;
+};
+
+}  // namespace net
+
+#endif  // NET_REPORTING_REPORTING_POLICY_H_
diff --git a/net/reporting/reporting_service.cc b/net/reporting/reporting_service.cc
new file mode 100644
index 0000000..128c510
--- /dev/null
+++ b/net/reporting/reporting_service.cc
@@ -0,0 +1,70 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_service.h"
+
+#include <memory>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "base/memory/ptr_util.h"
+#include "base/time/tick_clock.h"
+#include "base/time/time.h"
+#include "base/values.h"
+#include "net/reporting/reporting_cache.h"
+#include "net/reporting/reporting_context.h"
+#include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_header_parser.h"
+#include "url/gurl.h"
+
+namespace net {
+
+namespace {
+
+class ReportingServiceImpl : public ReportingService {
+ public:
+  ReportingServiceImpl(std::unique_ptr<ReportingContext> context)
+      : context_(std::move(context)) {}
+
+  ~ReportingServiceImpl() override {}
+
+  void QueueReport(const GURL& url,
+                   const std::string& group,
+                   const std::string& type,
+                   std::unique_ptr<const base::Value> body) override {
+    context_->cache()->AddReport(url, group, type, std::move(body),
+                                 context_->tick_clock()->NowTicks(), 0);
+  }
+
+  void ProcessHeader(const GURL& url,
+                     const std::string& header_value) override {
+    ReportingHeaderParser::ParseHeader(context_.get(), url, header_value);
+  }
+
+ private:
+  std::unique_ptr<ReportingContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReportingServiceImpl);
+};
+
+}  // namespace
+
+ReportingService::~ReportingService() {}
+
+// static
+std::unique_ptr<ReportingService> ReportingService::Create(
+    const ReportingPolicy& policy,
+    URLRequestContext* request_context,
+    std::unique_ptr<ReportingDelegate> delegate) {
+  return base::MakeUnique<ReportingServiceImpl>(
+      ReportingContext::Create(policy, std::move(delegate), request_context));
+}
+
+// static
+std::unique_ptr<ReportingService> ReportingService::CreateForTesting(
+    std::unique_ptr<ReportingContext> reporting_context) {
+  return base::MakeUnique<ReportingServiceImpl>(std::move(reporting_context));
+}
+
+}  // namespace net
diff --git a/net/reporting/reporting_service.h b/net/reporting/reporting_service.h
new file mode 100644
index 0000000..c305410
--- /dev/null
+++ b/net/reporting/reporting_service.h
@@ -0,0 +1,74 @@
+// Copyright 2017 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 NET_REPORTING_REPORTING_SERVICE_H_
+#define NET_REPORTING_REPORTING_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/callback.h"
+#include "base/macros.h"
+#include "net/base/net_export.h"
+
+class GURL;
+
+namespace base {
+class Value;
+}  // namespace
+
+namespace net {
+
+class ReportingContext;
+class ReportingDelegate;
+struct ReportingPolicy;
+class URLRequestContext;
+
+// The external interface to the Reporting system, used by the embedder of //net
+// and also other parts of //net.
+class NET_EXPORT ReportingService {
+ public:
+  virtual ~ReportingService();
+
+  // Creates a ReportingService. |policy| will be copied. |request_context| must
+  // outlive the ReportingService. The ReportingService will take ownership of
+  // |delegate| and destroy it when the service is destroyed.
+  static std::unique_ptr<ReportingService> Create(
+      const ReportingPolicy& policy,
+      URLRequestContext* request_context,
+      std::unique_ptr<ReportingDelegate> delegate);
+
+  // Creates a ReportingService for testing purposes using an
+  // already-constructed ReportingContext. The ReportingService will take
+  // ownership of |reporting_context| and destroy it when the service is
+  // destroyed.
+  static std::unique_ptr<ReportingService> CreateForTesting(
+      std::unique_ptr<ReportingContext> reporting_context);
+
+  // Queues a report for delivery. |url| is the URL that originated the report.
+  // |group| is the endpoint group to which the report should be delivered.
+  // |type| is the type of the report. |body| is the body of the report.
+  //
+  // The Reporting system will take ownership of |body|; all other parameters
+  // will be copied.
+  virtual void QueueReport(const GURL& url,
+                           const std::string& group,
+                           const std::string& type,
+                           std::unique_ptr<const base::Value> body) = 0;
+
+  // Processes a Report-To header. |url| is the URL that originated the header;
+  // |header_value| is the normalized value of the Report-To header.
+  virtual void ProcessHeader(const GURL& url,
+                             const std::string& header_value) = 0;
+
+ protected:
+  ReportingService() {}
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ReportingService);
+};
+
+}  // namespace net
+
+#endif  // NET_REPORTING_REPORTING_SERVICE_H_
diff --git a/net/reporting/reporting_service_unittest.cc b/net/reporting/reporting_service_unittest.cc
new file mode 100644
index 0000000..20c8656
--- /dev/null
+++ b/net/reporting/reporting_service_unittest.cc
@@ -0,0 +1,78 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/reporting/reporting_service.h"
+
+#include <memory>
+#include <string>
+
+#include "base/memory/ptr_util.h"
+#include "base/time/tick_clock.h"
+#include "base/values.h"
+#include "net/reporting/reporting_cache.h"
+#include "net/reporting/reporting_context.h"
+#include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_policy.h"
+#include "net/reporting/reporting_report.h"
+#include "net/reporting/reporting_service.h"
+#include "net/reporting/reporting_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+namespace {
+
+class ReportingServiceTest : public ::testing::Test {
+ protected:
+  const GURL kUrl_ = GURL("https://origin/path");
+  const url::Origin kOrigin_ = url::Origin(kUrl_);
+  const GURL kEndpoint_ = GURL("https://endpoint/");
+  const std::string kGroup_ = "group";
+  const std::string kType_ = "type";
+
+  ReportingServiceTest()
+      : context_(new TestReportingContext(ReportingPolicy())),
+        service_(
+            ReportingService::CreateForTesting(base::WrapUnique(context_))) {}
+
+  TestReportingContext* context() { return context_; }
+  ReportingService* service() { return service_.get(); }
+
+ private:
+  TestReportingContext* context_;
+  std::unique_ptr<ReportingService> service_;
+};
+
+TEST_F(ReportingServiceTest, QueueReport) {
+  service()->QueueReport(kUrl_, kGroup_, kType_,
+                         base::MakeUnique<base::DictionaryValue>());
+
+  std::vector<const ReportingReport*> reports;
+  context()->cache()->GetReports(&reports);
+  ASSERT_EQ(1u, reports.size());
+  EXPECT_EQ(kUrl_, reports[0]->url);
+  EXPECT_EQ(kGroup_, reports[0]->group);
+  EXPECT_EQ(kType_, reports[0]->type);
+}
+
+TEST_F(ReportingServiceTest, ProcessHeader) {
+  service()->ProcessHeader(kUrl_, "{\"url\":\"" + kEndpoint_.spec() +
+                                      "\","
+                                      "\"group\":\"" +
+                                      kGroup_ +
+                                      "\","
+                                      "\"max-age\":86400}");
+
+  const ReportingClient* client =
+      FindClientInCache(context()->cache(), kOrigin_, kEndpoint_);
+  ASSERT_TRUE(client != nullptr);
+  EXPECT_EQ(kOrigin_, client->origin);
+  EXPECT_EQ(kEndpoint_, client->endpoint);
+  EXPECT_EQ(ReportingClient::Subdomains::EXCLUDE, client->subdomains);
+  EXPECT_EQ(kGroup_, client->group);
+  EXPECT_EQ(context()->tick_clock()->NowTicks() + base::TimeDelta::FromDays(1),
+            client->expires);
+}
+
+}  // namespace
+}  // namespace net
diff --git a/net/reporting/reporting_test_util.cc b/net/reporting/reporting_test_util.cc
index 5f59c449..946e1e0 100644
--- a/net/reporting/reporting_test_util.cc
+++ b/net/reporting/reporting_test_util.cc
@@ -4,15 +4,77 @@
 
 #include "net/reporting/reporting_test_util.h"
 
+#include <memory>
+#include <string>
 #include <vector>
 
+#include "base/bind.h"
+#include "base/json/json_reader.h"
+#include "base/memory/ptr_util.h"
+#include "base/test/simple_test_clock.h"
+#include "base/test/simple_test_tick_clock.h"
 #include "net/reporting/reporting_cache.h"
 #include "net/reporting/reporting_client.h"
+#include "net/reporting/reporting_context.h"
+#include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_policy.h"
+#include "net/reporting/reporting_uploader.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
 #include "url/origin.h"
 
 namespace net {
 
+namespace {
+
+class PendingUploadImpl : public TestReportingUploader::PendingUpload {
+ public:
+  PendingUploadImpl(
+      const GURL& url,
+      const std::string& json,
+      const ReportingUploader::Callback& callback,
+      const base::Callback<void(PendingUpload*)>& complete_callback)
+      : url_(url),
+        json_(json),
+        callback_(callback),
+        complete_callback_(complete_callback) {}
+
+  ~PendingUploadImpl() override {}
+
+  // PendingUpload implementationP:
+  const GURL& url() const override { return url_; }
+  const std::string& json() const override { return json_; }
+  std::unique_ptr<base::Value> GetValue() const override {
+    return base::JSONReader::Read(json_);
+  }
+
+  void Complete(ReportingUploader::Outcome outcome) override {
+    callback_.Run(outcome);
+    // Deletes |this|.
+    complete_callback_.Run(this);
+  }
+
+ private:
+  GURL url_;
+  std::string json_;
+  ReportingUploader::Callback callback_;
+  base::Callback<void(PendingUpload*)> complete_callback_;
+};
+
+void ErasePendingUpload(
+    std::vector<std::unique_ptr<TestReportingUploader::PendingUpload>>* uploads,
+    TestReportingUploader::PendingUpload* upload) {
+  for (auto it = uploads->begin(); it != uploads->end(); ++it) {
+    if (it->get() == upload) {
+      uploads->erase(it);
+      return;
+    }
+  }
+  NOTREACHED();
+}
+
+}  // namespace
+
 const ReportingClient* FindClientInCache(const ReportingCache* cache,
                                          const url::Origin& origin,
                                          const GURL& endpoint) {
@@ -25,4 +87,61 @@
   return nullptr;
 }
 
+TestReportingDelegate::TestReportingDelegate() {}
+TestReportingDelegate::~TestReportingDelegate() {}
+
+void TestReportingDelegate::PersistData(
+    std::unique_ptr<const base::Value> persisted_data) {
+  persisted_data_ = std::move(persisted_data);
+}
+
+std::unique_ptr<const base::Value> TestReportingDelegate::GetPersistedData() {
+  if (!persisted_data_)
+    return std::unique_ptr<const base::Value>();
+  return persisted_data_->CreateDeepCopy();
+}
+
+TestReportingUploader::PendingUpload::~PendingUpload() {}
+TestReportingUploader::PendingUpload::PendingUpload() {}
+
+TestReportingUploader::TestReportingUploader() {}
+TestReportingUploader::~TestReportingUploader() {}
+
+void TestReportingUploader::StartUpload(const GURL& url,
+                                        const std::string& json,
+                                        const Callback& callback) {
+  pending_uploads_.push_back(base::MakeUnique<PendingUploadImpl>(
+      url, json, callback, base::Bind(&ErasePendingUpload, &pending_uploads_)));
+}
+
+TestReportingContext::TestReportingContext(const ReportingPolicy& policy)
+    : ReportingContext(policy,
+                       base::MakeUnique<TestReportingDelegate>(),
+                       base::MakeUnique<base::SimpleTestClock>(),
+                       base::MakeUnique<base::SimpleTestTickClock>(),
+                       base::MakeUnique<TestReportingUploader>()) {}
+
+TestReportingContext::~TestReportingContext() {}
+
+ReportingTestBase::ReportingTestBase() {
+  // For tests, disable jitter.
+  ReportingPolicy policy;
+  policy.endpoint_backoff_policy.jitter_factor = 0.0;
+  UsePolicy(policy);
+}
+
+ReportingTestBase::~ReportingTestBase() {}
+
+void ReportingTestBase::UsePolicy(const ReportingPolicy& policy) {
+  context_ = base::MakeUnique<TestReportingContext>(policy);
+}
+
+base::TimeTicks ReportingTestBase::yesterday() {
+  return tick_clock()->NowTicks() - base::TimeDelta::FromDays(1);
+}
+
+base::TimeTicks ReportingTestBase::tomorrow() {
+  return tick_clock()->NowTicks() + base::TimeDelta::FromDays(1);
+}
+
 }  // namespace net
diff --git a/net/reporting/reporting_test_util.h b/net/reporting/reporting_test_util.h
index bb279ebc..9aeaa2e 100644
--- a/net/reporting/reporting_test_util.h
+++ b/net/reporting/reporting_test_util.h
@@ -5,8 +5,24 @@
 #ifndef NET_REPORTING_REPORTING_TEST_UTIL_H_
 #define NET_REPORTING_REPORTING_TEST_UTIL_H_
 
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "net/reporting/reporting_context.h"
+#include "net/reporting/reporting_delegate.h"
+#include "net/reporting/reporting_uploader.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
 class GURL;
 
+namespace base {
+class SimpleTestClock;
+class SimpleTestTickClock;
+class Value;
+}  // namespace base
+
 namespace url {
 class Origin;
 }  // namespace url
@@ -22,6 +38,122 @@
                                          const url::Origin& origin,
                                          const GURL& endpoint);
 
+// A simple implementation of ReportingDelegate that only persists data in RAM.
+class TestReportingDelegate : public ReportingDelegate {
+ public:
+  TestReportingDelegate();
+
+  ~TestReportingDelegate() override;
+
+  // ReportingDelegate implementation:
+  std::unique_ptr<const base::Value> GetPersistedData() override;
+
+  void PersistData(std::unique_ptr<const base::Value> persisted_data) override;
+
+ private:
+  std::unique_ptr<const base::Value> persisted_data_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestReportingDelegate);
+};
+
+// A test implementation of ReportingUploader that holds uploads for tests to
+// examine and complete with a specified outcome.
+class TestReportingUploader : public ReportingUploader {
+ public:
+  class PendingUpload {
+   public:
+    virtual ~PendingUpload();
+
+    virtual const GURL& url() const = 0;
+    virtual const std::string& json() const = 0;
+    virtual std::unique_ptr<base::Value> GetValue() const = 0;
+
+    virtual void Complete(Outcome outcome) = 0;
+
+   protected:
+    PendingUpload();
+  };
+
+  TestReportingUploader();
+  ~TestReportingUploader() override;
+
+  const std::vector<std::unique_ptr<PendingUpload>>& pending_uploads() const {
+    return pending_uploads_;
+  }
+
+  // ReportingUploader implementation:
+  void StartUpload(const GURL& url,
+                   const std::string& json,
+                   const Callback& callback) override;
+
+ private:
+  std::vector<std::unique_ptr<PendingUpload>> pending_uploads_;
+
+  DISALLOW_COPY_AND_ASSIGN(TestReportingUploader);
+};
+
+// A test implementation of ReportingContext that uses test versions of
+// ReportingDelegate, Clock, TickClock, and ReportingUploader.
+class TestReportingContext : public ReportingContext {
+ public:
+  TestReportingContext(const ReportingPolicy& policy);
+  ~TestReportingContext();
+
+  TestReportingDelegate* test_delegate() {
+    return reinterpret_cast<TestReportingDelegate*>(delegate());
+  }
+  base::SimpleTestClock* test_clock() {
+    return reinterpret_cast<base::SimpleTestClock*>(clock());
+  }
+  base::SimpleTestTickClock* test_tick_clock() {
+    return reinterpret_cast<base::SimpleTestTickClock*>(tick_clock());
+  }
+  TestReportingUploader* test_uploader() {
+    return reinterpret_cast<TestReportingUploader*>(uploader());
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(TestReportingContext);
+};
+
+// A unit test base class that provides a TestReportingContext and shorthand
+// getters.
+class ReportingTestBase : public ::testing::Test {
+ protected:
+  ReportingTestBase();
+  ~ReportingTestBase() override;
+
+  void UsePolicy(const ReportingPolicy& policy);
+
+  TestReportingContext* context() { return context_.get(); }
+
+  const ReportingPolicy& policy() { return context_->policy(); }
+
+  TestReportingDelegate* delegate() { return context_->test_delegate(); }
+  base::SimpleTestClock* clock() { return context_->test_clock(); }
+  base::SimpleTestTickClock* tick_clock() {
+    return context_->test_tick_clock();
+  }
+  TestReportingUploader* uploader() { return context_->test_uploader(); }
+
+  ReportingCache* cache() { return context_->cache(); }
+  ReportingEndpointManager* endpoint_manager() {
+    return context_->endpoint_manager();
+  }
+  ReportingDeliveryAgent* delivery_agent() {
+    return context_->delivery_agent();
+  }
+
+  base::TimeTicks yesterday();
+
+  base::TimeTicks tomorrow();
+
+ private:
+  std::unique_ptr<TestReportingContext> context_;
+
+  DISALLOW_COPY_AND_ASSIGN(ReportingTestBase);
+};
+
 }  // namespace net
 
 #endif  // NET_REPORING_REPORTING_TEST_UTIL_H_
diff --git a/ppapi/api/dev/ppb_audio_output_dev.idl b/ppapi/api/dev/ppb_audio_output_dev.idl
new file mode 100644
index 0000000..edd8679
--- /dev/null
+++ b/ppapi/api/dev/ppb_audio_output_dev.idl
@@ -0,0 +1,229 @@
+/* Copyright (c) 2017 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.
+ */
+
+/**
+ * This file defines the <code>PPB_AudioOutput_dev</code> interface, which
+ * provides realtime stereo audio streaming capabilities.
+ */
+
+[generate_thunk]
+
+label Chrome {
+  M59 = 0.1
+};
+
+/**
+ * <code>PPB_AudioOutput_Callback</code> defines the type of an audio callback
+ * function used to fill the audio buffer with data. Please see the
+ * Create() function in the <code>PPB_AudioOutput</code> interface for
+ * more details on this callback.
+ *
+ * @param[out] sample_buffer A buffer to fill with audio data.
+ * @param[in] buffer_size_in_bytes The size of the buffer in bytes.
+ * @param[in] latency How long before the audio data is to be presented.
+ * @param[inout] user_data An opaque pointer that was passed into
+ * <code>PPB_AudioOutput.Create()</code>.
+ */
+typedef void PPB_AudioOutput_Callback([out] mem_t sample_buffer,
+                                      [in] uint32_t buffer_size_in_bytes,
+                                      [in] PP_TimeDelta latency,
+                                      [inout] mem_t user_data);
+
+/**
+ * The <code>PPB_AudioOutput</code> interface contains pointers to several
+ * functions for handling audio resources.
+ * Please see descriptions for each <code>PPB_AudioOutput</code> and
+ * <code>PPB_AudioConfig</code> function for more details. A C example using
+ * <code>PPB_AudioOutput</code> and <code>PPB_AudioConfig</code> follows.
+ *
+ * <strong>Example: </strong>
+ * 
+ * @code
+ * void audio_output_callback(void* sample_buffer,
+ *                            uint32_t buffer_size_in_bytes,
+ *                            PP_TimeDelta latency,
+ *                            void* user_data) {
+ *   ... quickly fill in the buffer with samples and return to caller ...
+ *  }
+ *
+ * ...Assume the application has cached the audio configuration interface in
+ * audio_config_interface and the audio interface in
+ * audio_output_interface...
+ *
+ * uint32_t count = audio_config_interface->RecommendSampleFrameCount(
+ *     PP_AUDIOSAMPLERATE_44100, 4096);
+ * PP_Resource pp_audio_config = audio_config_interface->CreateStereo16Bit(
+ *     pp_instance, PP_AUDIOSAMPLERATE_44100, count);
+ * PP_Resource pp_audio_output = audio_interface->Create(pp_instance,
+ *     pp_audio_config, audio_callback, NULL);
+ * audio_interface->EnumerateDevices(pp_audio_output, output_device_list,
+ *     callback);
+ * audio_interface->Open(pp_audio_output, device_ref, pp_audio_config,
+ *     audio_output_callback, user_data, callback);
+ * audio_output_interface->StartPlayback(pp_audio_output);
+ *
+ * ...audio_output_callback() will now be periodically invoked on a separate
+ * thread...
+ * @endcode
+ */
+
+[macro="PPB_AUDIO_OUTPUT_DEV_INTERFACE"]
+interface PPB_AudioOutput_Dev {
+  /**
+   * Creates an audio output resource.
+   *
+   * @param[in] instance A <code>PP_Instance</code> identifying one instance of
+   * a module.
+   *
+   * @return A <code>PP_Resource</code> corresponding to an audio output resource
+   * if successful, 0 if failed.
+   */
+  PP_Resource Create(
+      [in] PP_Instance instance);
+
+  /**
+   * Determines if the given resource is an audio output resource.
+   *
+   * @param[in] resource A <code>PP_Resource</code> containing a resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if the given
+   * resource is an audio output resource, otherwise <code>PP_FALSE</code>.
+   */
+  PP_Bool IsAudioOutput(
+      [in] PP_Resource resource);
+
+  /**
+   * Enumerates audio output devices.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   * @param[in] output An output array which will receive
+   * <code>PPB_DeviceRef_Dev</code> resources on success. Please note that the
+   * ref count of those resources has already been increased by 1 for the
+   * caller.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to run on
+   * completion.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t EnumerateDevices(
+      [in] PP_Resource audio_output,
+      [in] PP_ArrayOutput output,
+      [in] PP_CompletionCallback callback);
+
+  /**
+   * Requests device change notifications.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   * @param[in] callback The callback to receive notifications. If not NULL, it
+   * will be called once for the currently available devices, and then every
+   * time the list of available devices changes. All calls will happen on the
+   * same thread as the one on which MonitorDeviceChange() is called. It will
+   * receive notifications until <code>audio_output</code> is destroyed or
+   * <code>MonitorDeviceChange()</code> is called to set a new callback for
+   * <code>audio_output</code>. You can pass NULL to cancel sending
+   * notifications.
+   * @param[inout] user_data An opaque pointer that will be passed to
+   * <code>callback</code>.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t MonitorDeviceChange(
+      [in] PP_Resource audio_output,
+      [in] PP_MonitorDeviceChangeCallback callback,
+      [inout] mem_t user_data);
+
+  /**
+   * Open() opens an audio output device. No sound will be heard until
+   * StartPlayback() is called. The callback is called with the buffer address
+   * and given user data whenever the buffer needs to be filled. From within the
+   * callback, you should not call <code>PPB_AudioOutput</code> functions. The
+   * callback will be called on a different thread than the one which created
+   * the interface. For performance-critical applications (i.e. low-latency
+   * audio), the callback should avoid blocking or calling functions that can
+   * obtain locks, such as malloc. The layout and the size of the buffer passed
+   * to the audio callback will be determined by the device configuration and is
+   * specified in the <code>AudioConfig</code> documentation.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   * @param[in] device_ref Identifies an audio output device. It could be one of
+   * the resource in the array returned by EnumerateDevices(), or 0 which means
+   * the default device.
+   * @param[in] config A <code>PPB_AudioConfig</code> audio configuration
+   * resource.
+   * @param[in] audio_output_callback A <code>PPB_AudioOutput_Callback</code>
+   * function that will be called when audio buffer needs to be filled.
+   * @param[inout] user_data An opaque pointer that will be passed into
+   * <code>audio_output_callback</code>.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to run when this
+   * open operation is completed.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t Open(
+      [in] PP_Resource audio_output,
+      [in] PP_Resource device_ref,
+      [in] PP_Resource config,
+      [in] PPB_AudioOutput_Callback audio_output_callback,
+      [inout] mem_t user_data,
+      [in] PP_CompletionCallback callback);
+
+  /**
+   * GetCurrrentConfig() returns an audio config resource for the given audio
+   * output resource.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Resource</code> containing the audio config resource if
+   * successful.
+   */
+  PP_Resource GetCurrentConfig(
+      [in] PP_Resource audio_output);
+
+  /**
+   * StartPlayback() starts the playback of the audio output resource and begins
+   * periodically calling the callback.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if
+   * successful, otherwise <code>PP_FALSE</code>. Also returns
+   * <code>PP_TRUE</code> (and be a no-op) if called while playback is already
+   * in progress.
+   */
+  PP_Bool StartPlayback(
+      [in] PP_Resource audio_output);
+
+  /**
+   * StopPlayback() stops the playback of the audio resource.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if
+   * successful, otherwise <code>PP_FALSE</code>. Also returns
+   * <code>PP_TRUE</code> (and is a no-op) if called while playback is already
+   * stopped. If a callback is in progress, StopPlayback() will block until the
+   * callback completes.
+   */
+  PP_Bool StopPlayback(
+      [in] PP_Resource audio_output);
+
+  /**
+   * Close() closes the audio output device, and stops playback if necessary. It is
+   * not valid to call Open() again after a call to this method.
+   * If an audio output resource is destroyed while a device is still open, then
+   * it will be implicitly closed, so you are not required to call this method.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   */
+  void Close(
+      [in] PP_Resource audio_output);
+};
diff --git a/ppapi/api/dev/ppb_device_ref_dev.idl b/ppapi/api/dev/ppb_device_ref_dev.idl
index 98d1272..b613584 100644
--- a/ppapi/api/dev/ppb_device_ref_dev.idl
+++ b/ppapi/api/dev/ppb_device_ref_dev.idl
@@ -36,7 +36,8 @@
 enum PP_DeviceType_Dev {
   PP_DEVICETYPE_DEV_INVALID = 0,
   PP_DEVICETYPE_DEV_AUDIOCAPTURE = 1,
-  PP_DEVICETYPE_DEV_VIDEOCAPTURE = 2
+  PP_DEVICETYPE_DEV_VIDEOCAPTURE = 2,
+  PP_DEVICETYPE_DEV_AUDIOOUTPUT = 3
 };
 
 interface PPB_DeviceRef_Dev {
diff --git a/ppapi/c/BUILD.gn b/ppapi/c/BUILD.gn
index 9894dd9..cd3fda9d 100644
--- a/ppapi/c/BUILD.gn
+++ b/ppapi/c/BUILD.gn
@@ -87,6 +87,7 @@
     "dev/pp_video_capture_dev.h",
     "dev/pp_video_dev.h",
     "dev/ppb_audio_input_dev.h",
+    "dev/ppb_audio_output_dev.h",
     "dev/ppb_buffer_dev.h",
     "dev/ppb_char_set_dev.h",
     "dev/ppb_crypto_dev.h",
diff --git a/ppapi/c/dev/ppb_audio_output_dev.h b/ppapi/c/dev/ppb_audio_output_dev.h
new file mode 100644
index 0000000..7edfc42
--- /dev/null
+++ b/ppapi/c/dev/ppb_audio_output_dev.h
@@ -0,0 +1,249 @@
+/* Copyright (c) 2017 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.
+ */
+
+/* From dev/ppb_audio_output_dev.idl modified Fri Apr  7 07:07:59 2017. */
+
+#ifndef PPAPI_C_DEV_PPB_AUDIO_OUTPUT_DEV_H_
+#define PPAPI_C_DEV_PPB_AUDIO_OUTPUT_DEV_H_
+
+#include "ppapi/c/dev/ppb_device_ref_dev.h"
+#include "ppapi/c/pp_array_output.h"
+#include "ppapi/c/pp_bool.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_instance.h"
+#include "ppapi/c/pp_macros.h"
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/c/pp_stdint.h"
+#include "ppapi/c/pp_time.h"
+
+#define PPB_AUDIO_OUTPUT_DEV_INTERFACE_0_1 "PPB_AudioOutput(Dev);0.1"
+#define PPB_AUDIO_OUTPUT_DEV_INTERFACE PPB_AUDIO_OUTPUT_DEV_INTERFACE_0_1
+
+/**
+ * @file
+ * This file defines the <code>PPB_AudioOutput_dev</code> interface, which
+ * provides realtime stereo audio streaming capabilities.
+ */
+
+
+/**
+ * @addtogroup Typedefs
+ * @{
+ */
+/**
+ * <code>PPB_AudioOutput_Callback</code> defines the type of an audio callback
+ * function used to fill the audio buffer with data. Please see the
+ * Create() function in the <code>PPB_AudioOutput</code> interface for
+ * more details on this callback.
+ *
+ * @param[out] sample_buffer A buffer to fill with audio data.
+ * @param[in] buffer_size_in_bytes The size of the buffer in bytes.
+ * @param[in] latency How long before the audio data is to be presented.
+ * @param[inout] user_data An opaque pointer that was passed into
+ * <code>PPB_AudioOutput.Create()</code>.
+ */
+typedef void (*PPB_AudioOutput_Callback)(void* sample_buffer,
+                                         uint32_t buffer_size_in_bytes,
+                                         PP_TimeDelta latency,
+                                         void* user_data);
+/**
+ * @}
+ */
+
+/**
+ * @addtogroup Interfaces
+ * @{
+ */
+/**
+ * The <code>PPB_AudioOutput</code> interface contains pointers to several
+ * functions for handling audio resources.
+ * Please see descriptions for each <code>PPB_AudioOutput</code> and
+ * <code>PPB_AudioConfig</code> function for more details. A C example using
+ * <code>PPB_AudioOutput</code> and <code>PPB_AudioConfig</code> follows.
+ *
+ * <strong>Example: </strong>
+ *
+ * @code
+ * void audio_output_callback(void* sample_buffer,
+ *                            uint32_t buffer_size_in_bytes,
+ *                            PP_TimeDelta latency,
+ *                            void* user_data) {
+ *   ... quickly fill in the buffer with samples and return to caller ...
+ *  }
+ *
+ * ...Assume the application has cached the audio configuration interface in
+ * audio_config_interface and the audio interface in
+ * audio_output_interface...
+ *
+ * uint32_t count = audio_config_interface->RecommendSampleFrameCount(
+ *     PP_AUDIOSAMPLERATE_44100, 4096);
+ * PP_Resource pp_audio_config = audio_config_interface->CreateStereo16Bit(
+ *     pp_instance, PP_AUDIOSAMPLERATE_44100, count);
+ * PP_Resource pp_audio_output = audio_interface->Create(pp_instance,
+ *     pp_audio_config, audio_callback, NULL);
+ * audio_interface->EnumerateDevices(pp_audio_output, output_device_list,
+ *     callback);
+ * audio_interface->Open(pp_audio_output, device_ref, pp_audio_config,
+ *     audio_output_callback, user_data, callback);
+ * audio_output_interface->StartPlayback(pp_audio_output);
+ *
+ * ...audio_output_callback() will now be periodically invoked on a separate
+ * thread...
+ * @endcode
+ */
+struct PPB_AudioOutput_Dev_0_1 {
+  /**
+   * Creates an audio output resource.
+   *
+   * @param[in] instance A <code>PP_Instance</code> identifying one instance of
+   * a module.
+   *
+   * @return A <code>PP_Resource</code> corresponding to an audio output
+    resource
+   * if successful, 0 if failed.
+   */
+  PP_Resource (*Create)(PP_Instance instance);
+  /**
+   * Determines if the given resource is an audio output resource.
+   *
+   * @param[in] resource A <code>PP_Resource</code> containing a resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if the given
+   * resource is an audio output resource, otherwise <code>PP_FALSE</code>.
+   */
+  PP_Bool (*IsAudioOutput)(PP_Resource resource);
+  /**
+   * Enumerates audio output devices.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an
+    audio
+   * output resource.
+   * @param[in] output An output array which will receive
+   * <code>PPB_DeviceRef_Dev</code> resources on success. Please note that the
+   * ref count of those resources has already been increased by 1 for the
+   * caller.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to run on
+   * completion.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t (*EnumerateDevices)(PP_Resource audio_output,
+                              struct PP_ArrayOutput output,
+                              struct PP_CompletionCallback callback);
+  /**
+   * Requests device change notifications.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an
+    audio
+   * output resource.
+   * @param[in] callback The callback to receive notifications. If not NULL, it
+   * will be called once for the currently available devices, and then every
+   * time the list of available devices changes. All calls will happen on the
+   * same thread as the one on which MonitorDeviceChange() is called. It will
+   * receive notifications until <code>audio_output</code> is destroyed or
+   * <code>MonitorDeviceChange()</code> is called to set a new callback for
+   * <code>audio_output</code>. You can pass NULL to cancel sending
+   * notifications.
+   * @param[inout] user_data An opaque pointer that will be passed to
+   * <code>callback</code>.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t (*MonitorDeviceChange)(PP_Resource audio_output,
+                                 PP_MonitorDeviceChangeCallback callback,
+                                 void* user_data);
+  /**
+   * Open() opens an audio output device. No sound will be heard until
+   * StartPlayback() is called. The callback is called with the buffer address
+   * and given user data whenever the buffer needs to be filled. From within the
+   * callback, you should not call <code>PPB_AudioOutput</code> functions. The
+   * callback will be called on a different thread than the one which created
+   * the interface. For performance-critical applications (i.e. low-latency
+   * audio), the callback should avoid blocking or calling functions that can
+   * obtain locks, such as malloc. The layout and the size of the buffer passed
+   * to the audio callback will be determined by the device configuration and is
+   * specified in the <code>AudioConfig</code> documentation.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an
+    audio
+   * output resource.
+   * @param[in] device_ref Identifies an audio output device. It could be one of
+   * the resource in the array returned by EnumerateDevices(), or 0 which means
+   * the default device.
+   * @param[in] config A <code>PPB_AudioConfig</code> audio configuration
+   * resource.
+   * @param[in] audio_output_callback A <code>PPB_AudioOutput_Callback</code>
+   * function that will be called when audio buffer needs to be filled.
+   * @param[inout] user_data An opaque pointer that will be passed into
+   * <code>audio_output_callback</code>.
+   * @param[in] callback A <code>PP_CompletionCallback</code> to run when this
+   * open operation is completed.
+   *
+   * @return An error code from <code>pp_errors.h</code>.
+   */
+  int32_t (*Open)(PP_Resource audio_output,
+                  PP_Resource device_ref,
+                  PP_Resource config,
+                  PPB_AudioOutput_Callback audio_output_callback,
+                  void* user_data,
+                  struct PP_CompletionCallback callback);
+  /**
+   * GetCurrrentConfig() returns an audio config resource for the given audio
+   * output resource.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Resource</code> containing the audio config resource if
+   * successful.
+   */
+  PP_Resource (*GetCurrentConfig)(PP_Resource audio_output);
+  /**
+   * StartPlayback() starts the playback of the audio output resource and begins
+   * periodically calling the callback.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if
+   * successful, otherwise <code>PP_FALSE</code>. Also returns
+   * <code>PP_TRUE</code> (and be a no-op) if called while playback is already
+   * in progress.
+   */
+  PP_Bool (*StartPlayback)(PP_Resource audio_output);
+  /**
+   * StopPlayback() stops the playback of the audio resource.
+   *
+   * @param[in] config A <code>PP_Resource</code> corresponding to an audio
+   * output resource.
+   *
+   * @return A <code>PP_Bool</code> containing <code>PP_TRUE</code> if
+   * successful, otherwise <code>PP_FALSE</code>. Also returns
+   * <code>PP_TRUE</code> (and is a no-op) if called while playback is already
+   * stopped. If a callback is in progress, StopPlayback() will block until the
+   * callback completes.
+   */
+  PP_Bool (*StopPlayback)(PP_Resource audio_output);
+  /**
+   * Close() closes the audio output device,
+           and stops playback if necessary. It is
+   * not valid to call Open() again after a call to this method.
+   * If an audio output resource is destroyed while a device is still open, then
+   * it will be implicitly closed, so you are not required to call this method.
+   *
+   * @param[in] audio_output A <code>PP_Resource</code> corresponding to an
+    audio
+   * output resource.
+   */
+  void (*Close)(PP_Resource audio_output);
+};
+
+typedef struct PPB_AudioOutput_Dev_0_1 PPB_AudioOutput_Dev;
+/**
+ * @}
+ */
+
+#endif  /* PPAPI_C_DEV_PPB_AUDIO_OUTPUT_DEV_H_ */
+
diff --git a/ppapi/c/dev/ppb_device_ref_dev.h b/ppapi/c/dev/ppb_device_ref_dev.h
index 62d75a6..2b8b18d 100644
--- a/ppapi/c/dev/ppb_device_ref_dev.h
+++ b/ppapi/c/dev/ppb_device_ref_dev.h
@@ -3,7 +3,7 @@
  * found in the LICENSE file.
  */
 
-/* From dev/ppb_device_ref_dev.idl modified Wed Nov 07 13:28:37 2012. */
+/* From dev/ppb_device_ref_dev.idl modified Mon Jan 09 12:04:09 2017. */
 
 #ifndef PPAPI_C_DEV_PPB_DEVICE_REF_DEV_H_
 #define PPAPI_C_DEV_PPB_DEVICE_REF_DEV_H_
@@ -55,7 +55,8 @@
 typedef enum {
   PP_DEVICETYPE_DEV_INVALID = 0,
   PP_DEVICETYPE_DEV_AUDIOCAPTURE = 1,
-  PP_DEVICETYPE_DEV_VIDEOCAPTURE = 2
+  PP_DEVICETYPE_DEV_VIDEOCAPTURE = 2,
+  PP_DEVICETYPE_DEV_AUDIOOUTPUT = 3
 } PP_DeviceType_Dev;
 PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_DeviceType_Dev, 4);
 /**
diff --git a/ppapi/c/pp_macros.h b/ppapi/c/pp_macros.h
index 16dc079..291d2d6 100644
--- a/ppapi/c/pp_macros.h
+++ b/ppapi/c/pp_macros.h
@@ -3,13 +3,13 @@
  * found in the LICENSE file.
  */
 
-/* From pp_macros.idl modified Thu Oct 15 10:46:35 2015. */
+/* From pp_macros.idl modified Sat Dec  6 22:11:47 2014. */
 
 #ifndef PPAPI_C_PP_MACROS_H_
 #define PPAPI_C_PP_MACROS_H_
 
 
-#define PPAPI_RELEASE 55
+#define PPAPI_RELEASE 59
 
 /**
  * @file
diff --git a/ppapi/cpp/BUILD.gn b/ppapi/cpp/BUILD.gn
index 586ca71..1252d7af 100644
--- a/ppapi/cpp/BUILD.gn
+++ b/ppapi/cpp/BUILD.gn
@@ -161,6 +161,8 @@
     # Dev interfaces.
     "dev/audio_input_dev.cc",
     "dev/audio_input_dev.h",
+    "dev/audio_output_dev.cc",
+    "dev/audio_output_dev.h",
     "dev/buffer_dev.cc",
     "dev/buffer_dev.h",
     "dev/crypto_dev.cc",
diff --git a/ppapi/cpp/dev/audio_output_dev.cc b/ppapi/cpp/dev/audio_output_dev.cc
new file mode 100644
index 0000000..56e55b0
--- /dev/null
+++ b/ppapi/cpp/dev/audio_output_dev.cc
@@ -0,0 +1,98 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ppapi/cpp/dev/audio_output_dev.h"
+
+#include "ppapi/c/pp_bool.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/cpp/instance_handle.h"
+#include "ppapi/cpp/module_impl.h"
+
+namespace pp {
+
+namespace {
+
+template <>
+const char* interface_name<PPB_AudioOutput_Dev_0_1>() {
+  return PPB_AUDIO_OUTPUT_DEV_INTERFACE_0_1;
+}
+
+}  // namespace
+
+AudioOutput_Dev::AudioOutput_Dev() {}
+
+AudioOutput_Dev::AudioOutput_Dev(const InstanceHandle& instance) {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    PassRefFromConstructor(get_interface<PPB_AudioOutput_Dev_0_1>()->Create(
+        instance.pp_instance()));
+  }
+}
+
+AudioOutput_Dev::~AudioOutput_Dev() {}
+
+// static
+bool AudioOutput_Dev::IsAvailable() {
+  return has_interface<PPB_AudioOutput_Dev_0_1>();
+}
+
+int32_t AudioOutput_Dev::EnumerateDevices(
+    const CompletionCallbackWithOutput<std::vector<DeviceRef_Dev> >& callback) {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    return get_interface<PPB_AudioOutput_Dev_0_1>()->EnumerateDevices(
+        pp_resource(), callback.output(), callback.pp_completion_callback());
+  }
+
+  return callback.MayForce(PP_ERROR_NOINTERFACE);
+}
+
+int32_t AudioOutput_Dev::MonitorDeviceChange(
+    PP_MonitorDeviceChangeCallback callback,
+    void* user_data) {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    return get_interface<PPB_AudioOutput_Dev_0_1>()->MonitorDeviceChange(
+        pp_resource(), callback, user_data);
+  }
+
+  return PP_ERROR_NOINTERFACE;
+}
+
+int32_t AudioOutput_Dev::Open(const DeviceRef_Dev& device_ref,
+                              const AudioConfig& config,
+                              PPB_AudioOutput_Callback audio_output_callback,
+                              void* user_data,
+                              const CompletionCallback& callback) {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    return get_interface<PPB_AudioOutput_Dev_0_1>()->Open(
+        pp_resource(), device_ref.pp_resource(), config.pp_resource(),
+        audio_output_callback, user_data, callback.pp_completion_callback());
+  }
+
+  return callback.MayForce(PP_ERROR_NOINTERFACE);
+}
+
+bool AudioOutput_Dev::StartPlayback() {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    return PP_ToBool(
+        get_interface<PPB_AudioOutput_Dev_0_1>()->StartPlayback(pp_resource()));
+  }
+
+  return false;
+}
+
+bool AudioOutput_Dev::StopPlayback() {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    return PP_ToBool(
+        get_interface<PPB_AudioOutput_Dev_0_1>()->StopPlayback(pp_resource()));
+  }
+
+  return false;
+}
+
+void AudioOutput_Dev::Close() {
+  if (has_interface<PPB_AudioOutput_Dev_0_1>()) {
+    get_interface<PPB_AudioOutput_Dev_0_1>()->Close(pp_resource());
+  }
+}
+
+}  // namespace pp
diff --git a/ppapi/cpp/dev/audio_output_dev.h b/ppapi/cpp/dev/audio_output_dev.h
new file mode 100644
index 0000000..ebe2fb50
--- /dev/null
+++ b/ppapi/cpp/dev/audio_output_dev.h
@@ -0,0 +1,84 @@
+// Copyright (c) 2017 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 PPAPI_CPP_DEV_AUDIO_OUTPUT_DEV_H_
+#define PPAPI_CPP_DEV_AUDIO_OUTPUT_DEV_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
+#include "ppapi/cpp/audio_config.h"
+#include "ppapi/cpp/completion_callback.h"
+#include "ppapi/cpp/dev/device_ref_dev.h"
+#include "ppapi/cpp/resource.h"
+
+namespace pp {
+
+class InstanceHandle;
+
+class AudioOutput_Dev : public Resource {
+ public:
+  // An empty constructor for an AudioOutput resource.
+  AudioOutput_Dev();
+
+  // Constructor to create an audio output resource.
+  explicit AudioOutput_Dev(const InstanceHandle& instance);
+
+  virtual ~AudioOutput_Dev();
+
+  // Static function for determining whether the browser supports the required
+  // AudioOutput interface.
+  //
+  // @return true if the interface is available, false otherwise.
+  static bool IsAvailable();
+
+  int32_t EnumerateDevices(
+      const CompletionCallbackWithOutput<std::vector<DeviceRef_Dev> >& cb);
+
+  int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback,
+                              void* user_data);
+
+  // If |device_ref| is null (i.e., is_null() returns true), the default device
+  // will be used.
+  int32_t Open(const DeviceRef_Dev& device_ref,
+               const AudioConfig& config,
+               PPB_AudioOutput_Callback audio_output_callback,
+               void* user_data,
+               const CompletionCallback& callback);
+
+  // Getter function for returning the internal <code>PPB_AudioConfig</code>
+  // struct.
+  //
+  // @return A mutable reference to the PPB_AudioConfig struct.
+  AudioConfig& config() { return config_; }
+
+  // Getter function for returning the internal <code>PPB_AudioConfig</code>
+  // struct.
+  //
+  // @return A const reference to the internal <code>PPB_AudioConfig</code>
+  // struct.
+  const AudioConfig& config() const { return config_; }
+
+  // StartPlayback() starts playback of audio.
+  //
+  // @return true if successful, otherwise false.
+  bool StartPlayback();
+
+  // StopPlayback stops playback of audio.
+  //
+  // @return true if successful, otherwise false.
+  bool StopPlayback();
+
+  // Close closes the audio output device.
+  void Close();
+
+ private:
+  AudioConfig config_;
+};
+
+}  // namespace pp
+
+#endif  // PPAPI_CPP_DEV_AUDIO_OUTPUT_DEV_H_
diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
index 56b190b..6e8a8aa 100644
--- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
+++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016 The Chromium Authors. All rights reserved.
+/* Copyright (c) 2017 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.
  */
@@ -8,6 +8,7 @@
 
 #include "ppapi/c/ppb.h"
 #include "ppapi/c/dev/ppb_audio_input_dev.h"
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
 #include "ppapi/c/dev/ppb_device_ref_dev.h"
 #include "ppapi/c/dev/ppb_file_chooser_dev.h"
 #include "ppapi/c/dev/ppb_ime_input_event_dev.h"
@@ -156,6 +157,7 @@
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPP_Messaging_1_0;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_4;
+static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_DeviceRef_Dev_0_1;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileChooser_Dev_0_5;
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_FileChooser_Dev_0_6;
@@ -2751,6 +2753,55 @@
 
 /* End wrapper methods for PPB_AudioInput_Dev_0_4 */
 
+/* Begin wrapper methods for PPB_AudioOutput_Dev_0_1 */
+
+static PP_Resource Pnacl_M59_PPB_AudioOutput_Dev_Create(PP_Instance instance) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->Create(instance);
+}
+
+static PP_Bool Pnacl_M59_PPB_AudioOutput_Dev_IsAudioOutput(PP_Resource resource) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->IsAudioOutput(resource);
+}
+
+static int32_t Pnacl_M59_PPB_AudioOutput_Dev_EnumerateDevices(PP_Resource audio_output, struct PP_ArrayOutput* output, struct PP_CompletionCallback* callback) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->EnumerateDevices(audio_output, *output, *callback);
+}
+
+static int32_t Pnacl_M59_PPB_AudioOutput_Dev_MonitorDeviceChange(PP_Resource audio_output, PP_MonitorDeviceChangeCallback callback, void* user_data) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->MonitorDeviceChange(audio_output, callback, user_data);
+}
+
+static int32_t Pnacl_M59_PPB_AudioOutput_Dev_Open(PP_Resource audio_output, PP_Resource device_ref, PP_Resource config, PPB_AudioOutput_Callback audio_output_callback, void* user_data, struct PP_CompletionCallback* callback) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->Open(audio_output, device_ref, config, audio_output_callback, user_data, *callback);
+}
+
+static PP_Resource Pnacl_M59_PPB_AudioOutput_Dev_GetCurrentConfig(PP_Resource audio_output) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->GetCurrentConfig(audio_output);
+}
+
+static PP_Bool Pnacl_M59_PPB_AudioOutput_Dev_StartPlayback(PP_Resource audio_output) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->StartPlayback(audio_output);
+}
+
+static PP_Bool Pnacl_M59_PPB_AudioOutput_Dev_StopPlayback(PP_Resource audio_output) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  return iface->StopPlayback(audio_output);
+}
+
+static void Pnacl_M59_PPB_AudioOutput_Dev_Close(PP_Resource audio_output) {
+  const struct PPB_AudioOutput_Dev_0_1 *iface = Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1.real_iface;
+  iface->Close(audio_output);
+}
+
+/* End wrapper methods for PPB_AudioOutput_Dev_0_1 */
+
 /* Not generating wrapper methods for PPB_Buffer_Dev_0_4 */
 
 /* Not generating wrapper methods for PPB_Crypto_Dev_0_1 */
@@ -5408,6 +5459,18 @@
     .Close = (void (*)(PP_Resource audio_input))&Pnacl_M30_PPB_AudioInput_Dev_Close
 };
 
+static const struct PPB_AudioOutput_Dev_0_1 Pnacl_Wrappers_PPB_AudioOutput_Dev_0_1 = {
+    .Create = (PP_Resource (*)(PP_Instance instance))&Pnacl_M59_PPB_AudioOutput_Dev_Create,
+    .IsAudioOutput = (PP_Bool (*)(PP_Resource resource))&Pnacl_M59_PPB_AudioOutput_Dev_IsAudioOutput,
+    .EnumerateDevices = (int32_t (*)(PP_Resource audio_output, struct PP_ArrayOutput output, struct PP_CompletionCallback callback))&Pnacl_M59_PPB_AudioOutput_Dev_EnumerateDevices,
+    .MonitorDeviceChange = (int32_t (*)(PP_Resource audio_output, PP_MonitorDeviceChangeCallback callback, void* user_data))&Pnacl_M59_PPB_AudioOutput_Dev_MonitorDeviceChange,
+    .Open = (int32_t (*)(PP_Resource audio_output, PP_Resource device_ref, PP_Resource config, PPB_AudioOutput_Callback audio_output_callback, void* user_data, struct PP_CompletionCallback callback))&Pnacl_M59_PPB_AudioOutput_Dev_Open,
+    .GetCurrentConfig = (PP_Resource (*)(PP_Resource audio_output))&Pnacl_M59_PPB_AudioOutput_Dev_GetCurrentConfig,
+    .StartPlayback = (PP_Bool (*)(PP_Resource audio_output))&Pnacl_M59_PPB_AudioOutput_Dev_StartPlayback,
+    .StopPlayback = (PP_Bool (*)(PP_Resource audio_output))&Pnacl_M59_PPB_AudioOutput_Dev_StopPlayback,
+    .Close = (void (*)(PP_Resource audio_output))&Pnacl_M59_PPB_AudioOutput_Dev_Close
+};
+
 /* Not generating wrapper interface for PPB_Buffer_Dev_0_4 */
 
 /* Not generating wrapper interface for PPB_Crypto_Dev_0_1 */
@@ -6322,6 +6385,12 @@
   .real_iface = NULL
 };
 
+static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1 = {
+  .iface_macro = PPB_AUDIO_OUTPUT_DEV_INTERFACE_0_1,
+  .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_AudioOutput_Dev_0_1,
+  .real_iface = NULL
+};
+
 static struct __PnaclWrapperInfo Pnacl_WrapperInfo_PPB_DeviceRef_Dev_0_1 = {
   .iface_macro = PPB_DEVICEREF_DEV_INTERFACE_0_1,
   .wrapped_iface = (const void *) &Pnacl_Wrappers_PPB_DeviceRef_Dev_0_1,
@@ -6690,6 +6759,7 @@
   &Pnacl_WrapperInfo_PPB_WebSocket_1_0,
   &Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_3,
   &Pnacl_WrapperInfo_PPB_AudioInput_Dev_0_4,
+  &Pnacl_WrapperInfo_PPB_AudioOutput_Dev_0_1,
   &Pnacl_WrapperInfo_PPB_DeviceRef_Dev_0_1,
   &Pnacl_WrapperInfo_PPB_FileChooser_Dev_0_5,
   &Pnacl_WrapperInfo_PPB_FileChooser_Dev_0_6,
diff --git a/ppapi/proxy/BUILD.gn b/ppapi/proxy/BUILD.gn
index da070f8..b572778 100644
--- a/ppapi/proxy/BUILD.gn
+++ b/ppapi/proxy/BUILD.gn
@@ -219,6 +219,8 @@
     sources += [
       "audio_input_resource.cc",
       "audio_input_resource.h",
+      "audio_output_resource.cc",
+      "audio_output_resource.h",
       "broker_dispatcher.cc",
       "broker_dispatcher.h",
       "browser_font_singleton_resource.cc",
diff --git a/ppapi/proxy/audio_output_resource.cc b/ppapi/proxy/audio_output_resource.cc
new file mode 100644
index 0000000..6eeb0f0
--- /dev/null
+++ b/ppapi/proxy/audio_output_resource.cc
@@ -0,0 +1,318 @@
+// Copyright (c) 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ppapi/proxy/audio_output_resource.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/numerics/safe_conversions.h"
+#include "ipc/ipc_platform_file.h"
+#include "media/base/audio_bus.h"
+#include "media/base/audio_parameters.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/proxy/ppapi_messages.h"
+#include "ppapi/proxy/resource_message_params.h"
+#include "ppapi/proxy/serialized_handle.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/ppb_audio_config_shared.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppb_audio_config_api.h"
+
+namespace ppapi {
+namespace proxy {
+
+AudioOutputResource::AudioOutputResource(Connection connection,
+                                         PP_Instance instance)
+    : PluginResource(connection, instance),
+      open_state_(BEFORE_OPEN),
+      playing_(false),
+      shared_memory_size_(0),
+      audio_output_callback_(NULL),
+      user_data_(NULL),
+      enumeration_helper_(this),
+      bytes_per_second_(0),
+      sample_frame_count_(0),
+      client_buffer_size_bytes_(0) {
+  SendCreate(RENDERER, PpapiHostMsg_AudioOutput_Create());
+}
+
+AudioOutputResource::~AudioOutputResource() {
+  Close();
+}
+
+thunk::PPB_AudioOutput_API* AudioOutputResource::AsPPB_AudioOutput_API() {
+  return this;
+}
+
+void AudioOutputResource::OnReplyReceived(
+    const ResourceMessageReplyParams& params,
+    const IPC::Message& msg) {
+  if (!enumeration_helper_.HandleReply(params, msg))
+    PluginResource::OnReplyReceived(params, msg);
+}
+
+int32_t AudioOutputResource::EnumerateDevices(
+    const PP_ArrayOutput& output,
+    scoped_refptr<TrackedCallback> callback) {
+  return enumeration_helper_.EnumerateDevices(output, callback);
+}
+
+int32_t AudioOutputResource::MonitorDeviceChange(
+    PP_MonitorDeviceChangeCallback callback,
+    void* user_data) {
+  return enumeration_helper_.MonitorDeviceChange(callback, user_data);
+}
+
+int32_t AudioOutputResource::Open(
+    PP_Resource device_ref,
+    PP_Resource config,
+    PPB_AudioOutput_Callback audio_output_callback,
+    void* user_data,
+    scoped_refptr<TrackedCallback> callback) {
+  return CommonOpen(device_ref, config, audio_output_callback, user_data,
+                    callback);
+}
+
+PP_Resource AudioOutputResource::GetCurrentConfig() {
+  // AddRef for the caller.
+  if (config_.get())
+    PpapiGlobals::Get()->GetResourceTracker()->AddRefResource(config_);
+  return config_;
+}
+
+PP_Bool AudioOutputResource::StartPlayback() {
+  if (open_state_ == CLOSED || (open_state_ == BEFORE_OPEN &&
+                                !TrackedCallback::IsPending(open_callback_))) {
+    return PP_FALSE;
+  }
+  if (playing_)
+    return PP_TRUE;
+
+  playing_ = true;
+
+  StartThread();
+
+  Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(true));
+  return PP_TRUE;
+}
+
+PP_Bool AudioOutputResource::StopPlayback() {
+  if (open_state_ == CLOSED)
+    return PP_FALSE;
+  if (!playing_)
+    return PP_TRUE;
+
+  // If the audio output device hasn't been opened, set |playing_| to false and
+  // return directly.
+  if (open_state_ == BEFORE_OPEN) {
+    playing_ = false;
+    return PP_TRUE;
+  }
+
+  Post(RENDERER, PpapiHostMsg_AudioOutput_StartOrStop(false));
+
+  StopThread();
+  playing_ = false;
+
+  return PP_TRUE;
+}
+
+void AudioOutputResource::Close() {
+  if (open_state_ == CLOSED)
+    return;
+
+  open_state_ = CLOSED;
+  Post(RENDERER, PpapiHostMsg_AudioOutput_Close());
+  StopThread();
+
+  if (TrackedCallback::IsPending(open_callback_))
+    open_callback_->PostAbort();
+}
+
+void AudioOutputResource::LastPluginRefWasDeleted() {
+  enumeration_helper_.LastPluginRefWasDeleted();
+}
+
+void AudioOutputResource::OnPluginMsgOpenReply(
+    const ResourceMessageReplyParams& params) {
+  if (open_state_ == BEFORE_OPEN && params.result() == PP_OK) {
+    IPC::PlatformFileForTransit socket_handle_for_transit =
+        IPC::InvalidPlatformFileForTransit();
+    params.TakeSocketHandleAtIndex(0, &socket_handle_for_transit);
+    base::SyncSocket::Handle socket_handle =
+        IPC::PlatformFileForTransitToPlatformFile(socket_handle_for_transit);
+    CHECK(socket_handle != base::SyncSocket::kInvalidHandle);
+
+    SerializedHandle serialized_shared_memory_handle =
+        params.TakeHandleOfTypeAtIndex(1, SerializedHandle::SHARED_MEMORY);
+    CHECK(serialized_shared_memory_handle.IsHandleValid());
+
+    open_state_ = OPENED;
+    SetStreamInfo(serialized_shared_memory_handle.shmem(),
+                  serialized_shared_memory_handle.size(), socket_handle);
+  } else {
+    playing_ = false;
+  }
+
+  // The callback may have been aborted by Close().
+  if (TrackedCallback::IsPending(open_callback_))
+    open_callback_->Run(params.result());
+}
+
+void AudioOutputResource::SetStreamInfo(
+    base::SharedMemoryHandle shared_memory_handle,
+    size_t shared_memory_size,
+    base::SyncSocket::Handle socket_handle) {
+  socket_.reset(new base::CancelableSyncSocket(socket_handle));
+  shared_memory_.reset(new base::SharedMemory(shared_memory_handle, false));
+  shared_memory_size_ = shared_memory_size;
+  DCHECK(!shared_memory_->memory());
+
+  // If we fail to map the shared memory into the caller's address space we
+  // might as well fail here since nothing will work if this is the case.
+  CHECK(shared_memory_->Map(shared_memory_size_));
+
+  // Create a new audio bus and wrap the audio data section in shared memory.
+  media::AudioOutputBuffer* buffer =
+      static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
+  audio_bus_ = media::AudioBus::WrapMemory(kAudioOutputChannels,
+                                           sample_frame_count_, buffer->audio);
+
+  // Ensure that the size of the created audio bus matches the allocated
+  // size in shared memory.
+  // Example: DCHECK_EQ(8208 - 16, 8192) for |sample_frame_count_| = 2048.
+  const uint32_t audio_bus_size_bytes = media::AudioBus::CalculateMemorySize(
+      audio_bus_->channels(), audio_bus_->frames());
+  DCHECK_EQ(shared_memory_size_ - sizeof(media::AudioOutputBufferParameters),
+            audio_bus_size_bytes);
+
+  // Setup integer audio buffer for user audio data
+  client_buffer_size_bytes_ = audio_bus_->frames() * audio_bus_->channels() *
+                              kBitsPerAudioOutputSample / 8;
+  client_buffer_.reset(new uint8_t[client_buffer_size_bytes_]);
+}
+
+void AudioOutputResource::StartThread() {
+  // Don't start the thread unless all our state is set up correctly.
+  if (!audio_output_callback_ || !socket_.get() || !shared_memory_->memory() ||
+      !audio_bus_.get() || !client_buffer_.get() || bytes_per_second_ == 0)
+    return;
+
+  // Clear contents of shm buffer before starting audio thread. This will
+  // prevent a burst of static if for some reason the audio thread doesn't
+  // start up quickly enough.
+  memset(shared_memory_->memory(), 0, shared_memory_size_);
+  memset(client_buffer_.get(), 0, client_buffer_size_bytes_);
+
+  DCHECK(!audio_output_thread_.get());
+  audio_output_thread_.reset(
+      new base::DelegateSimpleThread(this, "plugin_audio_output_thread"));
+  audio_output_thread_->Start();
+}
+
+void AudioOutputResource::StopThread() {
+  // Shut down the socket to escape any hanging |Receive|s.
+  if (socket_.get())
+    socket_->Shutdown();
+  if (audio_output_thread_.get()) {
+    audio_output_thread_->Join();
+    audio_output_thread_.reset();
+  }
+}
+
+void AudioOutputResource::Run() {
+  // The shared memory represents AudioOutputBufferParameters and the actual
+  // data buffer stored as an audio bus.
+  media::AudioOutputBuffer* buffer =
+      static_cast<media::AudioOutputBuffer*>(shared_memory_->memory());
+
+  // This is a constantly increasing counter that is used to verify on the
+  // browser side that buffers are in sync.
+  uint32_t buffer_index = 0;
+
+  while (true) {
+    int pending_data = 0;
+    size_t bytes_read = socket_->Receive(&pending_data, sizeof(pending_data));
+    if (bytes_read != sizeof(pending_data)) {
+      DCHECK_EQ(bytes_read, 0U);
+      break;
+    }
+    if (pending_data < 0)
+      break;
+
+    {
+      base::TimeDelta delay =
+          base::TimeDelta::FromMicroseconds(buffer->params.delay);
+
+      audio_output_callback_(client_buffer_.get(), client_buffer_size_bytes_,
+                             delay.InSecondsF(), user_data_);
+    }
+
+    // Deinterleave the audio data into the shared memory as floats.
+    audio_bus_->FromInterleaved(client_buffer_.get(), audio_bus_->frames(),
+                                kBitsPerAudioOutputSample / 8);
+
+    // Inform other side that we have read the data from the shared memory.
+    // Let the other end know which buffer we just filled.  The buffer index is
+    // used to ensure the other end is getting the buffer it expects.  For more
+    // details on how this works see AudioSyncReader::WaitUntilDataIsReady().
+    ++buffer_index;
+    size_t bytes_sent = socket_->Send(&buffer_index, sizeof(buffer_index));
+    if (bytes_sent != sizeof(buffer_index)) {
+      DCHECK_EQ(bytes_sent, 0U);
+      break;
+    }
+  }
+}
+
+int32_t AudioOutputResource::CommonOpen(
+    PP_Resource device_ref,
+    PP_Resource config,
+    PPB_AudioOutput_Callback audio_output_callback,
+    void* user_data,
+    scoped_refptr<TrackedCallback> callback) {
+  std::string device_id;
+  // |device_id| remains empty if |device_ref| is 0, which means the default
+  // device.
+  if (device_ref != 0) {
+    thunk::EnterResourceNoLock<thunk::PPB_DeviceRef_API> enter_device_ref(
+        device_ref, true);
+    if (enter_device_ref.failed())
+      return PP_ERROR_BADRESOURCE;
+    device_id = enter_device_ref.object()->GetDeviceRefData().id;
+  }
+
+  if (TrackedCallback::IsPending(open_callback_))
+    return PP_ERROR_INPROGRESS;
+  if (open_state_ != BEFORE_OPEN)
+    return PP_ERROR_FAILED;
+
+  if (!audio_output_callback)
+    return PP_ERROR_BADARGUMENT;
+  thunk::EnterResourceNoLock<thunk::PPB_AudioConfig_API> enter_config(config,
+                                                                      true);
+  if (enter_config.failed())
+    return PP_ERROR_BADARGUMENT;
+
+  config_ = config;
+  audio_output_callback_ = audio_output_callback;
+  user_data_ = user_data;
+  open_callback_ = callback;
+  bytes_per_second_ = kAudioOutputChannels * (kBitsPerAudioOutputSample / 8) *
+                      enter_config.object()->GetSampleRate();
+  sample_frame_count_ = enter_config.object()->GetSampleFrameCount();
+
+  PpapiHostMsg_AudioOutput_Open msg(
+      device_id, enter_config.object()->GetSampleRate(),
+      enter_config.object()->GetSampleFrameCount());
+  Call<PpapiPluginMsg_AudioOutput_OpenReply>(
+      RENDERER, msg,
+      base::Bind(&AudioOutputResource::OnPluginMsgOpenReply,
+                 base::Unretained(this)));
+  return PP_OK_COMPLETIONPENDING;
+}
+}  // namespace proxy
+}  // namespace ppapi
diff --git a/ppapi/proxy/audio_output_resource.h b/ppapi/proxy/audio_output_resource.h
new file mode 100644
index 0000000..ee654e1
--- /dev/null
+++ b/ppapi/proxy/audio_output_resource.h
@@ -0,0 +1,149 @@
+// Copyright (c) 2017 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 PPAPI_PROXY_AUDIO_OUTPUT_RESOURCE_H_
+#define PPAPI_PROXY_AUDIO_OUTPUT_RESOURCE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+
+#include "base/compiler_specific.h"
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/shared_memory.h"
+#include "base/sync_socket.h"
+#include "base/threading/simple_thread.h"
+#include "ppapi/c/ppb_audio_config.h"
+#include "ppapi/proxy/device_enumeration_resource_helper.h"
+#include "ppapi/proxy/plugin_resource.h"
+#include "ppapi/shared_impl/scoped_pp_resource.h"
+#include "ppapi/thunk/ppb_audio_output_api.h"
+
+namespace media {
+class AudioBus;
+}
+
+namespace ppapi {
+namespace proxy {
+
+class ResourceMessageReplyParams;
+
+class AudioOutputResource : public PluginResource,
+                            public thunk::PPB_AudioOutput_API,
+                            public base::DelegateSimpleThread::Delegate {
+ public:
+  AudioOutputResource(Connection connection, PP_Instance instance);
+  ~AudioOutputResource() override;
+
+  // Resource overrides.
+  thunk::PPB_AudioOutput_API* AsPPB_AudioOutput_API() override;
+  void OnReplyReceived(const ResourceMessageReplyParams& params,
+                       const IPC::Message& msg) override;
+
+  // PPB_AudioOutput_API implementation.
+  int32_t EnumerateDevices(const PP_ArrayOutput& output,
+                           scoped_refptr<TrackedCallback> callback) override;
+  int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback,
+                              void* user_data) override;
+  int32_t Open(PP_Resource device_ref,
+               PP_Resource config,
+               PPB_AudioOutput_Callback audio_output_callback,
+               void* user_data,
+               scoped_refptr<TrackedCallback> callback) override;
+
+  PP_Resource GetCurrentConfig() override;
+
+  bool playing() const { return playing_; }
+
+  PP_Bool StartPlayback() override;
+  PP_Bool StopPlayback() override;
+  void Close() override;
+
+ protected:
+  // Resource override.
+  void LastPluginRefWasDeleted() override;
+
+ private:
+  enum OpenState { BEFORE_OPEN, OPENED, CLOSED };
+
+  void OnPluginMsgOpenReply(const ResourceMessageReplyParams& params);
+
+  // Sets the shared memory and socket handles.
+  void SetStreamInfo(base::SharedMemoryHandle shared_memory_handle,
+                     size_t shared_memory_size,
+                     base::SyncSocket::Handle socket_handle);
+
+  // Starts execution of the audio output thread.
+  void StartThread();
+
+  // Stops execution of the audio output thread.
+  void StopThread();
+
+  // DelegateSimpleThread::Delegate implementation.
+  // Run on the audio output thread.
+  void Run() override;
+
+  int32_t CommonOpen(PP_Resource device_ref,
+                     PP_Resource config,
+                     PPB_AudioOutput_Callback audio_output_callback,
+                     void* user_data,
+                     scoped_refptr<TrackedCallback> callback);
+
+  OpenState open_state_;
+
+  // True if playing the stream.
+  bool playing_;
+
+  // Socket used to notify us when new samples are available. This pointer is
+  // created in SetStreamInfo().
+  std::unique_ptr<base::CancelableSyncSocket> socket_;
+
+  // Sample buffer in shared memory. This pointer is created in
+  // SetStreamInfo(). The memory is only mapped when the audio thread is
+  // created.
+  std::unique_ptr<base::SharedMemory> shared_memory_;
+
+  // The size of the sample buffer in bytes.
+  size_t shared_memory_size_;
+
+  // When the callback is set, this thread is spawned for calling it.
+  std::unique_ptr<base::DelegateSimpleThread> audio_output_thread_;
+
+  // Callback to call when new samples are available.
+  PPB_AudioOutput_Callback audio_output_callback_;
+
+  // User data pointer passed verbatim to the callback function.
+  void* user_data_;
+
+  // The callback is not directly passed to OnPluginMsgOpenReply() because we
+  // would like to be able to cancel it early in Close().
+  scoped_refptr<TrackedCallback> open_callback_;
+
+  // Owning reference to the current config object. This isn't actually used,
+  // we just dish it out as requested by the plugin.
+  ScopedPPResource config_;
+
+  DeviceEnumerationResourceHelper enumeration_helper_;
+
+  // The data size (in bytes) of one second of audio output. Used to calculate
+  // latency.
+  size_t bytes_per_second_;
+
+  // AudioBus for shuttling data across the shared memory.
+  std::unique_ptr<media::AudioBus> audio_bus_;
+  int sample_frame_count_;
+
+  // Internal buffer for client's integer audio data.
+  int client_buffer_size_bytes_;
+  std::unique_ptr<uint8_t[]> client_buffer_;
+
+  DISALLOW_COPY_AND_ASSIGN(AudioOutputResource);
+};
+
+}  // namespace proxy
+}  // namespace ppapi
+
+#endif  // PPAPI_PROXY_AUDIO_OUTPUT_RESOURCE_H_
diff --git a/ppapi/proxy/interface_list.cc b/ppapi/proxy/interface_list.cc
index 4984753..d14a931 100644
--- a/ppapi/proxy/interface_list.cc
+++ b/ppapi/proxy/interface_list.cc
@@ -12,6 +12,7 @@
 #include "base/memory/singleton.h"
 #include "build/build_config.h"
 #include "ppapi/c/dev/ppb_audio_input_dev.h"
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
 #include "ppapi/c/dev/ppb_buffer_dev.h"
 #include "ppapi/c/dev/ppb_char_set_dev.h"
 #include "ppapi/c/dev/ppb_crypto_dev.h"
diff --git a/ppapi/proxy/ppapi_messages.h b/ppapi/proxy/ppapi_messages.h
index c99a276..39bcec7 100644
--- a/ppapi/proxy/ppapi_messages.h
+++ b/ppapi/proxy/ppapi_messages.h
@@ -2246,6 +2246,18 @@
 IPC_MESSAGE_CONTROL1(PpapiHostMsg_AudioInput_StartOrStop, bool /* capture */)
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_AudioInput_Close)
 
+// Audio output.
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_AudioOutput_Create)
+IPC_MESSAGE_CONTROL3(PpapiHostMsg_AudioOutput_Open,
+                     std::string /* device_id */,
+                     PP_AudioSampleRate /* sample_rate */,
+                     uint32_t /* sample_frame_count */)
+// Reply to an Open call. This supplies a socket handle and a shared memory
+// handle. Both handles are passed in the ReplyParams struct.
+IPC_MESSAGE_CONTROL0(PpapiPluginMsg_AudioOutput_OpenReply)
+IPC_MESSAGE_CONTROL1(PpapiHostMsg_AudioOutput_StartOrStop, bool /* playback */)
+IPC_MESSAGE_CONTROL0(PpapiHostMsg_AudioOutput_Close)
+
 // BrowserFont -----------------------------------------------------------------
 
 IPC_MESSAGE_CONTROL0(PpapiHostMsg_BrowserFontSingleton_Create)
diff --git a/ppapi/proxy/resource_creation_proxy.cc b/ppapi/proxy/resource_creation_proxy.cc
index 1c79bc5..0e414285 100644
--- a/ppapi/proxy/resource_creation_proxy.cc
+++ b/ppapi/proxy/resource_creation_proxy.cc
@@ -9,6 +9,7 @@
 #include "ppapi/c/pp_size.h"
 #include "ppapi/proxy/audio_encoder_resource.h"
 #include "ppapi/proxy/audio_input_resource.h"
+#include "ppapi/proxy/audio_output_resource.h"
 #include "ppapi/proxy/camera_device_resource.h"
 #include "ppapi/proxy/compositor_resource.h"
 #include "ppapi/proxy/connection.h"
@@ -429,6 +430,10 @@
   return (new AudioInputResource(GetConnection(), instance))->GetReference();
 }
 
+PP_Resource ResourceCreationProxy::CreateAudioOutput(PP_Instance instance) {
+  return (new AudioOutputResource(GetConnection(), instance))->GetReference();
+}
+
 PP_Resource ResourceCreationProxy::CreateBroker(PP_Instance instance) {
   return PPB_Broker_Proxy::CreateProxyResource(instance);
 }
diff --git a/ppapi/proxy/resource_creation_proxy.h b/ppapi/proxy/resource_creation_proxy.h
index b7b0c3138..f83c53d 100644
--- a/ppapi/proxy/resource_creation_proxy.h
+++ b/ppapi/proxy/resource_creation_proxy.h
@@ -163,6 +163,7 @@
   PP_Resource CreateX509CertificatePrivate(PP_Instance instance) override;
 #if !defined(OS_NACL)
   PP_Resource CreateAudioInput(PP_Instance instance) override;
+  PP_Resource CreateAudioOutput(PP_Instance instance) override;
   PP_Resource CreateBroker(PP_Instance instance) override;
   PP_Resource CreateBrowserFont(
       PP_Instance instance,
diff --git a/ppapi/shared_impl/api_id.h b/ppapi/shared_impl/api_id.h
index a049948..ff8511d 100644
--- a/ppapi/shared_impl/api_id.h
+++ b/ppapi/shared_impl/api_id.h
@@ -15,6 +15,7 @@
   API_ID_PPB_AUDIO = 1,
   API_ID_PPB_AUDIO_CONFIG,
   API_ID_PPB_AUDIO_INPUT_DEV,
+  API_ID_PPB_AUDIO_OUTPUT_DEV,
   API_ID_PPB_BROKER,
   API_ID_PPB_BUFFER,
   API_ID_PPB_CONTEXT_3D,
diff --git a/ppapi/shared_impl/resource.h b/ppapi/shared_impl/resource.h
index 8d30ed4..687f961 100644
--- a/ppapi/shared_impl/resource.h
+++ b/ppapi/shared_impl/resource.h
@@ -24,6 +24,7 @@
   F(PPB_AudioConfig_API)                \
   F(PPB_AudioEncoder_API)               \
   F(PPB_AudioInput_API)                 \
+  F(PPB_AudioOutput_API)                \
   F(PPB_AudioTrusted_API)               \
   F(PPB_Broker_API)                     \
   F(PPB_Broker_Instance_API)            \
diff --git a/ppapi/thunk/BUILD.gn b/ppapi/thunk/BUILD.gn
index 913748f..44ef7d05 100644
--- a/ppapi/thunk/BUILD.gn
+++ b/ppapi/thunk/BUILD.gn
@@ -30,6 +30,7 @@
     "ppb_audio_encoder_api.h",
     "ppb_audio_encoder_thunk.cc",
     "ppb_audio_input_api.h",
+    "ppb_audio_output_api.h",
     "ppb_audio_thunk.cc",
     "ppb_broker_api.h",
     "ppb_browser_font_singleton_api.h",
@@ -163,6 +164,7 @@
   if (!is_nacl) {
     sources += [
       "ppb_audio_input_dev_thunk.cc",
+      "ppb_audio_output_dev_thunk.cc",
       "ppb_broker_thunk.cc",
       "ppb_browser_font_trusted_thunk.cc",
       "ppb_buffer_thunk.cc",
diff --git a/ppapi/thunk/interfaces_ppb_public_dev.h b/ppapi/thunk/interfaces_ppb_public_dev.h
index ca28d014..5c69935a 100644
--- a/ppapi/thunk/interfaces_ppb_public_dev.h
+++ b/ppapi/thunk/interfaces_ppb_public_dev.h
@@ -26,6 +26,7 @@
 
 PROXIED_IFACE(PPB_AUDIO_INPUT_DEV_INTERFACE_0_3, PPB_AudioInput_Dev_0_3)
 PROXIED_IFACE(PPB_AUDIO_INPUT_DEV_INTERFACE_0_4, PPB_AudioInput_Dev_0_4)
+PROXIED_IFACE(PPB_AUDIO_OUTPUT_DEV_INTERFACE_0_1, PPB_AudioOutput_Dev_0_1)
 PROXIED_IFACE(PPB_BUFFER_DEV_INTERFACE_0_4, PPB_Buffer_Dev_0_4)
 PROXIED_IFACE(PPB_CHAR_SET_DEV_INTERFACE_0_4, PPB_CharSet_Dev_0_4)
 PROXIED_IFACE(PPB_CRYPTO_DEV_INTERFACE_0_1, PPB_Crypto_Dev_0_1)
diff --git a/ppapi/thunk/ppb_audio_output_api.h b/ppapi/thunk/ppb_audio_output_api.h
new file mode 100644
index 0000000..4fceee75
--- /dev/null
+++ b/ppapi/thunk/ppb_audio_output_api.h
@@ -0,0 +1,43 @@
+// Copyright (c) 2017 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 PPAPI_THUNK_AUDIO_OUTPUT_API_H_
+#define PPAPI_THUNK_AUDIO_OUTPUT_API_H_
+
+#include <stdint.h>
+
+#include <string>
+
+#include "base/memory/ref_counted.h"
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
+
+namespace ppapi {
+
+class TrackedCallback;
+
+namespace thunk {
+
+class PPB_AudioOutput_API {
+ public:
+  virtual ~PPB_AudioOutput_API() {}
+
+  virtual int32_t EnumerateDevices(const PP_ArrayOutput& output,
+                                   scoped_refptr<TrackedCallback> callback) = 0;
+  virtual int32_t MonitorDeviceChange(PP_MonitorDeviceChangeCallback callback,
+                                      void* user_data) = 0;
+  virtual int32_t Open(PP_Resource device_ref,
+                       PP_Resource config,
+                       PPB_AudioOutput_Callback audio_output_callback,
+                       void* user_data,
+                       scoped_refptr<TrackedCallback> callback) = 0;
+  virtual PP_Resource GetCurrentConfig() = 0;
+  virtual PP_Bool StartPlayback() = 0;
+  virtual PP_Bool StopPlayback() = 0;
+  virtual void Close() = 0;
+};
+
+}  // namespace thunk
+}  // namespace ppapi
+
+#endif  // PPAPI_THUNK_AUDIO_OUTPUT_API_H_
diff --git a/ppapi/thunk/ppb_audio_output_dev_thunk.cc b/ppapi/thunk/ppb_audio_output_dev_thunk.cc
new file mode 100644
index 0000000..a5f5b12
--- /dev/null
+++ b/ppapi/thunk/ppb_audio_output_dev_thunk.cc
@@ -0,0 +1,116 @@
+// Copyright (c) 2017 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.
+
+// From dev/ppb_audio_output_dev.idl modified Fri Mar 31 08:08:16 2017.
+
+#include <stdint.h>
+
+#include "ppapi/c/dev/ppb_audio_output_dev.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "ppapi/thunk/enter.h"
+#include "ppapi/thunk/ppapi_thunk_export.h"
+#include "ppapi/thunk/ppb_audio_output_api.h"
+
+namespace ppapi {
+namespace thunk {
+
+namespace {
+
+PP_Resource Create(PP_Instance instance) {
+  VLOG(4) << "PPB_AudioOutput_Dev::Create()";
+  EnterResourceCreation enter(instance);
+  if (enter.failed())
+    return 0;
+  return enter.functions()->CreateAudioOutput(instance);
+}
+
+PP_Bool IsAudioOutput(PP_Resource resource) {
+  VLOG(4) << "PPB_AudioOutput_Dev::IsAudioOutput()";
+  EnterResource<PPB_AudioOutput_API> enter(resource, false);
+  return PP_FromBool(enter.succeeded());
+}
+
+int32_t EnumerateDevices(PP_Resource audio_output,
+                         struct PP_ArrayOutput output,
+                         struct PP_CompletionCallback callback) {
+  VLOG(4) << "PPB_AudioOutput_Dev::EnumerateDevices()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, callback, true);
+  if (enter.failed())
+    return enter.retval();
+  return enter.SetResult(
+      enter.object()->EnumerateDevices(output, enter.callback()));
+}
+
+int32_t MonitorDeviceChange(PP_Resource audio_output,
+                            PP_MonitorDeviceChangeCallback callback,
+                            void* user_data) {
+  VLOG(4) << "PPB_AudioOutput_Dev::MonitorDeviceChange()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, true);
+  if (enter.failed())
+    return enter.retval();
+  return enter.object()->MonitorDeviceChange(callback, user_data);
+}
+
+int32_t Open(PP_Resource audio_output,
+             PP_Resource device_ref,
+             PP_Resource config,
+             PPB_AudioOutput_Callback audio_output_callback,
+             void* user_data,
+             struct PP_CompletionCallback callback) {
+  VLOG(4) << "PPB_AudioOutput_Dev::Open()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, callback, true);
+  if (enter.failed())
+    return enter.retval();
+  return enter.SetResult(enter.object()->Open(
+      device_ref, config, audio_output_callback, user_data, enter.callback()));
+}
+
+PP_Resource GetCurrentConfig(PP_Resource audio_output) {
+  VLOG(4) << "PPB_AudioOutput_Dev::GetCurrentConfig()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, true);
+  if (enter.failed())
+    return 0;
+  return enter.object()->GetCurrentConfig();
+}
+
+PP_Bool StartPlayback(PP_Resource audio_output) {
+  VLOG(4) << "PPB_AudioOutput_Dev::StartPlayback()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, true);
+  if (enter.failed())
+    return PP_FALSE;
+  return enter.object()->StartPlayback();
+}
+
+PP_Bool StopPlayback(PP_Resource audio_output) {
+  VLOG(4) << "PPB_AudioOutput_Dev::StopPlayback()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, true);
+  if (enter.failed())
+    return PP_FALSE;
+  return enter.object()->StopPlayback();
+}
+
+void Close(PP_Resource audio_output) {
+  VLOG(4) << "PPB_AudioOutput_Dev::Close()";
+  EnterResource<PPB_AudioOutput_API> enter(audio_output, true);
+  if (enter.failed())
+    return;
+  enter.object()->Close();
+}
+
+const PPB_AudioOutput_Dev_0_1 g_ppb_audiooutput_dev_thunk_0_1 = {
+    &Create, &IsAudioOutput,    &EnumerateDevices, &MonitorDeviceChange,
+    &Open,   &GetCurrentConfig, &StartPlayback,    &StopPlayback,
+    &Close};
+
+}  // namespace
+
+PPAPI_THUNK_EXPORT const PPB_AudioOutput_Dev_0_1*
+GetPPB_AudioOutput_Dev_0_1_Thunk() {
+  return &g_ppb_audiooutput_dev_thunk_0_1;
+}
+
+}  // namespace thunk
+}  // namespace ppapi
diff --git a/ppapi/thunk/resource_creation_api.h b/ppapi/thunk/resource_creation_api.h
index aba7f8b..682f680 100644
--- a/ppapi/thunk/resource_creation_api.h
+++ b/ppapi/thunk/resource_creation_api.h
@@ -185,6 +185,7 @@
   virtual PP_Resource CreateX509CertificatePrivate(PP_Instance instance) = 0;
 #if !defined(OS_NACL)
   virtual PP_Resource CreateAudioInput(PP_Instance instance) = 0;
+  virtual PP_Resource CreateAudioOutput(PP_Instance instance) = 0;
   virtual PP_Resource CreateBroker(PP_Instance instance) = 0;
   virtual PP_Resource CreateBrowserFont(
       PP_Instance instance,
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.cc b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
index 6c72bff..347f7e5b 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.cc
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.cc
@@ -9,6 +9,7 @@
 #include "base/files/important_file_writer.h"
 #include "base/json/json_file_value_serializer.h"
 #include "base/json/json_string_value_serializer.h"
+#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/path_service.h"
@@ -41,24 +42,25 @@
     FILE_PATH_LITERAL("RLZ Data.lock");
 
 // RLZ store path for testing.
-base::FilePath g_testing_rlz_store_path_;
+base::LazyInstance<base::FilePath>::Leaky g_testing_rlz_store_path =
+    LAZY_INSTANCE_INITIALIZER;
+
+base::FilePath GetRlzStorePathCommon() {
+  base::FilePath homedir;
+  PathService::Get(base::DIR_HOME, &homedir);
+  return g_testing_rlz_store_path.Get().empty()
+             ? homedir
+             : g_testing_rlz_store_path.Get();
+}
 
 // Returns file path of the RLZ storage.
 base::FilePath GetRlzStorePath() {
-  base::FilePath homedir;
-  PathService::Get(base::DIR_HOME, &homedir);
-  return g_testing_rlz_store_path_.empty() ?
-      homedir.Append(kRLZDataFileName) :
-      g_testing_rlz_store_path_.Append(kRLZDataFileName);
+  return GetRlzStorePathCommon().Append(kRLZDataFileName);
 }
 
 // Returns file path of the RLZ storage lock file.
 base::FilePath GetRlzStoreLockPath() {
-  base::FilePath homedir;
-  PathService::Get(base::DIR_HOME, &homedir);
-  return g_testing_rlz_store_path_.empty() ?
-      homedir.Append(kRLZLockFileName) :
-      g_testing_rlz_store_path_.Append(kRLZLockFileName);
+  return GetRlzStorePathCommon().Append(kRLZLockFileName);
 }
 
 // Returns the dictionary key for storing access point-related prefs.
@@ -155,7 +157,7 @@
     Product product,
     std::vector<std::string>* events) {
   DCHECK(CalledOnValidThread());
-  base::ListValue* events_list = NULL; ;
+  base::ListValue* events_list = nullptr;
   if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list))
     return false;
   events->clear();
@@ -311,7 +313,7 @@
 
 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
   --g_lock_depth;
-  DCHECK(g_lock_depth >= 0);
+  DCHECK_GE(g_lock_depth, 0);
 
   if (g_lock_depth > 0) {
     // Other locks are still using store_, so don't free it yet.
@@ -331,7 +333,7 @@
 namespace testing {
 
 void SetRlzStoreDirectory(const base::FilePath& directory) {
-  g_testing_rlz_store_path_ = directory;
+  g_testing_rlz_store_path.Get() = directory;
 }
 
 std::string RlzStoreFilenameStr() {
diff --git a/rlz/chromeos/lib/rlz_value_store_chromeos.h b/rlz/chromeos/lib/rlz_value_store_chromeos.h
index ec26248..cae01ba 100644
--- a/rlz/chromeos/lib/rlz_value_store_chromeos.h
+++ b/rlz/chromeos/lib/rlz_value_store_chromeos.h
@@ -13,11 +13,10 @@
 #include "base/files/file_path.h"
 #include "base/macros.h"
 #include "base/threading/non_thread_safe.h"
-#include "base/values.h"
 #include "rlz/lib/rlz_value_store.h"
 
 namespace base {
-class SequencedTaskRunner;
+class DictionaryValue;
 class Value;
 }
 
@@ -27,12 +26,8 @@
 class RlzValueStoreChromeOS : public RlzValueStore,
                               public base::NonThreadSafe {
  public:
-  // // Sets the task runner that will be used by ImportantFileWriter for write
-  // // operations.
-  // static void SetFileTaskRunner(base::SequencedTaskRunner* file_task_runner);
-
   // Creates new instance and synchronously reads data from file.
-  RlzValueStoreChromeOS(const base::FilePath& store_path);
+  explicit RlzValueStoreChromeOS(const base::FilePath& store_path);
   ~RlzValueStoreChromeOS() override;
 
   // RlzValueStore overrides:
diff --git a/services/shape_detection/OWNERS b/services/shape_detection/OWNERS
new file mode 100644
index 0000000..26e1bed
--- /dev/null
+++ b/services/shape_detection/OWNERS
@@ -0,0 +1,5 @@
+mcasas@chromium.org
+reillyg@chromium.org
+
+# COMPONENT: Blink>ImageCapture
+# TEAM: device-dev@chromium.org
diff --git a/services/ui/public/cpp/bitmap/BUILD.gn b/services/ui/public/cpp/bitmap/BUILD.gn
new file mode 100644
index 0000000..2562f20
--- /dev/null
+++ b/services/ui/public/cpp/bitmap/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2017 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//build/config/ui.gni")
+
+source_set("bitmap") {
+  sources = [
+    "child_shared_bitmap_manager.cc",
+    "child_shared_bitmap_manager.h",
+  ]
+
+  deps = [
+    "//base",
+    "//cc",
+    "//cc/ipc:interfaces",
+    "//mojo/public/cpp/bindings",
+    "//ui/gfx",
+  ]
+}
diff --git a/content/child/child_shared_bitmap_manager.cc b/services/ui/public/cpp/bitmap/child_shared_bitmap_manager.cc
similarity index 68%
rename from content/child/child_shared_bitmap_manager.cc
rename to services/ui/public/cpp/bitmap/child_shared_bitmap_manager.cc
index 225c569..92fc430 100644
--- a/content/child/child_shared_bitmap_manager.cc
+++ b/services/ui/public/cpp/bitmap/child_shared_bitmap_manager.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/child/child_shared_bitmap_manager.h"
+#include "services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h"
 
 #include <stddef.h>
 
@@ -12,44 +12,43 @@
 #include "base/memory/ptr_util.h"
 #include "base/process/memory.h"
 #include "base/process/process_metrics.h"
+#include "base/trace_event/trace_event.h"
 #include "build/build_config.h"
-#include "content/child/child_thread_impl.h"
-#include "content/common/child_process_messages.h"
 #include "mojo/public/cpp/system/platform_handle.h"
 #include "ui/gfx/geometry/size.h"
 
-namespace content {
+namespace ui {
 
 namespace {
 
 class ChildSharedBitmap : public cc::SharedBitmap {
  public:
   ChildSharedBitmap(
-      const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-          render_message_filter_ptr,
+      const scoped_refptr<cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>
+          shared_bitmap_manager_ptr,
       base::SharedMemory* shared_memory,
       const cc::SharedBitmapId& id)
       : cc::SharedBitmap(static_cast<uint8_t*>(shared_memory->memory()), id),
-        render_message_filter_ptr_(render_message_filter_ptr) {}
+        shared_bitmap_manager_ptr_(shared_bitmap_manager_ptr) {}
 
   ChildSharedBitmap(
-      const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-          render_message_filter_ptr,
+      const scoped_refptr<cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>
+          shared_bitmap_manager_ptr,
       std::unique_ptr<base::SharedMemory> shared_memory_holder,
       const cc::SharedBitmapId& id)
-      : ChildSharedBitmap(render_message_filter_ptr,
+      : ChildSharedBitmap(shared_bitmap_manager_ptr,
                           shared_memory_holder.get(),
                           id) {
     shared_memory_holder_ = std::move(shared_memory_holder);
   }
 
   ~ChildSharedBitmap() override {
-    (*render_message_filter_ptr_)->DeletedSharedBitmap(id());
+    (*shared_bitmap_manager_ptr_)->DidDeleteSharedBitmap(id());
   }
 
  private:
-  scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>
-      render_message_filter_ptr_;
+  scoped_refptr<cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>
+      shared_bitmap_manager_ptr_;
   std::unique_ptr<base::SharedMemory> shared_memory_holder_;
 };
 
@@ -77,12 +76,32 @@
   base::TerminateBecauseOutOfMemory(alloc_size);
 }
 
+// Allocates a block of shared memory of the given size. Returns nullptr on
+// failure.
+std::unique_ptr<base::SharedMemory> AllocateSharedMemory(size_t buf_size) {
+  mojo::ScopedSharedBufferHandle mojo_buf =
+      mojo::SharedBufferHandle::Create(buf_size);
+  if (!mojo_buf->is_valid()) {
+    LOG(WARNING) << "Browser failed to allocate shared memory";
+    return nullptr;
+  }
+
+  base::SharedMemoryHandle shared_buf;
+  if (mojo::UnwrapSharedMemoryHandle(std::move(mojo_buf), &shared_buf, nullptr,
+                                     nullptr) != MOJO_RESULT_OK) {
+    LOG(WARNING) << "Browser failed to allocate shared memory";
+    return nullptr;
+  }
+
+  return base::MakeUnique<base::SharedMemory>(shared_buf, false);
+}
+
 }  // namespace
 
 ChildSharedBitmapManager::ChildSharedBitmapManager(
-    const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-        render_message_filter_ptr)
-    : render_message_filter_ptr_(render_message_filter_ptr) {}
+    const scoped_refptr<cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>&
+        shared_bitmap_manager_ptr)
+    : shared_bitmap_manager_ptr_(shared_bitmap_manager_ptr) {}
 
 ChildSharedBitmapManager::~ChildSharedBitmapManager() {}
 
@@ -95,7 +114,7 @@
     return nullptr;
   cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
   std::unique_ptr<base::SharedMemory> memory =
-      ChildThreadImpl::AllocateSharedMemory(memory_size);
+      AllocateSharedMemory(memory_size);
   if (!memory || !memory->Map(memory_size))
     CollectMemoryUsageAndDie(size, memory_size);
 
@@ -105,7 +124,7 @@
   // remains available.
   memory->Close();
 
-  return base::MakeUnique<ChildSharedBitmap>(render_message_filter_ptr_,
+  return base::MakeUnique<ChildSharedBitmap>(shared_bitmap_manager_ptr_,
                                              std::move(memory), id);
 }
 
@@ -120,7 +139,7 @@
 ChildSharedBitmapManager::GetBitmapForSharedMemory(base::SharedMemory* mem) {
   cc::SharedBitmapId id = cc::SharedBitmap::GenerateId();
   NotifyAllocatedSharedBitmap(mem, cc::SharedBitmap::GenerateId());
-  return base::MakeUnique<ChildSharedBitmap>(render_message_filter_ptr_,
+  return base::MakeUnique<ChildSharedBitmap>(shared_bitmap_manager_ptr_,
                                              std::move(mem), id);
 }
 
@@ -139,8 +158,8 @@
   mojo::ScopedSharedBufferHandle buffer_handle = mojo::WrapSharedMemoryHandle(
       handle_to_send, memory->mapped_size(), true /* read_only */);
 
-  (*render_message_filter_ptr_)
-      ->AllocatedSharedBitmap(std::move(buffer_handle), id);
+  (*shared_bitmap_manager_ptr_)
+      ->DidAllocateSharedBitmap(std::move(buffer_handle), id);
 }
 
-}  // namespace content
+}  // namespace ui
diff --git a/content/child/child_shared_bitmap_manager.h b/services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h
similarity index 67%
rename from content/child/child_shared_bitmap_manager.h
rename to services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h
index dbf74ef..7e8eb3a9 100644
--- a/content/child/child_shared_bitmap_manager.h
+++ b/services/ui/public/cpp/bitmap/child_shared_bitmap_manager.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
-#define CONTENT_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
+#ifndef SERVICES_UI_PUBLIC_CPP_BITMAP_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
+#define SERVICES_UI_PUBLIC_CPP_BITMAP_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
 
 #include <stdint.h>
 
@@ -12,17 +12,18 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/memory/shared_memory.h"
+#include "cc/ipc/shared_bitmap_manager.mojom.h"
 #include "cc/resources/shared_bitmap_manager.h"
-#include "content/common/render_message_filter.mojom.h"
 #include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h"
 
-namespace content {
+namespace ui {
 
 class ChildSharedBitmapManager : public cc::SharedBitmapManager {
  public:
   explicit ChildSharedBitmapManager(
-      const scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>&
-          render_message_filter_ptr);
+      const scoped_refptr<
+          cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>&
+          shared_bitmap_manager_ptr);
   ~ChildSharedBitmapManager() override;
 
   // cc::SharedBitmapManager implementation.
@@ -39,12 +40,12 @@
   void NotifyAllocatedSharedBitmap(base::SharedMemory* memory,
                                    const cc::SharedBitmapId& id);
 
-  scoped_refptr<mojom::ThreadSafeRenderMessageFilterAssociatedPtr>
-      render_message_filter_ptr_;
+  scoped_refptr<cc::mojom::ThreadSafeSharedBitmapManagerAssociatedPtr>
+      shared_bitmap_manager_ptr_;
 
   DISALLOW_COPY_AND_ASSIGN(ChildSharedBitmapManager);
 };
 
-}  // namespace content
+}  // namespace ui
 
-#endif  // CONTENT_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
+#endif  // SERVICES_UI_PUBLIC_CPP_BITMAP_CHILD_CHILD_SHARED_BITMAP_MANAGER_H_
diff --git a/testing/buildbot/chromium.android.fyi.json b/testing/buildbot/chromium.android.fyi.json
index 0628bd8..6df5157 100644
--- a/testing/buildbot/chromium.android.fyi.json
+++ b/testing/buildbot/chromium.android.fyi.json
@@ -225,7 +225,6 @@
   "Lollipop Low-end Tester": {
     "gtest_tests": [
       {
-        "expiration": 14400,
         "override_compile_targets": [
           "android_webview_test_apk"
         ],
@@ -239,12 +238,12 @@
               "device_type": "sprout"
             }
           ],
+          "expiration": 14400,
           "shards": 2
         },
         "test": "android_webview_test_apk"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "android_webview_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -254,12 +253,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "android_webview_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "base_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -269,12 +268,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "base_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "blink_heap_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -284,12 +283,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "blink_heap_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "breakpad_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -299,12 +298,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "breakpad_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "capture_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -314,12 +313,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "capture_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "cc_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -329,12 +328,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "cc_unittests"
       },
       {
-        "expiration": 14400,
         "override_compile_targets": [
           "chrome_public_test_apk"
         ],
@@ -348,12 +347,12 @@
               "device_type": "sprout"
             }
           ],
+          "expiration": 14400,
           "shards": 6
         },
         "test": "chrome_public_test_apk"
       },
       {
-        "expiration": 14400,
         "override_compile_targets": [
           "chrome_sync_shell_test_apk"
         ],
@@ -366,12 +365,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "chrome_sync_shell_test_apk"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "components_browsertests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -381,12 +380,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "components_browsertests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "components_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -396,12 +395,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "components_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "content_browsertests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -412,12 +411,12 @@
               "device_type": "sprout"
             }
           ],
+          "expiration": 14400,
           "shards": 4
         },
         "test": "content_browsertests"
       },
       {
-        "expiration": 14400,
         "override_compile_targets": [
           "content_shell_test_apk"
         ],
@@ -430,12 +429,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "content_shell_test_apk"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "content_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -445,12 +444,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "content_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "device_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -460,12 +459,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "device_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "events_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -475,12 +474,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "events_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "gl_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -490,12 +489,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "gl_tests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "gl_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -505,12 +504,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "gl_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "gpu_ipc_service_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -520,12 +519,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "gpu_ipc_service_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "gpu_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -535,12 +534,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "gpu_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "ipc_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -550,12 +549,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "ipc_tests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "media_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -565,12 +564,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "media_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "net_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -580,12 +579,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "net_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "sandbox_linux_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -595,12 +594,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "sandbox_linux_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "sql_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -610,12 +609,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "sql_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "storage_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -625,12 +624,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "storage_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "ui_android_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -640,12 +639,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "ui_android_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "ui_base_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -655,12 +654,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "ui_base_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "ui_touch_selection_unittests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -670,12 +669,12 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "ui_touch_selection_unittests"
       },
       {
-        "expiration": 14400,
         "override_isolate_target": "unit_tests",
         "swarming": {
           "can_use_on_swarming_builders": true,
@@ -685,7 +684,8 @@
               "device_os": "LMY47W",
               "device_type": "sprout"
             }
-          ]
+          ],
+          "expiration": 14400
         },
         "test": "unit_tests"
       }
diff --git a/testing/variations/fieldtrial_testing_config.json b/testing/variations/fieldtrial_testing_config.json
index 3a92224c..81fb72a 100644
--- a/testing/variations/fieldtrial_testing_config.json
+++ b/testing/variations/fieldtrial_testing_config.json
@@ -1377,6 +1377,24 @@
             ]
         }
     ],
+    "NTPLaunchAfterInactivity": [
+        {
+            "platforms": [
+                "android"
+            ],
+            "experiments": [
+                {
+                    "name": "OneHourDelay",
+                    "params": {
+                        "delay_in_mins": "60"
+                    },
+                    "enable_features": [
+                        "NTPLaunchAfterInactivity"
+                    ]
+                }
+            ]
+        }
+    ],
     "NTPPersonalizedSectionRanking": [
         {
             "platforms": [
diff --git a/third_party/.gitignore b/third_party/.gitignore
index 176819f..64515737 100644
--- a/third_party/.gitignore
+++ b/third_party/.gitignore
@@ -32,6 +32,7 @@
 /cardboard-java/src
 /catapult
 /ced/src
+/checkstyle/*.jar
 /chromeos_login_manager
 /chromeos_text_input
 /chromite
diff --git a/third_party/WebKit/LayoutTests/ASANExpectations b/third_party/WebKit/LayoutTests/ASANExpectations
index 021eb38..b72d4ec 100644
--- a/third_party/WebKit/LayoutTests/ASANExpectations
+++ b/third_party/WebKit/LayoutTests/ASANExpectations
@@ -94,8 +94,6 @@
 # Reliably timeout on Linux ASAN bots.
 crbug.com/248938 [ Linux ] virtual/threaded/animations/transition-and-animation-2.html [ Skip ]
 
-crbug.com/446661 [Linux ] geolocation-api/success-clear-watch.html [ Timeout Pass ]
-
 crbug.com/464065 [ Linux ] media/track/css-cue-for-video-in-shadow.html [ Timeout ]
 crbug.com/464065 [ Linux ] media/track/css-cue-for-video-in-shadow-2.html [ Timeout ]
 crbug.com/525975 [ Linux ] http/tests/websocket/workers/close-before-open.html [ Timeout Pass ]
diff --git a/third_party/WebKit/LayoutTests/SlowTests b/third_party/WebKit/LayoutTests/SlowTests
index c5ea71d..4abba26 100644
--- a/third_party/WebKit/LayoutTests/SlowTests
+++ b/third_party/WebKit/LayoutTests/SlowTests
@@ -80,7 +80,6 @@
 crbug.com/24182 virtual/gpu/fast/canvas/canvas-strokePath-gradient-shadow.html [ Slow ]
 crbug.com/24182 virtual/gpu/fast/canvas/canvas-strokeRect-gradient-shadow.html [ Slow ]
 crbug.com/24182 virtual/gpu/fast/canvas/canvas-toDataURL-jpeg-crash.html [ Slow ]
-crbug.com/24182 virtual/gpu/fast/canvas/getPutImageDataPairTest.html [ Slow ]
 crbug.com/24182 fast/dom/timer-throttling-background-page-near-alignment-interval.html [ Slow ]
 crbug.com/24182 http/tests/perf/large-inlined-script.html [ Slow ]
 crbug.com/24182 virtual/mojo-loading/http/tests/perf/large-inlined-script.html [ Slow ]
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index da7b77511..0d513db 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -5,7 +5,6 @@
 # TODO(qyearsley): Remove these lines and uncomment other test expectation
 # lines related to CSS tests (crbug.com/706118).
 crbug.com/706118 external/wpt/css/CSS2 [ Skip ]
-crbug.com/706118 external/wpt/css/css-align-3 [ Skip ]
 crbug.com/706118 external/wpt/css/css-flexbox-1 [ Skip ]
 crbug.com/706118 external/wpt/css/css-scoping-1 [ Skip ]
 crbug.com/706118 external/wpt/css/css-shapes-1 [ Skip ]
@@ -15,7 +14,6 @@
 crbug.com/706118 external/wpt/css/css-writing-modes-3 [ Skip ]
 crbug.com/706118 external/wpt/css/cssom-1 [ Skip ]
 crbug.com/706118 external/wpt/css/cssom-view-1 [ Skip ]
-crbug.com/706118 external/wpt/css/selectors4 [ Skip ]
 
 # ====== Oilpan-only failures from here ======
 # Most of these actually cause the tests to report success, rather than
@@ -125,6 +123,9 @@
 
 crbug.com/671048 virtual/color_space/fast/canvas/color-space/display_linear-rgb.html [ Pass Failure Crash ]
 
+# Can be broken into smaller tests, probably.
+crbug.com/709009 virtual/gpu/fast/canvas/getPutImageDataPairTest.html [ Timeout Pass ]
+
 # ====== Paint team owned tests to here ======
 
 # ====== LayoutNG-only failures from here ======
@@ -1112,8 +1113,6 @@
 
 crbug.com/637255 [ Win10 ] media/video-transformed.html [ Pass Failure ]
 
-crbug.com/691045 media/autoplay-muted.html [ Pass Timeout ]
-
 # These tests pass but images do not match because tests are stricter than the spec.
 #crbug.com/492664 external/wpt/css/css-writing-modes-3/text-combine-upright-value-all-001.html [ Failure ]
 #crbug.com/492664 external/wpt/css/css-writing-modes-3/text-combine-upright-value-all-002.html [ Failure ]
@@ -1234,7 +1233,6 @@
 
 crbug.com/613887 http/tests/preload/meta-viewport-link-headers.html [ Failure Pass ]
 crbug.com/613887 virtual/mojo-loading/http/tests/preload/meta-viewport-link-headers.html [ Failure Pass ]
-crbug.com/564403 [ Win Debug ] http/tests/inspector/service-workers/service-worker-manager.html [ Failure Pass Timeout ]
 
 crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas.html [ Skip ]
 crbug.com/306222 fast/hidpi/image-srcset-relative-svg-canvas-2x.html [ Skip ]
@@ -1296,8 +1294,6 @@
 crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollbar-tickmarks-styled.html [ Failure ]
 crbug.com/417782 virtual/rootlayerscrolls/fast/scrolling/scrollbar-tickmarks-styled-after-onload.html [ Failure ]
 
-crbug.com/651343 fast/text/international/inline-plaintext-relayout-with-leading-neutrals.html [ Pass Failure ]
-
 crbug.com/574283 [ Mac ] fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html [ Skip ]
 crbug.com/574283 [ Mac ] virtual/scroll_customization/fast/scroll-behavior/smooth-scroll/ongoing-smooth-scroll-anchors.html [ Skip ]
 crbug.com/574283 [ Mac ] virtual/threaded/fast/scroll-behavior/first-scroll-runs-on-compositor.html [ Skip ]
@@ -1655,8 +1651,6 @@
 crbug.com/564109 [ Win7 ]  virtual/mojo-loading/http/tests/webfont/font-display.html [ Pass Failure ]
 crbug.com/564109 [ Win7 ]  virtual/mojo-loading/http/tests/webfont/font-display-intervention.html [ Pass Failure ]
 
-crbug.com/581468 http/tests/inspector/resource-tree/resource-tree-non-unique-url.html [ Pass Failure ]
-
 crbug.com/399951 http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ]
 crbug.com/399951 virtual/mojo-loading/http/tests/mime/javascript-mimetype-usecounters.html [ Pass Failure ]
 
@@ -2423,39 +2417,28 @@
 crbug.com/707359 [ Mac ] fast/css-grid-layout/grid-self-baseline-vertical-rl-05.html [ Failure ]
 
 # [css-align]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-001.html [ Failure ]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-002.html [ Failure ]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-003.html [ Failure ]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-004.html [ Failure ]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-005.html [ Failure ]
-#crbug.com/703584 external/wpt/css/css-align-3/content-distribution/place-content-shorthand-006.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-001.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-002.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-003.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-004.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-005.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-006.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-001.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-002.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-003.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-004.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-005.html [ Failure ]
-#crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-006.html [ Failure ]
+crbug.com/668639 external/wpt/css/css-align-3/default-alignment/place-items-shorthand-006.html [ Failure ]
+crbug.com/668639 external/wpt/css/css-align-3/self-alignment/place-self-shorthand-006.html [ Failure ]
+crbug.com/708121 external/wpt/css/css-align-3/distribution-values/space-evenly-001.html [ Failure ]
 
 # [selectors-4]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-001.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-002.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-003.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-004.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-005.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-006.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-shadow-001.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-shadow-002.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-shadow-003.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-shadow-004.html [ Failure ]
-#crbug.com/617371 external/wpt/css/selectors-4/focus-within-shadow-005.html [ Failure ]
-#crbug.com/576815 external/wpt/css/selectors-4/selectors-dir-selector-ltr-001.html [ Failure ]
-#crbug.com/576815 external/wpt/css/selectors-4/selectors-dir-selector-rtl-001.html [ Failure ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-001.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-002.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-003.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-004.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-005.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-006.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-007.html [ Failure ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-008.html [ Failure ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-009.html [ Failure ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-shadow-001.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-shadow-002.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-shadow-003.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-shadow-004.html [ Skip ]
+crbug.com/617371 external/wpt/css/selectors4/focus-within-shadow-005.html [ Skip ]
+crbug.com/706118 external/wpt/css/selectors4/hover-001-manual.html [ Skip ]
+crbug.com/576815 external/wpt/css/selectors4/selectors-dir-selector-ltr-001.html [ Failure ]
+crbug.com/576815 external/wpt/css/selectors4/selectors-dir-selector-rtl-001.html [ Failure ]
 crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-01a.html [ Failure ]
 crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-01b.html [ Failure ]
 crbug.com/576815 external/wpt/css/vendor-imports/mozilla/mozilla-central-reftests/selectors4/dir-style-02a.html [ Failure ]
@@ -2524,9 +2507,6 @@
 crbug.com/595993 external/wpt/service-workers/service-worker/fetch-header-visibility.https.html [ Failure ]
 crbug.com/595993 external/wpt/service-workers/service-worker/request-end-to-end.https.html [ Failure ]
 
-crbug.com/666628 http/tests/inspector-enabled/console-stack-overflow-source-url.html [ Pass Failure ]
-crbug.com/666628 virtual/mojo-loading/http/tests/inspector-enabled/console-stack-overflow-source-url.html [ Pass Failure ]
-
 crbug.com/619427 [ Mac Linux ] fast/overflow/overflow-height-float-not-removed-crash3.html [ Pass Failure ]
 
 crbug.com/667371 inspector/elements/styles-1/color-aware-property-value-edit.html [ Pass Failure ]
@@ -2592,7 +2572,6 @@
 crbug.com/664819 virtual/mojo-loading/http/tests/security/isolatedWorld/bypass-main-world-csp-for-xhr.html [ Pass Failure ]
 crbug.com/664819 virtual/mojo-loading/http/tests/security/isolatedWorld/bypass-main-world-csp-iframes.html [ Pass Failure ]
 crbug.com/664819 virtual/mojo-loading/http/tests/security/isolatedWorld/events.html [ Pass Failure ]
-crbug.com/664839 virtual/mojo-loading/http/tests/security/link-crossorigin-preload-no-cors.html [ Pass Failure ]
 
 # Possible duplicate of crbug.com/498539
 # crbug.com/664843 inspector/elements/styles-4/styles-update-from-js.html [ Pass Failure ]
@@ -2641,10 +2620,7 @@
 
 crbug.com/678487 http/tests/inspector/resource-tree/resource-tree-reload.html [ Failure Timeout Pass ]
 crbug.com/678487 virtual/mojo-loading/http/tests/inspector/resource-tree/resource-tree-reload.html [ Failure Timeout Pass ]
-crbug.com/678489 http/tests/inspector/tracing/timeline-script-parse.html [ Timeout Pass ]
 crbug.com/678489 virtual/mojo-loading/http/tests/inspector/tracing/timeline-script-parse.html [ Timeout Pass ]
-crbug.com/678490 http/tests/media/media-source/mediasource-seek-beyond-duration.html [ Failure Pass ]
-crbug.com/678490 virtual/mojo-loading/http/tests/media/media-source/mediasource-seek-beyond-duration.html [ Failure Pass ]
 crbug.com/678492 http/tests/misc/webtiming-ssl.php [ Failure Pass ]
 crbug.com/678492 virtual/mojo-loading/http/tests/misc/webtiming-ssl.php [ Failure Pass ]
 crbug.com/678493 http/tests/permissions/chromium/test-request-window.html [ Timeout Pass ]
@@ -2683,10 +2659,6 @@
 
 # Importing 'fetch' tests from WPT.
 crbug.com/705490 external/wpt/fetch/api/basic/error-after-response.html [ Timeout Pass ]
-crbug.com/705490 external/wpt/fetch/api/redirect/redirect-location-worker.html [ Timeout Pass ]
-crbug.com/705490 external/wpt/fetch/api/redirect/redirect-location.html [ Timeout Pass ]
-crbug.com/705490 external/wpt/fetch/api/redirect/redirect-origin-worker.html [ Timeout Pass ]
-crbug.com/705490 external/wpt/fetch/api/request/request-cache-default-conditional.html [ Timeout Pass ]
 
 # Non-deterministic output, as the failing subtests dump timestamps.
 crbug.com/705490 external/wpt/fetch/api/request/request-cache-reload.html [ Failure ]
@@ -2707,8 +2679,6 @@
 crbug.com/694338 [ Mac10.9 ] fast/forms/calendar-picker/calendar-picker-appearance-zoom125.html [ Failure ]
 crbug.com/694338 [ Mac10.9 ] fast/forms/calendar-picker/calendar-picker-appearance-zoom200.html [ Failure ]
 
-crbug.com/697087 external/wpt/service-workers/service-worker/registration-iframe.https.html [ Pass Timeout ]
-
 crbug.com/697342 http/tests/push_messaging/permission-state-granted-in-document.html [ Pass Failure ]
 crbug.com/697342 virtual/mojo-loading/http/tests/push_messaging/permission-state-granted-in-document.html [ Pass Failure ]
 
diff --git a/third_party/WebKit/LayoutTests/W3CImportExpectations b/third_party/WebKit/LayoutTests/W3CImportExpectations
index 390b11f..7686140e 100644
--- a/third_party/WebKit/LayoutTests/W3CImportExpectations
+++ b/third_party/WebKit/LayoutTests/W3CImportExpectations
@@ -289,7 +289,6 @@
 # external/wpt/innerText [ Pass ]
 external/wpt/js [ Skip ]
 external/wpt/lint [ Skip ]
-external/wpt/lint.whitelist [ Skip ]
 external/wpt/manifest [ Skip ]
 external/wpt/mathml [ Skip ]
 ## Owners: none; No tests in the directory.
diff --git a/third_party/WebKit/LayoutTests/bindings/blink-in-js-asan-crash-expected.txt b/third_party/WebKit/LayoutTests/bindings/blink-in-js-asan-crash-expected.txt
index 9fad658..c57c260 100644
--- a/third_party/WebKit/LayoutTests/bindings/blink-in-js-asan-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/bindings/blink-in-js-asan-crash-expected.txt
@@ -1 +1,2 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Test passes if it does not crash on ASan builds.  
diff --git a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-in-composited-layer-expected.txt b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-in-composited-layer-expected.txt
index 3255aa6..c90a95b4 100644
--- a/third_party/WebKit/LayoutTests/compositing/iframes/iframe-in-composited-layer-expected.txt
+++ b/third_party/WebKit/LayoutTests/compositing/iframes/iframe-in-composited-layer-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/editing/pasteboard/dragstart-contains-default-content-expected.txt b/third_party/WebKit/LayoutTests/editing/pasteboard/dragstart-contains-default-content-expected.txt
index d7f3e48..9c915e4 100644
--- a/third_party/WebKit/LayoutTests/editing/pasteboard/dragstart-contains-default-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/editing/pasteboard/dragstart-contains-default-content-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Simple test that the dragstart event contains the default data in the event.
 
 Select some text in this box and drag it.
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js
new file mode 100644
index 0000000..08535fa5
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js
@@ -0,0 +1,3 @@
+// Identical to simpleSourcedScript.js but with a different hash, thanks to
+// this comment!
+window.postMessage(document.currentScript.id, "*");
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js.headers b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js.headers
new file mode 100644
index 0000000..cb762eff
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/crossoriginScript.js.headers
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: *
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/externalScript.js b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/externalScript.js
new file mode 100644
index 0000000..2920b03
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/externalScript.js
@@ -0,0 +1 @@
+externalRan = true;
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html
new file mode 100644
index 0000000..2c888f4
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+    <title>External scripts with matching SRI hash should be allowed.</title>
+    <script src='/resources/testharness.js' nonce='dummy'></script>
+    <script src='/resources/testharnessreport.js' nonce='dummy'></script>
+
+    <!-- CSP served: script-src {{domains[www]}}:* 'nonce-dummy' 'sha256-wIc3KtqOuTFEu6t17sIBuOswgkV406VJvhSk79Gw6U0=' 'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c=' 'sha512-rYCVMxWV5nq8IsMo+UZNObWtEiWGok/vDN8BMoEQi41s0znSes6E1Q2aag3Lw3u2J1w2rqH7uF2ws6FpQhfSOA=' -->
+</head>
+
+<body>
+    <h1>External scripts with matching SRI hash should be allowed.</h1>
+    <div id='log'></div>
+
+    <script nonce='dummy'>
+        var port = "{{ports[http][0]}}";
+        if (location.protocol === "https:")
+          port = "{{ports[https][0]}}";
+        var crossorigin_base = location.protocol + "//{{domains[www]}}:" + port;
+
+        // Test name, src, integrity, expected to run.
+        var test_cases = [
+          [ 'matching integrity',
+            './simpleSourcedScript.js',
+            'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c=',
+            true ],
+          [ 'multiple matching integrity',
+            './simpleSourcedScript.js',
+            'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c= sha512-rYCVMxWV5nq8IsMo+UZNObWtEiWGok/vDN8BMoEQi41s0znSes6E1Q2aag3Lw3u2J1w2rqH7uF2ws6FpQhfSOA=',
+            true ],
+          [ 'no integrity',
+            './simpleSourcedScript.js',
+            '',
+            false ],
+          [ 'matching plus unsupported integrity',
+            './simpleSourcedScript.js',
+            'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c= sha999-xyz',
+            true ],
+          [ 'mismatched integrity',
+            './simpleSourcedScript.js',
+            'sha256-xyz',
+            false ],
+          [ 'multiple mismatched integrity',
+            './simpleSourcedScript.js',
+            'sha256-xyz sha256-zyx',
+            false ],
+          [ 'partially matching integrity',
+            './simpleSourcedScript.js',
+            'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c= sha256-xyz',
+            false ],
+          [ 'crossorigin no integrity but whitelisted host',
+            crossorigin_base + '/content-security-policy/script-src/crossoriginScript.js',
+            '',
+            true ],
+          [ 'crossorigin mismatched integrity but whitelisted host',
+            crossorigin_base + '/content-security-policy/script-src/crossoriginScript.js',
+            'sha256-kKJ5c48yxzaaSBupJSCmY50hkD8xbVgZgLHLtmnkeAo=',
+            true ],
+        ];
+
+        test(_ => {
+          for (item of test_cases) {
+            async_test(t => {
+              var s = document.createElement('script');
+              s.id = item[0].replace(' ', '-');
+              s.src = item[1];
+              s.integrity = item[2];
+              s.setAttribute('crossorigin', 'anonymous');
+
+              if (item[3]) {
+                s.onerror = t.unreached_func("Script should load! " + s.src);
+                window.addEventListener('message', t.step_func(e => {
+                  if (e.data == s.id)
+                    t.done();
+                }));
+              } else {
+                s.onerror = t.step_func_done();
+                window.addEventListener('message', t.step_func(e => {
+                  if (e.data == s.id)
+                    assert_unreached("Script should not execute!");
+                }));
+              }
+
+              document.body.appendChild(s);
+            }, item[0]);
+          }
+        }, "Load all the tests.");
+    </script>
+
+    <script nonce='dummy'>
+        var externalRan = false;
+    </script>
+    <script src='./externalScript.js'
+        integrity="sha256-wIc3KtqOuTFEu6t17sIBuOswgkV406VJvhSk79Gw6U0="></script>
+    <script nonce='dummy'>
+        test(function() {
+            assert_true(externalRan, 'External script ran.');
+        }, 'External script in a script tag with matching SRI hash should run.');
+    </script>
+
+</body>
+
+</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html.sub.headers b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html.sub.headers
new file mode 100644
index 0000000..25cd654
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/content-security-policy/script-src/script-src-sri_hash.sub.html.sub.headers
@@ -0,0 +1,5 @@
+Expires: Mon, 26 Jul 1997 05:00:00 GMT
+Cache-Control: no-store, no-cache, must-revalidate
+Cache-Control: post-check=0, pre-check=0, false
+Pragma: no-cache
+Content-Security-Policy: script-src {{domains[www]}}:* 'nonce-dummy' 'sha256-wIc3KtqOuTFEu6t17sIBuOswgkV406VJvhSk79Gw6U0=' 'sha256-L7/UQ9VWpyG7C9RDEC4ctS5hI3Zcw+ta+haPGlByG9c=' 'sha512-rYCVMxWV5nq8IsMo+UZNObWtEiWGok/vDN8BMoEQi41s0znSes6E1Q2aag3Lw3u2J1w2rqH7uF2ws6FpQhfSOA='
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https-expected.txt b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https-expected.txt
new file mode 100644
index 0000000..8f72ff81
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https-expected.txt
@@ -0,0 +1,9 @@
+This is a testharness.js-based test.
+PASS Test install event waitUntil fulfilled 
+PASS Test ExtendableEvent multiple waitUntil fulfilled. 
+FAIL Test ExtendableEvent waitUntil reject precedence. assert_unreached: unexpected rejection: assert_true: The "redundant" state was entered after the first "extend lifetime promise" resolved. expected true got undefined Reached unreachable code
+PASS Test activate event waitUntil fulfilled 
+PASS Test install event waitUntil rejected 
+PASS Test activate event waitUntil rejected. 
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https.html b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https.html
index b2029ae..8e790d0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https.html
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/extendable-event-waituntil.https.html
@@ -16,14 +16,17 @@
 // |obj.synced| to true once ack'd.
 function syncWorker(test, worker, obj) {
   var channel = new MessageChannel();
-  channel.port1.onmessage = test.step_func(function(e) {
-      var message = e.data;
-      assert_equals(message, 'SYNC',
-                    'Should receive sync message from worker.');
-      obj.synced = true;
-      channel.port1.postMessage('ACK');
-    });
   worker.postMessage({port: channel.port2}, [channel.port2]);
+  return new Promise(function(resolve) {
+      channel.port1.onmessage = test.step_func(function(e) {
+          var message = e.data;
+          assert_equals(message, 'SYNC',
+                        'Should receive sync message from worker.');
+          obj.synced = true;
+          channel.port1.postMessage('ACK');
+          resolve();
+        });
+    });
 }
 
 async_test(function(t) {
@@ -68,13 +71,28 @@
 async_test(function(t) {
     var scope = 'resources/install-reject-precedence';
     var onRegister = function(worker) {
-        var obj = {};
+        var obj1 = {};
+        var obj2 = {};
         wait_for_state(t, worker, 'redundant')
           .then(function() {
+              assert_true(
+                obj1.synced,
+                'The "redundant" state was entered after the first "extend ' +
+                  'lifetime promise" resolved.'
+              );
+              assert_true(
+                obj2.synced,
+                'The "redundant" state was entered after the third "extend ' +
+                  'lifetime promise" resolved.'
+              );
               service_worker_unregister_and_done(t, scope);
             })
           .catch(unreached_rejection(t));
-        syncWorker(t, worker, obj);
+
+        syncWorker(t, worker, obj1)
+          .then(function() {
+              syncWorker(t, worker, obj2);
+            });
       };
     runTest(t, scope, onRegister);
   }, 'Test ExtendableEvent waitUntil reject precedence.');
diff --git a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/extendable-event-waituntil.js b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/extendable-event-waituntil.js
index 48fdf1b9..20a9eb0 100644
--- a/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/extendable-event-waituntil.js
+++ b/third_party/WebKit/LayoutTests/external/wpt/service-workers/service-worker/resources/extendable-event-waituntil.js
@@ -57,8 +57,20 @@
       e.waitUntil(fulfillPromise());
       break;
     case 'install-reject-precedence':
+      // Three "extend lifetime promises" are needed to verify that the user
+      // agent waits for all promises to settle even in the event of rejection.
+      // The first promise is fulfilled on demand by the client, the second is
+      // immediately scheduled for rejection, and the third is fulfilled on
+      // demand by the client (but only after the first promise has been
+      // fulfilled).
+      //
+      // User agents which simply expose `Promise.all` semantics in this case
+      // (by entering the "redundant state" following the rejection of the
+      // second promise but prior to the fulfillment of the third) can be
+      // identified from the client context.
       e.waitUntil(fulfillPromise());
       e.waitUntil(rejectPromise());
+      e.waitUntil(fulfillPromise());
       break;
   }
 };
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-canvas-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-canvas-expected.txt
index 292b39a..e534171 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-canvas-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-canvas-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Test Results
 solid on solid	alpha on solid	solid on alpha	alpha on alpha
 source-over				
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-image-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-image-expected.txt
index 6a84b9e..db4cbb3 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-image-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-composite-image-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Test Results
 solid on solid	alpha on solid	solid on alpha	alpha on alpha
 source-over				
diff --git a/third_party/WebKit/LayoutTests/fast/css/counters/counter-traverse-table-cell-expected.txt b/third_party/WebKit/LayoutTests/fast/css/counters/counter-traverse-table-cell-expected.txt
index 2186b92..4b1e555 100644
--- a/third_party/WebKit/LayoutTests/fast/css/counters/counter-traverse-table-cell-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/css/counters/counter-traverse-table-cell-expected.txt
@@ -1 +1,2 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 This tests that we don't crash when using the CSS counters feature.  
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-body-quirk-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-body-quirk-expected.txt
index 89d26cf..568beb4 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-body-quirk-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-body-quirk-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 static: body: (0, 0) child: (27, 27)
 relative: body: (0, 0) child: (17, 17)
 fixed: body: (0, 0) child: (17, 17)
diff --git a/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-html-expected.txt b/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-html-expected.txt
index 5e3da62..07884717 100644
--- a/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-html-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/dom/Element/offsetLeft-offsetTop-html-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 control: (0, 0)
 static: (10, 10)
 absolute: (20, 10)
diff --git a/third_party/WebKit/LayoutTests/fast/events/constructors/track-event-constructor-expected.txt b/third_party/WebKit/LayoutTests/fast/events/constructors/track-event-constructor-expected.txt
index 74a01c9a..2a0d8d3a6 100644
--- a/third_party/WebKit/LayoutTests/fast/events/constructors/track-event-constructor-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/constructors/track-event-constructor-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 This tests the constructor for the TrackEvent DOM class.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt
index bc4f50d..4683904 100644
--- a/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/drag-and-drop-autoscroll-inner-frame-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Check autoscroll within an inner frame by drag-and-drop
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
diff --git a/third_party/WebKit/LayoutTests/fast/events/resize-subframe-expected.txt b/third_party/WebKit/LayoutTests/fast/events/resize-subframe-expected.txt
index 9c70321..fc99a4e3 100644
--- a/third_party/WebKit/LayoutTests/fast/events/resize-subframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/resize-subframe-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 ALERT: PASS
 
diff --git a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt
index 79cf179f..5c13fc0 100644
--- a/third_party/WebKit/LayoutTests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/events/touch/gesture/long-press-focuses-frame-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 PASS successfullyParsed is true
 
 TEST COMPLETE
diff --git a/third_party/WebKit/LayoutTests/fast/files/null-origin-string-expected.txt b/third_party/WebKit/LayoutTests/fast/files/null-origin-string-expected.txt
index a117861..415ccd9 100644
--- a/third_party/WebKit/LayoutTests/fast/files/null-origin-string-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/files/null-origin-string-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: line 30: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE MESSAGE: line 1: Started reading...
 PASS if no crash.
diff --git a/third_party/WebKit/LayoutTests/fast/frames/content-opacity-1-expected.txt b/third_party/WebKit/LayoutTests/fast/frames/content-opacity-1-expected.txt
index 270de6a0..fb16425f 100644
--- a/third_party/WebKit/LayoutTests/fast/frames/content-opacity-1-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/frames/content-opacity-1-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/fast/frames/negative-remaining-length-crash-expected.txt b/third_party/WebKit/LayoutTests/fast/frames/negative-remaining-length-crash-expected.txt
index 8b13789..6e41aad 100644
--- a/third_party/WebKit/LayoutTests/fast/frames/negative-remaining-length-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/frames/negative-remaining-length-crash-expected.txt
@@ -1 +1,2 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 
diff --git a/third_party/WebKit/LayoutTests/fast/loader/simultaneous-reloads-assert-expected.txt b/third_party/WebKit/LayoutTests/fast/loader/simultaneous-reloads-assert-expected.txt
index d911ebb7..181979e 100644
--- a/third_party/WebKit/LayoutTests/fast/loader/simultaneous-reloads-assert-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/loader/simultaneous-reloads-assert-expected.txt
@@ -1,3 +1,4 @@
 ALERT: If you do not hit an assertion failure when running this test with a debug build and you get a SUCCESS message, then you pass the test.
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 ALERT: SUCCESS
 
diff --git a/third_party/WebKit/LayoutTests/fast/loader/stateobjects/pushstate-in-data-url-denied-expected.txt b/third_party/WebKit/LayoutTests/fast/loader/stateobjects/pushstate-in-data-url-denied-expected.txt
index d8a96f92..14ac789 100644
--- a/third_party/WebKit/LayoutTests/fast/loader/stateobjects/pushstate-in-data-url-denied-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/loader/stateobjects/pushstate-in-data-url-denied-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 ALERT: PASS: data URLs cannot be manipulated via pushState.
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-expected.txt
index 25061c5..1e28fae0 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 This is link_1.
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-zero-size-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-zero-size-expected.txt
index 0019f62..195ec453 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-zero-size-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-hidden-iframe-zero-size-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 This is link_1.
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-nested-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-nested-expected.txt
index 6f67f45..f58c98af 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-nested-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-nested-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Link
 
 Link
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-focusable-content-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
index 3348dcd1..0c77c6e 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-focusable-content-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 a
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
index cf213f6..bfa9010 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-no-scrollable-content-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 a
 
 
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-recursive-offset-parent-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-recursive-offset-parent-expected.txt
index 3f78909b..122c3a9f 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-recursive-offset-parent-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-recursive-offset-parent-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Link
 
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
index c296742..09d33bb 100644
--- a/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
+++ b/third_party/WebKit/LayoutTests/fast/spatial-navigation/snav-iframe-with-offscreen-focusable-element-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 
 
 PASS successfullyParsed is true
diff --git a/third_party/WebKit/LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt b/third_party/WebKit/LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt
index c6343a5..1689b406 100644
--- a/third_party/WebKit/LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt
+++ b/third_party/WebKit/LayoutTests/fullscreen/full-screen-iframe-without-allow-attribute-allowed-from-parent-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Test entering full screen security restrictions. An iframe without an allow attribute is still permitted to fullscreen if the request comes from the containing document.
 
 To test manually, press any key - the page should enter full screen mode.
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/download.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/download.html
index 7529945..65ea094a 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/download.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/download.html
@@ -18,7 +18,7 @@
 
     function responseReceived(requestId, time, resourceType, response)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/download\.zzz/.exec(request.url())) {
             InspectorTest.addResult("Received response for download.zzz");
             InspectorTest.addResult("SUCCESS");
@@ -28,14 +28,14 @@
 
     function loadingFinished(requestId, finishTime)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/download\.zzz/.exec(request.url()))
             InspectorTest.completeTest();
     }
 
     function loadingFailed(requestId, time, localizedDescription, canceled)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/download\.zzz/.exec(request.url()))
             InspectorTest.completeTest();
     }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-datareceived.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-datareceived.html
index 3e9c9ab0..42a0d45 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-datareceived.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-datareceived.html
@@ -21,7 +21,7 @@
     var encodedBytesReceived = 0;
     function responseReceived(requestId, frameId, loaderId, time, resourceType, response)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/resource\.php/.exec(request.url())) {
             InspectorTest.addResult("Received response.");
             encodedBytesReceived += response.encodedDataLength;
@@ -30,7 +30,7 @@
 
     function loadingFinished(requestId, finishTime, encodedDataLength)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/resource\.php/.exec(request.url())) {
             InspectorTest.assertEquals(encodedBytesReceived, encodedDataLength, "Data length mismatch");
             InspectorTest.addResult("SUCCESS");
@@ -40,7 +40,7 @@
 
     function loadingFailed(requestId, time, localizedDescription, canceled)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/resource\.php/.exec(request.url())) {
             InspectorTest.addResult("Loading failed!");
             InspectorTest.completeTest();
@@ -50,7 +50,7 @@
     function dataReceived(requestId, time, dataLength, encodedDataLength)
     {
         InspectorTest.addSniffer(SDK.NetworkDispatcher.prototype, "dataReceived", dataReceived);
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/resource\.php/.exec(request.url()))
             encodedBytesReceived += encodedDataLength;
     }
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xhr-data-received-async-response-type-blob.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xhr-data-received-async-response-type-blob.html
index d49d86da..4db5357 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xhr-data-received-async-response-type-blob.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xhr-data-received-async-response-type-blob.html
@@ -13,7 +13,7 @@
 
     function dataReceived(requestId, time, dataLength, encodedDataLength)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/resource\.php/.exec(request.url())) {
             InspectorTest.addResult("Received data for resource.php");
             InspectorTest.addResult("SUCCESS");
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xsl-content.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xsl-content.html
index a75e785..4181f7b 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xsl-content.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/network-xsl-content.html
@@ -21,7 +21,7 @@
 
     function loadingFinished(requestId)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         request.requestContent().then(contentReceived.bind(this, request));
     }
     function contentReceived(request, content)
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/request-name-path.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/request-name-path.html
index c4ae8ed9..8048eb4 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/request-name-path.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/request-name-path.html
@@ -15,7 +15,7 @@
         if (targetUrl)
             mainTarget.setInspectedURL(targetUrl);
         InspectorTest.addResult("Dumping request name and path for url: " + url);
-        var request = new SDK.NetworkRequest(mainTarget, 0, url);
+        var request = new SDK.NetworkRequest(InspectorTest.networkManager, 0, url);
         InspectorTest.addResult("    name = " + request.name());
         InspectorTest.addResult("    path = " + request.path());
         InspectorTest.addResult("    targetUrl = " + (targetUrl ? targetUrl : currentTargetURL));
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/waterfall-images.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/waterfall-images.html
index eb029afc..15432bce 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/waterfall-images.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/waterfall-images.html
@@ -237,7 +237,7 @@
   }
 
   function makeRequest(requestData) {
-    var request = new SDK.NetworkRequest(SDK.targetManager.mainTarget(), (++requestIds).toString(), requestData.url);
+    var request = new SDK.NetworkRequest(InspectorTest.networkManager, (++requestIds).toString(), requestData.url);
     request.setResourceType(requestData.type);
     request.setIssueTime(requestData.issueTime, requestData.wallIssueTime);
     request.timing = requestData.timing;
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
index 033ab7a..b2eadbcb 100644
--- a/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector/network/x-frame-options-deny.html
@@ -18,7 +18,7 @@
 
     function responseReceived(requestId, time, resourceType, response)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/x-frame-options-deny\.cgi/.exec(request.url())) {
             InspectorTest.addResult("Received response for x-frame-options-deny.cgi");
             InspectorTest.addResult("SUCCESS");
@@ -28,14 +28,14 @@
 
     function loadingFinished(requestId, finishTime)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/x-frame-options-deny\.cgi/.exec(request.url()))
             InspectorTest.completeTest();
     }
 
     function loadingFailed(requestId, time, localizedDescription, canceled)
     {
-        var request = InspectorTest.networkLog.requestForId(InspectorTest.mainTarget, requestId);
+        var request = InspectorTest.networkLog.requestForId(InspectorTest.networkManager, requestId);
         if (/x-frame-options-deny\.cgi/.exec(request.url())) {
             InspectorTest.addResult("TODO(mkwst): This started failing when we moved XFO to the browser.");
             InspectorTest.completeTest();
diff --git a/third_party/WebKit/LayoutTests/http/tests/local/absolute-url-strip-whitespace-expected.txt b/third_party/WebKit/LayoutTests/http/tests/local/absolute-url-strip-whitespace-expected.txt
index 7ef22e9..dfd51a52 100644
--- a/third_party/WebKit/LayoutTests/http/tests/local/absolute-url-strip-whitespace-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/local/absolute-url-strip-whitespace-expected.txt
@@ -1 +1,2 @@
-PASS
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
+FAIL, script did not run.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute-expected.txt
new file mode 100644
index 0000000..dd27ac26
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute-expected.txt
@@ -0,0 +1,15 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
+This is a testharness.js-based test.
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=1&#10;b"> 
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=2&#10;b&lt;c"> 
+PASS       <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=3        b&lt;c      ">     
+FAIL <img id="dangling" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEwAAABnAQMAAACQMjadAAAAA1BMVEX///+nxBvIAAAAEUlEQVQ4y2MYBaNgFIwCegAABG0AAd5G4RkAAAAASUVORK5CYII="> assert_equals: Height expected 0 but got 103
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?4&img=&lt;b"> 
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?5&data=&amp;#10;b"> 
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?6&img=&amp;lt;b"> 
+PASS <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?7&img=&amp;#10;b&amp;lt;c"> 
+PASS       <img id="dangling" src="        http://127.0.0.1:8000/security/resources/abe.png?8      ">      <input type=hidden name=csrf value=sekrit>     
+PASS       <img id="dangling" src="      http://127.0.0.1:8000/security/resources/abe.png?9&img=&amp;lt;      ">      <input type=hidden name=csrf value=sekrit>     
+PASS       <img id="dangling" src="      http://127.0.0.1:8000/security/resources/abe.png?10&img=&amp;#10;      ">      <input type=hidden name=csrf value=sekrit>     
+Harness: the test ran to completion.
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html
index 7c3639e..3c03d51 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html
+++ b/third_party/WebKit/LayoutTests/http/tests/security/dangling-markup/src-attribute.html
@@ -16,12 +16,14 @@
   var abeSizedPngWithNewline = abeSizedPng.replace("i", "i\n");
 
   var should_block = [
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${rawNewline}b${rawBrace}c">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=1${rawNewline}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=2${rawNewline}b${rawBrace}c">`,
     `
-      <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a
+      <img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=3
         b${rawBrace}c
       ">
     `,
+    `<img id="dangling" src="${abeSizedPngWithNewline}">`,
   ];
 
   should_block.forEach(markup => {
@@ -32,35 +34,30 @@
   });
 
   var should_load = [
-
-    // `data:` and `javascript:` URLs don't check the content:
-    `<img id="dangling" src="${abeSizedPngWithNewline}">`,
-
-    // Just one or the other isn't enough:
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=a${rawNewline}b">`,
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${rawBrace}b">`,
+    // Brace alone doesn't block:
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?4&img=${rawBrace}b">`,
 
     // Entity-escaped characters don't trigger blocking:
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?data=a${escapedNewline}b">`,
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${escapedBrace}b">`,
-    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?img=a${escapedNewline}b${escapedBrace}c">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?5&data=${escapedNewline}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?6&img=${escapedBrace}b">`,
+    `<img id="dangling" src="http://127.0.0.1:8000/security/resources/abe.png?7&img=${escapedNewline}b${escapedBrace}c">`,
 
     // Leading and trailing whitespace is stripped:
     `
       <img id="dangling" src="
-        http://127.0.0.1:8000/security/resources/abe.png
+        http://127.0.0.1:8000/security/resources/abe.png?8
       ">
       <input type=hidden name=csrf value=sekrit>
     `,
     `
       <img id="dangling" src="
-      http://127.0.0.1:8000/security/resources/abe.png?img=${escapedBrace}
+      http://127.0.0.1:8000/security/resources/abe.png?9&img=${escapedBrace}
       ">
       <input type=hidden name=csrf value=sekrit>
     `,
     `
       <img id="dangling" src="
-      http://127.0.0.1:8000/security/resources/abe.png?img=${escapedNewline}
+      http://127.0.0.1:8000/security/resources/abe.png?10&img=${escapedNewline}
       ">
       <input type=hidden name=csrf value=sekrit>
     `,
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/document-all-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/document-all-expected.txt
index ca9071d..44c56277 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/document-all-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/document-all-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 ALERT: true
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/no-indexeddb-from-sandbox-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/no-indexeddb-from-sandbox-expected.txt
index e391647..3fb9246 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/no-indexeddb-from-sandbox-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/no-indexeddb-from-sandbox-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE MESSAGE: line 1: PASS: indexedDB.open() threw a SECURITY_ERR!
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-expected.txt
index 3df45730..35ec840 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE ERROR: line 1: Blocked opening 'about:blank' in a new window because the request was made in a sandboxed frame whose 'allow-popups' permission is not set.
 CONSOLE MESSAGE: line 1: PASS
 To run this test outside of DumpRenderTree, please disable your popup blocker!
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-top-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-top-expected.txt
index 007894b..b63c4d7f 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-top-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/no-popup-from-sandbox-top-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE ERROR: line 1: Unsafe JavaScript attempt to initiate navigation for frame with URL 'http://127.0.0.1:8000/security/no-popup-from-sandbox-top.html' from frame with URL 'data:text/html,       <script>       var win = window.open('about:blank', '_top');       console.log(win ? 'FAIL' : 'PASS');       </script>'. The frame attempting navigation of the top-level window is sandboxed, but the flag of 'allow-top-navigation' or 'allow-top-navigation-by-user-activation' is not set.
 
 CONSOLE MESSAGE: line 1: PASS
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-can-navigate-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-can-navigate-expected.txt
index c1b12c5..9d9f590 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-can-navigate-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-can-navigate-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 To run this test outside of DumpRenderTree, please disable your popup blocker!
 
 This test passes if it doesn't hang.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-control-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-control-expected.txt
index c58943b..43a1631 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-control-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-control-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE MESSAGE: line 1: /PASS/
 To run this test outside of DumpRenderTree, please disable your popup blocker!
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-expected.txt
index 89b29a71..7e64c99c 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-is-sandboxed-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE ERROR: line 1: Blocked form submission to 'javascript:alert(/FAIL/)' because the form's frame is sandboxed and the 'allow-forms' permission is not set.
 To run this test outside of DumpRenderTree, please disable your popup blocker!
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-when-allowed-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-when-allowed-expected.txt
index 7f3be17..da508271 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-when-allowed-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/popup-allowed-by-sandbox-when-allowed-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE MESSAGE: line 1: PASS
 To run this test outside of DumpRenderTree, please disable your popup blocker!
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/sandboxed-opener-can-close-window-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/sandboxed-opener-can-close-window-expected.txt
index 2d8d1cf..26ecb629 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/sandboxed-opener-can-close-window-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/sandboxed-opener-can-close-window-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 To run this test outside of DumpRenderTree, please disable your popup blocker!
 
 This test passes if it doesn't hang.
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/window-named-proto-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/window-named-proto-expected.txt
index 415dc9e..2d0ed3f8 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/window-named-proto-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/window-named-proto-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE ERROR: line 2: Uncaught TypeError: Cannot read property 'innerHTML' of null
 
diff --git a/third_party/WebKit/LayoutTests/http/tests/security/window-named-valueOf-expected.txt b/third_party/WebKit/LayoutTests/http/tests/security/window-named-valueOf-expected.txt
index 61813406..b7bbfad 100644
--- a/third_party/WebKit/LayoutTests/http/tests/security/window-named-valueOf-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/security/window-named-valueOf-expected.txt
@@ -1 +1,2 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 This passes if it doesn't alert the contents of innocent-victim.  
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-async-waituntil.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-async-waituntil.html
deleted file mode 100644
index 5a7b1c3..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-async-waituntil.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="resources/test-helpers.js"></script>
-<script>
-promise_test(function(t) {
-    var script = 'resources/extendable-event-async-waituntil.js';
-    var scope = 'resources/async-waituntil';
-    var worker;
-
-    return service_worker_unregister_and_register(t, script, scope)
-      .then(function(registration) {
-          worker = registration.installing;
-          return wait_for_state(t, worker, 'activated');
-        })
-      .then(function() {
-          var channel = new MessageChannel();
-          var saw_message = new Promise(function(resolve) {
-              channel.port1.onmessage = function(e) { resolve(e.data); }
-            });
-          worker.postMessage({port: channel.port2}, [channel.port2]);
-          return saw_message;
-        })
-      .then(function(message) {
-          assert_equals(message, 'PASS');
-          return service_worker_unregister_and_done(t, scope);
-        })
-  }, 'Calling waitUntil asynchronously throws an exception');
-</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-waituntil.html b/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-waituntil.html
deleted file mode 100644
index 4a5d7dd..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/extendable-event-waituntil.html
+++ /dev/null
@@ -1,125 +0,0 @@
-<!DOCTYPE html>
-<title>ExtendableEvent: waitUntil</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<script src="resources/test-helpers.js"></script>
-<script>
-function runTest(test, scope, onRegister) {
-  var script = 'resources/extendable-event-waituntil.js?' + scope;
-  service_worker_unregister_and_register(test, script, scope)
-    .then(function(registration) {
-        onRegister(registration.installing);
-      });
-}
-
-// Sends a SYN to the worker and asynchronously listens for an ACK; sets
-// |obj.synced| to true once ack'd.
-function syncWorker(test, worker, obj) {
-  var channel = new MessageChannel();
-  channel.port1.onmessage = test.step_func(function(e) {
-      var message = e.data;
-      assert_equals(message, 'SYNC',
-                    'Should receive sync message from worker.');
-      obj.synced = true;
-      channel.port1.postMessage('ACK');
-    });
-  worker.postMessage({port: channel.port2}, [channel.port2]);
-}
-
-async_test(function(t) {
-    // Passing scope as the test switch for worker script.
-    var scope = 'resources/install-fulfilled';
-    var onRegister = function(worker) {
-        var obj = {};
-        wait_for_state(t, worker, 'installed')
-          .then(function() {
-              assert_true(
-                obj.synced,
-                'state should be "installed" after the waitUntil promise ' +
-                    'for "oninstall" is fulfilled.');
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-        syncWorker(t, worker, obj);
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test install event waitUntil fulfilled');
-
-async_test(function(t) {
-    var scope = 'resources/install-multiple-fulfilled';
-    var onRegister = function(worker) {
-        var obj1 = {};
-        var obj2 = {};
-        wait_for_state(t, worker, 'installed')
-          .then(function() {
-              assert_true(
-                obj1.synced && obj2.synced,
-                'state should be "installed" after all waitUntil promises ' +
-                    'for "oninstall" are fulfilled.');
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-        syncWorker(t, worker, obj1);
-        syncWorker(t, worker, obj2);
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test ExtendableEvent multiple waitUntil fulfilled.');
-
-async_test(function(t) {
-    var scope = 'resources/install-reject-precedence';
-    var onRegister = function(worker) {
-        wait_for_state(t, worker, 'redundant')
-          .then(function() {
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test ExtendableEvent waitUntil reject precedence.');
-
-async_test(function(t) {
-    var scope = 'resources/activate-fulfilled';
-    var onRegister = function(worker) {
-        var obj = {};
-        wait_for_state(t, worker, 'activating')
-          .then(function() {
-              syncWorker(t, worker, obj);
-              return wait_for_state(t, worker, 'activated');
-            })
-          .then(function() {
-              assert_true(
-                obj.synced,
-                'state should be "activated" after the waitUntil promise ' +
-                    'for "onactivate" is fulfilled.');
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test activate event waitUntil fulfilled');
-
-async_test(function(t) {
-    var scope = 'resources/install-rejected';
-    var onRegister = function(worker) {
-        wait_for_state(t, worker, 'redundant')
-          .then(function() {
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test install event waitUntil rejected');
-
-async_test(function(t) {
-    var scope = 'resources/activate-rejected';
-    var onRegister = function(worker) {
-        wait_for_state(t, worker, 'activated')
-          .then(function() {
-              service_worker_unregister_and_done(t, scope);
-            })
-          .catch(unreached_rejection(t));
-      };
-    runTest(t, scope, onRegister);
-  }, 'Test activate event waitUntil rejected.');
-
-</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/serviceworker/resources/extendable-event-waituntil.js b/third_party/WebKit/LayoutTests/http/tests/serviceworker/resources/extendable-event-waituntil.js
deleted file mode 100644
index 48fdf1b9..0000000
--- a/third_party/WebKit/LayoutTests/http/tests/serviceworker/resources/extendable-event-waituntil.js
+++ /dev/null
@@ -1,75 +0,0 @@
-var pendingPorts = [];
-var portResolves = [];
-
-onmessage = function(e) {
-  var message = e.data;
-  if ('port' in message) {
-    var resolve = self.portResolves.shift();
-    if (resolve)
-      resolve(message.port);
-    else
-      self.pendingPorts.push(message.port);
-  }
-};
-
-function fulfillPromise() {
-  return new Promise(function(resolve) {
-      // Make sure the oninstall/onactivate callback returns first.
-      Promise.resolve().then(function() {
-          var port = self.pendingPorts.shift();
-          if (port)
-            resolve(port);
-          else
-            self.portResolves.push(resolve);
-        });
-    }).then(function(port) {
-        port.postMessage('SYNC');
-        return new Promise(function(resolve) {
-            port.onmessage = function(e) {
-              if (e.data == 'ACK')
-                resolve();
-            };
-          });
-      });
-}
-
-function rejectPromise() {
-  return new Promise(function(resolve, reject) {
-      // Make sure the oninstall/onactivate callback returns first.
-      Promise.resolve().then(reject);
-    });
-}
-
-function stripScopeName(url) {
-  return url.split('/').slice(-1)[0];
-}
-
-oninstall = function(e) {
-  switch (stripScopeName(self.location.href)) {
-    case 'install-fulfilled':
-      e.waitUntil(fulfillPromise());
-      break;
-    case 'install-rejected':
-      e.waitUntil(rejectPromise());
-      break;
-    case 'install-multiple-fulfilled':
-      e.waitUntil(fulfillPromise());
-      e.waitUntil(fulfillPromise());
-      break;
-    case 'install-reject-precedence':
-      e.waitUntil(fulfillPromise());
-      e.waitUntil(rejectPromise());
-      break;
-  }
-};
-
-onactivate = function(e) {
-  switch (stripScopeName(self.location.href)) {
-    case 'activate-fulfilled':
-      e.waitUntil(fulfillPromise());
-      break;
-    case 'activate-rejected':
-      e.waitUntil(rejectPromise());
-      break;
-  }
-};
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.html b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.html
new file mode 100644
index 0000000..b6b1d4fb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script src="../../../resources/testharness.js"></script>
+<script src="../../../resources/testharnessreport.js"></script>
+<script src="wasm_response_apis.js"></script>
+<script>
+  promise_test(TestStreamedCompile, "test streamed compile");
+  promise_test(TestShortFormStreamedCompile, "test streamed compile with promise parameter");
+  promise_test(NegativeTestStreamedCompilePromise, "promise must produce a Response");
+  promise_test(BlankResponse, "blank response");
+  promise_test(FromArrayBuffer, "from array buffer");
+  promise_test(FromInvalidArrayBuffer, "from an invalid array buffer");
+</script>
diff --git a/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.js b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.js
new file mode 100644
index 0000000..dc2ebcb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/wasm/wasm_response_apis.js
@@ -0,0 +1,48 @@
+// Copyright 2017 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.
+
+
+function TestStreamedCompile() {
+  return fetch('incrementer.wasm')
+    .then(WebAssembly.compile)
+    .then(m => new WebAssembly.Instance(m))
+    .then(i => assert_equals(5, i.exports.increment(4)));
+}
+
+function TestShortFormStreamedCompile() {
+  return WebAssembly.compile(fetch('incrementer.wasm'))
+    .then(m => new WebAssembly.Instance(m))
+    .then(i => assert_equals(5, i.exports.increment(4)));
+}
+
+function NegativeTestStreamedCompilePromise() {
+  return WebAssembly.compile(new Promise((resolve, reject)=>{resolve(5);}))
+    .then(assert_unreached,
+          e => assert_true(e instanceof TypeError));
+}
+
+function BlankResponse() {
+  return WebAssembly.compile(new Response())
+    .then(assert_unreached,
+          e => assert_true(e instanceof TypeError));
+}
+
+function FromArrayBuffer() {
+  return fetch('incrementer.wasm')
+    .then(r => r.arrayBuffer())
+    .then(arr => new Response(arr))
+    .then(WebAssembly.compile)
+    .then(m => new WebAssembly.Instance(m))
+    .then(i => assert_equals(6, i.exports.increment(5)));
+}
+
+function FromInvalidArrayBuffer() {
+  var arr = new ArrayBuffer(10);
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < view.length; ++i) view[i] = i;
+
+  return WebAssembly.compile(new Response(arr))
+    .then(assert_unreached,
+          e => assert_true(e instanceof Error));
+}
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-access-control-origin-header-data-url-expected.txt b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-access-control-origin-header-data-url-expected.txt
index 4f7bafa8..f2353ff 100644
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-access-control-origin-header-data-url-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/access-control-basic-allow-access-control-origin-header-data-url-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 CONSOLE WARNING: line 1: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
 PASS: Cross-domain access allowed.
 HTTP_ORIGIN: null
diff --git a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/newline-in-request-uri-expected.txt b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/newline-in-request-uri-expected.txt
index 87f0768..b88c102 100644
--- a/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/newline-in-request-uri-expected.txt
+++ b/third_party/WebKit/LayoutTests/http/tests/xmlhttprequest/newline-in-request-uri-expected.txt
@@ -1,5 +1,7 @@
 CONSOLE WARNING: line 10: Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.
+CONSOLE WARNING: line 11: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
+CONSOLE ERROR: line 11: Uncaught NetworkError: Failed to execute 'send' on 'XMLHttpRequest': Failed to load 'http://127.0.0.1:8000/xmlhttprequest/resources/print-headers.cgi'.
 Test for bug 22731: Newline in XMLHttpRequest URL can be used to remove HTTP headers (e.g. Host:).
 
-LF SUCCESS
-CR SUCCESS
+LF FAIL: test did not run
+CR FAIL: test did not run
diff --git a/third_party/WebKit/LayoutTests/inspector/network/network-filter-updated-requests.html b/third_party/WebKit/LayoutTests/inspector/network/network-filter-updated-requests.html
index 16ecdf6..610b327 100644
--- a/third_party/WebKit/LayoutTests/inspector/network/network-filter-updated-requests.html
+++ b/third_party/WebKit/LayoutTests/inspector/network/network-filter-updated-requests.html
@@ -11,11 +11,11 @@
     target._resourceCategoryFilterUI._toggleTypeFilter(categoryName, false);
     InspectorTest.addResult("Clicked '" + categoryName + "' button.");
 
-    var requestFoo = new SDK.NetworkRequest(SDK.targetManager.mainTarget(), "", "", "", "", "");
+    var requestFoo = new SDK.NetworkRequest(InspectorTest.networkManager, "", "", "", "", "");
     requestFoo.setResourceType(types.Script);
     requestFoo.setRequestId("foo");
     target._appendRequest(requestFoo);
-    var requestBar = new SDK.NetworkRequest(SDK.targetManager.mainTarget(), "", "", "", "", "");
+    var requestBar = new SDK.NetworkRequest(InspectorTest.networkManager, "", "", "", "", "");
     requestBar.setResourceType(types.Script);
     requestBar.setRequestId("bar");
     target._appendRequest(requestBar);
diff --git a/third_party/WebKit/LayoutTests/inspector/network/network-status-non-http.html b/third_party/WebKit/LayoutTests/inspector/network/network-status-non-http.html
index 8ef89af..649c948 100644
--- a/third_party/WebKit/LayoutTests/inspector/network/network-status-non-http.html
+++ b/third_party/WebKit/LayoutTests/inspector/network/network-status-non-http.html
@@ -16,7 +16,7 @@
         var urls = document.evaluate("//tbody/tr/td[position()=1]/@title", dataGrid, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
         var outputStrings = [];
 
-        for (var request of NetworkLog.networkLog.requestsForTarget(SDK.targetManager.mainTarget())) {
+        for (var request of NetworkLog.networkLog.requestsForManager(InspectorTest.networkManager)) {
             var line = request.displayName + ":" + request.statusCode + " " + request.statusText
             if (request.failed)
                 line += "(failed)";
diff --git a/third_party/WebKit/LayoutTests/inspector/network/network-update-calculator-for-all-requests.html b/third_party/WebKit/LayoutTests/inspector/network/network-update-calculator-for-all-requests.html
index 95163ab..b4f6e9c 100644
--- a/third_party/WebKit/LayoutTests/inspector/network/network-update-calculator-for-all-requests.html
+++ b/third_party/WebKit/LayoutTests/inspector/network/network-update-calculator-for-all-requests.html
@@ -11,7 +11,7 @@
 
     function appendRequest(id, type, startTime, endTime)
     {
-        var request = new SDK.NetworkRequest(SDK.targetManager.mainTarget(), "", "", "", "", "");
+        var request = new SDK.NetworkRequest(InspectorTest.networkManager, "", "", "", "", "");
         request.setResourceType(type);
         request.setRequestId(id);
         request.setIssueTime(startTime);
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index bbe89d1ef..d3c0ec2 100644
--- a/third_party/WebKit/LayoutTests/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "layers": [
     {
diff --git a/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 272f337..0b85a81 100644
--- a/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/android/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "bounds": [785, 933],
   "children": [
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/frames/frameset-style-recalc-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/frames/frameset-style-recalc-expected.txt
index 8dde2be..ccd8f62d 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/frames/frameset-style-recalc-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/frames/frameset-style-recalc-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-hard-linebreak-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-hard-linebreak-expected.txt
index 3fe7f1a..29c7132e 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-hard-linebreak-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/fast/text/selection-hard-linebreak-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 97e19398..81109b1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "layers": [
     {
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 97e19398..81109b1 100644
--- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "layers": [
     {
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/frames/frameset-style-recalc-expected.txt b/third_party/WebKit/LayoutTests/platform/mac/fast/frames/frameset-style-recalc-expected.txt
index a9bd29e..3369ae7 100644
--- a/third_party/WebKit/LayoutTests/platform/mac/fast/frames/frameset-style-recalc-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/mac/fast/frames/frameset-style-recalc-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/frames/frameset-style-recalc-expected.txt b/third_party/WebKit/LayoutTests/platform/win/fast/frames/frameset-style-recalc-expected.txt
index 55265c98..96acac2 100644
--- a/third_party/WebKit/LayoutTests/platform/win/fast/frames/frameset-style-recalc-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/fast/frames/frameset-style-recalc-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 800x600
   LayoutView at (0,0) size 800x600
 layer at (0,0) size 800x600
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 911d579b..e5c8188 100644
--- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "layers": [
     {
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
index 911d579b..e5c8188 100644
--- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
+++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/canvas-composite-repaint-by-all-imagesource-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 {
   "layers": [
     {
diff --git a/third_party/WebKit/LayoutTests/plugins/createScriptableObject-before-start-expected.txt b/third_party/WebKit/LayoutTests/plugins/createScriptableObject-before-start-expected.txt
index 83a7a2f..df3f12d 100644
--- a/third_party/WebKit/LayoutTests/plugins/createScriptableObject-before-start-expected.txt
+++ b/third_party/WebKit/LayoutTests/plugins/createScriptableObject-before-start-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 Test for http://bugs.webkit.org/show_bug.cgi?id=12050 REGRESSION: Assertion failure in -[WebBaseNetscapePluginView willCallPlugInFunction] (plugin).
 
 No ASSERT means test PASS.
diff --git a/third_party/WebKit/LayoutTests/printing/subframes-percentage-height-expected.txt b/third_party/WebKit/LayoutTests/printing/subframes-percentage-height-expected.txt
index 92de9dd5..1c028157 100644
--- a/third_party/WebKit/LayoutTests/printing/subframes-percentage-height-expected.txt
+++ b/third_party/WebKit/LayoutTests/printing/subframes-percentage-height-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 layer at (0,0) size 1066x799
   LayoutView at (0,0) size 1066x799
 layer at (0,0) size 1066x400
diff --git a/third_party/WebKit/LayoutTests/svg/custom/g-outside-svg-expected.txt b/third_party/WebKit/LayoutTests/svg/custom/g-outside-svg-expected.txt
index 03938ca..d0f1de7 100644
--- a/third_party/WebKit/LayoutTests/svg/custom/g-outside-svg-expected.txt
+++ b/third_party/WebKit/LayoutTests/svg/custom/g-outside-svg-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 The SVG in the below frame should not render or crash.
 
diff --git a/third_party/WebKit/LayoutTests/svg/custom/large-image-pattern-crash-expected.txt b/third_party/WebKit/LayoutTests/svg/custom/large-image-pattern-crash-expected.txt
index 886a96b8..00e3afa9 100644
--- a/third_party/WebKit/LayoutTests/svg/custom/large-image-pattern-crash-expected.txt
+++ b/third_party/WebKit/LayoutTests/svg/custom/large-image-pattern-crash-expected.txt
@@ -1,2 +1,3 @@
+CONSOLE WARNING: Resource requests whose URLs contain raw newline characters are deprecated, and may be blocked in M60, around August 2017. Please remove newlines from places like element attribute values in order to continue loading those resources. See https://www.chromestatus.com/features/5735596811091968 for more details.
 PASS: did not crash.
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/biquad-automation.html b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/biquad-automation.html
index 04b9946d..b001ef42 100644
--- a/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/biquad-automation.html
+++ b/third_party/WebKit/LayoutTests/webaudio/BiquadFilter/biquad-automation.html
@@ -5,7 +5,7 @@
     <script src="../../resources/testharness.js"></script>
     <script src="../../resources/testharnessreport.js"></script> 
     <script src="../resources/audit-util.js"></script>
-    <script src="../resources/audio-testing.js"></script>
+    <script src="../resources/audit.js"></script>
     <script src="../resources/biquad-filters.js"></script>
     <script src="../resources/audioparam-testing.js"></script>
   </head>
@@ -128,22 +128,22 @@
         return {filter: f, source: b};
       }
 
-      function createFilterVerifier(filterCreator, threshold, parameters, input, message) {
+      function createFilterVerifier(should, filterCreator, threshold, parameters, input, message) {
         return function (resultBuffer) {
           var actual = resultBuffer.getChannelData(0);
           var coefs = generateFilterCoefficients(filterCreator, parameters, automationEndTime);
 
           reference = timeVaryingFilter(input, coefs);
 
-          Should(message, actual, {
-            verbose: true
-          }).beCloseToArray(reference, threshold);
+          should(actual, message).beCloseToArray(reference, {
+            absoluteThreshold: threshold
+          });
         };
       }
 
       // Automate just the frequency parameter.  A bandpass filter is used where the center
       // frequency is swept across the source (which is a simple tone).
-      audit.defineTask("automate-freq", function (done) {
+      audit.define("automate-freq", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
 
         // Center frequency of bandpass filter and also the frequency of the test tone.
@@ -167,14 +167,15 @@
         f.frequency.linearRampToValueAtTime(parameters.freq[1], automationEndTime);
 
         context.startRendering()
-          .then(createFilterVerifier(createBandpassFilter, 4.6455e-6, parameters, b.getChannelData(0),
+          .then(createFilterVerifier(should, createBandpassFilter, 4.6455e-6,
+            parameters, b.getChannelData(0),
             "Output of bandpass filter with frequency automation"))
-          .then(done);
+          .then(() => task.done());
       });
 
       // Automate just the Q parameter.  A bandpass filter is used where the Q of the filter is
       // swept.
-      audit.defineTask("automate-q", function (done) {
+      audit.define("automate-q", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
 
         // The frequency of the test tone.
@@ -199,14 +200,15 @@
         f.Q.linearRampToValueAtTime(parameters.Q[1], automationEndTime);
 
         context.startRendering()
-          .then(createFilterVerifier(createBandpassFilter, 9.8348e-7, parameters, b.getChannelData(0),
+          .then(createFilterVerifier(should, createBandpassFilter, 9.8348e-7,
+            parameters, b.getChannelData(0),
             "Output of bandpass filter with Q automation"))
-          .then(done);
+          .then(() => task.done());
       });
 
       // Automate just the gain of the lowshelf filter.  A test tone will be in the lowshelf part of
       // the filter.  The output will vary as the gain of the lowshelf is changed.
-      audit.defineTask("automate-gain", function (done) {
+      audit.define("automate-gain", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
 
         // Frequency of the test tone.
@@ -229,14 +231,15 @@
         f.gain.linearRampToValueAtTime(parameters.gain[1], automationEndTime);
 
         context.startRendering()
-          .then(createFilterVerifier(createLowShelfFilter, 2.7657e-5, parameters, b.getChannelData(0),
+          .then(createFilterVerifier(should, createLowShelfFilter, 2.7657e-5,
+            parameters, b.getChannelData(0),
             "Output of lowshelf filter with gain automation"))
-          .then(done);
+          .then(() => task.done());
       });
 
       // Automate just the detune parameter.  Basically the same test as for the frequncy parameter
       // but we just use the detune parameter to modulate the frequency parameter.
-      audit.defineTask("automate-detune", function (done) {
+      audit.define("automate-detune", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
         var centerFreq = 10*440;
         var parameters = {
@@ -253,14 +256,15 @@
         f.detune.linearRampToValueAtTime(parameters.detune[1], automationEndTime);
 
         context.startRendering()
-          .then(createFilterVerifier(createBandpassFilter, 3.1471e-5, parameters, b.getChannelData(0),
+          .then(createFilterVerifier(should, createBandpassFilter, 3.1471e-5,
+            parameters, b.getChannelData(0),
             "Output of bandpass filter with detune automation"))
-          .then(done);
+          .then(() => task.done());
       });
 
       // Automate all of the filter parameters at once.  This is a basic check that everything is
       // working.  A peaking filter is used because it uses all of the parameters.
-      audit.defineTask("automate-all", function (done) {
+      audit.define("automate-all", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
         var graph = configureGraph(context, 10*440);
         var f = graph.filter;
@@ -289,9 +293,10 @@
         f.detune.linearRampToValueAtTime(parameters.detune[1], automationEndTime);
 
         context.startRendering()
-          .then(createFilterVerifier(createPeakingFilter, 6.2907e-4, parameters, b.getChannelData(0),
+          .then(createFilterVerifier(should, createPeakingFilter, 6.2907e-4,
+            parameters, b.getChannelData(0),
             "Output of peaking filter with automation of all parameters"))
-          .then(done);
+          .then(() => task.done());
       });
 
       // Test that modulation of the frequency parameter of the filter works.  A sinusoid of 440 Hz
@@ -299,7 +304,7 @@
       // the filter is modulated by a sinusoid at 103 Hz, and the frequency modulation varies from
       // 116 to 412 Hz.  (This test was taken from the description in
       // https://github.com/WebAudio/web-audio-api/issues/509#issuecomment-94731355)
-      audit.defineTask("modulation", function (done) {
+      audit.define("modulation", (task, should) => {
         var context = new OfflineAudioContext(1, renderDuration * sampleRate, sampleRate);
 
         // Create a graph with the sinusoidal source at 440 Hz as the input to a biquad filter.
@@ -353,19 +358,17 @@
              reference = timeVaryingFilter(b.getChannelData(0),
                {b0: b0, b1: b1, b2: b2, a1: a1, a2: a2});
 
-             Should("Output of bandpass filter with sinusoidal modulation of bandpass center frequency",
-               actual)
-               .beCloseToArray(reference, 3.9787e-5);
+             should(actual,
+                 "Output of bandpass filter with sinusoidal modulation of bandpass center frequency"
+               )
+               .beCloseToArray(reference, {
+                 absoluteThreshold: 3.9787e-5
+               });
            })
-          .then(done);
+          .then(() => task.done());
       });
 
-      // All done!
-      audit.defineTask("finish", function (done) {
-        done();
-      });
-
-      audit.runTasks();
+      audit.run();
     </script>
   </body>
 </html>
diff --git a/third_party/WebKit/LayoutTests/webaudio/Convolver/convolver-response-4-chan.html b/third_party/WebKit/LayoutTests/webaudio/Convolver/convolver-response-4-chan.html
index 843c81e..34a0486 100644
--- a/third_party/WebKit/LayoutTests/webaudio/Convolver/convolver-response-4-chan.html
+++ b/third_party/WebKit/LayoutTests/webaudio/Convolver/convolver-response-4-chan.html
@@ -96,6 +96,51 @@
                 .then(() => task.done());
           });
 
+      audit.define(
+        {
+          label: 'delayed buffer set',
+          description: 'Delayed set of 4-channel response'
+        },
+        (task, should) => {
+          // Don't really care about the output for this test.  It's to verify
+          // we don't crash in a debug build when setting the convolver buffer
+          // after creating the graph.
+          let context = new OfflineAudioContext(1, renderFrames, sampleRate);
+          let src = new OscillatorNode(context);
+          let convolver = new ConvolverNode(context, {
+            disableNormalization: true
+          });
+          let buffer = new AudioBuffer({
+            numberOfChannels: 4,
+            length: 4,
+            sampleRate: context.sampleRate
+          });
+
+          // Impulse responses for the convolver aren't important, as long as
+          // it's not all zeroes.
+          for (let k = 0; k < buffer.numberOfChannels; ++k) {
+            buffer.getChannelData(k).fill(1);
+          }
+
+          src.connect(convolver).connect(context.destination);
+
+          // Set the buffer after a few render quanta have passed.  The actual
+          // value must be least one, but is otherwise arbitrary.
+          context.suspend(512 / context.sampleRate)
+            .then(() => convolver.buffer = buffer)
+            .then(() => context.resume());
+
+         src.start();
+         context.startRendering()
+           .then(audioBuffer => {
+             // Just make sure output is not silent.
+             should(audioBuffer.getChannelData(0),
+                 'Output with delayed setting of convolver buffer')
+               .notBeConstantValueOf(0);
+           })
+           .then(() => task.done());
+        });
+
       function fourChannelResponseTest(options, should) {
         // Create an 4-channel offline context.  The first two channels are for
         // the stereo output of the convolver and the next two channels are for
diff --git a/third_party/WebKit/LayoutTests/webaudio/audio-scheduled-source-basic.html b/third_party/WebKit/LayoutTests/webaudio/audio-scheduled-source-basic.html
index d67350d..751effe 100644
--- a/third_party/WebKit/LayoutTests/webaudio/audio-scheduled-source-basic.html
+++ b/third_party/WebKit/LayoutTests/webaudio/audio-scheduled-source-basic.html
@@ -36,22 +36,17 @@
 
             // AudioScheduledSourceNode must have these properties.
             for (p in expectedProperties) {
-              should(
-                  AudioScheduledSourceNode.prototype.hasOwnProperty(
-                      expectedProperties[p]),
-                  'AudioScheduledSourceNode.' + expectedProperties[p])
-                  .beTrue();
+              should(AudioScheduledSourceNode.prototype,
+                     'AudioScheduledSourceNode.prototype')
+                  .haveOwnProperty(expectedProperties[p]);
             }
 
             // ConstantSource and Oscillator must not
             var nodes = ['ConstantSourceNode', 'OscillatorNode'];
             for (n in nodes) {
               for (p in expectedProperties) {
-                should(
-                    window[nodes[n]].prototype.hasOwnProperty(
-                        expectedProperties[p]),
-                    nodes[n] + '.' + expectedProperties[p])
-                    .beFalse();
+                should(window[nodes[n]].prototype, nodes[n] + '.prototype')
+                    .notHaveOwnProperty(expectedProperties[p]);
               }
             }
 
@@ -59,18 +54,15 @@
             // have the others.
             for (p in expectedProperties) {
               if (expectedProperties[p] !== 'start') {
-                should(
-                    AudioBufferSourceNode.prototype.hasOwnProperty(
-                        expectedProperties[p]),
-                    'AudioBufferSourceNode.' + expectedProperties[p])
-                    .beFalse();
+                should(AudioBufferSourceNode.prototype,
+                       'AudioBufferSourceNode.prototype')
+                    .notHaveOwnProperty(expectedProperties[p]);
               }
             }
 
-            should(
-                AudioBufferSourceNode.prototype.hasOwnProperty('start'),
-                'AudioBufferSourceNode.start')
-                .beTrue();
+            should(AudioBufferSourceNode.prototype,
+                   'AudioBufferSourceNode.prototype')
+                .haveOwnProperty('start');
 
             task.done();
           });
diff --git a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
index f73cd58..4fa4535 100644
--- a/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
+++ b/third_party/WebKit/LayoutTests/webaudio/resources/audit.js
@@ -977,6 +977,76 @@
                           '${actual} ' + passDetail,
                           '${actual} ' + failDetail);
     }
+
+    /**
+     * Check if |expected| property is truly owned by |actual| object.
+     *
+     * @example
+     *   should(BaseAudioContext.prototype,
+     *          'BaseAudioContext.prototype').haveOwnProperty('createGain');
+     *
+     * @result
+     *   "PASS   BaseAudioContext.prototype has an own property of
+     *       'createGain'."
+     */
+    haveOwnProperty () {
+      this._processArguments(arguments);
+
+      return this._assert(
+          this._actual.hasOwnProperty(this._expected),
+          '${actual} has an own property of "${expected}".',
+          '${actual} does not own the property of "${expected}".');
+    }
+
+
+    /**
+     * Check if |expected| property is not owned by |actual| object.
+     *
+     * @example
+     *   should(BaseAudioContext.prototype,
+     *          'BaseAudioContext.prototype')
+     *       .notHaveOwnProperty('startRendering');
+     *
+     * @result
+     *   "PASS   BaseAudioContext.prototype does not have an own property of
+     *       'startRendering'."
+     */
+    notHaveOwnProperty () {
+      this._processArguments(arguments);
+
+      return this._assert(
+          !this._actual.hasOwnProperty(this._expected),
+          '${actual} does not have an own property of "${expected}".',
+          '${actual} has an own the property of "${expected}".')
+    }
+
+
+    /**
+     * Check if an object is inherited from a class. This looks up the entire
+     * prototype chain of a given object and tries to find a match.
+     *
+     * @example
+     *   should(sourceNode, 'A buffer source node')
+     *       .inheritFrom('AudioScheduledSourceNode');
+     *
+     * @result
+     *   "PASS   A buffer source node inherits from 'AudioScheduledSourceNode'."
+     */
+    inheritFrom () {
+      this._processArguments(arguments);
+
+      let prototypes = [];
+      let currentPrototype = Object.getPrototypeOf(this._actual);
+      while (currentPrototype) {
+        prototypes.push(currentPrototype.constructor.name);
+        currentPrototype = Object.getPrototypeOf(currentPrototype);
+      }
+
+      return this._assert(
+          prototypes.includes(this._expected),
+          '${actual} inherits from "${expected}".',
+          '${actual} does not inherit from "${expected}".');
+    }
   }
 
 
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
index fc77c59..015e0a7 100644
--- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
+++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit-expected.txt
@@ -36,10 +36,14 @@
 PASS   1 is equal to 1. 
 PASS   should(1).beEqualTo(1) is true. 
 PASS   The message is truthful! 
+PASS   BaseAudioContext.prototype has an own property of "createGain". 
+PASS   BaseAudioContext.prototype does not have an own property of "startRendering". 
+PASS   An AudioBufferSourceNode inherits from "AudioScheduledSourceNode". 
+PASS   An AudioBufferSourceNode inherits from "AudioNode". 
 PASS   Decoding audio data with no argument rejected correctly with TypeError: Failed to execute 'decodeAudioData' on 'BaseAudioContext': 1 argument required, but only 0 present.. 
 PASS   Suspending OAC with no argument rejected correctly with TypeError. 
 PASS   Start OAC rendering resolved correctly. 
-PASS < [basic] All assertions passed. (total 22 assertions) 
+PASS < [basic] All assertions passed. (total 26 assertions) 
 PASS > [load-file-in-should] Test Audit.loadFileFromUrl() within |should| assertion. 
 PASS   Loading file within should().beResolved() resolved correctly. 
 PASS < [load-file-in-should] All assertions passed. (total 1 assertions) 
diff --git a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
index 5c56e6a..a219d30 100644
--- a/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
+++ b/third_party/WebKit/LayoutTests/webaudio/unit-tests/audit.html
@@ -36,6 +36,18 @@
         should(should(1).beEqualTo(1), 'should(1).beEqualTo(1)').beTrue();
         should(true, 'The message is').message('truthful!', 'false!');
 
+        should(BaseAudioContext.prototype, 'BaseAudioContext.prototype')
+            .haveOwnProperty('createGain');
+        should(BaseAudioContext.prototype, 'BaseAudioContext.prototype')
+            .notHaveOwnProperty('startRendering');
+
+        let ac = new AudioContext();
+        let sourceNode = new AudioBufferSourceNode(ac);
+        should(sourceNode, 'An AudioBufferSourceNode')
+            .inheritFrom('AudioScheduledSourceNode');
+        should(sourceNode, 'An AudioBufferSourceNode')
+            .inheritFrom('AudioNode');
+
         let oac = new OfflineAudioContext(1, 128, 44100);
         Promise.all([
             should(oac.startRendering(), 'Start OAC rendering').beResolved(),
diff --git a/third_party/WebKit/OWNERS b/third_party/WebKit/OWNERS
index 10be8916..60a1066 100644
--- a/third_party/WebKit/OWNERS
+++ b/third_party/WebKit/OWNERS
@@ -1,12 +1,10 @@
 # Blink API OWNERS
 file://third_party/WebKit/API_OWNERS
 
-# For .gyp* / *.gn* changes only.
+# For *.gn* changes only.
 dpranke@chromium.org
 thakis@chromium.org
 
-per-file .gitignore=*
-per-file DEPS=*
 per-file PRESUBMIT*.py=dpranke@chromium.org
 per-file PRESUBMIT*.py=thakis@chromium.org
 
diff --git a/third_party/WebKit/Source/SpecMapping.md b/third_party/WebKit/Source/SpecMapping.md
index ac54be9..e217562 100644
--- a/third_party/WebKit/Source/SpecMapping.md
+++ b/third_party/WebKit/Source/SpecMapping.md
@@ -28,9 +28,9 @@
 using `SecurityOrigin::isSameSchemeHostPort`.
 
 The [Suborigins spec](https://w3c.github.io/webappsec-suborigins/) extends
-HTML's definition of origins. To check for same-origin and same-origin domain
-use `SecurityOrigin::canAccessCheckSuborigins` and
-`SecurityOrigin::isSameSchemeHostPortAndSuborigin`.
+HTML's definition of origins. To check for same-origin corresponds to
+`SecurityOrigin::isSameSchemeHostPortAndSuborigin` while the check for same-origin
+domain already takes the suborigin into account.
 
 ### [Window object](https://html.spec.whatwg.org/#window)
 
diff --git a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
index 516ef4d..9dcfbd9 100644
--- a/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp
@@ -60,7 +60,7 @@
 
   const SecurityOrigin* accessingOrigin =
       accessingWindow->document()->getSecurityOrigin();
-  if (!accessingOrigin->canAccessCheckSuborigins(targetFrameOrigin))
+  if (!accessingOrigin->canAccess(targetFrameOrigin))
     return false;
 
   // Notify the loader's client if the initial document has been accessed.
@@ -241,7 +241,7 @@
   SECURITY_CHECK(!(targetWindow && targetWindow->frame()) ||
                  targetWindow == targetWindow->frame()->domWindow());
 
-  if (!accessingOrigin->canAccessCheckSuborigins(targetOrigin))
+  if (!accessingOrigin->canAccess(targetOrigin))
     return false;
 
   // Note that there is no need to call back
diff --git a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
index 3e2e56c..d9d8c393 100644
--- a/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/NativeValueTraitsImplTest.cpp
@@ -7,8 +7,10 @@
 #include <utility>
 #include "bindings/core/v8/ExceptionState.h"
 #include "bindings/core/v8/IDLTypes.h"
+#include "bindings/core/v8/TestSequenceCallback.h"
 #include "bindings/core/v8/ToV8.h"
 #include "bindings/core/v8/V8BindingForTesting.h"
+#include "bindings/core/v8/V8Internals.h"
 #include "platform/wtf/Vector.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
@@ -21,6 +23,29 @@
   return blink::ToV8(value, scope->context()->Global(), scope->isolate());
 }
 
+TEST(NativeValueTraitsImplTest, IDLInterface) {
+  V8TestingScope scope;
+  {
+    DummyExceptionStateForTesting exceptionState;
+    Internals* internals = NativeValueTraits<Internals>::nativeValue(
+        scope.isolate(), v8::Number::New(scope.isolate(), 42), exceptionState);
+    EXPECT_TRUE(exceptionState.hadException());
+    EXPECT_EQ("Unable to convert value to Internals.",
+              exceptionState.message());
+    EXPECT_EQ(nullptr, internals);
+  }
+  {
+    DummyExceptionStateForTesting exceptionState;
+    TestSequenceCallback* callbackFunction =
+        NativeValueTraits<TestSequenceCallback>::nativeValue(
+            scope.isolate(), v8::Undefined(scope.isolate()), exceptionState);
+    EXPECT_TRUE(exceptionState.hadException());
+    EXPECT_EQ("Unable to convert value to TestSequenceCallback.",
+              exceptionState.message());
+    EXPECT_EQ(nullptr, callbackFunction);
+  }
+}
+
 void ThrowException(v8::Local<v8::Name>,
                     const v8::PropertyCallbackInfo<v8::Value>& info) {
   info.GetIsolate()->ThrowException(v8String(info.GetIsolate(), "bogus!"));
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp b/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
index 13598e6..37b5c5b 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptState.cpp
@@ -70,8 +70,4 @@
   return toExecutionContext(context());
 }
 
-void ScriptState::setExecutionContext(ExecutionContext*) {
-  ASSERT_NOT_REACHED();
-}
-
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScriptState.h b/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
index 7935856..f78576ef 100644
--- a/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
+++ b/third_party/WebKit/Source/bindings/core/v8/ScriptState.h
@@ -137,7 +137,6 @@
   v8::Isolate* isolate() const { return m_isolate; }
   DOMWrapperWorld& world() const { return *m_world; }
   virtual ExecutionContext* getExecutionContext() const;
-  virtual void setExecutionContext(ExecutionContext*);
 
   // This can return an empty handle if the v8::Context is gone.
   v8::Local<v8::Context> context() const {
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.cpp b/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.cpp
index 692c798..f19b6e5 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.cpp
@@ -29,11 +29,6 @@
   return m_executionContext;
 }
 
-void ScriptStateForTesting::setExecutionContext(
-    ExecutionContext* executionContext) {
-  m_executionContext = executionContext;
-}
-
 V8TestingScope::V8TestingScope()
     : m_holder(DummyPageHolder::create()),
       m_handleScope(isolate()),
diff --git a/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.h b/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.h
index 09f41f5..8299477 100644
--- a/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.h
+++ b/third_party/WebKit/Source/bindings/core/v8/V8BindingForTesting.h
@@ -25,7 +25,6 @@
   static PassRefPtr<ScriptStateForTesting> create(v8::Local<v8::Context>,
                                                   PassRefPtr<DOMWrapperWorld>);
   ExecutionContext* getExecutionContext() const override;
-  void setExecutionContext(ExecutionContext*) override;
 
  private:
   ScriptStateForTesting(v8::Local<v8::Context>, PassRefPtr<DOMWrapperWorld>);
diff --git a/third_party/WebKit/Source/bindings/modules/v8/ModuleBindingsInitializer.cpp b/third_party/WebKit/Source/bindings/modules/v8/ModuleBindingsInitializer.cpp
index a74c96a..529cb9ef 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/ModuleBindingsInitializer.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/ModuleBindingsInitializer.cpp
@@ -7,6 +7,7 @@
 #include "bindings/core/v8/V8PerIsolateData.h"
 #include "bindings/modules/v8/ConditionalFeaturesForModules.h"
 #include "bindings/modules/v8/SerializedScriptValueForModulesFactory.h"
+#include "bindings/modules/v8/wasm/WasmResponseExtensions.h"
 
 namespace blink {
 
@@ -19,6 +20,7 @@
   initPartialInterfacesInModules();
   SerializedScriptValueFactory::initialize(
       new SerializedScriptValueForModulesFactory);
+  WasmResponseExtensions::initialize(V8PerIsolateData::mainThreadIsolate());
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/modules/v8/v8.gni b/third_party/WebKit/Source/bindings/modules/v8/v8.gni
index 9a668b0..5b082cf 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/v8.gni
+++ b/third_party/WebKit/Source/bindings/modules/v8/v8.gni
@@ -22,6 +22,8 @@
                     "V8BindingForModules.cpp",
                     "V8BindingForModules.h",
                     "V8ServiceWorkerMessageEventInternal.h",
+                    "wasm/WasmResponseExtensions.cpp",
+                    "wasm/WasmResponseExtensions.h",
                     "WebGLAny.cpp",
                     "WebGLAny.h",
                   ],
diff --git a/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp b/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp
new file mode 100644
index 0000000..a2a8dc1d
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.cpp
@@ -0,0 +1,234 @@
+// Copyright 2017 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bindings/modules/v8/wasm/WasmResponseExtensions.h"
+
+#include "bindings/core/v8/ExceptionState.h"
+#include "bindings/core/v8/ScriptPromise.h"
+#include "bindings/core/v8/ScriptPromiseResolver.h"
+#include "bindings/core/v8/ScriptState.h"
+#include "bindings/modules/v8/V8Response.h"
+#include "modules/fetch/BodyStreamBuffer.h"
+#include "modules/fetch/FetchDataLoader.h"
+#include "platform/heap/Handle.h"
+#include "wtf/RefPtr.h"
+
+namespace blink {
+
+namespace {
+
+class FetchDataLoaderAsWasmModule final : public FetchDataLoader,
+                                          public BytesConsumer::Client {
+  USING_GARBAGE_COLLECTED_MIXIN(FetchDataLoaderAsWasmModule);
+
+ public:
+  FetchDataLoaderAsWasmModule(ScriptPromiseResolver* resolver,
+                              ScriptState* scriptState)
+      : m_resolver(resolver),
+        m_builder(scriptState->isolate()),
+        m_scriptState(scriptState) {}
+
+  void start(BytesConsumer* consumer,
+             FetchDataLoader::Client* client) override {
+    DCHECK(!m_consumer);
+    DCHECK(!m_client);
+    m_client = client;
+    m_consumer = consumer;
+    m_consumer->setClient(this);
+    onStateChange();
+  }
+
+  void onStateChange() override {
+    while (true) {
+      // {buffer} is owned by {m_consumer}.
+      const char* buffer = nullptr;
+      size_t available = 0;
+      BytesConsumer::Result result = m_consumer->beginRead(&buffer, &available);
+
+      if (result == BytesConsumer::Result::ShouldWait)
+        return;
+      if (result == BytesConsumer::Result::Ok) {
+        if (available > 0) {
+          DCHECK_NE(buffer, nullptr);
+          m_builder.OnBytesReceived(reinterpret_cast<const uint8_t*>(buffer),
+                                    available);
+        }
+        result = m_consumer->endRead(available);
+      }
+      switch (result) {
+        case BytesConsumer::Result::ShouldWait:
+          NOTREACHED();
+          return;
+        case BytesConsumer::Result::Ok: {
+          break;
+        }
+        case BytesConsumer::Result::Done: {
+          v8::Isolate* isolate = m_scriptState->isolate();
+          ScriptState::Scope scope(m_scriptState.get());
+
+          {
+            // The TryCatch destructor will clear the exception. We
+            // scope the block here to ensure tight control over the
+            // lifetime of the exception.
+            v8::TryCatch trycatch(isolate);
+            v8::Local<v8::WasmCompiledModule> module;
+            if (m_builder.Finish().ToLocal(&module)) {
+              DCHECK(!trycatch.HasCaught());
+              ScriptValue scriptValue(m_scriptState.get(), module);
+              m_resolver->resolve(scriptValue);
+            } else {
+              DCHECK(trycatch.HasCaught());
+              m_resolver->reject(trycatch.Exception());
+            }
+          }
+
+          m_client->didFetchDataLoadedCustomFormat();
+          return;
+        }
+        case BytesConsumer::Result::Error: {
+          // TODO(mtrofin): do we need an abort on the wasm side?
+          // Something like "m_outStream->abort()" maybe?
+          return rejectPromise();
+        }
+      }
+    }
+  }
+
+  void cancel() override {
+    m_consumer->cancel();
+    return rejectPromise();
+  }
+
+  DEFINE_INLINE_TRACE() {
+    visitor->trace(m_consumer);
+    visitor->trace(m_resolver);
+    visitor->trace(m_client);
+    FetchDataLoader::trace(visitor);
+    BytesConsumer::Client::trace(visitor);
+  }
+
+ private:
+  // TODO(mtrofin): replace with spec-ed error types, once spec clarifies
+  // what they are.
+  void rejectPromise() {
+    m_resolver->reject(V8ThrowException::createTypeError(
+        m_scriptState->isolate(), "Could not download wasm module"));
+  }
+  Member<BytesConsumer> m_consumer;
+  Member<ScriptPromiseResolver> m_resolver;
+  Member<FetchDataLoader::Client> m_client;
+  v8::WasmModuleObjectBuilder m_builder;
+  const RefPtr<ScriptState> m_scriptState;
+};
+
+// TODO(mtrofin): WasmDataLoaderClient is necessary so we may provide an
+// argument to BodyStreamBuffer::startLoading, however, it fulfills
+// a very small role. Consider refactoring to avoid it.
+class WasmDataLoaderClient final
+    : public GarbageCollectedFinalized<WasmDataLoaderClient>,
+      public FetchDataLoader::Client {
+  WTF_MAKE_NONCOPYABLE(WasmDataLoaderClient);
+  USING_GARBAGE_COLLECTED_MIXIN(WasmDataLoaderClient);
+
+ public:
+  explicit WasmDataLoaderClient() {}
+  void didFetchDataLoadedCustomFormat() override {}
+  void didFetchDataLoadFailed() override { NOTREACHED(); }
+};
+
+// This callback may be entered as a promise is resolved, or directly
+// from the overload callback.
+// See
+// https://github.com/WebAssembly/design/blob/master/Web.md#webassemblycompile
+void compileFromResponseCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  ExceptionState exceptionState(args.GetIsolate(),
+                                ExceptionState::ExecutionContext, "WebAssembly",
+                                "compile");
+  ExceptionToRejectPromiseScope rejectPromiseScope(args, exceptionState);
+
+  ScriptState* scriptState = ScriptState::forReceiverObject(args);
+  if (!scriptState->getExecutionContext()) {
+    v8SetReturnValue(args, ScriptPromise().v8Value());
+    return;
+  }
+
+  if (args.Length() < 1 || !args[0]->IsObject() ||
+      !V8Response::hasInstance(args[0], args.GetIsolate())) {
+    v8SetReturnValue(
+        args, ScriptPromise::reject(
+                  scriptState, V8ThrowException::createTypeError(
+                                   scriptState->isolate(),
+                                   "Promise argument must be called with a "
+                                   "Promise<Response> object"))
+                  .v8Value());
+    return;
+  }
+
+  Response* response = V8Response::toImpl(v8::Local<v8::Object>::Cast(args[0]));
+  ScriptPromise promise;
+  if (response->isBodyLocked() || response->bodyUsed()) {
+    promise = ScriptPromise::reject(
+        scriptState,
+        V8ThrowException::createTypeError(
+            scriptState->isolate(),
+            "Cannot compile WebAssembly.Module from an already read Response"));
+  } else {
+    ScriptPromiseResolver* resolver =
+        ScriptPromiseResolver::create(scriptState);
+    if (response->bodyBuffer()) {
+      promise = resolver->promise();
+      response->bodyBuffer()->startLoading(
+          new FetchDataLoaderAsWasmModule(resolver, scriptState),
+          new WasmDataLoaderClient());
+    } else {
+      promise = ScriptPromise::reject(
+          scriptState,
+          V8ThrowException::createTypeError(
+              scriptState->isolate(), "Response object has a null body."));
+    }
+  }
+  v8SetReturnValue(args, promise.v8Value());
+}
+
+// See https://crbug.com/708238 for tracking avoiding the hand-generated code.
+bool wasmCompileOverload(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (args.Length() < 1 || !args[0]->IsObject())
+    return false;
+
+  if (!args[0]->IsPromise() &&
+      !V8Response::hasInstance(args[0], args.GetIsolate()))
+    return false;
+
+  v8::Isolate* isolate = args.GetIsolate();
+  ScriptState* scriptState = ScriptState::forReceiverObject(args);
+
+  v8::Local<v8::Function> compileCallback =
+      v8::Function::New(isolate, compileFromResponseCallback);
+
+  ScriptPromiseResolver* scriptPromiseResolver =
+      ScriptPromiseResolver::create(scriptState);
+  // treat either case of parameter as
+  // Promise.resolve(parameter)
+  // as per https://www.w3.org/2001/tag/doc/promises-guide#resolve-arguments
+
+  // Ending with:
+  //    return Promise.resolve(parameter).then(compileCallback);
+  ScriptPromise parameterAsPromise = scriptPromiseResolver->promise();
+  v8SetReturnValue(args, ScriptPromise::cast(scriptState, args[0])
+                             .then(compileCallback)
+                             .v8Value());
+
+  // resolve the first parameter promise.
+  scriptPromiseResolver->resolve(ScriptValue::from(scriptState, args[0]));
+  return true;
+}
+
+}  // namespace
+
+void WasmResponseExtensions::initialize(v8::Isolate* isolate) {
+  isolate->SetWasmCompileCallback(wasmCompileOverload);
+}
+
+}  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.h b/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.h
new file mode 100644
index 0000000..c465c10
--- /dev/null
+++ b/third_party/WebKit/Source/bindings/modules/v8/wasm/WasmResponseExtensions.h
@@ -0,0 +1,25 @@
+// Copyright 2017 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 WasmResponseExtensions_h
+#define WasmResponseExtensions_h
+
+#include "modules/ModulesExport.h"
+#include "v8/include/v8.h"
+#include "wtf/Allocator.h"
+
+namespace blink {
+
+// Injects Web Platform - specific overloads for WebAssembly APIs.
+// See https://github.com/WebAssembly/design/blob/master/Web.md
+class MODULES_EXPORT WasmResponseExtensions {
+  STATIC_ONLY(WasmResponseExtensions);
+
+ public:
+  static void initialize(v8::Isolate*);
+};
+
+}  // namespace blink
+
+#endif  // WasmResponseextensions_h
diff --git a/third_party/WebKit/Source/bindings/scripts/v8_callback_function.py b/third_party/WebKit/Source/bindings/scripts/v8_callback_function.py
index 1bbd2cb..37be007 100644
--- a/third_party/WebKit/Source/bindings/scripts/v8_callback_function.py
+++ b/third_party/WebKit/Source/bindings/scripts/v8_callback_function.py
@@ -39,6 +39,11 @@
         argument.idl_type.add_includes_for_type(callback_function.extended_attributes)
 
     context = {
+        # While both |callback_function_name| and |cpp_class| are identical at
+        # the moment, the two are being defined because their values may change
+        # in the future (e.g. if we support [ImplementedAs=] in callback
+        # functions).
+        'callback_function_name': callback_function.name,
         'cpp_class': callback_function.name,
         'cpp_includes': sorted(includes),
         'forward_declarations': sorted(forward_declarations),
diff --git a/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
index 50e3b25..8390f85 100644
--- a/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/callback_function.cpp.tmpl
@@ -81,7 +81,10 @@
 }
 
 {{cpp_class}}* NativeValueTraits<{{cpp_class}}>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return {{cpp_class}}::create(ScriptState::current(isolate), value);
+  {{cpp_class}}* nativeValue = {{cpp_class}}::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to {{callback_function_name}}.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
index 7ed4d7fc..e840b30e 100644
--- a/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
+++ b/third_party/WebKit/Source/bindings/templates/interface.cpp.tmpl
@@ -892,7 +892,10 @@
 {##############################################################################}
 {% block native_value_traits %}
 {{cpp_class}}* NativeValueTraits<{{cpp_class}}>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return {{v8_class}}::toImplWithTypeCheck(isolate, value);
+  {{cpp_class}}* nativeValue = {{v8_class}}::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to {{interface_name}}.");
+  return nativeValue;
 }
 
 {% endblock %}
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
index 25c98c8..e383a0b 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/AnyCallbackFunctionOptionalAnyArg.cpp
@@ -83,7 +83,10 @@
 }
 
 AnyCallbackFunctionOptionalAnyArg* NativeValueTraits<AnyCallbackFunctionOptionalAnyArg>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return AnyCallbackFunctionOptionalAnyArg::create(ScriptState::current(isolate), value);
+  AnyCallbackFunctionOptionalAnyArg* nativeValue = AnyCallbackFunctionOptionalAnyArg::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to AnyCallbackFunctionOptionalAnyArg.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp b/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
index ba29c67..334b6d9 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/LongCallbackFunction.cpp
@@ -87,7 +87,10 @@
 }
 
 LongCallbackFunction* NativeValueTraits<LongCallbackFunction>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return LongCallbackFunction::create(ScriptState::current(isolate), value);
+  LongCallbackFunction* nativeValue = LongCallbackFunction::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to LongCallbackFunction.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
index 53fe12a6..a23b6550 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/StringSequenceCallbackFunctionLongSequenceArg.cpp
@@ -86,7 +86,10 @@
 }
 
 StringSequenceCallbackFunctionLongSequenceArg* NativeValueTraits<StringSequenceCallbackFunctionLongSequenceArg>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return StringSequenceCallbackFunctionLongSequenceArg::create(ScriptState::current(isolate), value);
+  StringSequenceCallbackFunctionLongSequenceArg* nativeValue = StringSequenceCallbackFunctionLongSequenceArg::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to StringSequenceCallbackFunctionLongSequenceArg.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
index 66bb1291..b5360df 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBuffer.cpp
@@ -80,7 +80,10 @@
 }
 
 TestArrayBuffer* NativeValueTraits<TestArrayBuffer>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8ArrayBuffer::toImplWithTypeCheck(isolate, value);
+  TestArrayBuffer* nativeValue = V8ArrayBuffer::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to ArrayBuffer.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
index 55221da5..7086d60 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8ArrayBufferView.cpp
@@ -100,7 +100,10 @@
 }
 
 TestArrayBufferView* NativeValueTraits<TestArrayBufferView>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8ArrayBufferView::toImplWithTypeCheck(isolate, value);
+  TestArrayBufferView* nativeValue = V8ArrayBufferView::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to ArrayBufferView.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
index 33295e7f..9a92e8c 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8DataView.cpp
@@ -80,7 +80,10 @@
 }
 
 TestDataView* NativeValueTraits<TestDataView>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8DataView::toImplWithTypeCheck(isolate, value);
+  TestDataView* nativeValue = V8DataView::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to DataView.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
index 6b87d44..2dd8521 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8SVGTestInterface.cpp
@@ -131,7 +131,10 @@
 }
 
 SVGTestInterface* NativeValueTraits<SVGTestInterface>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8SVGTestInterface::toImplWithTypeCheck(isolate, value);
+  SVGTestInterface* nativeValue = V8SVGTestInterface::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to SVGTestInterface.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
index f83f346..6a547f22 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestCallbackFunctions.cpp
@@ -335,7 +335,10 @@
 }
 
 TestCallbackFunctions* NativeValueTraits<TestCallbackFunctions>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestCallbackFunctions::toImplWithTypeCheck(isolate, value);
+  TestCallbackFunctions* nativeValue = V8TestCallbackFunctions::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestCallbackFunctions.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
index 212e44cf..e22d7d9 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestConstants.cpp
@@ -198,7 +198,10 @@
 }
 
 TestConstants* NativeValueTraits<TestConstants>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestConstants::toImplWithTypeCheck(isolate, value);
+  TestConstants* nativeValue = V8TestConstants::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestConstants.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
index 18c7780..504eb3db 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestException.cpp
@@ -171,7 +171,10 @@
 }
 
 TestException* NativeValueTraits<TestException>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestException::toImplWithTypeCheck(isolate, value);
+  TestException* nativeValue = V8TestException::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestException.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
index a0cac5e..1c69f50 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexed.cpp
@@ -214,7 +214,10 @@
 }
 
 TestIntegerIndexed* NativeValueTraits<TestIntegerIndexed>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestIntegerIndexed::toImplWithTypeCheck(isolate, value);
+  TestIntegerIndexed* nativeValue = V8TestIntegerIndexed::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestIntegerIndexed.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
index d68aba31..fdbef25 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedGlobal.cpp
@@ -235,7 +235,10 @@
 }
 
 TestIntegerIndexedGlobal* NativeValueTraits<TestIntegerIndexedGlobal>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestIntegerIndexedGlobal::toImplWithTypeCheck(isolate, value);
+  TestIntegerIndexedGlobal* nativeValue = V8TestIntegerIndexedGlobal::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestIntegerIndexedGlobal.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
index 5d0aa01..c615d190 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestIntegerIndexedPrimaryGlobal.cpp
@@ -235,7 +235,10 @@
 }
 
 TestIntegerIndexedPrimaryGlobal* NativeValueTraits<TestIntegerIndexedPrimaryGlobal>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestIntegerIndexedPrimaryGlobal::toImplWithTypeCheck(isolate, value);
+  TestIntegerIndexedPrimaryGlobal* nativeValue = V8TestIntegerIndexedPrimaryGlobal::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestIntegerIndexedPrimaryGlobal.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
index 35e8fab2..02f56e6 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface.cpp
@@ -2970,7 +2970,10 @@
 }
 
 TestInterfaceImplementation* NativeValueTraits<TestInterfaceImplementation>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterface::toImplWithTypeCheck(isolate, value);
+  TestInterfaceImplementation* nativeValue = V8TestInterface::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterface.");
+  return nativeValue;
 }
 
 void V8TestInterface::preparePrototypeAndInterfaceObject(v8::Local<v8::Context> context, const DOMWrapperWorld& world, v8::Local<v8::Object> prototypeObject, v8::Local<v8::Function> interfaceObject, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
index 2f6cec9..b71d025 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface2.cpp
@@ -664,7 +664,10 @@
 }
 
 TestInterface2* NativeValueTraits<TestInterface2>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterface2::toImplWithTypeCheck(isolate, value);
+  TestInterface2* nativeValue = V8TestInterface2::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterface2.");
+  return nativeValue;
 }
 
 InstallTemplateFunction V8TestInterface2::installV8TestInterface2TemplateFunction =
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
index 50eca5c..39e7588 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterface3.cpp
@@ -221,7 +221,10 @@
 }
 
 TestInterface3* NativeValueTraits<TestInterface3>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterface3::toImplWithTypeCheck(isolate, value);
+  TestInterface3* nativeValue = V8TestInterface3::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterface3.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
index d9b4cdda..6ccef11 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCheckSecurity.cpp
@@ -555,7 +555,10 @@
 }
 
 TestInterfaceCheckSecurity* NativeValueTraits<TestInterfaceCheckSecurity>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceCheckSecurity::toImplWithTypeCheck(isolate, value);
+  TestInterfaceCheckSecurity* nativeValue = V8TestInterfaceCheckSecurity::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceCheckSecurity.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
index fea54928..23eb195 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor.cpp
@@ -443,7 +443,10 @@
 }
 
 TestInterfaceConstructor* NativeValueTraits<TestInterfaceConstructor>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceConstructor::toImplWithTypeCheck(isolate, value);
+  TestInterfaceConstructor* nativeValue = V8TestInterfaceConstructor::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceConstructor.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
index a49cec8..651610f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor2.cpp
@@ -267,7 +267,10 @@
 }
 
 TestInterfaceConstructor2* NativeValueTraits<TestInterfaceConstructor2>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceConstructor2::toImplWithTypeCheck(isolate, value);
+  TestInterfaceConstructor2* nativeValue = V8TestInterfaceConstructor2::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceConstructor2.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
index 355dce07..42bdd801 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor3.cpp
@@ -120,7 +120,10 @@
 }
 
 TestInterfaceConstructor3* NativeValueTraits<TestInterfaceConstructor3>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceConstructor3::toImplWithTypeCheck(isolate, value);
+  TestInterfaceConstructor3* nativeValue = V8TestInterfaceConstructor3::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceConstructor3.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
index 9ea7b4c8..8526d68 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceConstructor4.cpp
@@ -153,7 +153,10 @@
 }
 
 TestInterfaceConstructor4* NativeValueTraits<TestInterfaceConstructor4>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceConstructor4::toImplWithTypeCheck(isolate, value);
+  TestInterfaceConstructor4* nativeValue = V8TestInterfaceConstructor4::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceConstructor4.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
index 0143066..cabe2b4 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceCustomConstructor.cpp
@@ -101,7 +101,10 @@
 }
 
 TestInterfaceCustomConstructor* NativeValueTraits<TestInterfaceCustomConstructor>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceCustomConstructor::toImplWithTypeCheck(isolate, value);
+  TestInterfaceCustomConstructor* nativeValue = V8TestInterfaceCustomConstructor::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceCustomConstructor.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
index e9aaa0c4..715856f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceDocument.cpp
@@ -133,7 +133,10 @@
 }
 
 TestInterfaceDocument* NativeValueTraits<TestInterfaceDocument>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceDocument::toImplWithTypeCheck(isolate, value);
+  TestInterfaceDocument* nativeValue = V8TestInterfaceDocument::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceDocument.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
index 3c09298..7376770 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEmpty.cpp
@@ -84,7 +84,10 @@
 }
 
 TestInterfaceEmpty* NativeValueTraits<TestInterfaceEmpty>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceEmpty::toImplWithTypeCheck(isolate, value);
+  TestInterfaceEmpty* nativeValue = V8TestInterfaceEmpty::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceEmpty.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
index 596675e..d5a0573 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventInitConstructor.cpp
@@ -163,7 +163,10 @@
 }
 
 TestInterfaceEventInitConstructor* NativeValueTraits<TestInterfaceEventInitConstructor>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceEventInitConstructor::toImplWithTypeCheck(isolate, value);
+  TestInterfaceEventInitConstructor* nativeValue = V8TestInterfaceEventInitConstructor::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceEventInitConstructor.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
index 0cf77d3..a8d734ae 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceEventTarget.cpp
@@ -160,7 +160,10 @@
 }
 
 TestInterfaceEventTarget* NativeValueTraits<TestInterfaceEventTarget>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceEventTarget::toImplWithTypeCheck(isolate, value);
+  TestInterfaceEventTarget* nativeValue = V8TestInterfaceEventTarget::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceEventTarget.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
index ae0f2300..a461e7c 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceGarbageCollected.cpp
@@ -414,7 +414,10 @@
 }
 
 TestInterfaceGarbageCollected* NativeValueTraits<TestInterfaceGarbageCollected>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceGarbageCollected::toImplWithTypeCheck(isolate, value);
+  TestInterfaceGarbageCollected* nativeValue = V8TestInterfaceGarbageCollected::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceGarbageCollected.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
index 33d6f46b..44bd0bc 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor.cpp
@@ -236,7 +236,10 @@
 }
 
 TestInterfaceNamedConstructor* NativeValueTraits<TestInterfaceNamedConstructor>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceNamedConstructor::toImplWithTypeCheck(isolate, value);
+  TestInterfaceNamedConstructor* nativeValue = V8TestInterfaceNamedConstructor::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceNamedConstructor.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
index af24a78..b0f751f8 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNamedConstructor2.cpp
@@ -171,7 +171,10 @@
 }
 
 TestInterfaceNamedConstructor2* NativeValueTraits<TestInterfaceNamedConstructor2>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceNamedConstructor2::toImplWithTypeCheck(isolate, value);
+  TestInterfaceNamedConstructor2* nativeValue = V8TestInterfaceNamedConstructor2::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceNamedConstructor2.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
index 52511f3..4a96236 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceNode.cpp
@@ -400,7 +400,10 @@
 }
 
 TestInterfaceNode* NativeValueTraits<TestInterfaceNode>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceNode::toImplWithTypeCheck(isolate, value);
+  TestInterfaceNode* nativeValue = V8TestInterfaceNode::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceNode.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
index 95a45e0..0db3e10 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceOriginTrialEnabled.cpp
@@ -316,7 +316,10 @@
 }
 
 TestInterfaceOriginTrialEnabled* NativeValueTraits<TestInterfaceOriginTrialEnabled>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceOriginTrialEnabled::toImplWithTypeCheck(isolate, value);
+  TestInterfaceOriginTrialEnabled* nativeValue = V8TestInterfaceOriginTrialEnabled::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceOriginTrialEnabled.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
index 4c1f4dc1..4f89602 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestInterfaceSecureContext.cpp
@@ -381,7 +381,10 @@
 }
 
 TestInterfaceSecureContext* NativeValueTraits<TestInterfaceSecureContext>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterfaceSecureContext::toImplWithTypeCheck(isolate, value);
+  TestInterfaceSecureContext* nativeValue = V8TestInterfaceSecureContext::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterfaceSecureContext.");
+  return nativeValue;
 }
 
 void V8TestInterfaceSecureContext::preparePrototypeAndInterfaceObject(v8::Local<v8::Context> context, const DOMWrapperWorld& world, v8::Local<v8::Object> prototypeObject, v8::Local<v8::Function> interfaceObject, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
index 5ec366fc..d3001bf 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestNode.cpp
@@ -256,7 +256,10 @@
 }
 
 TestNode* NativeValueTraits<TestNode>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestNode::toImplWithTypeCheck(isolate, value);
+  TestNode* nativeValue = V8TestNode::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestNode.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
index 1d5d84bb4..83c57a5 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestObject.cpp
@@ -12294,7 +12294,10 @@
 }
 
 TestObject* NativeValueTraits<TestObject>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestObject::toImplWithTypeCheck(isolate, value);
+  TestObject* nativeValue = V8TestObject::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestObject.");
+  return nativeValue;
 }
 
 void V8TestObject::preparePrototypeAndInterfaceObject(v8::Local<v8::Context> context, const DOMWrapperWorld& world, v8::Local<v8::Object> prototypeObject, v8::Local<v8::Function> interfaceObject, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
index 9ba73767..318c2b0 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperations.cpp
@@ -213,7 +213,10 @@
 }
 
 TestSpecialOperations* NativeValueTraits<TestSpecialOperations>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestSpecialOperations::toImplWithTypeCheck(isolate, value);
+  TestSpecialOperations* nativeValue = V8TestSpecialOperations::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestSpecialOperations.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
index dd55044..502da94 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestSpecialOperationsNotEnumerable.cpp
@@ -127,7 +127,10 @@
 }
 
 TestSpecialOperationsNotEnumerable* NativeValueTraits<TestSpecialOperationsNotEnumerable>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestSpecialOperationsNotEnumerable::toImplWithTypeCheck(isolate, value);
+  TestSpecialOperationsNotEnumerable* nativeValue = V8TestSpecialOperationsNotEnumerable::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestSpecialOperationsNotEnumerable.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
index fafb6e3..bcc8cc7 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8TestTypedefs.cpp
@@ -531,7 +531,10 @@
 }
 
 TestTypedefs* NativeValueTraits<TestTypedefs>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestTypedefs::toImplWithTypeCheck(isolate, value);
+  TestTypedefs* nativeValue = V8TestTypedefs::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestTypedefs.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
index b7a9d71..8e46b47 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/V8Uint8ClampedArray.cpp
@@ -73,7 +73,10 @@
 }
 
 TestUint8ClampedArray* NativeValueTraits<TestUint8ClampedArray>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8Uint8ClampedArray::toImplWithTypeCheck(isolate, value);
+  TestUint8ClampedArray* nativeValue = V8Uint8ClampedArray::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to Uint8ClampedArray.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
index ab58b878..c577056a 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunction.cpp
@@ -79,7 +79,10 @@
 }
 
 VoidCallbackFunction* NativeValueTraits<VoidCallbackFunction>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return VoidCallbackFunction::create(ScriptState::current(isolate), value);
+  VoidCallbackFunction* nativeValue = VoidCallbackFunction::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to VoidCallbackFunction.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
index a0781ed9..c69f2c1 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionInterfaceArg.cpp
@@ -81,7 +81,10 @@
 }
 
 VoidCallbackFunctionInterfaceArg* NativeValueTraits<VoidCallbackFunctionInterfaceArg>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return VoidCallbackFunctionInterfaceArg::create(ScriptState::current(isolate), value);
+  VoidCallbackFunctionInterfaceArg* nativeValue = VoidCallbackFunctionInterfaceArg::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to VoidCallbackFunctionInterfaceArg.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
index 03c882e5..3901747 100644
--- a/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/core/VoidCallbackFunctionTypedef.cpp
@@ -82,7 +82,10 @@
 }
 
 VoidCallbackFunctionTypedef* NativeValueTraits<VoidCallbackFunctionTypedef>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return VoidCallbackFunctionTypedef::create(ScriptState::current(isolate), value);
+  VoidCallbackFunctionTypedef* nativeValue = VoidCallbackFunctionTypedef::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to VoidCallbackFunctionTypedef.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
index 52d41b5..91c2a43f 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/V8TestInterface5.cpp
@@ -878,7 +878,10 @@
 }
 
 TestInterface5Implementation* NativeValueTraits<TestInterface5Implementation>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return V8TestInterface5::toImplWithTypeCheck(isolate, value);
+  TestInterface5Implementation* nativeValue = V8TestInterface5::toImplWithTypeCheck(isolate, value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to TestInterface5.");
+  return nativeValue;
 }
 
 void V8TestInterface5::preparePrototypeAndInterfaceObject(v8::Local<v8::Context> context, const DOMWrapperWorld& world, v8::Local<v8::Object> prototypeObject, v8::Local<v8::Function> interfaceObject, v8::Local<v8::FunctionTemplate> interfaceTemplate) {
diff --git a/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp b/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
index 59b0fcd..1a41198 100644
--- a/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
+++ b/third_party/WebKit/Source/bindings/tests/results/modules/VoidCallbackFunctionModules.cpp
@@ -79,7 +79,10 @@
 }
 
 VoidCallbackFunctionModules* NativeValueTraits<VoidCallbackFunctionModules>::nativeValue(v8::Isolate* isolate, v8::Local<v8::Value> value, ExceptionState& exceptionState) {
-  return VoidCallbackFunctionModules::create(ScriptState::current(isolate), value);
+  VoidCallbackFunctionModules* nativeValue = VoidCallbackFunctionModules::create(ScriptState::current(isolate), value);
+  if (!nativeValue)
+    exceptionState.throwTypeError("Unable to convert value to VoidCallbackFunctionModules.");
+  return nativeValue;
 }
 
 }  // namespace blink
diff --git a/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp b/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
index e6d8576..5c13fc6 100644
--- a/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
+++ b/third_party/WebKit/Source/core/css/CSSStyleSheet.cpp
@@ -256,10 +256,10 @@
     return true;
   if (document->getSecurityOrigin()->canRequestNoSuborigin(baseURL))
     return true;
-  if (m_allowRuleAccessFromOrigin &&
-      document->getSecurityOrigin()->canAccessCheckSuborigins(
-          m_allowRuleAccessFromOrigin.get()))
+  if (m_allowRuleAccessFromOrigin && document->getSecurityOrigin()->canAccess(
+                                         m_allowRuleAccessFromOrigin.get())) {
     return true;
+  }
   return false;
 }
 
diff --git a/third_party/WebKit/Source/core/dom/Document.cpp b/third_party/WebKit/Source/core/dom/Document.cpp
index 2d23e78..e98e4b9 100644
--- a/third_party/WebKit/Source/core/dom/Document.cpp
+++ b/third_party/WebKit/Source/core/dom/Document.cpp
@@ -5047,9 +5047,6 @@
         UseCounter::count(
             *this,
             UseCounter::DocumentCompleteURLHTTPContainingNewlineAndLessThan);
-
-        if (RuntimeEnabledFeatures::restrictCompleteURLCharacterSetEnabled())
-          return KURL();
       }
     } else {
       UseCounter::count(
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
index 85b3719..51dc725 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
+++ b/third_party/WebKit/Source/core/editing/EditingUtilities.cpp
@@ -1834,6 +1834,13 @@
   return offset < textNode->length() && textNode->data()[offset] == '\n';
 }
 
+bool elementCannotHaveEndTag(const Node& node) {
+  if (!node.isHTMLElement())
+    return false;
+
+  return !toHTMLElement(node).shouldSerializeEndTag();
+}
+
 // Modifies selections that have an end point at the edge of a table
 // that contains the other endpoint so that they don't confuse
 // code that iterates over selected paragraphs.
diff --git a/third_party/WebKit/Source/core/editing/EditingUtilities.h b/third_party/WebKit/Source/core/editing/EditingUtilities.h
index 28fc78a..33419aa 100644
--- a/third_party/WebKit/Source/core/editing/EditingUtilities.h
+++ b/third_party/WebKit/Source/core/editing/EditingUtilities.h
@@ -379,6 +379,8 @@
 
 bool canMergeLists(Element* firstList, Element* secondList);
 
+bool elementCannotHaveEndTag(const Node&);
+
 // -------------------------------------------------------------------------
 // VisibleSelection
 // -------------------------------------------------------------------------
diff --git a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
index bb9b4a5b..74c4706 100644
--- a/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/MarkupAccumulator.cpp
@@ -37,6 +37,7 @@
 #include "core/dom/DocumentFragment.h"
 #include "core/dom/DocumentType.h"
 #include "core/dom/ProcessingInstruction.h"
+#include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/HTMLTemplateElement.h"
@@ -84,13 +85,6 @@
   }
 }
 
-static bool elementCannotHaveEndTag(const Node& node) {
-  if (!node.isHTMLElement())
-    return false;
-
-  return !toHTMLElement(node).shouldSerializeEndTag();
-}
-
 void MarkupAccumulator::appendEndMarkup(StringBuilder& result,
                                         const Element& element) {
   m_formatter.appendEndMarkup(result, element);
diff --git a/third_party/WebKit/Source/core/editing/serializers/MarkupFormatter.cpp b/third_party/WebKit/Source/core/editing/serializers/MarkupFormatter.cpp
index b75932dc..ea8f5cb 100644
--- a/third_party/WebKit/Source/core/editing/serializers/MarkupFormatter.cpp
+++ b/third_party/WebKit/Source/core/editing/serializers/MarkupFormatter.cpp
@@ -37,6 +37,7 @@
 #include "core/dom/DocumentFragment.h"
 #include "core/dom/DocumentType.h"
 #include "core/dom/ProcessingInstruction.h"
+#include "core/editing/EditingUtilities.h"
 #include "core/editing/Editor.h"
 #include "core/html/HTMLElement.h"
 #include "core/html/HTMLTemplateElement.h"
@@ -178,13 +179,6 @@
   }
 }
 
-static bool elementCannotHaveEndTag(const Node& node) {
-  if (!node.isHTMLElement())
-    return false;
-
-  return !toHTMLElement(node).shouldSerializeEndTag();
-}
-
 void MarkupFormatter::appendEndMarkup(StringBuilder& result,
                                       const Element& element) {
   if (shouldSelfClose(element) ||
diff --git a/third_party/WebKit/Source/core/frame/DOMWindow.cpp b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
index 831460a2..0507b7d1 100644
--- a/third_party/WebKit/Source/core/frame/DOMWindow.cpp
+++ b/third_party/WebKit/Source/core/frame/DOMWindow.cpp
@@ -137,9 +137,10 @@
     // FIXME: The name canAccess seems to be a roundabout way to ask "can
     // execute script".  Can we name the SecurityOrigin function better to make
     // this more clear?
-    if (callingWindow.document()->getSecurityOrigin()->canAccessCheckSuborigins(
-            frame()->securityContext()->getSecurityOrigin()))
+    if (callingWindow.document()->getSecurityOrigin()->canAccess(
+            frame()->securityContext()->getSecurityOrigin())) {
       return false;
+    }
   }
 
   callingWindow.printErrorMessage(
@@ -270,8 +271,7 @@
   // It's possible for a remote frame to be same origin with respect to a
   // local frame, but it must still be treated as a disallowed cross-domain
   // access. See https://crbug.com/601629.
-  ASSERT(frame()->isRemoteFrame() ||
-         !activeOrigin->canAccessCheckSuborigins(targetOrigin));
+  DCHECK(frame()->isRemoteFrame() || !activeOrigin->canAccess(targetOrigin));
 
   String message = "Blocked a frame with origin \"" + activeOrigin->toString() +
                    "\" from accessing a frame with origin \"" +
diff --git a/third_party/WebKit/Source/core/frame/Deprecation.cpp b/third_party/WebKit/Source/core/frame/Deprecation.cpp
index 5de1b12..90782d2 100644
--- a/third_party/WebKit/Source/core/frame/Deprecation.cpp
+++ b/third_party/WebKit/Source/core/frame/Deprecation.cpp
@@ -424,6 +424,16 @@
                                    "the 'script-src' directive for Workers",
                                    M60, "5922594955984896");
 
+    case UseCounter::CanRequestURLHTTPContainingNewline:
+      return String::format(
+          "Resource requests whose URLs contain raw newline characters are "
+          "deprecated, and may be blocked in %s. Please remove newlines from "
+          "places like element attribute values in order to continue loading "
+          "those resources. See "
+          "https://www.chromestatus.com/features/5735596811091968 for more "
+          "details.",
+          milestoneString(M60));
+
     // Features that aren't deprecated don't have a deprecation message.
     default:
       return String();
diff --git a/third_party/WebKit/Source/core/frame/UseCounter.h b/third_party/WebKit/Source/core/frame/UseCounter.h
index 63783f9..c9cc3b5 100644
--- a/third_party/WebKit/Source/core/frame/UseCounter.h
+++ b/third_party/WebKit/Source/core/frame/UseCounter.h
@@ -1527,6 +1527,8 @@
     MenuItemElementIconAttribute = 1911,
     WebkitCSSMatrixSetMatrixValue = 1912,
     WebkitCSSMatrixConstructFromString = 1913,
+    CanRequestURLHTTPContainingNewline = 1914,
+    CanRequestURLNonHTTPContainingNewline = 1915,
 
     // Add new features immediately above this line. Don't change assigned
     // numbers of any item, and don't reuse removed slots.
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
index 6afd03c..c95f01f 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.cpp
@@ -45,6 +45,36 @@
   return isASCIIAlphanumeric(c) || c == '-';
 }
 
+ContentSecurityPolicyHashAlgorithm convertHashAlgorithmToCSPHashAlgorithm(
+    HashAlgorithm algorithm) {
+  switch (algorithm) {
+    case HashAlgorithmSha1:
+      // Sha1 is not supported.
+      return ContentSecurityPolicyHashAlgorithmNone;
+    case HashAlgorithmSha256:
+      return ContentSecurityPolicyHashAlgorithmSha256;
+    case HashAlgorithmSha384:
+      return ContentSecurityPolicyHashAlgorithmSha384;
+    case HashAlgorithmSha512:
+      return ContentSecurityPolicyHashAlgorithmSha512;
+  }
+  NOTREACHED();
+  return ContentSecurityPolicyHashAlgorithmNone;
+}
+
+// IntegrityMetadata (from SRI) has base64-encoded digest values, but CSP uses
+// binary format. This converts from the former to the latter.
+bool parseBase64Digest(String base64, DigestValue& hash) {
+  Vector<char> hashVector;
+  // We accept base64url-encoded data here by normalizing it to base64.
+  if (!base64Decode(normalizeToBase64(base64), hashVector))
+    return false;
+  if (hashVector.isEmpty() || hashVector.size() > kMaxDigestSize)
+    return false;
+  hash.append(reinterpret_cast<uint8_t*>(hashVector.data()), hashVector.size());
+  return true;
+}
+
 }  // namespace
 
 CSPDirectiveList::CSPDirectiveList(ContentSecurityPolicy* policy,
@@ -178,6 +208,24 @@
   return directive && directive->allowNonce(nonce);
 }
 
+bool CSPDirectiveList::areAllMatchingHashesPresent(
+    SourceListDirective* directive,
+    const IntegrityMetadataSet& hashes) const {
+  if (!directive || hashes.isEmpty())
+    return false;
+  for (const std::pair<WTF::String, HashAlgorithm>& hash : hashes) {
+    // Convert the hash from integrity metadata format to CSP format.
+    CSPHashValue cspHash;
+    cspHash.first = convertHashAlgorithmToCSPHashAlgorithm(hash.second);
+    if (!parseBase64Digest(hash.first, cspHash.second))
+      return false;
+    // All integrity hashes must be listed in the CSP.
+    if (!directive->allowHash(cspHash))
+      return false;
+  }
+  return true;
+}
+
 bool CSPDirectiveList::checkHash(SourceListDirective* directive,
                                  const CSPHashValue& hashValue) const {
   return !directive || directive->allowHash(hashValue);
@@ -633,20 +681,23 @@
 bool CSPDirectiveList::allowScriptFromSource(
     const KURL& url,
     const String& nonce,
+    const IntegrityMetadataSet& hashes,
     ParserDisposition parserDisposition,
     ResourceRequest::RedirectStatus redirectStatus,
     SecurityViolationReportingPolicy reportingPolicy) const {
-  if (isMatchingNoncePresent(operativeDirective(m_scriptSrc.get()), nonce))
+  SourceListDirective* directive = operativeDirective(m_scriptSrc.get());
+  if (isMatchingNoncePresent(directive, nonce))
     return true;
   if (parserDisposition == NotParserInserted && allowDynamic())
     return true;
+  if (areAllMatchingHashesPresent(directive, hashes))
+    return true;
   return reportingPolicy == SecurityViolationReportingPolicy::Report
              ? checkSourceAndReportViolation(
-                   operativeDirective(m_scriptSrc.get()), url,
+                   directive, url,
                    ContentSecurityPolicy::DirectiveType::ScriptSrc,
                    redirectStatus)
-             : checkSource(operativeDirective(m_scriptSrc.get()), url,
-                           redirectStatus);
+             : checkSource(directive, url, redirectStatus);
 }
 
 bool CSPDirectiveList::allowObjectFromSource(
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
index d1a122b..e61167a 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveList.h
@@ -78,6 +78,7 @@
 
   bool allowScriptFromSource(const KURL&,
                              const String& nonce,
+                             const IntegrityMetadataSet& hashes,
                              ParserDisposition,
                              ResourceRequest::RedirectStatus,
                              SecurityViolationReportingPolicy) const;
@@ -242,6 +243,8 @@
   bool checkEval(SourceListDirective*) const;
   bool checkDynamic(SourceListDirective*) const;
   bool isMatchingNoncePresent(SourceListDirective*, const String&) const;
+  bool areAllMatchingHashesPresent(SourceListDirective*,
+                                   const IntegrityMetadataSet&) const;
   bool checkHash(SourceListDirective*, const CSPHashValue&) const;
   bool checkHashedAttributes(SourceListDirective*) const;
   bool checkSource(SourceListDirective*,
diff --git a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
index 86f819e..5546fc54 100644
--- a/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/CSPDirectiveListTest.cpp
@@ -4,6 +4,7 @@
 
 #include "core/frame/csp/CSPDirectiveList.h"
 
+#include "core/frame/SubresourceIntegrity.h"
 #include "core/frame/csp/ContentSecurityPolicy.h"
 #include "core/frame/csp/SourceListDirective.h"
 #include "platform/loader/fetch/ResourceRequest.h"
@@ -165,7 +166,7 @@
         createList(test.list, ContentSecurityPolicyHeaderTypeReport);
     EXPECT_EQ(test.expected,
               directiveList->allowScriptFromSource(
-                  scriptSrc, String(), ParserInserted,
+                  scriptSrc, String(), IntegrityMetadataSet(), ParserInserted,
                   ResourceRequest::RedirectStatus::NoRedirect,
                   SecurityViolationReportingPolicy::SuppressReporting));
 
@@ -174,7 +175,7 @@
         createList(test.list, ContentSecurityPolicyHeaderTypeEnforce);
     EXPECT_EQ(test.expected,
               directiveList->allowScriptFromSource(
-                  scriptSrc, String(), ParserInserted,
+                  scriptSrc, String(), IntegrityMetadataSet(), ParserInserted,
                   ResourceRequest::RedirectStatus::NoRedirect,
                   SecurityViolationReportingPolicy::SuppressReporting));
   }
@@ -222,7 +223,150 @@
                    ContentSecurityPolicyHeaderTypeReport);
     EXPECT_EQ(test.expected,
               directiveList->allowScriptFromSource(
-                  resource, String(test.nonce), ParserInserted,
+                  resource, String(test.nonce), IntegrityMetadataSet(),
+                  ParserInserted, ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+
+    // Enforce 'script-src'
+    directiveList = createList(String("script-src ") + test.list,
+                               ContentSecurityPolicyHeaderTypeEnforce);
+    EXPECT_EQ(test.expected,
+              directiveList->allowScriptFromSource(
+                  resource, String(test.nonce), IntegrityMetadataSet(),
+                  ParserInserted, ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+
+    // Report-only 'style-src'
+    directiveList = createList(String("style-src ") + test.list,
+                               ContentSecurityPolicyHeaderTypeReport);
+    EXPECT_EQ(test.expected,
+              directiveList->allowStyleFromSource(
+                  resource, String(test.nonce),
+                  ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+
+    // Enforce 'style-src'
+    directiveList = createList(String("style-src ") + test.list,
+                               ContentSecurityPolicyHeaderTypeEnforce);
+    EXPECT_EQ(test.expected,
+              directiveList->allowStyleFromSource(
+                  resource, String(test.nonce),
+                  ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+
+    // Report-only 'style-src'
+    directiveList = createList(String("default-src ") + test.list,
+                               ContentSecurityPolicyHeaderTypeReport);
+    EXPECT_EQ(test.expected,
+              directiveList->allowScriptFromSource(
+                  resource, String(test.nonce), IntegrityMetadataSet(),
+                  ParserInserted, ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+    EXPECT_EQ(test.expected,
+              directiveList->allowStyleFromSource(
+                  resource, String(test.nonce),
+                  ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+
+    // Enforce 'style-src'
+    directiveList = createList(String("default-src ") + test.list,
+                               ContentSecurityPolicyHeaderTypeEnforce);
+    EXPECT_EQ(test.expected,
+              directiveList->allowScriptFromSource(
+                  resource, String(test.nonce), IntegrityMetadataSet(),
+                  ParserInserted, ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+    EXPECT_EQ(test.expected,
+              directiveList->allowStyleFromSource(
+                  resource, String(test.nonce),
+                  ResourceRequest::RedirectStatus::NoRedirect,
+                  SecurityViolationReportingPolicy::SuppressReporting));
+  }
+}
+
+TEST_F(CSPDirectiveListTest, AllowScriptFromSourceWithHash) {
+  struct TestCase {
+    const char* list;
+    const char* url;
+    const char* integrity;
+    bool expected;
+  } cases[] = {
+      // Doesn't affect lists without hashes.
+      {"https://example.com", "https://example.com/file", "sha256-yay", true},
+      {"https://example.com", "https://example.com/file", "sha256-boo", true},
+      {"https://example.com", "https://example.com/file", "", true},
+      {"https://example.com", "https://not.example.com/file", "sha256-yay",
+       false},
+      {"https://example.com", "https://not.example.com/file", "sha256-boo",
+       false},
+      {"https://example.com", "https://not.example.com/file", "", false},
+
+      // Doesn't affect URLs that match the whitelist.
+      {"https://example.com 'sha256-yay'", "https://example.com/file",
+       "sha256-yay", true},
+      {"https://example.com 'sha256-yay'", "https://example.com/file",
+       "sha256-boo", true},
+      {"https://example.com 'sha256-yay'", "https://example.com/file", "",
+       true},
+
+      // Does affect URLs that don't match the whitelist.
+      {"https://example.com 'sha256-yay'", "https://not.example.com/file",
+       "sha256-yay", true},
+      {"https://example.com 'sha256-yay'", "https://not.example.com/file",
+       "sha256-boo", false},
+      {"https://example.com 'sha256-yay'", "https://not.example.com/file", "",
+       false},
+
+      // Both algorithm and digest must match.
+      {"'sha256-yay'", "https://a.com/file", "sha384-yay", false},
+
+      // Sha-1 is not supported, but -384 and -512 are.
+      {"'sha1-yay'", "https://a.com/file", "sha1-yay", false},
+      {"'sha384-yay'", "https://a.com/file", "sha384-yay", true},
+      {"'sha512-yay'", "https://a.com/file", "sha512-yay", true},
+
+      // Unknown (or future) hash algorithms don't work.
+      {"'asdf256-yay'", "https://a.com/file", "asdf256-yay", false},
+
+      // But they also don't interfere.
+      {"'sha256-yay'", "https://a.com/file", "sha256-yay asdf256-boo", true},
+
+      // Additional whitelisted hashes in the CSP don't interfere.
+      {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha256-yay", true},
+      {"'sha256-yay' 'sha384-boo'", "https://a.com/file", "sha384-boo", true},
+
+      // All integrity hashes must appear in the CSP (and match).
+      {"'sha256-yay'", "https://a.com/file", "sha256-yay sha384-boo", false},
+      {"'sha384-boo'", "https://a.com/file", "sha256-yay sha384-boo", false},
+      {"'sha256-yay' 'sha384-boo'", "https://a.com/file",
+       "sha256-yay sha384-yay", false},
+      {"'sha256-yay' 'sha384-boo'", "https://a.com/file",
+       "sha256-boo sha384-boo", false},
+      {"'sha256-yay' 'sha384-boo'", "https://a.com/file",
+       "sha256-yay sha384-boo", true},
+
+      // At least one integrity hash must be present.
+      {"'sha256-yay'", "https://a.com/file", "", false},
+  };
+
+  for (const auto& test : cases) {
+    SCOPED_TRACE(testing::Message()
+                 << "List: `" << test.list << "`, URL: `" << test.url
+                 << "`, Integrity: `" << test.integrity << "`");
+    KURL resource = KURL(KURL(), test.url);
+
+    IntegrityMetadataSet integrityMetadata;
+    ASSERT_EQ(SubresourceIntegrity::IntegrityParseValidResult,
+              SubresourceIntegrity::parseIntegrityAttribute(test.integrity,
+                                                            integrityMetadata));
+
+    // Report-only 'script-src'
+    Member<CSPDirectiveList> directiveList =
+        createList(String("script-src ") + test.list,
+                   ContentSecurityPolicyHeaderTypeReport);
+    EXPECT_EQ(test.expected,
+              directiveList->allowScriptFromSource(
+                  resource, String(), integrityMetadata, ParserInserted,
                   ResourceRequest::RedirectStatus::NoRedirect,
                   SecurityViolationReportingPolicy::SuppressReporting));
 
@@ -231,53 +375,7 @@
                                ContentSecurityPolicyHeaderTypeEnforce);
     EXPECT_EQ(test.expected,
               directiveList->allowScriptFromSource(
-                  resource, String(test.nonce), ParserInserted,
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-
-    // Report-only 'style-src'
-    directiveList = createList(String("style-src ") + test.list,
-                               ContentSecurityPolicyHeaderTypeReport);
-    EXPECT_EQ(test.expected,
-              directiveList->allowStyleFromSource(
-                  resource, String(test.nonce),
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-
-    // Enforce 'style-src'
-    directiveList = createList(String("style-src ") + test.list,
-                               ContentSecurityPolicyHeaderTypeEnforce);
-    EXPECT_EQ(test.expected,
-              directiveList->allowStyleFromSource(
-                  resource, String(test.nonce),
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-
-    // Report-only 'style-src'
-    directiveList = createList(String("default-src ") + test.list,
-                               ContentSecurityPolicyHeaderTypeReport);
-    EXPECT_EQ(test.expected,
-              directiveList->allowScriptFromSource(
-                  resource, String(test.nonce), ParserInserted,
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-    EXPECT_EQ(test.expected,
-              directiveList->allowStyleFromSource(
-                  resource, String(test.nonce),
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-
-    // Enforce 'style-src'
-    directiveList = createList(String("default-src ") + test.list,
-                               ContentSecurityPolicyHeaderTypeEnforce);
-    EXPECT_EQ(test.expected,
-              directiveList->allowScriptFromSource(
-                  resource, String(test.nonce), ParserInserted,
-                  ResourceRequest::RedirectStatus::NoRedirect,
-                  SecurityViolationReportingPolicy::SuppressReporting));
-    EXPECT_EQ(test.expected,
-              directiveList->allowStyleFromSource(
-                  resource, String(test.nonce),
+                  resource, String(), integrityMetadata, ParserInserted,
                   ResourceRequest::RedirectStatus::NoRedirect,
                   SecurityViolationReportingPolicy::SuppressReporting));
   }
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
index afbe2531..4a391d9 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.cpp
@@ -495,12 +495,14 @@
 template <bool (CSPDirectiveList::*allowFromURLWithNonceAndParser)(
     const KURL&,
     const String& nonce,
+    const IntegrityMetadataSet& hashes,
     ParserDisposition parserDisposition,
     RedirectStatus,
     SecurityViolationReportingPolicy) const>
 bool isAllowedByAll(const CSPDirectiveListVector& policies,
                     const KURL& url,
                     const String& nonce,
+                    const IntegrityMetadataSet& hashes,
                     ParserDisposition parserDisposition,
                     RedirectStatus redirectStatus,
                     SecurityViolationReportingPolicy reportingPolicy) {
@@ -521,7 +523,7 @@
   bool isAllowed = true;
   for (const auto& policy : policies) {
     isAllowed &= (policy.get()->*allowFromURLWithNonceAndParser)(
-        url, nonce, parserDisposition, redirectStatus, reportingPolicy);
+        url, nonce, hashes, parserDisposition, redirectStatus, reportingPolicy);
   }
   return isAllowed;
 }
@@ -698,6 +700,7 @@
 bool ContentSecurityPolicy::allowScriptFromSource(
     const KURL& url,
     const String& nonce,
+    const IntegrityMetadataSet& hashes,
     ParserDisposition parserDisposition,
     RedirectStatus redirectStatus,
     SecurityViolationReportingPolicy reportingPolicy) const {
@@ -709,7 +712,7 @@
             : UseCounter::ScriptWithCSPBypassingSchemeNotParserInserted);
   }
   return isAllowedByAll<&CSPDirectiveList::allowScriptFromSource>(
-      m_policies, url, nonce, parserDisposition, redirectStatus,
+      m_policies, url, nonce, hashes, parserDisposition, redirectStatus,
       reportingPolicy);
 }
 
@@ -779,8 +782,9 @@
     case WebURLRequest::RequestContextImport:
     case WebURLRequest::RequestContextScript:
     case WebURLRequest::RequestContextXSLT:
-      return allowScriptFromSource(url, nonce, parserDisposition,
-                                   redirectStatus, reportingPolicy);
+      return allowScriptFromSource(url, nonce, integrityMetadata,
+                                   parserDisposition, redirectStatus,
+                                   reportingPolicy);
     case WebURLRequest::RequestContextManifest:
       return allowManifestFromSource(url, redirectStatus, reportingPolicy);
     case WebURLRequest::RequestContextServiceWorker:
@@ -901,7 +905,8 @@
             m_policies, url, redirectStatus,
             SecurityViolationReportingPolicy::SuppressReporting) &&
         !isAllowedByAll<&CSPDirectiveList::allowScriptFromSource>(
-            m_policies, url, AtomicString(), NotParserInserted, redirectStatus,
+            m_policies, url, AtomicString(), IntegrityMetadataSet(),
+            NotParserInserted, redirectStatus,
             SecurityViolationReportingPolicy::SuppressReporting)) {
       UseCounter::count(*document,
                         UseCounter::WorkerAllowedByChildBlockedByScript);
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
index 84ad9bb..b45692a 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicy.h
@@ -228,6 +228,7 @@
   bool allowScriptFromSource(
       const KURL&,
       const String& nonce,
+      const IntegrityMetadataSet& hashes,
       ParserDisposition,
       RedirectStatus = RedirectStatus::NoRedirect,
       SecurityViolationReportingPolicy =
diff --git a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyTest.cpp b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyTest.cpp
index ebb18ea..ca597451 100644
--- a/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyTest.cpp
+++ b/third_party/WebKit/Source/core/frame/csp/ContentSecurityPolicyTest.cpp
@@ -130,7 +130,7 @@
   ContentSecurityPolicy* csp2 = ContentSecurityPolicy::create();
   csp2->copyStateFrom(csp.get());
   EXPECT_FALSE(csp2->allowScriptFromSource(
-      exampleUrl, String(), ParserInserted,
+      exampleUrl, String(), IntegrityMetadataSet(), ParserInserted,
       ResourceRequest::RedirectStatus::NoRedirect,
       SecurityViolationReportingPolicy::SuppressReporting));
   EXPECT_TRUE(csp2->allowPluginType(
@@ -161,7 +161,7 @@
   ContentSecurityPolicy* csp2 = ContentSecurityPolicy::create();
   csp2->copyPluginTypesFrom(csp.get());
   EXPECT_TRUE(csp2->allowScriptFromSource(
-      exampleUrl, String(), ParserInserted,
+      exampleUrl, String(), IntegrityMetadataSet(), ParserInserted,
       ResourceRequest::RedirectStatus::NoRedirect,
       SecurityViolationReportingPolicy::SuppressReporting));
   EXPECT_TRUE(csp2->allowPluginType(
@@ -662,7 +662,8 @@
                              ContentSecurityPolicyHeaderTypeEnforce,
                              ContentSecurityPolicyHeaderSourceHTTP);
     EXPECT_EQ(test.allowed, policy->allowScriptFromSource(
-                                resource, String(test.nonce), ParserInserted));
+                                resource, String(test.nonce),
+                                IntegrityMetadataSet(), ParserInserted));
     // If this is expected to generate a violation, we should have sent a
     // report.
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
@@ -672,8 +673,8 @@
     policy->bindToExecutionContext(document.get());
     policy->didReceiveHeader(test.policy, ContentSecurityPolicyHeaderTypeReport,
                              ContentSecurityPolicyHeaderSourceHTTP);
-    EXPECT_TRUE(policy->allowScriptFromSource(resource, String(test.nonce),
-                                              ParserInserted));
+    EXPECT_TRUE(policy->allowScriptFromSource(
+        resource, String(test.nonce), IntegrityMetadataSet(), ParserInserted));
     // If this is expected to generate a violation, we should have sent a
     // report, even though we don't deny access in `allowScriptFromSource`:
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
@@ -815,7 +816,8 @@
                              ContentSecurityPolicyHeaderTypeReport,
                              ContentSecurityPolicyHeaderSourceHTTP);
     EXPECT_EQ(test.allowed1, policy->allowScriptFromSource(
-                                 resource, String(test.nonce), ParserInserted));
+                                 resource, String(test.nonce),
+                                 IntegrityMetadataSet(), ParserInserted));
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
 
     // Report / Enforce
@@ -828,7 +830,8 @@
                              ContentSecurityPolicyHeaderTypeEnforce,
                              ContentSecurityPolicyHeaderSourceHTTP);
     EXPECT_EQ(test.allowed2, policy->allowScriptFromSource(
-                                 resource, String(test.nonce), ParserInserted));
+                                 resource, String(test.nonce),
+                                 IntegrityMetadataSet(), ParserInserted));
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
 
     // Enforce / Enforce
@@ -840,9 +843,10 @@
     policy->didReceiveHeader(test.policy2,
                              ContentSecurityPolicyHeaderTypeEnforce,
                              ContentSecurityPolicyHeaderSourceHTTP);
-    EXPECT_EQ(test.allowed1 && test.allowed2,
-              policy->allowScriptFromSource(resource, String(test.nonce),
-                                            ParserInserted));
+    EXPECT_EQ(
+        test.allowed1 && test.allowed2,
+        policy->allowScriptFromSource(resource, String(test.nonce),
+                                      IntegrityMetadataSet(), ParserInserted));
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
 
     // Report / Report
@@ -854,8 +858,8 @@
     policy->didReceiveHeader(test.policy2,
                              ContentSecurityPolicyHeaderTypeReport,
                              ContentSecurityPolicyHeaderSourceHTTP);
-    EXPECT_TRUE(policy->allowScriptFromSource(resource, String(test.nonce),
-                                              ParserInserted));
+    EXPECT_TRUE(policy->allowScriptFromSource(
+        resource, String(test.nonce), IntegrityMetadataSet(), ParserInserted));
     EXPECT_EQ(expectedReports, policy->m_violationReportsSent.size());
   }
 }
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 56a560a..3222aeb8 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -1246,6 +1246,7 @@
         createAcceleratedImageBufferSurface(opacityMode, &msaaSampleCount);
     if (surface) {
       buffer()->setSurface(std::move(surface));
+      setNeedsCompositingUpdate();
     }
   }
 }
diff --git a/third_party/WebKit/Source/core/html/media/MediaControls.h b/third_party/WebKit/Source/core/html/media/MediaControls.h
index d22feef7..2066f74e2 100644
--- a/third_party/WebKit/Source/core/html/media/MediaControls.h
+++ b/third_party/WebKit/Source/core/html/media/MediaControls.h
@@ -93,6 +93,7 @@
   virtual void onLoadedMetadata() = 0;
   virtual void onEnteredFullscreen() = 0;
   virtual void onExitedFullscreen() = 0;
+  virtual void onPanelKeypress() = 0;
   virtual void beginScrubbing() = 0;
   virtual void endScrubbing() = 0;
   virtual void updateCurrentTimeDisplay() = 0;
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
index d49d069c..6f94865 100644
--- a/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
+++ b/third_party/WebKit/Source/core/html/shadow/MediaControlsMediaEventListener.cpp
@@ -7,6 +7,7 @@
 #include "core/events/Event.h"
 #include "core/html/HTMLMediaElement.h"
 #include "core/html/media/MediaControls.h"
+#include "core/html/shadow/MediaControlElements.h"
 #include "core/html/track/TextTrackList.h"
 
 namespace blink {
@@ -43,6 +44,12 @@
   textTracks->addEventListener(EventTypeNames::addtrack, this, false);
   textTracks->addEventListener(EventTypeNames::change, this, false);
   textTracks->addEventListener(EventTypeNames::removetrack, this, false);
+
+  // Keypress events.
+  if (m_mediaControls->panelElement()) {
+    m_mediaControls->panelElement()->addEventListener(EventTypeNames::keypress,
+                                                      this, false);
+  }
 }
 
 void MediaControlsMediaEventListener::detach() {
@@ -55,6 +62,11 @@
   textTracks->removeEventListener(EventTypeNames::addtrack, this, false);
   textTracks->removeEventListener(EventTypeNames::change, this, false);
   textTracks->removeEventListener(EventTypeNames::removetrack, this, false);
+
+  if (m_mediaControls->panelElement()) {
+    m_mediaControls->panelElement()->removeEventListener(
+        EventTypeNames::keypress, this, false);
+  }
 }
 
 bool MediaControlsMediaEventListener::operator==(
@@ -127,6 +139,13 @@
     return;
   }
 
+  // Keypress events.
+  if (event->type() == EventTypeNames::keypress) {
+    if (event->currentTarget() == m_mediaControls->panelElement())
+      m_mediaControls->onPanelKeypress();
+    return;
+  }
+
   NOTREACHED();
 }
 
diff --git a/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.cpp b/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.cpp
index 2d251c6..41292c6 100644
--- a/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.cpp
+++ b/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.cpp
@@ -88,10 +88,11 @@
   void layoutGridItemForMinSizeComputation(
       LayoutBox&,
       bool overrideSizeHasChanged) const override;
-  void maximizeTracks(Vector<GridTrack>&, LayoutUnit& freeSpace) override;
+  void maximizeTracks(Vector<GridTrack>&,
+                      Optional<LayoutUnit>& freeSpace) override;
   double findUsedFlexFraction(Vector<size_t>& flexibleSizedTracksIndex,
                               GridTrackSizingDirection,
-                              LayoutUnit freeSpace) const override;
+                              Optional<LayoutUnit> freeSpace) const override;
   bool recomputeUsedFlexFractionIfNeeded(
       Vector<size_t>& flexibleSizedTracksIndex,
       double& flexFraction,
@@ -111,10 +112,11 @@
   void layoutGridItemForMinSizeComputation(
       LayoutBox&,
       bool overrideSizeHasChanged) const override;
-  void maximizeTracks(Vector<GridTrack>&, LayoutUnit& freeSpace) override;
+  void maximizeTracks(Vector<GridTrack>&,
+                      Optional<LayoutUnit>& freeSpace) override;
   double findUsedFlexFraction(Vector<size_t>& flexibleSizedTracksIndex,
                               GridTrackSizingDirection,
-                              LayoutUnit freeSpace) const override;
+                              Optional<LayoutUnit> freeSpace) const override;
   bool recomputeUsedFlexFractionIfNeeded(
       Vector<size_t>& flexibleSizedTracksIndex,
       double& flexFraction,
@@ -446,7 +448,7 @@
 }
 
 void DefiniteSizeStrategy::maximizeTracks(Vector<GridTrack>& tracks,
-                                          LayoutUnit& freeSpace) {
+                                          Optional<LayoutUnit>& freeSpace) {
   size_t tracksSize = tracks.size();
   Vector<GridTrack*> tracksForDistribution(tracksSize);
   for (size_t i = 0; i < tracksSize; ++i) {
@@ -455,7 +457,8 @@
         tracksForDistribution[i]->baseSize());
   }
 
-  distributeSpaceToTracks(tracksForDistribution, freeSpace);
+  DCHECK(freeSpace);
+  distributeSpaceToTracks(tracksForDistribution, freeSpace.value());
 
   for (auto* track : tracksForDistribution)
     track->setBaseSize(track->plannedSize());
@@ -464,10 +467,11 @@
 double DefiniteSizeStrategy::findUsedFlexFraction(
     Vector<size_t>& flexibleSizedTracksIndex,
     GridTrackSizingDirection direction,
-    LayoutUnit freeSpace) const {
+    Optional<LayoutUnit> freeSpace) const {
   GridSpan allTracksSpan = GridSpan::translatedDefiniteGridSpan(
       0, m_algorithm.tracks(direction).size());
-  return findFrUnitSize(allTracksSpan, freeSpace);
+  DCHECK(freeSpace);
+  return findFrUnitSize(allTracksSpan, freeSpace.value());
 }
 
 LayoutUnit IndefiniteSizeStrategy::minLogicalWidthForChild(
@@ -494,7 +498,7 @@
 }
 
 void IndefiniteSizeStrategy::maximizeTracks(Vector<GridTrack>& tracks,
-                                            LayoutUnit&) {
+                                            Optional<LayoutUnit>&) {
   for (auto& track : tracks)
     track.setBaseSize(track.growthLimit());
 }
@@ -507,7 +511,7 @@
 double IndefiniteSizeStrategy::findUsedFlexFraction(
     Vector<size_t>& flexibleSizedTracksIndex,
     GridTrackSizingDirection direction,
-    LayoutUnit freeSpace) const {
+    Optional<LayoutUnit>) const {
   auto allTracks = m_algorithm.tracks(direction);
 
   double flexFraction = 0;
@@ -580,7 +584,7 @@
   return true;
 }
 
-LayoutUnit& GridTrackSizingAlgorithm::freeSpace(
+Optional<LayoutUnit> GridTrackSizingAlgorithm::freeSpace(
     GridTrackSizingDirection direction) {
   return direction == ForRows ? m_freeSpaceRows : m_freeSpaceColumns;
 }
@@ -595,6 +599,14 @@
   return direction == ForColumns ? m_columns : m_rows;
 }
 
+void GridTrackSizingAlgorithm::setFreeSpace(GridTrackSizingDirection direction,
+                                            Optional<LayoutUnit> freeSpace) {
+  if (direction == ForColumns)
+    m_freeSpaceColumns = freeSpace;
+  else
+    m_freeSpaceRows = freeSpace;
+}
+
 GridTrackSize GridTrackSizingAlgorithm::rawGridTrackSize(
     GridTrackSizingDirection direction,
     size_t translatedIndex) const {
@@ -701,7 +713,7 @@
 
   const Length& trackLength = gridLength.length();
   if (trackLength.isSpecified())
-    return valueForLength(trackLength, m_availableSpace.clampNegativeToZero());
+    return valueForLength(trackLength, m_availableSpace.value_or(LayoutUnit()));
 
   DCHECK(trackLength.isMinContent() || trackLength.isAuto() ||
          trackLength.isMaxContent());
@@ -717,7 +729,7 @@
 
   const Length& trackLength = gridLength.length();
   if (trackLength.isSpecified())
-    return valueForLength(trackLength, m_availableSpace.clampNegativeToZero());
+    return valueForLength(trackLength, m_availableSpace.value_or(LayoutUnit()));
 
   DCHECK(trackLength.isMinContent() || trackLength.isAuto() ||
          trackLength.isMaxContent());
@@ -728,7 +740,7 @@
   DCHECK(m_contentSizedTracksIndex.isEmpty());
   DCHECK(m_flexibleSizedTracksIndex.isEmpty());
   Vector<GridTrack>& trackList = tracks(m_direction);
-  bool hasDefiniteFreeSpace = m_sizingOperation == TrackSizing;
+  bool hasDefiniteFreeSpace = !!m_availableSpace;
   size_t numTracks = trackList.size();
   for (size_t i = 0; i < numTracks; ++i) {
     GridTrackSize trackSize = gridTrackSize(m_direction, i);
@@ -741,7 +753,7 @@
       GridLength gridLength = trackSize.fitContentTrackBreadth();
       if (!gridLength.hasPercentage() || hasDefiniteFreeSpace) {
         track.setGrowthLimitCap(valueForLength(
-            gridLength.length(), m_availableSpace.clampNegativeToZero()));
+            gridLength.length(), m_availableSpace.value_or(LayoutUnit())));
       }
     }
 
@@ -779,7 +791,7 @@
       growthLimit =
           std::min(growthLimit,
                    valueForLength(trackSize.fitContentTrackBreadth().length(),
-                                  m_availableSpace));
+                                  m_availableSpace.value_or(LayoutUnit())));
     }
     track.setGrowthLimit(std::max(track.growthLimit(), growthLimit));
   }
@@ -1296,7 +1308,8 @@
   }
 }
 
-void GridTrackSizingAlgorithm::stretchFlexibleTracks(LayoutUnit freeSpace) {
+void GridTrackSizingAlgorithm::stretchFlexibleTracks(
+    Optional<LayoutUnit> freeSpace) {
   double flexFraction = m_strategy->findUsedFlexFraction(
       m_flexibleSizedTracksIndex, m_direction, freeSpace);
 
@@ -1318,7 +1331,10 @@
     if (LayoutUnit increment = increments[i++])
       track.setBaseSize(track.baseSize() + increment);
   }
-  this->freeSpace(m_direction) -= totalGrowth;
+  if (this->freeSpace(m_direction)) {
+    setFreeSpace(m_direction,
+                 this->freeSpace(m_direction).value() - totalGrowth);
+  }
   m_maxContentSize += totalGrowth;
 }
 
@@ -1357,26 +1373,26 @@
 void GridTrackSizingAlgorithm::setup(GridTrackSizingDirection direction,
                                      size_t numTracks,
                                      SizingOperation sizingOperation,
-                                     LayoutUnit availableSpace,
-                                     LayoutUnit freeSpace) {
+                                     Optional<LayoutUnit> availableSpace,
+                                     Optional<LayoutUnit> freeSpace) {
   DCHECK(m_needsSetup);
+  DCHECK_EQ(!!availableSpace, !!freeSpace);
   m_direction = direction;
-  m_availableSpace = availableSpace;
+  m_availableSpace = availableSpace
+                         ? availableSpace.value().clampNegativeToZero()
+                         : availableSpace;
 
   m_sizingOperation = sizingOperation;
-  switch (m_sizingOperation) {
-    case IntrinsicSizeComputation:
-      m_strategy = WTF::makeUnique<IndefiniteSizeStrategy>(*this);
-      break;
-    case TrackSizing:
-      m_strategy = WTF::makeUnique<DefiniteSizeStrategy>(*this);
-      break;
-  }
+
+  if (availableSpace)
+    m_strategy = WTF::makeUnique<DefiniteSizeStrategy>(*this);
+  else
+    m_strategy = WTF::makeUnique<IndefiniteSizeStrategy>(*this);
 
   m_contentSizedTracksIndex.shrink(0);
   m_flexibleSizedTracksIndex.shrink(0);
 
-  this->freeSpace(direction) = freeSpace;
+  setFreeSpace(direction, freeSpace);
   tracks(direction).resize(numTracks);
 
   m_needsSetup = false;
@@ -1387,7 +1403,7 @@
   StateMachine stateMachine(*this);
 
   // Step 1.
-  LayoutUnit initialFreeSpace = freeSpace(m_direction);
+  Optional<LayoutUnit> initialFreeSpace = freeSpace(m_direction);
   initializeTrackSizes();
 
   // Step 2.
@@ -1399,13 +1415,19 @@
   // up to this moment (before maximization) to calculate the grid container
   // intrinsic sizes.
   computeGridContainerIntrinsicSizes();
-  freeSpace(m_direction) -= m_minContentSize;
 
-  if (m_sizingOperation == TrackSizing && freeSpace(m_direction) <= 0)
-    return;
+  if (freeSpace(m_direction)) {
+    LayoutUnit updatedFreeSpace =
+        freeSpace(m_direction).value() - m_minContentSize;
+    setFreeSpace(m_direction, updatedFreeSpace);
+    if (updatedFreeSpace <= 0)
+      return;
+  }
 
   // Step 3.
-  m_strategy->maximizeTracks(tracks(m_direction), freeSpace(m_direction));
+  m_strategy->maximizeTracks(tracks(m_direction), m_direction == ForColumns
+                                                      ? m_freeSpaceColumns
+                                                      : m_freeSpaceRows);
 
   if (m_flexibleSizedTracksIndex.isEmpty())
     return;
diff --git a/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.h b/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.h
index a868405d..cab44de 100644
--- a/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.h
+++ b/third_party/WebKit/Source/core/layout/GridTrackSizingAlgorithm.h
@@ -84,8 +84,8 @@
   void setup(GridTrackSizingDirection,
              size_t numTracks,
              SizingOperation,
-             LayoutUnit availableSpace,
-             LayoutUnit freeSpace);
+             Optional<LayoutUnit> availableSpace,
+             Optional<LayoutUnit> freeSpace);
   void run();
   void reset();
 
@@ -100,7 +100,8 @@
   Vector<GridTrack>& tracks(GridTrackSizingDirection);
   const Vector<GridTrack>& tracks(GridTrackSizingDirection) const;
 
-  LayoutUnit& freeSpace(GridTrackSizingDirection);
+  Optional<LayoutUnit> freeSpace(GridTrackSizingDirection);
+  void setFreeSpace(GridTrackSizingDirection, Optional<LayoutUnit>);
 
 #if DCHECK_IS_ON()
   bool tracksAreWiderThanMinTrackBreadth() const;
@@ -164,7 +165,7 @@
   // method at thise level.
   void initializeTrackSizes();
   void resolveIntrinsicTrackSizes();
-  void stretchFlexibleTracks(LayoutUnit freeSpace);
+  void stretchFlexibleTracks(Optional<LayoutUnit> freeSpace);
 
   // State machine.
   void advanceNextState();
@@ -172,10 +173,10 @@
 
   // Data.
   bool m_needsSetup{true};
-  LayoutUnit m_availableSpace;
+  Optional<LayoutUnit> m_availableSpace;
 
-  LayoutUnit m_freeSpaceColumns;
-  LayoutUnit m_freeSpaceRows;
+  Optional<LayoutUnit> m_freeSpaceColumns;
+  Optional<LayoutUnit> m_freeSpaceRows;
 
   // We need to keep both alive in order to properly size grids with orthogonal
   // writing modes.
@@ -231,10 +232,12 @@
   LayoutUnit maxContentForChild(LayoutBox&) const;
   LayoutUnit minSizeForChild(LayoutBox&) const;
 
-  virtual void maximizeTracks(Vector<GridTrack>&, LayoutUnit& freeSpace) = 0;
-  virtual double findUsedFlexFraction(Vector<size_t>& flexibleSizedTracksIndex,
-                                      GridTrackSizingDirection,
-                                      LayoutUnit initialFreeSpace) const = 0;
+  virtual void maximizeTracks(Vector<GridTrack>&,
+                              Optional<LayoutUnit>& freeSpace) = 0;
+  virtual double findUsedFlexFraction(
+      Vector<size_t>& flexibleSizedTracksIndex,
+      GridTrackSizingDirection,
+      Optional<LayoutUnit> initialFreeSpace) const = 0;
   virtual bool recomputeUsedFlexFractionIfNeeded(
       Vector<size_t>& flexibleSizedTracksIndex,
       double& flexFraction,
diff --git a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
index fc49a6d..7c551ca 100644
--- a/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
+++ b/third_party/WebKit/Source/core/layout/LayoutGrid.cpp
@@ -284,8 +284,8 @@
     // Once grid's indefinite height is resolved, we can compute the
     // available free space for Content Alignment.
     if (!cachedHasDefiniteLogicalHeight()) {
-      m_trackSizingAlgorithm.freeSpace(ForRows) =
-          logicalHeight() - trackBasedLogicalHeight;
+      m_trackSizingAlgorithm.setFreeSpace(
+          ForRows, logicalHeight() - trackBasedLogicalHeight);
     }
 
     // TODO (lajava): We need to compute baselines after step 2 so
@@ -436,7 +436,7 @@
     LayoutUnit& minIntrinsicSize,
     LayoutUnit& maxIntrinsicSize) const {
   algo.setup(direction, numTracks(direction, grid), IntrinsicSizeComputation,
-             LayoutUnit(), LayoutUnit());
+             WTF::nullopt, WTF::nullopt);
   algo.run();
 
   minIntrinsicSize = algo.minContentSize();
@@ -1046,8 +1046,8 @@
 
 void LayoutGrid::applyStretchAlignmentToTracksIfNeeded(
     GridTrackSizingDirection direction) {
-  LayoutUnit& availableSpace = m_trackSizingAlgorithm.freeSpace(direction);
-  if (availableSpace <= 0 ||
+  Optional<LayoutUnit> freeSpace = m_trackSizingAlgorithm.freeSpace(direction);
+  if (!freeSpace || freeSpace.value() <= 0 ||
       (direction == ForColumns &&
        styleRef().resolvedJustifyContentDistribution(
            contentAlignmentNormalBehavior()) != ContentDistributionStretch) ||
@@ -1071,14 +1071,13 @@
   if (numberOfAutoSizedTracks < 1)
     return;
 
-  LayoutUnit sizeToIncrease = availableSpace / numberOfAutoSizedTracks;
+  LayoutUnit sizeToIncrease = freeSpace.value() / numberOfAutoSizedTracks;
   for (const auto& trackIndex : autoSizedTracksIndex) {
     GridTrack* track = allTracks.data() + trackIndex;
     LayoutUnit baseSize = track->baseSize() + sizeToIncrease;
     track->setBaseSize(baseSize);
   }
-
-  availableSpace = LayoutUnit();
+  m_trackSizingAlgorithm.setFreeSpace(direction, LayoutUnit());
 }
 
 void LayoutGrid::layoutGridItems() {
@@ -1345,7 +1344,8 @@
   size_t numberOfLines = numberOfTracks + 1;
   size_t lastLine = numberOfLines - 1;
   ContentAlignmentData offset = computeContentPositionAndDistributionOffset(
-      direction, m_trackSizingAlgorithm.freeSpace(direction), numberOfTracks);
+      direction, m_trackSizingAlgorithm.freeSpace(direction).value(),
+      numberOfTracks);
   auto& positions = isRowAxis ? m_columnPositions : m_rowPositions;
   positions.resize(numberOfLines);
   auto borderAndPadding =
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
index 123efd1..7eea9c5 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
+++ b/third_party/WebKit/Source/core/layout/ng/ng_floats_utils.cc
@@ -127,9 +127,19 @@
       toNGPhysicalBoxFragment(floating_object->fragment.get()));
 
   // Find a layout opportunity that will fit our float.
-  const NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
+  NGLayoutOpportunity opportunity = FindLayoutOpportunityForFragment(
       new_parent_space, float_fragment, floating_object);
-  DCHECK(!opportunity.IsEmpty()) << "Opportunity is empty but it shouldn't be";
+
+  // TODO(glebl): This should check for infinite opportunity instead.
+  if (opportunity.IsEmpty()) {
+    // Because of the implementation specific of the layout opportunity iterator
+    // an empty opportunity can mean 2 things:
+    // - search for layout opportunities is exhausted.
+    // - opportunity has an infinite size. That's because CS is infinite.
+    opportunity = NGLayoutOpportunity(
+        NGLogicalOffset(),
+        NGLogicalSize(float_fragment.InlineSize(), float_fragment.BlockSize()));
+  }
 
   // Calculate the float offset if needed.
   LayoutUnit float_offset;
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
index cabc6106..7ed9543 100644
--- a/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
+++ b/third_party/WebKit/Source/core/layout/ng/ng_layout_opportunity_iterator.h
@@ -35,8 +35,9 @@
       const WTF::Optional<NGLogicalOffset>& opt_offset = WTF::nullopt,
       const WTF::Optional<NGLogicalOffset>& opt_leader_point = WTF::nullopt);
 
-  // Gets the next Layout Opportunity or nullptr if the search is exhausted.
+  // Gets the next Layout Opportunity or empty one if the search is exhausted.
   // TODO(chrome-layout-team): Refactor with using C++ <iterator> library.
+  // TODO(glebl): Refactor the iterator to return unique_ptr here.
   const NGLayoutOpportunity Next();
 
   // Offset that specifies the starting point to search layout opportunities.
diff --git a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
index 15a360b..c363fef4 100644
--- a/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
+++ b/third_party/WebKit/Source/core/loader/FrameFetchContext.cpp
@@ -843,6 +843,18 @@
                                             reportingPolicy))
     return ResourceRequestBlockedReason::MixedContent;
 
+  if (url.whitespaceRemoved()) {
+    Deprecation::countDeprecation(
+        frame()->document(), UseCounter::CanRequestURLHTTPContainingNewline);
+    if (url.protocolIsInHTTPFamily()) {
+      if (RuntimeEnabledFeatures::restrictCanRequestURLCharacterSetEnabled())
+        return ResourceRequestBlockedReason::Other;
+    } else {
+      UseCounter::count(frame()->document(),
+                        UseCounter::CanRequestURLNonHTTPContainingNewline);
+    }
+  }
+
   // Let the client have the final say into whether or not the load should
   // proceed.
   DocumentLoader* documentLoader = masterDocumentLoader();
diff --git a/third_party/WebKit/Source/core/page/FocusController.cpp b/third_party/WebKit/Source/core/page/FocusController.cpp
index 383f1623..6becd69 100644
--- a/third_party/WebKit/Source/core/page/FocusController.cpp
+++ b/third_party/WebKit/Source/core/page/FocusController.cpp
@@ -933,7 +933,11 @@
     start = toHTMLFrameOwnerElement(from->owner());
   }
 
-  return advanceFocusInDocumentOrder(to, start, type, false,
+  // If we're coming from a parent frame, we need to restart from the first or
+  // last focusable element.
+  bool initialFocus = to->tree().parent() == from;
+
+  return advanceFocusInDocumentOrder(to, start, type, initialFocus,
                                      sourceCapabilities);
 }
 
@@ -998,7 +1002,11 @@
   }
 
   if (element == document->focusedElement()) {
-    // Focus wrapped around to the same element.
+    // Focus is either coming from a remote frame or has wrapped around.
+    if (focusedFrame() != document->frame()) {
+      setFocusedFrame(document->frame());
+      dispatchFocusEvent(*document, *element);
+    }
     return true;
   }
 
@@ -1012,14 +1020,17 @@
       return false;
 
     document->clearFocusedElement();
-    setFocusedFrame(owner->contentFrame());
 
-    // If contentFrame is remote, continue the search for focusable
-    // elements in that frame's process.
-    // clearFocusedElement() fires events that might detach the
-    // contentFrame, hence the need to null-check it again.
+    // If contentFrame is remote, continue the search for focusable elements in
+    // that frame's process. The target contentFrame's process will grab focus
+    // from inside advanceFocusInDocumentOrder().
+    //
+    // clearFocusedElement() fires events that might detach the contentFrame,
+    // hence the need to null-check it again.
     if (owner->contentFrame() && owner->contentFrame()->isRemoteFrame())
       toRemoteFrame(owner->contentFrame())->advanceFocus(type, frame);
+    else
+      setFocusedFrame(owner->contentFrame());
 
     return true;
   }
diff --git a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
index 305eb1e..fc6caa2 100644
--- a/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
+++ b/third_party/WebKit/Source/core/workers/WorkerGlobalScope.cpp
@@ -182,8 +182,8 @@
           SyntaxError, "The URL '" + urlString + "' is invalid.");
       return;
     }
-    if (!contentSecurityPolicy()->allowScriptFromSource(url, AtomicString(),
-                                                        NotParserInserted)) {
+    if (!contentSecurityPolicy()->allowScriptFromSource(
+            url, AtomicString(), IntegrityMetadataSet(), NotParserInserted)) {
       exceptionState.throwDOMException(
           NetworkError,
           "The script at '" + url.elidedString() + "' failed to load.");
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
index 92dcc89..5fe56b5 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityModel.js
@@ -4,13 +4,12 @@
 /**
  * @unrestricted
  */
-Accessibility.AccessibilityNode = class extends SDK.SDKObject {
+Accessibility.AccessibilityNode = class {
   /**
    * @param {!Accessibility.AccessibilityModel} accessibilityModel
    * @param {!Protocol.Accessibility.AXNode} payload
    */
   constructor(accessibilityModel, payload) {
-    super(accessibilityModel.target());
     this._accessibilityModel = accessibilityModel;
     this._agent = accessibilityModel._agent;
 
@@ -19,7 +18,7 @@
     if (payload.backendDOMNodeId) {
       accessibilityModel._setAXNodeForBackendDOMNodeId(payload.backendDOMNodeId, this);
       this._backendDOMNodeId = payload.backendDOMNodeId;
-      this._deferredDOMNode = new SDK.DeferredDOMNode(this.target(), payload.backendDOMNodeId);
+      this._deferredDOMNode = new SDK.DeferredDOMNode(accessibilityModel.target(), payload.backendDOMNodeId);
     } else {
       this._backendDOMNodeId = null;
       this._deferredDOMNode = null;
@@ -38,6 +37,13 @@
   }
 
   /**
+   * @return {!Accessibility.AccessibilityModel}
+   */
+  accessibilityModel() {
+    return this._accessibilityModel;
+  }
+
+  /**
    * @return {boolean}
    */
   ignored() {
diff --git a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
index 9e8664c..0fea753 100644
--- a/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
+++ b/third_party/WebKit/Source/devtools/front_end/accessibility/AccessibilityNodeView.js
@@ -213,7 +213,8 @@
    * @param {number} index
    */
   appendRelatedNode(relatedNode, index) {
-    var deferredNode = new SDK.DeferredDOMNode(this._axNode.target(), relatedNode.backendDOMNodeId);
+    var deferredNode =
+        new SDK.DeferredDOMNode(this._axNode.accessibilityModel().target(), relatedNode.backendDOMNodeId);
     var nodeTreeElement = new Accessibility.AXRelatedNodeSourceTreeElement({deferredNode: deferredNode}, relatedNode);
     this.appendChild(nodeTreeElement);
   }
@@ -222,7 +223,8 @@
    * @param {!Protocol.Accessibility.AXRelatedNode} relatedNode
    */
   appendRelatedNodeInline(relatedNode) {
-    var deferredNode = new SDK.DeferredDOMNode(this._axNode.target(), relatedNode.backendDOMNodeId);
+    var deferredNode =
+        new SDK.DeferredDOMNode(this._axNode.accessibilityModel().target(), relatedNode.backendDOMNodeId);
     var linkedNode = new Accessibility.AXRelatedNodeElement({deferredNode: deferredNode}, relatedNode);
     this.listItemElement.appendChild(linkedNode.render());
   }
@@ -331,7 +333,8 @@
    * @param {string} idref
    */
   appendRelatedNodeWithIdref(relatedNode, index, idref) {
-    var deferredNode = new SDK.DeferredDOMNode(this._axNode.target(), relatedNode.backendDOMNodeId);
+    var deferredNode =
+        new SDK.DeferredDOMNode(this._axNode.accessibilityModel().target(), relatedNode.backendDOMNodeId);
     var nodeTreeElement =
         new Accessibility.AXRelatedNodeSourceTreeElement({deferredNode: deferredNode, idref: idref}, relatedNode);
     this.appendChild(nodeTreeElement);
diff --git a/third_party/WebKit/Source/devtools/front_end/audits/AuditController.js b/third_party/WebKit/Source/devtools/front_end/audits/AuditController.js
index ca17a15c..1771d4c 100644
--- a/third_party/WebKit/Source/devtools/front_end/audits/AuditController.js
+++ b/third_party/WebKit/Source/devtools/front_end/audits/AuditController.js
@@ -71,7 +71,8 @@
       resultCallback(mainResourceURL, results);
     }
 
-    var requests = NetworkLog.networkLog.requestsForTarget(target).slice();
+    var networkManager = target.model(SDK.NetworkManager);
+    var requests = networkManager ? NetworkLog.networkLog.requestsForManager(networkManager).slice() : [];
     var compositeProgress = new Common.CompositeProgress(this._progress);
     var subprogresses = [];
     for (var i = 0; i < categories.length; ++i)
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js b/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
index 80b52d1..547cbaf 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/NetworkProject.js
@@ -61,14 +61,14 @@
 /**
  * @unrestricted
  */
-Bindings.NetworkProject = class extends SDK.SDKObject {
+Bindings.NetworkProject = class {
   /**
    * @param {!SDK.Target} target
    * @param {!Workspace.Workspace} workspace
    * @param {?SDK.ResourceTreeModel} resourceTreeModel
    */
   constructor(target, workspace, resourceTreeModel) {
-    super(target);
+    this._target = target;
     this._workspace = workspace;
     /** @type {!Map<string, !Bindings.ContentProviderBasedProject>} */
     this._workspaceProjects = new Map();
@@ -171,7 +171,7 @@
    * @return {!Bindings.ContentProviderBasedProject}
    */
   _workspaceProject(frameId, isContentScripts) {
-    var projectId = Bindings.NetworkProject.projectId(this.target(), frameId, isContentScripts);
+    var projectId = Bindings.NetworkProject.projectId(this._target, frameId, isContentScripts);
     var projectType = isContentScripts ? Workspace.projectTypes.ContentScripts : Workspace.projectTypes.Network;
 
     var project = this._workspaceProjects.get(projectId);
@@ -180,7 +180,7 @@
 
     project = new Bindings.ContentProviderBasedProject(
         this._workspace, projectId, projectType, '', false /* isServiceProject */);
-    project[Bindings.NetworkProject._targetSymbol] = this.target();
+    project[Bindings.NetworkProject._targetSymbol] = this._target;
     project[Bindings.NetworkProject._frameSymbol] =
         frameId && this._resourceTreeModel ? this._resourceTreeModel.frameForId(frameId) : null;
     this._workspaceProjects.set(projectId, project);
@@ -217,7 +217,7 @@
    */
   _removeFileForURL(url, frameId, isContentScript) {
     var project =
-        this._workspaceProjects.get(Bindings.NetworkProject.projectId(this.target(), frameId, isContentScript));
+        this._workspaceProjects.get(Bindings.NetworkProject.projectId(this._target, frameId, isContentScript));
     if (!project)
       return;
     project.removeFile(url);
@@ -364,7 +364,7 @@
       return;
 
     // Never load document twice.
-    var projectId = Bindings.NetworkProject.projectId(this.target(), resource.frameId, false);
+    var projectId = Bindings.NetworkProject.projectId(this._target, resource.frameId, false);
     var project = this._workspaceProjects.get(projectId);
     if (project && project.uiSourceCodeForURL(resource.url))
       return;
@@ -410,7 +410,7 @@
   }
 
   _suspendStateChanged() {
-    if (this.target().targetManager().allTargetsSuspended())
+    if (this._target.targetManager().allTargetsSuspended())
       this._reset();
     else
       this._populate();
@@ -426,7 +426,7 @@
     var url = contentProvider.contentURL();
     var project = this._workspaceProject(frameId, isContentScript);
     var uiSourceCode = project.createUISourceCode(url, contentProvider.contentType());
-    uiSourceCode[Bindings.NetworkProject._targetSymbol] = this.target();
+    uiSourceCode[Bindings.NetworkProject._targetSymbol] = this._target;
     return uiSourceCode;
   }
 
@@ -443,7 +443,7 @@
   _dispose() {
     this._reset();
     Common.EventTarget.removeEventListeners(this._eventListeners);
-    delete this.target()[Bindings.NetworkProject._networkProjectSymbol];
+    delete this._target[Bindings.NetworkProject._networkProjectSymbol];
   }
 
   _reset() {
diff --git a/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js b/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
index 0fd18fc..e3c34c6 100644
--- a/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
+++ b/third_party/WebKit/Source/devtools/front_end/bindings/StylesSourceMapping.js
@@ -145,8 +145,6 @@
    * @param {!Common.Event} event
    */
   _unbindAllUISourceCodes(event) {
-    if (event.data.target() !== this._cssModel.target())
-      return;
     for (var styleFile of this._styleFiles.values())
       styleFile.dispose();
     this._styleFiles.clear();
diff --git a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
index 4e64cf59..86d6747 100644
--- a/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/console_model/ConsoleModel.js
@@ -453,7 +453,8 @@
     this.scriptId = scriptId || null;
     this.workerId = workerId || null;
 
-    this.request = (target && requestId) ? NetworkLog.networkLog.requestForId(target, requestId) : null;
+    var networkManager = (target && requestId) ? target.model(SDK.NetworkManager) : null;
+    this.request = (networkManager && requestId) ? NetworkLog.networkLog.requestForId(networkManager, requestId) : null;
 
     if (this.request) {
       var initiator = this.request.initiator();
diff --git a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
index ab810bc7..5c945f0 100644
--- a/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
+++ b/third_party/WebKit/Source/devtools/front_end/extensions/ExtensionServer.js
@@ -932,7 +932,7 @@
     else if (options.scriptExecutionContext)
       contextSecurityOrigin = options.scriptExecutionContext;
 
-    var runtimeModel = frame.target().model(SDK.RuntimeModel);
+    var runtimeModel = frame.resourceTreeModel().target().model(SDK.RuntimeModel);
     var executionContexts = runtimeModel ? runtimeModel.executionContexts() : [];
     if (contextSecurityOrigin) {
       for (var i = 0; i < executionContexts.length; ++i) {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
index 57b80e79..aafd190d 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
@@ -724,8 +724,10 @@
   }
 
   dispose() {
-    if (this._linkifiedInitiatorAnchor)
-      this.parentView().linkifier.disposeAnchor(this._request.target(), this._linkifiedInitiatorAnchor);
+    if (this._linkifiedInitiatorAnchor) {
+      this.parentView().linkifier.disposeAnchor(
+          this._request.networkManager().target(), this._linkifiedInitiatorAnchor);
+    }
   }
 
   /**
@@ -783,7 +785,7 @@
     iconElement.classList.add(this._request.resourceType().name());
 
     cell.appendChild(iconElement);
-    cell.createTextChild(this._request.target().decorateLabel(this._request.name()));
+    cell.createTextChild(this._request.networkManager().target().decorateLabel(this._request.name()));
     this._appendSubtitle(cell, this._request.path());
     cell.title = this._request.url();
   }
@@ -875,7 +877,8 @@
       case SDK.NetworkRequest.InitiatorType.Script:
         if (!this._linkifiedInitiatorAnchor) {
           this._linkifiedInitiatorAnchor = this.parentView().linkifier.linkifyScriptLocation(
-              request.target(), initiator.scriptId, initiator.url, initiator.lineNumber, initiator.columnNumber);
+              request.networkManager().target(), initiator.scriptId, initiator.url, initiator.lineNumber,
+              initiator.columnNumber);
           this._linkifiedInitiatorAnchor.title = '';
         }
         cell.appendChild(this._linkifiedInitiatorAnchor);
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
index d55d185..752ab84 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -29,7 +29,7 @@
  */
 /**
  * @implements {UI.Searchable}
- * @implements {SDK.TargetManager.Observer}
+ * @implements {SDK.SDKModelObserver<!SDK.NetworkManager>}
  * @unrestricted
  */
 Network.NetworkLogView = class extends UI.VBox {
@@ -110,7 +110,7 @@
     Common.moduleSetting('networkColorCodeResourceTypes')
         .addChangeListener(this._invalidateAllItems.bind(this, false), this);
 
-    SDK.targetManager.observeTargets(this);
+    SDK.targetManager.observeModels(SDK.NetworkManager, this);
     SDK.targetManager.addModelListener(
         SDK.NetworkManager, SDK.NetworkManager.Events.RequestStarted, this._onRequestStarted, this);
     SDK.targetManager.addModelListener(
@@ -389,11 +389,11 @@
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.NetworkManager} networkManager
    */
-  targetAdded(target) {
-    if (!target.parentTarget()) {
-      var resourceTreeModel = target.model(SDK.ResourceTreeModel);
+  modelAdded(networkManager) {
+    if (!networkManager.target().parentTarget()) {
+      var resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
       if (resourceTreeModel) {
         resourceTreeModel.addEventListener(
             SDK.ResourceTreeModel.Events.MainFrameNavigated, this._mainFrameNavigated, this);
@@ -402,16 +402,16 @@
             SDK.ResourceTreeModel.Events.DOMContentLoaded, this._domContentLoadedEventFired, this);
       }
     }
-    NetworkLog.networkLog.requestsForTarget(target).forEach(this._appendRequest.bind(this));
+    NetworkLog.networkLog.requestsForManager(networkManager).forEach(this._appendRequest.bind(this));
   }
 
   /**
    * @override
-   * @param {!SDK.Target} target
+   * @param {!SDK.NetworkManager} networkManager
    */
-  targetRemoved(target) {
-    if (!target.parentTarget()) {
-      var resourceTreeModel = target.model(SDK.ResourceTreeModel);
+  modelRemoved(networkManager) {
+    if (!networkManager.target().parentTarget()) {
+      var resourceTreeModel = networkManager.target().model(SDK.ResourceTreeModel);
       if (resourceTreeModel) {
         resourceTreeModel.removeEventListener(
             SDK.ResourceTreeModel.Events.MainFrameNavigated, this._mainFrameNavigated, this);
@@ -605,7 +605,8 @@
         selectedRequestsNumber++;
         selectedTransferSize += requestTransferSize;
       }
-      if (request.url() === request.target().inspectedURL() && request.resourceType() === Common.resourceTypes.Document)
+      if (request.url() === request.networkManager().target().inspectedURL() &&
+          request.resourceType() === Common.resourceTypes.Document)
         baseTime = request.startTime;
       if (request.endTime > maxTime)
         maxTime = request.endTime;
@@ -1038,7 +1039,8 @@
 
     // Pick provisional load requests.
     var requestsToPick = [];
-    var requests = NetworkLog.networkLog.requestsForTarget(frame.target());
+    var networkManager = frame.resourceTreeModel().target().model(SDK.NetworkManager);
+    var requests = networkManager ? NetworkLog.networkLog.requestsForManager(networkManager) : [];
     for (var i = 0; i < requests.length; ++i) {
       var request = requests[i];
       if (request.loaderId === loaderId)
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
index 6508efe8..e206ff06 100644
--- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
+++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogViewColumns.js
@@ -585,7 +585,7 @@
       box: anchor.boxInWindow(),
       show: popover => {
         var content = Components.DOMPresentationUtils.buildStackTracePreviewContents(
-            anchor.request.target(), this._popupLinkifier, initiator.stack);
+            anchor.request.networkManager().target(), this._popupLinkifier, initiator.stack);
         popover.contentElement.appendChild(content);
         return Promise.resolve(true);
       },
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
index 95edcda..356b4a1 100644
--- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
+++ b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
@@ -61,7 +61,7 @@
    * @return {?*}
    */
   groupForRequest(request) {
-    var resourceTreeModel = request.target().model(SDK.ResourceTreeModel);
+    var resourceTreeModel = request.networkManager().target().model(SDK.ResourceTreeModel);
     if (!resourceTreeModel)
       return null;
     var frame = resourceTreeModel.frameForId(request.frameId);
diff --git a/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js b/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
index fa2663b..638f68d2 100644
--- a/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
+++ b/third_party/WebKit/Source/devtools/front_end/network_log/NetworkLog.js
@@ -35,9 +35,9 @@
   constructor() {
     /** @type {!Array<!SDK.NetworkRequest>} */
     this._requests = [];
-    /** @type {!Map<!SDK.Target, !Map<string, !SDK.NetworkRequest>>} */
-    this._requestsByTargetAndId = new Map();
-    /** @type {!Map<!SDK.Target, !NetworkLog.PageLoad>} */
+    /** @type {!Map<!SDK.NetworkManager, !Map<string, !SDK.NetworkRequest>>} */
+    this._requestsByManagerAndId = new Map();
+    /** @type {!Map<!SDK.NetworkManager, !NetworkLog.PageLoad>} */
     this._currentPageLoad = new Map();
     SDK.targetManager.observeModels(SDK.NetworkManager, this);
   }
@@ -63,7 +63,7 @@
     }
 
     networkManager[NetworkLog.NetworkLog._events] = eventListeners;
-    this._requestsByTargetAndId.set(networkManager.target(), new Map());
+    this._requestsByManagerAndId.set(networkManager, new Map());
   }
 
   /**
@@ -71,7 +71,7 @@
    * @param {!SDK.NetworkManager} networkManager
    */
   modelRemoved(networkManager) {
-    this._requestsByTargetAndId.delete(networkManager.target());
+    this._requestsByManagerAndId.delete(networkManager);
     Common.EventTarget.removeEventListeners(networkManager[NetworkLog.NetworkLog._events]);
   }
 
@@ -91,21 +91,21 @@
   }
 
   /**
-   * @param {!SDK.Target} target
+   * @param {!SDK.NetworkManager} networkManager
    * @return {!Array<!SDK.NetworkRequest>}
    */
-  requestsForTarget(target) {
-    var map = this._requestsByTargetAndId.get(target);
+  requestsForManager(networkManager) {
+    var map = this._requestsByManagerAndId.get(networkManager);
     return map ? Array.from(map.values()) : [];
   }
 
   /**
+   * @param {!SDK.NetworkManager} networkManager
    * @param {string} url
-   * @param {!SDK.Target} target
    * @return {?SDK.NetworkRequest}
    */
-  _requestForURLInTarget(url, target) {
-    var map = this._requestsByTargetAndId.get(target);
+  _requestByManagerAndURL(networkManager, url) {
+    var map = this._requestsByManagerAndId.get(networkManager);
     if (!map)
       return null;
     for (var request of map.values()) {
@@ -182,7 +182,7 @@
   initiatorGraphForRequest(request) {
     /** @type {!Set<!SDK.NetworkRequest>} */
     var initiated = new Set();
-    var map = this._requestsByTargetAndId.get(request.target());
+    var map = this._requestsByManagerAndId.get(request.networkManager());
     if (map) {
       for (var otherRequest of map.values()) {
         if (this._initiatorChain(otherRequest).has(request))
@@ -223,7 +223,8 @@
     if (request[NetworkLog.NetworkLog._initiatorDataSymbol].request !== undefined)
       return request[NetworkLog.NetworkLog._initiatorDataSymbol].request;
     var url = this.initiatorInfoForRequest(request).url;
-    request[NetworkLog.NetworkLog._initiatorDataSymbol].request = this._requestForURLInTarget(url, request.target());
+    request[NetworkLog.NetworkLog._initiatorDataSymbol].request =
+        this._requestByManagerAndURL(request.networkManager(), url);
     return request[NetworkLog.NetworkLog._initiatorDataSymbol].request;
   }
 
@@ -240,12 +241,15 @@
    */
   _onMainFrameNavigated(event) {
     var mainFrame = /** @type {!SDK.ResourceTreeFrame} */ (event.data);
-    var target = mainFrame.target();
-    this._currentPageLoad.delete(target);
-    var oldRequests = this.requestsForTarget(target);
-    this._requests = this._requests.filter(request => request.target() !== target);
+    var networkManager = mainFrame.resourceTreeModel().target().model(SDK.NetworkManager);
+    if (!networkManager)
+      return;
+
+    this._currentPageLoad.delete(networkManager);
+    var oldRequests = this.requestsForManager(networkManager);
+    this._requests = this._requests.filter(request => request.networkManager() !== networkManager);
     var idMap = new Map();
-    this._requestsByTargetAndId.set(target, idMap);
+    this._requestsByManagerAndId.set(networkManager, idMap);
 
     // Preserve requests from the new session.
     var currentPageLoad = null;
@@ -260,7 +264,7 @@
       }
     }
     if (currentPageLoad)
-      this._currentPageLoad.set(target, currentPageLoad);
+      this._currentPageLoad.set(networkManager, currentPageLoad);
   }
 
   /**
@@ -269,8 +273,8 @@
   _onRequestStarted(event) {
     var request = /** @type {!SDK.NetworkRequest} */ (event.data);
     this._requests.push(request);
-    this._requestsByTargetAndId.get(request.target()).set(request.requestId(), request);
-    request[NetworkLog.NetworkLog._pageLoadForRequestSymbol] = this._currentPageLoad.get(request.target());
+    this._requestsByManagerAndId.get(request.networkManager()).set(request.requestId(), request);
+    request[NetworkLog.NetworkLog._pageLoadForRequestSymbol] = this._currentPageLoad.get(request.networkManager());
   }
 
   /**
@@ -286,7 +290,8 @@
    * @param {!Common.Event} event
    */
   _onDOMContentLoaded(resourceTreeModel, event) {
-    var pageLoad = this._currentPageLoad.get(resourceTreeModel.target());
+    var networkManager = resourceTreeModel.target().model(SDK.NetworkManager);
+    var pageLoad = networkManager ? this._currentPageLoad.get(networkManager) : null;
     if (pageLoad)
       pageLoad.contentLoadTime = /** @type {number} */ (event.data);
   }
@@ -295,18 +300,19 @@
    * @param {!Common.Event} event
    */
   _onLoad(event) {
-    var pageLoad = this._currentPageLoad.get(event.data.resourceTreeModel.target());
+    var networkManager = event.data.resourceTreeModel.target().model(SDK.NetworkManager);
+    var pageLoad = networkManager ? this._currentPageLoad.get(networkManager) : null;
     if (pageLoad)
       pageLoad.loadTime = /** @type {number} */ (event.data.loadTime);
   }
 
   /**
-   * @param {!SDK.Target} target
+   * @param {!SDK.NetworkManager} networkManager
    * @param {!Protocol.Network.RequestId} requestId
    * @return {?SDK.NetworkRequest}
    */
-  requestForId(target, requestId) {
-    var map = this._requestsByTargetAndId.get(target);
+  requestForId(networkManager, requestId) {
+    var map = this._requestsByManagerAndId.get(networkManager);
     return map ? (map.get(requestId) || null) : null;
   }
 };
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
index 2c738b0..c68aa70 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ApplicationPanelSidebar.js
@@ -1511,7 +1511,7 @@
    */
   constructor(storagePanel, frame, cookieDomain) {
     super(storagePanel, cookieDomain ? cookieDomain : Common.UIString('Local Files'), false);
-    this._target = frame.target();
+    this._target = frame.resourceTreeModel().target();
     this._cookieDomain = cookieDomain;
     var icon = UI.Icon.create('mediumicon-cookie', 'resource-tree-item');
     this.setLeadingIcons([icon]);
diff --git a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js
index 43ecf0d..de9dee3 100644
--- a/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js
+++ b/third_party/WebKit/Source/devtools/front_end/resources/ResourcesSection.js
@@ -36,7 +36,7 @@
     var parentFrame = frame.parentFrame;
     if (parentFrame)
       return parentFrame;
-    var parentTarget = frame.target().parentTarget();
+    var parentTarget = frame.resourceTreeModel().target().parentTarget();
     if (!parentTarget)
       return null;
     return parentTarget.model(SDK.ResourceTreeModel).mainFrame;
@@ -164,9 +164,8 @@
   set hovered(hovered) {
     if (hovered) {
       this.listItemElement.classList.add('hovered');
-      var domModel = this._frame.target().model(SDK.DOMModel);
-      if (domModel)
-        domModel.highlightFrame(this._frameId);
+      var domModel = this._frame.resourceTreeModel().domModel();
+      domModel.highlightFrame(this._frameId);
     } else {
       this.listItemElement.classList.remove('hovered');
       SDK.DOMModel.hideDOMNodeHighlight();
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
index 046cb79..ad4ada9e 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkManager.js
@@ -458,8 +458,7 @@
    * @param {!Protocol.Network.Initiator=} initiator
    */
   webSocketCreated(requestId, requestURL, initiator) {
-    var networkRequest =
-        new SDK.NetworkRequest(this._manager.target(), requestId, requestURL, '', '', '', initiator || null);
+    var networkRequest = new SDK.NetworkRequest(this._manager, requestId, requestURL, '', '', '', initiator || null);
     networkRequest.setResourceType(Common.resourceTypes.WebSocket);
     this._startNetworkRequest(networkRequest);
   }
@@ -647,7 +646,7 @@
    * @param {?Protocol.Network.Initiator} initiator
    */
   _createNetworkRequest(requestId, frameId, loaderId, url, documentURL, initiator) {
-    return new SDK.NetworkRequest(this._manager.target(), requestId, url, documentURL, frameId, loaderId, initiator);
+    return new SDK.NetworkRequest(this._manager, requestId, url, documentURL, frameId, loaderId, initiator);
   }
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
index 45d11b2..4750c08 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/NetworkRequest.js
@@ -31,20 +31,20 @@
  * @implements {Common.ContentProvider}
  * @unrestricted
  */
-SDK.NetworkRequest = class extends SDK.SDKObject {
+SDK.NetworkRequest = class extends Common.Object {
   /**
+   * @param {!SDK.NetworkManager} networkManager
    * @param {!Protocol.Network.RequestId} requestId
-   * @param {!SDK.Target} target
    * @param {string} url
    * @param {string} documentURL
    * @param {!Protocol.Page.FrameId} frameId
    * @param {!Protocol.Network.LoaderId} loaderId
    * @param {?Protocol.Network.Initiator} initiator
    */
-  constructor(target, requestId, url, documentURL, frameId, loaderId, initiator) {
-    super(target);
+  constructor(networkManager, requestId, url, documentURL, frameId, loaderId, initiator) {
+    super();
 
-    this._networkManager = /** @type {!SDK.NetworkManager} */ (target.model(SDK.NetworkManager));
+    this._networkManager = networkManager;
     this._requestId = requestId;
     this.setUrl(url);
     this._documentURL = documentURL;
@@ -531,7 +531,7 @@
     } else {
       this._path = this._parsedURL.host + this._parsedURL.folderPathComponents;
 
-      var inspectedURL = this.target().inspectedURL().asParsedURL();
+      var inspectedURL = this._networkManager.target().inspectedURL().asParsedURL();
       this._path = this._path.trimURL(inspectedURL ? inspectedURL.host : '');
       if (this._parsedURL.lastPathComponent || this._parsedURL.queryParams) {
         this._name =
@@ -1034,7 +1034,7 @@
       this._pendingContentCallbacks.length = 0;
       delete this._contentRequested;
     }
-    this.target().networkAgent().getResponseBody(this._requestId, onResourceContent.bind(this));
+    this._networkManager.target().networkAgent().getResponseBody(this._requestId, onResourceContent.bind(this));
   }
 
   /**
@@ -1109,7 +1109,7 @@
   }
 
   replayXHR() {
-    this.target().networkAgent().replayXHR(this._requestId);
+    this._networkManager.target().networkAgent().replayXHR(this._requestId);
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/Resource.js b/third_party/WebKit/Source/devtools/front_end/sdk/Resource.js
index 9c55e71..91e50c8c5 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/Resource.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/Resource.js
@@ -29,9 +29,9 @@
  * @implements {Common.ContentProvider}
  * @unrestricted
  */
-SDK.Resource = class extends SDK.SDKObject {
+SDK.Resource = class {
   /**
-   * @param {!SDK.Target} target
+   * @param {!SDK.ResourceTreeModel} resourceTreeModel
    * @param {?SDK.NetworkRequest} request
    * @param {string} url
    * @param {string} documentURL
@@ -42,8 +42,9 @@
    * @param {?Date} lastModified
    * @param {?number} contentSize
    */
-  constructor(target, request, url, documentURL, frameId, loaderId, type, mimeType, lastModified, contentSize) {
-    super(target);
+  constructor(
+      resourceTreeModel, request, url, documentURL, frameId, loaderId, type, mimeType, lastModified, contentSize) {
+    this._resourceTreeModel = resourceTreeModel;
     this._request = request;
     this.url = url;
     this._documentURL = documentURL;
@@ -223,7 +224,7 @@
     }
 
     if (this.frameId) {
-      this.target().pageAgent().searchInResource(
+      this._resourceTreeModel.target().pageAgent().searchInResource(
           this.frameId, this.url, query, caseSensitive, isRegex, callbackWrapper);
     } else {
       callback([]);
@@ -311,7 +312,8 @@
       contentLoaded.call(this, null, content, this.request.contentEncoded);
     }
 
-    this.target().pageAgent().getResourceContent(this.frameId, this.url, resourceContentLoaded.bind(this));
+    this._resourceTreeModel.target().pageAgent().getResourceContent(
+        this.frameId, this.url, resourceContentLoaded.bind(this));
   }
 
   /**
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
index fcaa246..d49669a 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/ResourceTreeModel.js
@@ -93,6 +93,13 @@
     }
   }
 
+  /**
+   * @return {!SDK.DOMModel}
+   */
+  domModel() {
+    return /** @type {!SDK.DOMModel} */ (this.target().model(SDK.DOMModel));
+  }
+
   _fetchResourceTree() {
     /** @type {!Map<string, !SDK.ResourceTreeFrame>} */
     this._frames = new Map();
@@ -261,8 +268,8 @@
       return;
 
     var resource = new SDK.Resource(
-        this.target(), null, url, frame.url, frameId, event.data.loaderId,
-        Common.resourceTypes[event.data.resourceType], event.data.mimeType, event.data.lastModified, null);
+        this, null, url, frame.url, frameId, event.data.loaderId, Common.resourceTypes[event.data.resourceType],
+        event.data.mimeType, event.data.lastModified, null);
     frame.addResource(resource);
   }
 
@@ -337,7 +344,7 @@
   _createResourceFromFramePayload(frame, url, type, mimeType, lastModifiedTime, contentSize) {
     var lastModified = typeof lastModifiedTime === 'number' ? new Date(lastModifiedTime * 1000) : null;
     return new SDK.Resource(
-        this.target(), null, url, frame.url, frame.id, frame.loaderId, type, mimeType, lastModified, contentSize);
+        this, null, url, frame.url, frame.id, frame.loaderId, type, mimeType, lastModified, contentSize);
   }
 
   suspendReload() {
@@ -514,10 +521,10 @@
   }
 
   /**
-   * @return {!SDK.Target}
+   * @return {!SDK.ResourceTreeModel}
    */
-  target() {
-    return this._model.target();
+  resourceTreeModel() {
+    return this._model;
   }
 
   /**
@@ -650,7 +657,7 @@
       return;
     }
     resource = new SDK.Resource(
-        this.target(), request, request.url(), request.documentURL, request.frameId, request.loaderId,
+        this._model, request, request.url(), request.documentURL, request.frameId, request.loaderId,
         request.resourceType(), request.mimeType, null, null);
     this._resourcesMap[resource.url] = resource;
     this._model.dispatchEventToListeners(SDK.ResourceTreeModel.Events.ResourceAdded, resource);
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js b/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js
index 5a29b7e1..c41b50d 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/SourceMapManager.js
@@ -5,13 +5,14 @@
 /**
  * @template T
  */
-SDK.SourceMapManager = class extends SDK.SDKObject {
+SDK.SourceMapManager = class extends Common.Object {
   /**
    * @param {!SDK.Target} target
    */
   constructor(target) {
-    super(target);
+    super();
 
+    this._target = target;
     this._isEnabled = true;
 
     /** @type {!Map<!T, string>} */
@@ -51,7 +52,7 @@
    * @param {!Common.Event} event
    */
   _inspectedURLChanged(event) {
-    if (event.data !== this.target())
+    if (event.data !== this._target)
       return;
 
     var clients = Array.from(this._resolvedSourceMapURL.keys());
@@ -105,7 +106,7 @@
   _resolveRelativeURLs(sourceURL, sourceMapURL) {
     // |sourceURL| can be a random string, but is generally an absolute path.
     // Complete it to inspected page url for relative links.
-    var resolvedSourceURL = Common.ParsedURL.completeURL(this.target().inspectedURL(), sourceURL);
+    var resolvedSourceURL = Common.ParsedURL.completeURL(this._target.inspectedURL(), sourceURL);
     var resolvedSourceMapURL = resolvedSourceURL ? Common.ParsedURL.completeURL(resolvedSourceURL, sourceMapURL) : null;
     return {sourceURL: resolvedSourceURL, sourceMapURL: resolvedSourceMapURL};
   }
@@ -157,7 +158,7 @@
       if (!factoryExtension)
         return Promise.resolve(/** @type {?SDK.SourceMap} */ (sourceMap));
       return factoryExtension.instance()
-          .then(factory => factory.editableSourceMap(this.target(), sourceMap))
+          .then(factory => factory.editableSourceMap(this._target, sourceMap))
           .then(map => map || sourceMap)
           .catchException(/** @type {?SDK.SourceMap} */ (null));
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/sdk/Target.js b/third_party/WebKit/Source/devtools/front_end/sdk/Target.js
index b29aa14..bd25a76 100644
--- a/third_party/WebKit/Source/devtools/front_end/sdk/Target.js
+++ b/third_party/WebKit/Source/devtools/front_end/sdk/Target.js
@@ -218,7 +218,7 @@
 /**
  * @unrestricted
  */
-SDK.SDKObject = class extends Common.Object {
+SDK.SDKModel = class extends Common.Object {
   /**
    * @param {!SDK.Target} target
    */
@@ -233,18 +233,6 @@
   target() {
     return this._target;
   }
-};
-
-/**
- * @unrestricted
- */
-SDK.SDKModel = class extends SDK.SDKObject {
-  /**
-   * @param {!SDK.Target} target
-   */
-  constructor(target) {
-    super(target);
-  }
 
   /**
    * @return {!Promise}
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
index e7f9f3bf..caf941af 100644
--- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -31,6 +31,7 @@
 #include "platform/graphics/paint/PaintCanvas.h"
 #include "platform/graphics/paint/PaintFlags.h"
 #include "platform/graphics/skia/SkiaUtils.h"
+#include "platform/wtf/CheckedNumeric.h"
 
 namespace blink {
 
@@ -1530,6 +1531,11 @@
     int sw,
     int sh,
     ExceptionState& exceptionState) const {
+  if (!WTF::CheckMul(sw, sh).IsValid<int>()) {
+    exceptionState.throwRangeError("Out of memory at ImageData creation");
+    return nullptr;
+  }
+
   m_usageCounters.numGetImageDataCalls++;
   m_usageCounters.areaGetImageDataCalls += sw * sh;
   if (!originClean())
@@ -1552,6 +1558,12 @@
     sh = -sh;
   }
 
+  if (!WTF::CheckAdd(sx, sw).IsValid<int>() ||
+      !WTF::CheckAdd(sy, sh).IsValid<int>()) {
+    exceptionState.throwRangeError("Out of memory at ImageData creation");
+    return nullptr;
+  }
+
   Optional<ScopedUsHistogramTimer> timer;
   if (imageBuffer() && imageBuffer()->isAccelerated()) {
     DEFINE_THREAD_SAFE_STATIC_LOCAL(
@@ -1574,7 +1586,6 @@
   }
 
   IntRect imageDataRect(sx, sy, sw, sh);
-  DVLOG(1) << sx << ", " << sy << ", " << sw << ", " << sh;
   ImageBuffer* buffer = imageBuffer();
   if (!buffer || isContextLost()) {
     ImageData* result = ImageData::create(imageDataRect.size());
@@ -1611,6 +1622,10 @@
                                           int dirtyWidth,
                                           int dirtyHeight,
                                           ExceptionState& exceptionState) {
+  if (!WTF::CheckMul(dirtyWidth, dirtyHeight).IsValid<int>()) {
+    return;
+  }
+
   m_usageCounters.numPutImageDataCalls++;
   m_usageCounters.areaPutImageDataCalls += dirtyWidth * dirtyHeight;
   if (data->data()->bufferBase()->isNeutered()) {
diff --git a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
index 828b6d0e8..d781b3c 100644
--- a/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
+++ b/third_party/WebKit/Source/modules/fetch/BodyStreamBuffer.cpp
@@ -56,6 +56,11 @@
     m_client->didFetchDataLoadedStream();
   }
 
+  void didFetchDataLoadedCustomFormat() override {
+    m_buffer->endLoading();
+    m_client->didFetchDataLoadedCustomFormat();
+  }
+
   void didFetchDataLoadFailed() override {
     m_buffer->endLoading();
     m_client->didFetchDataLoadFailed();
diff --git a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
index e2b4d83..336e761 100644
--- a/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
+++ b/third_party/WebKit/Source/modules/fetch/FetchDataLoader.h
@@ -35,17 +35,19 @@
 
     // The method corresponding to createLoaderAs... is called on success.
     virtual void didFetchDataLoadedBlobHandle(PassRefPtr<BlobDataHandle>) {
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
     }
     virtual void didFetchDataLoadedArrayBuffer(DOMArrayBuffer*) {
-      ASSERT_NOT_REACHED();
+      NOTREACHED();
     }
-    virtual void didFetchDataLoadedString(const String&) {
-      ASSERT_NOT_REACHED();
-    }
+    virtual void didFetchDataLoadedString(const String&) { NOTREACHED(); }
     // This is called after all data are read from |handle| and written
     // to |outStream|, and |outStream| is closed or aborted.
-    virtual void didFetchDataLoadedStream() { ASSERT_NOT_REACHED(); }
+    virtual void didFetchDataLoadedStream() { NOTREACHED(); }
+
+    // This function is called when a "custom" FetchDataLoader (none of the
+    // ones listed above) finishes loading.
+    virtual void didFetchDataLoadedCustomFormat() { NOTREACHED(); }
 
     virtual void didFetchDataLoadFailed() = 0;
 
@@ -55,8 +57,7 @@
   static FetchDataLoader* createLoaderAsBlobHandle(const String& mimeType);
   static FetchDataLoader* createLoaderAsArrayBuffer();
   static FetchDataLoader* createLoaderAsString();
-  static FetchDataLoader* createLoaderAsStream(Stream* outStream);
-
+  static FetchDataLoader* createLoaderAsStream(Stream*);
   virtual ~FetchDataLoader() {}
 
   // |consumer| must not have a client when called.
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
index ede66379..3273092 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -765,6 +765,13 @@
       startHideMediaControlsTimer();
     return;
   }
+
+  // If the user is interacting with the controls via the keyboard, don't hide
+  // the controls. This will fire when the user tabs between controls (focusin)
+  // or when they seek either the timeline or volume sliders (input).
+  if (event->type() == EventTypeNames::focusin ||
+      event->type() == EventTypeNames::input)
+    resetHideMediaControlsTimer();
 }
 
 void MediaControlsImpl::hideMediaControlsTimerFired(TimerBase*) {
@@ -912,6 +919,13 @@
   startHideMediaControlsTimer();
 }
 
+void MediaControlsImpl::onPanelKeypress() {
+  // If the user is interacting with the controls via the keyboard, don't hide
+  // the controls. This is called when the user mutes/unmutes, turns CC on/off,
+  // etc.
+  resetHideMediaControlsTimer();
+}
+
 void MediaControlsImpl::notifyElementSizeChanged(ClientRect* newSize) {
   // Note that this code permits a bad frame on resize, since it is
   // run after the relayout / paint happens.  It would be great to improve
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
index 515f9766..80924c4 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.h
@@ -126,6 +126,7 @@
   void onLoadedMetadata() override;
   void onEnteredFullscreen() override;
   void onExitedFullscreen() override;
+  void onPanelKeypress() override;
   Document& ownerDocument() { return document(); }
 
   DECLARE_VIRTUAL_TRACE();
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
index 6b640083..ef94cfe 100644
--- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
+++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImplTest.cpp
@@ -24,6 +24,7 @@
 #include "platform/heap/Handle.h"
 #include "platform/testing/EmptyWebMediaPlayer.h"
 #include "platform/testing/HistogramTester.h"
+#include "platform/testing/TestingPlatformSupport.h"
 #include "platform/testing/UnitTestHelpers.h"
 #include "public/platform/WebMouseEvent.h"
 #include "public/platform/WebScreenInfo.h"
@@ -160,7 +161,9 @@
 
 class MediaControlsImplTest : public ::testing::Test {
  protected:
-  virtual void SetUp() {
+  virtual void SetUp() { initializePage(); }
+
+  void initializePage() {
     Page::PageClients clients;
     fillWithEmptyClients(clients);
     clients.chromeClient = new MockChromeClient();
@@ -176,7 +179,7 @@
     m_pageHolder->frame().settings()->setScriptEnabled(true);
   }
 
-  void simulateRouteAvailabe() {
+  void simulateRouteAvailable() {
     m_mediaControls->mediaElement().remoteRouteAvailabilityChanged(
         WebRemotePlaybackAvailability::DeviceAvailable);
   }
@@ -321,7 +324,7 @@
 
   ASSERT_FALSE(isElementVisible(*castButton));
 
-  simulateRouteAvailabe();
+  simulateRouteAvailable();
   ASSERT_TRUE(isElementVisible(*castButton));
 }
 
@@ -335,7 +338,7 @@
   ASSERT_NE(nullptr, castButton);
 
   ASSERT_FALSE(isElementVisible(*castButton));
-  simulateRouteAvailabe();
+  simulateRouteAvailable();
   ASSERT_TRUE(isElementVisible(*castButton));
 
   mediaControls().mediaElement().setBooleanAttribute(
@@ -352,7 +355,7 @@
       mediaControls(), "-internal-media-controls-overlay-cast-button");
   ASSERT_NE(nullptr, castOverlayButton);
 
-  simulateRouteAvailabe();
+  simulateRouteAvailable();
   ASSERT_TRUE(isElementVisible(*castOverlayButton));
 }
 
@@ -362,7 +365,7 @@
   ASSERT_NE(nullptr, castOverlayButton);
 
   ASSERT_FALSE(isElementVisible(*castOverlayButton));
-  simulateRouteAvailabe();
+  simulateRouteAvailable();
   ASSERT_TRUE(isElementVisible(*castOverlayButton));
 
   mediaControls().mediaElement().setBooleanAttribute(
@@ -380,7 +383,7 @@
   ASSERT_NE(nullptr, castOverlayButton);
 
   EXPECT_FALSE(isElementVisible(*castOverlayButton));
-  simulateRouteAvailabe();
+  simulateRouteAvailable();
   EXPECT_TRUE(isElementVisible(*castOverlayButton));
 
   document().settings()->setMediaControlsEnabled(false);
@@ -793,4 +796,46 @@
                                        9 /* (-4m, -2m] */, 1);
 }
 
+TEST_F(MediaControlsImplTest, ControlsRemainVisibleDuringKeyboardInteraction) {
+  ScopedTestingPlatformSupport<TestingPlatformSupportWithMockScheduler>
+      m_platform;
+
+  // DocumentParserTiming has DCHECKS to make sure time > 0.0.
+  m_platform->advanceClockSeconds(1);
+
+  // Need to reinitialize page since we changed the platform.
+  initializePage();
+  ensureSizing();
+
+  Element* panel = mediaControls().panelElement();
+
+  mediaControls().mediaElement().setBooleanAttribute(HTMLNames::controlsAttr,
+                                                     true);
+  mediaControls().mediaElement().setSrc("http://example.com");
+  mediaControls().mediaElement().play();
+
+  // Controls start out visible.
+  EXPECT_TRUE(isElementVisible(*panel));
+
+  // Tabbing between controls prevents controls from hiding.
+  m_platform->runForPeriodSeconds(2);
+  mediaControls().dispatchEvent(Event::create("focusin"));
+  m_platform->runForPeriodSeconds(2);
+  EXPECT_TRUE(isElementVisible(*panel));
+
+  // Seeking on the timeline or volume bar prevents controls from hiding.
+  mediaControls().dispatchEvent(Event::create("input"));
+  m_platform->runForPeriodSeconds(2);
+  EXPECT_TRUE(isElementVisible(*panel));
+
+  // Pressing a key prevents controls from hiding.
+  mediaControls().panelElement()->dispatchEvent(Event::create("keypress"));
+  m_platform->runForPeriodSeconds(2);
+  EXPECT_TRUE(isElementVisible(*panel));
+
+  // Once user interaction stops, controls can hide.
+  m_platform->runForPeriodSeconds(2);
+  EXPECT_FALSE(isElementVisible(*panel));
+}
+
 }  // namespace blink
diff --git a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
index b319462..57aa8f7 100644
--- a/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
+++ b/third_party/WebKit/Source/modules/serviceworkers/NavigatorServiceWorker.cpp
@@ -57,7 +57,7 @@
     ExceptionState& exceptionState) {
   ExecutionContext* executionContext = scriptState->getExecutionContext();
   DCHECK(!navigator.frame() ||
-         executionContext->getSecurityOrigin()->canAccessCheckSuborigins(
+         executionContext->getSecurityOrigin()->canAccess(
              navigator.frame()->securityContext()->getSecurityOrigin()));
   return NavigatorServiceWorker::from(navigator).serviceWorker(
       navigator.frame(), exceptionState);
@@ -69,7 +69,7 @@
     String& errorMessage) {
   ExecutionContext* executionContext = scriptState->getExecutionContext();
   DCHECK(!navigator.frame() ||
-         executionContext->getSecurityOrigin()->canAccessCheckSuborigins(
+         executionContext->getSecurityOrigin()->canAccess(
              navigator.frame()->securityContext()->getSecurityOrigin()));
   return NavigatorServiceWorker::from(navigator).serviceWorker(
       navigator.frame(), errorMessage);
diff --git a/third_party/WebKit/Source/modules/vibration/NavigatorVibration.cpp b/third_party/WebKit/Source/modules/vibration/NavigatorVibration.cpp
index 4ff4d91..6ca7fb2 100644
--- a/third_party/WebKit/Source/modules/vibration/NavigatorVibration.cpp
+++ b/third_party/WebKit/Source/modules/vibration/NavigatorVibration.cpp
@@ -81,6 +81,7 @@
   // TODO(lunalu): When FeaturePolicy is ready, take out the check for the
   // runtime flag. Please pay attention to the user gesture code below.
   if (RuntimeEnabledFeatures::featurePolicyEnabled() &&
+      RuntimeEnabledFeatures::featurePolicyExperimentalFeaturesEnabled() &&
       !frame->isFeatureEnabled(blink::WebFeaturePolicyFeature::Vibrate)) {
     frame->domWindow()->printErrorMessage(
         "Navigator.vibrate() is not enabled in feature policy for this "
diff --git a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
index 3e3e9e7..f1e7ff7 100644
--- a/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/AnalyserNode.cpp
@@ -141,9 +141,7 @@
 }
 
 void AnalyserHandler::updatePullStatus() {
-#if DCHECK_IS_ON()
   DCHECK(context()->isGraphOwner());
-#endif
 
   if (output(0).isConnected()) {
     // When an AudioBasicInspectorNode is connected to a downstream node, it
diff --git a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
index 63af742..fae3a93 100644
--- a/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
+++ b/third_party/WebKit/Source/modules/webaudio/BaseAudioContext.h
@@ -290,10 +290,10 @@
   void lock() { deferredTaskHandler().lock(); }
   bool tryLock() { return deferredTaskHandler().tryLock(); }
   void unlock() { deferredTaskHandler().unlock(); }
-#if DCHECK_IS_ON()
+
   // Returns true if this thread owns the context's lock.
   bool isGraphOwner() { return deferredTaskHandler().isGraphOwner(); }
-#endif
+
   using AutoLocker = DeferredTaskHandler::AutoLocker;
 
   // Returns the maximum numuber of channels we can support.
diff --git a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
index 71ef965c..0c39337 100644
--- a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.cpp
@@ -141,10 +141,20 @@
       context() && context()->hasRealtimeConstraint(), m_normalize));
 
   {
+    // The context must be locked since changing the buffer can
+    // re-configure the number of channels that are output.
+    BaseAudioContext::AutoLocker contextLocker(context());
+
     // Synchronize with process().
     MutexLocker locker(m_processLock);
     m_reverb = std::move(reverb);
     m_buffer = buffer;
+    if (buffer) {
+      // This will propagate the channel count to any nodes connected further
+      // downstream in the graph.
+      output(0).setNumberOfChannels(computeNumberOfOutputChannels(
+          input(0).numberOfChannels(), m_buffer->numberOfChannels()));
+    }
   }
 }
 
@@ -177,6 +187,15 @@
   return std::numeric_limits<double>::infinity();
 }
 
+unsigned ConvolverHandler::computeNumberOfOutputChannels(
+    unsigned inputChannels,
+    unsigned responseChannels) const {
+  // The number of output channels for a Convolver must be one or two.
+  // And can only be one if there's a mono source and a mono response
+  // buffer.
+  return clampTo(std::max(inputChannels, responseChannels), 1, 2);
+}
+
 void ConvolverHandler::setChannelCount(unsigned long channelCount,
                                        ExceptionState& exceptionState) {
   DCHECK(isMainThread());
@@ -205,9 +224,7 @@
 
 void ConvolverHandler::checkNumberOfChannelsForInput(AudioNodeInput* input) {
   DCHECK(context()->isAudioThread());
-#if DCHECK_IS_ON()
   DCHECK(context()->isGraphOwner());
-#endif
 
   DCHECK(input);
   DCHECK_EQ(input, &this->input(0));
@@ -215,10 +232,8 @@
     return;
 
   if (m_buffer) {
-    unsigned numberOfChannels = input->numberOfChannels();
-    unsigned numberOfReverbeChannels = m_buffer->numberOfChannels();
-    unsigned numberOfOutputChannels =
-        std::min(2u, std::max(numberOfChannels, numberOfReverbeChannels));
+    unsigned numberOfOutputChannels = computeNumberOfOutputChannels(
+        input->numberOfChannels(), m_buffer->numberOfChannels());
 
     if (isInitialized() &&
         numberOfOutputChannels != output(0).numberOfChannels()) {
diff --git a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.h b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.h
index ba221ab..ab77e47 100644
--- a/third_party/WebKit/Source/modules/webaudio/ConvolverNode.h
+++ b/third_party/WebKit/Source/modules/webaudio/ConvolverNode.h
@@ -65,6 +65,12 @@
   double tailTime() const override;
   double latencyTime() const override;
 
+  // Determine how many output channels to use from the number of
+  // input channels and the number of channels in the impulse response
+  // buffer.
+  unsigned computeNumberOfOutputChannels(unsigned inputChannels,
+                                         unsigned responseChannels) const;
+
   std::unique_ptr<Reverb> m_reverb;
   // This Persistent doesn't make a reference cycle including the owner
   // ConvolverNode.
diff --git a/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.cpp b/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.cpp
index f03fd8605..e68c869 100644
--- a/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.cpp
@@ -65,11 +65,15 @@
   m_contextGraphMutex.lock();
 }
 
-#if DCHECK_IS_ON()
 bool DeferredTaskHandler::isGraphOwner() {
+#if DCHECK_IS_ON()
   return m_contextGraphMutex.locked();
-}
+#else
+  // The method is only used inside of DCHECK() so it must be no-op in the
+  // release build. Returning false so we can catch when it happens.
+  return false;
 #endif
+}
 
 void DeferredTaskHandler::addDeferredBreakConnection(AudioHandler& node) {
   DCHECK(isAudioThread());
diff --git a/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.h b/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.h
index 4cd4909..8f740150 100644
--- a/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.h
+++ b/third_party/WebKit/Source/modules/webaudio/DeferredTaskHandler.h
@@ -128,10 +128,8 @@
   // MUST NOT be used in the real-time audio context.
   void offlineLock();
 
-#if DCHECK_IS_ON()
   // Returns true if this thread owns the context's lock.
   bool isGraphOwner();
-#endif
 
   class MODULES_EXPORT AutoLocker {
     STACK_ALLOCATED();
diff --git a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
index d8e5c2ed..0c94207 100644
--- a/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
+++ b/third_party/WebKit/Source/modules/webaudio/IIRFilterNode.cpp
@@ -88,29 +88,6 @@
     return nullptr;
   }
 
-  // Make sure all coefficents are finite.
-  for (size_t k = 0; k < feedforwardCoef.size(); ++k) {
-    double c = feedforwardCoef[k];
-    if (!std::isfinite(c)) {
-      String name = "feedforward coefficient " + String::number(k);
-      exceptionState.throwDOMException(
-          InvalidStateError,
-          ExceptionMessages::notAFiniteNumber(c, name.ascii().data()));
-      return nullptr;
-    }
-  }
-
-  for (size_t k = 0; k < feedbackCoef.size(); ++k) {
-    double c = feedbackCoef[k];
-    if (!std::isfinite(c)) {
-      String name = "feedback coefficient " + String::number(k);
-      exceptionState.throwDOMException(
-          InvalidStateError,
-          ExceptionMessages::notAFiniteNumber(c, name.ascii().data()));
-      return nullptr;
-    }
-  }
-
   return new IIRFilterNode(context, feedforwardCoef, feedbackCoef);
 }
 
diff --git a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5 b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
index 12135b4..5b1eea80 100644
--- a/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
+++ b/third_party/WebKit/Source/platform/RuntimeEnabledFeatures.json5
@@ -802,7 +802,7 @@
       status: "experimental",
     },
     {
-      name: "RestrictCompleteURLCharacterSet",
+      name: "RestrictCanRequestURLCharacterSet",
       status: "experimental",
     },
     // Handles frame scrolling via the root PaintLayer instead of the FrameView.
diff --git a/third_party/WebKit/Source/platform/audio/AudioArray.h b/third_party/WebKit/Source/platform/audio/AudioArray.h
index 8965c14..a05bf4f 100644
--- a/third_party/WebKit/Source/platform/audio/AudioArray.h
+++ b/third_party/WebKit/Source/platform/audio/AudioArray.h
@@ -58,7 +58,7 @@
     // Although n is a size_t, its true limit is max unsigned because we use
     // unsigned in zeroRange() and copyToRange(). Also check for integer
     // overflow.
-    RELEASE_ASSERT(n <= std::numeric_limits<unsigned>::max() / sizeof(T));
+    CHECK_LE(n, std::numeric_limits<unsigned>::max() / sizeof(T));
 
     unsigned initialSize = sizeof(T) * n;
 
@@ -79,12 +79,12 @@
       static size_t extraAllocationBytes = 0;
 
       // Again, check for integer overflow.
-      RELEASE_ASSERT(initialSize + extraAllocationBytes >= initialSize);
+      CHECK_GE(initialSize + extraAllocationBytes, initialSize);
 
       T* allocation = static_cast<T*>(WTF::Partitions::fastMalloc(
           initialSize + extraAllocationBytes,
           WTF_HEAP_PROFILER_TYPE_NAME(AudioArray<T>)));
-      RELEASE_ASSERT(allocation);
+      CHECK(allocation);
 
       T* alignedData = alignedAddress(allocation, alignment);
 
diff --git a/third_party/WebKit/Source/platform/audio/AudioBus.cpp b/third_party/WebKit/Source/platform/audio/AudioBus.cpp
index 2fbbb68..5d59fc4 100644
--- a/third_party/WebKit/Source/platform/audio/AudioBus.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioBus.cpp
@@ -49,7 +49,7 @@
 PassRefPtr<AudioBus> AudioBus::create(unsigned numberOfChannels,
                                       size_t length,
                                       bool allocate) {
-  ASSERT(numberOfChannels <= MaxBusChannels);
+  DCHECK_LE(numberOfChannels, MaxBusChannels);
   if (numberOfChannels > MaxBusChannels)
     return nullptr;
 
@@ -81,7 +81,7 @@
 }
 
 void AudioBus::resizeSmaller(size_t newLength) {
-  ASSERT(newLength <= m_length);
+  DCHECK_LE(newLength, m_length);
   if (newLength <= m_length)
     m_length = newLength;
 
@@ -479,7 +479,7 @@
   }
 
   unsigned numberOfChannels = this->numberOfChannels();
-  ASSERT(numberOfChannels <= MaxBusChannels);
+  DCHECK_LE(numberOfChannels, MaxBusChannels);
   if (numberOfChannels > MaxBusChannels)
     return;
 
@@ -615,7 +615,8 @@
     bool mixToMono,
     double newSampleRate) {
   // sourceBus's sample-rate must be known.
-  ASSERT(sourceBus && sourceBus->sampleRate());
+  DCHECK(sourceBus);
+  DCHECK(sourceBus->sampleRate());
   if (!sourceBus || !sourceBus->sampleRate())
     return nullptr;
 
diff --git a/third_party/WebKit/Source/platform/audio/AudioChannel.cpp b/third_party/WebKit/Source/platform/audio/AudioChannel.cpp
index 751fc1d..459e85a 100644
--- a/third_party/WebKit/Source/platform/audio/AudioChannel.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioChannel.cpp
@@ -36,7 +36,7 @@
 using namespace VectorMath;
 
 void AudioChannel::resizeSmaller(size_t newLength) {
-  ASSERT(newLength <= m_length);
+  DCHECK_LE(newLength, m_length);
   if (newLength <= m_length)
     m_length = newLength;
 }
diff --git a/third_party/WebKit/Source/platform/audio/AudioDSPKernelProcessor.cpp b/third_party/WebKit/Source/platform/audio/AudioDSPKernelProcessor.cpp
index dcea2266..45bdfd4 100644
--- a/third_party/WebKit/Source/platform/audio/AudioDSPKernelProcessor.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioDSPKernelProcessor.cpp
@@ -67,7 +67,8 @@
 void AudioDSPKernelProcessor::process(const AudioBus* source,
                                       AudioBus* destination,
                                       size_t framesToProcess) {
-  ASSERT(source && destination);
+  DCHECK(source);
+  DCHECK(destination);
   if (!source || !destination)
     return;
 
diff --git a/third_party/WebKit/Source/platform/audio/AudioDelayDSPKernel.cpp b/third_party/WebKit/Source/platform/audio/AudioDelayDSPKernel.cpp
index 2630b916..4324c5c 100644
--- a/third_party/WebKit/Source/platform/audio/AudioDelayDSPKernel.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioDelayDSPKernel.cpp
@@ -45,7 +45,8 @@
       m_maxDelayTime(maxDelayTime),
       m_writeIndex(0),
       m_firstTime(true) {
-  ASSERT(maxDelayTime > 0.0 && !std::isnan(maxDelayTime));
+  DCHECK_GT(maxDelayTime, 0.0);
+  DCHECK(!std::isnan(maxDelayTime));
   if (maxDelayTime <= 0.0 || std::isnan(maxDelayTime))
     return;
 
@@ -91,7 +92,8 @@
   if (!bufferLength)
     return;
 
-  ASSERT(source && destination);
+  DCHECK(source);
+  DCHECK(destination);
   if (!source || !destination)
     return;
 
diff --git a/third_party/WebKit/Source/platform/audio/AudioResamplerKernel.cpp b/third_party/WebKit/Source/platform/audio/AudioResamplerKernel.cpp
index 3a948618..973a1cb6 100644
--- a/third_party/WebKit/Source/platform/audio/AudioResamplerKernel.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioResamplerKernel.cpp
@@ -46,7 +46,7 @@
 float* AudioResamplerKernel::getSourcePointer(
     size_t framesToProcess,
     size_t* numberOfSourceFramesNeededP) {
-  ASSERT(framesToProcess <= MaxFramesToProcess);
+  DCHECK_LE(framesToProcess, MaxFramesToProcess);
 
   // Calculate the next "virtual" index.  After process() is called,
   // m_virtualReadIndex will equal this value.
@@ -75,7 +75,7 @@
 }
 
 void AudioResamplerKernel::process(float* destination, size_t framesToProcess) {
-  ASSERT(framesToProcess <= MaxFramesToProcess);
+  DCHECK_LE(framesToProcess, MaxFramesToProcess);
 
   float* source = m_sourceBuffer.data();
 
@@ -92,11 +92,11 @@
   double virtualReadIndex = m_virtualReadIndex;
 
   // Sanity check source buffer access.
-  ASSERT(framesToProcess > 0);
-  ASSERT(virtualReadIndex >= 0 &&
-         1 + static_cast<unsigned>(virtualReadIndex +
-                                   (framesToProcess - 1) * rate) <
-             m_sourceBuffer.size());
+  DCHECK_GT(framesToProcess, 0u);
+  DCHECK_GE(virtualReadIndex, 0);
+  DCHECK_LT(1 + static_cast<unsigned>(virtualReadIndex +
+                                      (framesToProcess - 1) * rate),
+            m_sourceBuffer.size());
 
   // Do the linear interpolation.
   int n = framesToProcess;
diff --git a/third_party/WebKit/Source/platform/audio/AudioUtilities.cpp b/third_party/WebKit/Source/platform/audio/AudioUtilities.cpp
index 77000c25..7d66756 100644
--- a/third_party/WebKit/Source/platform/audio/AudioUtilities.cpp
+++ b/third_party/WebKit/Source/platform/audio/AudioUtilities.cpp
@@ -36,7 +36,7 @@
 }
 
 float linearToDecibels(float linear) {
-  ASSERT(linear >= 0);
+  DCHECK_GE(linear, 0);
 
   return 20 * log10f(linear);
 }
@@ -47,7 +47,7 @@
 }
 
 size_t timeToSampleFrame(double time, double sampleRate) {
-  ASSERT(time >= 0);
+  DCHECK_GE(time, 0);
   double frame = round(time * sampleRate);
 
   // Just return the largest possible size_t value if necessary.
diff --git a/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp b/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp
index 1cf4ce5..3b2d84c 100644
--- a/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp
+++ b/third_party/WebKit/Source/platform/audio/DirectConvolver.cpp
@@ -50,13 +50,13 @@
                               const float* sourceP,
                               float* destP,
                               size_t framesToProcess) {
-  ASSERT(framesToProcess == m_inputBlockSize);
+  DCHECK_EQ(framesToProcess, m_inputBlockSize);
   if (framesToProcess != m_inputBlockSize)
     return;
 
   // Only support kernelSize <= m_inputBlockSize
   size_t kernelSize = convolutionKernel->size();
-  ASSERT(kernelSize <= m_inputBlockSize);
+  DCHECK_LE(kernelSize, m_inputBlockSize);
   if (kernelSize > m_inputBlockSize)
     return;
 
diff --git a/third_party/WebKit/Source/platform/audio/DynamicsCompressor.cpp b/third_party/WebKit/Source/platform/audio/DynamicsCompressor.cpp
index 38d1da6..074a05a 100644
--- a/third_party/WebKit/Source/platform/audio/DynamicsCompressor.cpp
+++ b/third_party/WebKit/Source/platform/audio/DynamicsCompressor.cpp
@@ -51,7 +51,7 @@
 }
 
 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value) {
-  ASSERT(parameterID < ParamLast);
+  DCHECK_LT(parameterID, static_cast<unsigned>(ParamLast));
   if (parameterID < ParamLast)
     m_parameters[parameterID] = value;
 }
@@ -84,7 +84,7 @@
 }
 
 float DynamicsCompressor::parameterValue(unsigned parameterID) {
-  ASSERT(parameterID < ParamLast);
+  DCHECK_LT(parameterID, static_cast<unsigned>(ParamLast));
   return m_parameters[parameterID];
 }
 
@@ -100,7 +100,8 @@
   unsigned numberOfChannels = destinationBus->numberOfChannels();
   unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
 
-  ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
+  DCHECK_EQ(numberOfChannels, m_numberOfChannels);
+  DCHECK(numberOfSourceChannels);
 
   if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
     destinationBus->zero();
diff --git a/third_party/WebKit/Source/platform/audio/DynamicsCompressorKernel.cpp b/third_party/WebKit/Source/platform/audio/DynamicsCompressorKernel.cpp
index cd62be4..7004855 100644
--- a/third_party/WebKit/Source/platform/audio/DynamicsCompressorKernel.cpp
+++ b/third_party/WebKit/Source/platform/audio/DynamicsCompressorKernel.cpp
@@ -216,7 +216,7 @@
     float releaseZone2,
     float releaseZone3,
     float releaseZone4) {
-  ASSERT(m_preDelayBuffers.size() == numberOfChannels);
+  DCHECK_EQ(m_preDelayBuffers.size(), numberOfChannels);
 
   float sampleRate = this->sampleRate();
 
diff --git a/third_party/WebKit/Source/platform/audio/HRTFDatabase.cpp b/third_party/WebKit/Source/platform/audio/HRTFDatabase.cpp
index 2787305..7da4c3e 100644
--- a/third_party/WebKit/Source/platform/audio/HRTFDatabase.cpp
+++ b/third_party/WebKit/Source/platform/audio/HRTFDatabase.cpp
@@ -76,7 +76,7 @@
             static_cast<float>(jj) / static_cast<float>(InterpolationFactor);
         m_elevations[i + jj] = HRTFElevation::createByInterpolatingSlices(
             m_elevations[i].get(), m_elevations[j].get(), x, sampleRate);
-        ASSERT(m_elevations[i + jj].get());
+        DCHECK(m_elevations[i + jj].get());
       }
     }
   }
diff --git a/third_party/WebKit/Source/platform/audio/HRTFDatabaseLoader.cpp b/third_party/WebKit/Source/platform/audio/HRTFDatabaseLoader.cpp
index ff73e21..d8cd48fd 100644
--- a/third_party/WebKit/Source/platform/audio/HRTFDatabaseLoader.cpp
+++ b/third_party/WebKit/Source/platform/audio/HRTFDatabaseLoader.cpp
@@ -52,7 +52,7 @@
 
   RefPtr<HRTFDatabaseLoader> loader = getLoaderMap().at(sampleRate);
   if (loader) {
-    ASSERT(sampleRate == loader->databaseSampleRate());
+    DCHECK_EQ(sampleRate, loader->databaseSampleRate());
     return loader.release();
   }
 
diff --git a/third_party/WebKit/Source/platform/audio/HRTFElevation.cpp b/third_party/WebKit/Source/platform/audio/HRTFElevation.cpp
index bf0df80..af42a3e 100644
--- a/third_party/WebKit/Source/platform/audio/HRTFElevation.cpp
+++ b/third_party/WebKit/Source/platform/audio/HRTFElevation.cpp
@@ -309,11 +309,13 @@
     HRTFElevation* hrtfElevation2,
     float x,
     float sampleRate) {
-  ASSERT(hrtfElevation1 && hrtfElevation2);
+  DCHECK(hrtfElevation1);
+  DCHECK(hrtfElevation2);
   if (!hrtfElevation1 || !hrtfElevation2)
     return nullptr;
 
-  ASSERT(x >= 0.0 && x < 1.0);
+  DCHECK_GE(x, 0.0);
+  DCHECK_LT(x, 1.0);
 
   std::unique_ptr<HRTFKernelList> kernelListL =
       WTF::makeUnique<HRTFKernelList>(NumberOfTotalAzimuths);
diff --git a/third_party/WebKit/Source/platform/audio/HRTFKernel.cpp b/third_party/WebKit/Source/platform/audio/HRTFKernel.cpp
index 32e5a4f..f6277e1 100644
--- a/third_party/WebKit/Source/platform/audio/HRTFKernel.cpp
+++ b/third_party/WebKit/Source/platform/audio/HRTFKernel.cpp
@@ -53,8 +53,8 @@
     return 0;
 
   // Check for power-of-2.
-  ASSERT(1UL << static_cast<unsigned>(log2(analysisFFTSize)) ==
-         analysisFFTSize);
+  DCHECK_EQ(1UL << static_cast<unsigned>(log2(analysisFFTSize)),
+            analysisFFTSize);
 
   FFTFrame estimationFrame(analysisFFTSize);
   estimationFrame.doFFT(impulseP);
@@ -83,7 +83,7 @@
   // Quick fade-out (apply window) at truncation point
   unsigned numberOfFadeOutFrames = static_cast<unsigned>(
       sampleRate / 4410);  // 10 sample-frames @44.1KHz sample-rate
-  ASSERT(numberOfFadeOutFrames < truncatedResponseLength);
+  DCHECK_LT(numberOfFadeOutFrames, truncatedResponseLength);
   if (numberOfFadeOutFrames < truncatedResponseLength) {
     for (unsigned i = truncatedResponseLength - numberOfFadeOutFrames;
          i < truncatedResponseLength; ++i) {
@@ -116,16 +116,18 @@
     HRTFKernel* kernel1,
     HRTFKernel* kernel2,
     float x) {
-  ASSERT(kernel1 && kernel2);
+  DCHECK(kernel1);
+  DCHECK(kernel2);
   if (!kernel1 || !kernel2)
     return nullptr;
 
-  ASSERT(x >= 0.0 && x < 1.0);
+  DCHECK_GE(x, 0.0);
+  DCHECK_LT(x, 1.0);
   x = clampTo(x, 0.0f, 1.0f);
 
   float sampleRate1 = kernel1->sampleRate();
   float sampleRate2 = kernel2->sampleRate();
-  ASSERT(sampleRate1 == sampleRate2);
+  DCHECK_EQ(sampleRate1, sampleRate2);
   if (sampleRate1 != sampleRate2)
     return nullptr;
 
diff --git a/third_party/WebKit/Source/platform/audio/HRTFPanner.cpp b/third_party/WebKit/Source/platform/audio/HRTFPanner.cpp
index 0861ebc2..efc2f8b 100644
--- a/third_party/WebKit/Source/platform/audio/HRTFPanner.cpp
+++ b/third_party/WebKit/Source/platform/audio/HRTFPanner.cpp
@@ -210,7 +210,7 @@
 
   // This algorithm currently requires that we process in power-of-two size
   // chunks at least AudioUtilities::kRenderQuantumFrames.
-  ASSERT(1UL << static_cast<int>(log2(framesToProcess)) == framesToProcess);
+  DCHECK_EQ(1UL << static_cast<int>(log2(framesToProcess)), framesToProcess);
   DCHECK_GE(framesToProcess, AudioUtilities::kRenderQuantumFrames);
 
   const unsigned framesPerSegment = AudioUtilities::kRenderQuantumFrames;
@@ -240,10 +240,10 @@
       return;
     }
 
-    ASSERT(frameDelayL1 / sampleRate() < MaxDelayTimeSeconds &&
-           frameDelayR1 / sampleRate() < MaxDelayTimeSeconds);
-    ASSERT(frameDelayL2 / sampleRate() < MaxDelayTimeSeconds &&
-           frameDelayR2 / sampleRate() < MaxDelayTimeSeconds);
+    DCHECK_LT(frameDelayL1 / sampleRate(), MaxDelayTimeSeconds);
+    DCHECK_LT(frameDelayR1 / sampleRate(), MaxDelayTimeSeconds);
+    DCHECK_LT(frameDelayL2 / sampleRate(), MaxDelayTimeSeconds);
+    DCHECK_LT(frameDelayR2 / sampleRate(), MaxDelayTimeSeconds);
 
     // Crossfade inter-aural delays based on transitions.
     double frameDelayL =
diff --git a/third_party/WebKit/Source/platform/audio/IIRFilter.cpp b/third_party/WebKit/Source/platform/audio/IIRFilter.cpp
index 2dca1b1..c914a6d 100644
--- a/third_party/WebKit/Source/platform/audio/IIRFilter.cpp
+++ b/third_party/WebKit/Source/platform/audio/IIRFilter.cpp
@@ -65,7 +65,7 @@
 
   // Sanity check to see if the feedback coefficients have been scaled
   // appropriately. It must be EXACTLY 1!
-  ASSERT(feedback[0] == 1);
+  DCHECK_EQ(feedback[0], 1);
 
   int feedbackLength = m_feedback->size();
   int feedforwardLength = m_feedforward->size();
diff --git a/third_party/WebKit/Source/platform/audio/MultiChannelResampler.cpp b/third_party/WebKit/Source/platform/audio/MultiChannelResampler.cpp
index 9e3e3be..175af08f 100644
--- a/third_party/WebKit/Source/platform/audio/MultiChannelResampler.cpp
+++ b/third_party/WebKit/Source/platform/audio/MultiChannelResampler.cpp
@@ -74,7 +74,7 @@
       return;
 
     // Copy the channel data from what we received from m_multiChannelProvider.
-    ASSERT(m_currentChannel <= m_numberOfChannels);
+    DCHECK_LE(m_currentChannel, m_numberOfChannels);
     if (m_currentChannel < m_numberOfChannels) {
       memcpy(bus->channel(0)->mutableData(),
              m_multiChannelBus->channel(m_currentChannel)->data(),
diff --git a/third_party/WebKit/Source/platform/audio/ReverbConvolverStage.cpp b/third_party/WebKit/Source/platform/audio/ReverbConvolverStage.cpp
index 3cb3469..3d1f3e78 100644
--- a/third_party/WebKit/Source/platform/audio/ReverbConvolverStage.cpp
+++ b/third_party/WebKit/Source/platform/audio/ReverbConvolverStage.cpp
@@ -61,7 +61,7 @@
     m_fftConvolver = WTF::makeUnique<FFTConvolver>(fftSize);
   } else {
     DCHECK(!stageOffset);
-    ASSERT(stageLength <= fftSize / 2);
+    DCHECK_LE(stageLength, fftSize / 2);
 
     m_directKernel = WTF::wrapUnique(new AudioFloatArray(fftSize / 2));
     m_directKernel->copyToRange(impulseResponse, 0, stageLength);
@@ -77,7 +77,7 @@
   // this out...
   size_t halfSize = fftSize / 2;
   if (!m_directMode) {
-    ASSERT(totalDelay >= halfSize);
+    DCHECK_GE(totalDelay, halfSize);
     if (totalDelay >= halfSize)
       totalDelay -= halfSize;
   }
@@ -177,7 +177,7 @@
     memcpy(preDelayedDestination, source, sizeof(float) * framesToProcess);
     m_preReadWriteIndex += framesToProcess;
 
-    ASSERT(m_preReadWriteIndex <= m_preDelayLength);
+    DCHECK_LE(m_preReadWriteIndex, m_preDelayLength);
     if (m_preReadWriteIndex >= m_preDelayLength)
       m_preReadWriteIndex = 0;
   }
diff --git a/third_party/WebKit/Source/platform/audio/ReverbInputBuffer.cpp b/third_party/WebKit/Source/platform/audio/ReverbInputBuffer.cpp
index ef1f7fa..8838bdd 100644
--- a/third_party/WebKit/Source/platform/audio/ReverbInputBuffer.cpp
+++ b/third_party/WebKit/Source/platform/audio/ReverbInputBuffer.cpp
@@ -44,7 +44,7 @@
          sizeof(float) * numberOfFrames);
 
   m_writeIndex += numberOfFrames;
-  ASSERT(m_writeIndex <= bufferLength);
+  DCHECK_LE(m_writeIndex, bufferLength);
 
   if (m_writeIndex >= bufferLength)
     m_writeIndex = 0;
diff --git a/third_party/WebKit/Source/platform/audio/SincResampler.cpp b/third_party/WebKit/Source/platform/audio/SincResampler.cpp
index 7705c19..6d358ee 100644
--- a/third_party/WebKit/Source/platform/audio/SincResampler.cpp
+++ b/third_party/WebKit/Source/platform/audio/SincResampler.cpp
@@ -156,7 +156,8 @@
 
   // Consumes samples from the in-memory buffer.
   void provideInput(AudioBus* bus, size_t framesToProcess) override {
-    ASSERT(m_source && bus);
+    DCHECK(m_source);
+    DCHECK(bus);
     if (!m_source || !bus)
       return;
 
diff --git a/third_party/WebKit/Source/platform/audio/android/FFTFrameOpenMAXDLAndroid.cpp b/third_party/WebKit/Source/platform/audio/android/FFTFrameOpenMAXDLAndroid.cpp
index 366e614..9fc2c676 100644
--- a/third_party/WebKit/Source/platform/audio/android/FFTFrameOpenMAXDLAndroid.cpp
+++ b/third_party/WebKit/Source/platform/audio/android/FFTFrameOpenMAXDLAndroid.cpp
@@ -35,9 +35,7 @@
 
 namespace blink {
 
-#if DCHECK_IS_ON()
 const unsigned kMaxFFTPow2Size = 15;
-#endif
 
 // Normal constructor: allocates for a given fftSize.
 FFTFrame::FFTFrame(unsigned fftSize)
@@ -49,7 +47,7 @@
       m_inverseContext(nullptr),
       m_complexData(fftSize) {
   // We only allow power of two.
-  ASSERT(1UL << m_log2FFTSize == m_FFTSize);
+  DCHECK_EQ(1UL << m_log2FFTSize, m_FFTSize);
 
   m_forwardContext = contextForSize(m_log2FFTSize);
   m_inverseContext = contextForSize(m_log2FFTSize);
@@ -143,7 +141,7 @@
 
 OMXFFTSpec_R_F32* FFTFrame::contextForSize(unsigned log2FFTSize) {
   DCHECK(log2FFTSize);
-  ASSERT(log2FFTSize <= kMaxFFTPow2Size);
+  DCHECK_LE(log2FFTSize, kMaxFFTPow2Size);
   int bufSize;
   OMXResult status = omxSP_FFTGetBufSize_R_F32(log2FFTSize, &bufSize);
 
diff --git a/third_party/WebKit/Source/platform/audio/ffmpeg/FFTFrameFFMPEG.cpp b/third_party/WebKit/Source/platform/audio/ffmpeg/FFTFrameFFMPEG.cpp
index 8c6c046..f46ac8c2 100644
--- a/third_party/WebKit/Source/platform/audio/ffmpeg/FFTFrameFFMPEG.cpp
+++ b/third_party/WebKit/Source/platform/audio/ffmpeg/FFTFrameFFMPEG.cpp
@@ -42,11 +42,9 @@
 
 namespace blink {
 
-#if DCHECK_IS_ON()
 // Max FFT size for FFMPEG.  WebAudio currently only uses FFTs up to size 15
 // (2^15 points).
 const int kMaxFFTPow2Size = 16;
-#endif
 
 // Normal constructor: allocates for a given fftSize.
 FFTFrame::FFTFrame(unsigned fftSize)
@@ -58,7 +56,7 @@
       m_inverseContext(nullptr),
       m_complexData(fftSize) {
   // We only allow power of two.
-  ASSERT(1UL << m_log2FFTSize == m_FFTSize);
+  DCHECK_EQ(1UL << m_log2FFTSize, m_FFTSize);
 
   m_forwardContext = contextForSize(fftSize, DFT_R2C);
   m_inverseContext = contextForSize(fftSize, IDFT_C2R);
@@ -157,7 +155,7 @@
   // by sharing the FFTFrames on a per-thread basis.
   DCHECK(fftSize);
   int pow2size = static_cast<int>(log2(fftSize));
-  ASSERT(pow2size < kMaxFFTPow2Size);
+  DCHECK_LT(pow2size, kMaxFFTPow2Size);
 
   RDFTContext* context = av_rdft_init(pow2size, (RDFTransformType)trans);
   return context;
diff --git a/third_party/WebKit/Source/platform/audio/ipp/FFTFrameIPP.cpp b/third_party/WebKit/Source/platform/audio/ipp/FFTFrameIPP.cpp
index f745e050..367707c 100644
--- a/third_party/WebKit/Source/platform/audio/ipp/FFTFrameIPP.cpp
+++ b/third_party/WebKit/Source/platform/audio/ipp/FFTFrameIPP.cpp
@@ -47,8 +47,8 @@
       m_imagData(fftSize / 2),
       m_complexData(fftSize) {
   // We only allow power of two.
-  ASSERT(1UL << m_log2FFTSize == m_FFTSize);
-  ASSERT(m_log2FFTSize <= maximumFFTPower2Size);
+  DCHECK_EQ(1UL << m_log2FFTSize, m_FFTSize);
+  DCHECK_LE(m_log2FFTSize, maximumFFTPower2Size);
 
   ippsDFTInitAlloc_R_32f(&m_DFTSpec, m_FFTSize, IPP_FFT_NODIV_BY_ANY,
                          ippAlgHintFast);
diff --git a/third_party/WebKit/Source/platform/audio/mac/FFTFrameMac.cpp b/third_party/WebKit/Source/platform/audio/mac/FFTFrameMac.cpp
index 47562dc1..f57f096 100644
--- a/third_party/WebKit/Source/platform/audio/mac/FFTFrameMac.cpp
+++ b/third_party/WebKit/Source/platform/audio/mac/FFTFrameMac.cpp
@@ -49,7 +49,7 @@
   m_log2FFTSize = static_cast<unsigned>(log2(fftSize));
 
   // We only allow power of two
-  ASSERT(1UL << m_log2FFTSize == m_FFTSize);
+  DCHECK_EQ(1UL << m_log2FFTSize, m_FFTSize);
 
   // Lazily create and share fftSetup with other frames
   m_FFTSetup = fftSetupForSize(fftSize);
@@ -116,7 +116,7 @@
   }
 
   int pow2size = static_cast<int>(log2(fftSize));
-  ASSERT(pow2size < kMaxFFTPow2Size);
+  DCHECK_LT(pow2size, kMaxFFTPow2Size);
   if (!fftSetups[pow2size])
     fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2);
 
diff --git a/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h b/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
index a53210d..340d96c 100644
--- a/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
+++ b/third_party/WebKit/Source/platform/graphics/ExpensiveCanvasHeuristicParameters.h
@@ -98,7 +98,7 @@
   // acceleration on the destination first. If that does not succeed,
   // we disable acceleration on the source canvas. Either way, future
   // readbacks are prevented.
-  EnableAccelerationToAvoidReadbacks = 0,
+  EnableAccelerationToAvoidReadbacks = 1,
 
 };  // enum
 
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
index 0442195..f9ca955 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.cpp
@@ -231,6 +231,12 @@
   if (isUnique() || other->isUnique())
     return false;
 
+  if (hasSuborigin() != other->hasSuborigin())
+    return false;
+
+  if (hasSuborigin() && suborigin()->name() != other->suborigin()->name())
+    return false;
+
   // document.domain handling, as per
   // https://html.spec.whatwg.org/multipage/browsers.html#dom-document-domain:
   //
@@ -257,17 +263,6 @@
   return canAccess;
 }
 
-bool SecurityOrigin::canAccessCheckSuborigins(
-    const SecurityOrigin* other) const {
-  if (hasSuborigin() != other->hasSuborigin())
-    return false;
-
-  if (hasSuborigin() && suborigin()->name() != other->suborigin()->name())
-    return false;
-
-  return canAccess(other);
-}
-
 bool SecurityOrigin::passesFileCheck(const SecurityOrigin* other) const {
   ASSERT(isLocal() && other->isLocal());
 
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.h b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.h
index a88e255..838e219 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.h
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityOrigin.h
@@ -102,19 +102,9 @@
   // SecurityOrigin. For example, call this function before allowing
   // script from one security origin to read or write objects from
   // another SecurityOrigin.
-  bool canAccess(const SecurityOrigin*) const;
-
-  // Same as canAccess, except that it adds an additional check to make sure
-  // that the SecurityOrigins have the same suborigin name. If you're not
-  // familiar with Suborigins, you probably want canAccess() for now.
-  // Suborigins is a spec in progress, and where it should be enforced is
-  // still in flux. See https://crbug.com/336894 for more details.
   //
-  // TODO(jww): Once the Suborigin spec has become more settled, and we are
-  // confident in the correctness of our implementation, canAccess should be
-  // made to check the suborigin and this should be turned into
-  // canAccessBypassSuborigin check, which should be the exceptional case.
-  bool canAccessCheckSuborigins(const SecurityOrigin*) const;
+  // This takes suborigins into account.
+  bool canAccess(const SecurityOrigin*) const;
 
   // Returns true if this SecurityOrigin can read content retrieved from
   // the given URL. For example, call this function before issuing
@@ -122,11 +112,10 @@
   bool canRequest(const KURL&) const;
 
   // Same as canRequest, except that it adds an additional check to make sure
-  // that the SecurityOrigin does not have a suborigin name. Like with
-  // canAccessCheckSuborigins() above, if you're not familiar with
-  // Suborigins, you probably want canRequest() for now. Suborigins is a spec
-  // in progress, and where it should be enforced is still in flux. See
-  // https://crbug.com/336894 for more details.
+  // that the SecurityOrigin does not have a suborigin name. If you're not
+  // familiar with Suborigins, you probably want canRequest() for now.
+  // Suborigins is a spec in progress, and where it should be enforced is still
+  // in flux. See https://crbug.com/336894 for more details.
   //
   // TODO(jww): Once the Suborigin spec has become more settled, and we are
   // confident in the correctness of our implementation, canRequest should be
diff --git a/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp b/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
index 11521af..c8eb945 100644
--- a/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
+++ b/third_party/WebKit/Source/platform/weborigin/SecurityOriginTest.cpp
@@ -334,17 +334,16 @@
 
   struct TestCase {
     bool canAccess;
-    bool canAccessCheckSuborigins;
     const char* origin1;
     const char* origin2;
   };
 
   TestCase tests[] = {
-      {true, true, "https://foobar.com", "https://foobar.com"},
-      {false, false, "https://foobar.com", "https://bazbar.com"},
-      {true, false, "https://foobar.com", "https-so://name.foobar.com"},
-      {true, false, "https-so://name.foobar.com", "https://foobar.com"},
-      {true, true, "https-so://name.foobar.com", "https-so://name.foobar.com"},
+      {true, "https://foobar.com", "https://foobar.com"},
+      {false, "https://foobar.com", "https://bazbar.com"},
+      {false, "https://foobar.com", "https-so://name.foobar.com"},
+      {false, "https-so://name.foobar.com", "https://foobar.com"},
+      {true, "https-so://name.foobar.com", "https-so://name.foobar.com"},
   };
 
   for (size_t i = 0; i < WTF_ARRAY_LENGTH(tests); ++i) {
@@ -353,8 +352,6 @@
     RefPtr<SecurityOrigin> origin2 =
         SecurityOrigin::createFromString(tests[i].origin2);
     EXPECT_EQ(tests[i].canAccess, origin1->canAccess(origin2.get()));
-    EXPECT_EQ(tests[i].canAccessCheckSuborigins,
-              origin1->canAccessCheckSuborigins(origin2.get()));
   }
 }
 
diff --git a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
index 919b58b..6a39c13 100644
--- a/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
+++ b/third_party/WebKit/Source/web/WebFrameWidgetImpl.cpp
@@ -203,9 +203,11 @@
 
 void WebFrameWidgetImpl::resizeVisualViewport(const WebSize& newSize) {
   // TODO(alexmos, kenrb): resizing behavior such as this should be changed
-  // to use Page messages.  https://crbug.com/599688.
-  page()->visualViewport().setSize(newSize);
-  page()->visualViewport().clampToBoundaries();
+  // to use Page messages.  This uses the visual viewport size to set size on
+  // both the WebViewImpl size and the Page's VisualViewport. If there are
+  // multiple OOPIFs on a page, this will currently be set redundantly by
+  // each of them. See https://crbug.com/599688.
+  view()->resize(newSize);
 
   view()->didUpdateFullscreenSize();
 }
diff --git a/third_party/WebKit/Tools/OWNERS b/third_party/WebKit/Tools/OWNERS
index c04522f..5c5aa17 100644
--- a/third_party/WebKit/Tools/OWNERS
+++ b/third_party/WebKit/Tools/OWNERS
@@ -1,6 +1,9 @@
 dpranke@chromium.org
-jochen@chromium.org
-peter@chromium.org
-tony@chromium.org
+jeffcarp@chromium.org
+qyearsley@chromium.org
+tansell@chromium.org
+tkent@chromium.org
+wangxianzhu@chromium.org
 
+# TEAM: blink-infra@chromium.org
 # COMPONENT: Blink>Infra
diff --git a/third_party/WebKit/Tools/Scripts/OWNERS b/third_party/WebKit/Tools/Scripts/OWNERS
deleted file mode 100644
index 72e8ffc..0000000
--- a/third_party/WebKit/Tools/Scripts/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-*
diff --git a/third_party/WebKit/public/BUILD.gn b/third_party/WebKit/public/BUILD.gn
index 03fd0e4..febc9f15 100644
--- a/third_party/WebKit/public/BUILD.gn
+++ b/third_party/WebKit/public/BUILD.gn
@@ -747,9 +747,6 @@
   visibility_blink = [ ":mojo_bindings_blink" ]
   sources = [
     "platform/modules/document_metadata/copyless_paste.mojom",
-
-    # The following file will be removed once the downstream is updated.
-    "platform/modules/document_metadata/copyless_paste-deprecated.mojom",
     "platform/modules/installation/installation.mojom",
     "platform/modules/installedapp/installed_app_provider.mojom",
     "platform/modules/installedapp/related_application.mojom",
diff --git a/third_party/WebKit/public/platform/modules/document_metadata/copyless_paste-deprecated.mojom b/third_party/WebKit/public/platform/modules/document_metadata/copyless_paste-deprecated.mojom
deleted file mode 100644
index 6d8f3b6..0000000
--- a/third_party/WebKit/public/platform/modules/document_metadata/copyless_paste-deprecated.mojom
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2017 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.
-
-module blink.mojom;
-
-import "url/mojo/url.mojom";
-
-// Due to the restriction of AppIndexing, all elements should be of the
-// same type. Non-array values are converted to arrays of one element.
-union Values {
-  array<bool> bool_values;
-  array<int64> long_values;
-  array<string> string_values;
-  array<Entity> entity_values;
-};
-
-// Key-value pair for the attributes of an |Entity|.
-struct Property {
-  string name;
-  Values values;
-};
-
-// Top-level metadata entry using schema.org vocabulary.
-// Tree structure of entities is possible.
-// Ref: https://developers.google.com/schemas/formats/json-ld
-struct Entity {
-  string type;  // Correspond to the "@type" key, defined in JSON-LD.
-  array<Property> properties;
-};
-
-struct WebPage {
-  url.mojom.Url url;
-  string title;
-  array<Entity> entities;
-};
diff --git a/third_party/checkstyle/OWNERS b/third_party/checkstyle/OWNERS
index 812e8d9..3daedd9 100644
--- a/third_party/checkstyle/OWNERS
+++ b/third_party/checkstyle/OWNERS
@@ -1 +1,5 @@
-aurimas@chromium.org
+agrieve@chromium.org
+jbudorick@chromium.org
+nyquist@chromium.org
+zpeng@chromium.org
+
diff --git a/third_party/checkstyle/README.chromium b/third_party/checkstyle/README.chromium
index e84fcf8..947034b0 100644
--- a/third_party/checkstyle/README.chromium
+++ b/third_party/checkstyle/README.chromium
@@ -1,8 +1,8 @@
 Name: Checkstyle is a development tool to help programmers write Java code that
       adheres to a coding standard.
 Short Name: checkstyle
-URL: http://checkstyle.sourceforge.net/
-Version: 6.5
+URL: https://github.com/checkstyle/checkstyle
+Version: 7.6.1
 License: LGPL 2.1
 License File: NOT_SHIPPED
 Security Critical: no
@@ -11,5 +11,15 @@
 Checkstyle is used to validate Java code style on Chromium PRESUBMIT step.
 
 Local Modifications:
-- Downloaded checkstyle-6.5-all.jar without source code development
-  documentation.
+None
+
+Update instructions (requires @google.com account):
+- Download fat jar from https://sourceforge.net/projects/checkstyle/files/checkstyle/
+- Modify tools/android/checkstyle/checkstyle.py and verify the new fat jar works
+- Remove existing SHA1 file
+- If gcloud auth tokens are not set up, run
+$ download_from_google_storage --config
+- Upload new fat jar to gcloud. In third_party/checkstyle, run
+$ upload_to_google_storage.py -b chromium-android-tools/checkstyle {new_far_jar}
+- Check in new SHA1 file
+
diff --git a/third_party/checkstyle/checkstyle-6.5-all.jar b/third_party/checkstyle/checkstyle-6.5-all.jar
deleted file mode 100644
index b4f571f..0000000
--- a/third_party/checkstyle/checkstyle-6.5-all.jar
+++ /dev/null
Binary files differ
diff --git a/third_party/checkstyle/checkstyle-7.6.1-all.jar.sha1 b/third_party/checkstyle/checkstyle-7.6.1-all.jar.sha1
new file mode 100644
index 0000000..e0aec8e
--- /dev/null
+++ b/third_party/checkstyle/checkstyle-7.6.1-all.jar.sha1
@@ -0,0 +1 @@
+c6889fa07ec9afb0ca8029be75de31dc29dc4f05
\ No newline at end of file
diff --git a/third_party/libvpx/README.chromium b/third_party/libvpx/README.chromium
index 6848b8a..8d18a4a 100644
--- a/third_party/libvpx/README.chromium
+++ b/third_party/libvpx/README.chromium
@@ -5,9 +5,9 @@
 License File: source/libvpx/LICENSE
 Security Critical: yes
 
-Date: Wednesday March 29 2017
+Date: Friday April 07 2017
 Branch: master
-Commit: 32b3d2f174f69f1484e024b276aff9226a751d4f
+Commit: 6af42f5102ad7c00d3fed389b186663a88d812ee
 
 Description:
 Contains the sources used to compile libvpx binaries used by Google Chrome and
diff --git a/third_party/libvpx/source/config/vpx_version.h b/third_party/libvpx/source/config/vpx_version.h
index 491209c1..eb2eb3d8 100644
--- a/third_party/libvpx/source/config/vpx_version.h
+++ b/third_party/libvpx/source/config/vpx_version.h
@@ -1,7 +1,7 @@
 #define VERSION_MAJOR  1
 #define VERSION_MINOR  6
 #define VERSION_PATCH  1
-#define VERSION_EXTRA  "409-g32b3d2f17"
+#define VERSION_EXTRA  "433-g6af42f510"
 #define VERSION_PACKED ((VERSION_MAJOR<<16)|(VERSION_MINOR<<8)|(VERSION_PATCH))
-#define VERSION_STRING_NOSP "v1.6.1-409-g32b3d2f17"
-#define VERSION_STRING      " v1.6.1-409-g32b3d2f17"
+#define VERSION_STRING_NOSP "v1.6.1-433-g6af42f510"
+#define VERSION_STRING      " v1.6.1-433-g6af42f510"
diff --git a/third_party/polymer/v1_0/bower.json b/third_party/polymer/v1_0/bower.json
index 720d993e..be512b27 100644
--- a/third_party/polymer/v1_0/bower.json
+++ b/third_party/polymer/v1_0/bower.json
@@ -53,7 +53,7 @@
     "paper-radio-button": "PolymerElements/paper-radio-button#1.3.1",
     "paper-radio-group": "PolymerElements/paper-radio-group#1.2.0",
     "paper-ripple": "PolymerElements/paper-ripple#1.0.9",
-    "paper-slider": "PolymerElements/paper-slider#1.0.13",
+    "paper-slider": "PolymerElements/paper-slider#1.0.15",
     "paper-spinner": "PolymerElements/paper-spinner#1.2.0",
     "paper-styles": "PolymerElements/paper-styles#1.1.4",
     "paper-tabs": "PolymerElements/paper-tabs#1.6.2",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-slider/bower.json b/third_party/polymer/v1_0/components-chromium/paper-slider/bower.json
index abca1a4..8062b3ce 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-slider/bower.json
+++ b/third_party/polymer/v1_0/components-chromium/paper-slider/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "paper-slider",
-  "version": "1.0.13",
+  "version": "1.0.15",
   "description": "A material design-style slider",
   "license": "http://polymer.github.io/LICENSE.txt",
   "authors": "The Polymer Authors",
diff --git a/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider-extracted.js b/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider-extracted.js
index b555b0a..d5f02d6 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider-extracted.js
+++ b/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider-extracted.js
@@ -114,8 +114,10 @@
       },
 
       keyBindings: {
-        'left down pagedown home': '_decrementKey',
-        'right up pageup end': '_incrementKey'
+        'left': '_leftKey',
+        'right': '_rightKey',
+        'down pagedown home': '_decrementKey',
+        'up pageup end': '_incrementKey'
       },
 
       /**
@@ -213,7 +215,9 @@
           this._trackStart(event);
         }
 
-        var dx = Math.min(this._maxx, Math.max(this._minx, event.detail.dx));
+        var direction = this._isRTL ? -1 : 1;
+        var dx = Math.min(
+            this._maxx, Math.max(this._minx, event.detail.dx * direction));
         this._x = this._startx + dx;
 
         var immediateValue = this._calcStep(this._calcKnobPosition(this._x / this._w));
@@ -251,6 +255,9 @@
         this._w = this.$.sliderBar.offsetWidth;
         var rect = this.$.sliderBar.getBoundingClientRect();
         var ratio = (event.detail.x - rect.left) / this._w;
+        if (this._isRTL) {
+          ratio = 1 - ratio;
+        }
         var prevRatio = this.ratio;
 
         this._setTransiting(true);
@@ -292,6 +299,9 @@
         if (steps > maxMarkers) {
           steps = maxMarkers;
         }
+        if (steps < 0 || !isFinite(steps)) {
+          steps = 0;
+        }
         this._setMarkers(new Array(steps));
       },
 
@@ -315,6 +325,27 @@
         });
       },
 
+      get _isRTL() {
+        if (this.__isRTL === undefined) {
+          this.__isRTL = window.getComputedStyle(this)['direction'] === 'rtl';
+        }
+        return this.__isRTL;
+      },
+
+      _leftKey: function(event) {
+        if (this._isRTL)
+          this._incrementKey(event);
+        else
+          this._decrementKey(event);
+      },
+
+      _rightKey: function(event) {
+        if (this._isRTL)
+          this._decrementKey(event);
+        else
+          this._incrementKey(event);
+      },
+
       _incrementKey: function(event) {
         if (!this.disabled) {
           if (event.detail.key === 'end') {
diff --git a/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider.html b/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider.html
index a53a0bb..ab7e2dc7 100644
--- a/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider.html
+++ b/third_party/polymer/v1_0/components-chromium/paper-slider/paper-slider.html
@@ -88,6 +88,11 @@
       :host(:focus) {
         outline: none;
       }
+        
+      :host-context([dir="rtl"]) #sliderContainer {
+        -webkit-transform: scaleX(-1);
+        transform: scaleX(-1);
+      }
 
       #sliderContainer {
         position: relative;
diff --git a/third_party/polymer/v1_0/components_summary.txt b/third_party/polymer/v1_0/components_summary.txt
index 8c1c7a4b..98995c2 100644
--- a/third_party/polymer/v1_0/components_summary.txt
+++ b/third_party/polymer/v1_0/components_summary.txt
@@ -1,353 +1,353 @@
 Name: font-roboto
-Repository: https://github.com/PolymerElements/font-roboto.git
+Repository: git://github.com/PolymerElements/font-roboto.git
 Tree: v1.0.1
 Revision: 21ce9b51a417fa9995cf6606e886aba0728f70a1
 Tree link: https://github.com/PolymerElements/font-roboto/tree/v1.0.1
 
 Name: iron-a11y-announcer
-Repository: https://github.com/PolymerElements/iron-a11y-announcer.git
+Repository: git://github.com/PolymerElements/iron-a11y-announcer.git
 Tree: v1.0.5
 Revision: 2432d39a1693ccd728cbe7eb55810063737d3403
 Tree link: https://github.com/PolymerElements/iron-a11y-announcer/tree/v1.0.5
 
 Name: iron-a11y-keys
-Repository: https://github.com/PolymerElements/iron-a11y-keys.git
+Repository: git://github.com/PolymerElements/iron-a11y-keys.git
 Tree: v1.0.7
 Revision: 7830e675c3d0df6e7a7377d64fc3d6e6ae0e65a5
 Tree link: https://github.com/PolymerElements/iron-a11y-keys/tree/v1.0.7
 
 Name: iron-a11y-keys-behavior
-Repository: https://github.com/PolymerElements/iron-a11y-keys-behavior.git
+Repository: git://github.com/PolymerElements/iron-a11y-keys-behavior.git
 Tree: v1.1.9
 Revision: 26243e0f8687b8ea3d95eed3cefb7661a388dbce
 Tree link: https://github.com/PolymerElements/iron-a11y-keys-behavior/tree/v1.1.9
 
 Name: iron-autogrow-textarea
-Repository: https://github.com/PolymerElements/iron-autogrow-textarea.git
+Repository: git://github.com/PolymerElements/iron-autogrow-textarea.git
 Tree: v1.0.13
 Revision: 399cfdbb3fac0c7b61d14a8cf8402c0195b0ff04
 Tree link: https://github.com/PolymerElements/iron-autogrow-textarea/tree/v1.0.13
 
 Name: iron-behaviors
-Repository: https://github.com/PolymerElements/iron-behaviors.git
+Repository: git://github.com/PolymerElements/iron-behaviors.git
 Tree: v1.0.17
 Revision: ef8e89b5f0aa4e8a6b51ca6491ea453bf395f94f
 Tree link: https://github.com/PolymerElements/iron-behaviors/tree/v1.0.17
 
 Name: iron-checked-element-behavior
-Repository: https://github.com/PolymerElements/iron-checked-element-behavior.git
+Repository: git://github.com/PolymerElements/iron-checked-element-behavior.git
 Tree: v1.0.5
 Revision: c70add47a9af62d30746587e8a1303fb390787c6
 Tree link: https://github.com/PolymerElements/iron-checked-element-behavior/tree/v1.0.5
 
 Name: iron-collapse
-Repository: https://github.com/PolymerElements/iron-collapse.git
+Repository: git://github.com/PolymerElements/iron-collapse.git
 Tree: v1.2.1
 Revision: 5f994b1ac109925f6849b134e479229a16a752db
 Tree link: https://github.com/PolymerElements/iron-collapse/tree/v1.2.1
 
 Name: iron-dropdown
-Repository: https://github.com/PolymerElements/iron-dropdown.git
+Repository: git://github.com/PolymerElements/iron-dropdown.git
 Tree: v1.5.2
 Revision: c5dbb9404ee56e00a0e893b95b1253be5181ae2b
 Tree link: https://github.com/PolymerElements/iron-dropdown/tree/v1.5.2
 
 Name: iron-fit-behavior
-Repository: https://github.com/PolymerElements/iron-fit-behavior.git
+Repository: git://github.com/PolymerElements/iron-fit-behavior.git
 Tree: v1.2.5
 Revision: 8bc774b9376882ae5ba09f9e007764c6ee9fbbca
 Tree link: https://github.com/PolymerElements/iron-fit-behavior/tree/v1.2.5
 
 Name: iron-flex-layout
-Repository: https://github.com/PolymerElements/iron-flex-layout.git
+Repository: git://github.com/PolymerElements/iron-flex-layout.git
 Tree: v1.3.1
 Revision: 6d88f29f3a7181daa2a5c7f678de44f0a0e6a717
 Tree link: https://github.com/PolymerElements/iron-flex-layout/tree/v1.3.1
 
 Name: iron-form-element-behavior
-Repository: https://github.com/PolymerElements/iron-form-element-behavior.git
+Repository: git://github.com/PolymerElements/iron-form-element-behavior.git
 Tree: v1.0.6
 Revision: cf9e09ded62daf3363852ce98260aaad1ed0fae1
 Tree link: https://github.com/PolymerElements/iron-form-element-behavior/tree/v1.0.6
 
 Name: iron-icon
-Repository: https://github.com/PolymerElements/iron-icon.git
+Repository: git://github.com/PolymerElements/iron-icon.git
 Tree: v1.0.10
 Revision: f4e146da4982ff96bb25db85290c09e8de4ec734
 Tree link: https://github.com/PolymerElements/iron-icon/tree/v1.0.10
 
 Name: iron-icons
-Repository: https://github.com/PolymerElements/iron-icons.git
+Repository: git://github.com/PolymerElements/iron-icons.git
 Tree: v1.1.3
 Revision: c13869b57a9464dfc3a1f26e89858f8be37e7441
 Tree link: https://github.com/PolymerElements/iron-icons/tree/v1.1.3
 
 Name: iron-iconset-svg
-Repository: https://github.com/PolymerElements/iron-iconset-svg.git
+Repository: git://github.com/PolymerElements/iron-iconset-svg.git
 Tree: v1.0.11
 Revision: 9ecdf97d854d36b7715c7b250bdab9b3d5cfaa26
 Tree link: https://github.com/PolymerElements/iron-iconset-svg/tree/v1.0.11
 
 Name: iron-input
-Repository: https://github.com/PolymerElements/iron-input.git
+Repository: git://github.com/PolymerElements/iron-input.git
 Tree: 1.0.10
 Revision: 01d17407672ad8033ee447c9c7a65162f13c8f49
 Tree link: https://github.com/PolymerElements/iron-input/tree/1.0.10
 
 Name: iron-list
-Repository: https://github.com/PolymerElements/iron-list.git
+Repository: git://github.com/PolymerElements/iron-list.git
 Tree: v1.4.4
 Revision: 93490bcb5baba88d642d1f550dad3ed52b2c864f
 Tree link: https://github.com/PolymerElements/iron-list/tree/v1.4.4
 
 Name: iron-location
-Repository: https://github.com/PolymerElements/iron-location.git
+Repository: git://github.com/PolymerElements/iron-location.git
 Tree: v0.8.8
 Revision: ab65525d349f13c467653a200711202eeae17ff0
 Tree link: https://github.com/PolymerElements/iron-location/tree/v0.8.8
 
 Name: iron-media-query
-Repository: https://github.com/PolymerElements/iron-media-query.git
+Repository: git://github.com/PolymerElements/iron-media-query.git
 Tree: v1.0.8
 Revision: 3f916be171af7a3e03eb019acdfea71055d3c744
 Tree link: https://github.com/PolymerElements/iron-media-query/tree/v1.0.8
 
 Name: iron-menu-behavior
-Repository: https://github.com/PolymerElements/iron-menu-behavior.git
+Repository: git://github.com/PolymerElements/iron-menu-behavior.git
 Tree: v1.3.0
 Revision: 707791b8166d9233a582fee5b2df0f6eaea40973
 Tree link: https://github.com/PolymerElements/iron-menu-behavior/tree/v1.3.0
 
 Name: iron-meta
-Repository: https://github.com/PolymerElements/iron-meta.git
+Repository: git://github.com/PolymerElements/iron-meta.git
 Tree: v1.1.2
 Revision: bae96531b63ea6d4ce982f5592248aea849c0f5a
 Tree link: https://github.com/PolymerElements/iron-meta/tree/v1.1.2
 
 Name: iron-overlay-behavior
-Repository: https://github.com/PolymerElements/iron-overlay-behavior.git
+Repository: git://github.com/PolymerElements/iron-overlay-behavior.git
 Tree: v1.10.2
 Revision: 27558b9ceeba7c670999818fc50eebe7e044ed5c
 Tree link: https://github.com/PolymerElements/iron-overlay-behavior/tree/v1.10.2
 
 Name: iron-pages
-Repository: https://github.com/PolymerElements/iron-pages.git
+Repository: git://github.com/PolymerElements/iron-pages.git
 Tree: v1.0.8
 Revision: 1399d2d51a0ce50edd2c79a6d37419967509bf2c
 Tree link: https://github.com/PolymerElements/iron-pages/tree/v1.0.8
 
 Name: iron-range-behavior
-Repository: https://github.com/PolymerElements/iron-range-behavior.git
+Repository: git://github.com/PolymerElements/iron-range-behavior.git
 Tree: v1.0.6
 Revision: 1317604307387599725b80b63cbd293102ea2db0
 Tree link: https://github.com/PolymerElements/iron-range-behavior/tree/v1.0.6
 
 Name: iron-resizable-behavior
-Repository: https://github.com/PolymerElements/iron-resizable-behavior.git
+Repository: git://github.com/PolymerElements/iron-resizable-behavior.git
 Tree: v1.0.5
 Revision: 354f287922e497b79797348b31596eebaccb9761
 Tree link: https://github.com/PolymerElements/iron-resizable-behavior/tree/v1.0.5
 
 Name: iron-scroll-target-behavior
-Repository: https://github.com/PolymerElements/iron-scroll-target-behavior.git
+Repository: git://github.com/PolymerElements/iron-scroll-target-behavior.git
 Tree: v1.1.0
 Revision: eb6a4c81b13d2437360a2d56d99f99d63277ce5d
 Tree link: https://github.com/PolymerElements/iron-scroll-target-behavior/tree/v1.1.0
 
 Name: iron-scroll-threshold
-Repository: https://github.com/PolymerElements/iron-scroll-threshold.git
+Repository: git://github.com/PolymerElements/iron-scroll-threshold.git
 Tree: v1.0.2
 Revision: 3b0ded11ea87703a4f1ef48cea93226fb0e71ef1
 Tree link: https://github.com/PolymerElements/iron-scroll-threshold/tree/v1.0.2
 
 Name: iron-selector
-Repository: https://github.com/PolymerElements/iron-selector.git
+Repository: git://github.com/PolymerElements/iron-selector.git
 Tree: v1.5.2
 Revision: 18e8e12dcd9a4560de480562f65935feed334b86
 Tree link: https://github.com/PolymerElements/iron-selector/tree/v1.5.2
 
 Name: iron-test-helpers
-Repository: https://github.com/PolymerElements/iron-test-helpers.git
+Repository: git://github.com/PolymerElements/iron-test-helpers.git
 Tree: v1.2.5
 Revision: a6a123f1330e8e146def1b947b4db0b951003fc0
 Tree link: https://github.com/PolymerElements/iron-test-helpers/tree/v1.2.5
 
 Name: iron-validatable-behavior
-Repository: https://github.com/PolymerElements/iron-validatable-behavior.git
+Repository: git://github.com/PolymerElements/iron-validatable-behavior.git
 Tree: v1.1.1
 Revision: 2ecd3f411e298733b29f1660f75cb9b03ea31d77
 Tree link: https://github.com/PolymerElements/iron-validatable-behavior/tree/v1.1.1
 
 Name: neon-animation
-Repository: https://github.com/PolymerElements/neon-animation.git
+Repository: git://github.com/PolymerElements/neon-animation.git
 Tree: v1.2.4
 Revision: bd4f50c9a84023363e80513e10456a18232b21a7
 Tree link: https://github.com/PolymerElements/neon-animation/tree/v1.2.4
 
 Name: paper-behaviors
-Repository: https://github.com/PolymerElements/paper-behaviors.git
+Repository: git://github.com/PolymerElements/paper-behaviors.git
 Tree: v1.0.12
 Revision: 424919089ce3a68dfac1de17e6a56f4e09048e95
 Tree link: https://github.com/PolymerElements/paper-behaviors/tree/v1.0.12
 
 Name: paper-button
-Repository: https://github.com/PolymerElements/paper-button.git
+Repository: git://github.com/PolymerElements/paper-button.git
 Tree: v1.0.13
 Revision: 649ac0c09fa74b5af258cb6debebba811024f7cc
 Tree link: https://github.com/PolymerElements/paper-button/tree/v1.0.13
 
 Name: paper-checkbox
-Repository: https://github.com/PolymerElements/paper-checkbox.git
+Repository: git://github.com/PolymerElements/paper-checkbox.git
 Tree: v1.4.0
 Revision: 1d1c9439fe3a056356233e04171fd9c62f0857fb
 Tree link: https://github.com/PolymerElements/paper-checkbox/tree/v1.4.0
 
 Name: paper-dialog
-Repository: https://github.com/PolymerElements/paper-dialog.git
+Repository: git://github.com/PolymerElements/paper-dialog.git
 Tree: v1.1.0
 Revision: 7f31fa918fbd562b516eb262f1ed527dcaf7b7c0
 Tree link: https://github.com/PolymerElements/paper-dialog/tree/v1.1.0
 
 Name: paper-dialog-behavior
-Repository: https://github.com/PolymerElements/paper-dialog-behavior.git
+Repository: git://github.com/PolymerElements/paper-dialog-behavior.git
 Tree: v1.2.7
 Revision: decc77ca55c0381cf01d7409ddf1eb966543e84e
 Tree link: https://github.com/PolymerElements/paper-dialog-behavior/tree/v1.2.7
 
 Name: paper-drawer-panel
-Repository: https://github.com/PolymerElements/paper-drawer-panel.git
+Repository: git://github.com/PolymerElements/paper-drawer-panel.git
 Tree: v1.0.10
 Revision: 34c1d82dc3048dff33c83047bfaa95414e36d673
 Tree link: https://github.com/PolymerElements/paper-drawer-panel/tree/v1.0.10
 
 Name: paper-dropdown-menu
-Repository: https://github.com/PolymerElements/paper-dropdown-menu.git
+Repository: git://github.com/PolymerElements/paper-dropdown-menu.git
 Tree: v1.4.1
 Revision: d4178271f56b2237870e5761d3717ee6d99b3ab4
 Tree link: https://github.com/PolymerElements/paper-dropdown-menu/tree/v1.4.1
 
 Name: paper-fab
-Repository: https://github.com/PolymerElements/paper-fab.git
+Repository: git://github.com/PolymerElements/paper-fab.git
 Tree: v1.2.0
 Revision: 691638ca9b922411926b916a592f01a5f25c1af8
 Tree link: https://github.com/PolymerElements/paper-fab/tree/v1.2.0
 
 Name: paper-header-panel
-Repository: https://github.com/PolymerElements/paper-header-panel.git
+Repository: git://github.com/PolymerElements/paper-header-panel.git
 Tree: v1.1.6
 Revision: 3bf4e4f22eb12dd98039bad0eaaa942c2e869961
 Tree link: https://github.com/PolymerElements/paper-header-panel/tree/v1.1.6
 
 Name: paper-icon-button
-Repository: https://github.com/PolymerElements/paper-icon-button.git
+Repository: git://github.com/PolymerElements/paper-icon-button.git
 Tree: v1.1.2
 Revision: 0a6c65f73765d6f6ae6cfe90ddc9905a2cf45f20
 Tree link: https://github.com/PolymerElements/paper-icon-button/tree/v1.1.2
 
 Name: paper-input
-Repository: https://github.com/PolymerElements/paper-input.git
+Repository: git://github.com/PolymerElements/paper-input.git
 Tree: v1.1.21
 Revision: 2628898fe8541df9df66bb353417c2ee168e7c6a
 Tree link: https://github.com/PolymerElements/paper-input/tree/v1.1.21
 
 Name: paper-item
-Repository: https://github.com/PolymerElements/paper-item.git
+Repository: git://github.com/PolymerElements/paper-item.git
 Tree: v1.2.1
 Revision: 1eab91333b318ae19e315866575b2dddd38e6abc
 Tree link: https://github.com/PolymerElements/paper-item/tree/v1.2.1
 
 Name: paper-listbox
-Repository: https://github.com/PolymerelEments/paper-listbox.git
+Repository: git://github.com/PolymerelEments/paper-listbox.git
 Tree: v1.1.2
 Revision: b0fde50f57db3e8e4926e9d046be9d3c159a2bff
 Tree link: https://github.com/PolymerelEments/paper-listbox/tree/v1.1.2
 
 Name: paper-material
-Repository: https://github.com/PolymerElements/paper-material.git
+Repository: git://github.com/PolymerElements/paper-material.git
 Tree: v1.0.6
 Revision: 6aef0896fcbc25f9f5bd1dd55f7679e6ab7f92ad
 Tree link: https://github.com/PolymerElements/paper-material/tree/v1.0.6
 
 Name: paper-menu
-Repository: https://github.com/PolymerElements/paper-menu.git
+Repository: git://github.com/PolymerElements/paper-menu.git
 Tree: v1.2.2
 Revision: f31a1dbc5b594a84c8c01eca0f23f9bcb8f6ba76
 Tree link: https://github.com/PolymerElements/paper-menu/tree/v1.2.2
 
 Name: paper-menu-button
-Repository: https://github.com/PolymerElements/paper-menu-button.git
+Repository: git://github.com/PolymerElements/paper-menu-button.git
 Tree: v1.5.2
 Revision: 0f43421bc76ca863b16b82b8e4c67efb0267b818
 Tree link: https://github.com/PolymerElements/paper-menu-button/tree/v1.5.2
 
 Name: paper-progress
-Repository: https://github.com/PolymerElements/paper-progress.git
+Repository: git://github.com/PolymerElements/paper-progress.git
 Tree: v1.0.10
 Revision: dfdde2b02947fb2dfd3f2d303ec17f7f4919348c
 Tree link: https://github.com/PolymerElements/paper-progress/tree/v1.0.10
 
 Name: paper-radio-button
-Repository: https://github.com/PolymerElements/paper-radio-button.git
+Repository: git://github.com/PolymerElements/paper-radio-button.git
 Tree: v1.3.1
 Revision: 3a961e367e3d25d7e192fca0c1496af0f3feab44
 Tree link: https://github.com/PolymerElements/paper-radio-button/tree/v1.3.1
 
 Name: paper-radio-group
-Repository: https://github.com/PolymerElements/paper-radio-group.git
+Repository: git://github.com/PolymerElements/paper-radio-group.git
 Tree: v1.2.0
 Revision: 1505d1a57fbeabcb779de3e9f0e9857acd8b5f13
 Tree link: https://github.com/PolymerElements/paper-radio-group/tree/v1.2.0
 
 Name: paper-ripple
-Repository: https://github.com/PolymerElements/paper-ripple.git
+Repository: git://github.com/PolymerElements/paper-ripple.git
 Tree: v1.0.9
 Revision: 2ec18bb9e80320bf20619e1359791fb632e9c768
 Tree link: https://github.com/PolymerElements/paper-ripple/tree/v1.0.9
 
 Name: paper-slider
-Repository: https://github.com/PolymerElements/paper-slider.git
-Tree: v1.0.13
-Revision: ed9d5b512be1591b402e8a9c4961145576f8cc02
-Tree link: https://github.com/PolymerElements/paper-slider/tree/v1.0.13
+Repository: git://github.com/PolymerElements/paper-slider.git
+Tree: v1.0.15
+Revision: b678296daa93526adc8bf4b75628a7a5759bdeea
+Tree link: https://github.com/PolymerElements/paper-slider/tree/v1.0.15
 
 Name: paper-spinner
-Repository: https://github.com/PolymerElements/paper-spinner.git
+Repository: git://github.com/PolymerElements/paper-spinner.git
 Tree: v1.2.0
 Revision: 66dc50a940aa9a3a067137defe1712aa85de6f35
 Tree link: https://github.com/PolymerElements/paper-spinner/tree/v1.2.0
 
 Name: paper-styles
-Repository: https://github.com/PolymerElements/paper-styles.git
+Repository: git://github.com/PolymerElements/paper-styles.git
 Tree: v1.1.4
 Revision: 885bbd74db88dab4fb5dc229cdf994c55fb2b31b
 Tree link: https://github.com/PolymerElements/paper-styles/tree/v1.1.4
 
 Name: paper-tabs
-Repository: https://github.com/PolymerElements/paper-tabs.git
+Repository: git://github.com/PolymerElements/paper-tabs.git
 Tree: v1.6.2
 Revision: fc3df3875f97cbcee8cfa8f4895d86a4fd2925c7
 Tree link: https://github.com/PolymerElements/paper-tabs/tree/v1.6.2
 
 Name: paper-toggle-button
-Repository: https://github.com/PolymerElements/paper-toggle-button.git
+Repository: git://github.com/PolymerElements/paper-toggle-button.git
 Tree: v1.3.0
 Revision: 2f279868a9c8965aba76017c7ee6008ac4879f6a
 Tree link: https://github.com/PolymerElements/paper-toggle-button/tree/v1.3.0
 
 Name: paper-toolbar
-Repository: https://github.com/PolymerElements/paper-toolbar.git
+Repository: git://github.com/PolymerElements/paper-toolbar.git
 Tree: v1.1.6
 Revision: 39b8ad381bd4ba7834ed8f30a938b49810b9204f
 Tree link: https://github.com/PolymerElements/paper-toolbar/tree/v1.1.6
 
 Name: paper-tooltip
-Repository: https://github.com/PolymerElements/paper-tooltip.git
+Repository: git://github.com/PolymerElements/paper-tooltip.git
 Tree: v1.1.3
 Revision: 05fe3cfcb0e7e6853fa337344227f74a2ec7e07e
 Tree link: https://github.com/PolymerElements/paper-tooltip/tree/v1.1.3
 
 Name: polymer
-Repository: https://github.com/Polymer/polymer.git
+Repository: git://github.com/Polymer/polymer.git
 Tree: v1.8.1
 Revision: d89302fc3755b04ad4171f431e719a08b5b816f3
 Tree link: https://github.com/Polymer/polymer/tree/v1.8.1
diff --git a/third_party/web-animations-js/sources/.bower.json b/third_party/web-animations-js/sources/.bower.json
index 60acaf7..b3fb204 100644
--- a/third_party/web-animations-js/sources/.bower.json
+++ b/third_party/web-animations-js/sources/.bower.json
@@ -35,7 +35,7 @@
     "tag": "2.2.2",
     "commit": "8cf9e3567c8a30e905e6a2aefb2ebf4120da6859"
   },
-  "_source": "https://github.com/web-animations/web-animations-js.git",
+  "_source": "git://github.com/web-animations/web-animations-js.git",
   "_target": "2.2.2",
   "_originalSource": "web-animations/web-animations-js"
 }
\ No newline at end of file
diff --git a/tools/android/checkstyle/checkstyle.py b/tools/android/checkstyle/checkstyle.py
index 44e1ecf5..2433850 100644
--- a/tools/android/checkstyle/checkstyle.py
+++ b/tools/android/checkstyle/checkstyle.py
@@ -13,7 +13,15 @@
     os.path.join(os.path.dirname(__file__),
                  os.pardir, os.pardir, os.pardir))
 CHECKSTYLE_ROOT = os.path.join(CHROMIUM_SRC, 'third_party', 'checkstyle',
-                               'checkstyle-6.5-all.jar')
+                               'checkstyle-7.6.1-all.jar')
+
+
+def FormatCheckstyleOutput(checkstyle_output):
+  lines = checkstyle_output.splitlines(True)
+  if 'Checkstyle ends with' in lines[-1]:
+    return ''.join(lines[:-1])
+  else:
+    return checkstyle_output
 
 
 def RunCheckstyle(input_api, output_api, style_file, black_list=None):
@@ -49,8 +57,10 @@
   result_errors = []
   result_warnings = []
 
+  formatted_checkstyle_output = FormatCheckstyleOutput(stdout)
+
   local_path = input_api.PresubmitLocalPath()
-  root = xml.dom.minidom.parseString(stdout)
+  root = xml.dom.minidom.parseString(formatted_checkstyle_output)
   for fileElement in root.getElementsByTagName('file'):
     fileName = fileElement.attributes['name'].value
     fileName = os.path.relpath(fileName, local_path)
diff --git a/tools/clang/scripts/run_tool.py b/tools/clang/scripts/run_tool.py
index 53c7d0f..37ff3cc 100755
--- a/tools/clang/scripts/run_tool.py
+++ b/tools/clang/scripts/run_tool.py
@@ -195,6 +195,9 @@
       action='store_true',
       help='regenerate the compile database before running the tool')
   parser.add_argument(
+      '--shard',
+      metavar='<n>-of-<count>')
+  parser.add_argument(
       'compile_database',
       help='path to the directory that contains the compile database')
   parser.add_argument(
@@ -226,6 +229,19 @@
                         for f in git_filenames
                         if os.path.splitext(f)[1] in extensions]
 
+  if args.shard:
+    total_length = len(source_filenames)
+    match = re.match(r'(\d+)-of-(\d+)$', args.shard)
+    # Input is 1-based, but modular arithmetic is 0-based.
+    shard_number = int(match.group(1)) - 1
+    shard_count = int(match.group(2))
+    source_filenames = [
+        f[1] for f in enumerate(sorted(source_filenames))
+        if f[0] % shard_count == shard_number
+    ]
+    print 'Shard %d-of-%d will process %d entries out of %d' % (
+        shard_number, shard_count, len(source_filenames), total_length)
+
   dispatcher = _CompilerDispatcher(args.tool, args.tool_args,
                                    args.compile_database,
                                    source_filenames)
diff --git a/tools/clang/scripts/update.py b/tools/clang/scripts/update.py
index 2ea0d6d..44fc0d9 100755
--- a/tools/clang/scripts/update.py
+++ b/tools/clang/scripts/update.py
@@ -647,9 +647,6 @@
       '-DCMAKE_SHARED_LINKER_FLAGS=' + ' '.join(ldflags),
       '-DCMAKE_MODULE_LINKER_FLAGS=' + ' '.join(ldflags),
       '-DCMAKE_INSTALL_PREFIX=' + LLVM_BUILD_DIR,
-      # TODO(thakis): Remove this once official builds pass -Wl,--build-id
-      # explicitly, https://crbug.com/622775
-      '-DENABLE_LINKER_BUILD_ID=ON',
       '-DCHROMIUM_TOOLS_SRC=%s' % os.path.join(CHROMIUM_DIR, 'tools', 'clang'),
       '-DCHROMIUM_TOOLS=%s' % ';'.join(chrome_tools)]
 
diff --git a/tools/gn/visual_studio_writer.cc b/tools/gn/visual_studio_writer.cc
index 8a8cf528..76ebc562 100644
--- a/tools/gn/visual_studio_writer.cc
+++ b/tools/gn/visual_studio_writer.cc
@@ -37,9 +37,41 @@
 
 namespace {
 
+std::string EscapeString(const std::string& value) {
+  std::string result;
+  for (char c : value) {
+    switch (c) {
+      case '\n':
+        result += "&#10;";
+        break;
+      case '\r':
+        result += "&#13;";
+        break;
+      case '\t':
+        result += "&#9;";
+        break;
+      case '"':
+        result += "&quot;";
+        break;
+      case '<':
+        result += "&lt;";
+        break;
+      case '>':
+        result += "&gt;";
+        break;
+      case '&':
+        result += "&amp;";
+        break;
+      default:
+        result += c;
+    }
+  }
+  return result;
+}
+
 struct SemicolonSeparatedWriter {
   void operator()(const std::string& value, std::ostream& out) const {
-    out << value + ';';
+    out << EscapeString(value) + ';';
   }
 };
 
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index bdae8ad..ca44f8bf 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -15628,6 +15628,28 @@
   <description>Please enter the description of this user action.</description>
 </action>
 
+<action name="StatusArea_StickyKeysDisabled">
+  <owner>minch@chromium.org</owner>
+  <description>Ash system menu: Accessibility: Disable sticky keys</description>
+</action>
+
+<action name="StatusArea_StickyKeysEnabled">
+  <owner>minch@chromium.org</owner>
+  <description>Ash system menu: Accessibility: Enable sticky keys</description>
+</action>
+
+<action name="StatusArea_TapDraggingDisabled">
+  <owner>minch@chromium.org</owner>
+  <description>
+    Ash system menu: Accessibility: Disable tap dragging
+  </description>
+</action>
+
+<action name="StatusArea_TapDraggingEnabled">
+  <owner>minch@chromium.org</owner>
+  <description>Ash system menu: Accessibility: Enable tap dragging</description>
+</action>
+
 <action name="StatusArea_Tracing_Default_Selected">
   <owner>bruthig@chromium.org</owner>
   <owner>tbuckley@chromium.org</owner>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 96cbcd3..573a82f 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -10669,6 +10669,25 @@
   </summary>
 </histogram>
 
+<histogram name="DataUse.FavIcon.Downstream" units="bytes">
+  <owner>rajendrant@chromium.org</owner>
+  <owner>bengr@chromium.org</owner>
+  <summary>
+    Records the downstream network data use of favicon requests. Logged when the
+    request is completed or redirected. Zero bytes are recorded when the request
+    is served from cache.
+  </summary>
+</histogram>
+
+<histogram name="DataUse.FavIcon.Downstream.Non200Response" units="bytes">
+  <owner>rajendrant@chromium.org</owner>
+  <owner>bengr@chromium.org</owner>
+  <summary>
+    Records the downstream network data use of favicon requests with non 200
+    response code. Logged when the request is completed or redirected.
+  </summary>
+</histogram>
+
 <histogram name="DataUse.MessageSize" units="bytes">
   <owner>amohammadkhan@chromium.org</owner>
   <owner>bengr@chromium.org</owner>
@@ -95043,6 +95062,8 @@
   <int value="1911" label="MenuItemElementIconAttribute"/>
   <int value="1912" label="WebkitCSSMatrixSetMatrixValue"/>
   <int value="1913" label="WebkitCSSMatrixConstructFromString"/>
+  <int value="1914" label="CanRequestURLHTTPContainingNewline"/>
+  <int value="1915" label="CanRequestURLNonHTTPContainingNewline"/>
 </enum>
 
 <enum name="FetchRequestMode" type="int">
@@ -101208,6 +101229,7 @@
   <int value="-378180863" label="disable-panels"/>
   <int value="-378033324" label="disable-win32k-renderer-lockdown"/>
   <int value="-364325011" label="enable-files-quick-view"/>
+  <int value="-364267715" label="disable-native-cups"/>
   <int value="-362022976" label="disable-quirks-client"/>
   <int value="-361948582" label="material-security-verbose"/>
   <int value="-360038744" label="invert-viewport-scroll-order"/>
@@ -106879,6 +106901,7 @@
   <int value="1360443600" label="PPB_OpenGLES2FramebufferMultisample;1.0"/>
   <int value="1374404330" label="PPB_BrokerTrusted;0.3"/>
   <int value="1374976378" label="PPB_OpenGLES2Query;1.0"/>
+  <int value="1388412013" label="PPB_AudioOutput(Dev);0.1"/>
   <int value="1423820530" label="PPB_ContentDecryptor_Private;0.14"/>
   <int value="1437724812" label="PPB_AudioConfig;1.0"/>
   <int value="1443771913" label="PPB_NetAddress;1.0"/>
diff --git a/ui/accessibility/platform/ax_platform_node_base.cc b/ui/accessibility/platform/ax_platform_node_base.cc
index b72f5e8..4e830355 100644
--- a/ui/accessibility/platform/ax_platform_node_base.cc
+++ b/ui/accessibility/platform/ax_platform_node_base.cc
@@ -23,9 +23,7 @@
 
 gfx::Rect AXPlatformNodeBase::GetBoundsInScreen() const {
   CHECK(delegate_);
-  gfx::Rect bounds = gfx::ToEnclosingRect(GetData().location);
-  bounds.Offset(delegate_->GetGlobalCoordinateOffset());
-  return bounds;
+  return delegate_->GetScreenBoundsRect();
 }
 
 gfx::NativeViewAccessible AXPlatformNodeBase::GetParent() {
diff --git a/ui/accessibility/platform/ax_platform_node_delegate.h b/ui/accessibility/platform/ax_platform_node_delegate.h
index ab6de02..6960a44 100644
--- a/ui/accessibility/platform/ax_platform_node_delegate.h
+++ b/ui/accessibility/platform/ax_platform_node_delegate.h
@@ -49,8 +49,8 @@
   // Get the child of a node given a 0-based index.
   virtual gfx::NativeViewAccessible ChildAtIndex(int index) = 0;
 
-  // Get the offset to convert local coordinates to screen global coordinates.
-  virtual gfx::Vector2d GetGlobalCoordinateOffset() = 0;
+  // Get the bounds of this node in screen coordinates.
+  virtual gfx::Rect GetScreenBoundsRect() const = 0;
 
   // Do a *synchronous* hit test of the given location in global screen
   // coordinates, and the node within this node's subtree (inclusive) that's
diff --git a/ui/accessibility/platform/ax_platform_node_win.cc b/ui/accessibility/platform/ax_platform_node_win.cc
index bcfc909fc..c3535613 100644
--- a/ui/accessibility/platform/ax_platform_node_win.cc
+++ b/ui/accessibility/platform/ax_platform_node_win.cc
@@ -263,10 +263,9 @@
 STDMETHODIMP AXPlatformNodeWin::accLocation(
     LONG* x_left, LONG* y_top, LONG* width, LONG* height, VARIANT var_id) {
   COM_OBJECT_VALIDATE_VAR_ID_4_ARGS(var_id, x_left, y_top, width, height);
-  gfx::Rect bounds = gfx::ToEnclosingRect(GetData().location);
-  bounds += delegate_->GetGlobalCoordinateOffset();
+  gfx::Rect bounds = delegate_->GetScreenBoundsRect();
   *x_left = bounds.x();
-  *y_top  = bounds.y();
+  *y_top = bounds.y();
   *width  = bounds.width();
   *height = bounds.height();
 
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.cc b/ui/accessibility/platform/test_ax_node_wrapper.cc
index 7e52077..6f918c29 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.cc
+++ b/ui/accessibility/platform/test_ax_node_wrapper.cc
@@ -5,6 +5,7 @@
 #include "base/containers/hash_tables.h"
 #include "ui/accessibility/ax_action_data.h"
 #include "ui/accessibility/platform/test_ax_node_wrapper.h"
+#include "ui/gfx/geometry/rect_conversions.h"
 
 namespace ui {
 
@@ -100,8 +101,10 @@
       nullptr;
 }
 
-gfx::Vector2d TestAXNodeWrapper::GetGlobalCoordinateOffset() {
-  return g_offset;
+gfx::Rect TestAXNodeWrapper::GetScreenBoundsRect() const {
+  gfx::RectF bounds = GetData().location;
+  bounds.Offset(g_offset);
+  return gfx::ToEnclosingRect(bounds);
 }
 
 gfx::NativeViewAccessible TestAXNodeWrapper::HitTestSync(int x, int y) {
diff --git a/ui/accessibility/platform/test_ax_node_wrapper.h b/ui/accessibility/platform/test_ax_node_wrapper.h
index a781a0c..4d3f2b9 100644
--- a/ui/accessibility/platform/test_ax_node_wrapper.h
+++ b/ui/accessibility/platform/test_ax_node_wrapper.h
@@ -34,7 +34,7 @@
   gfx::NativeViewAccessible GetParent() override;
   int GetChildCount() override;
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
-  gfx::Vector2d GetGlobalCoordinateOffset() override;
+  gfx::Rect GetScreenBoundsRect() const override;
   gfx::NativeViewAccessible HitTestSync(int x, int y) override;
   gfx::NativeViewAccessible GetFocus() override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
diff --git a/ui/aura/gestures/gesture_recognizer_unittest.cc b/ui/aura/gestures/gesture_recognizer_unittest.cc
index 3c3e14f..0513496 100644
--- a/ui/aura/gestures/gesture_recognizer_unittest.cc
+++ b/ui/aura/gestures/gesture_recognizer_unittest.cc
@@ -4690,5 +4690,65 @@
   EXPECT_FALSE(queued_delegate->tap_down());
 }
 
+// Test for crbug/698843. Checks whether the events are routed to the correct
+// consumer in the event of TransferEventsTo() function call.
+TEST_F(GestureRecognizerTest, TransferEventsToRoutesAckCorrectly) {
+  std::unique_ptr<QueueTouchEventDelegate> delegate_1(
+      new QueueTouchEventDelegate(host()->dispatcher()));
+  TimedEvents tes;
+  const int kTouchId = 7;
+  gfx::Rect bounds(0, 0, 1000, 1000);
+
+  std::unique_ptr<aura::Window> window_1(CreateTestWindowWithDelegate(
+      delegate_1.get(), -1234, bounds, root_window()));
+
+  delegate_1->set_window(window_1.get());
+
+  delegate_1->Reset();
+  ui::TouchEvent press(
+      ui::ET_TOUCH_PRESSED, gfx::Point(512, 512), tes.Now(),
+      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, kTouchId));
+  DispatchEventUsingWindowDispatcher(&press);
+
+  // Create a new consumer and Touch event delegate.
+  std::unique_ptr<QueueTouchEventDelegate> delegate_2(
+      new QueueTouchEventDelegate(host()->dispatcher()));
+  std::unique_ptr<aura::Window> window_2(CreateTestWindowWithDelegate(
+      delegate_2.get(), -2345, bounds, root_window()));
+  delegate_2->set_window(window_2.get());
+
+  // Transfer event sequence from previous window to the new window.
+  ui::GestureRecognizer::Get()->TransferEventsTo(
+      window_1.get(), window_2.get(),
+      ui::GestureRecognizer::ShouldCancelTouches::DontCancel);
+
+  delegate_1->Reset();
+  delegate_1->ReceivedAck();
+
+  // ACK for events that were dispatched before the transfer should go to the
+  // original consumer. See crbug/698843 for more details.
+  EXPECT_2_EVENTS(delegate_1->events(), ui::ET_GESTURE_BEGIN,
+                  ui::ET_GESTURE_TAP_DOWN);
+
+  delegate_1->Reset();
+
+  ui::TouchEvent release(
+      ui::ET_TOUCH_RELEASED, gfx::Point(550, 512), tes.LeapForward(50),
+      ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, kTouchId));
+  DispatchEventUsingWindowDispatcher(&release);
+
+  // Events dispatched after the transfer should go to the new window.
+  EXPECT_0_EVENTS(delegate_1->events());
+
+  delegate_2->ReceivedAck();
+
+  // The event sequence transfer should mean that the new window receives the
+  // gesture sequence state.
+  EXPECT_3_EVENTS(delegate_2->events(), ui::ET_GESTURE_SHOW_PRESS,
+                  ui::ET_GESTURE_TAP, ui::ET_GESTURE_END);
+
+  EXPECT_TRUE(delegate_2->tap());
+}
+
 }  // namespace test
 }  // namespace aura
diff --git a/ui/events/BUILD.gn b/ui/events/BUILD.gn
index 26937014..873bb19 100644
--- a/ui/events/BUILD.gn
+++ b/ui/events/BUILD.gn
@@ -407,6 +407,7 @@
         "blink/web_input_event_unittest.cc",
         "devices/mojo/device_struct_traits_unittest.cc",
         "gestures/blink/web_gesture_curve_impl_unittest.cc",
+        "mojo/struct_traits_unittest.cc",
       ]
       deps += [
         "//cc",
diff --git a/ui/events/blink/input_handler_proxy_unittest.cc b/ui/events/blink/input_handler_proxy_unittest.cc
index c3515978..6c061fd 100644
--- a/ui/events/blink/input_handler_proxy_unittest.cc
+++ b/ui/events/blink/input_handler_proxy_unittest.cc
@@ -181,6 +181,7 @@
   MOCK_METHOD1(ScrollBy, cc::InputHandlerScrollResult(cc::ScrollState*));
   MOCK_METHOD1(ScrollEnd, void(cc::ScrollState*));
   MOCK_METHOD0(FlingScrollBegin, cc::InputHandler::ScrollStatus());
+  MOCK_METHOD0(ScrollingShouldSwitchtoMainThread, bool());
 
   std::unique_ptr<cc::SwapPromiseMonitor> CreateLatencyInfoSwapPromiseMonitor(
       ui::LatencyInfo* latency) override {
@@ -198,8 +199,6 @@
     return false;
   }
 
-  bool ScrollingShouldSwitchtoMainThread() override { return false; }
-
   void BindToClient(cc::InputHandlerClient* client,
                     bool touchpad_and_wheel_scroll_latching_enabled) override {}
 
@@ -371,14 +370,17 @@
     : public testing::Test,
       public testing::WithParamInterface<InputHandlerProxyTestType> {
  public:
-  InputHandlerProxyTest()
+  InputHandlerProxyTest(bool touchpad_and_wheel_scroll_latching_enabled = true)
       : synchronous_root_scroll_(GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER),
         install_synchronous_handler_(
             GetParam() == ROOT_SCROLL_SYNCHRONOUS_HANDLER ||
             GetParam() == CHILD_SCROLL_SYNCHRONOUS_HANDLER),
-        expected_disposition_(InputHandlerProxy::DID_HANDLE) {
+        expected_disposition_(InputHandlerProxy::DID_HANDLE),
+        touchpad_and_wheel_scroll_latching_enabled_(
+            touchpad_and_wheel_scroll_latching_enabled) {
     input_handler_.reset(
-        new TestInputHandlerProxy(&mock_input_handler_, &mock_client_, false));
+        new TestInputHandlerProxy(&mock_input_handler_, &mock_client_,
+                                  touchpad_and_wheel_scroll_latching_enabled_));
     scroll_result_did_scroll_.did_scroll = true;
     scroll_result_did_not_scroll_.did_scroll = false;
 
@@ -396,9 +398,7 @@
     gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   }
 
-  ~InputHandlerProxyTest() {
-    input_handler_.reset();
-  }
+  virtual ~InputHandlerProxyTest() { input_handler_.reset(); }
 
 // This is defined as a macro so the line numbers can be traced back to the
 // correct spot when it fails.
@@ -481,6 +481,14 @@
   }
 
  protected:
+  void GestureFlingAnimatesTouchpad();
+  void DidReceiveInputEvent_ForFling();
+  void GestureScrollStarted();
+  void GestureFlingPassiveListener();
+  void GestureFlingStartedTouchpad();
+  void GestureFlingStopsAtContentEdge();
+  void GestureFlingTransferResetsTouchpad();
+
   const bool synchronous_root_scroll_;
   const bool install_synchronous_handler_;
   testing::StrictMock<MockInputHandler> mock_input_handler_;
@@ -493,9 +501,17 @@
   base::HistogramTester histogram_tester_;
   cc::InputHandlerScrollResult scroll_result_did_scroll_;
   cc::InputHandlerScrollResult scroll_result_did_not_scroll_;
+  bool touchpad_and_wheel_scroll_latching_enabled_;
 };
 
-class InputHandlerProxyEventQueueTest : public testing::Test {
+class InputHandlerProxyWithoutWheelScrollLatchingTest
+    : public InputHandlerProxyTest {
+ public:
+  InputHandlerProxyWithoutWheelScrollLatchingTest()
+      : InputHandlerProxyTest(false) {}
+};
+
+class InputHandlerProxyEventQueueTest : public testing::TestWithParam<bool> {
  public:
   InputHandlerProxyEventQueueTest() : weak_ptr_factory_(this) {
     feature_list_.InitAndEnableFeature(features::kVsyncAlignedInputEvents);
@@ -504,9 +520,10 @@
   ~InputHandlerProxyEventQueueTest() { input_handler_proxy_.reset(); }
 
   void SetUp() override {
+    bool wheel_scroll_latching_enabled = GetParam();
     event_disposition_recorder_.clear();
     input_handler_proxy_ = base::MakeUnique<TestInputHandlerProxy>(
-        &mock_input_handler_, &mock_client_, false);
+        &mock_input_handler_, &mock_client_, wheel_scroll_latching_enabled);
     if (input_handler_proxy_->compositor_event_queue_)
       input_handler_proxy_->compositor_event_queue_ =
           base::MakeUnique<CompositorThreadEventQueue>();
@@ -627,7 +644,7 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-TEST_P(InputHandlerProxyTest, GestureScrollStarted) {
+void InputHandlerProxyTest::GestureScrollStarted() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
@@ -649,6 +666,8 @@
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
       .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   // Mark the event as handled if scroll happens.
@@ -673,6 +692,12 @@
 
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, GestureScrollStarted) {
+  GestureScrollStarted();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest, GestureScrollStarted) {
+  GestureScrollStarted();
+}
 
 TEST_P(InputHandlerProxyTest, GestureScrollOnMainThread) {
   // We should send all events to the widget for this gesture.
@@ -957,14 +982,25 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchpad) {
+void InputHandlerProxyTest::GestureFlingStartedTouchpad() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
 
   gesture_.setType(WebInputEvent::GestureFlingStart);
@@ -974,10 +1010,24 @@
 
   VERIFY_AND_RESET_MOCKS();
 
+  if (touchpad_and_wheel_scroll_latching_enabled_) {
+    // The fling cancellation shouldn't get deferred because velocityX is less
+    // than minimum.
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(1);
+  }
   // Verify that a GestureFlingCancel during an animation cancels it.
   gesture_.setType(WebInputEvent::GestureFlingCancel);
   gesture_.sourceDevice = blink::WebGestureDeviceTouchpad;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+
+  VERIFY_AND_RESET_MOCKS();
+}
+TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchpad) {
+  GestureFlingStartedTouchpad();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       GestureFlingStartedTouchpad) {
+  GestureFlingStartedTouchpad();
 }
 
 TEST_P(InputHandlerProxyTest, GestureFlingTouchpadScrollLatchingEnabled) {
@@ -1070,6 +1120,8 @@
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
       .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+  EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+      .WillOnce(testing::Return(false));
 
   // When scroll latching is enabled, ScrollEnd gets called when the last
   // ScrollBy did not scroll.
@@ -1133,11 +1185,21 @@
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 }
 
-TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
+void InputHandlerProxyTest::GestureFlingAnimatesTouchpad() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
   gesture_.setType(WebInputEvent::GestureFlingStart);
@@ -1155,7 +1217,8 @@
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
@@ -1178,13 +1241,16 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
 
@@ -1198,10 +1264,20 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kMainThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kMainThreadScrollState));
+    EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  } else {
+    EXPECT_CALL(
+        mock_input_handler_,
+        ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
+        .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+    EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+        .WillOnce(testing::Return(true));
+  }
   // Expected wheel fling animation parameters:
   // *) fling_delta and fling_point should match the original GestureFlingStart
   // event
@@ -1247,12 +1323,29 @@
 
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, GestureFlingAnimatesTouchpad) {
+  GestureFlingAnimatesTouchpad();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       GestureFlingAnimatesTouchpad) {
+  GestureFlingAnimatesTouchpad();
+}
 
-TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) {
+void InputHandlerProxyTest::GestureFlingPassiveListener() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
   gesture_.setType(WebInputEvent::GestureFlingStart);
@@ -1267,7 +1360,8 @@
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
   VERIFY_AND_RESET_MOCKS();
@@ -1291,8 +1385,10 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kPassive));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
@@ -1308,7 +1404,8 @@
   EXPECT_CALL(mock_client_, DispatchNonBlockingEventToMainThread_(
                                 WheelEventsMatch(expected_wheel)))
       .Times(1);
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(1);
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(1);
 
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
@@ -1319,15 +1416,36 @@
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   gesture_.setType(WebInputEvent::GestureFlingCancel);
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  if (touchpad_and_wheel_scroll_latching_enabled_) {
+    // The fling cancellation should be deferred.
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  }
 
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, GestureFlingPassiveListener) {
+  GestureFlingPassiveListener();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       GestureFlingPassiveListener) {
+  GestureFlingPassiveListener();
+}
 
-TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
+void InputHandlerProxyTest::GestureFlingTransferResetsTouchpad() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   // Start a gesture fling in the -X direction with zero Y movement.
   WebFloatPoint fling_delta = WebFloatPoint(1000, 0);
   WebPoint fling_point = WebPoint(7, 13);
@@ -1343,7 +1461,8 @@
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   VERIFY_AND_RESET_MOCKS();
 
@@ -1362,13 +1481,16 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
 
@@ -1382,11 +1504,19 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kMainThreadScrollState));
-
-  EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kMainThreadScrollState));
+    EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  } else {
+    EXPECT_CALL(
+        mock_input_handler_,
+        ScrollBy(testing::Property(&cc::ScrollState::delta_x, testing::Lt(0))))
+        .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+    EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+        .WillOnce(testing::Return(true));
+  }
 
   // Expected wheel fling animation parameters:
   // *) fling_delta and fling_point should match the original GestureFlingStart
@@ -1410,6 +1540,9 @@
                          testing::Eq(10)),
           testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
                          testing::Field(&WebSize::width, testing::Gt(0))))));
+  if (touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
 
@@ -1436,6 +1569,17 @@
   VERIFY_AND_RESET_MOCKS();
   input_handler_->MainThreadHasStoppedFlinging();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  expected_disposition_ = InputHandlerProxy::DID_HANDLE;
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   // Start a second gesture fling, this time in the +Y direction with no X.
   fling_delta = WebFloatPoint(0, -1000);
   fling_point = WebPoint(95, 87);
@@ -1449,7 +1593,8 @@
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
 
@@ -1469,13 +1614,16 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
 
@@ -1485,10 +1633,19 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kMainThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kMainThreadScrollState));
+    EXPECT_CALL(mock_input_handler_, ScrollBy(testing::_)).Times(0);
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_)).Times(0);
+  } else {
+    EXPECT_CALL(
+        mock_input_handler_,
+        ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Gt(0))))
+        .WillOnce(testing::Return(scroll_result_did_not_scroll_));
+    EXPECT_CALL(mock_input_handler_, ScrollingShouldSwitchtoMainThread())
+        .WillOnce(testing::Return(true));
+  }
 
   // We should get parameters from the second fling, nothing from the first
   // fling should "leak".
@@ -1507,11 +1664,22 @@
                          testing::Eq(30)),
           testing::Field(&WebActiveWheelFlingParameters::cumulativeScroll,
                          testing::Field(&WebSize::height, testing::Lt(0))))));
+
+  if (touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
 
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, GestureFlingTransferResetsTouchpad) {
+  GestureFlingTransferResetsTouchpad();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       GestureFlingTransferResetsTouchpad) {
+  GestureFlingTransferResetsTouchpad();
+}
 
 TEST_P(InputHandlerProxyTest, GestureFlingStartedTouchscreen) {
   // We shouldn't send any events to the widget for this gesture.
@@ -1919,11 +2087,21 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
+void InputHandlerProxyTest::GestureFlingStopsAtContentEdge() {
   // We shouldn't send any events to the widget for this gesture.
   expected_disposition_ = InputHandlerProxy::DID_HANDLE;
   VERIFY_AND_RESET_MOCKS();
 
+  EXPECT_CALL(mock_input_handler_, ScrollBegin(::testing::_, ::testing::_))
+      .WillOnce(testing::Return(kImplThreadScrollState));
+
+  // HandleGestureScrollBegin will set gesture_scroll_on_impl_thread_.
+  gesture_.setType(WebInputEvent::GestureScrollBegin);
+  EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
+  EXPECT_TRUE(input_handler_->gesture_scroll_on_impl_thread_for_testing());
+
+  VERIFY_AND_RESET_MOCKS();
+
   // On the fling start, we should schedule an animation but not actually start
   // scrolling.
   gesture_.setType(WebInputEvent::GestureFlingStart);
@@ -1932,7 +2110,8 @@
   gesture_.data.flingStart.velocityY = fling_delta.y;
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(gesture_));
   VERIFY_AND_RESET_MOCKS();
@@ -1947,13 +2126,16 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0))))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
@@ -1969,8 +2151,10 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Lt(0))))
@@ -1982,7 +2166,8 @@
           overscroll.unused_scroll_delta,
           testing::Property(&gfx::Vector2dF::y, testing::Lt(0)),
           testing::_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
@@ -1993,17 +2178,27 @@
   EXPECT_CALL(mock_input_handler_,
               GetEventListenerProperties(cc::EventListenerClass::kMouseWheel))
       .WillOnce(testing::Return(cc::EventListenerProperties::kNone));
-  EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
-      .WillOnce(testing::Return(kImplThreadScrollState));
+  if (!touchpad_and_wheel_scroll_latching_enabled_) {
+    EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
+        .WillOnce(testing::Return(kImplThreadScrollState));
+  }
   EXPECT_CALL(
       mock_input_handler_,
       ScrollBy(testing::Property(&cc::ScrollState::delta_y, testing::Eq(0))))
       .WillOnce(testing::Return(scroll_result_did_scroll_));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   time += base::TimeDelta::FromMilliseconds(100);
   Animate(time);
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, GestureFlingStopsAtContentEdge) {
+  GestureFlingStopsAtContentEdge();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       GestureFlingStopsAtContentEdge) {
+  GestureFlingStopsAtContentEdge();
+}
 
 TEST_P(InputHandlerProxyTest, GestureFlingNotCancelledBySmallTimeDelta) {
   // We shouldn't send any events to the widget for this gesture.
@@ -2943,12 +3138,12 @@
 
   VERIFY_AND_RESET_MOCKS();
 }
-
-TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFling) {
+void InputHandlerProxyTest::DidReceiveInputEvent_ForFling() {
   testing::StrictMock<MockInputHandlerProxyClientWithDidAnimateForInput>
       mock_client;
   input_handler_.reset(
-      new TestInputHandlerProxy(&mock_input_handler_, &mock_client, false));
+      new TestInputHandlerProxy(&mock_input_handler_, &mock_client,
+                                touchpad_and_wheel_scroll_latching_enabled_));
   if (install_synchronous_handler_) {
     EXPECT_CALL(mock_input_handler_, RequestUpdateForSynchronousInputHandler())
         .Times(1);
@@ -2964,7 +3159,8 @@
   EXPECT_SET_NEEDS_ANIMATE_INPUT(1);
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
-  EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
+  if (!touchpad_and_wheel_scroll_latching_enabled_)
+    EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_));
   EXPECT_EQ(InputHandlerProxy::DID_HANDLE,
       input_handler_->HandleInputEvent(gesture_));
   VERIFY_AND_RESET_MOCKS();
@@ -2976,6 +3172,13 @@
 
   VERIFY_AND_RESET_MOCKS();
 }
+TEST_P(InputHandlerProxyTest, DidReceiveInputEvent_ForFling) {
+  DidReceiveInputEvent_ForFling();
+}
+TEST_P(InputHandlerProxyWithoutWheelScrollLatchingTest,
+       DidReceiveInputEvent_ForFling) {
+  DidReceiveInputEvent_ForFling();
+}
 
 TEST(SynchronousInputHandlerProxyTest, StartupShutdown) {
   testing::StrictMock<MockInputHandler> mock_input_handler;
@@ -3254,7 +3457,7 @@
   VERIFY_AND_RESET_MOCKS();
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScroll) {
+TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScroll) {
   base::HistogramTester histogram_tester;
 
   // Handle scroll on compositor.
@@ -3317,7 +3520,7 @@
   histogram_tester.ExpectUniqueSample(kCoalescedCountHistogram, 2, 1);
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScrollPinchScroll) {
+TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScrollPinchScroll) {
   base::HistogramTester histogram_tester;
 
   // Handle scroll on compositor.
@@ -3386,7 +3589,7 @@
   histogram_tester.ExpectBucketCount(kCoalescedCountHistogram, 2, 2);
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedQueueingTime) {
+TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedQueueingTime) {
   base::HistogramTester histogram_tester;
   std::unique_ptr<base::SimpleTestTickClock> tick_clock =
       base::MakeUnique<base::SimpleTestTickClock>();
@@ -3432,7 +3635,7 @@
                                      1);
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedCoalesceScrollAndPinch) {
+TEST_P(InputHandlerProxyEventQueueTest, VSyncAlignedCoalesceScrollAndPinch) {
   // Start scroll in the first frame.
   EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_))
       .WillOnce(testing::Return(kImplThreadScrollState));
@@ -3484,7 +3687,7 @@
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, OriginalEventsTracing) {
+TEST_P(InputHandlerProxyEventQueueTest, OriginalEventsTracing) {
   // Handle scroll on compositor.
   cc::InputHandlerScrollResult scroll_result_did_scroll_;
   scroll_result_did_scroll_.did_scroll = true;
@@ -3555,7 +3758,7 @@
   testing::Mock::VerifyAndClearExpectations(&mock_input_handler_);
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, GestureScrollFlingOrder) {
+TEST_P(InputHandlerProxyEventQueueTest, GestureScrollFlingOrder) {
   // Handle scroll on compositor.
   cc::InputHandlerScrollResult scroll_result_did_scroll_;
   scroll_result_did_scroll_.did_scroll = true;
@@ -3604,7 +3807,7 @@
       !input_handler_proxy_->gesture_scroll_on_impl_thread_for_testing());
 }
 
-TEST_F(InputHandlerProxyEventQueueTest, GestureScrollAfterFling) {
+TEST_P(InputHandlerProxyEventQueueTest, GestureScrollAfterFling) {
   // Handle scroll on compositor.
   cc::InputHandlerScrollResult scroll_result_did_scroll_;
   scroll_result_did_scroll_.did_scroll = true;
@@ -3643,5 +3846,13 @@
                         InputHandlerProxyTest,
                         testing::ValuesIn(test_types));
 
+INSTANTIATE_TEST_CASE_P(AnimateInput,
+                        InputHandlerProxyWithoutWheelScrollLatchingTest,
+                        testing::ValuesIn(test_types));
+
+INSTANTIATE_TEST_CASE_P(InputHandlerProxyEventQueueTests,
+                        InputHandlerProxyEventQueueTest,
+                        testing::Bool());
+
 }  // namespace test
 }  // namespace ui
diff --git a/ui/events/gestures/gesture_recognizer_impl.cc b/ui/events/gestures/gesture_recognizer_impl.cc
index 206ef82c..5b2263f 100644
--- a/ui/events/gestures/gesture_recognizer_impl.cc
+++ b/ui/events/gestures/gesture_recognizer_impl.cc
@@ -244,6 +244,8 @@
 
 void GestureRecognizerImpl::SetupTargets(const TouchEvent& event,
                                          GestureConsumer* target) {
+  event_to_gesture_provider_[event.unique_event_id()] =
+      GetGestureProviderForConsumer(target);
   if (event.type() == ui::ET_TOUCH_RELEASED ||
       event.type() == ui::ET_TOUCH_CANCELLED) {
     touch_id_target_.erase(event.pointer_details().id);
@@ -280,8 +282,18 @@
     uint32_t unique_event_id,
     ui::EventResult result,
     GestureConsumer* consumer) {
-  GestureProviderAura* gesture_provider =
-      GetGestureProviderForConsumer(consumer);
+  GestureProviderAura* gesture_provider = nullptr;
+
+  // Check if we have already processed this event before dispatch and have a
+  // consumer associated with it.
+  auto event_to_gesture_provider_iterator =
+      event_to_gesture_provider_.find(unique_event_id);
+  if (event_to_gesture_provider_iterator != event_to_gesture_provider_.end()) {
+    gesture_provider = event_to_gesture_provider_iterator->second;
+    event_to_gesture_provider_.erase(event_to_gesture_provider_iterator);
+  } else {
+    gesture_provider = GetGestureProviderForConsumer(consumer);
+  }
   gesture_provider->OnTouchEventAck(unique_event_id, result != ER_UNHANDLED);
   return gesture_provider->GetAndResetPendingGestures();
 }
diff --git a/ui/events/gestures/gesture_recognizer_impl.h b/ui/events/gestures/gesture_recognizer_impl.h
index a3e0f573..036a1a6f 100644
--- a/ui/events/gestures/gesture_recognizer_impl.h
+++ b/ui/events/gestures/gesture_recognizer_impl.h
@@ -88,6 +88,12 @@
   std::map<GestureConsumer*, std::unique_ptr<GestureProviderAura>>
       consumer_gesture_provider_;
 
+  // Maps an event via its |unique_event_id| to the corresponding gesture
+  // provider. This avoids any invalid reference while routing ACKs for events
+  // that may arise post |TransferEventsTo()| function call.
+  // See http://crbug.com/698843 for more info.
+  std::map<uint32_t, GestureProviderAura*> event_to_gesture_provider_;
+
   // |touch_id_target_| maps a touch-id to its target window.
   // touch-ids are removed from |touch_id_target_| on
   // ET_TOUCH_RELEASE and ET_TOUCH_CANCEL.
diff --git a/ui/events/mojo/struct_traits_unittest.cc b/ui/events/mojo/struct_traits_unittest.cc
index a0e79f1..5f2ba7b 100644
--- a/ui/events/mojo/struct_traits_unittest.cc
+++ b/ui/events/mojo/struct_traits_unittest.cc
@@ -9,6 +9,8 @@
 #include "ui/events/keycodes/dom/dom_code.h"
 #include "ui/events/mojo/event.mojom.h"
 #include "ui/events/mojo/event_struct_traits.h"
+#include "ui/events/mojo/traits_test_service.mojom.h"
+#include "ui/latency/mojo/latency_info_struct_traits.h"
 
 namespace ui {
 
diff --git a/ui/gfx/canvas.cc b/ui/gfx/canvas.cc
index 0b064b5..53226fd 100644
--- a/ui/gfx/canvas.cc
+++ b/ui/gfx/canvas.cc
@@ -104,15 +104,14 @@
 }
 
 ImageSkiaRep Canvas::ExtractImageRep() const {
-  // Make a bitmap to return, and a canvas to draw into it. We don't just want
-  // to call extractSubset or the copy constructor, since we want an actual copy
-  // of the bitmap.
-  const SkISize size = canvas_->getBaseLayerSize();
-  SkBitmap result;
-  result.allocN32Pixels(size.width(), size.height());
-
-  canvas_->readPixels(&result, 0, 0);
-  return ImageSkiaRep(result, image_scale_);
+  DCHECK(bitmap_);
+  SkBitmap bitmap_copy;
+  // copyTo() will perform a deep copy, which is what we want.
+  bool result = bitmap_->copyTo(&bitmap_copy);
+  // This should succeed since the destination bitmap is empty to begin with.
+  // The only failure is an allocation failure, which we want to DCHECK anyway.
+  DCHECK(result);
+  return ImageSkiaRep(bitmap_copy, image_scale_);
 }
 
 void Canvas::DrawDashedRect(const Rect& rect, SkColor color) {
diff --git a/ui/gfx/color_analysis.cc b/ui/gfx/color_analysis.cc
index 301b6b9..d3e8998 100644
--- a/ui/gfx/color_analysis.cc
+++ b/ui/gfx/color_analysis.cc
@@ -368,15 +368,16 @@
   const uint32_t* pixels = static_cast<uint32_t*>(bitmap.getPixels());
   const int pixel_count = bitmap.width() * bitmap.height();
 
-  // We don't know exactly how many distinct colors there will be, so just
-  // reserve enough space to keep the maximum number of table resizes low.
-  // In our testing set, 2/3 of wallpapers have <200k unique colors and 1/4
-  // have <100k. Thus 200k is picked as a number that usually amounts to zero
-  // resizes but usually doesn't waste a lot of space.
-  std::unordered_map<SkColor, int> color_counts(200000);
+  // For better performance, only consider at most 10k pixels (evenly
+  // distributed throughout the image). This has a very minor impact on the
+  // outcome but improves runtime substantially for large images. 10,007 is a
+  // prime number to reduce the chance of picking an unrepresentative sample.
+  constexpr int kMaxConsideredPixels = 10007;
+  const int pixel_increment = std::max(1, pixel_count / kMaxConsideredPixels);
+  std::unordered_map<SkColor, int> color_counts(kMaxConsideredPixels);
 
   // First extract all colors into counts.
-  for (int i = 0; i < pixel_count; ++i) {
+  for (int i = 0; i < pixel_count; i += pixel_increment) {
     // SkBitmap uses pre-multiplied alpha but the prominent color algorithm
     // needs non-pre-multiplied alpha.
     const SkColor pixel = SkUnPreMultiply::PMColorToColor(pixels[i]);
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index b6b98d1..a4268c63 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -1295,7 +1295,8 @@
                 'extensions': ['GL_ANGLE_robust_client_memory']}],
   'arguments':
       'GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, '
-      'GLenum type, GLsizei bufSize, GLsizei* length, void* data', },
+      'GLenum type, GLsizei bufSize, GLsizei* length, GLsizei* columns, '
+      'GLsizei* rows, void* data', },
 { 'return_type': 'void',
   'names': ['glReadPixels'],
   'arguments':
@@ -1306,7 +1307,8 @@
                 'extensions': ['GL_ANGLE_robust_client_memory']}],
   'arguments':
       'GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, '
-      'GLenum type, GLsizei bufSize, GLsizei* length, void* pixels', },
+      'GLenum type, GLsizei bufSize, GLsizei* length, GLsizei* columns, '
+      'GLsizei* rows, void* pixels', },
 { 'return_type': 'void',
   'names': ['glReleaseShaderCompiler'],
   'arguments': 'void', },
diff --git a/ui/gl/gl_bindings_api_autogen_gl.h b/ui/gl/gl_bindings_api_autogen_gl.h
index b2a6ae4..fcb7b66 100644
--- a/ui/gl/gl_bindings_api_autogen_gl.h
+++ b/ui/gl/gl_bindings_api_autogen_gl.h
@@ -849,6 +849,8 @@
                                 GLenum type,
                                 GLsizei bufSize,
                                 GLsizei* length,
+                                GLsizei* columns,
+                                GLsizei* rows,
                                 void* data) override;
 void glReadPixelsFn(GLint x,
                     GLint y,
@@ -865,6 +867,8 @@
                                GLenum type,
                                GLsizei bufSize,
                                GLsizei* length,
+                               GLsizei* columns,
+                               GLsizei* rows,
                                void* pixels) override;
 void glReleaseShaderCompilerFn(void) override;
 void glRenderbufferStorageEXTFn(GLenum target,
diff --git a/ui/gl/gl_bindings_autogen_gl.cc b/ui/gl/gl_bindings_autogen_gl.cc
index 8740c1d..7e7b73b2 100644
--- a/ui/gl/gl_bindings_autogen_gl.cc
+++ b/ui/gl/gl_bindings_autogen_gl.cc
@@ -4247,9 +4247,11 @@
                                            GLenum type,
                                            GLsizei bufSize,
                                            GLsizei* length,
+                                           GLsizei* columns,
+                                           GLsizei* rows,
                                            void* data) {
   driver_->fn.glReadnPixelsRobustANGLEFn(x, y, width, height, format, type,
-                                         bufSize, length, data);
+                                         bufSize, length, columns, rows, data);
 }
 
 void GLApiBase::glReadPixelsFn(GLint x,
@@ -4270,9 +4272,11 @@
                                           GLenum type,
                                           GLsizei bufSize,
                                           GLsizei* length,
+                                          GLsizei* columns,
+                                          GLsizei* rows,
                                           void* pixels) {
   driver_->fn.glReadPixelsRobustANGLEFn(x, y, width, height, format, type,
-                                        bufSize, length, pixels);
+                                        bufSize, length, columns, rows, pixels);
 }
 
 void GLApiBase::glReleaseShaderCompilerFn(void) {
@@ -7146,10 +7150,12 @@
                                             GLenum type,
                                             GLsizei bufSize,
                                             GLsizei* length,
+                                            GLsizei* columns,
+                                            GLsizei* rows,
                                             void* data) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glReadnPixelsRobustANGLE")
   gl_api_->glReadnPixelsRobustANGLEFn(x, y, width, height, format, type,
-                                      bufSize, length, data);
+                                      bufSize, length, columns, rows, data);
 }
 
 void TraceGLApi::glReadPixelsFn(GLint x,
@@ -7171,10 +7177,12 @@
                                            GLenum type,
                                            GLsizei bufSize,
                                            GLsizei* length,
+                                           GLsizei* columns,
+                                           GLsizei* rows,
                                            void* pixels) {
   TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glReadPixelsRobustANGLE")
   gl_api_->glReadPixelsRobustANGLEFn(x, y, width, height, format, type, bufSize,
-                                     length, pixels);
+                                     length, columns, rows, pixels);
 }
 
 void TraceGLApi::glReleaseShaderCompilerFn(void) {
@@ -10859,15 +10867,19 @@
                                             GLenum type,
                                             GLsizei bufSize,
                                             GLsizei* length,
+                                            GLsizei* columns,
+                                            GLsizei* rows,
                                             void* data) {
   GL_SERVICE_LOG("glReadnPixelsRobustANGLE"
                  << "(" << x << ", " << y << ", " << width << ", " << height
                  << ", " << GLEnums::GetStringEnum(format) << ", "
                  << GLEnums::GetStringEnum(type) << ", " << bufSize << ", "
                  << static_cast<const void*>(length) << ", "
+                 << static_cast<const void*>(columns) << ", "
+                 << static_cast<const void*>(rows) << ", "
                  << static_cast<const void*>(data) << ")");
   gl_api_->glReadnPixelsRobustANGLEFn(x, y, width, height, format, type,
-                                      bufSize, length, data);
+                                      bufSize, length, columns, rows, data);
 }
 
 void DebugGLApi::glReadPixelsFn(GLint x,
@@ -10893,15 +10905,19 @@
                                            GLenum type,
                                            GLsizei bufSize,
                                            GLsizei* length,
+                                           GLsizei* columns,
+                                           GLsizei* rows,
                                            void* pixels) {
   GL_SERVICE_LOG("glReadPixelsRobustANGLE"
                  << "(" << x << ", " << y << ", " << width << ", " << height
                  << ", " << GLEnums::GetStringEnum(format) << ", "
                  << GLEnums::GetStringEnum(type) << ", " << bufSize << ", "
                  << static_cast<const void*>(length) << ", "
+                 << static_cast<const void*>(columns) << ", "
+                 << static_cast<const void*>(rows) << ", "
                  << static_cast<const void*>(pixels) << ")");
   gl_api_->glReadPixelsRobustANGLEFn(x, y, width, height, format, type, bufSize,
-                                     length, pixels);
+                                     length, columns, rows, pixels);
 }
 
 void DebugGLApi::glReleaseShaderCompilerFn(void) {
@@ -14474,6 +14490,8 @@
                                                 GLenum type,
                                                 GLsizei bufSize,
                                                 GLsizei* length,
+                                                GLsizei* columns,
+                                                GLsizei* rows,
                                                 void* data) {
   NOTREACHED()
       << "Trying to call glReadnPixelsRobustANGLE() without current GL context";
@@ -14500,6 +14518,8 @@
                                                GLenum type,
                                                GLsizei bufSize,
                                                GLsizei* length,
+                                               GLsizei* columns,
+                                               GLsizei* rows,
                                                void* pixels) {
   NOTREACHED()
       << "Trying to call glReadPixelsRobustANGLE() without current GL context";
diff --git a/ui/gl/gl_bindings_autogen_gl.h b/ui/gl/gl_bindings_autogen_gl.h
index f72be65..6c9cc2de 100644
--- a/ui/gl/gl_bindings_autogen_gl.h
+++ b/ui/gl/gl_bindings_autogen_gl.h
@@ -1003,6 +1003,8 @@
                                                             GLenum type,
                                                             GLsizei bufSize,
                                                             GLsizei* length,
+                                                            GLsizei* columns,
+                                                            GLsizei* rows,
                                                             void* data);
 typedef void(GL_BINDING_CALL* glReadPixelsProc)(GLint x,
                                                 GLint y,
@@ -1019,6 +1021,8 @@
                                                            GLenum type,
                                                            GLsizei bufSize,
                                                            GLsizei* length,
+                                                           GLsizei* columns,
+                                                           GLsizei* rows,
                                                            void* pixels);
 typedef void(GL_BINDING_CALL* glReleaseShaderCompilerProc)(void);
 typedef void(GL_BINDING_CALL* glRenderbufferStorageEXTProc)(
@@ -2831,6 +2835,8 @@
                                           GLenum type,
                                           GLsizei bufSize,
                                           GLsizei* length,
+                                          GLsizei* columns,
+                                          GLsizei* rows,
                                           void* data) = 0;
   virtual void glReadPixelsFn(GLint x,
                               GLint y,
@@ -2847,6 +2853,8 @@
                                          GLenum type,
                                          GLsizei bufSize,
                                          GLsizei* length,
+                                         GLsizei* columns,
+                                         GLsizei* rows,
                                          void* pixels) = 0;
   virtual void glReleaseShaderCompilerFn(void) = 0;
   virtual void glRenderbufferStorageEXTFn(GLenum target,
diff --git a/ui/gl/gl_bindings_autogen_mock.cc b/ui/gl/gl_bindings_autogen_mock.cc
index ea0f41d..de274b3 100644
--- a/ui/gl/gl_bindings_autogen_mock.cc
+++ b/ui/gl/gl_bindings_autogen_mock.cc
@@ -2851,10 +2851,12 @@
                                               GLenum type,
                                               GLsizei bufSize,
                                               GLsizei* length,
+                                              GLsizei* columns,
+                                              GLsizei* rows,
                                               void* pixels) {
   MakeFunctionUnique("glReadPixelsRobustANGLE");
   interface_->ReadPixelsRobustANGLE(x, y, width, height, format, type, bufSize,
-                                    length, pixels);
+                                    length, columns, rows, pixels);
 }
 
 void GL_BINDING_CALL
@@ -2866,10 +2868,12 @@
                                                GLenum type,
                                                GLsizei bufSize,
                                                GLsizei* length,
+                                               GLsizei* columns,
+                                               GLsizei* rows,
                                                void* data) {
   MakeFunctionUnique("glReadnPixelsRobustANGLE");
   interface_->ReadnPixelsRobustANGLE(x, y, width, height, format, type, bufSize,
-                                     length, data);
+                                     length, columns, rows, data);
 }
 
 void GL_BINDING_CALL MockGLInterface::Mock_glReleaseShaderCompiler(void) {
diff --git a/ui/gl/gl_bindings_autogen_mock.h b/ui/gl/gl_bindings_autogen_mock.h
index 1e91945..340b8a6 100644
--- a/ui/gl/gl_bindings_autogen_mock.h
+++ b/ui/gl/gl_bindings_autogen_mock.h
@@ -1177,6 +1177,8 @@
                                                          GLenum type,
                                                          GLsizei bufSize,
                                                          GLsizei* length,
+                                                         GLsizei* columns,
+                                                         GLsizei* rows,
                                                          void* pixels);
 static void GL_BINDING_CALL Mock_glReadnPixelsRobustANGLE(GLint x,
                                                           GLint y,
@@ -1186,6 +1188,8 @@
                                                           GLenum type,
                                                           GLsizei bufSize,
                                                           GLsizei* length,
+                                                          GLsizei* columns,
+                                                          GLsizei* rows,
                                                           void* data);
 static void GL_BINDING_CALL Mock_glReleaseShaderCompiler(void);
 static void GL_BINDING_CALL Mock_glRenderbufferStorage(GLenum target,
diff --git a/ui/gl/gl_mock.h b/ui/gl/gl_mock.h
index e7440cf..ab78732f 100644
--- a/ui/gl/gl_mock.h
+++ b/ui/gl/gl_mock.h
@@ -116,6 +116,34 @@
                      GLenum format,
                      GLenum type));
 
+  void ReadPixelsRobustANGLE(GLint /*x*/,
+                             GLint /*y*/,
+                             GLsizei /*width*/,
+                             GLsizei /*height*/,
+                             GLenum /*format*/,
+                             GLenum /*type*/,
+                             GLsizei /*bufSize*/,
+                             GLsizei* /*length*/,
+                             GLsizei* /*columns*/,
+                             GLsizei* /*rows*/,
+                             void* /*pixels*/) {
+    NOTREACHED();
+  }
+
+  void ReadnPixelsRobustANGLE(GLint /*x*/,
+                              GLint /*y*/,
+                              GLsizei /*width*/,
+                              GLsizei /*height*/,
+                              GLenum /*format*/,
+                              GLenum /*type*/,
+                              GLsizei /*bufSize*/,
+                              GLsizei* /*length*/,
+                              GLsizei* /*columns*/,
+                              GLsizei* /*rows*/,
+                              void* /*data*/) {
+    NOTREACHED();
+  }
+
  private:
   static MockGLInterface* interface_;
 
diff --git a/ui/gl/gl_mock_autogen_gl.h b/ui/gl/gl_mock_autogen_gl.h
index 82525d6..60d1d2e 100644
--- a/ui/gl/gl_mock_autogen_gl.h
+++ b/ui/gl/gl_mock_autogen_gl.h
@@ -865,16 +865,8 @@
 MOCK_METHOD2(PushGroupMarkerEXT, void(GLsizei length, const char* marker));
 MOCK_METHOD2(QueryCounter, void(GLuint id, GLenum target));
 MOCK_METHOD1(ReadBuffer, void(GLenum src));
-MOCK_METHOD9(ReadnPixelsRobustANGLE,
-             void(GLint x,
-                  GLint y,
-                  GLsizei width,
-                  GLsizei height,
-                  GLenum format,
-                  GLenum type,
-                  GLsizei bufSize,
-                  GLsizei* length,
-                  void* data));
+// TODO(zmo): crbug.com/456340
+// glReadnPixelsRobustANGLE cannot be mocked because it has 11 args.
 MOCK_METHOD7(ReadPixels,
              void(GLint x,
                   GLint y,
@@ -883,16 +875,8 @@
                   GLenum format,
                   GLenum type,
                   void* pixels));
-MOCK_METHOD9(ReadPixelsRobustANGLE,
-             void(GLint x,
-                  GLint y,
-                  GLsizei width,
-                  GLsizei height,
-                  GLenum format,
-                  GLenum type,
-                  GLsizei bufSize,
-                  GLsizei* length,
-                  void* pixels));
+// TODO(zmo): crbug.com/456340
+// glReadPixelsRobustANGLE cannot be mocked because it has 11 args.
 MOCK_METHOD0(ReleaseShaderCompiler, void());
 MOCK_METHOD4(
     RenderbufferStorageEXT,
diff --git a/ui/gl/gl_stub_autogen_gl.h b/ui/gl/gl_stub_autogen_gl.h
index 9c48030..a49500dc 100644
--- a/ui/gl/gl_stub_autogen_gl.h
+++ b/ui/gl/gl_stub_autogen_gl.h
@@ -862,6 +862,8 @@
                                 GLenum type,
                                 GLsizei bufSize,
                                 GLsizei* length,
+                                GLsizei* columns,
+                                GLsizei* rows,
                                 void* data) override {}
 void glReadPixelsFn(GLint x,
                     GLint y,
@@ -878,6 +880,8 @@
                                GLenum type,
                                GLsizei bufSize,
                                GLsizei* length,
+                               GLsizei* columns,
+                               GLsizei* rows,
                                void* pixels) override {}
 void glReleaseShaderCompilerFn() override {}
 void glRenderbufferStorageEXTFn(GLenum target,
diff --git a/ui/latency/BUILD.gn b/ui/latency/BUILD.gn
index bcb868c..5afbb031 100644
--- a/ui/latency/BUILD.gn
+++ b/ui/latency/BUILD.gn
@@ -24,8 +24,8 @@
   deps = [
     ":latency",
     "//base",
-    "//base/test:run_all_unittests",
     "//base/test:test_support",
+    "//mojo/edk/test:run_all_unittests",
     "//testing/gmock",
     "//testing/gtest",
   ]
@@ -33,11 +33,13 @@
   if (!is_ios) {
     sources += [
       "ipc/latency_info_param_traits_unittest.cc",
-      "mojo/struct_traits_unittest",
+      "mojo/struct_traits_unittest.cc",
     ]
     deps += [
       "//ipc:test_support",
+      "//mojo/public/cpp/bindings",
       "//ui/latency/ipc",
+      "//ui/latency/mojo:test_interfaces",
     ]
   }
 }
diff --git a/ui/latency/mojo/struct_traits_unittest.cc b/ui/latency/mojo/struct_traits_unittest.cc
index b9a930f..5a30329 100644
--- a/ui/latency/mojo/struct_traits_unittest.cc
+++ b/ui/latency/mojo/struct_traits_unittest.cc
@@ -5,8 +5,8 @@
 #include "base/message_loop/message_loop.h"
 #include "mojo/public/cpp/bindings/binding_set.h"
 #include "testing/gtest/include/gtest/gtest.h"
-#include "ui/events/mojo/latency_info_struct_traits.h"
-#include "ui/events/mojo/traits_test_service.mojom.h"
+#include "ui/latency/mojo/latency_info_struct_traits.h"
+#include "ui/latency/mojo/traits_test_service.mojom.h"
 
 namespace ui {
 
diff --git a/ui/message_center/views/notification_view_unittest.cc b/ui/message_center/views/notification_view_unittest.cc
index 6f1211b..4f9413d 100644
--- a/ui/message_center/views/notification_view_unittest.cc
+++ b/ui/message_center/views/notification_view_unittest.cc
@@ -132,10 +132,7 @@
     canvas.DrawColor(SK_ColorBLACK);
     view->OnPaint(&canvas);
 
-    SkBitmap bitmap;
-    bitmap.allocN32Pixels(canvas_size.width(), canvas_size.height());
-    canvas.sk_canvas()->readPixels(&bitmap, 0, 0);
-
+    SkBitmap bitmap = canvas.GetBitmap();
     // Incrementally inset each edge at its midpoint to find the bounds of the
     // rect containing the image's color. This assumes that the image is
     // centered in the canvas.
diff --git a/ui/views/accessibility/native_view_accessibility_auralinux.cc b/ui/views/accessibility/native_view_accessibility_auralinux.cc
index fabcb24..89b42737 100644
--- a/ui/views/accessibility/native_view_accessibility_auralinux.cc
+++ b/ui/views/accessibility/native_view_accessibility_auralinux.cc
@@ -91,9 +91,7 @@
     return widget->GetRootView()->GetNativeViewAccessible();
   }
 
-  gfx::Vector2d GetGlobalCoordinateOffset() override {
-    return gfx::Vector2d();
-  }
+  gfx::Rect GetScreenBoundsRect() const override { return gfx::Rect(); }
 
   gfx::NativeViewAccessible HitTestSync(int x, int y) override {
     return nullptr;
diff --git a/ui/views/accessibility/native_view_accessibility_base.cc b/ui/views/accessibility/native_view_accessibility_base.cc
index 5e5bdcd..2ed7e26f 100644
--- a/ui/views/accessibility/native_view_accessibility_base.cc
+++ b/ui/views/accessibility/native_view_accessibility_base.cc
@@ -111,8 +111,8 @@
   return nullptr;
 }
 
-gfx::Vector2d NativeViewAccessibilityBase::GetGlobalCoordinateOffset() {
-  return gfx::Vector2d(0, 0);  // location is already in screen coordinates.
+gfx::Rect NativeViewAccessibilityBase::GetScreenBoundsRect() const {
+  return view_->GetBoundsInScreen();
 }
 
 gfx::NativeViewAccessible NativeViewAccessibilityBase::HitTestSync(int x,
diff --git a/ui/views/accessibility/native_view_accessibility_base.h b/ui/views/accessibility/native_view_accessibility_base.h
index 87d1d87..fb0d0fe 100644
--- a/ui/views/accessibility/native_view_accessibility_base.h
+++ b/ui/views/accessibility/native_view_accessibility_base.h
@@ -42,7 +42,7 @@
   gfx::NativeViewAccessible ChildAtIndex(int index) override;
   gfx::NativeWindow GetTopLevelWidget() override;
   gfx::NativeViewAccessible GetParent() override;
-  gfx::Vector2d GetGlobalCoordinateOffset() override;
+  gfx::Rect GetScreenBoundsRect() const override;
   gfx::NativeViewAccessible HitTestSync(int x, int y) override;
   gfx::NativeViewAccessible GetFocus() override;
   gfx::AcceleratedWidget GetTargetForNativeAccessibilityEvent() override;
diff --git a/ui/views/accessibility/native_view_accessibility_unittest.cc b/ui/views/accessibility/native_view_accessibility_unittest.cc
index f57d7a2..0deb578 100644
--- a/ui/views/accessibility/native_view_accessibility_unittest.cc
+++ b/ui/views/accessibility/native_view_accessibility_unittest.cc
@@ -93,8 +93,10 @@
 TEST_F(NativeViewAccessibilityTest, BoundsShouldMatch) {
   gfx::Rect bounds =
       gfx::ToEnclosingRect(button_accessibility()->GetData().location);
-  bounds.Offset(button_accessibility()->GetGlobalCoordinateOffset());
+  gfx::Rect screen_bounds = button_accessibility()->GetScreenBoundsRect();
+
   EXPECT_EQ(button_->GetBoundsInScreen(), bounds);
+  EXPECT_EQ(screen_bounds, bounds);
 }
 
 TEST_F(NativeViewAccessibilityTest, LabelIsChildOfButton) {
diff --git a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
index f2fa45e5..33c95776 100644
--- a/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
+++ b/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html
@@ -9,7 +9,6 @@
         background-color: white;
         border: none;
         box-shadow: 0 2px 6px var(--paper-grey-500);
-        color: inherit;
         margin: 0;
         outline: none;
         padding: 8px 0;
@@ -23,11 +22,12 @@
         background: none;
         border: none;
         box-sizing: border-box;
-        color: inherit;
+        color: var(--paper-grey-900);
         font: inherit;
         min-height: 32px;
         padding: 0 24px;
         text-align: start;
+        user-select: none;
         width: 100%;
       }
 
diff --git a/url/gurl_unittest.cc b/url/gurl_unittest.cc
index 258ea03e..d3a59de 100644
--- a/url/gurl_unittest.cc
+++ b/url/gurl_unittest.cc
@@ -645,10 +645,12 @@
   // Constructor.
   GURL url_1(" \t ht\ntp://\twww.goo\rgle.com/as\ndf \n ");
   EXPECT_EQ("http://www.google.com/asdf", url_1.spec());
+  EXPECT_TRUE(url_1.parsed_for_possibly_invalid_spec().whitespace_removed);
 
   // Relative path resolver.
   GURL url_2 = url_1.Resolve(" \n /fo\to\r ");
   EXPECT_EQ("http://www.google.com/foo", url_2.spec());
+  EXPECT_TRUE(url_2.parsed_for_possibly_invalid_spec().whitespace_removed);
 
   // Note that newlines are NOT stripped from ReplaceComponents.
 }
diff --git a/url/third_party/mozilla/url_parse.cc b/url/third_party/mozilla/url_parse.cc
index 41768601..1c1f3dd 100644
--- a/url/third_party/mozilla/url_parse.cc
+++ b/url/third_party/mozilla/url_parse.cc
@@ -692,16 +692,17 @@
 
 Parsed::Parsed() : whitespace_removed(false), inner_parsed_(NULL) {}
 
-Parsed::Parsed(const Parsed& other) :
-    scheme(other.scheme),
-    username(other.username),
-    password(other.password),
-    host(other.host),
-    port(other.port),
-    path(other.path),
-    query(other.query),
-    ref(other.ref),
-    inner_parsed_(NULL) {
+Parsed::Parsed(const Parsed& other)
+    : scheme(other.scheme),
+      username(other.username),
+      password(other.password),
+      host(other.host),
+      port(other.port),
+      path(other.path),
+      query(other.query),
+      ref(other.ref),
+      whitespace_removed(other.whitespace_removed),
+      inner_parsed_(NULL) {
   if (other.inner_parsed_)
     set_inner_parsed(*other.inner_parsed_);
 }
@@ -716,6 +717,7 @@
     path = other.path;
     query = other.query;
     ref = other.ref;
+    whitespace_removed = other.whitespace_removed;
     if (other.inner_parsed_)
       set_inner_parsed(*other.inner_parsed_);
     else