diff --git a/AUTHORS b/AUTHORS
index d7f0a3d..d231ba2 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -667,6 +667,7 @@
 Ravi Nanjundappa <nravi.n@samsung.com>
 Ravi Phaneendra Kasibhatla <r.kasibhatla@samsung.com>
 Ravi Phaneendra Kasibhatla <ravi.kasibhatla@motorola.com>
+Réda Housni Alaoui <alaoui.rda@gmail.com>
 Refael Ackermann <refack@gmail.com>
 Renata Hodovan <rhodovan.u-szeged@partner.samsung.com>
 Rene Bolldorf <rb@radix.io>
@@ -913,6 +914,7 @@
 Canonical Limited <*@canonical.com>
 Code Aurora Forum <*@codeaurora.org>
 Comodo CA Limited
+Cosium <*@cosium.com>
 Endless Mobile, Inc. <*@endlessm.com>
 Facebook, Inc. <*@fb.com>
 Facebook, Inc. <*@oculus.com>
diff --git a/DEPS b/DEPS
index 33e0d3a..47167b12 100644
--- a/DEPS
+++ b/DEPS
@@ -74,7 +74,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'bee6cacc54357e0af1de67d527fe51eb3602c2d6',
+  'skia_revision': '7ffbcf909d365eb22c5e22b776aeac7b7472fbf3',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -98,7 +98,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': 'a827770faf5c3cfb157d59d401134e986dc4a18d',
+  'pdfium_revision': '0ddd5dc7969676f0c661c859512be50e379a2260',
   # 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.
@@ -634,7 +634,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '05591bbeae6592fd924caec8e728a4ea86cbb8c9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '86de7e898a636c61fc6d86fdaf8f538f6662ed55', # commit position 20628
+    Var('webrtc_git') + '/src.git' + '@' + '83d27683a83d8c4307ca9275779e822cb4604ccf', # commit position 20628
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
diff --git a/android_webview/java/src/org/chromium/android_webview/AwContents.java b/android_webview/java/src/org/chromium/android_webview/AwContents.java
index 76412ce3..d157663 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwContents.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwContents.java
@@ -27,6 +27,7 @@
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
+import android.support.annotation.IntDef;
 import android.text.TextUtils;
 import android.util.Base64;
 import android.util.Pair;
@@ -57,6 +58,7 @@
 import org.chromium.base.VisibleForTesting;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.blink_public.web.WebReferrerPolicy;
 import org.chromium.components.autofill.AutofillProvider;
 import org.chromium.components.navigation_interception.InterceptNavigationDelegate;
@@ -134,6 +136,16 @@
         }
     }
 
+    // Used to record the UMA histogram WebView.LoadDataWithBaseUrl.HistoryUrl. Since these values
+    // are persisted to logs, they should never be renumbered nor reused.
+    @IntDef({HistoryUrl.EMPTY, HistoryUrl.BASEURL, HistoryUrl.DIFFERENT, HistoryUrl.COUNT})
+    @interface HistoryUrl {
+        int EMPTY = 0;
+        int BASEURL = 1;
+        int DIFFERENT = 2;
+        int COUNT = 3;
+    }
+
     /**
      * WebKit hit test related data structure. These are used to implement
      * getHitTestResult, requestFocusNodeHref, requestImageRef methods in WebView.
@@ -1611,6 +1623,11 @@
         return "base64".equals(encoding);
     }
 
+    private static void recordHistoryUrl(@HistoryUrl int value) {
+        RecordHistogram.recordEnumeratedHistogram(
+                "WebView.LoadDataWithBaseUrl.HistoryUrl", value, HistoryUrl.COUNT);
+    }
+
     /**
      * WebView.loadData.
      */
@@ -1635,6 +1652,14 @@
         baseUrl = fixupBase(baseUrl);
         historyUrl = fixupHistory(historyUrl);
 
+        if (historyUrl.equals(ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL)) {
+            recordHistoryUrl(HistoryUrl.EMPTY);
+        } else if (historyUrl.equals(baseUrl)) {
+            recordHistoryUrl(HistoryUrl.BASEURL);
+        } else {
+            recordHistoryUrl(HistoryUrl.DIFFERENT);
+        }
+
         if (baseUrl.startsWith("data:")) {
             // For backwards compatibility with WebViewClassic, we use the value of |encoding|
             // as the charset, as long as it's not "base64".
diff --git a/base/test/scoped_task_environment_unittest.cc b/base/test/scoped_task_environment_unittest.cc
index 8395cf54..d39700e 100644
--- a/base/test/scoped_task_environment_unittest.cc
+++ b/base/test/scoped_task_environment_unittest.cc
@@ -136,6 +136,8 @@
 }
 
 TEST_P(ScopedTaskEnvironmentTest, DelayedTasks) {
+  // Use a QUEUED execution-mode environment, so that no tasks are actually
+  // executed until RunUntilIdle()/FastForwardBy() are invoked.
   ScopedTaskEnvironment scoped_task_environment(
       GetParam(), ScopedTaskEnvironment::ExecutionMode::QUEUED);
 
@@ -191,11 +193,8 @@
                           },
                           Unretained(&counter)));
 
-  // Verify that tasks are not being processed asynchronously. Since scheduling
-  // is non-deterministic, we pause the main thread momentarily, to increase
-  // the likelihood of the preceding PostTask() being run.
-  base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
-
+  // This expectation will fail flakily if the preceding PostTask() is executed
+  // asynchronously, indicating a problem with the QUEUED execution mode.
   int expected_value = 0;
   EXPECT_EQ(expected_value, counter);
 
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 7b32a01d2..2875725 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1351,13 +1351,12 @@
 
 gfx::ScrollOffset ScrollTree::PullDeltaForMainThread(
     SyncedScrollOffset* scroll_offset) {
+  DCHECK(property_trees()->is_active);
   // TODO(miletus): Remove all this temporary flooring machinery when
   // Blink fully supports fractional scrolls.
   gfx::ScrollOffset current_offset =
-      scroll_offset->Current(property_trees()->is_active);
-  gfx::ScrollOffset current_delta = property_trees()->is_active
-                                        ? scroll_offset->Delta()
-                                        : scroll_offset->PendingDelta().get();
+      scroll_offset->Current(/* is_active_tree */ true);
+  gfx::ScrollOffset current_delta = scroll_offset->Delta();
   gfx::ScrollOffset floored_delta(floor(current_delta.x()),
                                   floor(current_delta.y()));
   gfx::ScrollOffset diff_delta = floored_delta - current_delta;
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp
index 685ffde..56976db 100644
--- a/chrome/app/chromeos_strings.grdp
+++ b/chrome/app/chromeos_strings.grdp
@@ -2280,6 +2280,9 @@
   <message name="IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO" desc="The text on the button to take photo of the current user.">
     Take photo
   </message>
+  <message name="IDS_OPTIONS_CHANGE_PICTURE_CAPTURE_VIDEO" desc="The text on the button to capture video of the current user.">
+    Capture video
+  </message>
   <message name="IDS_OPTIONS_CHANGE_PICTURE_PHOTO_FROM_CAMERA" desc="The accessible text on the icon in the user image grid for a camera photo, when a photo has been captured.">
     Photo from internal camera
   </message>
diff --git a/chrome/app/settings_strings.grdp b/chrome/app/settings_strings.grdp
index 713d71b4..f967cc7f 100644
--- a/chrome/app/settings_strings.grdp
+++ b/chrome/app/settings_strings.grdp
@@ -2975,6 +2975,9 @@
     <message name="IDS_SETTINGS_CHANGE_PICTURE_TAKE_PHOTO" desc="The text on the button to take photo of the current user.">
       Take photo
     </message>
+    <message name="IDS_SETTINGS_CHANGE_PICTURE_CAPTURE_VIDEO" desc="The text on the button to capture video of the current user.">
+      Capture video
+    </message>
     <message name="IDS_SETTINGS_CHANGE_PICTURE_DISCARD_PHOTO" desc="The text on the button to discard the captured photo of the current user.">
       Discard photo
     </message>
diff --git a/chrome/browser/browser_keyevents_browsertest.cc b/chrome/browser/browser_keyevents_browsertest.cc
index 372c9bc..e49eba6 100644
--- a/chrome/browser/browser_keyevents_browsertest.cc
+++ b/chrome/browser/browser_keyevents_browsertest.cc
@@ -295,8 +295,16 @@
   }
 };
 
-// Flaky: http://crbug.com/129235, http://crbug.com/81451.
-IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, DISABLED_NormalKeyEvents) {
+#if defined(OS_MACOSX)
+// http://crbug.com/81451
+#define MAYBE_NormalKeyEvents DISABLED_NormalKeyEvents
+#elif defined(OS_LINUX)
+// http://crbug.com/129235
+#define MAYBE_NormalKeyEvents DISABLED_NormalKeyEvents
+#else
+#define MAYBE_NormalKeyEvents NormalKeyEvents
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_NormalKeyEvents) {
   static const KeyEventTestData kTestNoInput[] = {
     // a
     { ui::VKEY_A, false, false, false, false,
@@ -521,9 +529,16 @@
 }
 #endif
 
-// Flaky: http://crbug.com/81451 , http://crbug.com/129235 ,
-// also fails on Windows.
-IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, DISABLED_AccessKeys) {
+#if defined(OS_MACOSX)
+// http://crbug.com/81451 for mac
+#define MAYBE_AccessKeys DISABLED_AccessKeys
+#elif defined(OS_LINUX)
+// http://crbug.com/129235
+#define MAYBE_AccessKeys DISABLED_AccessKeys
+#else
+#define MAYBE_AccessKeys AccessKeys
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_AccessKeys) {
 #if defined(OS_MACOSX)
   // On Mac, access keys use ctrl+alt modifiers.
   static const KeyEventTestData kTestAccessA = {
@@ -658,7 +673,7 @@
 }
 
 // Flaky, http://crbug.com/69475.
-#if defined(OS_LINUX) || defined(OS_WIN)
+#if defined(OS_LINUX)
 #define MAYBE_ReservedAccelerators DISABLED_ReservedAccelerators
 #else
 #define MAYBE_ReservedAccelerators ReservedAccelerators
@@ -786,7 +801,12 @@
 #endif
 
 // See http://crbug.com/147579
-IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, DISABLED_PageUpDownKeys) {
+#if defined(OS_WIN)
+#define MAYBE_PageUpDownKeys PageUpDownKeys
+#else
+#define MAYBE_PageUpDownKeys DISABLED_PageUpDownKeys
+#endif
+IN_PROC_BROWSER_TEST_F(BrowserKeyEventsTest, MAYBE_PageUpDownKeys) {
   static const KeyEventTestData kTestPageUp = {
     ui::VKEY_PRIOR, false, false, false, false,
     false, false, false, false, 2,
diff --git a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
index 91655c7..28c8ba6 100644
--- a/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
+++ b/chrome/browser/chromeos/printing/cups_print_job_manager_impl.cc
@@ -40,7 +40,9 @@
 const int kPollRate = 1000;
 
 // How long we'll wait to connect to a printer before declaring an error.
-const int kConnectingTimeout = 20;
+// TODO(crbug.com/786182): Increase to 120s to give pipeline more time to
+// complete.
+const int kConnectingTimeout = 120;
 
 // Threshold for giving up on communicating with CUPS.
 const int kRetryMax = 6;
diff --git a/chrome/browser/mouseleave_browsertest.cc b/chrome/browser/mouseleave_browsertest.cc
index 7d34aac..c7a348e 100644
--- a/chrome/browser/mouseleave_browsertest.cc
+++ b/chrome/browser/mouseleave_browsertest.cc
@@ -82,10 +82,9 @@
   DISALLOW_COPY_AND_ASSIGN(MouseLeaveTest);
 };
 
-#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_WIN)
+#if defined(OS_MACOSX) || defined(OS_LINUX)
 // OS_MACOSX: Missing automation provider support: http://crbug.com/45892.
 // OS_LINUX: http://crbug.com/133361.
-// OS_WIN: http://crbug.com/419468
 #define MAYBE_TestOnMouseOut DISABLED_TestOnMouseOut
 #else
 #define MAYBE_TestOnMouseOut TestOnMouseOut
diff --git a/chrome/browser/resources/chromeos/login/oobe_change_picture.html b/chrome/browser/resources/chromeos/login/oobe_change_picture.html
index 9d87215..2c4f29da 100644
--- a/chrome/browser/resources/chromeos/login/oobe_change_picture.html
+++ b/chrome/browser/resources/chromeos/login/oobe_change_picture.html
@@ -49,6 +49,7 @@
           image-type="[[getImageType_(selectedItem_)]]"
           discard-image-label="[[i18nDynamic(locale, 'discardPhoto')]]"
           take-photo-label="[[i18nDynamic(locale, 'takePhoto')]]"
+          capture-video-label="[[i18nDynamic(locale, 'captureVideo')]]"
           switch-mode-to-camera-label="[[i18nDynamic(locale,
               'switchModeToCamera')]]"
           switch-mode-to-video-label="[[i18nDynamic(locale,
@@ -62,7 +63,8 @@
           selected-item="{{selectedItem_}}"
           old-image-label="[[i18nDynamic(locale, 'photoFromCamera')]]"
           profile-image-label="[[i18nDynamic(locale, 'profilePhoto')]]"
-          take-photo-label="[[i18nDynamic(locale, 'takePhoto')]]">
+          take-photo-label="[[i18nDynamic(locale, 'takePhoto')]]"
+          capture-video-label="[[i18nDynamic(locale, 'captureVideo')]]">
       </cr-picture-list>
     </div>
   </template>
diff --git a/chrome/browser/resources/settings/people_page/change_picture.html b/chrome/browser/resources/settings/people_page/change_picture.html
index 13bd6d99..87ec0b19 100644
--- a/chrome/browser/resources/settings/people_page/change_picture.html
+++ b/chrome/browser/resources/settings/people_page/change_picture.html
@@ -84,6 +84,7 @@
             discard-image-label="$i18n{discardPhoto}"
             preview-alt-text="$i18n{previewAltText}"
             take-photo-label="$i18n{takePhoto}"
+            capture-video-label="$i18n{captureVideo}"
             switch-mode-to-camera-label="$i18n{switchModeToCamera}"
             switch-mode-to-video-label="$i18n{switchModeToVideo}"
             camera-video-mode-enabled="[[cameraVideoModeEnabled_]]">
@@ -106,6 +107,7 @@
           old-image-label="$i18n{oldPhoto}"
           profile-image-label="$i18n{profilePhoto}"
           take-photo-label="$i18n{takePhoto}">
+          capture-video-label="$i18n{captureVideo}">
       </cr-picture-list>
     </div>
   </template>
diff --git a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
index 15da7df..346df8c 100644
--- a/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
+++ b/chrome/browser/ui/omnibox/omnibox_view_browsertest.cc
@@ -458,7 +458,7 @@
 #define MAYBE_PopupAccelerators PopupAccelerators
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_PopupAccelerators) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_PopupAccelerators) {
   // Create a popup.
   Browser* popup = CreateBrowserForPopup(browser()->profile());
   ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(popup));
@@ -514,7 +514,7 @@
 #define MAYBE_BackspaceInKeywordMode BackspaceInKeywordMode
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_BackspaceInKeywordMode) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_BackspaceInKeywordMode) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -568,8 +568,8 @@
             UTF16ToUTF8(omnibox_view->GetText()));
 }
 
-// Flaky on Windows and Linux. http://crbug.com/751543
-#if defined(OS_WIN) || defined(OS_LINUX)
+// Flaky on Linux. http://crbug.com/751543
+#if defined(OS_LINUX)
 #define MAYBE_Escape DISABLED_Escape
 #else
 #define MAYBE_Escape Escape
@@ -601,7 +601,7 @@
 #define MAYBE_DesiredTLD DesiredTLD
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_DesiredTLD) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_DesiredTLD) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -633,7 +633,7 @@
 #define MAYBE_DesiredTLDWithTemporaryText DesiredTLDWithTemporaryText
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_DesiredTLDWithTemporaryText) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_DesiredTLDWithTemporaryText) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -684,8 +684,16 @@
 }
 
 // See http://crbug.com/431575.
+#if defined(OS_WIN)
+#define MAYBE_ClearUserTextAfterBackgroundCommit \
+  ClearUserTextAfterBackgroundCommit
+#else
+// Flaky; https;//crbug.com/751031.
+#define MAYBE_ClearUserTextAfterBackgroundCommit \
+  DISABLED_ClearUserTextAfterBackgroundCommit
+#endif
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_ClearUserTextAfterBackgroundCommit) {
+                       MAYBE_ClearUserTextAfterBackgroundCommit) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -714,7 +722,13 @@
   EXPECT_EQ(ASCIIToUTF16(url2.spec()), omnibox_view->GetText());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_AltEnter) {
+#if defined(OS_WIN)
+#define MAYBE_AltEnter AltEnter
+#else
+// https://crbug.com/751031
+#define MAYBE_AltEnter DISABLED_AltEnter
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_AltEnter) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -775,7 +789,13 @@
   EXPECT_EQ("http://www.foo.com/search?q=z", url.spec());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_EscapeToDefaultMatch) {
+#if defined(OS_WIN)
+#define MAYBE_EscapeToDefaultMatch EscapeToDefaultMatch
+#else
+// https://crbug.com/751031
+#define MAYBE_EscapeToDefaultMatch DISABLED_EscapeToDefaultMatch
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_EscapeToDefaultMatch) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -811,7 +831,13 @@
   EXPECT_EQ(old_selected_line, popup_model->selected_line());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_BasicTextOperations) {
+#if defined(OS_WIN)
+#define MAYBE_BasicTextOperations BasicTextOperations
+#else
+// https://crbug.com/751031
+#define MAYBE_BasicTextOperations DISABLED_BasicTextOperations
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_BasicTextOperations) {
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   chrome::FocusLocationBar(browser());
 
@@ -905,7 +931,13 @@
 // Make sure the cursor position doesn't get set past the last character of
 // user input text when the URL is longer than the keyword.
 // (http://crbug.com/656209)
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_FocusSearchLongUrl) {
+#if defined(OS_WIN)
+#define MAYBE_FocusSearchLongUrl FocusSearchLongUrl
+#else
+// https://crbug.com/751031.
+#define MAYBE_FocusSearchLongUrl DISABLED_FocusSearchLongUrl
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_FocusSearchLongUrl) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -919,8 +951,15 @@
 
 // Make sure the display text is preserved when calling FocusSearch() when the
 // display text is not the permanent text.
+#if defined(OS_WIN)
+#define MAYBE_PreserveDisplayTextOnFocusSearch PreserveDisplayTextOnFocusSearch
+#else
+// https://crbug.com/751031.
+#define MAYBE_PreserveDisplayTextOnFocusSearch \
+  DISABLED_PreserveDisplayTextOnFocusSearch
+#endif
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_PreserveDisplayTextOnFocusSearch) {
+                       MAYBE_PreserveDisplayTextOnFocusSearch) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -972,8 +1011,16 @@
   EXPECT_EQ(19U, std::max(start, end));
 }
 
+#if defined(OS_WIN)
+#define MAYBE_AcceptKeywordByTypingQuestionMark \
+  AcceptKeywordByTypingQuestionMark
+#else
+// https://crbug.com/751031
+#define MAYBE_AcceptKeywordByTypingQuestionMark \
+  DISABLED_AcceptKeywordByTypingQuestionMark
+#endif
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_AcceptKeywordByTypingQuestionMark) {
+                       MAYBE_AcceptKeywordByTypingQuestionMark) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -995,8 +1042,8 @@
   EXPECT_FALSE(omnibox_view->model()->is_keyword_selected());
 }
 
-// Flaky on Windows and Linux. http://crbug.com/751543
-#if defined(OS_WIN) || defined(OS_LINUX)
+// Flaky on Linux. http://crbug.com/751543
+#if defined(OS_LINUX)
 #define MAYBE_AcceptKeywordBySpace DISABLED_AcceptKeywordBySpace
 #else
 #define MAYBE_AcceptKeywordBySpace AcceptKeywordBySpace
@@ -1371,7 +1418,7 @@
 #define MAYBE_TabAcceptKeyword TabAcceptKeyword
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_TabAcceptKeyword) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_TabAcceptKeyword) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -1426,7 +1473,7 @@
 #define MAYBE_TabTraverseResultsTest TabTraverseResultsTest
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_TabTraverseResultsTest) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_TabTraverseResultsTest) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -1519,8 +1566,7 @@
 #define MAYBE_PersistKeywordModeOnTabSwitch PersistKeywordModeOnTabSwitch
 #endif
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_PersistKeywordModeOnTabSwitch) {
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_PersistKeywordModeOnTabSwitch) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 
@@ -1568,7 +1614,7 @@
 #endif
 
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_CtrlKeyPressedWithInlineAutocompleteTest) {
+                       MAYBE_CtrlKeyPressedWithInlineAutocompleteTest) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -1591,7 +1637,13 @@
   EXPECT_EQ(old_text, omnibox_view->GetText());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_UndoRedo) {
+#if defined(OS_WIN)
+#define MAYBE_UndoRedo UndoRedo
+#else
+// https://crbug.com/751031.
+#define MAYBE_UndoRedo DISABLED_UndoRedo
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_UndoRedo) {
   ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL));
   chrome::FocusLocationBar(browser());
 
@@ -1660,8 +1712,15 @@
   EXPECT_EQ(old_text, omnibox_view->GetText());
 }
 
+#if defined(OS_WIN)
+#define MAYBE_BackspaceDeleteHalfWidthKatakana BackspaceDeleteHalfWidthKatakana
+#else
+// https://crbug.com/751031
+#define MAYBE_BackspaceDeleteHalfWidthKatakana \
+  DISABLED_BackspaceDeleteHalfWidthKatakana
+#endif
 IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_BackspaceDeleteHalfWidthKatakana) {
+                       MAYBE_BackspaceDeleteHalfWidthKatakana) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   // Insert text: ダ. This is two, 3-byte UTF-8 characters:
@@ -1687,8 +1746,13 @@
 #endif
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_DoesNotUpdateAutocompleteOnBlur) {
+#if defined(OS_WIN)
+#define MAYBE_DoesNotUpdateAutocompleteOnBlur DoesNotUpdateAutocompleteOnBlur
+#else
+#define MAYBE_DoesNotUpdateAutocompleteOnBlur \
+  DISABLED_DoesNotUpdateAutocompleteOnBlur
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_DoesNotUpdateAutocompleteOnBlur) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -1712,7 +1776,13 @@
       omnibox_view->model()->autocomplete_controller()->input_.text());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_Paste) {
+#if defined(OS_WIN)
+#define MAYBE_Paste Paste
+#else
+// https://crbug.com/751031.
+#define MAYBE_Paste DISABLED_Paste
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_Paste) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
@@ -1753,7 +1823,13 @@
   // TODO(msw): Test that AltGr+V does not paste.
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_CopyURLToClipboard) {
+#if defined(OS_WIN)
+#define MAYBE_CopyURLToClipboard CopyURLToClipboard
+#else
+// https://crbug.com/751031
+#define MAYBE_CopyURLToClipboard DISABLED_CopyURLToClipboard
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_CopyURLToClipboard) {
   // Set permanent text thus making sure that omnibox treats 'google.com'
   // as URL (not as ordinary user input).
   OmniboxView* omnibox_view = NULL;
@@ -1796,7 +1872,13 @@
   EXPECT_EQ(target_url, url);
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_CutURLToClipboard) {
+#if defined(OS_WIN)
+#define MAYBE_CutURLToClipboard CutURLToClipboard
+#else
+// https://crbug.com/751031
+#define MAYBE_CutURLToClipboard DISABLED_CutURLToClipboard
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_CutURLToClipboard) {
   // Set permanent text thus making sure that omnibox treats 'google.com'
   // as URL (not as ordinary user input).
   OmniboxView* omnibox_view = NULL;
@@ -1839,7 +1921,13 @@
   EXPECT_EQ(target_url, url);
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_CopyTextToClipboard) {
+#if defined(OS_WIN)
+#define MAYBE_CopyTextToClipboard CopyTextToClipboard
+#else
+// https://crbug.com/751031
+#define MAYBE_CopyTextToClipboard DISABLED_CopyTextToClipboard
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_CopyTextToClipboard) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   const char* target_text = "foo";
@@ -1862,7 +1950,13 @@
   EXPECT_EQ(ASCIIToUTF16(target_text), omnibox_view->GetText());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_CutTextToClipboard) {
+#if defined(OS_WIN)
+#define MAYBE_CutTextToClipboard CutTextToClipboard
+#else
+// https://crbug.com/751031
+#define MAYBE_CutTextToClipboard DISABLED_CutTextToClipboard
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_CutTextToClipboard) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   const char* target_text = "foo";
@@ -1885,7 +1979,12 @@
   EXPECT_EQ(base::string16(), omnibox_view->GetText());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest, DISABLED_EditSearchEngines) {
+#if defined(OS_WIN)
+#define MAYBE_EditSearchEngines EditSearchEngines
+#else
+#define MAYBE_EditSearchEngines DISABLED_EditSearchEngines
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_EditSearchEngines) {
   OmniboxView* omnibox_view = nullptr;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
 #if defined(OS_CHROMEOS)
@@ -1907,8 +2006,14 @@
   EXPECT_FALSE(omnibox_view->model()->popup_model()->IsOpen());
 }
 
-IN_PROC_BROWSER_TEST_F(OmniboxViewTest,
-                       DISABLED_CtrlArrowAfterArrowSuggestions) {
+#if defined(OS_WIN)
+#define MAYBE_CtrlArrowAfterArrowSuggestions CtrlArrowAfterArrowSuggestions
+#else
+// https://crbug.com/751031
+#define MAYBE_CtrlArrowAfterArrowSuggestions \
+  DISABLED_CtrlArrowAfterArrowSuggestions
+#endif
+IN_PROC_BROWSER_TEST_F(OmniboxViewTest, MAYBE_CtrlArrowAfterArrowSuggestions) {
   OmniboxView* omnibox_view = NULL;
   ASSERT_NO_FATAL_FAILURE(GetOmniboxView(&omnibox_view));
   OmniboxPopupModel* popup_model = omnibox_view->model()->popup_model();
diff --git a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
index ec2aa10..9f991a4 100644
--- a/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
+++ b/chrome/browser/ui/views/bookmarks/bookmark_bar_view_test.cc
@@ -1373,8 +1373,7 @@
   }
 };
 
-// Times out on Win. http://crbug.com/499858
-#if defined(OS_WIN) || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
 // TODO(erg): linux_aura bringup: http://crbug.com/163931
 #define MAYBE_CloseWithModalDialog DISABLED_CloseWithModalDialog
 #else
diff --git a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
index 0fbd60c..9f0f540 100644
--- a/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/user_image_screen_handler.cc
@@ -93,6 +93,7 @@
   builder->Add("userImageScreenDescription",
                IDS_USER_IMAGE_SCREEN_DESCRIPTION);
   builder->Add("takePhoto", IDS_OPTIONS_CHANGE_PICTURE_TAKE_PHOTO);
+  builder->Add("captureVideo", IDS_OPTIONS_CHANGE_PICTURE_CAPTURE_VIDEO);
   builder->Add("discardPhoto", IDS_OPTIONS_CHANGE_PICTURE_DISCARD_PHOTO);
   builder->Add("switchModeToCamera",
                IDS_OPTIONS_CHANGE_PICTURE_SWITCH_MODE_TO_CAMERA);
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 28b47f5..5356c893 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
@@ -1356,6 +1356,7 @@
     {"changePictureTitle", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TITLE},
     {"changePicturePageDescription", IDS_SETTINGS_CHANGE_PICTURE_DIALOG_TEXT},
     {"takePhoto", IDS_SETTINGS_CHANGE_PICTURE_TAKE_PHOTO},
+    {"captureVideo", IDS_SETTINGS_CHANGE_PICTURE_CAPTURE_VIDEO},
     {"discardPhoto", IDS_SETTINGS_CHANGE_PICTURE_DISCARD_PHOTO},
     {"switchModeToCamera", IDS_SETTINGS_CHANGE_PICTURE_SWITCH_MODE_TO_CAMERA},
     {"switchModeToVideo", IDS_SETTINGS_CHANGE_PICTURE_SWITCH_MODE_TO_VIDEO},
diff --git a/chromecast/tools/trace.py b/chromecast/tools/trace.py
index 4d66321..f77bd85b 100755
--- a/chromecast/tools/trace.py
+++ b/chromecast/tools/trace.py
@@ -63,7 +63,7 @@
       '-c', '--category-filter',
       help='Apply filter to control what category groups should be traced.',
       type='string',
-      default='"*"')
+      default='')
   tracing_opts.add_option(
       '--record-continuously',
       help='Keep recording until stopped. The trace buffer is of fixed size '
diff --git a/chromecast/tools/tracinglib.py b/chromecast/tools/tracinglib.py
index 5f96f91..edb8290 100644
--- a/chromecast/tools/tracinglib.py
+++ b/chromecast/tools/tracinglib.py
@@ -41,6 +41,8 @@
     self._devtools_port = devtools_port
     self._timeout = timeout
     self._buffer_usage_reporting_interval = buffer_usage_reporting_interval
+    self._included_categories = []
+    self._excluded_categories = []
 
   def Connect(self):
     """Connect to cast_shell."""
@@ -71,14 +73,23 @@
     """
     self._tracing_client = TracingClient()
     self._socket.settimeout(self._timeout)
+    self._ParseCustomCategories(custom_categories)
     req = {
-      'method': 'Tracing.start',
-      'params': {
-        'categories': custom_categories,
-        'bufferUsageReportingInterval': self._buffer_usage_reporting_interval,
-        'options': 'record-continuously' if record_continuously else
-                   'record-until-full'
-      }
+        'method': 'Tracing.start',
+        'params': {
+            'transferMode': 'ReportEvents',
+            'traceConfig': {
+                'recordMode':
+                    'recordContinuously'
+                    if record_continuously else 'recordUntilFull',
+                'includedCategories':
+                    self._included_categories,
+                'excludedCategories':
+                    self._excluded_categories,
+            },
+            'bufferUsageReportingInterval':
+                self._buffer_usage_reporting_interval,
+        }
     }
     self._SendRequest(req)
 
@@ -147,6 +158,22 @@
     elif 'Tracing.tracingComplete' == method:
       return True
 
+  def _ParseCustomCategories(self, custom_categories):
+    """Parse a category filter into trace config format"""
+
+    self._included_categories = []
+    self._excluded_categories = []
+
+    # See TraceConfigCategoryFilter::InitializeFromString in chromium.
+    categories = (token.strip() for token in custom_categories.split(','))
+    for category in categories:
+      if not category:
+        continue
+      if category.startswith('-'):
+        self._excluded_categories.append(category[1:])
+      else:
+        self._included_categories.append(category)
+
 
 class TracingBackendAndroid(object):
   """Android version of TracingBackend."""
diff --git a/content/browser/renderer_host/frame_sink_provider_impl.cc b/content/browser/renderer_host/frame_sink_provider_impl.cc
index b308029..376fc69 100644
--- a/content/browser/renderer_host/frame_sink_provider_impl.cc
+++ b/content/browser/renderer_host/frame_sink_provider_impl.cc
@@ -32,7 +32,7 @@
       RenderWidgetHostImpl::FromID(process_id_, widget_id);
   if (!render_widget_host_impl) {
     DLOG(ERROR) << "No RenderWidgetHost exists with id " << widget_id
-                << "in process " << process_id_;
+                << " in process " << process_id_;
     return;
   }
   render_widget_host_impl->RequestCompositorFrameSink(std::move(request),
diff --git a/content/browser/webrtc/webrtc_image_capture_browsertest.cc b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
index f4757d0..2028b32 100644
--- a/content/browser/webrtc/webrtc_image_capture_browsertest.cc
+++ b/content/browser/webrtc/webrtc_image_capture_browsertest.cc
@@ -46,13 +46,26 @@
 
 static const char kImageCaptureHtmlFile[] = "/media/image_capture_test.html";
 
+enum class Camera {
+  FAKE,
+  DEFAULT,
+#if defined(OS_WIN)
+  // Media Foundation is only available in Windows versions >= 7, below that the
+  // following flag has no effect
+  WIN_MEDIA_FOUNDATION
+#endif
+};
+
 // TODO(mcasas): enable real-camera tests by disabling the Fake Device for
 // platforms where the ImageCaptureCode is landed, https://crbug.com/656810
 static struct TargetCamera {
-  bool use_fake;
-} const kTargetCameras[] = {{true},
+  Camera camera;
+} const kTargetCameras[] = {{Camera::FAKE},
 #if defined(OS_LINUX) || defined(OS_MACOSX) || defined(OS_ANDROID)
-                            {false}
+                            {Camera::DEFAULT},
+#endif
+#if defined(OS_WIN)
+                            {Camera::WIN_MEDIA_FOUNDATION}
 #endif
 };
 
@@ -145,11 +158,23 @@
   void SetUpCommandLine(base::CommandLine* command_line) override {
     WebRtcImageCaptureBrowserTestBase::SetUpCommandLine(command_line);
 
-    if (std::get<0>(GetParam()).use_fake) {
-      base::CommandLine::ForCurrentProcess()->AppendSwitch(
-          switches::kUseFakeDeviceForMediaStream);
-      ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kUseFakeDeviceForMediaStream));
+    switch (std::get<0>(GetParam()).camera) {
+      case Camera::FAKE:
+        base::CommandLine::ForCurrentProcess()->AppendSwitch(
+            switches::kUseFakeDeviceForMediaStream);
+        ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kUseFakeDeviceForMediaStream));
+        break;
+#if defined(OS_WIN)
+      case Camera::WIN_MEDIA_FOUNDATION:
+        base::CommandLine::ForCurrentProcess()->AppendSwitch(
+            switches::kForceMediaFoundationVideoCapture);
+        ASSERT_TRUE(base::CommandLine::ForCurrentProcess()->HasSwitch(
+            switches::kForceMediaFoundationVideoCapture));
+        break;
+#endif
+      default:
+        break;
     }
   }
 
@@ -157,7 +182,7 @@
     // TODO(chfremer): Enable test cases using the video capture service with
     // real cameras as soon as root cause for https://crbug.com/733582 is
     // understood and resolved.
-    if ((!std::get<0>(GetParam()).use_fake) &&
+    if ((std::get<0>(GetParam()).camera != Camera::FAKE) &&
         (std::get<1>(GetParam()).use_video_capture_service)) {
       LOG(INFO) << "Skipping this test case";
       return true;
diff --git a/content/test/data/media/image_capture_test.html b/content/test/data/media/image_capture_test.html
index beb85fa..5239163 100644
--- a/content/test/data/media/image_capture_test.html
+++ b/content/test/data/media/image_capture_test.html
@@ -6,7 +6,7 @@
 <body>
 <script type="text/javascript" src="webrtc_test_utilities.js"></script>
 <script>
-const WIDTH = 320;
+const WIDTH = 640;
 /** @const */ var CONSTRAINTS = { width: { max : WIDTH } };
 
 // Returns a Promise resolved with |object| after a delay of |delayInMs|.
diff --git a/media/base/ipc/media_param_traits_macros.h b/media/base/ipc/media_param_traits_macros.h
index 9eeaebc..33efc65 100644
--- a/media/base/ipc/media_param_traits_macros.h
+++ b/media/base/ipc/media_param_traits_macros.h
@@ -31,8 +31,13 @@
 #include "media/base/video_rotation.h"
 #include "media/base/video_types.h"
 #include "media/base/watch_time_keys.h"
+#include "media/media_features.h"
 #include "ui/gfx/ipc/color/gfx_param_traits_macros.h"
 
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+#include "media/cdm/cdm_proxy.h"  // nogncheck
+#endif                            // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
 // Enum traits.
 
 IPC_ENUM_TRAITS_MAX_VALUE(media::AudioCodec, media::AudioCodec::kAudioCodecMax)
@@ -55,6 +60,17 @@
 IPC_ENUM_TRAITS_MAX_VALUE(media::CdmPromise::Exception,
                           media::CdmPromise::Exception::EXCEPTION_MAX)
 
+#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
+IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Function,
+                          media::CdmProxy::Function::kMax)
+
+IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Protocol,
+                          media::CdmProxy::Protocol::kMax)
+
+IPC_ENUM_TRAITS_MAX_VALUE(media::CdmProxy::Status,
+                          media::CdmProxy::Status::kMax)
+#endif  // BUILDFLAG(ENABLE_LIBRARY_CDMS)
+
 IPC_ENUM_TRAITS_MAX_VALUE(media::CdmSessionType,
                           media::CdmSessionType::SESSION_TYPE_MAX)
 
diff --git a/media/capture/video/video_capture_device_unittest.cc b/media/capture/video/video_capture_device_unittest.cc
index c9d9cf4..bac8c1c8 100644
--- a/media/capture/video/video_capture_device_unittest.cc
+++ b/media/capture/video/video_capture_device_unittest.cc
@@ -28,9 +28,11 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(OS_WIN)
+#include <mfcaptureengine.h>
 #include "base/win/scoped_com_initializer.h"
 #include "base/win/windows_version.h"  // For fine-grained suppression.
 #include "media/capture/video/win/video_capture_device_factory_win.h"
+#include "media/capture/video/win/video_capture_device_mf_win.h"
 #endif
 
 #if defined(OS_MACOSX)
@@ -88,9 +90,16 @@
 #define MAYBE_GetPhotoState DISABLED_GetPhotoState
 #endif
 
+// Wrap the TEST_P macro into another one to allow to preprocess |test_name|
+// macros. Needed until https://github.com/google/googletest/issues/389 is
+// fixed.
+#define WRAPPED_TEST_P(test_case_name, test_name) \
+  TEST_P(test_case_name, test_name)
+
 using ::testing::_;
 using ::testing::Invoke;
 using ::testing::SaveArg;
+using ::testing::Return;
 
 namespace media {
 namespace {
@@ -116,6 +125,35 @@
 };
 #endif
 
+enum VideoCaptureImplementationTweak {
+  NONE,
+#if defined(OS_WIN)
+  WIN_MEDIA_FOUNDATION
+#endif
+};
+
+#if defined(OS_WIN)
+class MockMFPhotoCallback final : public IMFCaptureEngineOnSampleCallback {
+ public:
+  ~MockMFPhotoCallback() {}
+
+  MOCK_METHOD2(DoQueryInterface, HRESULT(REFIID, void**));
+  MOCK_METHOD0(DoAddRef, ULONG(void));
+  MOCK_METHOD0(DoRelease, ULONG(void));
+  MOCK_METHOD1(DoOnSample, HRESULT(IMFSample*));
+
+  STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
+    return DoQueryInterface(riid, object);
+  }
+
+  STDMETHOD_(ULONG, AddRef)() override { return DoAddRef(); }
+
+  STDMETHOD_(ULONG, Release)() override { return DoRelease(); }
+
+  STDMETHOD(OnSample)(IMFSample* sample) override { return DoOnSample(sample); }
+};
+#endif
+
 class MockVideoCaptureClient : public VideoCaptureDevice::Client {
  public:
   MOCK_METHOD0(DoReserveOutputBuffer, void(void));
@@ -261,7 +299,19 @@
 
 }  // namespace
 
-class VideoCaptureDeviceTest : public testing::TestWithParam<gfx::Size> {
+class VideoCaptureDeviceTest
+    : public testing::TestWithParam<
+          std::tuple<gfx::Size, VideoCaptureImplementationTweak>> {
+ public:
+#if defined(OS_WIN)
+  scoped_refptr<IMFCaptureEngineOnSampleCallback> CreateMockPhotoCallback(
+      MockMFPhotoCallback* mock_photo_callback,
+      VideoCaptureDevice::TakePhotoCallback callback,
+      VideoCaptureFormat format) {
+    return scoped_refptr<IMFCaptureEngineOnSampleCallback>(mock_photo_callback);
+  }
+#endif
+
  protected:
   typedef VideoCaptureDevice::Client Client;
 
@@ -294,6 +344,10 @@
     static_cast<VideoCaptureDeviceFactoryAndroid*>(
         video_capture_device_factory_.get())
         ->ConfigureForTesting();
+#elif defined(OS_WIN)
+    static_cast<VideoCaptureDeviceFactoryWin*>(
+        video_capture_device_factory_.get())
+        ->set_use_media_foundation_for_testing(UseWinMediaFoundation());
 #endif
     EXPECT_CALL(*video_capture_client_, DoReserveOutputBuffer()).Times(0);
     EXPECT_CALL(*video_capture_client_, DoOnIncomingCapturedBuffer()).Times(0);
@@ -301,6 +355,12 @@
         .Times(0);
   }
 
+#if defined(OS_WIN)
+  bool UseWinMediaFoundation() {
+    return std::get<1>(GetParam()) == WIN_MEDIA_FOUNDATION;
+  }
+#endif
+
   void ResetWithNewClient() {
     video_capture_client_.reset(new MockVideoCaptureClient(base::Bind(
         &VideoCaptureDeviceTest::OnFrameCaptured, base::Unretained(this))));
@@ -407,7 +467,7 @@
 #define MAYBE_OpenInvalidDevice OpenInvalidDevice
 #endif
 // Tries to allocate an invalid device and verifies it doesn't work.
-TEST_F(VideoCaptureDeviceTest, MAYBE_OpenInvalidDevice) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, MAYBE_OpenInvalidDevice) {
   VideoCaptureDeviceDescriptor invalid_descriptor;
   invalid_descriptor.device_id = "jibberish";
   invalid_descriptor.display_name = "jibberish";
@@ -439,12 +499,12 @@
 }
 
 // Allocates the first enumerated device, and expects a frame.
-TEST_P(VideoCaptureDeviceTest, CaptureWithSize) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, CaptureWithSize) {
   const auto descriptor = FindUsableDeviceDescriptor();
   if (!descriptor)
     return;
 
-  const gfx::Size& size = GetParam();
+  const gfx::Size& size = std::get<0>(GetParam());
   if (!IsCaptureSizeSupported(*descriptor, size))
     return;
   const int width = size.width();
@@ -474,14 +534,22 @@
 }
 
 const gfx::Size kCaptureSizes[] = {gfx::Size(640, 480), gfx::Size(1280, 720)};
+const VideoCaptureImplementationTweak kCaptureImplementationTweaks[] = {
+    NONE,
+#if defined(OS_WIN)
+    WIN_MEDIA_FOUNDATION
+#endif
+};
 
-INSTANTIATE_TEST_CASE_P(VideoCaptureDeviceTests,
-                        VideoCaptureDeviceTest,
-                        testing::ValuesIn(kCaptureSizes));
+INSTANTIATE_TEST_CASE_P(
+    VideoCaptureDeviceTests,
+    VideoCaptureDeviceTest,
+    testing::Combine(testing::ValuesIn(kCaptureSizes),
+                     testing::ValuesIn(kCaptureImplementationTweaks)));
 
 // Allocates a device with an uncommon resolution and verifies frames are
 // captured in a close, much more typical one.
-TEST_F(VideoCaptureDeviceTest, MAYBE_AllocateBadSize) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, MAYBE_AllocateBadSize) {
   const auto descriptor = FindUsableDeviceDescriptor();
   if (!descriptor)
     return;
@@ -508,7 +576,7 @@
 }
 
 // Cause hangs on Windows, Linux. Fails Android. https://crbug.com/417824
-TEST_F(VideoCaptureDeviceTest, DISABLED_ReAllocateCamera) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, DISABLED_ReAllocateCamera) {
   const auto descriptor = FindUsableDeviceDescriptor();
   if (!descriptor)
     return;
@@ -552,7 +620,7 @@
 }
 
 // Starts the camera in 720p to try and capture MJPEG format.
-TEST_F(VideoCaptureDeviceTest, MAYBE_CaptureMjpeg) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, MAYBE_CaptureMjpeg) {
   std::unique_ptr<VideoCaptureDeviceDescriptor> device_descriptor =
       GetFirstDeviceDescriptorSupportingPixelFormat(PIXEL_FORMAT_MJPEG);
   if (!device_descriptor) {
@@ -589,7 +657,7 @@
   device->StopAndDeAllocate();
 }
 
-TEST_F(VideoCaptureDeviceTest, NoCameraSupportsPixelFormatMax) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, NoCameraSupportsPixelFormatMax) {
   // Use PIXEL_FORMAT_MAX to iterate all device names for testing
   // GetDeviceSupportedFormats().
   std::unique_ptr<VideoCaptureDeviceDescriptor> device_descriptor =
@@ -601,7 +669,7 @@
 
 // Starts the camera and verifies that a photo can be taken. The correctness of
 // the photo is enforced by MockImageCaptureClient.
-TEST_F(VideoCaptureDeviceTest, MAYBE_TakePhoto) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, MAYBE_TakePhoto) {
   const auto descriptor = FindUsableDeviceDescriptor();
   if (!descriptor)
     return;
@@ -650,7 +718,7 @@
 }
 
 // Starts the camera and verifies that the photo capabilities can be retrieved.
-TEST_F(VideoCaptureDeviceTest, MAYBE_GetPhotoState) {
+WRAPPED_TEST_P(VideoCaptureDeviceTest, MAYBE_GetPhotoState) {
   const auto descriptor = FindUsableDeviceDescriptor();
   if (!descriptor)
     return;
@@ -701,4 +769,55 @@
   device->StopAndDeAllocate();
 }
 
+#if defined(OS_WIN)
+// Verifies that the photo callback is correctly released by MediaFoundation
+WRAPPED_TEST_P(VideoCaptureDeviceTest, CheckPhotoCallbackRelease) {
+  if (!UseWinMediaFoundation())
+    return;
+
+  std::unique_ptr<VideoCaptureDeviceDescriptor> descriptor =
+      GetFirstDeviceDescriptorSupportingPixelFormat(PIXEL_FORMAT_MJPEG);
+  if (!descriptor) {
+    DVLOG(1) << "No usable media foundation device descriptor. Exiting test.";
+    return;
+  }
+
+  MockMFPhotoCallback* callback = new MockMFPhotoCallback();
+  EXPECT_CALL(*callback, DoQueryInterface(_, _)).WillRepeatedly(Return(S_OK));
+  EXPECT_CALL(*callback, DoAddRef()).WillOnce(Return(1U));
+  EXPECT_CALL(*callback, DoRelease()).WillOnce(Return(1U));
+  EXPECT_CALL(*callback, DoOnSample(_)).WillOnce(Return(S_OK));
+
+  EXPECT_CALL(*video_capture_client_, OnError(_, _)).Times(0);
+  EXPECT_CALL(*video_capture_client_, OnStarted());
+
+  std::unique_ptr<VideoCaptureDevice> device(
+      video_capture_device_factory_->CreateDevice(*descriptor));
+  ASSERT_TRUE(device);
+  static_cast<VideoCaptureDeviceMFWin*>(device.get())
+      ->set_create_mf_photo_callback_for_testing(
+          base::Bind(&VideoCaptureDeviceTest::CreateMockPhotoCallback,
+                     base::Unretained(this), callback));
+
+  VideoCaptureParams capture_params;
+  capture_params.requested_format.frame_size.SetSize(320, 240);
+  capture_params.requested_format.frame_rate = 30;
+  capture_params.requested_format.pixel_format = PIXEL_FORMAT_MJPEG;
+  device->AllocateAndStart(capture_params, std::move(video_capture_client_));
+
+  VideoCaptureDevice::TakePhotoCallback scoped_callback = base::BindOnce(
+      &MockImageCaptureClient::DoOnPhotoTaken, image_capture_client_);
+
+  base::RunLoop run_loop;
+  base::Closure quit_closure = BindToCurrentLoop(run_loop.QuitClosure());
+  EXPECT_CALL(*image_capture_client_.get(), OnCorrectPhotoTaken())
+      .WillOnce(RunClosure(quit_closure));
+
+  device->TakePhoto(std::move(scoped_callback));
+  run_loop.Run();
+
+  device->StopAndDeAllocate();
+}
+#endif
+
 };  // namespace media
diff --git a/media/capture/video/win/video_capture_device_factory_win.cc b/media/capture/video/win/video_capture_device_factory_win.cc
index fec2ed9..8f6168f 100644
--- a/media/capture/video/win/video_capture_device_factory_win.cc
+++ b/media/capture/video/win/video_capture_device_factory_win.cc
@@ -69,10 +69,9 @@
 
 static bool LoadMediaFoundationDlls() {
   static const wchar_t* const kMfDLLs[] = {
-      L"%WINDIR%\\system32\\mf.dll",
-      L"%WINDIR%\\system32\\mfplat.dll",
+      L"%WINDIR%\\system32\\mf.dll", L"%WINDIR%\\system32\\mfplat.dll",
       L"%WINDIR%\\system32\\mfreadwrite.dll",
-  };
+      L"%WINDIR%\\system32\\MFCaptureEngine.dll"};
 
   for (const wchar_t* kMfDLL : kMfDLLs) {
     wchar_t path[MAX_PATH] = {0};
@@ -86,8 +85,13 @@
 static bool PrepareVideoCaptureAttributesMediaFoundation(
     IMFAttributes** attributes,
     int count) {
-  if (!InitializeMediaFoundation())
+  // Once https://bugs.chromium.org/p/chromium/issues/detail?id=791615 is fixed,
+  // we must make sure that this method succeeds in capture_unittests context
+  // when MediaFoundation is enabled.
+  if (!VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() ||
+      !InitializeMediaFoundation()) {
     return false;
+  }
 
   if (FAILED(MFCreateAttributes(attributes, count)))
     return false;
@@ -286,8 +290,9 @@
 
   DWORD stream_index = 0;
   ComPtr<IMFMediaType> type;
-  while (SUCCEEDED(reader->GetNativeMediaType(kFirstVideoStream, stream_index,
-                                              type.GetAddressOf()))) {
+  while (SUCCEEDED(hr = reader->GetNativeMediaType(
+                       static_cast<DWORD>(MF_SOURCE_READER_FIRST_VIDEO_STREAM),
+                       stream_index, type.GetAddressOf()))) {
     UINT32 width, height;
     hr = MFGetAttributeSize(type.Get(), MF_MT_FRAME_SIZE, &width, &height);
     if (FAILED(hr)) {
diff --git a/media/capture/video/win/video_capture_device_factory_win.h b/media/capture/video/win/video_capture_device_factory_win.h
index 9afcc64..a7d36783 100644
--- a/media/capture/video/win/video_capture_device_factory_win.h
+++ b/media/capture/video/win/video_capture_device_factory_win.h
@@ -30,10 +30,12 @@
       const VideoCaptureDeviceDescriptor& device_descriptor,
       VideoCaptureFormats* supported_formats) override;
 
+  void set_use_media_foundation_for_testing(bool use) {
+    use_media_foundation_ = use;
+  }
+
  private:
-  // Media Foundation is available in Win7 and later, use it if explicitly
-  // forced via flag, else use DirectShow.
-  const bool use_media_foundation_;
+  bool use_media_foundation_;
 
   DISALLOW_COPY_AND_ASSIGN(VideoCaptureDeviceFactoryWin);
 };
diff --git a/media/capture/video/win/video_capture_device_mf_win.cc b/media/capture/video/win/video_capture_device_mf_win.cc
index 73a144b..1c07614 100644
--- a/media/capture/video/win/video_capture_device_mf_win.cc
+++ b/media/capture/video/win/video_capture_device_mf_win.cc
@@ -7,7 +7,9 @@
 #include <mfapi.h>
 #include <mferror.h>
 #include <stddef.h>
+#include <wincodec.h>
 
+#include <thread>
 #include <utility>
 
 #include "base/location.h"
@@ -17,14 +19,102 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/win/scoped_co_mem.h"
 #include "base/win/windows_version.h"
+#include "media/capture/video/blob_utils.h"
 #include "media/capture/video/win/capability_list_win.h"
 #include "media/capture/video/win/sink_filter_win.h"
 
 using base::win::ScopedCoMem;
+using Microsoft::WRL::ComPtr;
+using base::Location;
 
 namespace media {
 
-static bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) {
+namespace {
+class MFPhotoCallback final
+    : public base::RefCountedThreadSafe<MFPhotoCallback>,
+      public IMFCaptureEngineOnSampleCallback {
+ public:
+  MFPhotoCallback(VideoCaptureDevice::TakePhotoCallback callback,
+                  VideoCaptureFormat format)
+      : callback_(std::move(callback)), format_(format) {}
+
+  STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
+    if (riid == IID_IUnknown || riid == IID_IMFCaptureEngineOnSampleCallback) {
+      AddRef();
+      *object = static_cast<IMFCaptureEngineOnSampleCallback*>(this);
+      return S_OK;
+    }
+    return E_NOINTERFACE;
+  }
+
+  STDMETHOD_(ULONG, AddRef)() override {
+    base::RefCountedThreadSafe<MFPhotoCallback>::AddRef();
+    return 1U;
+  }
+
+  STDMETHOD_(ULONG, Release)() override {
+    base::RefCountedThreadSafe<MFPhotoCallback>::Release();
+    return 1U;
+  }
+
+  STDMETHOD(OnSample)(IMFSample* sample) override {
+    if (!sample)
+      return S_OK;
+
+    DWORD buffer_count = 0;
+    sample->GetBufferCount(&buffer_count);
+
+    for (DWORD i = 0; i < buffer_count; ++i) {
+      ComPtr<IMFMediaBuffer> buffer;
+      sample->GetBufferByIndex(i, buffer.GetAddressOf());
+      if (!buffer)
+        continue;
+
+      BYTE* data = nullptr;
+      DWORD max_length = 0;
+      DWORD length = 0;
+      buffer->Lock(&data, &max_length, &length);
+      mojom::BlobPtr blob = Blobify(data, length, format_);
+      buffer->Unlock();
+      if (blob) {
+        std::move(callback_).Run(std::move(blob));
+        // What is it supposed to mean if there is more than one buffer sent to
+        // us as a response to requesting a single still image? Are we supposed
+        // to somehow concatenate the buffers? Or is it safe to ignore extra
+        // buffers? For now, we ignore extra buffers.
+        break;
+      }
+    }
+    return S_OK;
+  }
+
+ private:
+  friend class base::RefCountedThreadSafe<MFPhotoCallback>;
+  ~MFPhotoCallback() = default;
+
+  VideoCaptureDevice::TakePhotoCallback callback_;
+  const VideoCaptureFormat format_;
+
+  DISALLOW_COPY_AND_ASSIGN(MFPhotoCallback);
+};
+
+scoped_refptr<IMFCaptureEngineOnSampleCallback> CreateMFPhotoCallback(
+    VideoCaptureDevice::TakePhotoCallback callback,
+    VideoCaptureFormat format) {
+  return scoped_refptr<IMFCaptureEngineOnSampleCallback>(
+      new MFPhotoCallback(std::move(callback), format));
+}
+}  // namespace
+
+void LogError(const Location& from_here, HRESULT hr) {
+#if !defined(NDEBUG)
+  DPLOG(ERROR) << from_here.ToString()
+               << " hr = " << logging::SystemErrorCodeToString(hr);
+#endif
+}
+
+static bool GetFrameSizeFromMediaType(IMFMediaType* type,
+                                      gfx::Size* frame_size) {
   UINT32 width32, height32;
   if (FAILED(MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width32, &height32)))
     return false;
@@ -32,7 +122,7 @@
   return true;
 }
 
-static bool GetFrameRate(IMFMediaType* type, float* frame_rate) {
+static bool GetFrameRateFromMediaType(IMFMediaType* type, float* frame_rate) {
   UINT32 numerator, denominator;
   if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator,
                                  &denominator)) ||
@@ -43,12 +133,19 @@
   return true;
 }
 
-static bool FillFormat(IMFMediaType* type, VideoCaptureFormat* format) {
-  GUID type_guid;
-  if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) ||
-      !GetFrameSize(type, &format->frame_size) ||
-      !GetFrameRate(type, &format->frame_rate) ||
-      !VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
+static bool GetFormatFromMediaType(IMFMediaType* type,
+                                   VideoCaptureFormat* format) {
+  GUID major_type_guid;
+  if (FAILED(type->GetGUID(MF_MT_MAJOR_TYPE, &major_type_guid)) ||
+      (major_type_guid != MFMediaType_Image &&
+       !GetFrameRateFromMediaType(type, &format->frame_rate))) {
+    return false;
+  }
+
+  GUID sub_type_guid;
+  if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &sub_type_guid)) ||
+      !GetFrameSizeFromMediaType(type, &format->frame_size) ||
+      !VideoCaptureDeviceMFWin::FormatFromGuid(sub_type_guid,
                                                &format->pixel_format)) {
     return false;
   }
@@ -56,18 +153,98 @@
   return true;
 }
 
-HRESULT FillCapabilities(IMFSourceReader* source,
-                         CapabilityList* capabilities) {
-  DWORD stream_index = 0;
-  Microsoft::WRL::ComPtr<IMFMediaType> type;
+static HRESULT CopyAttribute(IMFAttributes* source_attributes,
+                             IMFAttributes* destination_attributes,
+                             const GUID& key) {
+  PROPVARIANT var;
+  PropVariantInit(&var);
+  HRESULT hr = source_attributes->GetItem(key, &var);
+  if (FAILED(hr))
+    return hr;
+
+  hr = destination_attributes->SetItem(key, var);
+  PropVariantClear(&var);
+  return hr;
+}
+
+static HRESULT ConvertToPhotoJpegMediaType(
+    IMFMediaType* source_media_type,
+    IMFMediaType* destination_media_type) {
+  HRESULT hr =
+      destination_media_type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Image);
+  if (FAILED(hr))
+    return hr;
+
+  hr = destination_media_type->SetGUID(MF_MT_SUBTYPE, GUID_ContainerFormatJpeg);
+  if (FAILED(hr))
+    return hr;
+
+  return CopyAttribute(source_media_type, destination_media_type,
+                       MF_MT_FRAME_SIZE);
+}
+
+static const CapabilityWin& GetBestMatchedPhotoCapability(
+    ComPtr<IMFMediaType> current_media_type,
+    gfx::Size requested_size,
+    const CapabilityList& capabilities) {
+  gfx::Size current_size;
+  GetFrameSizeFromMediaType(current_media_type.Get(), &current_size);
+
+  int requested_height = requested_size.height() > 0 ? requested_size.height()
+                                                     : current_size.height();
+  int requested_width = requested_size.width() > 0 ? requested_size.width()
+                                                   : current_size.width();
+
+  const CapabilityWin* best_match = &(*capabilities.begin());
+  for (const CapabilityWin& capability : capabilities) {
+    int height = capability.supported_format.frame_size.height();
+    int width = capability.supported_format.frame_size.width();
+    int best_height = best_match->supported_format.frame_size.height();
+    int best_width = best_match->supported_format.frame_size.width();
+
+    if (std::abs(height - requested_height) <= std::abs(height - best_height) &&
+        std::abs(width - requested_width) <= std::abs(width - best_width)) {
+      best_match = &capability;
+    }
+  }
+  return *best_match;
+}
+
+HRESULT GetAvailableDeviceMediaType(IMFCaptureSource* source,
+                                    DWORD stream_index,
+                                    DWORD media_type_index,
+                                    IMFMediaType** type) {
   HRESULT hr;
-  while (SUCCEEDED(hr = source->GetNativeMediaType(
-                       kFirstVideoStream, stream_index, type.GetAddressOf()))) {
+  // Rarely, for some unknown reason, GetAvailableDeviceMediaType returns an
+  // undocumented MF_E_INVALIDREQUEST. Retrying solves the issue.
+  int retry_count = 0;
+  do {
+    hr = source->GetAvailableDeviceMediaType(stream_index, media_type_index,
+                                             type);
+    if (FAILED(hr))
+      base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
+
+    // Give up after ~10 seconds
+  } while (hr == MF_E_INVALIDREQUEST && retry_count++ < 200);
+
+  return hr;
+}
+
+HRESULT FillCapabilities(DWORD stream,
+                         IMFCaptureSource* source,
+                         CapabilityList* capabilities) {
+  DWORD media_type_index = 0;
+  ComPtr<IMFMediaType> type;
+  HRESULT hr;
+
+  while (
+      SUCCEEDED(hr = GetAvailableDeviceMediaType(
+                    source, stream, media_type_index, type.GetAddressOf()))) {
     VideoCaptureFormat format;
-    if (FillFormat(type.Get(), &format))
-      capabilities->emplace_back(stream_index, format);
+    if (GetFormatFromMediaType(type.Get(), &format))
+      capabilities->emplace_back(media_type_index, format);
     type.Reset();
-    ++stream_index;
+    ++media_type_index;
   }
 
   if (capabilities->empty() && (SUCCEEDED(hr) || hr == MF_E_NO_MORE_TYPES))
@@ -76,40 +253,51 @@
   return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr;
 }
 
-class MFReaderCallback final
-    : public base::RefCountedThreadSafe<MFReaderCallback>,
-      public IMFSourceReaderCallback {
+class MFVideoCallback final
+    : public base::RefCountedThreadSafe<MFVideoCallback>,
+      public IMFCaptureEngineOnSampleCallback,
+      public IMFCaptureEngineOnEventCallback {
  public:
-  MFReaderCallback(VideoCaptureDeviceMFWin* observer)
-      : observer_(observer), wait_event_(NULL) {}
-
-  void SetSignalOnFlush(base::WaitableEvent* event) { wait_event_ = event; }
+  MFVideoCallback(VideoCaptureDeviceMFWin* observer) : observer_(observer) {}
 
   STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
-    if (riid != IID_IUnknown && riid != IID_IMFSourceReaderCallback)
-      return E_NOINTERFACE;
-    *object = static_cast<IMFSourceReaderCallback*>(this);
-    AddRef();
-    return S_OK;
+    HRESULT hr = E_NOINTERFACE;
+    if (riid == IID_IUnknown) {
+      *object = this;
+      hr = S_OK;
+    } else if (riid == IID_IMFCaptureEngineOnSampleCallback) {
+      *object = static_cast<IMFCaptureEngineOnSampleCallback*>(this);
+      hr = S_OK;
+    } else if (riid == IID_IMFCaptureEngineOnEventCallback) {
+      *object = static_cast<IMFCaptureEngineOnEventCallback*>(this);
+      hr = S_OK;
+    }
+    if (SUCCEEDED(hr))
+      AddRef();
+
+    return hr;
   }
 
   STDMETHOD_(ULONG, AddRef)() override {
-    base::RefCountedThreadSafe<MFReaderCallback>::AddRef();
+    base::RefCountedThreadSafe<MFVideoCallback>::AddRef();
     return 1U;
   }
 
   STDMETHOD_(ULONG, Release)() override {
-    base::RefCountedThreadSafe<MFReaderCallback>::Release();
+    base::RefCountedThreadSafe<MFVideoCallback>::Release();
     return 1U;
   }
 
-  STDMETHOD(OnReadSample)
-  (HRESULT status,
-   DWORD stream_index,
-   DWORD stream_flags,
-   LONGLONG raw_time_stamp,
-   IMFSample* sample) override {
+  STDMETHOD(OnEvent)(IMFMediaEvent* media_event) override {
+    observer_->OnEvent(media_event);
+    return S_OK;
+  }
+
+  STDMETHOD(OnSample)(IMFSample* sample) override {
     base::TimeTicks reference_time(base::TimeTicks::Now());
+
+    LONGLONG raw_time_stamp = 0;
+    sample->GetSampleTime(&raw_time_stamp);
     base::TimeDelta timestamp =
         base::TimeDelta::FromMicroseconds(raw_time_stamp / 10);
     if (!sample) {
@@ -121,9 +309,9 @@
     sample->GetBufferCount(&count);
 
     for (DWORD i = 0; i < count; ++i) {
-      Microsoft::WRL::ComPtr<IMFMediaBuffer> buffer;
+      ComPtr<IMFMediaBuffer> buffer;
       sample->GetBufferByIndex(i, buffer.GetAddressOf());
-      if (buffer.Get()) {
+      if (buffer) {
         DWORD length = 0, max_length = 0;
         BYTE* data = NULL;
         buffer->Lock(&data, &max_length, &length);
@@ -135,25 +323,10 @@
     return S_OK;
   }
 
-  STDMETHOD(OnFlush)(DWORD stream_index) override {
-    if (wait_event_) {
-      wait_event_->Signal();
-      wait_event_ = NULL;
-    }
-    return S_OK;
-  }
-
-  STDMETHOD(OnEvent)(DWORD stream_index, IMFMediaEvent* event) override {
-    NOTIMPLEMENTED();
-    return S_OK;
-  }
-
  private:
-  friend class base::RefCountedThreadSafe<MFReaderCallback>;
-  ~MFReaderCallback() {}
-
+  friend class base::RefCountedThreadSafe<MFVideoCallback>;
+  ~MFVideoCallback() {}
   VideoCaptureDeviceMFWin* observer_;
-  base::WaitableEvent* wait_event_;
 };
 
 // static
@@ -169,6 +342,7 @@
       {MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24},
       {MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB},
       {MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG},
+      {GUID_ContainerFormatJpeg, PIXEL_FORMAT_MJPEG},
       {MFVideoFormat_YV12, PIXEL_FORMAT_YV12},
       {kMediaSubTypeY16, PIXEL_FORMAT_Y16},
       {kMediaSubTypeZ16, PIXEL_FORMAT_Y16},
@@ -187,7 +361,9 @@
 
 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
     const VideoCaptureDeviceDescriptor& device_descriptor)
-    : descriptor_(device_descriptor), capture_(0) {
+    : descriptor_(device_descriptor),
+      create_mf_photo_callback_(base::Bind(&CreateMFPhotoCallback)),
+      is_started_(false) {
   DETACH_FROM_SEQUENCE(sequence_checker_);
 }
 
@@ -195,20 +371,44 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 }
 
-bool VideoCaptureDeviceMFWin::Init(
-    const Microsoft::WRL::ComPtr<IMFMediaSource>& source) {
+bool VideoCaptureDeviceMFWin::Init(const ComPtr<IMFMediaSource>& source) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  DCHECK(!reader_.Get());
+  DCHECK(!engine_);
 
-  Microsoft::WRL::ComPtr<IMFAttributes> attributes;
+  HRESULT hr = S_OK;
+  ComPtr<IMFAttributes> attributes;
+  ComPtr<IMFCaptureEngineClassFactory> capture_engine_class_factory;
   MFCreateAttributes(attributes.GetAddressOf(), 1);
-  DCHECK(attributes.Get());
+  DCHECK(attributes);
 
-  callback_ = new MFReaderCallback(this);
-  attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get());
+  hr = CoCreateInstance(
+      CLSID_MFCaptureEngineClassFactory, NULL, CLSCTX_INPROC_SERVER,
+      IID_PPV_ARGS(capture_engine_class_factory.GetAddressOf()));
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return false;
+  }
+  hr = capture_engine_class_factory->CreateInstance(
+      CLSID_MFCaptureEngine, IID_PPV_ARGS(engine_.GetAddressOf()));
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return false;
+  }
 
-  return SUCCEEDED(MFCreateSourceReaderFromMediaSource(
-      source.Get(), attributes.Get(), reader_.GetAddressOf()));
+  video_callback_ = new MFVideoCallback(this);
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return false;
+  }
+
+  hr = engine_->Initialize(video_callback_.get(), attributes.Get(), nullptr,
+                           source.Get());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return false;
+  }
+
+  return true;
 }
 
 void VideoCaptureDeviceMFWin::AllocateAndStart(
@@ -219,65 +419,314 @@
   base::AutoLock lock(lock_);
 
   client_ = std::move(client);
-  DCHECK_EQ(capture_, false);
+  DCHECK_EQ(false, is_started_);
 
   CapabilityList capabilities;
-  HRESULT hr = S_OK;
-  if (reader_.Get()) {
-    hr = FillCapabilities(reader_.Get(), &capabilities);
-    if (SUCCEEDED(hr)) {
-      const CapabilityWin found_capability =
-          GetBestMatchedCapability(params.requested_format, capabilities);
-      Microsoft::WRL::ComPtr<IMFMediaType> type;
-      hr = reader_->GetNativeMediaType(kFirstVideoStream,
-                                       found_capability.stream_index,
-                                       type.GetAddressOf());
-      if (SUCCEEDED(hr)) {
-        hr = reader_->SetCurrentMediaType(kFirstVideoStream, NULL, type.Get());
-        if (SUCCEEDED(hr)) {
-          hr =
-              reader_->ReadSample(kFirstVideoStream, 0, NULL, NULL, NULL, NULL);
-          if (SUCCEEDED(hr)) {
-            capture_format_ = found_capability.supported_format;
-            client_->OnStarted();
-            capture_ = true;
-            return;
-          }
-        }
-      }
-    }
+  if (!engine_) {
+    OnError(FROM_HERE, E_FAIL);
+    return;
   }
 
-  OnError(FROM_HERE, hr);
+  ComPtr<IMFCaptureSource> source;
+  HRESULT hr = engine_->GetSource(source.GetAddressOf());
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = FillCapabilities(kPreferredVideoPreviewStream, source.Get(),
+                        &capabilities);
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  const CapabilityWin found_capability =
+      GetBestMatchedCapability(params.requested_format, capabilities);
+  ComPtr<IMFMediaType> type;
+  hr = GetAvailableDeviceMediaType(source.Get(), kPreferredVideoPreviewStream,
+                                   found_capability.stream_index,
+                                   type.GetAddressOf());
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = source->SetCurrentDeviceMediaType(kPreferredVideoPreviewStream,
+                                         type.Get());
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFCaptureSink> sink;
+  hr = engine_->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PREVIEW,
+                        sink.GetAddressOf());
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFCapturePreviewSink> preview_sink;
+  hr = sink->QueryInterface(IID_PPV_ARGS(preview_sink.GetAddressOf()));
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = preview_sink->RemoveAllStreams();
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  DWORD dw_sink_stream_index = 0;
+  hr = preview_sink->AddStream(kPreferredVideoPreviewStream, type.Get(), NULL,
+                               &dw_sink_stream_index);
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = preview_sink->SetSampleCallback(dw_sink_stream_index,
+                                       video_callback_.get());
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = engine_->StartPreview();
+  if (FAILED(hr)) {
+    OnError(FROM_HERE, hr);
+    return;
+  }
+
+  capture_video_format_ = found_capability.supported_format;
+  client_->OnStarted();
+  is_started_ = true;
 }
 
 void VideoCaptureDeviceMFWin::StopAndDeAllocate() {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
-  base::WaitableEvent flushed(base::WaitableEvent::ResetPolicy::AUTOMATIC,
-                              base::WaitableEvent::InitialState::NOT_SIGNALED);
-  const int kFlushTimeOutInMs = 1000;
-  bool wait = false;
-  {
-    base::AutoLock lock(lock_);
-    if (capture_) {
-      capture_ = false;
-      callback_->SetSignalOnFlush(&flushed);
-      wait = SUCCEEDED(
-          reader_->Flush(static_cast<DWORD>(MF_SOURCE_READER_ALL_STREAMS)));
-      if (!wait) {
-        callback_->SetSignalOnFlush(NULL);
-      }
-    }
-    client_.reset();
+  base::AutoLock lock(lock_);
+
+  if (is_started_ && engine_)
+    engine_->StopPreview();
+  is_started_ = false;
+
+  client_.reset();
+}
+
+void VideoCaptureDeviceMFWin::TakePhoto(TakePhotoCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!is_started_)
+    return;
+
+  ComPtr<IMFCaptureSource> source;
+  HRESULT hr = engine_->GetSource(source.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
   }
 
-  // If the device has been unplugged, the Flush() won't trigger the event
-  // and a timeout will happen.
-  // TODO(tommi): Hook up the IMFMediaEventGenerator notifications API and
-  // do not wait at all after getting MEVideoCaptureDeviceRemoved event.
-  // See issue/226396.
-  if (wait)
-    flushed.TimedWait(base::TimeDelta::FromMilliseconds(kFlushTimeOutInMs));
+  ComPtr<IMFMediaType> current_media_type;
+  hr = source->GetCurrentDeviceMediaType(kPreferredPhotoStream,
+                                         current_media_type.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFMediaType> photo_media_type;
+  hr = MFCreateMediaType(photo_media_type.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = ConvertToPhotoJpegMediaType(current_media_type.Get(),
+                                   photo_media_type.Get());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = source->SetCurrentDeviceMediaType(kPreferredPhotoStream,
+                                         photo_media_type.Get());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  VideoCaptureFormat format;
+  hr = GetFormatFromMediaType(photo_media_type.Get(), &format) ? S_OK : E_FAIL;
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFCaptureSink> sink;
+  hr = engine_->GetSink(MF_CAPTURE_ENGINE_SINK_TYPE_PHOTO, sink.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFCapturePhotoSink> photo_sink;
+  hr = sink->QueryInterface(IID_PPV_ARGS(photo_sink.GetAddressOf()));
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = photo_sink->RemoveAllStreams();
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  DWORD dw_sink_stream_index = 0;
+  hr = photo_sink->AddStream(kPreferredPhotoStream, photo_media_type.Get(),
+                             NULL, &dw_sink_stream_index);
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  scoped_refptr<IMFCaptureEngineOnSampleCallback> photo_callback =
+      create_mf_photo_callback_.Run(std::move(callback), format);
+  hr = photo_sink->SetSampleCallback(photo_callback.get());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  hr = engine_->TakePhoto();
+  if (FAILED(hr))
+    LogError(FROM_HERE, hr);
+}
+
+void VideoCaptureDeviceMFWin::GetPhotoState(GetPhotoStateCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!is_started_)
+    return;
+
+  ComPtr<IMFCaptureSource> source;
+  HRESULT hr = engine_->GetSource(source.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  CapabilityList capabilities;
+  hr = FillCapabilities(kPreferredPhotoStream, source.Get(), &capabilities);
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  ComPtr<IMFMediaType> current_media_type;
+  hr = source->GetCurrentDeviceMediaType(kPreferredPhotoStream,
+                                         current_media_type.GetAddressOf());
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  auto photo_capabilities = mojom::PhotoState::New();
+  gfx::Size current_size;
+  GetFrameSizeFromMediaType(current_media_type.Get(), &current_size);
+
+  gfx::Size min_size = gfx::Size(current_size.width(), current_size.height());
+  gfx::Size max_size = gfx::Size(current_size.width(), current_size.height());
+  for (const CapabilityWin& capability : capabilities) {
+    min_size.SetToMin(capability.supported_format.frame_size);
+    max_size.SetToMax(capability.supported_format.frame_size);
+  }
+
+  photo_capabilities->height = mojom::Range::New(
+      max_size.height(), min_size.height(), current_size.height(), 1);
+  photo_capabilities->width = mojom::Range::New(
+      max_size.width(), min_size.width(), current_size.width(), 1);
+
+  photo_capabilities->exposure_compensation = mojom::Range::New();
+  photo_capabilities->color_temperature = mojom::Range::New();
+  photo_capabilities->iso = mojom::Range::New();
+  photo_capabilities->brightness = mojom::Range::New();
+  photo_capabilities->contrast = mojom::Range::New();
+  photo_capabilities->saturation = mojom::Range::New();
+  photo_capabilities->sharpness = mojom::Range::New();
+  photo_capabilities->zoom = mojom::Range::New();
+  photo_capabilities->red_eye_reduction = mojom::RedEyeReduction::NEVER;
+
+  photo_capabilities->torch = false;
+  std::move(callback).Run(std::move(photo_capabilities));
+}
+
+void VideoCaptureDeviceMFWin::SetPhotoOptions(
+    mojom::PhotoSettingsPtr settings,
+    SetPhotoOptionsCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+
+  if (!is_started_)
+    return;
+
+  HRESULT hr = S_OK;
+  ComPtr<IMFCaptureSource> source;
+  hr = engine_->GetSource(source.GetAddressOf());
+
+  if (FAILED(hr)) {
+    LogError(FROM_HERE, hr);
+    return;
+  }
+
+  if (settings->has_height || settings->has_width) {
+    CapabilityList capabilities;
+    hr = FillCapabilities(kPreferredPhotoStream, source.Get(), &capabilities);
+
+    if (FAILED(hr)) {
+      LogError(FROM_HERE, hr);
+      return;
+    }
+
+    ComPtr<IMFMediaType> current_media_type;
+    hr = source->GetCurrentDeviceMediaType(kPreferredPhotoStream,
+                                           current_media_type.GetAddressOf());
+
+    if (FAILED(hr)) {
+      LogError(FROM_HERE, hr);
+      return;
+    }
+
+    gfx::Size requested_size = gfx::Size();
+    if (settings->has_height)
+      requested_size.set_height(settings->height);
+
+    if (settings->has_width)
+      requested_size.set_width(settings->width);
+
+    const CapabilityWin best_match = GetBestMatchedPhotoCapability(
+        current_media_type, requested_size, capabilities);
+
+    ComPtr<IMFMediaType> type;
+    hr = GetAvailableDeviceMediaType(source.Get(), kPreferredPhotoStream,
+                                     best_match.stream_index,
+                                     type.GetAddressOf());
+    if (FAILED(hr)) {
+      LogError(FROM_HERE, hr);
+      return;
+    }
+
+    hr = source->SetCurrentDeviceMediaType(kPreferredPhotoStream, type.Get());
+    if (FAILED(hr)) {
+      LogError(FROM_HERE, hr);
+      return;
+    }
+  }
+
+  std::move(callback).Run(true);
 }
 
 void VideoCaptureDeviceMFWin::OnIncomingCapturedData(
@@ -287,27 +736,27 @@
     base::TimeTicks reference_time,
     base::TimeDelta timestamp) {
   base::AutoLock lock(lock_);
-  if (data && client_.get()) {
-    client_->OnIncomingCapturedData(data, length, capture_format_, rotation,
-                                    reference_time, timestamp);
-  }
 
-  if (capture_) {
-    HRESULT hr =
-        reader_->ReadSample(kFirstVideoStream, 0, NULL, NULL, NULL, NULL);
-    if (FAILED(hr)) {
-      // If running the *VideoCap* unit tests on repeat, this can sometimes
-      // fail with HRESULT_FROM_WINHRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION).
-      // It's not clear to me why this is, but it is possible that it has
-      // something to do with this bug:
-      // http://support.microsoft.com/kb/979567
-      OnError(FROM_HERE, hr);
-    }
+  if (data && client_.get()) {
+    client_->OnIncomingCapturedData(data, length, capture_video_format_,
+                                    rotation, reference_time, timestamp);
   }
 }
 
-void VideoCaptureDeviceMFWin::OnError(const base::Location& from_here,
-                                      HRESULT hr) {
+void VideoCaptureDeviceMFWin::OnEvent(IMFMediaEvent* media_event) {
+  base::AutoLock lock(lock_);
+
+  GUID event_type;
+  HRESULT hr = media_event->GetExtendedType(&event_type);
+
+  if (SUCCEEDED(hr) && event_type == MF_CAPTURE_ENGINE_ERROR)
+    media_event->GetStatus(&hr);
+
+  if (FAILED(hr))
+    OnError(FROM_HERE, hr);
+}
+
+void VideoCaptureDeviceMFWin::OnError(const Location& from_here, HRESULT hr) {
   if (client_.get()) {
     client_->OnError(
         from_here,
@@ -316,4 +765,4 @@
   }
 }
 
-}  // namespace media
+}  // namespace media
\ No newline at end of file
diff --git a/media/capture/video/win/video_capture_device_mf_win.h b/media/capture/video/win/video_capture_device_mf_win.h
index 93534f9..8cc5f58 100644
--- a/media/capture/video/win/video_capture_device_mf_win.h
+++ b/media/capture/video/win/video_capture_device_mf_win.h
@@ -3,12 +3,13 @@
 // found in the LICENSE file.
 
 // Windows specific implementation of VideoCaptureDevice.
-// DirectShow is used for capturing. DirectShow provide its own threads
-// for capturing.
+// MediaFoundation is used for capturing. MediaFoundation provides its own
+// threads for capturing.
 
 #ifndef MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_MF_WIN_H_
 #define MEDIA_CAPTURE_VIDEO_WIN_VIDEO_CAPTURE_DEVICE_MF_WIN_H_
 
+#include <mfcaptureengine.h>
 #include <mfidl.h>
 #include <mfreadwrite.h>
 #include <stdint.h>
@@ -16,9 +17,9 @@
 
 #include <vector>
 
+#include "base/callback_forward.h"
 #include "base/macros.h"
 #include "base/sequence_checker.h"
-#include "base/synchronization/lock.h"
 #include "media/capture/capture_export.h"
 #include "media/capture/video/video_capture_device.h"
 
@@ -30,10 +31,12 @@
 
 namespace media {
 
-class MFReaderCallback;
+class MFVideoCallback;
 
-const DWORD kFirstVideoStream =
-    static_cast<DWORD>(MF_SOURCE_READER_FIRST_VIDEO_STREAM);
+const DWORD kPreferredVideoPreviewStream = static_cast<DWORD>(
+    MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_VIDEO_PREVIEW);
+const DWORD kPreferredPhotoStream =
+    static_cast<DWORD>(MF_CAPTURE_ENGINE_PREFERRED_SOURCE_STREAM_FOR_PHOTO);
 
 class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice {
  public:
@@ -51,6 +54,10 @@
       const VideoCaptureParams& params,
       std::unique_ptr<VideoCaptureDevice::Client> client) override;
   void StopAndDeAllocate() override;
+  void TakePhoto(TakePhotoCallback callback) override;
+  void GetPhotoState(GetPhotoStateCallback callback) override;
+  void SetPhotoOptions(mojom::PhotoSettingsPtr settings,
+                       SetPhotoOptionsCallback callback) override;
 
   // Captured new video data.
   void OnIncomingCapturedData(const uint8_t* data,
@@ -58,19 +65,33 @@
                               int rotation,
                               base::TimeTicks reference_time,
                               base::TimeDelta timestamp);
+  void OnEvent(IMFMediaEvent* media_event);
+
+  using CreateMFPhotoCallbackCB =
+      base::Callback<scoped_refptr<IMFCaptureEngineOnSampleCallback>(
+          VideoCaptureDevice::TakePhotoCallback callback,
+          VideoCaptureFormat format)>;
+
+  void set_create_mf_photo_callback_for_testing(CreateMFPhotoCallbackCB cb) {
+    create_mf_photo_callback_ = cb;
+  }
 
  private:
   void OnError(const base::Location& from_here, HRESULT hr);
 
   VideoCaptureDeviceDescriptor descriptor_;
-  Microsoft::WRL::ComPtr<IMFActivate> device_;
-  scoped_refptr<MFReaderCallback> callback_;
+  CreateMFPhotoCallbackCB create_mf_photo_callback_;
+  scoped_refptr<MFVideoCallback> video_callback_;
 
-  base::Lock lock_;  // Used to guard the below variables.
+  // Guards the below variables from concurrent access between methods running
+  // on |sequence_checker_| and calls to OnIncomingCapturedData() and OnEvent()
+  // made by MediaFoundation on threads outside of our control.
+  base::Lock lock_;
+
   std::unique_ptr<VideoCaptureDevice::Client> client_;
-  Microsoft::WRL::ComPtr<IMFSourceReader> reader_;
-  VideoCaptureFormat capture_format_;
-  bool capture_;
+  Microsoft::WRL::ComPtr<IMFCaptureEngine> engine_;
+  VideoCaptureFormat capture_video_format_;
+  bool is_started_;
 
   SEQUENCE_CHECKER(sequence_checker_);
 
diff --git a/media/cdm/cdm_proxy.cc b/media/cdm/cdm_proxy.cc
index 482adb8..033ff63 100644
--- a/media/cdm/cdm_proxy.cc
+++ b/media/cdm/cdm_proxy.cc
@@ -6,7 +6,7 @@
 
 namespace media {
 
-KeyInfo::KeyInfo() = default;
-KeyInfo::~KeyInfo() = default;
+CdmProxyKeyInfo::CdmProxyKeyInfo() = default;
+CdmProxyKeyInfo::~CdmProxyKeyInfo() = default;
 
 }  // namespace media
\ No newline at end of file
diff --git a/media/cdm/cdm_proxy.h b/media/cdm/cdm_proxy.h
index 7388032a..04db0354 100644
--- a/media/cdm/cdm_proxy.h
+++ b/media/cdm/cdm_proxy.h
@@ -16,9 +16,9 @@
 namespace media {
 
 // Key information structure containing data necessary to decrypt/decode media.
-struct MEDIA_EXPORT KeyInfo {
-  KeyInfo();
-  ~KeyInfo();
+struct MEDIA_EXPORT CdmProxyKeyInfo {
+  CdmProxyKeyInfo();
+  ~CdmProxyKeyInfo();
   // Crypto session for decryption.
   uint32_t crypto_session_id = 0;
   // ID of the key.
@@ -49,6 +49,7 @@
   enum class Status {
     kOk,
     kFail,
+    kMax = kFail,
   };
 
   enum class Protocol {
@@ -56,6 +57,7 @@
     kIntelConvergedSecurityAndManageabilityEngine,
     // There will be more values in the future e.g. kD3D11RsaHardware,
     // kD3D11RsaSoftware to use the D3D11 RSA method.
+    kMax = kIntelConvergedSecurityAndManageabilityEngine,
   };
 
   enum class Function {
@@ -63,6 +65,7 @@
     // ID3D11VideoContext::NegotiateCryptoSessionKeyExchange.
     kIntelNegotiateCryptoSessionKeyExchange,
     // There will be more values in the future e.g. for D3D11 RSA method.
+    kMax = kIntelNegotiateCryptoSessionKeyExchange,
   };
 
   CdmProxy() {}
@@ -109,7 +112,7 @@
       CreateMediaCryptoSessionCB create_media_crypto_session_cb) = 0;
 
   // Send multiple key information to the proxy.
-  virtual void SetKeyInfo(const std::vector<KeyInfo>& key_infos) = 0;
+  virtual void SetKeyInfo(const std::vector<CdmProxyKeyInfo>& key_infos) = 0;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(CdmProxy);
diff --git a/media/mojo/interfaces/BUILD.gn b/media/mojo/interfaces/BUILD.gn
index d7bacb7..caf3906 100644
--- a/media/mojo/interfaces/BUILD.gn
+++ b/media/mojo/interfaces/BUILD.gn
@@ -2,6 +2,7 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//media/media_options.gni")
 import("//mojo/public/tools/bindings/mojom.gni")
 
 mojom("interfaces") {
@@ -29,6 +30,10 @@
     "watch_time_recorder.mojom",
   ]
 
+  if (enable_library_cdms) {
+    sources += [ "cdm_proxy.mojom" ]
+  }
+
   if (is_android) {
     sources += [ "media_drm_storage.mojom" ]
   }
diff --git a/media/mojo/interfaces/cdm_proxy.mojom b/media/mojo/interfaces/cdm_proxy.mojom
new file mode 100644
index 0000000..be185f96
--- /dev/null
+++ b/media/mojo/interfaces/cdm_proxy.mojom
@@ -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.
+
+module media.mojom;
+
+// In general, the interpretation of the method and callback parameters are
+// protocol dependent.
+// CdmProxy implemenation is hosted in the GPU process.
+interface CdmProxy {
+  // See media/cdm/cdm_proxy.h.
+  [Native]
+  enum Status;
+
+  // See media/cdm/cdm_proxy.h.
+  [Native]
+  enum Protocol;
+
+  // See media/cdm/cdm_proxy.h.
+  [Native]
+  enum Function;
+
+  // TODO(xhwang): Add methods for this interface so that they match
+  // the methods in media/cdm/cdm_proxy.h.
+};
+
+// Client of CdmProxy.
+// CdmProxyClient is running in CDM utility process.
+interface CdmProxyClient {
+  // Notifies the client that there has been a hardware reset.
+  NotifyHardwareReset();
+};
\ No newline at end of file
diff --git a/media/mojo/interfaces/cdm_proxy.typemap b/media/mojo/interfaces/cdm_proxy.typemap
new file mode 100644
index 0000000..f887340
--- /dev/null
+++ b/media/mojo/interfaces/cdm_proxy.typemap
@@ -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.
+
+mojom = "//media/mojo/interfaces/cdm_proxy.mojom"
+
+public_headers = [ "//media/cdm/cdm_proxy.h" ]
+
+public_deps = [
+  "//media",
+]
+
+deps = [
+  "//media/base/ipc",
+]
+
+type_mappings = [
+  "media.mojom.CdmProxy.Status=media::CdmProxy::Status",
+  "media.mojom.CdmProxy.Protocol=media::CdmProxy::Protocol",
+  "media.mojom.CdmProxy.Function=media::CdmProxy::Function",
+]
diff --git a/media/mojo/interfaces/typemaps.gni b/media/mojo/interfaces/typemaps.gni
index 2b9a3e8..61e7af6 100644
--- a/media/mojo/interfaces/typemaps.gni
+++ b/media/mojo/interfaces/typemaps.gni
@@ -5,6 +5,7 @@
 typemaps = [
   "//media/mojo/interfaces/audio_decoder_config.typemap",
   "//media/mojo/interfaces/audio_parameters.typemap",
+  "//media/mojo/interfaces/cdm_proxy.typemap",
   "//media/mojo/interfaces/content_decryption_module.typemap",
   "//media/mojo/interfaces/decryptor.typemap",
   "//media/mojo/interfaces/demuxer_stream.typemap",
diff --git a/net/BUILD.gn b/net/BUILD.gn
index 8ff3e0b..17641e4 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -1459,6 +1459,7 @@
       "quic/platform/api/quic_mutex.h",
       "quic/platform/api/quic_optional.h",
       "quic/platform/api/quic_pcc_sender.h",
+      "quic/platform/api/quic_prefetch.h",
       "quic/platform/api/quic_ptr_util.h",
       "quic/platform/api/quic_reconstruct_object.h",
       "quic/platform/api/quic_reference_counted.h",
@@ -1499,6 +1500,7 @@
       "quic/platform/impl/quic_mutex_impl.h",
       "quic/platform/impl/quic_optional_impl.h",
       "quic/platform/impl/quic_pcc_sender_impl.h",
+      "quic/platform/impl/quic_prefetch_impl.h",
       "quic/platform/impl/quic_ptr_util_impl.h",
       "quic/platform/impl/quic_reconstruct_object_impl.h",
       "quic/platform/impl/quic_reference_counted_impl.h",
diff --git a/net/quic/core/quic_utils.cc b/net/quic/core/quic_utils.cc
index cce8c16c..1fb7800 100644
--- a/net/quic/core/quic_utils.cc
+++ b/net/quic/core/quic_utils.cc
@@ -11,8 +11,10 @@
 #include "base/containers/adapters.h"
 #include "base/logging.h"
 #include "net/quic/core/quic_constants.h"
+#include "net/quic/platform/api/quic_aligned.h"
 #include "net/quic/platform/api/quic_bug_tracker.h"
 #include "net/quic/platform/api/quic_flags.h"
+#include "net/quic/platform/api/quic_prefetch.h"
 
 using std::string;
 
@@ -237,16 +239,12 @@
   // generally, the iov_offset is not 0, input iov consists of 2K buffers and
   // the output buffer is ~1.4K.
   if (copy_len == iov_available && iovnum + 1 < iov_count) {
-    // TODO(ckrasic) - this is unused without prefetch()
-    // char* next_base = static_cast<char*>(iov.iov[iovnum + 1].iov_base);
-    // char* next_base = static_cast<char*>(iov.iov[iovnum + 1].iov_base);
     // Prefetch 2 cachelines worth of data to get the prefetcher started; leave
     // it to the hardware prefetcher after that.
-    // TODO(ckrasic) - investigate what to do about prefetch directives.
-    // ::base::PrefetchT0(next_base);
+    char* next_base = static_cast<char*>(iov[iovnum + 1].iov_base);
+    QuicPrefetchT0(next_base);
     if (iov[iovnum + 1].iov_len >= 64) {
-      // TODO(ckrasic) - investigate what to do about prefetch directives.
-      // ::base::PrefetchT0(next_base + ABSL_CACHELINE_SIZE);
+      QuicPrefetchT0(next_base + QUIC_CACHELINE_SIZE);
     }
   }
 
diff --git a/net/quic/platform/api/quic_aligned.h b/net/quic/platform/api/quic_aligned.h
index 7e5d935..202c219 100644
--- a/net/quic/platform/api/quic_aligned.h
+++ b/net/quic/platform/api/quic_aligned.h
@@ -10,5 +10,6 @@
 #define QUIC_ALIGN_OF QUIC_ALIGN_OF_IMPL
 #define QUIC_ALIGNED(X) QUIC_ALIGNED_IMPL(X)
 #define QUIC_CACHELINE_ALIGNED QUIC_CACHELINE_ALIGNED_IMPL
+#define QUIC_CACHELINE_SIZE QUIC_CACHELINE_SIZE_IMPL
 
 #endif  // NET_QUIC_PLATFORM_API_QUIC_ALIGNED_H_
diff --git a/net/quic/platform/api/quic_prefetch.h b/net/quic/platform/api/quic_prefetch.h
new file mode 100644
index 0000000..edc73a6
--- /dev/null
+++ b/net/quic/platform/api/quic_prefetch.h
@@ -0,0 +1,39 @@
+// 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_QUIC_PLATFORM_API_QUIC_PREFETCH_H_
+#define NET_QUIC_PLATFORM_API_QUIC_PREFETCH_H_
+
+#include "net/quic/platform/impl/quic_prefetch_impl.h"
+
+namespace net {
+
+// Move data into the cache before it is read, or "prefetch" it.
+//
+// The value of `addr` is the address of the memory to prefetch. If
+// the target and compiler support it, data prefetch instructions are
+// generated. If the prefetch is done some time before the memory is
+// read, it may be in the cache by the time the read occurs.
+//
+// The function names specify the temporal locality heuristic applied,
+// using the names of Intel prefetch instructions:
+//
+//   T0 - high degree of temporal locality; data should be left in as
+//        many levels of the cache possible
+//   T1 - moderate degree of temporal locality
+//   T2 - low degree of temporal locality
+//   Nta - no temporal locality, data need not be left in the cache
+//         after the read
+//
+// Incorrect or gratuitous use of these functions can degrade
+// performance, so use them only when representative benchmarks show
+// an improvement.
+
+inline void QuicPrefetchT0(const void* addr) {
+  return QuicPrefetchT0Impl(addr);
+}
+
+}  // namespace net
+
+#endif  // NET_QUIC_PLATFORM_API_QUIC_PREFETCH_H_
diff --git a/net/quic/platform/impl/quic_aligned_impl.h b/net/quic/platform/impl/quic_aligned_impl.h
index 3ca83db..f38835e 100644
--- a/net/quic/platform/impl/quic_aligned_impl.h
+++ b/net/quic/platform/impl/quic_aligned_impl.h
@@ -17,6 +17,7 @@
 
 // TODO(rtenneti): Change the default 64 alignas value (used the default
 // value from ABSL_CACHELINE_SIZE).
-#define QUIC_CACHELINE_ALIGNED_IMPL ALIGNAS(64)
+#define QUIC_CACHELINE_SIZE_IMPL (64)
+#define QUIC_CACHELINE_ALIGNED_IMPL ALIGNAS(QUIC_CACHELINE_SIZE)
 
 #endif  // NET_QUIC_PLATFORM_IMPL_QUIC_ALIGNED_IMPL_H_
diff --git a/net/quic/platform/impl/quic_prefetch_impl.h b/net/quic/platform/impl/quic_prefetch_impl.h
new file mode 100644
index 0000000..2f5d1eb
--- /dev/null
+++ b/net/quic/platform/impl/quic_prefetch_impl.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 NET_QUIC_PLATFORM_IMPL_QUIC_PREFETCH_IMPL_H_
+#define NET_QUIC_PLATFORM_IMPL_QUIC_PREFETCH_IMPL_H_
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+namespace net {
+
+inline void QuicPrefetchT0Impl(const void* addr) {
+#if defined(__GNUC__)
+  __builtin_prefetch(addr, 0, 3);
+#elif defined(_MSC_VER)
+  _mm_prefetch(reinterpret_cast<const char*>(addr), _MM_HINT_T0);
+#endif
+}
+
+}  // namespace net
+
+#endif  // NET_QUIC_PLATFORM_IMPL_QUIC_PREFETCH_IMPL_H_
diff --git a/net/spdy/chromium/spdy_network_transaction_unittest.cc b/net/spdy/chromium/spdy_network_transaction_unittest.cc
index 14541b6..71c8358 100644
--- a/net/spdy/chromium/spdy_network_transaction_unittest.cc
+++ b/net/spdy/chromium/spdy_network_transaction_unittest.cc
@@ -392,8 +392,8 @@
         session->spdy_session_pool()->FindAvailableSession(
             key, /* enable_ip_based_pooling = */ true, log_);
     ASSERT_TRUE(spdy_session);
-    EXPECT_EQ(0u, spdy_session->num_active_streams());
-    EXPECT_EQ(0u, spdy_session->num_unclaimed_pushed_streams());
+    EXPECT_EQ(0u, num_active_streams(spdy_session));
+    EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
   }
 
   void RunServerPushTest(SequencedSocketData* data,
@@ -518,6 +518,20 @@
     return SpdyString(kDefaultUrl) + path;
   }
 
+  size_t num_active_streams(base::WeakPtr<SpdySession> session) {
+    return session->active_streams_.size();
+  }
+
+  size_t num_unclaimed_pushed_streams(base::WeakPtr<SpdySession> session) {
+    return session->unclaimed_pushed_streams_.CountStreamsForSession();
+  }
+
+  bool has_unclaimed_pushed_stream_for_url(base::WeakPtr<SpdySession> session,
+                                           const GURL& url) {
+    return session->unclaimed_pushed_streams_.FindStream(url) !=
+           kNoPushedStreamFound;
+  }
+
   const GURL default_url_;
   const HostPortPair host_port_pair_;
   HttpRequestInfo request_;
@@ -4973,10 +4987,9 @@
       spdy_session_pool->FindAvailableSession(
           key, /* enable_ip_based_pooling = */ true, log_);
 
-  EXPECT_EQ(1u,
-            spdy_session->unclaimed_pushed_streams_.CountStreamsForSession());
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session));
   EXPECT_TRUE(
-      spdy_session->has_unclaimed_pushed_stream_for_url(GURL(url_to_push)));
+      has_unclaimed_pushed_stream_for_url(spdy_session, GURL(url_to_push)));
 
   HttpNetworkTransaction trans1(DEFAULT_PRIORITY, helper.session());
   HttpRequestInfo push_request;
@@ -4987,8 +5000,7 @@
   rv = callback1.GetResult(rv);
   EXPECT_THAT(rv, IsOk());
 
-  EXPECT_EQ(0u,
-            spdy_session->unclaimed_pushed_streams_.CountStreamsForSession());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session));
 
   HttpResponseInfo response = *trans0->GetResponseInfo();
   EXPECT_TRUE(response.headers);
@@ -5115,8 +5127,7 @@
       spdy_session_pool->FindAvailableSession(
           key0, /* enable_ip_based_pooling = */ true, log_);
 
-  EXPECT_EQ(0u,
-            spdy_session0->unclaimed_pushed_streams_.CountStreamsForSession());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session0));
 
   HostPortPair host_port_pair1("docs.example.org", 443);
   SpdySessionKey key1(host_port_pair1, ProxyServer::Direct(),
@@ -5125,10 +5136,9 @@
       spdy_session_pool->FindAvailableSession(
           key1, /* enable_ip_based_pooling = */ true, log_);
 
-  EXPECT_EQ(1u,
-            spdy_session1->unclaimed_pushed_streams_.CountStreamsForSession());
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams(spdy_session1));
   EXPECT_TRUE(
-      spdy_session1->has_unclaimed_pushed_stream_for_url(GURL(url_to_push)));
+      has_unclaimed_pushed_stream_for_url(spdy_session1, GURL(url_to_push)));
 
   // Request |url_to_push|, which should be served from the pushed resource.
   HttpNetworkTransaction trans2(DEFAULT_PRIORITY, helper.session());
@@ -5140,10 +5150,8 @@
   rv = callback2.GetResult(rv);
   EXPECT_THAT(rv, IsOk());
 
-  EXPECT_EQ(0u,
-            spdy_session0->unclaimed_pushed_streams_.CountStreamsForSession());
-  EXPECT_EQ(0u,
-            spdy_session1->unclaimed_pushed_streams_.CountStreamsForSession());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session0));
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams(spdy_session1));
 
   HttpResponseInfo response0 = *trans0->GetResponseInfo();
   EXPECT_TRUE(response0.headers);
diff --git a/net/spdy/chromium/spdy_session.cc b/net/spdy/chromium/spdy_session.cc
index 8eb9757..c541069 100644
--- a/net/spdy/chromium/spdy_session.cc
+++ b/net/spdy/chromium/spdy_session.cc
@@ -1287,14 +1287,6 @@
                                         load_timing_info);
 }
 
-size_t SpdySession::num_unclaimed_pushed_streams() const {
-  return unclaimed_pushed_streams_.CountStreamsForSession();
-}
-
-bool SpdySession::has_unclaimed_pushed_stream_for_url(const GURL& url) const {
-  return unclaimed_pushed_streams_.FindStream(url) != kNoPushedStreamFound;
-}
-
 int SpdySession::GetPeerAddress(IPEndPoint* address) const {
   if (connection_->socket())
     return connection_->socket()->GetPeerAddress(address);
diff --git a/net/spdy/chromium/spdy_session.h b/net/spdy/chromium/spdy_session.h
index 5f39b6d..51c7236 100644
--- a/net/spdy/chromium/spdy_session.h
+++ b/net/spdy/chromium/spdy_session.h
@@ -452,29 +452,6 @@
     return !active_streams_.empty() || !created_streams_.empty();
   }
 
-  // Access to the number of active and pending streams.  These are primarily
-  // available for testing and diagnostics.
-  size_t num_active_streams() const { return active_streams_.size(); }
-  size_t num_unclaimed_pushed_streams() const;
-  size_t num_created_streams() const { return created_streams_.size(); }
-  bool has_unclaimed_pushed_stream_for_url(const GURL& url) const;
-
-  size_t num_pushed_streams() const { return num_pushed_streams_; }
-  size_t num_active_pushed_streams() const {
-    return num_active_pushed_streams_;
-  }
-
-  size_t pending_create_stream_queue_size(RequestPriority priority) const {
-    DCHECK_GE(priority, MINIMUM_PRIORITY);
-    DCHECK_LE(priority, MAXIMUM_PRIORITY);
-    return pending_create_stream_queues_[priority].size();
-  }
-
-  // Returns the current |stream_initial_send_window_size_|.
-  int32_t stream_initial_send_window_size() const {
-    return stream_initial_send_window_size_;
-  }
-
   // Returns true if no stream in the session can send data due to
   // session flow control.
   bool IsSendStalled() const { return session_send_window_size_ == 0; }
@@ -527,44 +504,10 @@
   friend class SpdyHttpStreamTest;
   friend class SpdyNetworkTransactionTest;
   friend class SpdyProxyClientSocketTest;
+  friend class SpdySessionPoolTest;
   friend class SpdySessionTest;
   friend class SpdyStreamRequest;
 
-  // Allow tests to access our innards for testing purposes.
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, ClientPing);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, FailedPing);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, WaitingForWrongPing);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, CancelPushBeforeClaimed);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, CancelPushAfterSessionGoesAway);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, CancelPushAfterExpired);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, ClaimPushedStreamBeforeExpires);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, ProtocolNegotiation);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, AdjustRecvWindowSize);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, AdjustSendWindowSize);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlInactiveStream);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlPadding);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest,
-                           SessionFlowControlTooMuchDataTwoDataFrames);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest,
-                           StreamFlowControlTooMuchDataTwoDataFrames);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoReceiveLeaks);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlNoSendLeaks);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, SessionFlowControlEndToEnd);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, StreamIdSpaceExhausted);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, MaxConcurrentStreamsZero);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, UnstallRacesWithStreamCreation);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, GoAwayOnSessionFlowControlError);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest,
-                           RejectPushedStreamExceedingConcurrencyLimit);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, IgnoreReservedRemoteStreamsCount);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest,
-                           CancelReservedStreamOnHeadersReceived);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionTest, RejectInvalidUnknownFrames);
-  FRIEND_TEST_ALL_PREFIXES(SpdySessionPoolTest, IPAddressChanged);
-  FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionTest,
-                           ServerPushValidCrossOrigin);
-  FRIEND_TEST_ALL_PREFIXES(SpdyNetworkTransactionTest,
-                           ServerPushValidCrossOriginWithOpenSession);
   FRIEND_TEST_ALL_PREFIXES(RecordPushedStreamHistogramTest, VaryResponseHeader);
 
   using PendingStreamRequestQueue =
@@ -601,7 +544,7 @@
   // Container class for unclaimed pushed streams on a SpdySession.  Guarantees
   // that |spdy_session_.pool_| gets notified every time a stream is pushed or
   // an unclaimed pushed stream is claimed.
-  class UnclaimedPushedStreamContainer {
+  class NET_EXPORT_PRIVATE UnclaimedPushedStreamContainer {
    public:
     UnclaimedPushedStreamContainer() = delete;
     explicit UnclaimedPushedStreamContainer(SpdySession* spdy_session);
@@ -952,30 +895,6 @@
   // empty.
   SpdyStreamId PopStreamToPossiblyResume();
 
-  // --------------------------
-  // Helper methods for testing
-  // --------------------------
-
-  void set_connection_at_risk_of_loss_time(base::TimeDelta duration) {
-    connection_at_risk_of_loss_time_ = duration;
-  }
-
-  void set_hung_interval(base::TimeDelta duration) {
-    hung_interval_ = duration;
-  }
-
-  void set_max_concurrent_pushed_streams(size_t value) {
-    max_concurrent_pushed_streams_ = value;
-  }
-
-  int64_t pings_in_flight() const { return pings_in_flight_; }
-
-  SpdyPingId next_ping_id() const { return next_ping_id_; }
-
-  base::TimeTicks last_read_time() const { return last_read_time_; }
-
-  bool check_ping_status_pending() const { return check_ping_status_pending_; }
-
   // Whether Do{Read,Write}Loop() is in the call stack. Useful for
   // making sure we don't destroy ourselves prematurely in that case.
   bool in_io_loop_;
diff --git a/net/spdy/chromium/spdy_session_pool_unittest.cc b/net/spdy/chromium/spdy_session_pool_unittest.cc
index 83614a4..9bbf636 100644
--- a/net/spdy/chromium/spdy_session_pool_unittest.cc
+++ b/net/spdy/chromium/spdy_session_pool_unittest.cc
@@ -66,6 +66,10 @@
 
   void RunIPPoolingTest(SpdyPoolCloseSessionsType close_sessions_type);
 
+  size_t num_active_streams(base::WeakPtr<SpdySession> session) {
+    return session->active_streams_.size();
+  }
+
   SpdySessionDependencies session_deps_;
   std::unique_ptr<HttpNetworkSession> http_session_;
   SpdySessionPool* spdy_session_pool_;
@@ -778,7 +782,7 @@
   EXPECT_TRUE(sessionC->IsDraining());
 
   EXPECT_EQ(1u,
-            sessionA->num_active_streams());  // Active stream is still active.
+            num_active_streams(sessionA));  // Active stream is still active.
   EXPECT_FALSE(delegateA.StreamIsClosed());
 
   EXPECT_TRUE(delegateB.StreamIsClosed());  // Created stream was closed.
diff --git a/net/spdy/chromium/spdy_session_unittest.cc b/net/spdy/chromium/spdy_session_unittest.cc
index 5e8020b..1738b134 100644
--- a/net/spdy/chromium/spdy_session_unittest.cc
+++ b/net/spdy/chromium/spdy_session_unittest.cc
@@ -175,14 +175,14 @@
 
   void StallSessionSend() {
     // Reduce the send window size to 0 to stall.
-    while (session_->session_send_window_size_ > 0) {
-      session_->DecreaseSendWindowSize(std::min(
-          kMaxSpdyFrameChunkSize, session_->session_send_window_size_));
+    while (session_send_window_size() > 0) {
+      DecreaseSendWindowSize(
+          std::min(kMaxSpdyFrameChunkSize, session_send_window_size()));
     }
   }
 
   void UnstallSessionSend(int32_t delta_window_size) {
-    session_->IncreaseSendWindowSize(delta_window_size);
+    IncreaseSendWindowSize(delta_window_size);
   }
 
   void StallStreamSend(SpdyStream* stream) {
@@ -201,6 +201,134 @@
       const base::Callback<void(SpdyStream*)>& stall_function,
       const base::Callback<void(SpdyStream*, int32_t)>& unstall_function);
 
+  // SpdySession private methods.
+
+  void MaybeSendPrefacePing() { session_->MaybeSendPrefacePing(); }
+
+  void WritePingFrame(SpdyPingId unique_id, bool is_ack) {
+    session_->WritePingFrame(unique_id, is_ack);
+  }
+
+  void CheckPingStatus(base::TimeTicks last_check_time) {
+    session_->CheckPingStatus(last_check_time);
+  }
+
+  bool OnUnknownFrame(SpdyStreamId stream_id, uint8_t frame_type) {
+    return session_->OnUnknownFrame(stream_id, frame_type);
+  }
+
+  void IncreaseSendWindowSize(int delta_window_size) {
+    session_->IncreaseSendWindowSize(delta_window_size);
+  }
+
+  void DecreaseSendWindowSize(int32_t delta_window_size) {
+    session_->DecreaseSendWindowSize(delta_window_size);
+  }
+
+  void IncreaseRecvWindowSize(int delta_window_size) {
+    session_->IncreaseRecvWindowSize(delta_window_size);
+  }
+
+  void DecreaseRecvWindowSize(int32_t delta_window_size) {
+    session_->DecreaseRecvWindowSize(delta_window_size);
+  }
+
+  // Accessors for SpdySession private members.
+
+  void set_in_io_loop(bool in_io_loop) { session_->in_io_loop_ = in_io_loop; }
+
+  void set_stream_hi_water_mark(SpdyStreamId stream_hi_water_mark) {
+    session_->stream_hi_water_mark_ = stream_hi_water_mark;
+  }
+
+  void set_last_accepted_push_stream_id(
+      SpdyStreamId last_accepted_push_stream_id) {
+    session_->last_accepted_push_stream_id_ = last_accepted_push_stream_id;
+  }
+
+  size_t num_pushed_streams() { return session_->num_pushed_streams_; }
+
+  size_t num_active_pushed_streams() {
+    return session_->num_active_pushed_streams_;
+  }
+
+  size_t max_concurrent_streams() { return session_->max_concurrent_streams_; }
+
+  void set_max_concurrent_streams(size_t max_concurrent_streams) {
+    session_->max_concurrent_streams_ = max_concurrent_streams;
+  }
+
+  void set_max_concurrent_pushed_streams(size_t max_concurrent_pushed_streams) {
+    session_->max_concurrent_pushed_streams_ = max_concurrent_pushed_streams;
+  }
+
+  int64_t pings_in_flight() { return session_->pings_in_flight_; }
+
+  SpdyPingId next_ping_id() { return session_->next_ping_id_; }
+
+  base::TimeTicks last_read_time() { return session_->last_read_time_; }
+
+  void set_last_read_time(base::TimeTicks last_read_time) {
+    session_->last_read_time_ = last_read_time;
+  }
+
+  bool check_ping_status_pending() {
+    return session_->check_ping_status_pending_;
+  }
+
+  void set_check_ping_status_pending(bool check_ping_status_pending) {
+    session_->check_ping_status_pending_ = check_ping_status_pending;
+  }
+
+  int32_t session_send_window_size() {
+    return session_->session_send_window_size_;
+  }
+
+  int32_t session_recv_window_size() {
+    return session_->session_recv_window_size_;
+  }
+
+  void set_session_recv_window_size(int32_t session_recv_window_size) {
+    session_->session_recv_window_size_ = session_recv_window_size;
+  }
+
+  int32_t session_unacked_recv_window_bytes() {
+    return session_->session_unacked_recv_window_bytes_;
+  }
+
+  int32_t stream_initial_send_window_size() {
+    return session_->stream_initial_send_window_size_;
+  }
+
+  void set_connection_at_risk_of_loss_time(base::TimeDelta duration) {
+    session_->connection_at_risk_of_loss_time_ = duration;
+  }
+
+  void set_hung_interval(base::TimeDelta duration) {
+    session_->hung_interval_ = duration;
+  }
+
+  // Quantities derived from SpdySession private members.
+
+  size_t pending_create_stream_queue_size(RequestPriority priority) {
+    DCHECK_GE(priority, MINIMUM_PRIORITY);
+    DCHECK_LE(priority, MAXIMUM_PRIORITY);
+    return session_->pending_create_stream_queues_[priority].size();
+  }
+
+  size_t num_active_streams() { return session_->active_streams_.size(); }
+
+  size_t num_created_streams() { return session_->created_streams_.size(); }
+
+  size_t num_unclaimed_pushed_streams() {
+    return session_->unclaimed_pushed_streams_.CountStreamsForSession();
+  }
+
+  bool has_unclaimed_pushed_stream_for_url(const GURL& url) {
+    return session_->unclaimed_pushed_streams_.FindStream(url) !=
+           kNoPushedStreamFound;
+  }
+
   // Original socket limits.  Some tests set these.  Safest to always restore
   // them once each test has been run.
   int old_max_group_sockets_;
@@ -892,24 +1020,23 @@
 
   base::TimeTicks before_ping_time = base::TimeTicks::Now();
 
-  session_->set_connection_at_risk_of_loss_time(
-      base::TimeDelta::FromSeconds(-1));
-  session_->set_hung_interval(base::TimeDelta::FromMilliseconds(50));
+  set_connection_at_risk_of_loss_time(base::TimeDelta::FromSeconds(-1));
+  set_hung_interval(base::TimeDelta::FromMilliseconds(50));
 
-  session_->MaybeSendPrefacePing();
+  MaybeSendPrefacePing();
 
-  EXPECT_EQ(1, session_->pings_in_flight());
-  EXPECT_EQ(2u, session_->next_ping_id());
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  EXPECT_EQ(1, pings_in_flight());
+  EXPECT_EQ(2u, next_ping_id());
+  EXPECT_TRUE(check_ping_status_pending());
 
   base::RunLoop().RunUntilIdle();
 
-  session_->CheckPingStatus(before_ping_time);
+  CheckPingStatus(before_ping_time);
 
-  EXPECT_EQ(0, session_->pings_in_flight());
-  EXPECT_EQ(2u, session_->next_ping_id());
-  EXPECT_FALSE(session_->check_ping_status_pending());
-  EXPECT_GE(session_->last_read_time(), before_ping_time);
+  EXPECT_EQ(0, pings_in_flight());
+  EXPECT_EQ(2u, next_ping_id());
+  EXPECT_FALSE(check_ping_status_pending());
+  EXPECT_GE(last_read_time(), before_ping_time);
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -1050,9 +1177,9 @@
   CreateSpdySession();
 
   // Fix stream_hi_water_mark_ to allow for two stream activations.
-  session_->stream_hi_water_mark_ = kLastStreamId - 2;
+  set_stream_hi_water_mark(kLastStreamId - 2);
   // Fix max_concurrent_streams to allow for three stream creations.
-  session_->max_concurrent_streams_ = 3;
+  set_max_concurrent_streams(3);
 
   // Create three streams synchronously, and begin a fourth (which is stalled).
   base::WeakPtr<SpdyStream> stream1 =
@@ -1081,9 +1208,9 @@
                             MEDIUM, NetLogWithSource(), callback4.callback()));
 
   // Streams 1-3 were created. 4th is stalled. No streams are active yet.
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(3u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(3u, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
 
   // Activate stream 1. One ID remains available.
   stream1->SendRequestHeaders(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl),
@@ -1091,9 +1218,9 @@
   base::RunLoop().RunUntilIdle();
 
   EXPECT_EQ(kLastStreamId - 2u, stream1->stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(2u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(2u, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
 
   // Activate stream 2. ID space is exhausted.
   stream2->SendRequestHeaders(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl),
@@ -1102,14 +1229,14 @@
 
   // Active streams remain active.
   EXPECT_EQ(kLastStreamId, stream2->stream_id());
-  EXPECT_EQ(2u, session_->num_active_streams());
+  EXPECT_EQ(2u, num_active_streams());
 
   // Session is going away. Created and stalled streams were aborted.
-  EXPECT_EQ(SpdySession::STATE_GOING_AWAY, session_->availability_state_);
+  EXPECT_TRUE(session_->IsGoingAway());
   EXPECT_THAT(delegate3.WaitForClose(), IsError(ERR_ABORTED));
   EXPECT_THAT(callback4.WaitForResult(), IsError(ERR_ABORTED));
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(MEDIUM));
 
   // Read responses on remaining active streams.
   data.Resume();
@@ -1175,7 +1302,7 @@
 
   // Receive SETTINGS frame that sets max_concurrent_streams to zero.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(0u, session_->max_concurrent_streams_);
+  EXPECT_EQ(0u, max_concurrent_streams());
 
   // Start request.
   SpdyStreamRequest request;
@@ -1186,17 +1313,17 @@
   EXPECT_THAT(rv, IsError(ERR_IO_PENDING));
 
   // Stream is stalled.
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(MEDIUM));
-  EXPECT_EQ(0u, session_->num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(0u, num_created_streams());
 
   // Receive SETTINGS frame that sets max_concurrent_streams to one.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1u, session_->max_concurrent_streams_);
+  EXPECT_EQ(1u, max_concurrent_streams());
 
   // Stream is created.
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(MEDIUM));
-  EXPECT_EQ(1u, session_->num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_created_streams());
 
   EXPECT_THAT(callback.WaitForResult(), IsOk());
 
@@ -1238,7 +1365,7 @@
   CreateSpdySession();
 
   // Fix max_concurrent_streams to allow for one open stream.
-  session_->max_concurrent_streams_ = 1;
+  set_max_concurrent_streams(1);
 
   // Create two streams: one synchronously, and one which stalls.
   base::WeakPtr<SpdyStream> stream1 =
@@ -1252,36 +1379,36 @@
       request2.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_,
                             MEDIUM, NetLogWithSource(), callback2.callback()));
 
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
 
   // Cancel the first stream. A callback to unstall the second stream was
   // posted. Don't run it yet.
   stream1->Cancel();
 
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(MEDIUM));
 
   // Create a third stream prior to the second stream's callback.
   base::WeakPtr<SpdyStream> stream3 =
       CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session_,
                                 test_url_, MEDIUM, NetLogWithSource());
 
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(MEDIUM));
 
   // Now run the message loop. The unstalled stream will re-stall itself.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(MEDIUM));
 
   // Cancel the third stream and run the message loop. Verify that the second
   // stream creation now completes.
   stream3->Cancel();
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(MEDIUM));
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(MEDIUM));
   EXPECT_THAT(callback2.WaitForResult(), IsOk());
 }
 
@@ -1321,14 +1448,14 @@
   base::RunLoop().RunUntilIdle();
 
   // Verify that there is one unclaimed push stream.
-  EXPECT_EQ(1u, session_->num_unclaimed_pushed_streams());
-  EXPECT_TRUE(session_->has_unclaimed_pushed_stream_for_url(
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams());
+  EXPECT_TRUE(has_unclaimed_pushed_stream_for_url(
       GURL("https://www.example.org/a.dat")));
 
   // Unclaimed push body consumes bytes from the session window.
   EXPECT_EQ(kDefaultInitialWindowSize - kUploadDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Read and process EOF.
   data.Resume();
@@ -1422,13 +1549,13 @@
 
   // Verify that there is one unclaimed push stream.
   const GURL pushed_url("https://www.example.org/a.dat");
-  EXPECT_EQ(1u, session_->num_unclaimed_pushed_streams());
-  EXPECT_TRUE(session_->has_unclaimed_pushed_stream_for_url(pushed_url));
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams());
+  EXPECT_TRUE(has_unclaimed_pushed_stream_for_url(pushed_url));
 
   // Unclaimed push body consumes bytes from the session window.
   EXPECT_EQ(kDefaultInitialWindowSize - kUploadDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Fast forward to CancelPushedStreamIfUnclaimed() that was posted with a
   // delay.
@@ -1436,16 +1563,16 @@
   task_runner->RunUntilIdle();
 
   // Verify that pushed stream is cancelled.
-  EXPECT_EQ(0u, session_->num_unclaimed_pushed_streams());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams());
 
   // Verify that the session window reclaimed the evicted stream body.
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(kUploadDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(kUploadDataSize, session_unacked_recv_window_bytes());
 
   // Try to cancel the expired push after its expiration: must not crash.
   EXPECT_TRUE(session_);
   EXPECT_TRUE(test_push_delegate_->CancelPush(pushed_url));
-  EXPECT_EQ(0u, session_->num_unclaimed_pushed_streams());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams());
 
   // Read and process EOF.
   data.Resume();
@@ -1530,13 +1657,13 @@
 
   // Verify that there is one unclaimed push stream.
   const GURL pushed_url("https://www.example.org/a.dat");
-  EXPECT_EQ(1u, session_->num_unclaimed_pushed_streams());
-  EXPECT_TRUE(session_->has_unclaimed_pushed_stream_for_url(pushed_url));
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams());
+  EXPECT_TRUE(has_unclaimed_pushed_stream_for_url(pushed_url));
 
   // Unclaimed push body consumes bytes from the session window.
   EXPECT_EQ(kDefaultInitialWindowSize - kUploadDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   SpdyStream* spdy_stream2;
   rv = session_->GetPushStream(pushed_url, MEDIUM, &spdy_stream2,
@@ -1548,7 +1675,7 @@
   spdy_stream2->SetDelegate(&delegate2);
 
   // Verify that pushed stream is claimed.
-  EXPECT_EQ(0u, session_->num_unclaimed_pushed_streams());
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams());
 
   // Fast forward to CancelPushedStreamIfUnclaimed() that was posted with a
   // delay.  CancelPushedStreamIfUnclaimed() must be a no-op.
@@ -1607,22 +1734,22 @@
 
   // Verify that there is one unclaimed push stream.
   const GURL pushed_url("https://www.example.org/a.dat");
-  EXPECT_EQ(1u, session_->num_unclaimed_pushed_streams());
-  EXPECT_TRUE(session_->has_unclaimed_pushed_stream_for_url(pushed_url));
+  EXPECT_EQ(1u, num_unclaimed_pushed_streams());
+  EXPECT_TRUE(has_unclaimed_pushed_stream_for_url(pushed_url));
 
   // Unclaimed push body consumes bytes from the session window.
   EXPECT_EQ(kDefaultInitialWindowSize - kUploadDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Cancel the push before it is claimed.
   EXPECT_TRUE(test_push_delegate_->CancelPush(pushed_url));
-  EXPECT_EQ(0u, session_->num_unclaimed_pushed_streams());
-  EXPECT_FALSE(session_->has_unclaimed_pushed_stream_for_url(pushed_url));
+  EXPECT_EQ(0u, num_unclaimed_pushed_streams());
+  EXPECT_FALSE(has_unclaimed_pushed_stream_for_url(pushed_url));
 
   // Verify that the session window reclaimed the evicted stream body.
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(kUploadDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(kUploadDataSize, session_unacked_recv_window_bytes());
 
   EXPECT_TRUE(session_);
 
@@ -1664,30 +1791,28 @@
   test::StreamDelegateSendImmediate delegate(spdy_stream1, nullptr);
   spdy_stream1->SetDelegate(&delegate);
 
-  session_->set_connection_at_risk_of_loss_time(
-      base::TimeDelta::FromSeconds(0));
-  session_->set_hung_interval(base::TimeDelta::FromSeconds(0));
+  set_connection_at_risk_of_loss_time(base::TimeDelta::FromSeconds(0));
+  set_hung_interval(base::TimeDelta::FromSeconds(0));
 
   // Send a PING frame.
-  session_->WritePingFrame(1, false);
-  EXPECT_LT(0, session_->pings_in_flight());
-  EXPECT_EQ(2u, session_->next_ping_id());
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  WritePingFrame(1, false);
+  EXPECT_LT(0, pings_in_flight());
+  EXPECT_EQ(2u, next_ping_id());
+  EXPECT_TRUE(check_ping_status_pending());
 
   // Assert session is not closed.
   EXPECT_TRUE(session_->IsAvailable());
-  EXPECT_LT(0u,
-            session_->num_active_streams() + session_->num_created_streams());
+  EXPECT_LT(0u, num_active_streams() + num_created_streams());
   EXPECT_TRUE(HasSpdySession(spdy_session_pool_, key_));
 
   // We set last time we have received any data in 1 sec less than now.
   // CheckPingStatus will trigger timeout because hung interval is zero.
   base::TimeTicks now = base::TimeTicks::Now();
-  session_->last_read_time_ = now - base::TimeDelta::FromSeconds(1);
-  session_->CheckPingStatus(now);
+  set_last_read_time(now - base::TimeDelta::FromSeconds(1));
+  CheckPingStatus(now);
   // Set check_ping_status_pending_ so that DCHECK in pending CheckPingStatus()
   // on message loop does not fail.
-  session_->check_ping_status_pending_ = true;
+  set_check_ping_status_pending(true);
   // Execute pending CheckPingStatus() and drain session.
   base::RunLoop().RunUntilIdle();
 
@@ -1719,11 +1844,10 @@
   CreateSpdySession();
 
   // Negative value means a preface ping will always be sent.
-  session_->set_connection_at_risk_of_loss_time(
-      base::TimeDelta::FromSeconds(-1));
+  set_connection_at_risk_of_loss_time(base::TimeDelta::FromSeconds(-1));
 
   const base::TimeDelta hung_interval = base::TimeDelta::FromMilliseconds(100);
-  session_->set_hung_interval(hung_interval);
+  set_hung_interval(hung_interval);
 
   base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
@@ -1732,31 +1856,31 @@
   test::StreamDelegateSendImmediate delegate(spdy_stream1, nullptr);
   spdy_stream1->SetDelegate(&delegate);
 
-  EXPECT_EQ(0, session_->pings_in_flight());
-  EXPECT_EQ(1u, session_->next_ping_id());
-  EXPECT_FALSE(session_->check_ping_status_pending());
+  EXPECT_EQ(0, pings_in_flight());
+  EXPECT_EQ(1u, next_ping_id());
+  EXPECT_FALSE(check_ping_status_pending());
 
   // Send preface ping and post CheckPingStatus() task with delay.
-  session_->MaybeSendPrefacePing();
+  MaybeSendPrefacePing();
 
-  EXPECT_EQ(1, session_->pings_in_flight());
-  EXPECT_EQ(2u, session_->next_ping_id());
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  EXPECT_EQ(1, pings_in_flight());
+  EXPECT_EQ(2u, next_ping_id());
+  EXPECT_TRUE(check_ping_status_pending());
 
   // Read PING ACK.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0, session_->pings_in_flight());
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  EXPECT_EQ(0, pings_in_flight());
+  EXPECT_TRUE(check_ping_status_pending());
 
   // Fast forward mock time and send another preface ping.
   // This will not post another CheckPingStatus().
   g_time_delta = base::TimeDelta::FromMilliseconds(150);
-  session_->MaybeSendPrefacePing();
+  MaybeSendPrefacePing();
 
-  EXPECT_EQ(0, session_->pings_in_flight());
-  EXPECT_EQ(2u, session_->next_ping_id());
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  EXPECT_EQ(0, pings_in_flight());
+  EXPECT_EQ(2u, next_ping_id());
+  EXPECT_TRUE(check_ping_status_pending());
 
   // Read EOF.
   data.Resume();
@@ -1768,7 +1892,7 @@
   // that could be used to call RunLoop::Quit().
   // TODO(bnc): Fix once a RunLoop-compatible mock time framework is supported.
   // See https://crbug.com/708584#c75.
-  EXPECT_TRUE(session_->check_ping_status_pending());
+  EXPECT_TRUE(check_ping_status_pending());
 
   // Finish going away.
   base::RunLoop().RunUntilIdle();
@@ -2667,9 +2791,9 @@
       request3.StartRequest(SPDY_REQUEST_RESPONSE_STREAM, session_, test_url_,
                             LOWEST, NetLogWithSource(), callback3.callback()));
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(2u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(2u, pending_create_stream_queue_size(LOWEST));
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream1->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -2680,16 +2804,16 @@
   EXPECT_FALSE(spdy_stream1);
   EXPECT_EQ(1u, delegate1.stream_id());
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(LOWEST));
 
   // Pump loop for SpdySession::ProcessPendingStreamRequests() to
   // create the 2nd stream.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(LOWEST));
 
   base::WeakPtr<SpdyStream> stream2 = request2.ReleaseStream();
   test::StreamDelegateDoNothing delegate2(stream2);
@@ -2703,16 +2827,16 @@
   EXPECT_FALSE(stream2);
   EXPECT_EQ(3u, delegate2.stream_id());
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(LOWEST));
 
   // Pump loop for SpdySession::ProcessPendingStreamRequests() to
   // create the 3rd stream.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(LOWEST));
 
   base::WeakPtr<SpdyStream> stream3 = request3.ReleaseStream();
   test::StreamDelegateDoNothing delegate3(stream3);
@@ -2726,9 +2850,9 @@
   EXPECT_FALSE(stream3);
   EXPECT_EQ(5u, delegate3.stream_id());
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(LOWEST));
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -2777,9 +2901,9 @@
       request3.StartRequest(SPDY_BIDIRECTIONAL_STREAM, session_, test_url_,
                             LOWEST, NetLogWithSource(), callback3.callback()));
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(kInitialMaxConcurrentStreams, session_->num_created_streams());
-  EXPECT_EQ(2u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(kInitialMaxConcurrentStreams, num_created_streams());
+  EXPECT_EQ(2u, pending_create_stream_queue_size(LOWEST));
 
   // Cancel the first stream; this will allow the second stream to be created.
   EXPECT_TRUE(spdy_stream1);
@@ -2787,9 +2911,9 @@
   EXPECT_FALSE(spdy_stream1);
 
   EXPECT_THAT(callback2.WaitForResult(), IsOk());
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(kInitialMaxConcurrentStreams, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(kInitialMaxConcurrentStreams, num_created_streams());
+  EXPECT_EQ(1u, pending_create_stream_queue_size(LOWEST));
 
   // Cancel the second stream; this will allow the third stream to be created.
   base::WeakPtr<SpdyStream> spdy_stream2 = request2.ReleaseStream();
@@ -2797,17 +2921,17 @@
   EXPECT_FALSE(spdy_stream2);
 
   EXPECT_THAT(callback3.WaitForResult(), IsOk());
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(kInitialMaxConcurrentStreams, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(kInitialMaxConcurrentStreams, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(LOWEST));
 
   // Cancel the third stream.
   base::WeakPtr<SpdyStream> spdy_stream3 = request3.ReleaseStream();
   spdy_stream3->Cancel();
   EXPECT_FALSE(spdy_stream3);
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(kInitialMaxConcurrentStreams - 1, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->pending_create_stream_queue_size(LOWEST));
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(kInitialMaxConcurrentStreams - 1, num_created_streams());
+  EXPECT_EQ(0u, pending_create_stream_queue_size(LOWEST));
 }
 
 // Test that SpdySession::DoReadLoop reads data from the socket
@@ -3280,9 +3404,9 @@
   CreateNetworkSession();
   session_ = CreateFakeSpdySession(spdy_session_pool_, key_);
 
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_send_window_size_);
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_send_window_size());
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 }
 
 // Tests the case of a non-SPDY request closing an idle SPDY session when no
@@ -3609,8 +3733,8 @@
 
   EXPECT_FALSE(spdy_stream);
   EXPECT_TRUE(delegate.StreamIsClosed());
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -3656,7 +3780,7 @@
 
   // Process the SETTINGS frame.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(session_->stream_initial_send_window_size(), window_size);
+  EXPECT_EQ(stream_initial_send_window_size(), window_size);
   EXPECT_EQ(spdy_stream1->send_window_size(), window_size);
 
   // Release the first one, this will allow the second to be created.
@@ -3703,29 +3827,29 @@
   CreateNetworkSession();
   CreateSpdySession();
 
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
-  session_->IncreaseRecvWindowSize(delta_window_size);
+  IncreaseRecvWindowSize(delta_window_size);
   EXPECT_EQ(initial_window_size + delta_window_size,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(delta_window_size, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(delta_window_size, session_unacked_recv_window_bytes());
 
   // Should trigger sending a WINDOW_UPDATE frame.
-  session_->IncreaseRecvWindowSize(initial_window_size);
+  IncreaseRecvWindowSize(initial_window_size);
   EXPECT_EQ(initial_window_size + delta_window_size + initial_window_size,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+            session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   base::RunLoop().RunUntilIdle();
 
   // DecreaseRecvWindowSize() expects |in_io_loop_| to be true.
-  session_->in_io_loop_ = true;
-  session_->DecreaseRecvWindowSize(initial_window_size + delta_window_size +
-                                   initial_window_size);
-  session_->in_io_loop_ = false;
-  EXPECT_EQ(0, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  set_in_io_loop(true);
+  DecreaseRecvWindowSize(initial_window_size + delta_window_size +
+                         initial_window_size);
+  set_in_io_loop(false);
+  EXPECT_EQ(0, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   EXPECT_TRUE(session_);
   data.Resume();
@@ -3751,14 +3875,14 @@
   const int32_t initial_window_size = kDefaultInitialWindowSize;
   const int32_t delta_window_size = 100;
 
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
 
-  session_->IncreaseSendWindowSize(delta_window_size);
+  IncreaseSendWindowSize(delta_window_size);
   EXPECT_EQ(initial_window_size + delta_window_size,
-            session_->session_send_window_size_);
+            session_send_window_size());
 
-  session_->DecreaseSendWindowSize(delta_window_size);
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
+  DecreaseSendWindowSize(delta_window_size);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
 }
 
 // Incoming data for an inactive stream should not cause the session
@@ -3780,13 +3904,13 @@
   CreateNetworkSession();
   CreateSpdySession();
 
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(kUploadDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(kUploadDataSize, session_unacked_recv_window_bytes());
 
   EXPECT_TRUE(session_);
   data.Resume();
@@ -3814,14 +3938,14 @@
   CreateNetworkSession();
   CreateSpdySession();
 
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(kDefaultInitialWindowSize, session_->session_recv_window_size_);
+  EXPECT_EQ(kDefaultInitialWindowSize, session_recv_window_size());
   EXPECT_EQ(kUploadDataSize + padding_length,
-            session_->session_unacked_recv_window_bytes_);
+            session_unacked_recv_window_bytes());
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -3935,20 +4059,19 @@
   CreateSpdySession();
   // Setting session level receiving window size to smaller than initial is not
   // possible via SpdySessionPoolPeer.
-  session_->session_recv_window_size_ = session_max_recv_window_size;
+  set_session_recv_window_size(session_max_recv_window_size);
 
   // First data frame is immediately consumed and does not trigger
   // WINDOW_UPDATE.
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(first_data_frame_size,
-            session_->session_unacked_recv_window_bytes_);
-  EXPECT_EQ(session_max_recv_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(SpdySession::STATE_AVAILABLE, session_->availability_state_);
+  EXPECT_EQ(first_data_frame_size, session_unacked_recv_window_bytes());
+  EXPECT_EQ(session_max_recv_window_size, session_recv_window_size());
+  EXPECT_TRUE(session_->IsAvailable());
 
   // Second data frame overflows receiving window, causes session to close.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(SpdySession::STATE_DRAINING, session_->availability_state_);
+  EXPECT_TRUE(session_->IsDraining());
 }
 
 // Regression test for a bug that was caused by including unsent WINDOW_UPDATE
@@ -4102,21 +4225,21 @@
             stream->SendRequestHeaders(std::move(headers), MORE_DATA_TO_SEND));
 
   const int32_t initial_window_size = kDefaultInitialWindowSize;
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(kMsgDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(kMsgDataSize, session_unacked_recv_window_bytes());
 
   stream->Close();
   EXPECT_FALSE(stream);
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
 
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(kMsgDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(kMsgDataSize, session_unacked_recv_window_bytes());
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -4167,25 +4290,24 @@
             stream->SendRequestHeaders(std::move(headers), MORE_DATA_TO_SEND));
 
   const int32_t initial_window_size = kDefaultInitialWindowSize;
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
 
   // Write request.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
 
   // Read response, but do not run the message loop, so that the body is not
   // written to the socket.
   data.Resume();
 
-  EXPECT_EQ(initial_window_size - kMsgDataSize,
-            session_->session_send_window_size_);
+  EXPECT_EQ(initial_window_size - kMsgDataSize, session_send_window_size());
 
   // Closing the stream should increase the session's send window.
   stream->Close();
   EXPECT_FALSE(stream);
 
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
 
@@ -4250,53 +4372,49 @@
             stream->SendRequestHeaders(std::move(headers), MORE_DATA_TO_SEND));
 
   const int32_t initial_window_size = kDefaultInitialWindowSize;
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Send request and message.
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(initial_window_size - kMsgDataSize,
-            session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size - kMsgDataSize, session_send_window_size());
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Read echo.
   data.Resume();
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(initial_window_size - kMsgDataSize,
-            session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size - kMsgDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size - kMsgDataSize, session_send_window_size());
+  EXPECT_EQ(initial_window_size - kMsgDataSize, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   // Read window update.
   data.Resume();
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size - kMsgDataSize,
-            session_->session_recv_window_size_);
-  EXPECT_EQ(0, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
+  EXPECT_EQ(initial_window_size - kMsgDataSize, session_recv_window_size());
+  EXPECT_EQ(0, session_unacked_recv_window_bytes());
 
   EXPECT_EQ(msg_data, delegate.TakeReceivedData());
 
   // Draining the delegate's read queue should increase the session's
   // receive window.
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(kMsgDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(kMsgDataSize, session_unacked_recv_window_bytes());
 
   stream->Close();
   EXPECT_FALSE(stream);
 
   EXPECT_THAT(delegate.WaitForClose(), IsOk());
 
-  EXPECT_EQ(initial_window_size, session_->session_send_window_size_);
-  EXPECT_EQ(initial_window_size, session_->session_recv_window_size_);
-  EXPECT_EQ(kMsgDataSize, session_->session_unacked_recv_window_bytes_);
+  EXPECT_EQ(initial_window_size, session_send_window_size());
+  EXPECT_EQ(initial_window_size, session_recv_window_size());
+  EXPECT_EQ(kMsgDataSize, session_unacked_recv_window_bytes());
 
   data.Resume();
   base::RunLoop().RunUntilIdle();
@@ -4947,7 +5065,7 @@
   base::RunLoop().RunUntilIdle();
 
   // Put session on the edge of overflowing it's recv window.
-  session_->session_recv_window_size_ = 1;
+  set_session_recv_window_size(1);
 
   // Read response headers & body. Body overflows the session window, and a
   // goaway is written.
@@ -5004,10 +5122,10 @@
   test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream1->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5016,18 +5134,18 @@
   EXPECT_EQ(0u, delegate1.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate1.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Run until pushed stream is created.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Second stream should not be stalled, although we have 2 active streams, but
   // one of them is push stream and should not be taken into account when we
@@ -5036,10 +5154,10 @@
       CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session_,
                                 test_url_, LOWEST, NetLogWithSource());
   EXPECT_TRUE(spdy_stream2);
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5078,7 +5196,7 @@
 
   CreateNetworkSession();
   CreateSpdySession();
-  session_->set_max_concurrent_pushed_streams(1);
+  set_max_concurrent_pushed_streams(1);
 
   base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session_,
@@ -5088,10 +5206,10 @@
   test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream1->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5100,26 +5218,26 @@
   EXPECT_EQ(0u, delegate1.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate1.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Run until pushed stream is created.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Reset incoming pushed stream.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5184,10 +5302,10 @@
   test::StreamDelegateDoNothing delegate(spdy_stream);
   spdy_stream->SetDelegate(&delegate);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5196,26 +5314,26 @@
   EXPECT_EQ(0u, delegate.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Run until pushed stream is created.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Reset incoming pushed stream.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5263,10 +5381,10 @@
   test::StreamDelegateDoNothing delegate(spdy_stream);
   spdy_stream->SetDelegate(&delegate);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5275,10 +5393,10 @@
   EXPECT_EQ(0u, delegate.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5324,7 +5442,7 @@
 
   CreateNetworkSession();
   CreateSpdySession();
-  session_->set_max_concurrent_pushed_streams(1);
+  set_max_concurrent_pushed_streams(1);
 
   base::WeakPtr<SpdyStream> spdy_stream1 =
       CreateStreamSynchronously(SPDY_REQUEST_RESPONSE_STREAM, session_,
@@ -5334,10 +5452,10 @@
   test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream1->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5346,35 +5464,35 @@
   EXPECT_EQ(0u, delegate1.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate1.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Run until pushed stream is created.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Accept promised stream. It should not count towards pushed stream limit.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(3u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(2u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(3u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(2u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Reset last pushed stream upon headers reception as it is going to be 2nd,
   // while we accept only one.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(1u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(1u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5424,10 +5542,10 @@
   test::StreamDelegateDoNothing delegate1(spdy_stream1);
   spdy_stream1->SetDelegate(&delegate1);
 
-  EXPECT_EQ(0u, session_->num_active_streams());
-  EXPECT_EQ(1u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(0u, num_active_streams());
+  EXPECT_EQ(1u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyHeaderBlock headers(spdy_util_.ConstructGetHeaderBlock(kDefaultUrl));
   spdy_stream1->SendRequestHeaders(std::move(headers), NO_MORE_DATA_TO_SEND);
@@ -5436,18 +5554,18 @@
   EXPECT_EQ(0u, delegate1.stream_id());
   base::RunLoop().RunUntilIdle();
   EXPECT_EQ(1u, delegate1.stream_id());
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Run until pushed stream is created.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(2u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(1u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(2u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(1u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   SpdyStream* pushed_stream;
   int rv = session_->GetPushStream(GURL(kPushedUrl), IDLE, &pushed_stream,
@@ -5461,10 +5579,10 @@
   // that all our counters are in consistent state.
   data.Resume();
   base::RunLoop().RunUntilIdle();
-  EXPECT_EQ(1u, session_->num_active_streams());
-  EXPECT_EQ(0u, session_->num_created_streams());
-  EXPECT_EQ(0u, session_->num_pushed_streams());
-  EXPECT_EQ(0u, session_->num_active_pushed_streams());
+  EXPECT_EQ(1u, num_active_streams());
+  EXPECT_EQ(0u, num_created_streams());
+  EXPECT_EQ(0u, num_pushed_streams());
+  EXPECT_EQ(0u, num_active_pushed_streams());
 
   // Read EOF.
   data.Resume();
@@ -5488,17 +5606,17 @@
   CreateNetworkSession();
   CreateSpdySession();
 
-  session_->stream_hi_water_mark_ = 5;
+  set_stream_hi_water_mark(5);
   // Low client (odd) ids are fine.
-  EXPECT_TRUE(session_->OnUnknownFrame(3, 0));
+  EXPECT_TRUE(OnUnknownFrame(3, 0));
   // Client id exceeding watermark.
-  EXPECT_FALSE(session_->OnUnknownFrame(9, 0));
+  EXPECT_FALSE(OnUnknownFrame(9, 0));
 
-  session_->last_accepted_push_stream_id_ = 6;
+  set_last_accepted_push_stream_id(6);
   // Low server (even) ids are fine.
-  EXPECT_TRUE(session_->OnUnknownFrame(2, 0));
+  EXPECT_TRUE(OnUnknownFrame(2, 0));
   // Server id exceeding last accepted id.
-  EXPECT_FALSE(session_->OnUnknownFrame(8, 0));
+  EXPECT_FALSE(OnUnknownFrame(8, 0));
 }
 
 enum ReadIfReadySupport {
diff --git a/net/ssl/client_cert_store_mac.cc b/net/ssl/client_cert_store_mac.cc
index cbbc35b..4f8c91b 100644
--- a/net/ssl/client_cert_store_mac.cc
+++ b/net/ssl/client_cert_store_mac.cc
@@ -13,6 +13,8 @@
 #include <algorithm>
 #include <memory>
 #include <string>
+#include <utility>
+#include <vector>
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
@@ -249,6 +251,43 @@
   sort(sort_begin, sort_end, ClientCertIdentitySorter());
 }
 
+// Given a |sec_identity|, identifies its corresponding certificate, and either
+// adds it to |regular_identities| or assigns it to |preferred_identity|, if the
+// |sec_identity| matches the |preferred_sec_identity|.
+void AddIdentity(ScopedCFTypeRef<SecIdentityRef> sec_identity,
+                 SecIdentityRef preferred_sec_identity,
+                 ClientCertIdentityList* regular_identities,
+                 std::unique_ptr<ClientCertIdentity>* preferred_identity) {
+  OSStatus err;
+  ScopedCFTypeRef<SecCertificateRef> cert_handle;
+  err = SecIdentityCopyCertificate(sec_identity.get(),
+                                   cert_handle.InitializeInto());
+  if (err != noErr)
+    return;
+
+  if (!SupportsSSLClientAuth(cert_handle.get()))
+    return;
+
+  // Allow UTF-8 inside PrintableStrings in client certificates. See
+  // crbug.com/770323.
+  X509Certificate::UnsafeCreateOptions options;
+  options.printable_string_is_utf8 = true;
+  scoped_refptr<X509Certificate> cert(
+      x509_util::CreateX509CertificateFromSecCertificate(cert_handle.get(), {},
+                                                         options));
+  if (!cert)
+    return;
+
+  if (preferred_sec_identity &&
+      CFEqual(preferred_sec_identity, sec_identity.get())) {
+    *preferred_identity = std::make_unique<ClientCertIdentityMac>(
+        std::move(cert), std::move(sec_identity));
+  } else {
+    regular_identities->push_back(std::make_unique<ClientCertIdentityMac>(
+        std::move(cert), std::move(sec_identity)));
+  }
+}
+
 ClientCertIdentityList GetClientCertsOnBackgroundThread(
     const SSLCertRequestInfo& request) {
   std::string server_domain = request.host_and_port.host();
@@ -293,36 +332,8 @@
     }
     if (err)
       break;
-
-    ScopedCFTypeRef<SecCertificateRef> cert_handle;
-    err = SecIdentityCopyCertificate(sec_identity.get(),
-                                     cert_handle.InitializeInto());
-    if (err != noErr)
-      continue;
-
-    if (!SupportsSSLClientAuth(cert_handle.get()))
-      continue;
-
-    // Allow UTF-8 inside PrintableStrings in client certificates. See
-    // crbug.com/770323.
-    X509Certificate::UnsafeCreateOptions options;
-    options.printable_string_is_utf8 = true;
-    scoped_refptr<X509Certificate> cert(
-        x509_util::CreateX509CertificateFromSecCertificate(
-            cert_handle.get(), std::vector<SecCertificateRef>(), options));
-    if (!cert)
-      continue;
-
-    if (preferred_sec_identity &&
-        CFEqual(preferred_sec_identity, sec_identity.get())) {
-      // Only one certificate should match.
-      DCHECK(!preferred_identity.get());
-      preferred_identity = std::make_unique<ClientCertIdentityMac>(
-          std::move(cert), std::move(sec_identity));
-    } else {
-      regular_identities.push_back(std::make_unique<ClientCertIdentityMac>(
-          std::move(cert), std::move(sec_identity)));
-    }
+    AddIdentity(std::move(sec_identity), preferred_sec_identity.get(),
+                &regular_identities, &preferred_identity);
   }
 
   if (err != errSecItemNotFound) {
@@ -330,6 +341,39 @@
     return ClientCertIdentityList();
   }
 
+  // macOS provides two ways to search for identities. SecIdentitySearchCreate()
+  // is deprecated, as it relies on CSSM_KEYUSE_SIGN (part of the deprecated
+  // CDSM/CSSA implementation), but is necessary to return some certificates
+  // that would otherwise not be returned by SecItemCopyMatching(), which is the
+  // non-deprecated way. However, SecIdentitySearchCreate() will not return all
+  // items, particularly smart-card based identities, so it's necessary to call
+  // both functions.
+  static const void* kKeys[] = {
+      kSecClass, kSecMatchLimit, kSecReturnRef, kSecAttrCanSign,
+  };
+  static const void* kValues[] = {
+      kSecClassIdentity, kSecMatchLimitAll, kCFBooleanTrue, kCFBooleanTrue,
+  };
+  ScopedCFTypeRef<CFDictionaryRef> query(CFDictionaryCreate(
+      kCFAllocatorDefault, kKeys, kValues, arraysize(kValues),
+      &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
+  ScopedCFTypeRef<CFArrayRef> result;
+  {
+    base::AutoLock lock(crypto::GetMacSecurityServicesLock());
+    err = SecItemCopyMatching(
+        query, reinterpret_cast<CFTypeRef*>(result.InitializeInto()));
+  }
+  if (!err) {
+    for (CFIndex i = 0; i < CFArrayGetCount(result); i++) {
+      SecIdentityRef item = reinterpret_cast<SecIdentityRef>(
+          const_cast<void*>(CFArrayGetValueAtIndex(result, i)));
+      AddIdentity(
+          ScopedCFTypeRef<SecIdentityRef>(item, base::scoped_policy::RETAIN),
+          preferred_sec_identity.get(), &regular_identities,
+          &preferred_identity);
+    }
+  }
+
   ClientCertIdentityList selected_identities;
   GetClientCertsImpl(std::move(preferred_identity),
                      std::move(regular_identities), request, true,
diff --git a/third_party/WebKit/LayoutTests/TestExpectations b/third_party/WebKit/LayoutTests/TestExpectations
index b85b56f..cb04fa8 100644
--- a/third_party/WebKit/LayoutTests/TestExpectations
+++ b/third_party/WebKit/LayoutTests/TestExpectations
@@ -132,8 +132,6 @@
 crbug.com/619103 paint/invalidation/background/background-resize-width.html [ Failure Pass ]
 crbug.com/619103 virtual/spv175/paint/invalidation/background/background-resize-width.html [ Skip ]
 
-crbug.com/627844 virtual/gpu/fast/canvas/canvas-createImageBitmap-colorClamping.html [ Failure ]
-
 # Scanlines off by 1, but code path should be the same
 crbug.com/644433 virtual/gpu/fast/canvas/OffscreenCanvas-2d-pattern-in-worker.html [ Failure ]
 
@@ -2386,8 +2384,6 @@
 crbug.com/788337 external/wpt/css/css-multicol/multicol-block-no-clip-002.xht [ Failure ]
 crbug.com/788337 external/wpt/css/css-multicol/multicol-count-computed-003.xht [ Failure ]
 crbug.com/788337 external/wpt/css/css-multicol/multicol-count-computed-005.xht [ Failure ]
-crbug.com/788337 external/wpt/css/css-multicol/multicol-count-large-001.xht [ Failure ]
-crbug.com/788337 external/wpt/css/css-multicol/multicol-count-large-002.xht [ Failure ]
 crbug.com/788337 external/wpt/css/css-multicol/multicol-fill-auto-block-children-002.xht [ Failure ]
 crbug.com/788337 external/wpt/css/css-multicol/multicol-fill-auto.xht [ Failure ]
 crbug.com/788337 external/wpt/css/css-multicol/multicol-height-block-child-001.xht [ Failure ]
@@ -3414,15 +3410,9 @@
 crbug.com/669329 http/tests/devtools/tracing/timeline-js/timeline-runtime-stats.js [ Pass Failure Crash ]
 crbug.com/669329 virtual/threaded/http/tests/devtools/tracing/timeline-js/timeline-runtime-stats.js [ Pass Failure Crash ]
 
-# These can be rebaselined after their V8 changes are rolled into Blink.
-crbug.com/700624 http/tests/devtools/console/console-tests.js [ NeedsManualRebaseline ]
-crbug.com/727514 http/tests/devtools/tracing/console-timeline.js [ NeedsManualRebaseline ]
-crbug.com/727514 virtual/threaded/http/tests/devtools/tracing/console-timeline.js [ NeedsManualRebaseline ]
-
 # Tests timing out on WebKit Linux Trusty MSAN
 crbug.com/760543 [ Linux Release ] http/tests/devtools/profiler/heap-snapshot-inspect-dom-wrapper.js [ Pass Timeout ]
-# Uncomment these lines after the NeedsManualRebaseline counterpart is rebaselined.
-# crbug.com/760543 [ Linux Release ] http/tests/devtools/tracing/console-timeline.js [ Pass Timeout ]
+crbug.com/760543 [ Linux Release ] http/tests/devtools/tracing/console-timeline.js [ Pass Timeout ]
 crbug.com/760543 [ Linux Release ] http/tests/devtools/tracing/timeline-layout/timeline-layout-with-invalidations.js [ Pass Timeout ]
 crbug.com/760543 [ Linux Release ] http/tests/devtools/tracing/timeline-paint/timeline-paint-with-layout-invalidations.js [ Pass Timeout ]
 crbug.com/760543 [ Linux Release ] http/tests/devtools/tracing/timeline-paint/timeline-paint-with-style-recalc-invalidations.js [ Pass Timeout ]
@@ -3444,7 +3434,6 @@
 crbug.com/770232 [ Win10 ] editing/selection/paint-hyphen.html [ Failure ]
 crbug.com/770232 [ Win10 ] fast/text/hyphenate-character.html [ Failure ]
 crbug.com/770232 [ Win10 ] fast/text/unicode-fallback-font.html [ Failure ]
-# Uncomment this line after the NeedsManualRebaseline counterpart is rebaselined.
 
 # Test flaky (crashing) on WebKit Android (Nexus4)
 crbug.com/762008 [ Android ] http/tests/appcache/abort-cache-onchecking-resource-404.html [ Pass Crash ]
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-001.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-001.xht
deleted file mode 100644
index 78fff42..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-001.xht
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>multicolumn | column-count</title>
-<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
-<link rel="help" href="http://www.w3.org/TR/css3-multicol/#the-number-and-width-of-columns"/>
-<link rel="match" href="multicol-count-large-ref.xht"/>
-<meta name="flags" content="ahem"/>
-<style type="text/css"><![CDATA[
-body>div {
-	font-family: ahem;
-	font-size: 1em;
-	line-height: 1em;
-	color: red;
-	background: yellow;
-	margin: 1em;
-	border: 1em solid gray;
-	width: 12em;
-	orphans: 1;
-	widows: 1;
-
-	column-count: 1000;
-	column-gap: 0;
-}
-span {
-	color: black;
-}
-]]></style>
-</head>
-
-<body>
-
-<div>
-	xx xx
-	xx xx
-	xx xx
-	<span>xx xx</span>
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-002.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-002.xht
deleted file mode 100644
index 09b296a..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-002.xht
+++ /dev/null
@@ -1,42 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>multicolumn | column-count</title>
-<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
-<link rel="help" href="http://www.w3.org/TR/css3-multicol/#the-number-and-width-of-columns"/>
-<link rel="match" href="multicol-count-large-2-ref.xht"/>
-<meta name="flags" content="ahem"/>
-<style type="text/css"><![CDATA[
-body>div {
-	font-family: ahem;
-	font-size: 1em;
-	line-height: 1em;
-	color: black;
-	background: yellow;
-	margin: 1em;
-	border: 1em solid gray;
-	width: 12em;
-	orphans: 1;
-	widows: 1;
-
-	column-count: 1000;
-	column-gap: 0;
-}
-span {
-	color: blue;
-}
-]]></style>
-</head>
-
-<body>
-
-<div>
-	xx xx
-	<span>xx xx</span>
-	xx xx
-	<span>xx xx</span>
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-2-ref.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-2-ref.xht
deleted file mode 100644
index cb4b26f..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-2-ref.xht
+++ /dev/null
@@ -1,40 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>multicolumn | column-count</title>
-<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
-<style type="text/css"><![CDATA[
-body>div {
-	font-family: ahem;
-	font-size: 1em;
-	line-height: 1em;
-	color: black;
-	background: yellow;
-	margin: 1em;
-	border: 1em solid gray;
-	width: 12em;
-	height: 1em;
-	position: relative;
-	orphans: 1;
-	widows: 1;
-}
-span {
-	color: blue;
-	background: blue;
-	width: 2em;
-	position: absolute;
-	left: 0;
-	top: 0;
-}
-]]></style>
-</head>
-
-<body>
-
-<div>
-	<span>x</span>
-</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-ref.xht b/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-ref.xht
deleted file mode 100644
index a561c94..0000000
--- a/third_party/WebKit/LayoutTests/external/wpt/css/css-multicol/multicol-count-large-ref.xht
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
-<title>multicolumn | column-count</title>
-<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
-<style type="text/css"><![CDATA[
-body>div {
-	font-family: ahem;
-	font-size: 1em;
-	line-height: 1em;
-	color: black;
-	background: yellow;
-	margin: 1em;
-	border: 1em solid gray;
-	width: 12em;
-	height: 1em;
-	orphans: 1;
-	widows: 1;
-
-}
-]]></style>
-</head>
-
-<body>
-
-<div>xx</div>
-
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping-expected.html
deleted file mode 100644
index b9356d3..0000000
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping-expected.html
+++ /dev/null
@@ -1,23 +0,0 @@
-<html>
-<body>
-<canvas id="c"></canvas>
-<script>
-var canvas = document.getElementById("c");
-canvas.setAttribute("width", "250");
-canvas.setAttribute("height", "200");
-var ctx = canvas.getContext("2d");
-
-ctx.fillStyle = 'green';
-ctx.fillRect(0, 0, 100, 100);
-
-ctx.save();
-ctx.beginPath();
-ctx.rect(120, 0, 150, 150);
-ctx.clip();
-ctx.fillRect(0, 0, 250, 200);
-ctx.restore();
-
-</script>
-<p>Two green squares with no color bleeding should be visible.</p>
-</body>
-</html>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping.html
index 2588f15e..399b2bc6 100644
--- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping.html
+++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-colorClamping.html
@@ -1,32 +1,62 @@
-<html>
-<body>
-<canvas id="c"></canvas>
+<script src="../../resources/testharness.js"></script>
+<script src="../../resources/testharnessreport.js"></script>
 <script>
-if (window.testRunner)
-      testRunner.waitUntilDone();
 
-var aCanvas = document.createElement("canvas");
-aCanvas.setAttribute("width", "300");
-aCanvas.setAttribute("height", "300");
-var aCtx = aCanvas.getContext("2d");
-aCtx.fillStyle = 'red';
-aCtx.fillRect(0, 0, 300, 300);
-aCtx.fillStyle = 'green';
-aCtx.fillRect(100, 100, 100, 100);
+function checkPixels(ctx, coordinates, color) {
+	for (i = 0; i < coordinates.length; i++) {
+		var pixel = ctx.getImageData(coordinates[i][0], coordinates[i][1], 1, 1).data;
+    var message = "Pixel at " + coordinates[i] + " must be " + color;
+		assert_array_equals(pixel, color, message);
+	}
+}
 
-var canvas = document.getElementById("c");
-canvas.setAttribute("width", "250");
-canvas.setAttribute("height", "200");
-var ctx = canvas.getContext("2d");
+function checkLines(ctx, lines, areVertical, color) {
+  var pixels;
+  for (l = 0; l < lines.length; l++) {
+    line = lines[l];
+    if (areVertical)
+      pixels = ctx.getImageData(line[0], line[1], 1, line[2]).data;
+    else
+      pixels = ctx.getImageData(line[0], line[1], line[2], 1).data;
+    for (i = 0; i < line[2]; i++) {
+      var pixel = pixels.slice(i * 4, i * 4 + 4);
+      var message = "Pixel #" + i + " must be " + color;
+      assert_array_equals(pixel, color, message);
+    }
+  }
+}
 
-createImageBitmap(aCanvas, 100, 100, 100, 100).then(function (imageBitmap) {
-    ctx.drawImage(imageBitmap, 0, 0);
-    ctx.drawImage(imageBitmap, 120, 0, 150, 150);
-    if (window.testRunner)
-         testRunner.notifyDone();
-});
+async_test(function(t) {
+  var aCanvas = document.createElement("canvas");
+  aCanvas.setAttribute("width", "300");
+  aCanvas.setAttribute("height", "300");
+  var aCtx = aCanvas.getContext("2d");
+  aCtx.fillStyle = 'red';
+  aCtx.fillRect(0, 0, 300, 300);
+  aCtx.fillStyle = 'green';
+  aCtx.fillRect(100, 100, 100, 100);
 
+  var canvas = document.createElement("canvas");
+  canvas.setAttribute("width", "350");
+  canvas.setAttribute("height", "200");
+  var ctx = canvas.getContext("2d");
+
+  var greenPixels = [[10, 10], [10,109], [109,10], [109,109],
+                     [150, 10], [150,159], [299,10], [299,159]];
+  // x, y, length
+  var noBleedingHorizontalLines = [[9, 9, 102], [9, 110, 102],
+                                   [149, 9, 152], [149, 160, 152]];
+  var noBleedingVerticalLines = [[9, 9, 100], [110, 9, 100],
+                                 [149, 9, 152], [300, 9, 152]];
+
+  createImageBitmap(aCanvas, 100, 100, 100, 100).then(t.step_func_done(function(imageBitmap) {
+      ctx.drawImage(imageBitmap, 10, 10);
+      ctx.drawImage(imageBitmap, 150, 10, 150, 150);
+      checkPixels(ctx, greenPixels, [0, 128, 0, 255]);
+      checkLines(ctx, noBleedingHorizontalLines, false, [0, 0, 0, 0]);
+      checkLines(ctx, noBleedingVerticalLines, true, [0, 0, 0, 0]);
+      t.done();
+  }));
+}, "Two green squares with no color bleeding should be visible.");
 </script>
-<p>Two green squares with no color bleeding should be visible.</p>
-</body>
-</html>
+                                                                                                    
\ No newline at end of file
diff --git a/third_party/WebKit/LayoutTests/http/tests/devtools/editor/text-editor-search-replace.js b/third_party/WebKit/LayoutTests/http/tests/devtools/editor/text-editor-search-replace.js
index 5b4fdf1..385dbf3 100644
--- a/third_party/WebKit/LayoutTests/http/tests/devtools/editor/text-editor-search-replace.js
+++ b/third_party/WebKit/LayoutTests/http/tests/devtools/editor/text-editor-search-replace.js
@@ -15,7 +15,8 @@
   function showReplaceField() {
     var searchableView = UI.panels.sources.searchableView();
     searchableView.showSearchField();
-    searchableView._replaceCheckboxElement.click();
+    searchableView._replaceToggleButton.setToggled(true);
+    searchableView._updateSecondRowVisibility();
   }
 
   function runReplaceAll(searchValue, replaceValue) {
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.cpp
index 40b4b4f8..cfa4c3f 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.cpp
@@ -298,19 +298,6 @@
   return wire_string;
 }
 
-void SerializedScriptValue::ToWireBytes(Vector<char>& result) const {
-  DCHECK(result.IsEmpty());
-
-  size_t result_size = (data_buffer_size_ + 1) & ~1;
-  result.resize(result_size);
-  memcpy(result.data(), data_buffer_.get(), data_buffer_size_);
-
-  if (result_size > data_buffer_size_) {
-    DCHECK_EQ(result_size, data_buffer_size_ + 1);
-    result[data_buffer_size_] = 0;
-  }
-}
-
 SerializedScriptValue::ImageBitmapContentsArray
 SerializedScriptValue::TransferImageBitmapContents(
     v8::Isolate* isolate,
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.h b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.h
index 7c17d33..95e48108 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.h
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValue.h
@@ -146,7 +146,6 @@
   static scoped_refptr<SerializedScriptValue> NullValue();
 
   String ToWireString() const;
-  void ToWireBytes(Vector<char>&) const;
 
   StringView GetWireData() const {
     return StringView(data_buffer_.get(), data_buffer_size_);
diff --git a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueTest.cpp b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueTest.cpp
index ace3e46..d0f4874d 100644
--- a/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueTest.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/serialization/SerializedScriptValueTest.cpp
@@ -12,6 +12,7 @@
 #include "bindings/core/v8/serialization/SerializedScriptValueFactory.h"
 #include "core/fileapi/File.h"
 #include "platform/testing/UnitTestHelpers.h"
+#include "platform/wtf/text/StringView.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace blink {
@@ -25,11 +26,13 @@
           scope.GetIsolate(), v8OriginalTrue,
           SerializedScriptValue::SerializeOptions(), ASSERT_NO_EXCEPTION);
 
-  Vector<char> wireData;
-  sourceSerializedScriptValue->ToWireBytes(wireData);
+  StringView wire_data = sourceSerializedScriptValue->GetWireData();
+  DCHECK(wire_data.Is8Bit());
 
   scoped_refptr<SerializedScriptValue> serializedScriptValue =
-      SerializedScriptValue::Create(wireData.data(), wireData.size());
+      SerializedScriptValue::Create(
+          reinterpret_cast<const char*>(wire_data.Characters8()),
+          wire_data.length());
   v8::Local<v8::Value> deserialized =
       serializedScriptValue->Deserialize(scope.GetIsolate());
   EXPECT_TRUE(deserialized->IsTrue());
diff --git a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp
index 2f7143bf..87a517f 100644
--- a/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp
+++ b/third_party/WebKit/Source/bindings/modules/v8/V8BindingForModulesTest.cpp
@@ -37,6 +37,7 @@
 #include "modules/indexeddb/IDBValue.h"
 #include "platform/SharedBuffer.h"
 #include "platform/bindings/V8PerIsolateData.h"
+#include "platform/wtf/text/StringView.h"
 #include "public/platform/WebBlobInfo.h"
 #include "public/platform/WebData.h"
 #include "public/platform/WebString.h"
@@ -147,7 +148,10 @@
   scoped_refptr<SerializedScriptValue> serialized_value =
       SerializedScriptValue::Serialize(isolate, value, options,
                                        non_throwable_exception_state);
-  serialized_value->ToWireBytes(*wire_bytes);
+  StringView ssv_wire_data = serialized_value->GetWireData();
+  DCHECK(ssv_wire_data.Is8Bit());
+  DCHECK(wire_bytes->IsEmpty());
+  wire_bytes->Append(ssv_wire_data.Characters8(), ssv_wire_data.length());
 
   // Sanity check that the serialization header has not changed, as the tests
   // that use this method rely on the header format.
diff --git a/third_party/WebKit/Source/core/html/HTMLAllCollection.cpp b/third_party/WebKit/Source/core/html/HTMLAllCollection.cpp
index 2165bd0b..a15c75e 100644
--- a/third_party/WebKit/Source/core/html/HTMLAllCollection.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAllCollection.cpp
@@ -64,7 +64,7 @@
 void HTMLAllCollection::namedGetter(const AtomicString& name,
                                     NodeListOrElement& return_value) {
   HeapVector<Member<Element>> named_items;
-  this->NamedItems(name, named_items);
+  NamedItems(name, named_items);
 
   if (!named_items.size())
     return;
diff --git a/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp b/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
index 1162998e..9b502411 100644
--- a/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLAreaElement.cpp
@@ -210,7 +210,7 @@
 
   HTMLAnchorElement::SetFocused(should_be_focused, focus_type);
 
-  HTMLImageElement* image_element = this->ImageElement();
+  HTMLImageElement* image_element = ImageElement();
   if (!image_element)
     return;
 
@@ -228,7 +228,7 @@
   if (!IsFocusable())
     return;
 
-  if (HTMLImageElement* image_element = this->ImageElement()) {
+  if (HTMLImageElement* image_element = ImageElement()) {
     image_element->UpdateFocusAppearanceWithOptions(selection_behavior,
                                                     options);
   }
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
index 77e831a..ce1aaac 100644
--- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -311,7 +311,7 @@
     UpdateMemoryUsage();
   }
 
-  LayoutObject* layout_object = this->GetLayoutObject();
+  LayoutObject* layout_object = GetLayoutObject();
   if (layout_object && Is2d() && !context_->CreationAttributes().alpha()) {
     // In the alpha false case, canvas is initially opaque even though there is
     // no ImageBuffer, so we need to trigger an invalidation.
@@ -599,7 +599,7 @@
   if (Is3d() && old_size != Size())
     context_->Reshape(width(), height());
 
-  if (LayoutObject* layout_object = this->GetLayoutObject()) {
+  if (LayoutObject* layout_object = GetLayoutObject()) {
     if (layout_object->IsCanvas()) {
       if (old_size != Size()) {
         ToLayoutHTMLCanvas(layout_object)->CanvasSizeChanged();
diff --git a/third_party/WebKit/Source/core/html/HTMLElement.cpp b/third_party/WebKit/Source/core/html/HTMLElement.cpp
index e0f40ec..b8bcc95b 100644
--- a/third_party/WebKit/Source/core/html/HTMLElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLElement.cpp
@@ -1282,7 +1282,7 @@
 Element* HTMLElement::unclosedOffsetParent() {
   GetDocument().UpdateStyleAndLayoutIgnorePendingStylesheetsForNode(this);
 
-  LayoutObject* layout_object = this->GetLayoutObject();
+  LayoutObject* layout_object = GetLayoutObject();
   if (!layout_object)
     return nullptr;
 
diff --git a/third_party/WebKit/Source/core/html/HTMLEmbedElement.cpp b/third_party/WebKit/Source/core/html/HTMLEmbedElement.cpp
index 3104764..6465eee 100644
--- a/third_party/WebKit/Source/core/html/HTMLEmbedElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLEmbedElement.cpp
@@ -134,7 +134,7 @@
 
 void HTMLEmbedElement::ParametersForPlugin(Vector<String>& param_names,
                                            Vector<String>& param_values) {
-  AttributeCollection attributes = this->Attributes();
+  AttributeCollection attributes = Attributes();
   for (const Attribute& attribute : attributes) {
     param_names.push_back(attribute.LocalName().GetString());
     param_values.push_back(attribute.Value().GetString());
diff --git a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
index 38a5718..536f38f4 100644
--- a/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLIFrameElement.cpp
@@ -109,7 +109,7 @@
   const AtomicString& value = params.new_value;
   if (name == nameAttr) {
     if (IsInDocumentTree() && GetDocument().IsHTMLDocument()) {
-      HTMLDocument& document = ToHTMLDocument(this->GetDocument());
+      HTMLDocument& document = ToHTMLDocument(GetDocument());
       document.RemoveNamedItem(name_);
       document.AddNamedItem(value);
     }
diff --git a/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp b/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
index 5f95320..c8bdccc5 100644
--- a/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLImageLoader.cpp
@@ -70,7 +70,7 @@
 
 void HTMLImageLoader::ImageNotifyFinished(ImageResourceContent*) {
   ImageResourceContent* cached_image = GetContent();
-  Element* element = this->GetElement();
+  Element* element = GetElement();
   ImageLoader::ImageNotifyFinished(cached_image);
 
   bool load_error = cached_image->ErrorOccurred();
diff --git a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
index 2308178..ce9289c 100644
--- a/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLMarqueeElement.cpp
@@ -279,8 +279,8 @@
   }
 
   AnimationParameters parameters = GetAnimationParameters();
-  int scroll_delay = this->scrollDelay();
-  int scroll_amount = this->scrollAmount();
+  int scroll_delay = scrollDelay();
+  int scroll_amount = scrollAmount();
 
   if (scroll_delay < kMinimumScrollDelayMS &&
       !FastHasAttribute(HTMLNames::truespeedAttr))
diff --git a/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp b/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
index 582d86f..325739e 100644
--- a/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
+++ b/third_party/WebKit/Source/core/html/HTMLObjectElement.cpp
@@ -186,7 +186,7 @@
 
   // Turn the attributes of the <object> element into arrays, but don't override
   // <param> values.
-  AttributeCollection attributes = this->Attributes();
+  AttributeCollection attributes = Attributes();
   for (const Attribute& attribute : attributes) {
     const AtomicString& name = attribute.GetName().LocalName();
     if (!unique_param_names.Contains(name.Impl())) {
diff --git a/third_party/WebKit/Source/core/html/ImageData.cpp b/third_party/WebKit/Source/core/html/ImageData.cpp
index 92c89ed9..82d2bb2e 100644
--- a/third_party/WebKit/Source/core/html/ImageData.cpp
+++ b/third_party/WebKit/Source/core/html/ImageData.cpp
@@ -753,7 +753,7 @@
   CanvasColorParams dst_color_params =
       CanvasColorParams(canvas_color_space, canvas_pixel_format, kNonOpaque);
 
-  void* src_data = this->BufferBase()->Data();
+  void* src_data = BufferBase()->Data();
   sk_sp<SkColorSpace> src_color_space =
       GetCanvasColorParams().GetSkColorSpaceForSkSurfaces();
   sk_sp<SkColorSpace> dst_color_space =
diff --git a/third_party/WebKit/Source/core/html/ImageDocument.cpp b/third_party/WebKit/Source/core/html/ImageDocument.cpp
index 752a84e..bd051f6 100644
--- a/third_party/WebKit/Source/core/html/ImageDocument.cpp
+++ b/third_party/WebKit/Source/core/html/ImageDocument.cpp
@@ -274,7 +274,7 @@
   if (ShouldShrinkToFit()) {
     // Add event listeners
     EventListener* listener = ImageEventListener::Create(this);
-    if (LocalDOMWindow* dom_window = this->domWindow())
+    if (LocalDOMWindow* dom_window = domWindow())
       dom_window->addEventListener(EventTypeNames::resize, listener, false);
 
     if (shrink_to_fit_mode_ == kDesktop) {
@@ -348,7 +348,7 @@
 
     UpdateStyleAndLayout();
 
-    double scale = this->Scale();
+    double scale = Scale();
     double device_scale_factor =
         GetFrame()->View()->GetChromeClient()->WindowToViewportScalar(1.f);
 
@@ -492,7 +492,7 @@
 
 bool ImageDocument::ImageFitsInWindow() const {
   DCHECK_EQ(shrink_to_fit_mode_, kDesktop);
-  return this->Scale() >= 1;
+  return Scale() >= 1;
 }
 
 int ImageDocument::CalculateDivWidth() {
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
index 21f633b..840343fc 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreator.cpp
@@ -250,11 +250,11 @@
     }
   } else {
     idle_task_status_ = kIdleTaskNotStarted;
-    this->ScheduleInitiateEncoding(quality);
+    ScheduleInitiateEncoding(quality);
 
     // We post the below task to check if the above idle task isn't late.
     // There's no risk of concurrency as both tasks are on the same thread.
-    this->PostDelayedTaskToCurrentThread(
+    PostDelayedTaskToCurrentThread(
         BLINK_FROM_HERE,
         WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent,
                   WrapPersistent(this), quality),
@@ -286,7 +286,7 @@
     return;
   }
 
-  this->IdleEncodeRows(deadline_seconds);
+  IdleEncodeRows(deadline_seconds);
 }
 
 void CanvasAsyncBlobCreator::IdleEncodeRows(double deadline_seconds) {
@@ -307,7 +307,7 @@
 
     if (!encoder_->encodeRows(1)) {
       idle_task_status_ = kIdleTaskFailed;
-      this->CreateNullAndReturnResult();
+      CreateNullAndReturnResult();
       return;
     }
   }
@@ -322,7 +322,7 @@
                    WTF::Bind(&CanvasAsyncBlobCreator::CreateBlobAndReturnResult,
                              WrapPersistent(this)));
   } else {
-    this->CreateBlobAndReturnResult();
+    CreateBlobAndReturnResult();
   }
 }
 
@@ -333,14 +333,14 @@
   for (int y = num_rows_completed_; y < src_data_.height(); ++y) {
     if (!encoder_->encodeRows(1)) {
       idle_task_status_ = kIdleTaskFailed;
-      this->CreateNullAndReturnResult();
+      CreateNullAndReturnResult();
       return;
     }
   }
   num_rows_completed_ = src_data_.height();
 
   if (IsMainThread()) {
-    this->CreateBlobAndReturnResult();
+    CreateBlobAndReturnResult();
   } else {
     context_->GetTaskRunner(TaskType::kCanvasBlobSerialization)
         ->PostTask(
@@ -349,7 +349,7 @@
                             WrapCrossThreadPersistent(this)));
   }
 
-  this->SignalAlternativeCodePathFinishedForTesting();
+  SignalAlternativeCodePathFinishedForTesting();
 }
 
 void CanvasAsyncBlobCreator::CreateBlobAndReturnResult() {
@@ -440,7 +440,7 @@
 void CanvasAsyncBlobCreator::IdleTaskStartTimeoutEvent(double quality) {
   if (idle_task_status_ == kIdleTaskStarted) {
     // Even if the task started quickly, we still want to ensure completion
-    this->PostDelayedTaskToCurrentThread(
+    PostDelayedTaskToCurrentThread(
         BLINK_FROM_HERE,
         WTF::Bind(&CanvasAsyncBlobCreator::IdleTaskCompleteTimeoutEvent,
                   WrapPersistent(this)),
@@ -461,12 +461,12 @@
                         WrapPersistent(this)));
     } else {
       // Failing in initialization of encoder
-      this->SignalAlternativeCodePathFinishedForTesting();
+      SignalAlternativeCodePathFinishedForTesting();
     }
   } else {
     DCHECK(idle_task_status_ == kIdleTaskFailed ||
            idle_task_status_ == kIdleTaskCompleted);
-    this->SignalAlternativeCodePathFinishedForTesting();
+    SignalAlternativeCodePathFinishedForTesting();
   }
 }
 
@@ -487,7 +487,7 @@
   } else {
     DCHECK(idle_task_status_ == kIdleTaskFailed ||
            idle_task_status_ == kIdleTaskCompleted);
-    this->SignalAlternativeCodePathFinishedForTesting();
+    SignalAlternativeCodePathFinishedForTesting();
   }
 }
 
diff --git a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
index 311abad..e8866cb 100644
--- a/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
+++ b/third_party/WebKit/Source/core/html/canvas/CanvasAsyncBlobCreatorTest.cpp
@@ -249,16 +249,16 @@
   // StartTimeoutEvent is inspecting the idle task status.
   // The whole image encoding process (including initialization)  will then
   // become carried out in the alternative code path instead.
-  this->PrepareMockCanvasAsyncBlobCreatorWithoutStartPng();
+  PrepareMockCanvasAsyncBlobCreatorWithoutStartPng();
   EXPECT_CALL(*(AsyncBlobCreator()),
               SignalTaskSwitchInStartTimeoutEventForTesting());
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
   testing::EnterRunLoop();
 
   ::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
   EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 
 TEST_F(CanvasAsyncBlobCreatorTest,
@@ -267,16 +267,16 @@
   // CompleteTimeoutEvent is inspecting the idle task status.
   // The remaining image encoding process (excluding initialization)  will
   // then become carried out in the alternative code path instead.
-  this->PrepareMockCanvasAsyncBlobCreatorWithoutCompletePng();
+  PrepareMockCanvasAsyncBlobCreatorWithoutCompletePng();
   EXPECT_CALL(*(AsyncBlobCreator()),
               SignalTaskSwitchInCompleteTimeoutEventForTesting());
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
   testing::EnterRunLoop();
 
   ::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
   EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 
 TEST_F(CanvasAsyncBlobCreatorTest,
@@ -284,53 +284,53 @@
   // This test mocks the scenario when idle task is not failed during when
   // either the StartTimeoutEvent or the CompleteTimeoutEvent is inspecting
   // the idle task status.
-  this->PrepareMockCanvasAsyncBlobCreatorFailPng();
+  PrepareMockCanvasAsyncBlobCreatorFailPng();
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(true);
   testing::EnterRunLoop();
 
   EXPECT_EQ(IdleTaskStatus::kIdleTaskFailed,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 
 // The below 3 unit tests have exactly same workflow as the above 3 unit tests
 // except that they are encoding on JPEG image formats instead of PNG.
 TEST_F(CanvasAsyncBlobCreatorTest,
        JpegIdleTaskNotStartedWhenStartTimeoutEventHappens) {
-  this->PrepareMockCanvasAsyncBlobCreatorWithoutStartJpeg();
+  PrepareMockCanvasAsyncBlobCreatorWithoutStartJpeg();
   EXPECT_CALL(*(AsyncBlobCreator()),
               SignalTaskSwitchInStartTimeoutEventForTesting());
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
   testing::EnterRunLoop();
 
   ::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
   EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 
 TEST_F(CanvasAsyncBlobCreatorTest,
        JpegIdleTaskNotCompletedWhenCompleteTimeoutEventHappens) {
-  this->PrepareMockCanvasAsyncBlobCreatorWithoutCompleteJpeg();
+  PrepareMockCanvasAsyncBlobCreatorWithoutCompleteJpeg();
   EXPECT_CALL(*(AsyncBlobCreator()),
               SignalTaskSwitchInCompleteTimeoutEventForTesting());
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
   testing::EnterRunLoop();
 
   ::testing::Mock::VerifyAndClearExpectations(AsyncBlobCreator());
   EXPECT_EQ(IdleTaskStatus::kIdleTaskSwitchedToImmediateTask,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 
 TEST_F(CanvasAsyncBlobCreatorTest,
        JpegIdleTaskFailedWhenStartTimeoutEventHappens) {
-  this->PrepareMockCanvasAsyncBlobCreatorFailJpeg();
+  PrepareMockCanvasAsyncBlobCreatorFailJpeg();
 
-  this->AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
+  AsyncBlobCreator()->ScheduleAsyncBlobCreation(1.0);
   testing::EnterRunLoop();
 
   EXPECT_EQ(IdleTaskStatus::kIdleTaskFailed,
-            this->AsyncBlobCreator()->GetIdleTaskStatus());
+            AsyncBlobCreator()->GetIdleTaskStatus());
 }
 }
diff --git a/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp b/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp
index 78e54ca..f39d962 100644
--- a/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/ColorInputType.cpp
@@ -151,7 +151,7 @@
   if (!Frame::HasTransientUserActivation(GetElement().GetDocument().GetFrame()))
     return;
 
-  ChromeClient* chrome_client = this->GetChromeClient();
+  ChromeClient* chrome_client = GetChromeClient();
   if (chrome_client && !chooser_)
     chooser_ = chrome_client->OpenColorChooser(
         GetElement().GetDocument().GetFrame(), this, ValueAsColor());
diff --git a/third_party/WebKit/Source/core/html/forms/DateTimeEditElement.cpp b/third_party/WebKit/Source/core/html/forms/DateTimeEditElement.cpp
index 37441b3..1736464 100644
--- a/third_party/WebKit/Source/core/html/forms/DateTimeEditElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/DateTimeEditElement.cpp
@@ -728,7 +728,7 @@
   }
   Element* fields_wrapper = FieldsWrapperElement();
 
-  size_t focused_field_index = this->FocusedFieldIndex();
+  size_t focused_field_index = FocusedFieldIndex();
   DateTimeFieldElement* const focused_field = FieldAt(focused_field_index);
   const AtomicString focused_field_id =
       focused_field ? focused_field->ShadowPseudoId() : g_null_atom;
diff --git a/third_party/WebKit/Source/core/html/forms/DateTimeNumericFieldElement.cpp b/third_party/WebKit/Source/core/html/forms/DateTimeNumericFieldElement.cpp
index 2c109a43..3f48539f 100644
--- a/third_party/WebKit/Source/core/html/forms/DateTimeNumericFieldElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/DateTimeNumericFieldElement.cpp
@@ -66,7 +66,7 @@
   // should follow the direction of numeric values.
   if (LocaleForOwner().IsRTL()) {
     WTF::Unicode::CharDirection dir =
-        WTF::Unicode::Direction(FormatValue(this->Maximum())[0]);
+        WTF::Unicode::Direction(FormatValue(Maximum())[0]);
     if (dir == WTF::Unicode::kLeftToRight ||
         dir == WTF::Unicode::kEuropeanNumber ||
         dir == WTF::Unicode::kArabicNumber) {
diff --git a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
index 0846461..19e4365 100644
--- a/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/FileInputType.cpp
@@ -149,7 +149,7 @@
   if (!Frame::HasTransientUserActivation(GetElement().GetDocument().GetFrame()))
     return;
 
-  if (ChromeClient* chrome_client = this->GetChromeClient()) {
+  if (ChromeClient* chrome_client = GetChromeClient()) {
     WebFileChooserParams params;
     HTMLInputElement& input = GetElement();
     bool is_directory = input.FastHasAttribute(webkitdirectoryAttr);
@@ -343,7 +343,7 @@
 }
 
 void FileInputType::SetFilesFromDirectory(const String& path) {
-  if (ChromeClient* chrome_client = this->GetChromeClient()) {
+  if (ChromeClient* chrome_client = GetChromeClient()) {
     Vector<String> files;
     files.push_back(path);
     WebFileChooserParams params;
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLFormControlElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLFormControlElement.cpp
index 2bc06184..ec8faf3 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLFormControlElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLFormControlElement.cpp
@@ -376,7 +376,7 @@
 }
 
 void HTMLFormControlElement::DidRecalcStyle() {
-  if (LayoutObject* layout_object = this->GetLayoutObject())
+  if (LayoutObject* layout_object = GetLayoutObject())
     layout_object->UpdateFromElement();
 }
 
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLFormControlsCollection.cpp b/third_party/WebKit/Source/core/html/forms/HTMLFormControlsCollection.cpp
index c4f60ce..b60d8dd 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLFormControlsCollection.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLFormControlsCollection.cpp
@@ -78,7 +78,7 @@
 
 HTMLElement* HTMLFormControlsCollection::VirtualItemAfter(
     Element* previous) const {
-  const ListedElement::List& listed_elements = this->ListedElements();
+  const ListedElement::List& listed_elements = ListedElements();
   unsigned offset;
   if (!previous)
     offset = 0;
@@ -176,7 +176,7 @@
     const AtomicString& name,
     RadioNodeListOrElement& return_value) {
   HeapVector<Member<Element>> named_items;
-  this->NamedItems(name, named_items);
+  NamedItems(name, named_items);
 
   if (named_items.IsEmpty())
     return;
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLFormElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLFormElement.cpp
index 861e546b..eeb9efc 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLFormElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLFormElement.cpp
@@ -139,7 +139,7 @@
   HTMLElement::InsertedInto(insertion_point);
   LogAddElementIfIsolatedWorldAndInDocument("form", methodAttr, actionAttr);
   if (insertion_point->isConnected())
-    this->GetDocument().DidAssociateFormControl(this);
+    GetDocument().DidAssociateFormControl(this);
   return kInsertionDone;
 }
 
@@ -694,7 +694,7 @@
     CheckValidityEventBehavior event_behavior) {
   // Copy listedElements because event handlers called from
   // HTMLFormControlElement::checkValidity() might change listedElements.
-  const ListedElement::List& listed_elements = this->ListedElements();
+  const ListedElement::List& listed_elements = ListedElements();
   HeapVector<Member<ListedElement>> elements;
   elements.ReserveCapacity(listed_elements.size());
   for (const auto& element : listed_elements)
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLInputElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLInputElement.cpp
index 5cbff23..2b5eee7 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLInputElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLInputElement.cpp
@@ -1599,7 +1599,7 @@
 }
 
 bool HTMLInputElement::HasValidDataListOptions() const {
-  HTMLDataListElement* data_list = this->DataList();
+  HTMLDataListElement* data_list = DataList();
   if (!data_list)
     return false;
   HTMLDataListOptionsCollection* options = data_list->options();
@@ -1614,7 +1614,7 @@
 HeapVector<Member<HTMLOptionElement>>
 HTMLInputElement::FilteredDataListOptions() const {
   HeapVector<Member<HTMLOptionElement>> filtered;
-  HTMLDataListElement* data_list = this->DataList();
+  HTMLDataListElement* data_list = DataList();
   if (!data_list)
     return filtered;
 
@@ -1868,7 +1868,7 @@
   parameters.double_value = input_type_->ValueAsDouble();
   parameters.is_anchor_element_rtl =
       input_type_view_->ComputedTextDirection() == TextDirection::kRtl;
-  if (HTMLDataListElement* data_list = this->DataList()) {
+  if (HTMLDataListElement* data_list = DataList()) {
     HTMLDataListOptionsCollection* options = data_list->options();
     for (unsigned i = 0; HTMLOptionElement* option = options->Item(i); ++i) {
       if (option->value().IsEmpty() || option->IsDisabledFormControl() ||
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLOptionElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLOptionElement.cpp
index e1261dc..050385a 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLOptionElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLOptionElement.cpp
@@ -112,7 +112,7 @@
 }
 
 String HTMLOptionElement::DisplayLabel() const {
-  Document& document = this->GetDocument();
+  Document& document = GetDocument();
   String text;
 
   // WinIE does not use the label attribute, so as a quirk, we ignore it.
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLOptionsCollection.cpp b/third_party/WebKit/Source/core/html/forms/HTMLOptionsCollection.cpp
index febdb329..89d5e6c 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLOptionsCollection.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLOptionsCollection.cpp
@@ -100,7 +100,7 @@
 void HTMLOptionsCollection::namedGetter(const AtomicString& name,
                                         NodeListOrElement& return_value) {
   HeapVector<Member<Element>> named_items;
-  this->NamedItems(name, named_items);
+  NamedItems(name, named_items);
 
   if (!named_items.size())
     return;
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLSelectElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLSelectElement.cpp
index 12c7e77..b016668 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLSelectElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLSelectElement.cpp
@@ -465,7 +465,7 @@
                                                       SkipDirection direction,
                                                       int skip) const {
   DCHECK(direction == kSkipBackwards || direction == kSkipForwards);
-  const ListItems& list_items = this->GetListItems();
+  const ListItems& list_items = GetListItems();
   HTMLOptionElement* last_good_option = nullptr;
   int size = list_items.size();
   for (list_index += direction; list_index >= 0 && list_index < size;
@@ -669,7 +669,7 @@
 void HTMLSelectElement::DispatchInputAndChangeEventForMenuList() {
   DCHECK(UsesMenuList());
 
-  HTMLOptionElement* selected_option = this->SelectedOption();
+  HTMLOptionElement* selected_option = SelectedOption();
   if (last_on_change_option_.Get() != selected_option) {
     last_on_change_option_ = selected_option;
     DispatchInputEvent();
@@ -688,7 +688,7 @@
 }
 
 void HTMLSelectElement::SetOptionsChangedOnLayoutObject() {
-  if (LayoutObject* layout_object = this->GetLayoutObject()) {
+  if (LayoutObject* layout_object = GetLayoutObject()) {
     if (!UsesMenuList())
       return;
     ToLayoutMenuList(layout_object)
@@ -868,7 +868,7 @@
     return;
   suggested_option_ = option;
 
-  if (LayoutObject* layout_object = this->GetLayoutObject()) {
+  if (LayoutObject* layout_object = GetLayoutObject()) {
     layout_object->UpdateFromElement();
     ScrollToOption(option);
   }
@@ -1015,7 +1015,7 @@
   }
 
   // For the menu list case, this is what makes the selected element appear.
-  if (LayoutObject* layout_object = this->GetLayoutObject())
+  if (LayoutObject* layout_object = GetLayoutObject())
     layout_object->UpdateFromElement();
   // PopupMenu::updateFromElement() posts an O(N) task.
   if (PopupIsVisible() && should_update_popup)
@@ -1029,7 +1029,7 @@
       DispatchInputEvent();
       DispatchChangeEvent();
     }
-    if (LayoutObject* layout_object = this->GetLayoutObject()) {
+    if (LayoutObject* layout_object = GetLayoutObject()) {
       // Need to check usesMenuList() again because event handlers might
       // change the status.
       if (UsesMenuList()) {
@@ -1196,7 +1196,7 @@
 }
 
 void HTMLSelectElement::AppendToFormData(FormData& form_data) {
-  const AtomicString& name = this->GetName();
+  const AtomicString& name = GetName();
   if (name.IsEmpty())
     return;
 
@@ -1291,7 +1291,7 @@
 
     const String& key = key_event->key();
     bool handled = true;
-    const ListItems& list_items = this->GetListItems();
+    const ListItems& list_items = GetListItems();
     HTMLOptionElement* option = SelectedOption();
     int list_index = option ? option->ListIndex() : -1;
 
@@ -1434,7 +1434,7 @@
 }
 
 int HTMLSelectElement::ListIndexForOption(const HTMLOptionElement& option) {
-  const ListItems& items = this->GetListItems();
+  const ListItems& items = GetListItems();
   size_t length = items.size();
   for (size_t i = 0; i < length; ++i) {
     if (items[i].Get() == &option)
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.cpp b/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.cpp
index ac3b09a..081300b4 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.cpp
+++ b/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.cpp
@@ -169,7 +169,7 @@
       cols = kDefaultCols;
     if (cols_ != cols) {
       cols_ = cols;
-      if (LayoutObject* layout_object = this->GetLayoutObject()) {
+      if (LayoutObject* layout_object = GetLayoutObject()) {
         layout_object
             ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
                 LayoutInvalidationReason::kAttributeChanged);
@@ -190,7 +190,7 @@
       wrap = kSoftWrap;
     if (wrap != wrap_) {
       wrap_ = wrap;
-      if (LayoutObject* layout_object = this->GetLayoutObject()) {
+      if (LayoutObject* layout_object = GetLayoutObject()) {
         layout_object
             ->SetNeedsLayoutAndPrefWidthsRecalcAndFullPaintInvalidation(
                 LayoutInvalidationReason::kAttributeChanged);
diff --git a/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.h b/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.h
index 2047e68..356eb4d5d 100644
--- a/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.h
+++ b/third_party/WebKit/Source/core/html/forms/HTMLTextAreaElement.h
@@ -135,7 +135,7 @@
   bool MatchesReadWritePseudoClass() const override;
   void CopyNonAttributePropertiesFromElement(const Element&) final;
 
-  // If the String* argument is 0, apply this->value().
+  // If the String* argument is 0, apply value().
   bool ValueMissing(const String*) const;
   bool TooLong(const String*, NeedsToCheckDirtyFlag) const;
   bool TooShort(const String*, NeedsToCheckDirtyFlag) const;
diff --git a/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp b/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
index 81bf1f3..36def01 100644
--- a/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
+++ b/third_party/WebKit/Source/core/html/forms/MultipleFieldsTemporalInputTypeView.cpp
@@ -273,7 +273,7 @@
     return;
   }
 
-  DateTimeEditElement* edit = this->GetDateTimeEditElement();
+  DateTimeEditElement* edit = GetDateTimeEditElement();
   if (!edit)
     return;
   EventQueueScope scope;
diff --git a/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp b/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp
index ca7252e0..4bf2b9e 100644
--- a/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/RangeInputType.cpp
@@ -345,7 +345,7 @@
     GetElement()
         .GetLayoutObject()
         ->SetShouldDoFullPaintInvalidationIncludingNonCompositingDescendants();
-  Element* slider_track_element = this->SliderTrackElement();
+  Element* slider_track_element = SliderTrackElement();
   if (slider_track_element->GetLayoutObject())
     slider_track_element->GetLayoutObject()->SetNeedsLayout(
         LayoutInvalidationReason::kAttributeChanged);
@@ -370,7 +370,7 @@
     String option_value = option_element->value();
     if (option_element->IsDisabledFormControl() || option_value.IsEmpty())
       continue;
-    if (!this->GetElement().IsValidValue(option_value))
+    if (!GetElement().IsValidValue(option_value))
       continue;
     tick_mark_values_.push_back(ParseToNumber(option_value, Decimal::Nan()));
   }
diff --git a/third_party/WebKit/Source/core/html/forms/StepRange.cpp b/third_party/WebKit/Source/core/html/forms/StepRange.cpp
index 124f1541..1978c1c2 100644
--- a/third_party/WebKit/Source/core/html/forms/StepRange.cpp
+++ b/third_party/WebKit/Source/core/html/forms/StepRange.cpp
@@ -180,7 +180,7 @@
 
 Decimal StepRange::StepSnappedMaximum() const {
   Decimal base = StepBase();
-  Decimal step = this->Step();
+  Decimal step = Step();
   if (base - step == base || !(base / step).IsFinite())
     return Decimal::Nan();
   Decimal aligned_maximum = base + ((Maximum() - base) / step).Floor() * step;
diff --git a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
index 790a6c2..ac970bb4 100644
--- a/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
+++ b/third_party/WebKit/Source/core/html/forms/TextFieldInputType.cpp
@@ -194,7 +194,7 @@
 void TextFieldInputType::HandleKeydownEvent(KeyboardEvent* event) {
   if (!GetElement().IsFocused())
     return;
-  if (ChromeClient* chrome_client = this->GetChromeClient()) {
+  if (ChromeClient* chrome_client = GetChromeClient()) {
     chrome_client->HandleKeyboardEventOnTextField(GetElement(), *event);
     return;
   }
@@ -280,7 +280,7 @@
   DCHECK(!shadow_root->HasChildren());
 
   Document& document = GetElement().GetDocument();
-  bool should_have_spin_button = this->ShouldHaveSpinButton();
+  bool should_have_spin_button = ShouldHaveSpinButton();
   bool should_have_data_list_indicator = GetElement().HasValidDataListOptions();
   bool creates_container = should_have_spin_button ||
                            should_have_data_list_indicator || NeedsContainer();
@@ -325,7 +325,7 @@
 }
 
 void TextFieldInputType::ListAttributeTargetChanged() {
-  if (ChromeClient* chrome_client = this->GetChromeClient())
+  if (ChromeClient* chrome_client = GetChromeClient())
     chrome_client->TextFieldDataListChanged(GetElement());
   Element* picker = GetElement().UserAgentShadowRoot()->getElementById(
       ShadowElementNames::PickerIndicator());
@@ -430,10 +430,10 @@
   // Selected characters will be removed by the next text event.
   unsigned base_length = old_length - selection_length;
   unsigned max_length;
-  if (this->MaxLength() < 0)
+  if (MaxLength() < 0)
     max_length = std::numeric_limits<int>::max();
   else
-    max_length = static_cast<unsigned>(this->MaxLength());
+    max_length = static_cast<unsigned>(MaxLength());
   unsigned appendable_length =
       max_length > base_length ? max_length - base_length : 0;
 
@@ -511,7 +511,7 @@
 void TextFieldInputType::DidSetValueByUserEdit() {
   if (!GetElement().IsFocused())
     return;
-  if (ChromeClient* chrome_client = this->GetChromeClient())
+  if (ChromeClient* chrome_client = GetChromeClient())
     chrome_client->DidChangeValueInTextField(GetElement());
 }
 
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
index 1e298a0c..6b7d062a 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImport.cpp
@@ -53,8 +53,8 @@
 }
 
 bool HTMLImport::FormsCycle() const {
-  for (const HTMLImport* i = this->Parent(); i; i = i->Parent()) {
-    if (i->GetDocument() == this->GetDocument())
+  for (const HTMLImport* i = Parent(); i; i = i->Parent()) {
+    if (i->GetDocument() == GetDocument())
       return true;
   }
 
@@ -74,7 +74,7 @@
 
 void HTMLImport::StateDidChange() {
   if (!GetState().ShouldBlockScriptExecution()) {
-    if (Document* document = this->GetDocument())
+    if (Document* document = GetDocument())
       document->DidLoadAllImports();
   }
 }
diff --git a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
index 43e247a..2281722d5 100644
--- a/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
+++ b/third_party/WebKit/Source/core/html/imports/HTMLImportChild.cpp
@@ -166,7 +166,7 @@
 void HTMLImportChild::Normalize() {
   DCHECK(loader_);
 
-  if (!loader_->IsFirstImport(this) && this->Precedes(loader_->FirstImport())) {
+  if (!loader_->IsFirstImport(this) && Precedes(loader_->FirstImport())) {
     HTMLImportChild* old_first = loader_->FirstImport();
     loader_->MoveToFirst(this);
     TakeChildrenFrom(old_first);
diff --git a/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
index d4cdd9f..0757369 100644
--- a/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
+++ b/third_party/WebKit/Source/core/html/media/HTMLMediaElement.cpp
@@ -2751,7 +2751,7 @@
   // algorithm.  The order is explicitly specified as text, then audio, and
   // finally video.  Also 'removetrack' events should not be fired.
   if (text_tracks_) {
-    TrackDisplayUpdateScope scope(this->GetCueTimeline());
+    TrackDisplayUpdateScope scope(GetCueTimeline());
     text_tracks_->RemoveAllInbandTracks();
   }
 
diff --git a/third_party/WebKit/Source/core/html/track/CueTimeline.cpp b/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
index c15b4616..4748f40 100644
--- a/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
+++ b/third_party/WebKit/Source/core/html/track/CueTimeline.cpp
@@ -120,7 +120,7 @@
   if (IgnoreUpdateRequests())
     return;
 
-  HTMLMediaElement& media_element = this->MediaElement();
+  HTMLMediaElement& media_element = MediaElement();
 
   // Don't run the "time marches on" algorithm if the document has been
   // detached. This primarily guards against dispatch of events w/
diff --git a/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp b/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
index ecd17dee..08ed4790 100644
--- a/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
+++ b/third_party/WebKit/Source/core/inspector/DevToolsHost.cpp
@@ -221,6 +221,18 @@
   return LayoutTheme::GetTheme().ActiveSelectionForegroundColor().Serialized();
 }
 
+String DevToolsHost::getInactiveSelectionBackgroundColor() {
+  return LayoutTheme::GetTheme()
+      .InactiveSelectionBackgroundColor()
+      .Serialized();
+}
+
+String DevToolsHost::getInactiveSelectionForegroundColor() {
+  return LayoutTheme::GetTheme()
+      .InactiveSelectionForegroundColor()
+      .Serialized();
+}
+
 bool DevToolsHost::isUnderTest() {
   return client_ && client_->IsUnderTest();
 }
diff --git a/third_party/WebKit/Source/core/inspector/DevToolsHost.h b/third_party/WebKit/Source/core/inspector/DevToolsHost.h
index 00cf2151..969f46bdb 100644
--- a/third_party/WebKit/Source/core/inspector/DevToolsHost.h
+++ b/third_party/WebKit/Source/core/inspector/DevToolsHost.h
@@ -68,6 +68,8 @@
 
   String getSelectionBackgroundColor();
   String getSelectionForegroundColor();
+  String getInactiveSelectionBackgroundColor();
+  String getInactiveSelectionForegroundColor();
 
   bool isUnderTest();
   bool isHostedMode();
diff --git a/third_party/WebKit/Source/core/inspector/DevToolsHost.idl b/third_party/WebKit/Source/core/inspector/DevToolsHost.idl
index 7aca074..2bbddc47 100644
--- a/third_party/WebKit/Source/core/inspector/DevToolsHost.idl
+++ b/third_party/WebKit/Source/core/inspector/DevToolsHost.idl
@@ -43,6 +43,8 @@
 
     DOMString getSelectionBackgroundColor();
     DOMString getSelectionForegroundColor();
+    DOMString getInactiveSelectionBackgroundColor();
+    DOMString getInactiveSelectionForegroundColor();
 
     boolean isUnderTest();
     boolean isHostedMode();
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
index 0114d7e..58bbd86 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
index d4451270..310543573 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
+++ b/third_party/WebKit/Source/devtools/front_end/Images/mediumIcons_2x.png
Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
index 0a2250d..861d177 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/mediumIcons.svg
@@ -100,8 +100,8 @@
      id="namedview4913"
      showgrid="true"
      inkscape:zoom="5.2149126"
-     inkscape:cx="27.187925"
-     inkscape:cy="37.929068"
+     inkscape:cx="-5.9228807"
+     inkscape:cy="72.509528"
      inkscape:window-x="0"
      inkscape:window-y="0"
      inkscape:window-maximized="0"
@@ -732,4 +732,33 @@
        id="path6344"
        d="m 0.5,14 15,0 L 8,1 0.5,14 l 0,0 z m 8.5,-2 -2,0 0,-2 2,0 0,2 0,0 z M 9,9 7,9 7,6 9,6 9,9 9,9 z" />
   </g>
+  <text
+     xml:space="preserve"
+     style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+     x="32.469646"
+     y="7.902472"
+     id="text3111"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       id="tspan3113"
+       x="32.469646"
+       y="7.902472"
+       style="font-size:10px;font-weight:bold">A</tspan></text>
+  <text
+     xml:space="preserve"
+     style="font-size:10px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+     x="40.362782"
+     y="15.525424"
+     id="text3115"
+     sodipodi:linespacing="125%"><tspan
+       sodipodi:role="line"
+       id="tspan3117"
+       x="40.362782"
+       y="15.525424"
+       style="font-size:10px;font-weight:bold">B</tspan></text>
+  <path
+     d="m 37.955041,13.088794 c -1.914609,-0.291809 -2.119489,-0.899233 -2.237,-4.129445 l -1.219783,0 c 0.04662,3.855641 0.06137,5.297137 5.735435,5.257508 l 0.316769,-0.01434 -2.595652,-2.995518 c -0.01446,0.611794 -0.01486,1.270551 2.31e-4,1.881795 z"
+     id="path448"
+     inkscape:connector-curvature="0"
+     sodipodi:nodetypes="ccccccc" />
 </svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
index 1e8a0aae..bb2b1f46 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
@@ -5,7 +5,7 @@
     "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
     "smallIcons.svg": "beee6da075d6de8ef77fdf430ee798e5",
-    "mediumIcons.svg": "5ecf5731037057c627e6a222e9b24350",
+    "mediumIcons.svg": "5305adcb3fa224710ef2efeec01667a8",
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "chevrons.svg": "79b4b527771e30b6388ce664077b3409"
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
index 1e8a0aae..bb2b1f46 100644
--- a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
+++ b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
@@ -5,7 +5,7 @@
     "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a",
     "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45",
     "smallIcons.svg": "beee6da075d6de8ef77fdf430ee798e5",
-    "mediumIcons.svg": "5ecf5731037057c627e6a222e9b24350",
+    "mediumIcons.svg": "5305adcb3fa224710ef2efeec01667a8",
     "breakpoint.svg": "69cd92d807259c022791112809b97799",
     "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
     "chevrons.svg": "79b4b527771e30b6388ce664077b3409"
diff --git a/third_party/WebKit/Source/devtools/front_end/devtools_compatibility.js b/third_party/WebKit/Source/devtools/front_end/devtools_compatibility.js
index 98ed9487..17cba4383 100644
--- a/third_party/WebKit/Source/devtools/front_end/devtools_compatibility.js
+++ b/third_party/WebKit/Source/devtools/front_end/devtools_compatibility.js
@@ -347,6 +347,22 @@
      * @override
      * @return {string}
      */
+    getInactiveSelectionBackgroundColor() {
+      return DevToolsHost.getInactiveSelectionBackgroundColor();
+    }
+
+    /**
+     * @override
+     * @return {string}
+     */
+    getInactiveSelectionForegroundColor() {
+      return DevToolsHost.getInactiveSelectionForegroundColor();
+    }
+
+    /**
+     * @override
+     * @return {string}
+     */
     platform() {
       return DevToolsHost.platform();
     }
diff --git a/third_party/WebKit/Source/devtools/front_end/externs.js b/third_party/WebKit/Source/devtools/front_end/externs.js
index 118ee60..26138d5 100644
--- a/third_party/WebKit/Source/devtools/front_end/externs.js
+++ b/third_party/WebKit/Source/devtools/front_end/externs.js
@@ -258,6 +258,16 @@
 DevToolsHost.getSelectionForegroundColor = function() {};
 
 /**
+ * @return {string}
+ */
+DevToolsHost.getInactiveSelectionBackgroundColor = function() {};
+
+/**
+ * @return {string}
+ */
+DevToolsHost.getInactiveSelectionForegroundColor = function() {};
+
+/**
  * @return {boolean}
  */
 DevToolsHost.isUnderTest = function() {};
diff --git a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
index a71e844c..0aac690 100644
--- a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
+++ b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHost.js
@@ -68,6 +68,22 @@
    * @override
    * @return {string}
    */
+  getInactiveSelectionBackgroundColor() {
+    return '#c9c8c8';
+  }
+
+  /**
+   * @override
+   * @return {string}
+   */
+  getInactiveSelectionForegroundColor() {
+    return '#323232';
+  }
+
+  /**
+   * @override
+   * @return {string}
+   */
   platform() {
     var match = navigator.userAgent.match(/Windows NT/);
     if (match)
diff --git a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js
index 30a6dea..ccb741d 100644
--- a/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js
+++ b/third_party/WebKit/Source/devtools/front_end/host/InspectorFrontendHostAPI.js
@@ -127,6 +127,16 @@
   getSelectionForegroundColor() {},
 
   /**
+   * @return {string}
+   */
+  getInactiveSelectionBackgroundColor() {},
+
+  /**
+   * @return {string}
+   */
+  getInactiveSelectionForegroundColor() {},
+
+  /**
    * Requests inspected page to be placed atop of the inspector frontend with specified bounds.
    * @param {{x: number, y: number, width: number, height: number}} bounds
    */
diff --git a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js
index 775940f..f0e7df2 100644
--- a/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js
+++ b/third_party/WebKit/Source/devtools/front_end/sources_test_runner/SearchTestRunner.js
@@ -99,7 +99,7 @@
   searchableView._caseSensitiveButton.setToggled(searchConfig.caseSensitive);
   searchableView._regexButton.setToggled(searchConfig.isRegex);
   searchableView._searchInputElement.value = searchConfig.query;
-  searchableView._replaceCheckboxElement.checked = true;
+  searchableView._replaceToggleButton.setToggled(true);
   searchableView._updateSecondRowVisibility();
   searchableView._replaceInputElement.value = replacement;
   searchableView._performSearch(true, true);
diff --git a/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorUtils.js b/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorUtils.js
index cf16d7885..0d0e2698 100644
--- a/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorUtils.js
+++ b/third_party/WebKit/Source/devtools/front_end/text_editor/CodeMirrorUtils.js
@@ -94,22 +94,36 @@
 TextEditor.CodeMirrorUtils.appendThemeStyle = function(element) {
   if (UI.themeSupport.hasTheme())
     return;
-  var backgroundColor = InspectorFrontendHost.getSelectionBackgroundColor();
-  var backgroundColorRule =
-      backgroundColor ? '.CodeMirror .CodeMirror-selected { background-color: ' + backgroundColor + ';}' : '';
-  var foregroundColor = InspectorFrontendHost.getSelectionForegroundColor();
-  var foregroundColorRule = foregroundColor ?
-      '.CodeMirror .CodeMirror-selectedtext:not(.CodeMirror-persist-highlight) { color: ' + foregroundColor +
-          '!important;}' :
-      '';
 
-  var selectionRule = (foregroundColor && backgroundColor) ?
-      '.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: ' +
-          backgroundColor + '; color: ' + foregroundColor + ' !important }' :
-      '';
+  var backgroundColor = InspectorFrontendHost.getSelectionBackgroundColor();
+  var foregroundColor = InspectorFrontendHost.getSelectionForegroundColor();
+  var inactiveBackgroundColor = InspectorFrontendHost.getInactiveSelectionBackgroundColor();
+  var inactiveForegroundColor = InspectorFrontendHost.getInactiveSelectionForegroundColor();
   var style = createElement('style');
-  if (foregroundColorRule || backgroundColorRule)
-    style.textContent = backgroundColorRule + foregroundColorRule + selectionRule;
+  style.textContent = `
+    .CodeMirror .CodeMirror-selected {
+      background-color: ${inactiveBackgroundColor};
+    }
+
+    .CodeMirror .CodeMirror-selectedtext:not(.CodeMirror-persist-highlight) {
+      color: ${inactiveForegroundColor} !important;
+    }
+
+    .CodeMirror-focused .CodeMirror-selected {
+      background-color: ${backgroundColor};
+    }
+
+    .CodeMirror-focused .CodeMirror-selectedtext:not(.CodeMirror-persist-highlight) {
+      color: ${foregroundColor} !important;
+    }
+
+    .CodeMirror .CodeMirror-line::selection,
+    .CodeMirror .CodeMirror-line > span::selection,
+    .CodeMirror .CodeMirror-line > span > span::selection {
+      background: ${backgroundColor};
+      color: ${foregroundColor} !important;
+    }
+  `;
   element.appendChild(style);
 };
 
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
index 3c65e20..9c23acef 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -168,6 +168,7 @@
   'mediumicon-red-cross-active': {position: 'd2', spritesheet: 'mediumicons'},
   'mediumicon-red-cross-hover': {position: 'a1', spritesheet: 'mediumicons'},
   'mediumicon-search': {position: 'b1', spritesheet: 'mediumicons'},
+  'mediumicon-replace': {position: 'c5', spritesheet: 'mediumicons', isMask: true},
   'mediumicon-account-circle': {position: 'e4', spritesheet: 'mediumicons'},
   'mediumicon-warning-triangle': {position: 'e1', spritesheet: 'mediumicons'},
   'mediumicon-error-circle': {position: 'e3', spritesheet: 'mediumicons'},
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js b/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
index b48a13c..06fa7c6 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
+++ b/third_party/WebKit/Source/devtools/front_end/ui/SearchableView.js
@@ -44,30 +44,18 @@
 
     this._searchProvider = searchable;
     this._setting = settingName ? Common.settings.createSetting(settingName, {}) : null;
+    this._replaceable = false;
 
     this.contentElement.createChild('content');
     this._footerElementContainer = this.contentElement.createChild('div', 'search-bar hidden');
     this._footerElementContainer.style.order = 100;
-
-    var toolbar = new UI.Toolbar('', this._footerElementContainer);
-
-    if (this._searchProvider.supportsCaseSensitiveSearch()) {
-      this._caseSensitiveButton = new UI.ToolbarToggle(Common.UIString('Case sensitive'), '');
-      this._caseSensitiveButton.setText('Aa');
-      this._caseSensitiveButton.addEventListener(UI.ToolbarButton.Events.Click, this._toggleCaseSensitiveSearch, this);
-      toolbar.appendToolbarItem(this._caseSensitiveButton);
-    }
-
-    if (this._searchProvider.supportsRegexSearch()) {
-      this._regexButton = new UI.ToolbarToggle(Common.UIString('Regex'), '');
-      this._regexButton.setText('.*');
-      this._regexButton.addEventListener(UI.ToolbarButton.Events.Click, this._toggleRegexSearch, this);
-      toolbar.appendToolbarItem(this._regexButton);
-    }
-
     this._footerElement = this._footerElementContainer.createChild('div', 'toolbar-search');
 
-    // Build the text input elements.
+    var replaceToggleToolbar = new UI.Toolbar('replace-toggle-toolbar', this._footerElement);
+    this._replaceToggleButton = new UI.ToolbarToggle(Common.UIString('Replace'), 'mediumicon-replace');
+    this._replaceToggleButton.addEventListener(UI.ToolbarButton.Events.Click, this._toggleReplace, this);
+    replaceToggleToolbar.appendToolbarItem(this._replaceToggleButton);
+
     var searchInputElements = this._footerElement.createChild('div', 'toolbar-search-inputs');
     var searchControlElement = searchInputElements.createChild('div', 'toolbar-search-control');
 
@@ -100,44 +88,42 @@
     this._replaceInputElement.addEventListener('keydown', this._onReplaceKeyDown.bind(this), true);
     this._replaceInputElement.placeholder = Common.UIString('Replace');
 
-    // Build the buttons (Find, Previous, Replace, Replace All).
-    this._buttonsContainer = this._footerElement.createChild('div', 'toolbar-search-buttons hidden');
+    this._buttonsContainer = this._footerElement.createChild('div', 'toolbar-search-buttons');
+    var firstRowButtons = this._buttonsContainer.createChild('div', 'first-row-buttons');
 
-    var findButtonElement =
-        UI.createTextButton(Common.UIString('Find'), this._onFindClick.bind(this), 'search-action-button');
-    findButtonElement.tabIndex = -1;
-    this._buttonsContainer.appendChild(findButtonElement);
+    var toolbar = new UI.Toolbar('', firstRowButtons);
 
-    var prevButtonElement =
-        UI.createTextButton(Common.UIString('Previous'), this._onPreviousClick.bind(this), 'search-action-button');
-    prevButtonElement.tabIndex = -1;
-    this._buttonsContainer.appendChild(prevButtonElement);
+    if (this._searchProvider.supportsCaseSensitiveSearch()) {
+      this._caseSensitiveButton = new UI.ToolbarToggle(Common.UIString('Case sensitive'));
+      this._caseSensitiveButton.setText('Aa');
+      this._caseSensitiveButton.addEventListener(UI.ToolbarButton.Events.Click, this._toggleCaseSensitiveSearch, this);
+      toolbar.appendToolbarItem(this._caseSensitiveButton);
+    }
+
+    if (this._searchProvider.supportsRegexSearch()) {
+      this._regexButton = new UI.ToolbarToggle('');
+      this._regexButton.setText('/regex/');
+      this._regexButton.addEventListener(UI.ToolbarButton.Events.Click, this._toggleRegexSearch, this);
+      toolbar.appendToolbarItem(this._regexButton);
+    }
+
+    var cancelButtonElement =
+        UI.createTextButton(Common.UIString('Cancel'), this.closeSearch.bind(this), 'search-action-button');
+    firstRowButtons.appendChild(cancelButtonElement);
+
+    this._secondRowButtons = this._buttonsContainer.createChild('div', 'second-row-buttons hidden');
 
     this._replaceButtonElement =
         UI.createTextButton(Common.UIString('Replace'), this._replace.bind(this), 'search-action-button');
     this._replaceButtonElement.disabled = true;
-    this._replaceButtonElement.tabIndex = -1;
-    this._buttonsContainer.appendChild(this._replaceButtonElement);
+    this._secondRowButtons.appendChild(this._replaceButtonElement);
 
-    var replaceAllButtonElement =
+    this._replaceAllButtonElement =
         UI.createTextButton(Common.UIString('Replace all'), this._replaceAll.bind(this), 'search-action-button');
-    this._buttonsContainer.appendChild(replaceAllButtonElement);
+    this._secondRowButtons.appendChild(this._replaceAllButtonElement);
+    this._replaceAllButtonElement.disabled = true;
 
-    // Build the replace checkbox and cancel button.
-    this._replaceElement = this._footerElement.createChild('div').createChild('span', 'toolbar-replace-checkbox');
-
-    var replaceLabelElement = UI.CheckboxLabel.create(Common.UIString('Replace'));
-    this._replaceCheckboxElement = replaceLabelElement.checkboxElement;
-    this._replaceCheckboxElement.addEventListener('change', this._updateSecondRowVisibility.bind(this), false);
-
-    this._replaceElement.appendChild(replaceLabelElement);
-
-    var cancelButtonElement =
-        UI.createTextButton(Common.UIString('Cancel'), this.closeSearch.bind(this), 'search-action-button');
-    cancelButtonElement.tabIndex = -1;
     this._minimalSearchQuerySize = 3;
-    this._footerElement.createChild('div').appendChild(cancelButtonElement);
-
     this._loadSetting();
   }
 
@@ -166,6 +152,11 @@
     this._performSearch(false, true);
   }
 
+  _toggleReplace() {
+    this._replaceToggleButton.setToggled(!this._replaceToggleButton.toggled());
+    this._updateSecondRowVisibility();
+  }
+
   _saveSetting() {
     if (!this._setting)
       return;
@@ -303,6 +294,7 @@
    */
   _updateSearchNavigationButtonState(enabled) {
     this._replaceButtonElement.disabled = !enabled;
+    this._replaceAllButtonElement.disabled = !enabled;
     if (enabled) {
       this._searchNavigationPrevElement.classList.add('enabled');
       this._searchNavigationNextElement.classList.add('enabled');
@@ -350,9 +342,9 @@
   }
 
   _updateReplaceVisibility() {
-    this._replaceElement.classList.toggle('hidden', !this._replaceable);
+    this._replaceToggleButton.setVisible(this._replaceable);
     if (!this._replaceable) {
-      this._replaceCheckboxElement.checked = false;
+      this._replaceToggleButton.setToggled(false);
       this._updateSecondRowVisibility();
     }
   }
@@ -465,11 +457,10 @@
   }
 
   _updateSecondRowVisibility() {
-    var secondRowVisible = this._replaceCheckboxElement.checked;
+    var secondRowVisible = this._replaceToggleButton.toggled();
     this._footerElementContainer.classList.toggle('replaceable', secondRowVisible);
-    this._buttonsContainer.classList.toggle('hidden', !secondRowVisible);
+    this._secondRowButtons.classList.toggle('hidden', !secondRowVisible);
     this._replaceInputElement.classList.toggle('hidden', !secondRowVisible);
-    this._replaceCheckboxElement.tabIndex = secondRowVisible ? -1 : 0;
 
     if (secondRowVisible)
       this._replaceInputElement.focus();
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
index d3bc06a..ca79d001 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/searchableView.css
@@ -29,12 +29,6 @@
     outline: none;
 }
 
-.toolbar-replace-checkbox {
-    margin-top: 4px;
-    margin-right: 2px;
-    display: block;
-}
-
 .toolbar-search {
     display: flex;
     width: 100%;
@@ -81,7 +75,8 @@
 }
 
 .toolbar-search-buttons {
-    flex-basis: 182px;
+    display: flex;
+    flex-direction: column;
 }
 
 .toolbar-replace-control,
@@ -132,3 +127,12 @@
     color: rgb(165, 165, 165);
     align-self: center;
 }
+
+.first-row-buttons {
+    display: flex;
+    justify-content: space-between;
+}
+
+.toolbar-search > .replace-toggle-toolbar {
+    margin: 0 -3px 0 0;
+}
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css b/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css
index f7a401d4..fbd9293 100644
--- a/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css
+++ b/third_party/WebKit/Source/devtools/front_end/ui/toolbar.css
@@ -110,7 +110,7 @@
     cursor: pointer;
 }
 
-:not(.toolbar-render-as-links) .toolbar-button:not(.toolbar-has-glyph):not(.toolbar-has-dropdown):not(.largeicon-menu):hover {
+.toolbar-toggled-gray:not(.toolbar-render-as-links) .toolbar-button:not(.toolbar-has-glyph):not(.toolbar-has-dropdown):not(.largeicon-menu):hover {
     background-color: #f3f3f3;
 }
 
diff --git a/third_party/WebKit/Source/modules/notifications/NotificationData.cpp b/third_party/WebKit/Source/modules/notifications/NotificationData.cpp
index 511132a1..cd3200cb 100644
--- a/third_party/WebKit/Source/modules/notifications/NotificationData.cpp
+++ b/third_party/WebKit/Source/modules/notifications/NotificationData.cpp
@@ -12,6 +12,7 @@
 #include "modules/notifications/NotificationOptions.h"
 #include "modules/vibration/VibrationController.h"
 #include "platform/wtf/Time.h"
+#include "platform/wtf/text/StringView.h"
 #include "public/platform/WebURL.h"
 
 namespace blink {
@@ -94,8 +95,11 @@
     if (exception_state.HadException())
       return WebNotificationData();
 
+    StringView ssv_wire_data = serialized_script_value->GetWireData();
+    DCHECK(ssv_wire_data.Is8Bit());
     Vector<char> serialized_data;
-    serialized_script_value->ToWireBytes(serialized_data);
+    serialized_data.ReserveInitialCapacity(ssv_wire_data.length());
+    serialized_data.Append(ssv_wire_data.Characters8(), ssv_wire_data.length());
 
     web_data.data = serialized_data;
   }
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 4243c80..4708f28d 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -20926,6 +20926,12 @@
   </int>
 </enum>
 
+<enum name="HistoryUrlType">
+  <int value="0" label="Empty"/>
+  <int value="1" label="Same as baseUrl"/>
+  <int value="2" label="Different from baseUrl"/>
+</enum>
+
 <enum name="HomedirEncryptionType">
   <int value="1" label="Ecryptfs"/>
   <int value="2" label="Ext4 Dir Encryption"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5897846..df47145 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -98262,6 +98262,14 @@
   </summary>
 </histogram>
 
+<histogram name="WebView.LoadDataWithBaseUrl.HistoryUrl" enum="HistoryUrlType">
+  <owner>jamwalla@chromium.org</owner>
+  <summary>
+    Records whether the historyUrl parameter to loadDataWithBaseUrl is empty/
+    null, the same as the baseUrl parameter, or different from baseUrl.
+  </summary>
+</histogram>
+
 <histogram name="Welcome.SignInPromptResult" enum="WelcomeSignInPromptOutcome">
   <owner>tmartino@chromium.org</owner>
   <summary>
diff --git a/ui/aura/window_occlusion_tracker.cc b/ui/aura/window_occlusion_tracker.cc
index dca604b..797da38 100644
--- a/ui/aura/window_occlusion_tracker.cc
+++ b/ui/aura/window_occlusion_tracker.cc
@@ -441,9 +441,15 @@
 
 void WindowOcclusionTracker::OnWindowDestroyed(Window* window) {
   DCHECK(!window->GetRootWindow() || (window == window->GetRootWindow()));
-  // Animations should be completed or aborted before a window is destroyed.
-  DCHECK(!WindowIsAnimated(window));
   tracked_windows_.erase(window);
+  // Animations should be completed or aborted before a window is destroyed.
+  DCHECK(!window->layer()->GetAnimator()->IsAnimatingOnePropertyOf(
+      kSkipWindowWhenPropertiesAnimated));
+  // |window| must be removed from |animated_windows_| to prevent an invalid
+  // access in CleanupAnimatedWindows() if |window| is being destroyed from a
+  // LayerAnimationObserver after an animation has ended but before |this| has
+  // been notified.
+  animated_windows_.erase(window);
 }
 
 void WindowOcclusionTracker::OnWindowAddedToRootWindow(Window* window) {
diff --git a/ui/aura/window_occlusion_tracker_unittest.cc b/ui/aura/window_occlusion_tracker_unittest.cc
index 3d71981..57cb808 100644
--- a/ui/aura/window_occlusion_tracker_unittest.cc
+++ b/ui/aura/window_occlusion_tracker_unittest.cc
@@ -13,7 +13,9 @@
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/test/test_windows.h"
 #include "ui/aura/window_observer.h"
+#include "ui/compositor/layer_animation_observer.h"
 #include "ui/compositor/layer_animation_sequence.h"
+#include "ui/compositor/layer_animator.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/scoped_layer_animation_settings.h"
 #include "ui/compositor/test/layer_animator_test_controller.h"
@@ -1160,4 +1162,64 @@
   window->RemoveObserver(&observer);
 }
 
+namespace {
+
+class ObserverDestroyingWindowOnAnimationEnded
+    : public ui::LayerAnimationObserver {
+ public:
+  ObserverDestroyingWindowOnAnimationEnded(Window* window) : window_(window) {}
+
+  ~ObserverDestroyingWindowOnAnimationEnded() override {
+    EXPECT_FALSE(window_);
+  }
+
+  void OnLayerAnimationEnded(ui::LayerAnimationSequence* sequence) override {
+    EXPECT_TRUE(window_);
+    delete window_;
+    window_ = nullptr;
+  }
+
+  void OnLayerAnimationAborted(ui::LayerAnimationSequence* sequence) override {}
+  void OnLayerAnimationScheduled(
+      ui::LayerAnimationSequence* sequence) override {}
+
+ private:
+  Window* window_;
+
+  DISALLOW_COPY_AND_ASSIGN(ObserverDestroyingWindowOnAnimationEnded);
+};
+
+}  // namespace
+
+// Verify that no crash occurs if a LayerAnimationObserver destroys a tracked
+// window before WindowOcclusionTracker is notified that the animation ended.
+TEST_F(WindowOcclusionTrackerTest,
+       DestroyTrackedWindowFromLayerAnimationObserver) {
+  ui::ScopedAnimationDurationScaleMode scoped_animation_duration_scale_mode(
+      ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+  ui::LayerAnimatorTestController test_controller(
+      ui::LayerAnimator::CreateImplicitAnimator());
+  ui::ScopedLayerAnimationSettings layer_animation_settings(
+      test_controller.animator());
+  layer_animation_settings.SetTransitionDuration(kTransitionDuration);
+
+  // Create a window. Expect it to be non-occluded.
+  MockWindowDelegate* delegate = new MockWindowDelegate();
+  delegate->set_expectation(WindowOcclusionChangedExpectation::NOT_OCCLUDED);
+  Window* window = CreateTrackedWindow(delegate, gfx::Rect(0, 0, 10, 10));
+  EXPECT_FALSE(delegate->is_expecting_call());
+  window->layer()->SetAnimator(test_controller.animator());
+
+  // Add a LayerAnimationObserver that destroys the window when an animation
+  // ends.
+  ObserverDestroyingWindowOnAnimationEnded observer(window);
+  window->layer()->GetAnimator()->AddObserver(&observer);
+
+  // Start animating the opacity of the window.
+  window->layer()->SetOpacity(0.5f);
+
+  // Complete the animation. Expect no crash.
+  window->layer()->GetAnimator()->StopAnimating();
+}
+
 }  // namespace aura
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
index 4f2c840..c7975556 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.html
@@ -144,7 +144,8 @@
       </div>
       <div>
         <button is="paper-icon-button-light" id="takePhoto" tabindex="1"
-            title="[[takePhotoLabel]]" on-tap="takePhoto"
+            title="[[getTakePhotoLabel_(videomode, takePhotoLabel,
+                captureVideoLabel)]]" on-tap="takePhoto"
             disabled="[[!cameraOnline_]]">
         </button>
       </div>
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
index b5af092..67e7b727 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_camera.js
@@ -35,6 +35,7 @@
   properties: {
     /** Strings provided by host */
     takePhotoLabel: String,
+    captureVideoLabel: String,
     switchModeToCameraLabel: String,
     switchModeToVideoLabel: String,
 
@@ -276,6 +277,15 @@
   },
 
   /**
+   * Returns the label to use for take photo button.
+   * @return {string}
+   * @private
+   */
+  getTakePhotoLabel_: function(videomode, photoLabel, videoLabel) {
+    return videomode ? videoLabel : photoLabel;
+  },
+
+  /**
    * Returns the label to use for switch mode button.
    * @return {string}
    * @private
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
index d71a41b..4abd85b3 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.html
@@ -76,6 +76,7 @@
     <template is="dom-if" if="[[cameraActive_]]">
       <cr-camera id="camera"
           take-photo-label="[[takePhotoLabel]]"
+          capture-video-label="[[captureVideoLabel]]"
           switch-mode-to-camera-label="[[switchModeToCameraLabel]]"
           switch-mode-to-video-label="[[switchModeToVideoLabel]]"
           video-mode-enabled="[[cameraVideoModeEnabled]]">
diff --git a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.js b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.js
index a3f4d46f..c7a869d 100644
--- a/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.js
+++ b/ui/webui/resources/cr_elements/chromeos/cr_picture/cr_picture_pane.js
@@ -43,6 +43,7 @@
     discardImageLabel: String,
     previewAltText: String,
     takePhotoLabel: String,
+    captureVideoLabel: String,
     switchModeToCameraLabel: String,
     switchModeToVideoLabel: String,